Z Wikipedie: Demoscéna je fenomén na pomezí digitálního umění, kreativní tvorby a undergroundu, které vzniká, je určeno a prezentováno na počítačích.
Dema jsou programy, které demonstrují schopnosti počítače a schopnosti programátora. Vznikají pro všechny platformy, které dokáží jakýmkoliv způsobem zobrazovat informaci a vydávat zvuk. Demoscéna jak jí známe dnes se vyvinula v 80. letech, kdy crackeři odstraňující ochrany her a programů přidávali do pirátské distribuce i svoje vlastní výtvory, FILE_ID.DIZ a *.NFO soubory. Většinou to byly krátké prográmky s logem, skrolujícím textem zdravice a hrající zvuk a soubory FILE_ID.DIZ a *.NFO obsahovaly informace vyšperkovanou bohatou ASCII grafikou.
Časem se demoscéna transformovala do samostatného IT podoboru, kdy jeden člověk nebo skupina lidí (grupa) se snaží vytvořit dílo, které pak může prezentovat komunitě, např. v rámci demo-party/compa. Hodnotí se hudba, grafika a kód. Přičemž na vše mohou být kladena různá omezení buď daná platformou nebo pravidly kategorie, např. velikost binárky, počet barev v paletě obrazu a mnoho dalších.
Ta nejlepší dema, pak musí být originální, nevídaná a musí předvádět něco neuvěřitelného nebo aspoň vtipného. Odborník žasne: opravdu je možné hrát hudbu a generovat průlet tunelem v 256 bytech!? Jak může holá 8086 v reálném čase generovat takový 3D effect!? Polyfonní symfonie reprodukovaná čtecí hlavičkou disketové mechaniky!? Tohle není možný!
Laik se diví a pokud zeptá: no a? Pak je jasné, že tomu houby rozumí a ocenit produkty demoscény nedokáže.
Jaké kategorie demoscény nějak souvisí s Linuxem?
Patří k nejstaršímu druhu moderního digitálního umění (zdůrazňuji moderního, protože když se začnete nořit dále do minulosti, narazíte na tkalcovské stavy programovatelné děrnými štítky vynalezené na přelomu 18. a 19. století Francouzem J. M. Jacquardem a možná i něco staršího). Vyvinulo se v době, kdy se uživatelé k počítačovému systému připojovali přes videoterminály a dálnopisy. Obrázky jsou složené z alfanumerických znaků 7bitové normy ASCII kde prvních 31 kódů tvoří řídící znaky, jako odřádkování, tabulátor nebo zvonek. A kódy 32 (mezera) až 127 pak patří znakům anglické abecedy, číslům a symbolům:
Vygenerovat všechny ASCII znaky v shellu:
for i in {32..127}; do printf \\$(printf "%o" $i); done
První ASCII Art vznikal už v 60. letech pro dálnopisy, ale tehdy se mu ještě říkalo RTTY Art, podle zkratky Radio Teletype a pro úplnost dodejme že RTTY používá jen 5bitový kód Mezinárodní telegrafní abecedy, který dovoluje zobrazit jen velká písmena, číslice a několik symbolů.
Na následující stránce z radioamatérského časopisu je slečna Deborah v rubrice RTTY Artwork of the month:
Obrázek má 10452 symbolů a při rychlosti běžně používané radioamatéry na radiovém dálnopisu 45 Baudů, by se přenášel skoro 4 minuty.
Prohlížením ASCII Artu není v GNU/Linuxu žádný problém, stačí příkazy cat, less nebo more v shellu ve vašem oblíbeném terminálovém emulátoru. Použitý font musí být neproporcionální, tedy takový kde každý znak včetně mezery je stejně široký.
Barevný text s bohatší množinou znaků je ANSI Art. Největšího rozmachu dosáhl v době telefonních BBSek a MS-DOSu (nutno zaplácnout paměť ovladačem ANSI.SYS). ANSI obsahuje sekvence, které definují barvu znaku, pozadí, blikání, podtržený nebo tučný text a další. Na původním IBM PC je kód pro zobrazení znaků 8bitový, prvních 127 znaků a řídících kódů je shodných s ASCII a přibylo dalších 127 znaků obsahující matematické symboly a semigrafiku. Kód je definován jako CP-437.
Běžný linuxový terminál dokáže ANSI sekvence správně vyhodnotit, takže veškerý barevný výstup na obrazovce máme díky nim. Trošku složitější je to s původní CP-437, ale protože všechny její znaky jsou obsaženy v Unicode, není problém výstup překódovat.
Znaky CP-437 zobrazené jako UTF-8, pokud se některý znak zobrazí jako čtvereček s kódem, potom znak chybí ve vašem fontu:
for i in {32..255};do ((i%16==0))&&echo;printf "\\$(printf "%o" $i) ";done|iconv -f cp437 -t utf8
A teď k tomu, jak ANSI Art zobrazit, některý už je přímo v UTF-8:
curl -L http://git.io/unix
Po provedení příkazu se zobrazí pan Denis Ritchie, ANSI text byl převeden do HTML pomocí ansi2html.sh.
,_ ,_==▄▂ , ▂▃▄▄▅▅▅▂▅¾. / / ▄▆<´ "»▓▓▓%\ / / / / ,▅7" ´>▓▓▓% / / > / >/% ▐¶▓ ,»▓▓¾´ /> %/%// / / ▓▃▅▅▅▃,,▄▅▅▅Æ\// ///>// />/ / V║«¼.;→ ║<«.,`=// />//%/% / / //╠<´ -²,)(▓~"-╝/¾/ %/>/ /> / / / ▐% -./▄▃▄▅▐, /7//;//% / / / ////`▌▐ %zWv xX▓▇▌//&;% / / / / / %//%/¾½´▌▃▄▄▄▄▃▃▐¶\/& / </ /</%//`▓!%▓%╣WY<Y)y&/`\ / / %/%//</%//\i7; ╠N>)VY>7; \_ UNIX IS VERY SIMPLE IT JUST NEEDS A / /</ //<///<_/%\▓ V%W%£)XY _/%‾\_, GENIUS TO UNDERSTAND ITS SIMPLICITY / / //%/_,=--^/%/%%\¾%¶%%} /%%%%%%;\, %/< /_/ %%%%%;X%%\%%;, _/%%%;, \ / / %%%%%%;, \%%l%%;// _/%;, dmr / %%%;, <;\-=-/ / ;, l
Pokud ANSI obrázek v CP-437, použijte výše zmíněný příkaz iconv -f cp437 -t utf8 a pokud má víc řádků než váš terminál přesměřujte výstup do less -r (volba -r sdělí less, že mé vyhodnocení escape-sekvencí nechat na terminálu). Pozor u některých starších obrázků se předpokládá, že šířka terminálu je přesně 80 znaků, takže si autor nedělal starosti s odřádkováním. Možná víte, že je možné vynutit počet znaků na řádek příkazem stty cols 80 a nastavit příznak terminálu setterm --linewrap on, ale tahle varianta nefunguje, tak jsem jsem předpokládal a pro nejlepší zobrazení je nutné změnit velikost terminálu (to lze také escape-sekvencí: echo -ne "\033[8;32;80t", zde pro velikost 80×32).
Některé login screeny z BBSek využívají možnosti adresovat řádky a sloupce na obrazovce a protože se uživatel připojoval rychlostí např. 1200 Bd, tak se mohl při načítání kochat jednoduchou animací.
Několik příkladů:
curl -L http://artscene.textfiles.com/ansi/scene/am-ice.ans | iconv -f cp437 -t utf-8 curl -L http://artscene.textfiles.com/ansi/scene/bigtime3.ans | iconv -f cp437 -t utf-8 curl -L http://artscene.textfiles.com/ansi/scene/fconfigs.ans | iconv -f cp437 -t utf-8
Komplexnější příklad se změnou velikosti terminálu a vylepšením zobrazení odstraněním atributu pozadí na konci řádku:
# Nastav velikost 80x32 echo -ne "\033[8;32;80t" # Stáhni a ulož soubor curl -L http://web.archive.org/web/20140407205152/http://sixteencolors.net/pack/blocktronics_blockalypse/rad-LOVE.ANS/download > rad-LOVE.ANS # Zobraz a nahraď konce řádků resetem pozadí iconv -f cp437 -t utf8 rad-LOVE.ANS | sed -e "s/$/\x1b[49m/"
Skript zobrazující 7 základních barev pomocí znaků ░▒▓█, často používaných v různých kombinacích barev popředí a pozadí pro vyvolání dojmu většího počtu barev:
for i in {0..7} do for j in 91 92 93 88 do echo -en "\E[3${i}m\u25$j" done done
Existuje zlotřilá sekvence může dokonce rozbít váš terminál:) Je to printf "\e(0", která zapíná příznak pro zobrazení semigrafiky. Nejčastěji na ní narazíte pokud omylem vypíšete velký binární soubor do stdout, kde je vysoká pravděpodobnost výskytu sekvence 0x1b 0x28 0x30. V takovém případě pomůže příkaz reset nebo tput sgr0. Mnohdy je nutné napsat poslepu nebo vyvolat v GUI terminálového emulátoru.
Největší webový portál pro democénu je pouët.net, v seznamu se nachází přibližně 90 platforem (moje nejoblíbenější jsou ZX Spectrum, MS-DOS, Linux) a 36 kategorií. Ty nejdůležitější kategorie jsou podle mě 256 Bytů, 1k, 4k, 64k a demo. Ovšem velikost začíná už na 32 Bytech.
Co se týče velikosti spustitelného souboru je GNU/Linux v nevýhodě. Pod MS-DOSem stačí říct kompilátoru nahraj instrukce od offsetu 100h a jede se, nahození grafického režimu obstará několik instrukcí volající službu BIOSu a pak už je vše jen o znalosti různých fint, délek instrukcí, atd.
V Linuxu musí být binární soubor ve formátu ELF (Executable and Linkable Format), ten obsahuje spoustu dat, hlaviček, údajů o architektuře, identifikaci kompilátoru a spoustu dalších zbytečností. Navíc operační systém pečlivě hlídá jak přistupujete ke zdrojům systému, takže přímý přístup k hardwaru nebo paměti obstarává linuxový kernel a tak je nutné nejdříve volat funkce operačního systému nebo různých nadřazených knihoven, které pak umožní provést požadovanou operaci.
Ve článku A Whirlwind Tutorial on Creating Really Teensy ELF Executables for Linux je popsáno jak autor zmenšoval spustitelný soubor minimálního C programu: int main(void) { return 42; }, který po kompilaci gcc měl 3998 Bytů a postupně přes zkoušení různých voleb překladače, přepsání kódu do assembleru a vytvoření vlastní ELF hlavičky (kdy ponechal jen opravdu to nejnutnější) se dostal až na 45 Bytů.
V tutoriálu 4k Intros in Linux jsou rozebrány další finty jak vytvořit malou binárku, je zde zmíněná sada utilit ELFKickers (github) pro úpravu ELF souborů, jak obejít automatické přidávání kódu v gcc a další.
Takže u nejmenších kategorií už místa moc nezbývá a na Pouetu jsou v kategorii 128B pouze 3 linuxová dema.
Situace o v kategorii 256B není o moc lepší. Demo AT LEAST IT AIN'T NO XOR TEXTURE? (NoXor), je shellový skript, který po spuštění rozpakuje binární data obsahující C zdroják, ten pošle do C kompilátoru, pak spustí výsledný ELF soubor (ten už má něco přes 8 kilo). Ovšem na rozdíl od předchozích výtvorů alespoň funguje bez zbytečného laborování:)
Tohle je docela častá finta i v rozměrnějších kategoriích, demo obsahuje C zdroják, pokud se spouští v grafickém okně, pak většinou pomocí SDL knihovny a nebo starší knihovny GLUT. Většina linuxových distribucí obsahuje obojí a protože se demy baví programátoři, tak se předpokládá že v systému jsou i hlavičkové soubory. Zdroják většinou vypadá jak ze soutěže o nejstřelenější C kód (The International Obfuscated C Code Contest) a pokud je potřeba srazit velikost, pak se prostě vytvoří samorozbalovací gzip nebo bzip2 (tj. shell skript který má od určité pozice uložená binární zabalená data, ty si sám ze sebe vyseparuje, rozbalí a dále nějak zpracuje). A protože i skriptovací jazyky dnes běží docela svižně, spousta dem je vytvořená i v nich.
Pro old-schoolové demokonzumenty/autory to jistě zavání lamerstvím, ovšem pro ty je lamerstvím i používání funkcí OpenGL, různých shaderů a dalších knihovních funkcí, které řeší spoustu práce za programátora.
U větších dem si tvůrci mohou dovolit přidat spoustu grafiky, hudby, složitých 3D objektů a textur navíc, mohou využít multiplatformních knihoven a demo pak běží nejen na Linuxu. U těch rozměrově malých si ale musí vystačit s nejrůznějšími algoritmy pro generování textur, umísťování objektů i procedurání generování hudby a zvuku. Některé z těchto efektů už jsou dost provařené, např. XOR textura:
for((y=0;y<$[LINES-1];y++));do for((x=0;x<=$COLUMNS;x++));do
printf "\e[${y};${x}f\e[38;5;$[232+(x^y)%24]m\u2588";done;done
Tahle verze využívá doplňkové ANSI sekvence pro zobrazení 256 barev \e[38;5;XXXm a musí se zkopírovat do shellu, protože proměnné $COLUMNS a $LINES nejsou nastavené, pokud se kód zavolá jako skript. V následující upravené verze se pomocí tput zjistí velikost vašeho terminálu, také jsou použity jen základní ANSI sekvence barev a vykreslování na obrazovku je postupné:
clear
W=$(tput cols)
H=$(tput lines)
for((i = 0;;i++)) do
for((y = 0; y < $[H-1]; y++))
do
c=$[31+i%7]
for((x = 0; x < $W; x++))
do
(( (x ^ y) == $i )) && echo -en "\e[${y};${x}f\e[${c}mX"
done
done
sleep 0.25
done
Podobně jako procedurální generování textur, je možné procedurálně generovat i zvuky. Přiznám se, že při hraní her a koukání na dema, jsem o tom, odkud se zvuky berou příliš neuvažoval, říkal jsem si, že v paměti programu jsou uloženy samply a ty se pak sypou do zvukovky a není kolem toho potřeba dělat velkou vědu.
Že tomu tak není vždy mi ukázal až následující C kód (song.c), jehož autorem je finský demo-programátor viznut:
#include <math.h> main(v,i,z,n,u,t){for(v=-1;;)for(n=pow(1.06,"`cW`g[`cgcg[eYcb^bV^eW^be^bVecb^"[++v&31]+(v&64)/21), i=999;i;putchar(128+((8191&u)>i?0:i/8)-((8191&(z+=n))*i-->>16)))u+=v&8?t/2:(t=v&64?t:n/4);}
Stáhněte soubor a zkompilujte pomocí: gcc -lm -o song song.c. Možnosti jak přehrát výstup je několik, ve většině případů je zvuková karta v defaultním nastavení: unsigned 8bit (u8), 8000 Hz, jinak je nastavení dáno přepínači:
O tom, jak předchozí generátor hudby funguje pojednává přednáška Making music with a C compiler (SIGINT13). Finta spočívá v tom, že pokud v nekonečném cyklu posíláte binární data o hodnotě 0, 1, 2,… 255 na vstup zvukovky posíláte tam vlastně pilovitý signál o frekvenci 8000/256 = 31,25 Hz. Pokud se vám nechce kompilovat C kód z přednášky zkuste následující one-liner pro Python 3:
export PYTHONIOENCODING=latin1 python3 -c "while 1:locals().setdefault('t',0);print(chr(t%255),end='');t+=1;" | aplay
Původně jsem si myslel, že to vysmrknu v shellu:
for((t=0;;t++)); do printf \\$(printf "%o" $[t % 255] ) ; done | aplay
Ale ukázalo se, že bash negeneruje data na stdout dostatečně rychle a plynule, protože data se ukládají do bufferu a ten se pak vyplivne do roury, znovu začne plnit atd. Existuje příkaz stdbuf, ale ten mi v tom případě nepomohl (můžete si ale výstup uložit do souboru a ten pak s cat přesměrovat do aplay).
Takže jsem sáhl po svém druhém oblíbeném skriptovacím jazyku. I tady se ukázalo, že to nebude tak úplně bez problémů. Nejprve jsem narazil na zápis cyklu s proměnnou jako one-liner, kde je potřeba proměnou definovat pomocí locals().setdefault('t',0). Poté jsem se zaposlouchal do signálu a ten nezněl úplně přesně jako s putchar, až porovnání dat která z toho lezou, jsem zjistil, že print() převede 8bitový bajt z chr() automaticky z latin1 do utf-8, takže na výstupu toho bylo víc, tady pomůže nastavit proměnnou prostředí export PYTHONIOENCODING=latin1.
Pokud provedete t*2 nebo t<<1 frekvence se zdvojnásobí, nebo je signál je možné modulovat např. logický součin s 42 (101010) a dále kombinovat s AND, OR, bit. posuvy a matematickými operacemi.
python3 -c "while 1:locals().setdefault('t',0);print(chr(t & 42 ),end='');t+=1;" | aplay
Kombinací bitových operací pak vznikají melodie, některé dokonce více čí méně poslouchatelné, ovšem správně retro-elektronicky znějící a díky dlouhé periodě i s překvapujícími rify, které se objeví až po delším poslechu:
python3 -c "while 1:locals().setdefault('t',0);print(chr((t*((t>>12|t>>8)&63&t>>4))%255),end='');t+=1;"|aplay python3 -c "while 1:locals().setdefault('t',0);print(chr(((t*9&t>>4)|(t*5&t>>7)|(t*3&t//0x400)-1)%255),end='');t+=1;"|aplay python3 -c "while 1:locals().setdefault('t',0);print(chr(((t//10**7*t*t+t)%127|t>>4|t>>5|t%127+(t>>16)|t)%255),end='');t+=1"|aplay python3 -c "while 1:locals().setdefault('t',0);print(chr(()%255),end='');t+=1"|aplay python3 -c "while 1:locals().setdefault('t',0);print(chr((((t*(t>>8|t>>9)&46&t>>8))^(t&t>>13|t<<6))%255),end='');t+=1"|aplay python3 -c "while 1:locals().setdefault('t',0);print(chr(((t*(t>>5|t>>8))>>(t>>16))&255),end='');t+=1"|aplay python3 -c "while 1:locals().setdefault('t',0);print(chr(((((t>>12)^(t>>12)-2)%11*t//4)&255)),end='');t+=1"|aplay -r 44100
A tady je příklad kdy pole znaků '36364689' tvoří notový zápis melodie, které se pak opakuje při měnícím se signálu v různých oktávách (vzorkovací kmitočet 44.1 kHz):
python3 -c "while 1:locals().setdefault('t',0);print(chr((((t*((ord('36364689'[t>>13&7])&15))) //12&128)+(((t>>12)^(t>>12)-2)%11*t//4|t>>13)&127)),end='');t+=1"|aplay -r 44100
Pokud vás procedurální generování hudby bitovými operacemi nadchlo stejně jako mě, doporučuji následující linky, kromě trochy teorie, už vygenerovaných dat (i s vizualizacemi) na nich najdete další příklady od různých autorů.