Java 2D gyors vektorgrafika
Sziasztok!
Egy grafikai API fejlesztésébe kezdtem bele, melyet egy többplatformos (mobil, applet, asztali) kottagrafikai szerkesztőhöz szeretnék használni. Elsősorban kottagrafikai kalkulációkért és a kirajzolás ütemezéséért felelne ez a library. A lényeg, hogy minimális beállítással egy komponenst lehessen létrehozni, amely a hozzá tartozó canvason megjeleníti a kottaképet. Ez a kottakép természetesen interakcióra képes, az interakció módja viszont már platformfüggő.
A kottagrafikában jártas vagyok, magam is sokat kottázom, ezért is mertem belefogni. A Java sem új a számomra. A problémám az, hogy a Java 2D lehetőségeire nincs elég rálátásom.
Alapvetően Androidra terveztem a szerkesztőt, és elsősorban a memóriával akartam spórolni. Később úgy adódott, hogy mégis a segességet helyeztem előtérbe a tervezésnél. Utóbbinak megfelelően minden soronkénti zenei ütem külön bitmapre került volna, majd a bitmapek megfelelő összeillesztéséért egy külön komponens felelt volna.
Ez azonban több problémát is felvetett. Például nem foglal-e túl sok helyet a memóriában a sok bitmap? Nagyításnál/szerkesztésnél így is folyamatos újrarenderelés szükséges, nem válik-e fölöslegessé az előrenderelés? És nem is minden osztható el ezekbe a bitmapekbe (átkötések, sorváltó szólamok)... Végül úgy kezdtem neki, hogy nincs előrenderelés, minden egyszerre rajzolódik ki a draw eseményre. Hamar kiderült viszont, hogy így nagyon lassúvá válik, ha sűrű a kotta.
Kérdezem tehát az ilyesmiben tapasztaltabbakat, hogy Jávában nagyszámú vektoros grafika kirajzolásánál nagyjából milyen elveket érdemes követni. Fontos még, hogy minél általánosabb legyen a megoldás, mert a platformok közötti eltérések miatt magát a grafikát absztraktan szeretném megoldani (tehát, hogy a canvas objektum és a grafikai könyvtár cserélhető legyen).
Üdv,
Süsü
■ Egy grafikai API fejlesztésébe kezdtem bele, melyet egy többplatformos (mobil, applet, asztali) kottagrafikai szerkesztőhöz szeretnék használni. Elsősorban kottagrafikai kalkulációkért és a kirajzolás ütemezéséért felelne ez a library. A lényeg, hogy minimális beállítással egy komponenst lehessen létrehozni, amely a hozzá tartozó canvason megjeleníti a kottaképet. Ez a kottakép természetesen interakcióra képes, az interakció módja viszont már platformfüggő.
A kottagrafikában jártas vagyok, magam is sokat kottázom, ezért is mertem belefogni. A Java sem új a számomra. A problémám az, hogy a Java 2D lehetőségeire nincs elég rálátásom.
Alapvetően Androidra terveztem a szerkesztőt, és elsősorban a memóriával akartam spórolni. Később úgy adódott, hogy mégis a segességet helyeztem előtérbe a tervezésnél. Utóbbinak megfelelően minden soronkénti zenei ütem külön bitmapre került volna, majd a bitmapek megfelelő összeillesztéséért egy külön komponens felelt volna.
Ez azonban több problémát is felvetett. Például nem foglal-e túl sok helyet a memóriában a sok bitmap? Nagyításnál/szerkesztésnél így is folyamatos újrarenderelés szükséges, nem válik-e fölöslegessé az előrenderelés? És nem is minden osztható el ezekbe a bitmapekbe (átkötések, sorváltó szólamok)... Végül úgy kezdtem neki, hogy nincs előrenderelés, minden egyszerre rajzolódik ki a draw eseményre. Hamar kiderült viszont, hogy így nagyon lassúvá válik, ha sűrű a kotta.
Kérdezem tehát az ilyesmiben tapasztaltabbakat, hogy Jávában nagyszámú vektoros grafika kirajzolásánál nagyjából milyen elveket érdemes követni. Fontos még, hogy minél általánosabb legyen a megoldás, mert a platformok közötti eltérések miatt magát a grafikát absztraktan szeretném megoldani (tehát, hogy a canvas objektum és a grafikai könyvtár cserélhető legyen).
Üdv,
Süsü
Mit jelent az egyszerre?
Ha a memóriafelhasználás számít, akkor az aktuálisan nem megjelenített képet ne rajzold ki; ha a sebesség számít, akkor gyorstárazz, amit csak lehetséges, akár előre is.
Alap dolog, de ne hozz létre minden lejegyzést külön, csak egyet, amit minden helyen megjelenítesz.
Használsz profilert?
Betöltés
Az eredeti elkézelésem ez volt:
Van a teljes kottarendszer (ScoreSystem), vannak ütemek (Bar), és az ütemeken belül soronként sorütemek (LineBar). Leegyszerűsítve, temészetesen. A Bar-ok szekvenciálisan követik egymást a ScoreSystem-en belül, ennek lapozásához elérhető egy kétirányú ScoreSystemBarIterator. Minden Bar-on belül a BarLine-onként (valójában szólamonként, de most ne bonyolítsuk) független LineBarTimedObjectIterator-okat pörgetjük, keressük, hogy melyik sorban van az időben következő objektum, illetve melyek az azzal egyazon időben lévő más objektumok. Erre már van egy algoritmus, ami Bar-onként számolja a pozíciókat, de BarLine-onként készíti a bitmapet (azt is csak akkor, ha végzett a kalkulációkkal, és a bitmap a látható részbe kerülne). Ezt sebességileg viszonylag jó megoldásnak gondoltam, de úgy, hogy a Java bitmap-lehetőségeivel nem volt jelentősebb tapasztalatom. Az ilyen bitmapes megoldás fő hátrányait a nyitó postban felsoroltam. Úgy megérzés szintjén sem hiszem, hogy ez a jó megoldás. Ekkor fölmerültek az első konkrét kérdések a grafikát illetően. A többszálúság kihasználása valószínűleg sokat lendítene a dolgon. De hogyan? Mivel kell egyáltalán spórolni? Ha betöltök a memóriába pl. 30 db 120x100-as bitmapet, az már sok (pl. mobilon)? Ha sokminden egy bitmapre kerül, akkor egy apró módosítás is a bitmap újrarenderelését követeli, mit érdemes tehát egybetenni? És így tovább.
De térjünk vissza a megoldás útjára.
Ez alatt azt érted, hogy pl. egy-egy kottafejet, kulcsot stb. rendereljek előre, majd Flyweight-szerűen osszam meg? Igen, lehet, hogy ezt így kell csinálni. Kérdés (és épp ez az, amiben nincs tapasztalatom), hogy egy nagyobb alkalmazásban az ilyen előrerenderelések mekkora tehermentestést jelentenek. Mennyivel gyorsabb kirajzolni a bitmapot, mint renderelni a vektor-ábrát az egyes esetekben? Egy kör kirajzolása is lassabb, mint az azt tartalmazó ARGB bitmapé? Egy nem fix vektor-ábránál ("vonal") mi az előnyös? Végülis mennyi előrenderelt képet lehet a memóriában tartani (mobilon)? Persze, igen-igen, csinálhatnék sebesség- és memóriateszteket (már csináltam is egy párat!), de valójában nem akartam hónapokat eltölteni a jó megoldások megtalálásával (teljes munkaidő, család mellett), amikor erre nyilvánvalóan megvannak a kész bevált módszerek, amiket egy tapasztaltabb valaki talán gyorsan össze is tud foglalni.
Kotta nehéz ügy
Mi van, ha egyik sor végéről a következő sor elejére is kell átkötés? (Pl. orgonapedál sok ütemen keresztül.)
Szóval szerintem itt a rajzolással is gondok lesznek, mint a legtöbb zenei szerkesztőben. A régiekben (ugye desktopon) egyetlen sort láttál (szólamonként) és vízszintes görgetés kilóméterszámra.
Szerintem ha meg tudod oldani sorról-sorra is, akkor több képből is.
Lesz belőle zene is? Vagy "csak" kottarajzoló?
Ütemközi elemek
Olyan szerkesztőre gondoltam, ahol soronként több szólam is lehet, és kivételes esetben egy szólam át is mászhat a másik szólamba. Összeszedtem néhány zeneművet referenciaként, amelyeket mindenképp le kell tudnjon kottázni a program (Bach-fúgák, Chopin-mazurkák stb.).
Azonnal lejátszható zene is lesz belőle. Ezt a részét már kvázi véglegesen specifikáltam magamnak, ez nem is bonyolult, sőt talán ez a legkezelhetőbb része. A lejátszáskor a betöltődő hangzó objektumok egy prioritásos sorba kerülnek. Így megoldódik az a probléma is, amit sok szerkesztő csinál, mégpedig, hogy minden előke, arpeggio stb. lejátszása csak a főhang időpontjában kezd lejátszódni, ami meglehetősen rosszul hangzik. A prioritásos sorral az előkék a főhang előtt sorra tudnak kerülni, és a megfelelő időben lejátszódni.
Az eredeti inspiráció az volt, hogy érintőképernyőn javarészt mozdulatparancsokkal nagyon hatékonyan lehessen kottázni, amellett, hogy a szerkesztő minden fontosabb dolgot ismerjen (ami az eddig megjelent nem túl sok mobilos kottaprogramoknál nem nagyon valósul meg).
A magam részéről a Lilypond nagy rajongója vagyok, de a hozzá tartozó Denemo sok szempontból nem éppen felhasználóbarát. Élő renderelés esetén természetesen nem lehetnek olyan minőségi elvek, mint egy Lilypondnál, de későbbi tervem, hogy egyúttal jól használható Lilypond GUI-t hozzak létre. Az viszont már semmiképp nem egyszemélyes projekt lesz.
Betvek
Fwiw: a Unicode kódol
Font
Hogyan?
Ha bármilyen hanghullámot gyártasz, akkor hacsak nem te kevered a frekiket (ez nagyon erőforrásigényes), akkor szinte biztos, hogy hamis lesz. A MIDI viszont pont erre való (mint nevéből is adódik).
Na ez engem is érdekelne.
Hang
Lejátszás esetén a logika a fentebb említett proritásos sor, amit a lejátszó iterál, és küldi a hangot a MIDI kimenetre a Java Sound API-n keresztül. Alapvetően a MIDI az egyetlen alternatíva, a legjobb kompromisszum. Egy beep jellegű nyávogás (mint a régi mobilok csengőhangjai) minőségileg elviselhetetlen lenne, olyan szintű hangszintetizáció pedig, mint pl. a Kontakt Player, nem fér el ebben a projektben. Jómagam pedig inkább nem vállalkozom hanggenerálás implementálására.
Mentés esetén a teljes kottát exportáljuk MIDI formátumba, attól függetlenül, hogy a kotta meg van-e nyitva. Ennek megvalósítását későbbre tervezem, mert ehhez alaposabban át kell néznem a MIDI specifikációját, mint eddig tettem. Bizonyos értelemben itt is hasonlóak az elvek a kotta feldolgozását illetően.
Na, ez érdekelne
Ha találsz olyat, amit te értesz is, egy cikkben igazán bemutathatnád... :) (Bár kicsit messi van a webfejlesztéstől, de jövőképet mutat: a telódon írod a saját csengőhangodat - az már valami.)
Komolyan érdekelne, de mikor Delphiben tiff kép olvasást és írást csináltam (mert nem volt rá komponens), belepistultam, mire kibogarásztam meg kijegyzeteltem magamnak...
Pedig csak a tömörítetlen formátumokkal foglalkoztam, de azokból is van vagy 8.
Szóval nagyon érdekelne, hogy műxik a MIDI, ha csak van egy jó doksid (vagy linked rá), megköszönném.
Rendben
Köszi!
Átkötés
Ez eléggé bevett, én még csak
Na, csak nem
Egy nyolc évet :) De fel kéne
Próbáltad már SVG-vel?
http://upload.wikimedia.org/w
https://github.com/0xfe/vexflow
stb...
svg-ben szerintem van már egy csomó ilyen rendszer, annyi plusz kell bele, hogy hozzácsapod a kirajzolást és a mozgást, bár talán még az is benne van egyikben másikban...
De ha nem akarsz más rendszereket megnézni, svg-ben akkor is itt van az összes kottában előforduló dolog:
http://en.wikipedia.org/wiki/List_of_musical_symbols
Igen de,
A vexflow egy nagyon ígéretesnek tűnő projekt, de nem Java, és nem egy interaktív canvast kezel.
Hát koncepciónak svg-ben elég
Annyira azért...
Ja, arra gondoltam, hogy egy
A java salamander nem tudom, hogy mennyire használható ilyen célra, vagy egyáltalán képes e js-t futtatni, vagy csak java-ból lehetne belenyúlni a kirajzolásba. Bonyolult téma, főleg úgy, hogy lövésem sincs androidhoz, még sosem fejlesztettem rá... http://stackoverflow.com/a/9333274/607033 Azt írjá, hogy android 3+ böngészők alapból támogatják svg-t, a 2.x-eseknél canvassal meg lehet oldani. Ugye jól gondolom, hogy ez böngészős alkalmazás lesz, és nem asztali? Ha asztali, akkor ez felejtős, mert szerintem a piacon lévő megjelenítők csak átkonvertálják swing vagy awt objektumokra az svg-t, amit kapnak, és úgy meg biztos, hogy lassabb lesz...
Nézegettem a salmandert, elvileg nem átfordítja más java lib-re, viszont nálam az animációk egyáltalán nem működtek benne, volt olyan, ami el sem indult, szóval én nem alapoznék rá. Ha nem böngészős dologról van szó, akkor felejtős, hacsak nem találsz valami jobbat, mint a salamander.
Sajnos nem
Ja, én is inkább ezt
Azt írják, hogy max
Tervezés
Ha használtál Javát biztos tisztában vagy a legtöbb népszerű tervezési mintával. Egy komponens cache-elésére (bitmap-ba) logikus választás például egy decorator pattern. Persze ez a dizájn nem akadályozza meg, hogy a komponensek cache-eljék önmagukat (ha úgy látják jónak).
Számomra első blikkre nem egyértelmű, mi jelent problémát a kirajzolásban (25% ellipszis, 60% egyenes vonal, 15% egyéb bezier-görbe). Memóriaproblémát sem látok abban, ha egy objektum mogatásakor lerendereled mi van alatta, mi van fölötte, aztán dinamikusan kompozitálod a 3 bitmapot (akár hardver-támogatással akár anélkül).
Interfész
Teljes gőzzel ezen vagyok.
Erre gondoltál?:
Van egy ScoreObjectDrawer és az abból származó NoteHeadDrawer interfész, amit a NoteHeadRenderer osztály és a NoteHeadBitmap osztály (a dekorátor) valósít meg. Ezek is absztrakt osztályok, amiket majd például az android.2DAPINoteHeaderRenderer, desktop.G2DNoteHeadRenderer és hasonló osztályok konkretizálnak platformonként.
De talán mégis jobb, ha a dekoráció helyett csak NoteHeaderRenderer van, ami lusta betöltés elvén a már kész bitmapot adja vissza, amit szükség esetén helyben generál. (Tehát csak a komponens cache-eli magát.)
Na jó, ezek végülis részletkérdések, a lényeg, hogy minden kis objektumot rendereljek előre, ha lehet.
Cache
Ezt nem mondtam. A cache nem szerves része a feladatnak (kotta rajzolás), ez azt sugalja, hogy csak fölöslegesen bonyolítaná az interfészeket, ha egy szinten akarjuk megvalósítani a kritikus funkciókkal.
Az, hogy mindent külön bitmapban kell tárolni nem feltétlenül jó ötlet. 4 voanlat egyszerűbb rajzolni, mint bitmapokat kompozitálni (egy bizonyos méretnél).
Bitmapok
Egy téglalap vagy vonal kirajzolását nyilván nem érdemes cache-elni. Viszont akkor hol a határ? Tulajdonképpen pont ezt próbálom kipuhatolni. (Nyilván meg lehet ezt úgy is csinálni, hogy a cahe-elés skálázható legyen platform/beállítás szerint.)
Én szerintem először csináld
Igaz
Minél több az absztrakció,
Ez egy kicsit bonyolultabb...
Előszöris, az ilyen szinten cache-elhető objektumok száma nem az üres és nemüres kottafejekre korlátozódik (khm. eleve négyféle általánosan használt kottafej van, plusz legalább egy tucat speciális, nem beszélve a kishangjegyekről), hanem kulcsokra, módosítójelekre, pontozásra, trillákra, arpeggiojelekre, pedáljelekre stb-stb.
Bizonyos jelek bizonyos szinteken egy jól megfogható csoportot alkotnak (akkordok a hozzá tartozó módosítókkal), amelyeket kívülről kizárólag a szárirány befolyásol, és így a környezettől függetlenül renderelhetők. Ezért gondoltam, hogy ezekhez is lehetne bitmapes cache-et rendelni. Az összekötött zászlók kirajzolása is bonyolult feladat, persze erre is megvannak az algoritmusaim.
Fontos, hogy bizonyos dolgokban nem akarok korlátozásokat. Ebben a filozófiám megegyezik a Lilypondéval. Tetszőleges számú szólam lehet. És ha belegondolsz, ezek összeillesztése nem egyszerű. Erre természetesen szintén megvannak a hagyományok és az algoritmusok, mindössze azt demonstrálom, hogy a kotta nem egy egyszerű dolog.
Nem lesznek sorok az interaktív módban, egyelőre legalábbis biztosan. Csak papíron van értelme a sorokba tördelésnek, a szerkesztés közben és lejátszáskor is sokkal jobb, ha csak egyetlen (tetszőlegesen) hosszú sor van (különösen mobil eszközön). Az, hogy hány ütem fér egy sorba, szintén nem egyszerű, egy kellően intelligens sortörési algoritmus kell hozzá. A kinyomtatás/PDF-gyártás más kérdés, ott már nem kell az interakcióval és a kirajzolás sebességével ilyen szinten foglalkozni. De arra meg azt mondom, inkább ott a Lilypond, amibe természetesen lehet majd exportálni.
Nézd meg amit linkeltél...
Én első körben megrajzolnám az egész kottát szólamonként egy-egy hosszú sorban, és a legvégén foglalkoznék azzal (elejéről indulva), hogy melyik ütemek végén kell "sort törni", ott javítanék a szmájlikon, ha vannak.
Kompozitálás
Pixel format
Nem értem
Kompozitor
A kutatás fázisa nagyon fontos, amikor megkeresed az API-kat, amik kielégítik a követelményeidet, esetleg egy kisebb sebességtesztet is futtatsz a saját "hello world" kódjuk segítségével. - Ilyenkor megismerheted az egyes libraryk által használt modelt, ami segít a tervezésben.
Platformfüggetlenség
Svg + ajax/socket.io-val
Gondoltam erre is, de...