Pohled pod kapotu JVM – přehrávání a mixování zvuků s využitím knihovny SDLJava

Pavel Tišnovský 6. 5. 2014

V předchozí části seriálu o JVM i o programovacím jazyku Java jsme si popsali způsob přehrávání hudby s využitím knihovny SDLJava. Dnes si řekneme, jakým způsobem je možné zajistit načítání, přehrávání i mixování zvuků, neboť i ty jsou součástí mnoha multimediálních aplikací.

Obsah

1. Pohled pod kapotu JVM – přehrávání a mixování zvuků s využitím knihovny SDLJava

2. Podporované zvukové formáty

3. Formát souborů RIFF/WAVE

4. Utilita pro načtení a výpis základních informací o souborech typu RIFF/WAVE

5. Načtení souboru se zvukem do aplikace využívající knihovnu SDLJava

6. Demonstrační příklad SDLTest66 – načtení a přehrání zvuku

7. Demonstrační příklad SDLTest67 – vliv velikosti bufferu na vznik chyb při přehrávání zvuků

8. Demonstrační příklad SDLTest68 – využití osmi zvukových kanálů pro mixování

9. Repositář se zdrojovými kódy všech čtyř dnešních demonstračních příkladů

10. Odkazy na Internetu

1. Pohled pod kapotu JVM – přehrávání a mixování zvuků s využitím knihovny SDLJava

V předchozí části seriálu o programovacím jazyku Java i o virtuálním stroji Javy jsme si na čtveřici demonstračních příkladů ukázali, jakým způsobem je možné v knihovně SDLJava přehrávat hudbu, a to s využitím třídy SDLMixer. Stejná třída, přesněji řečeno metody implementované v této třídě, se používají i při přehrávání a mixování zvuků, což je téma, kterému se budeme věnovat dnes. Přehrávání hudby bylo poměrně snadné, protože se – alespoň z hlediska aplikačního programátora – pro načtení a přehrání hudby použilo pouze několik metod. U zvuků může být situace paradoxně složitější, a to zejména z toho důvodu, že zvuky jsou v multimediálních aplikacích (především ve hrách) navázány na nějakou událost a zvolený zvuk se musí v ideálním případě začít přehrávat přesně ve chvíli, kdy k dané události dojde. Ovšem tento požadavek je poměrně problematické splnit, a to především kvůli existenci bufferů, do nichž se zvuková data vkládají, a absenci potřebných RT rozšíření operačních systémů.

2. Podporované zvukové formáty

Knihovna SDLJava podporuje několik zvukových formátů použitelných jako zdroj zvuků pro vytvářenou multimediální aplikaci. Tyto formáty jsou vypsány v tabulce umístěné pod tímto odstavcem:

# Formát Stručný popis
1 .wav Zvukové vzorky uložené ve formátu RIFF/WAVE
2 .aif, .aiff Audio Interchange File Format odvozený od staršího formátu .iff (Amiga)
3 .voc Zvukové vzorky uložené ve formátu VOC (použito v SW od firmy Creative Labs, výrobce SoundBlasterů)
4 .ogg Zvukový/hudební formát Ogg Vorbis

Povšimněte si mnoha rozdílů oproti podporovaným hudebním formátům (viz též předchozí část tohoto seriálu, v níž jsme se speciálně věnovali Amiga modulům i formátu General MIDI):

# Typ (koncovka souboru) Popis
1 .wav zvukové vzorky uložené ve formátu RIFF/WAVE
2 .voc zvukové vzorky uložené ve formátu VOC (použito v SW od firmy Creative Labs, výrobce SoundBlasterů)
3 .mod Amiga moduly a odvozené formáty
4 .xm Amiga moduly a odvozené formáty
5 .s3m Amiga moduly a odvozené formáty
6 .669, Amiga moduly a odvozené formáty
7 .it Amiga moduly a odvozené formáty
8 .midi Hudební formát MIDI
9 .ogg Zvukový/hudební formát Ogg Vorbis

3. Formát souborů RIFF/WAVE

Velká část her postavených na (nativní) knihovně SDL používá pro ukládání zvuků formát RIFF/WAVE, tj. soubory, které mají většinou příponu .wav. Jedná se o obecný formát, který zpočátku umožňoval pouze ukládání nezkomprimované hudby s využitím PCM (pulzně kódové modulace), ovšem posléze byl rozšířen i o možnosti využití různých metod pro bezeztrátovou i ztrátovou komprimaci zvuků. Vzhledem k jednoduchosti a taktéž univerzálnosti formátu RIFF/WAVE se můžeme s tímto formátem setkat i v dalších oblastech, které přímo nesouvisí se zpracováním zvuku – do souborů s koncovkou .wav je totiž možné ukládat prakticky libovolné digitalizované jednorozměrné signály o teoretické maximální vzorkovací frekvenci až 16 GHz (232-1 Hz), přičemž (opět teoretický) počet kanálů může dosahovat až 65535. To znamená, že do .wav lze ukládat například signály získané z různých čidel, signály určené pro digitální osciloskopy atd. Právě z tohoto důvodu si základní formát souborů WAVE popíšeme podrobněji, protože se může programátorům v některých situacích hodit (a většinou nemá smysl znovuvynalézat kolo :-).

Soubory typu RIFF/WAVE se skládají z takzvaných chunků, přičemž každý chunk (budeme mu možná poněkud nepřesně říkat blok) na svém začátku obsahuje čtyři bajty s jednoznačným jménem, za nímž jsou uloženy další čtyři bajty, v nichž je uložena délka bloku. Jednotlivé bloky se mohou vnořovat, což například znamená, že „hlavní“ blok má délku rovnou velikosti celého souboru. Nás v tuto chvíli zajímají především ty soubory RIFF/WAVE, v nichž jsou uložena nezkomprimovaná zvuková data. Tyto soubory se skládají ze tří typů bloků, jejichž jména jsou vypsána v následující tabulce:

Jméno bloku Význam
„RIFF“ blok obsahující hlavičku se základními informacemi o souboru
"fmt " blok obsahující informace o formátu uložených zvukových dat
„data“ vlastní zvuková data (ať již zkomprimovaná či nezkomprimovaná)

Hlavní blok má název „RIFF“ a jeho hlavička má délku pouze dvanáct bajtů. V prvních čtyřech bajtech je uloženo jméno bloku, což musí být ASCII znaky „R“, „I“, „F“ a „F“ v uvedeném pořadí. Za těmito čtyřmi bajty následuje délka bloku – protože se jedná o hlavní blok, je jeho délka rovna délce celého souboru, ovšem do této délky není započteno prvních osm bajtů hlavičky. Samotná délka i další celočíselné informace používá pořadí ukládání bajtů little endian. Následují čtyři bajty obsahující informace o formátu souboru/dat; v případě zvukových dat jsou zde uloženy bajty obsahující ASCII znaky „W“, „A“, „V“ a „E“, pokud by však soubor obsahoval jiné multimediální informace, byla by zde uložena jiná čtveřice znaků (takový soubor by však neměl koncovku .wav):

Offset Šířka Jméno Význam
0 4 bajty ChunkID musí obsahovat ASCII znaky „RIFF“ v uvedeném pořadí
4 4 bajty ChunkSize délka souboru snížená o osm bajtů
8 4 bajty Format musí obsahovat ASCII znaky „WAVE“ v uvedeném pořadí

Ve zvukových souborech .wav následuje ihned za hlavním blokem „RIFF“ blok s názvem „fmt “ (i s mezerou na konci), který obsahuje, jak již název tohoto bloku naznačuje, informace o způsobu uložení zvukových dat. Po jménu tohoto bloku (čtveřice znaků tvořících řetězec „fmt “) je ve čtyřech bajtech uložena délka bloku, která je u většiny souborů konstantní: 16 bajtů. Na dalších dvou bajtech je uložena celočíselná konstanta reprezentující formát dat. Pro nezkomprimované zvukové soubory využívající PCM (pulzně kódovou modulaci) obsahuje tato dvojice bajtů hodnotu 1. V další dvojici bajtů je uložen celkový počet zvukových kanálů, což typicky bývá jeden kanál pro mono výstup popř. dva kanály pro stereo výstup, ovšem teoreticky je možné využít až 65535 zvukových kanálů.

Další čtyři bajty obsahují vzorkovací frekvenci, typicky 11025, 22050, 44100 či 48000 Hz, ovšem lze nalézt i zvukové soubory s odlišnými frekvencemi. Za touto hodnotou je v navazujících čtyřech bajtech uložena rychlost přenosu dat, která se dá vypočítat pomocí vzorce SampleRate*NumChannels*Bit­sPerSample/8. Dalším údajem je hodnota o zarovnání bloku, která se dá taktéž vypočítat, a to s využitím vzorce NumChannels*BitsPerSample/8. Poslední údaj je velmi důležitý – ve dvou bajtech je uložen počet bitů pro každý zvukový vzorek. U osmibitového samplování je zde uložena hodnota 8, u 16bitového samplování (CD) hodnota 16, ovšem některé zvukové soubory mohou používat i další hodnoty, například 24 apod.:

Offset Šířka Jméno Význam
12 4 bajty Subchunk1ID musí obsahovat ASCII znaky "fmt " (i s mezerou na konci)
16 4 bajty Subchunk1Size délka tohoto bloku, pro PCM je zde uložena hodnota 16
20 2 bajty AudioFormat pro PCM je zde uložena hodnota 1
22 2 bajty NumChannels počet zvukových kanálů, typicky 1 pro mono výstup a 2 pro stereo výstup
24 4 bajty SampleRate vzorkovací frekvence, typicky 11025 (starší SW), 22050, 44100 (CD) či 48000 (DAT a další profi zařízení) atd.
28 4 bajty ByteRate rychlost přenosu dat do zvukového subsystému (SampleRate*NumChannels*Bit­sPerSample/8)
32 2 bajty BlockAlign zarovnání bloku (NumChannels*BitsPerSample/8)
34 2 bajty BitsPerSample počet bitů na každý zvukový vzorek, typicky 8, 16 či 24

Vlastní zvuková data jsou uložena v bloku s názvem „data“. Tento blok opět začíná svým jménem (čtveřicí ASCII znaků), délkou odpovídající vzorci NumSamples*NumChannels*Bit­sPerSample/8 a samotnými samply, tj. zvukovými vzorky tvořícími digitální signál v jednotlivých zvukových kanálech. Při formátu PCM nedochází k žádné komprimaci, snad s výjimkou čtyřbitových samplů, kde je dvojice vzorků spojena do jednoho bajtu, ovšem dnes snad už nikdo čtyřbitové samplování nepoužívá :-)

Offset Šířka Jméno Význam
36 4 bajty Subchunk2ID musí obsahovat ASCII znaky „data“
40 4 bajty Subchunk2Size délka datového bloku (==NumSamples*NumChannels*Bit­sPerSample/8)
44 proměnná délka Data vlastní zvuková data (za sebou uložené vzorky jednotlivých zvukových kanálů)

4. Utilita pro načtení a výpis základních informací o souborech typu RIFF/WAVE

Podívejme se nyní na zdrojový kód jednoduché utility, která se pokusí načíst a následně zobrazit základní informace o souborech typu RIFF/WAVE. Tato utilita pracuje velmi jednoduše – postupně načítá jednotlivé prvky ze dvou nám již známých bloků „RIFF“ a „fmt “, přičemž je kód utility napsán takovým způsobem, aby pracoval korektně jak na architekturách s pořadím bajtů little endian, tak i na architekturách s pořadím bajtů big endian (nebo se smíšeným pořadím, což je však dnes spíše již historická záležitost). Tuto utilitu by mělo jít (doufejme) přeložit jakýmkoli překladačem ANSI C:

/*
 * Nastroj pro vypis zakladnich udaju prectenych z hlavicky souboru .wav
 *
 * Pavel Tisnovsky 2014
 *
 * Preklad;
 * gcc -ansi -Wall -pedantic read_wav_header.c
 */
 
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
 
 
 
/*
 * Funkce zavolana v pripade, ze pri cteni ze souboru dojde k chybe.
 */
void read_error(FILE *fin)
{
    printf("Chyba pri cteni bajtu na offsetu %ld\n", ftell(fin));
    if (fclose(fin))
    {
        puts("... a uzavreni souboru taky selhalo!");
    }
    exit(1);
}
 
 
 
/*
 * Precteni jednoho bajtu ze souboru s kontrolou, zda nedoslo k chybe.
 */
uint8_t read_byte(FILE *fin)
{
    int i = fgetc(fin);
    if (i == EOF)
    {
        read_error(fin);
    }
    return i;
}
 
 
 
/*
 * Precteni dvou bajtu ze souboru s kontrolou, zda nedoslo k chybe.
 * (format little endian)
 */
uint16_t read_two_bytes(FILE *fin)
{
    int i1 = read_byte(fin);
    int i2 = read_byte(fin);
    return (i2 << 8) | (i1);
}
 
 
 
/*
 * Precteni ctyr bajtu ze souboru s kontrolou, zda nedoslo k chybe.
 * (format little endian)
 */
uint32_t read_four_bytes(FILE *fin)
{
    int i1 = read_byte(fin);
    int i2 = read_byte(fin);
    int i3 = read_byte(fin);
    int i4 = read_byte(fin);
    return (i4 << 24) | (i3 << 16) | (i2 << 8) | (i1);
}
 
 
 
/*
 * Precteni identifikatoru hlavniho bloku z hlavicky souboru .wav
 */
void print_chunk_id(FILE *fin)
{
    uint32_t chunkID = read_four_bytes(fin);
    /* identifikator musi byt slozen ze znaku "RIFF" */
    if (chunkID == (('R' ) | ('I' << 8) | ('F' << 16) | ('F' << 24)))
    {
        puts("ChunkID         = 'RIFF', ok");
    }
    else
    {
        printf("Wrong chunkID %x\n", chunkID);
        exit(1);
    }
}
 
 
 
/*
 * Precteni velikosti hlavniho bloku z hlavicky souboru .wav
 */
void print_chunk_size(FILE *fin)
{
    uint32_t chunk_size = read_four_bytes(fin);
    printf("Chunk size      = %d bytes\n", chunk_size);
    printf("Total file size = %d bytes\n", chunk_size + 8);
}
 
 
 
/*
 * Precteni formatu souboru z hlavicky souboru .wav
 */
void print_format(FILE *fin)
{
    uint32_t format = read_four_bytes(fin);
    /* identifikator musi byt slozen ze znaku "WAVE" */
    if (format == (('W' ) | ('A' << 8) | ('V' << 16) | ('E' << 24)))
    {
        puts("Format          = 'WAVE', ok");
    }
    else
    {
        printf("Wrong format %x\n", format);
        exit(1);
    }
}
 
 
 
/*
 * Precteni identifikatoru prvniho subbloku z hlavicky souboru .wav
 */
void print_subchunk1_id(FILE *fin)
{
    uint32_t chunkID = read_four_bytes(fin);
    /* identifikator musi byt slozen ze znaku "fmt " */
    if (chunkID == (('f' ) | ('m' << 8) | ('t' << 16) | (' ' << 24)))
    {
        puts("Subchunk1ID     = 'fmt ', ok");
    }
    else
    {
        printf("Wrong Subchunk1ID %x\n", chunkID);
        exit(1);
    }
}
 
 
 
/*
 * Precteni velikosti prvniho subbloku z hlavicky souboru .wav
 */
void print_subchunk1_size(FILE *fin)
{
    uint32_t chunk_size = read_four_bytes(fin);
    printf("Subchunk1 size  = %d bytes\n", chunk_size);
}
 
 
 
/*
 * Precteni audio formatu z hlavicky souboru .wav
 */
void print_audio_format(FILE *fin)
{
    uint16_t audio_format = read_two_bytes(fin);
    printf("Audio format    = %s\n", audio_format == 1 ? "PCM" : "compressed");
}
 
 
 
/*
 * Precteni poctu audio kanalu z hlavicky souboru .wav
 */
void print_num_channels(FILE *fin)
{
    uint16_t num_channels = read_two_bytes(fin);
    printf("Audio channels  = %d\n", num_channels);
}
 
 
 
/*
 * Precteni vzorkovaci frekvence z hlavicky souboru .wav
 */
void print_sample_rate(FILE *fin)
{
    uint32_t sample_rate = read_four_bytes(fin);
    printf("Sample rate     = %d samples/sec\n", sample_rate);
}
 
 
 
/*
 * Precteni rychlosti prenosu dat do audio subsystemu z hlavicky souboru .wav
 */
void print_byte_rate(FILE *fin)
{
    uint32_t byte_rate = read_four_bytes(fin);
    printf("Byte rate       = %d bytes/sec\n", byte_rate);
}
 
 
 
/*
 * Precteni poctu bitu na jeden vzorek z hlavicky souboru .wav
 */
void print_bits_per_sample(FILE *fin)
{
    uint16_t bits_per_sample = read_two_bytes(fin);
    printf("Bits per sample = %d\n", bits_per_sample);
}
 
 
 
/*
 * Tisk zakladnich informaci ziskanych z hlavicky souboru .wav
 */
int print_wav_header(char *file_name)
{
    FILE *fin;
    fin = fopen(file_name, "rb");
    if (!fin)
    {
        perror("File open error");
        return 1;
    }
    print_chunk_id(fin);
    print_chunk_size(fin);
    print_format(fin);
    print_subchunk1_id(fin);
    print_subchunk1_size(fin);
    print_audio_format(fin);
    print_num_channels(fin);
    print_sample_rate(fin);
    print_byte_rate(fin);
    read_two_bytes(fin);
    print_bits_per_sample(fin);
    fclose(fin);
    return 0;
}
 
 
 
/*
 * Zaciname...
 */
int main(int argc, char **argv)
{
    if (argc != 2)
    {
        puts("Usage: read_wav_header test.wav");
        return 1;
    }
    return print_wav_header(argv[1]);
}
 
 
 
/*
 * Finito
 */

Překlad této utility se provede jednoduše:

gcc -ansi -Wall -pedantic read_wav_header.c -o read_wav_header

Příklad použití si ukážeme na čtveřici demonstračních souborů .WAV:

8×8bitpcm.wav:

ChunkID         = 'RIFF', ok
Chunk size      = 110524 bytes
Total file size = 110532 bytes
Format          = 'WAVE', ok
Subchunk1ID     = 'fmt ', ok
Subchunk1 size  = 16 bytes
Audio format    = PCM
Audio channels  = 1
Sample rate     = 8000 samples/sec
Byte rate       = 8000 bytes/sec
Bits per sample = 8

11k8bitpcm.wav:

ChunkID         = 'RIFF', ok
Chunk size      = 152304 bytes
Total file size = 152312 bytes
Format          = 'WAVE', ok
Subchunk1ID     = 'fmt ', ok
Subchunk1 size  = 16 bytes
Audio format    = PCM
Audio channels  = 1
Sample rate     = 11025 samples/sec
Byte rate       = 11025 bytes/sec
Bits per sample = 8

11k16bitpcm.wav:

ChunkID         = 'RIFF', ok
Chunk size      = 304570 bytes
Total file size = 304578 bytes
Format          = 'WAVE', ok
Subchunk1ID     = 'fmt ', ok
Subchunk1 size  = 16 bytes
Audio format    = PCM
Audio channels  = 1
Sample rate     = 11025 samples/sec
Byte rate       = 22050 bytes/sec
Bits per sample = 16

Kitten.wav:

ChunkID         = 'RIFF', ok
Chunk size      = 37412 bytes
Total file size = 37420 bytes
Format          = 'WAVE', ok
Subchunk1ID     = 'fmt ', ok
Subchunk1 size  = 16 bytes
Audio format    = PCM
Audio channels  = 1
Sample rate     = 22050 samples/sec
Byte rate       = 44100 bytes/sec
Bits per sample = 16

5. Načtení souboru se zvukem do aplikace využívající knihovnu SDLJava

Pro načtení souboru se zvukem s využitím knihovny SDLJava nemusíme podrobně znát vnitřní formát zvukových souborů. Musíme pouze korektně inicializovat zvukový subsystém, nastavit hlasitost zvukových kanálů a zahájit přehrávání. Inicializace zvukového subsystému se provádí nám již známou metodou SDLMixer.openAudio(), které se předá frekvence přehrávaných samplů (na výstupu, nikoli nutně na vstupu), formát samplů, počet výstupních zvukových kanálů a velikost bufferu použitého při přehrávání a mixování:

SDLMixer.openAudio(44100, SDLMixer.AUDIO_S16, 1, 1024);

Frekvence přehrávaných samplů se u her většinou nastavuje na 22050, tj. na poloviční frekvenci, než je použita u Audio CD. Tato frekvence na jednu stranu zajistí poměrně kvalitní zvukový výstup, na stranu druhou není zbytečně zatěžován procesor v průběhu mixování. Jen pro zajímavost se podívejme na některé často používané samplovací/přehrávací frekvence:

# Frekvence Použito
1 8000 některé wireless telefony a další zařízení zaměřené pouze na přenos lidského hlasu
2 11025 čtvrtina samplovací frekvence CD, použito například u některých MPEG nebo i v počítačových hrách pro hlas a zvuky (nikoli pro hudbu)
3 16000 použito ve VoIP (postačuje bez větších zkreslení pro přenos lidského hlasu)
4 22050 polovina samplovací frekvence CD, použito i v mnoha hrách (výhodný poměr kvalita/nároky na mixování)
5 22254 kdysi použito na počítačích Macintosh, proto součást mnoha především starších multimediálních aplikací
6 32000 digi rádio apod. (dosažitelný frekvenční rozsah odpovídá FM radiu)
7 44056 použito v systémech, které kombinují audio signál s NTSC videem (přesně 3 samply na každý video řádek)
8 44100 samplovací frekvence CD, ve hrách může způsobit problémy při mixování většího množství kanálů (někdy i nutnost zvětšení bufferu)
9 48000 většina profi audio zařízení
10 88200 většina profi zařízení, použito ve chvíli, kdy má být výsledek uložen na CD
11 96000 DVD-audio

Načtení souboru se zvukem zajistí metoda SDLMixer.loadWAV():

MixChunk effect = SDLMixer.loadWAV(fileName);

Samotné přehrávání (i na všech kanálech) zajistí příkaz SDLMixer.playChannel(), kde lze i specifikovat, kolikrát se má zvuk přehrát (poslední parametr). Lze dokonce zvolit i neustálé opakování přehrávání (loop) zadáním záporné hodnoty –1:

SDLMixer.playChannel(ALL_SOUND_CHANNELS, effect, 0);

Test, zda se na určitém kanálu či na libovolném kanálu provádí přehrávání zvuku, provádí metoda SDLMixer.playing() vracející počet aktivních kanálů, popř. (při volbě jen jednoho kanálu) hodnotu 1 (přehrává se) či 0 (nepřehrává se):

while (SDLMixer.playing(ALL_SOUND_CHANNELS) > 0) {
    SDLTimer.delay(100);
}

6. Demonstrační příklad SDLTest66 – načtení a přehrání zvuku

V dnešním prvním demonstračním příkladu pojmenovaném SDLTest66 je ukázán nejjednodušší způsob přehrávání zvuků s využitím knihovny SDLJava. Nejprve je provedena inicializace zvukového subsystému zavoláním metody SDLMixer.openAudio(44100, SDLMixer.AUDIO_S16, 1, 1024), v níž je specifikováno, že se má vytvořit jeden (mono) výstupní zvukový kanál, frekvence mixování a přehrávání samplů má být 44100 Hz, formát přehrávaných a mixovaných samplů odpovídá šestnáctibitovým celým číslům (integer) se znaménkem a velikost zvukového bufferu má být 1024 bajtů, což je při přehrávání zvuku v jednom kanálu dostačující. Následně je nastavena hlasitost všech zvukových kanálů metodou SDLMixer.volume(ALL_SOUND_CHANNELS, 100), zvuk je načten příkazem SDLMixer.loadWAV(fileName) a konečně se zahájí jeho přehrávání s využitím metody SDLMixer.playChannel(ALL_SO­UND_CHANNELS, effect, 0). To, zda se stále provádí přehrávání zvuku, lze zjistit metodou SDLMixer.playing(ALL_SOUND_CHANNELS).

Následuje výpis zdrojového kódu tohoto demonstračního příkladu:

import sdljava.SDLMain;
import sdljava.SDLException;
import sdljava.SDLTimer;
 
import sdljava.mixer.SDLMixer;
import sdljava.mixer.MixChunk;
 
 
 
/**
 * Sedesaty sesty demonstracni priklad vyuzivajici knihovnu SDLjava.
 *
 * Prehravani zvuku s vyuzitim knihovny SDLJava.
 *
 * @author Pavel Tisnovsky
 */
public class SDLTest66 {
 
    /**
     * Konstanta zastupujici vsechny zvukove kanaly.
     */
    static final int ALL_SOUND_CHANNELS = -1;
 
    /**
     * Prehrani zvukoveho vzorku.
     */
    public static void playSample(String fileName) 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(fileName);
 
        // 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 sesteho demonstracniho prikladu.
     */
    public static void main(String[] args) {
        try {
            // inicializace knihovny SDLJava
            SDLMain.init(SDLMain.SDL_INIT_AUDIO);
 
            if (args.length != 1) {
                System.out.println("Usage: java SDLTest66 filename.wav");
            }
 
            // ziskat jmeno prehravaneho samplu
            final String fileName = args[0];
 
            // prehrani zvukoveho vzorku
            playSample(fileName);
        }
        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 SDLTest66.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 SDLTest66.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 SDLTest66

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% SDLTest66

7. Demonstrační příklad SDLTest67 – vliv velikosti bufferu na vznik chyb při přehrávání zvuků

V dnešním druhém demonstračním příkladu pojmenovaném SDLTest67 je ukázáno, k jakým problémům může dojít, pokud se zvolí příliš malá velikost bufferu používaného v průběhu přehrávání i mixování zvuků. V předchozím příkladu SDLTest66 byla velikost tohoto bufferu nastavena na 1024 bajtů, což odpovídá 512 šestnáctibitovým samplům, tj. přibližně 12 ms přehrávání při použití frekvence 44100 Hz. To (poněkud zjednodušeně řečeno) znamená, že mikroprocesor měl v průběhu přehrávání zvuků i hudby k dispozici maximálně oněch 12 ms, aby dokázal postupně mixovat a doplňovat audio data do bufferu. V příkladu SDLTest67 je naproti tomu velikost bufferu snížena na pouhých 32 bajtů, což znamená, že při přehrávání bude prakticky na všech počítačích docházet k jasně slyšitelným chybám, což si ostatně lze snadno ověřit překladem a následným spuštěním tohoto demonstračního příkladu (velikost bufferu je možné ještě více snížit nebo naopak zvýšit – pro přehrávání hudby se například doporučuje hodnota 4096 bajtů).

import sdljava.SDLMain;
import sdljava.SDLException;
import sdljava.SDLTimer;
 
import sdljava.mixer.SDLMixer;
import sdljava.mixer.MixChunk;
 
 
 
/**
 * Sedesaty sedmy demonstracni priklad vyuzivajici knihovnu SDLjava.
 *
 * Prehravani zvuku s vyuzitim knihovny SDLJava.
 *
 * @author Pavel Tisnovsky
 */
public class SDLTest67 {
 
    /**
     * Konstanta zastupujici vsechny zvukove kanaly.
     */
    static final int ALL_SOUND_CHANNELS = -1;
 
    /**
     * Prehrani zvukoveho vzorku.
     */
    public static void playSample(String fileName) throws SDLException, InterruptedException {
        // nastaveni vlastnosti zvukoveho subsystemu i vybranych zvukovych kanalu
        // velikost bufferu je velmi mala, coz povede k problemum pri prehravani zvuku
        SDLMixer.openAudio(44100, SDLMixer.AUDIO_S16, 1, 32);
        SDLMixer.volume(ALL_SOUND_CHANNELS, 100);
 
        // nacteni samplu z externiho souboru
        MixChunk effect = SDLMixer.loadWAV(fileName);
 
        // 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 sedmeho demonstracniho prikladu.
     */
    public static void main(String[] args) {
        try {
            // inicializace knihovny SDLJava
            SDLMain.init(SDLMain.SDL_INIT_AUDIO);
 
            if (args.length != 1) {
                System.out.println("Usage: java SDLTest66 filename.wav");
            }
 
            // ziskat jmeno prehravaneho samplu
            final String fileName = args[0];
 
            // prehrani zvukoveho vzorku
            playSample(fileName);
        }
        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 SDLTest67.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 SDLTest67.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 SDLTest67

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% SDLTest67

8. Demonstrační příklad SDLTest68 – využití osmi zvukových kanálů pro mixování

V dnešním posledním demonstračním příkladu SDLTest68 je ukázáno, jakým způsobem lze zajistit mixování většího množství vstupních zvukových kanálů do jediného kanálu výstupního. Vzhledem k tomu, že se má mixovat a přehrávat osm zvukových kanálů, je velikost bufferu v metodě SDLMixer.openAudio(44100, SDLMixer.AUDIO_S16, 1, 2048) zvětšena na 2048 bajtů. Tato velikost je dostačující i na mém obstarožním deset let starém počítači. Následně je načten jediný zvuk metodou SDLMixer.loadWAV(fileName) a posléze začne být tento zvuk přehráván osmkrát, vždy se zpožděním přibližně 100 ms, čímž vznikne efekt několikanásobného echa:

// prehrani samplu na osmi kanalech
for (int channel = 0; channel < CHANNELS; channel++) {
    SDLTimer.delay(100);
    SDLMixer.playChannel(channel, effect, 0);
}

Test na ukončení přehrávání je stále prakticky totožný s testy, které jsme již viděli i v předchozích demonstračních příkladech:

int channels;
while ((channels = (SDLMixer.playing(ALL_SOUND_CHANNELS))) > 0) {
    System.out.println(channels + " channel(s) are playing");
    SDLTimer.delay(100);
}

Zdrojový kód demonstračního příkladu SDLTest68 vypadá následovně:

import sdljava.SDLMain;
import sdljava.SDLException;
import sdljava.SDLTimer;
 
import sdljava.mixer.SDLMixer;
import sdljava.mixer.MixChunk;
 
 
 
/**
 * Sedesaty osmy demonstracni priklad vyuzivajici knihovnu SDLjava.
 *
 * Prehravani a mixovani zvuku s vyuzitim knihovny SDLJava.
 *
 * @author Pavel Tisnovsky
 */
public class SDLTest68 {
 
    /**
     * Konstanta zastupujici vsechny zvukove kanaly.
     */
    static final int ALL_SOUND_CHANNELS = -1;
 
    /**
     * Pocet pouzitych zvukovych kanalu.
     */
    static final int CHANNELS = 8;
 
    /**
     * Prehrani zvukoveho vzorku.
     */
    public static void playSample(String fileName) throws SDLException, InterruptedException {
        // nastaveni vlastnosti zvukoveho subsystemu i vybranych zvukovych kanalu
        SDLMixer.openAudio(44100, SDLMixer.AUDIO_S16, 1, 2048);
 
        // nastavit pocet zvukovych kanalu
        SDLMixer.allocateChannels(CHANNELS);
 
        // nastavit hlasitost vsech kanalu
        SDLMixer.volume(ALL_SOUND_CHANNELS, 100);
 
        // nacteni samplu z externiho souboru
        MixChunk effect = SDLMixer.loadWAV(fileName);
 
        // prehrani samplu na osmi kanalech
        for (int channel = 0; channel < CHANNELS; channel++) {
            SDLTimer.delay(100);
            SDLMixer.playChannel(channel, effect, 0);
        }
 
        // musime pockat na ukonceni prehravani
        int channels;
        while ((channels = (SDLMixer.playing(ALL_SOUND_CHANNELS))) > 0) {
            System.out.println(channels + " channel(s) are playing");
            SDLTimer.delay(100);
        }
    }
 
    /**
     * Spusteni sedesateho osmeho demonstracniho prikladu.
     */
    public static void main(String[] args) {
        try {
            // inicializace knihovny SDLJava
            SDLMain.init(SDLMain.SDL_INIT_AUDIO);
 
            if (args.length != 1) {
                System.out.println("Usage: java SDLTest66 filename.wav");
            }
 
            // ziskat jmeno prehravaneho samplu
            final String fileName = args[0];
 
            // prehrani zvukoveho vzorku
            playSample(fileName);
        }
        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 SDLTest68.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 SDLTest68.java

Skript pro spuštění na Linuxu:

widgety

#!/bin/sh
 
SDL_JAVA_LIBS=./sdljava-0.9.1/lib
 
java -cp .:$SDL_JAVA_LIBS/sdljava.jar -Djava.library.path=$SDL_JAVA_LIBS SDLTest68

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% SDLTest68

9. Repositář se zdrojovými kódy všech čtyř dnešních demonstračních příkladů

Všechny čtyři dnes popsané demonstrační příklady byly společně s podpůrnými skripty určenými pro jejich překlad a následné spuštění uloženy do Mercurial repositáře dostupného na adrese http://icedtea.classpath.or­g/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:

# Zdrojový soubor/skript Umístění souboru v repositáři
1 SDLTest66.java http://icedtea.classpath.or­g/people/ptisnovs/jvm-tools/file/e9dd7f620b94/sdlja­va/SDLTest66/SDLTest66.ja­va
2 SDLTest66_compile.sh http://icedtea.classpath.or­g/people/ptisnovs/jvm-tools/file/e9dd7f620b94/sdlja­va/SDLTest66/SDLTest66_com­pile.sh
3 SDLTest66_compile_sys.sh http://icedtea.classpath.or­g/people/ptisnovs/jvm-tools/file/e9dd7f620b94/sdlja­va/SDLTest66/SDLTest66_com­pile_sys.sh
4 SDLTest66_run.sh http://icedtea.classpath.or­g/people/ptisnovs/jvm-tools/file/e9dd7f620b94/sdlja­va/SDLTest66/SDLTest66_run­.sh
5 SDLTest66_run_sys.sh http://icedtea.classpath.or­g/people/ptisnovs/jvm-tools/file/e9dd7f620b94/sdlja­va/SDLTest66/SDLTest66_run_sys­.sh
6 SDLTest66_compile.bat http://icedtea.classpath.or­g/people/ptisnovs/jvm-tools/file/e9dd7f620b94/sdlja­va/SDLTest66/SDLTest66_com­pile.bat
7 SDLTest66_run.bat http://icedtea.classpath.or­g/people/ptisnovs/jvm-tools/file/e9dd7f620b94/sdlja­va/SDLTest66/SDLTest66_run­.bat
     
8 SDLTest67.java http://icedtea.classpath.or­g/people/ptisnovs/jvm-tools/file/e9dd7f620b94/sdlja­va/SDLTest67/SDLTest67.ja­va
9 SDLTest67_compile.sh http://icedtea.classpath.or­g/people/ptisnovs/jvm-tools/file/e9dd7f620b94/sdlja­va/SDLTest67/SDLTest67_com­pile.sh
10 SDLTest67_compile_sys.sh http://icedtea.classpath.or­g/people/ptisnovs/jvm-tools/file/e9dd7f620b94/sdlja­va/SDLTest67/SDLTest67_com­pile_sys.sh
11 SDLTest67_run.sh http://icedtea.classpath.or­g/people/ptisnovs/jvm-tools/file/e9dd7f620b94/sdlja­va/SDLTest67/SDLTest67_run­.sh
12 SDLTest67_run_sys.sh http://icedtea.classpath.or­g/people/ptisnovs/jvm-tools/file/e9dd7f620b94/sdlja­va/SDLTest67/SDLTest67_run_sys­.sh
13 SDLTest67_compile.bat http://icedtea.classpath.or­g/people/ptisnovs/jvm-tools/file/e9dd7f620b94/sdlja­va/SDLTest67/SDLTest67_com­pile.bat
14 SDLTest67_run.bat http://icedtea.classpath.or­g/people/ptisnovs/jvm-tools/file/e9dd7f620b94/sdlja­va/SDLTest67/SDLTest67_run­.bat
     
15 SDLTest68.java http://icedtea.classpath.or­g/people/ptisnovs/jvm-tools/file/e9dd7f620b94/sdlja­va/SDLTest68/SDLTest68.ja­va
16 SDLTest68_compile.sh http://icedtea.classpath.or­g/people/ptisnovs/jvm-tools/file/e9dd7f620b94/sdlja­va/SDLTest68/SDLTest68_com­pile.sh
17 SDLTest68_compile_sys.sh http://icedtea.classpath.or­g/people/ptisnovs/jvm-tools/file/e9dd7f620b94/sdlja­va/SDLTest68/SDLTest68_com­pile_sys.sh
18 SDLTest68_run.sh http://icedtea.classpath.or­g/people/ptisnovs/jvm-tools/file/e9dd7f620b94/sdlja­va/SDLTest68/SDLTest68_run­.sh
19 SDLTest68_run_sys.sh http://icedtea.classpath.or­g/people/ptisnovs/jvm-tools/file/e9dd7f620b94/sdlja­va/SDLTest68/SDLTest68_run_sys­.sh
20 SDLTest68_compile.bat http://icedtea.classpath.or­g/people/ptisnovs/jvm-tools/file/e9dd7f620b94/sdlja­va/SDLTest68/SDLTest68_com­pile.bat
21 SDLTest68_run.bat http://icedtea.classpath.or­g/people/ptisnovs/jvm-tools/file/e9dd7f620b94/sdlja­va/SDLTest68/SDLTest68_run­.bat
     
22 read_wav_header.c http://icedtea.classpath.or­g/people/ptisnovs/jvm-tools/file/75f0ad3bfdc7/to­ols/read_wav_header.c

10. Odkazy na Internetu

  1. Audio File Formats.
    http://sox.sourceforge.net/Au­dioFormats-11.html
  2. TestSounds.com: pure digital sounds to test your audio
    http://www.testsounds.com/
  3. Test Tones (20hz – 20khz)
    http://mdf1.tripod.com/test-tones.html
  4. WAV (Wikipedia)
    http://en.wikipedia.org/wiki/WAV
  5. WAVE PCM soundfile format
    https://ccrma.stanford.edu/cou­rses/422/projects/WaveFor­mat/
  6. Audio Interchange File Format
    http://en.wikipedia.org/wiki/Aiff
  7. Musical Instrument Digital Interface,
    http://en.wikipedia.org/wi­ki/Musical_Instrument_Digi­tal_Interface
  8. A MIDI Pedalboard Encode,
    http://www.pykett.org.uk/a_mi­di_pedalboard_encoder.htm
  9. MIDI Note Number, Frequency Table,
    http://tonalsoft.com/pub/news/pitch-bend.aspx
  10. Note names, MIDI numbers and frequencies,
    http://www.phys.unsw.edu.au­/jw/notes.html
  11. The MIDI Specification,
    http://www.gweep.net/~pre­fect/eng/reference/protocol/mi­dispec.html
  12. Essentials of the MIDI protocol,
    http://ccrma.stanford.edu/~cra­ig/articles/linuxmidi/mis­c/essenmidi.html
  13. General MIDI,
    http://en.wikipedia.org/wi­ki/General_MIDI
  14. Obecné MIDI (General MIDI),
    http://www-kiv.zcu.cz/~herout/html_sbo/mi­di/5.html
  15. Custom Chips: Paula
    http://www.amiga-hardware.com/showhardware­.cgi?HARDID=1460
  16. Big Book of Amiga Hardware
    http://www.amiga-resistance.info/bboahfaq/
  17. Amiga Hardware Database
    http://amiga.resource.cx/
  18. ExoticA
    http://www.exotica.org.uk/wi­ki/Main_Page
  19. The absolute basics of Amiga audio
    http://www.sufo.estates.co­.uk/amiga/amimus.html
  20. Wikipedia: Tracker
    http://en.wikipedia.org/wiki/Tracker
  21. Wikipedia: Trackers
    http://en.wikipedia.org/wiki/Trackers
  22. Ultimate Soundtracker
    http://en.wikipedia.org/wi­ki/Ultimate_Soundtracker
  23. Protracker
    http://en.wikipedia.org/wi­ki/ProTracker
  24. Impulse Tracker
    http://en.wikipedia.org/wi­ki/Impulse_Tracker
  25. Scream Tracker
    http://en.wikipedia.org/wi­ki/ScreamTracker
  26. MikMod for Java
    http://jmikmod.berlios.de/
  27. List of audio trackers
    http://en.wikipedia.org/wi­ki/List_of_audio_trackers
  28. Wikipedia: Module File
    http://en.wikipedia.org/wi­ki/Module_file
  29. Wikipedia: Chiptune
    http://en.wikipedia.org/wiki/Chiptune
  30. SDL_mixer 2.0
    http://www.libsdl.org/pro­jects/SDL_mixer/
  31. SDLJava: package sdljava.ttf
    http://sdljava.sourceforge­.net/docs/api/sdljava/ttf/pac­kage-summary.html#package_description
  32. SDLJava: class sdljava.ttf.SDLTTF
    http://sdljava.sourceforge­.net/docs/api/sdljava/ttf/SDLTTF­.html
  33. SDLJava: class sdljava.ttf.SDLTrueTypeFont
    http://sdljava.sourceforge­.net/docs/api/sdljava/ttf/SDLTru­eTypeFont.html
  34. SDL_ttf Documentation
    http://www.libsdl.org/pro­jects/SDL_ttf/docs/
  35. SDL_ttf 2.0 (není prozatím součástí SDLJava)
    http://www.libsdl.org/pro­jects/SDL_ttf/
  36. SDL_ttf doc
    http://www.libsdl.org/pro­jects/SDL_ttf/docs/SDL_ttf_fra­me.html
  37. SDL 1.2 Documentation: SDL_Surface
    http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlsurface.html
  38. SDL 1.2 Documentation: SDL_PixelFormat
    http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlpixelformat.html
  39. SDL 1.2 Documentation: SDL_LockSurface
    http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdllocksurface.html
  40. SDL 1.2 Documentation: SDL_UnlockSurface
    http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlunlocksurface.html
  41. SDL 1.2 Documentation: SDL_LoadBMP
    http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlloadbmp.html
  42. SDL 1.2 Documentation: SDL_SaveBMP
    http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlsavebmp.html
  43. SDL 1.2 Documentation: SDL_BlitSurface
    http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlblitsurface.html
  44. SDL 1.2 Documentation: SDL_VideoInfo
    http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlvideoinfo.html
  45. SDL 1.2 Documentation: SDL_GetVideoInfo
    http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlgetvideoinfo.html
  46. glDrawArrays
    http://www.opengl.org/sdk/doc­s/man4/xhtml/glDrawArrays­.xml
  47. glDrawElements
    http://www.opengl.org/sdk/doc­s/man4/xhtml/glDrawElemen­ts.xml
  48. glDrawArraysInstanced
    http://www.opengl.org/sdk/doc­s/man4/xhtml/glDrawArraysIn­stanced.xml
  49. glDrawElementsInstanced
    http://www.opengl.org/sdk/doc­s/man4/xhtml/glDrawElemen­tsInstanced.xml
  50. Root.cz: Seriál Grafická knihovna OpenGL
    http://www.root.cz/serialy/graficka-knihovna-opengl/
  51. 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/
  52. Best Practices for Working with Vertex Data
    https://developer.apple.com/li­brary/ios/documentation/3ddra­wing/conceptual/opengles_pro­grammingguide/Techniquesfor­WorkingwithVertexData/Techni­quesforWorkingwithVertexDa­ta.html
  53. Class BufferStrategy
    http://docs.oracle.com/ja­vase/6/docs/api/java/awt/i­mage/BufferStrategy.html
  54. Class Graphics
    http://docs.oracle.com/ja­vase/1.5.0/docs/api/java/aw­t/Graphics.html
  55. Double Buffering and Page Flipping
    http://docs.oracle.com/ja­vase/tutorial/extra/fullscre­en/doublebuf.html
  56. BufferStrategy and BufferCapabilities
    http://docs.oracle.com/ja­vase/tutorial/extra/fullscre­en/bufferstrategy.html
  57. Java:Tutorials:Double Buffering
    http://content.gpwiki.org/in­dex.php/Java:Tutorials:Dou­ble_Buffering
  58. Double buffer in standard Java AWT
    http://www.codeproject.com/Ar­ticles/2136/Double-buffer-in-standard-Java-AWT
  59. Java 2D: Hardware Accelerating – Part 1 – Volatile Images
    http://www.javalobby.org/fo­rums/thread.jspa?threadID=16840&tstar­t=0
  60. Java 2D: Hardware Accelerating – Part 2 – Buffer Strategies
    http://www.javalobby.org/ja­va/forums/t16867.html
  61. How does paintComponent work?
    http://stackoverflow.com/qu­estions/15544549/how-does-paintcomponent-work
  62. A Swing Architecture Overview
    http://www.oracle.com/technet­work/java/architecture-142923.html
  63. Class javax.swing.JComponent
    http://docs.oracle.com/ja­vase/6/docs/api/javax/swin­g/JComponent.html
  64. Class java.awt.Component
    http://docs.oracle.com/ja­vase/6/docs/api/java/awt/Com­ponent.html
  65. Class java.awt.Component.BltBufferStrategy
    http://docs.oracle.com/ja­vase/6/docs/api/java/awt/Com­ponent.BltBufferStrategy.html
  66. Class java.awt.Component.FlipBufferStrategy
    http://docs.oracle.com/ja­vase/6/docs/api/java/awt/Com­ponent.FlipBufferStrategy­.html
  67. Metoda java.awt.Component.isDoubleBuffered()
    http://docs.oracle.com/ja­vase/6/docs/api/java/awt/Com­ponent.html#isDoubleBuffe­red()
  68. Metoda javax.swing.JComponent.is­DoubleBuffered()
    http://docs.oracle.com/ja­vase/6/docs/api/javax/swin­g/JComponent.html#isDouble­Buffered()
  69. Metoda javax.swing.JComponent.set­DoubleBuffered()
    http://docs.oracle.com/ja­vase/6/docs/api/javax/swin­g/JComponent.html#setDouble­Buffered(boolean)
  70. Javadoc – třída GraphicsDevice
    http://docs.oracle.com/ja­vase/7/docs/api/java/awt/Grap­hicsDevice.html
  71. Javadoc – třída GraphicsEnvironment
    http://docs.oracle.com/ja­vase/7/docs/api/java/awt/Grap­hicsEnvironment.html
  72. Javadoc – třída GraphicsConfiguration
    http://docs.oracle.com/ja­vase/7/docs/api/java/awt/Grap­hicsConfiguration.html
  73. Javadoc – třída DisplayMode
    http://docs.oracle.com/ja­vase/7/docs/api/java/awt/Dis­playMode.html
  74. Lesson: Full-Screen Exclusive Mode API
    http://docs.oracle.com/ja­vase/tutorial/extra/fullscre­en/
  75. Full-Screen Exclusive Mode
    http://docs.oracle.com/ja­vase/tutorial/extra/fullscre­en/exclusivemode.html
  76. Display Mode
    http://docs.oracle.com/ja­vase/tutorial/extra/fullscre­en/displaymode.html
  77. Using the Full-Screen Exclusive Mode API in Java
    http://www.developer.com/ja­va/other/article.php/3609776/U­sing-the-Full-Screen-Exclusive-Mode-API-in-Java.htm
  78. Java quick guide: JVM Instruction Set (tabulka všech instrukcí JVM)
    http://www.mobilefish.com/tu­torials/java/java_quickgu­ide_jvm_instruction_set.html
  79. The JVM Instruction Set
    http://mpdeboer.home.xs4a­ll.nl/scriptie/node14.html
  80. MultiMedia eXtensions
    http://softpixel.com/~cwrig­ht/programming/simd/mmx.phpi
  81. SSE (Streaming SIMD Extentions)
    http://www.songho.ca/misc/sse/sse­.html
  82. Timothy A. Chagnon: SSE and SSE2
    http://www.cs.drexel.edu/~tc365/mpi-wht/sse.pdf
  83. Intel corporation: Extending the Worldr's Most Popular Processor Architecture
    http://download.intel.com/techno­logy/architecture/new-instructions-paper.pdf
  84. SIMD architectures:
    http://arstechnica.com/ol­d/content/2000/03/simd.ar­s/
  85. GC safe-point (or safepoint) and safe-region
    http://xiao-feng.blogspot.cz/2008/01/gc-safe-point-and-safe-region.html
  86. Safepoints in HotSpot JVM
    http://blog.ragozin.info/2012/10/sa­fepoints-in-hotspot-jvm.html
  87. Java theory and practice: Synchronization optimizations in Mustang
    http://www.ibm.com/develo­perworks/java/library/j-jtp10185/
  88. How to build hsdis
    http://hg.openjdk.java.net/jdk7/hot­spot/hotspot/file/tip/src/sha­re/tools/hsdis/README
  89. Java SE 6 Performance White Paper
    http://www.oracle.com/technet­work/java/6-performance-137236.html
  90. Lukas Stadler's Blog
    http://classparser.blogspot­.cz/2010/03/hsdis-i386dll.html
  91. How to build hsdis-amd64.dll and hsdis-i386.dll on Windows
    http://dropzone.nfshost.com/hsdis.htm
  92. PrintAssembly
    https://wikis.oracle.com/dis­play/HotSpotInternals/Prin­tAssembly
  93. The Java Virtual Machine Specification: 3.14. Synchronization
    http://docs.oracle.com/ja­vase/specs/jvms/se7/html/jvms-3.html#jvms-3.14
  94. The Java Virtual Machine Specification: 8.3.1.4. volatile Fields
    http://docs.oracle.com/ja­vase/specs/jls/se7/html/jls-8.html#jls-8.3.1.4
  95. The Java Virtual Machine Specification: 17.4. Memory Model
    http://docs.oracle.com/ja­vase/specs/jls/se7/html/jls-17.html#jls-17.4
  96. The Java Virtual Machine Specification: 17.7. Non-atomic Treatment of double and long
    http://docs.oracle.com/ja­vase/specs/jls/se7/html/jls-17.html#jls-17.7
  97. Open Source ByteCode Libraries in Java
    http://java-source.net/open-source/bytecode-libraries
  98. ASM Home page
    http://asm.ow2.org/
  99. Seznam nástrojů využívajících projekt ASM
    http://asm.ow2.org/users.html
  100. ObjectWeb ASM (Wikipedia)
    http://en.wikipedia.org/wi­ki/ObjectWeb_ASM
  101. Java Bytecode BCEL vs ASM
    http://james.onegoodcooki­e.com/2005/10/26/java-bytecode-bcel-vs-asm/
  102. BCEL Home page
    http://commons.apache.org/bcel/
  103. Byte Code Engineering Library (před verzí 5.0)
    http://bcel.sourceforge.net/
  104. Byte Code Engineering Library (verze >= 5.0)
    http://commons.apache.org/pro­per/commons-bcel/
  105. BCEL Manual
    http://commons.apache.org/bcel/ma­nual.html
  106. Byte Code Engineering Library (Wikipedia)
    http://en.wikipedia.org/wiki/BCEL
  107. BCEL Tutorial
    http://www.smfsupport.com/sup­port/java/bcel-tutorial!/
  108. Bytecode Engineering
    http://book.chinaunix.net/spe­cial/ebook/Core_Java2_Volu­me2AF/0131118269/ch13lev1sec6­.html
  109. Bytecode Outline plugin for Eclipse (screenshoty + info)
    http://asm.ow2.org/eclipse/index.html
  110. Javassist
    http://www.jboss.org/javassist/
  111. Byteman
    http://www.jboss.org/byteman
  112. Java programming dynamics, Part 7: Bytecode engineering with BCEL
    http://www.ibm.com/develo­perworks/java/library/j-dyn0414/
  113. The JavaTM Virtual Machine Specification, Second Edition
    http://java.sun.com/docs/bo­oks/jvms/second_edition/html/VMSpec­TOC.doc.html
  114. The class File Format
    http://java.sun.com/docs/bo­oks/jvms/second_edition/html/Clas­sFile.doc.html
  115. javap – The Java Class File Disassembler
    http://docs.oracle.com/ja­vase/1.4.2/docs/tooldocs/win­dows/javap.html
  116. javap-java-1.6.0-openjdk(1) – Linux man page
    http://linux.die.net/man/1/javap-java-1.6.0-openjdk
  117. Using javap
    http://www.idevelopment.in­fo/data/Programming/java/mis­cellaneous_java/Using_javap­.html
  118. Examine class files with the javap command
    http://www.techrepublic.com/ar­ticle/examine-class-files-with-the-javap-command/5815354
  119. aspectj (Eclipse)
    http://www.eclipse.org/aspectj/
  120. Aspect-oriented programming (Wikipedia)
    http://en.wikipedia.org/wi­ki/Aspect_oriented_program­ming
  121. AspectJ (Wikipedia)
    http://en.wikipedia.org/wiki/AspectJ
  122. EMMA: a free Java code coverage tool
    http://emma.sourceforge.net/
  123. Cobertura
    http://cobertura.sourceforge.net/
  124. jclasslib bytecode viewer
    http://www.ej-technologies.com/products/jclas­slib/overview.html
Našli jste v článku chybu?
DigiZone.cz: Světový pohár v přímém přenosu na ČT

Světový pohár v přímém přenosu na ČT

Podnikatel.cz: Letáky? Lidi zuří, ale ony stále fungují

Letáky? Lidi zuří, ale ony stále fungují

Lupa.cz: Proč jsou firemní počítače pomalé?

Proč jsou firemní počítače pomalé?

Vitalia.cz: Tesco nabízí desítky tun jídla zdarma

Tesco nabízí desítky tun jídla zdarma

Vitalia.cz: V Kauflandu už začaly Vánoce

V Kauflandu už začaly Vánoce

DigiZone.cz: Digi Slovakia zařazuje stanice SPI

Digi Slovakia zařazuje stanice SPI

Vitalia.cz: dTest odhalil ten nejlepší kečup

dTest odhalil ten nejlepší kečup

Root.cz: Hořící telefon Samsung Note 7 zapálil auto

Hořící telefon Samsung Note 7 zapálil auto

Vitalia.cz: Pryč se zastaralým stravováním ve školách

Pryč se zastaralým stravováním ve školách

Podnikatel.cz: ČSSZ posílá přehled o důchodovém kontě

ČSSZ posílá přehled o důchodovém kontě

Vitalia.cz: Tohle jsou nejlepší česká piva podle odborníků

Tohle jsou nejlepší česká piva podle odborníků

DigiZone.cz: Wimbledon na Nova Sport až do 2019

Wimbledon na Nova Sport až do 2019

Měšec.cz: TEST: Vyzkoušeli jsme pražské taxikáře

TEST: Vyzkoušeli jsme pražské taxikáře

Vitalia.cz: Jsou vegani a vyrábějí nemléko

Jsou vegani a vyrábějí nemléko

Vitalia.cz: Fyzioterapeutka: Chůze naboso? Rozhodně ano!

Fyzioterapeutka: Chůze naboso? Rozhodně ano!

DigiZone.cz: Parlamentní listy: kde končí PR...

Parlamentní listy: kde končí PR...

Vitalia.cz: Tradiční čínská medicína a rakovina

Tradiční čínská medicína a rakovina

Lupa.cz: Cimrman má hry na YouTube i vlastní doodle

Cimrman má hry na YouTube i vlastní doodle

120na80.cz: Co je padesátkrát sladší než cukr?

Co je padesátkrát sladší než cukr?

Vitalia.cz: Jak Ondra o astma přišel

Jak Ondra o astma přišel