Orinj версия 8.0.0
Бележка: Тази страница не е за потребителите на Orinj, а за програмистите, които искат да създават ефекти за работа с цифрови сигнали за Orinj.
За сваляне
oreffect.jar | 17-ти август, 2023 | Основата на всички ефекти в Orinj. Този файл JAR съдържа компилираните интерфейси, които трябва да се осъществят от ефектите в Orinj (7 KB). |
oreffect.zip | 17-ти август, 2023 | Кода за oreffect.jar (13 KB). |
oreffect.jar
Този Java JAR файл се намира в папката "orinj" в инсталацията на Orinj. Най-важната му функция е да даде двата интерфейса, които ефектите в Orinj трябва да осъществят в Java – EffectInterface и EffectPanelInterface.
- EffectInterface.java: Този интерфейс е самият ефект. Този интерфейс определя например, че един ефект в Orinj трябва да осъществи една функция "apply" ("приложи") с определен синтаксис, така че Orinj да може да изпрати буфери с данни за аудио към този ефект и този ефект може да бъде приложен към данните за аудио.
- EffectPanelInterface.java: Този интерфейс трябва да се осъществи от графичния интерфейс на ефекта. Самият графичен интерфейс трябва да съдържа контролите, с които потребителя може да работи за да промени ефекта. Този интерфейс определя например, че панела с графиките за ефекта трябва да осъществи функцията "updateData", така че Orinj да може да каже на ефекта да обнови контролите за потребителя със стойностите на ефекта.
Когато създаваш ефекти в Orinj, трябва да създадеш поне два класа в Java – един за ефекта и един за графиките на ефекта. Тези два класа трябва да осъществят съответно двата интерфейса по-горе.
Останалите класове в този JAR са следните.
- EffectFont: Този клас съдържа статични шрифтове от клас java.awt.Font. При някои от кожите на Orinj, тези шрифтове ще бъдат променени от Orinj за да са същите, като шрифтовете на кожата. Можеш да използваш тези шрифтове за панела с графиките на твоя ефект, така че да изглежда както останалите части на Orinj. Не е задължително да използваш тези шрифтове.
- EffectGraph: Този клас съдържа цветове за графиките в Orinj. Може а използваш тези цветове за графики в ефектите. Не е задължително да използваш тези цветове.
- EffectSkin: Този клас показва дали текущата кожа е тъмна (със светли шрифтове и граници) или светла (с тъмни шрифтове и граници). Можеш да използваш тази стойност, но не е задължително да я използваш.
- EnvelopeInterface: Това е един интерфейс, който символизира една амплитудна обвивка – едно описание на това, как някаква амплитуда се променя с времето по време на свиренето (автоматизация). Някои от ефектите в Orinj използват две обвивки и двете от които осъществяват този интерфейс. Те са обвивката за сухия микс и обвивката за мокрия микс (виж по-долу). Не всички ефекти използват обвивки.
- ReadInterface: Това е един интерфейс, който се използва подобно на java.io.ObjectInputStream. Помага при четенето на параметрите на ефекта, запазени в сесии или лупинги или запазени за могат да се връщат промените.
- Undo: Всички ефекти (самите ефекти, а не панелите с техните графики) трябва да разширят този клас. Този клас съдържа една поредица от слушатели за събития и може да бъде използван за да се изпращат събития undo (за връщане на промените), които Orinj използва за да запази информация за промените в твоя ефект, които могат да бъдат върнати от потребителя.
- UndoEvent: Събитието, което трябва да се изпрати, така че Orinj да запази информацията, която може да бъде върната и да покаже едно съобщение за това в своите менюта.
- UndoListener: Един интерфейс, който се осъществява от слушателите за събития за връщане. Този интерфейс се осъществява от компоненти на Orinj, които ще слушат за събития, изпратени от ефектите.
- WorkingFolder: Този клас съдържа пътя към orinj.jar.
- WriteInterface: Това е един интерфейс, който трябва да се използва подобно на java.io.ObjectOutputStream. Помага при запазването на параметри на ефектите при запазването на сесии, лупинги или информация за връщане на промените.
Всички класове в oreffect.jar са описани по-долу в азбучен ред.
EffectChannels.java
Този клас съдържа три статистични цели числа – BOTH, LEFT, and RIGHT. Това са каналите, към които този ефект ще се приложи. Една променлива "channel" ще се даде на всяка функция "apply" в класовете, които осъществяват EffectInterface.java.
- BOTH: Този ефект ще се приложи към двата канала.
- LEFT: Този ефект ще се приложи само към левия канал.
- RIGHT: Този ефект ще се приложи само към десния канал.
EffectFont.java
Този клас съдържа четири статични шрифта – LARGEFONT, MEDIUMFONT, SMALLFONT и SMALLERFONT – от клас java.awt.Font. Не е задължително да използваш тези шрифтове в твоите ефекти.
- LARGEFONT: В ефектите, които са в инсталацията на Orinj, този шрифт се използва предимно за етикетите на контролите.
- MEDIUMFONT: Този шрифт не се използва в ефектите, които са в инсталацията на Orinj. В Orinj, шрифтът със среден размер се използва например при етикетите на панела за времето и панела за ритъма.
- SMALLFONT: В ефектите, които са в инсталацията на Orinj, този шрифт се използва предимно за етикетите на плъзгачите.
- SMALLERFONT: Този шрифт не се използва в ефектите, които са в инсталацията на Orinj. В Orinj, този шрифт се използва например при клавиатурата в изгледа за MIDI и при обвивките.
EffectGraph.java
Този клас съдържа четири статични цвята – BACKGROUND, GRID, FOREGROUND и HIGHLIGHT – от класа java.awt.Color. Не е задължително да използваш тези цветове в твоите ефекти.
- BACKGROUND: Този цвят е фона на графиките в ефектите на Orinj. Това например е фона на графиката на компресора в Orinj.
- GRID: Това е цвета на решетките, осите и етикетите на осите в графиките на ефектите на Orinj. Това например е цвета на решетката на графиката на компресора в Orinj.
- FOREGROUND: Това е цвета на графиките в Orinj. Това например е цвета на линията за отношенията на компресиране в графиката на компресора в Orinj.
- HIGHLIGHT: Това е един втори цвят за това, което трябва да се рисува в графиките на ефектите в Orinj. Това например е цвета на избраната точка в графиката на компресора в Orinj.
EffectInterface.java
Всички ефекти в Orinj трябва да осъществят този интерфейс. Следното описва функциите в този интерфейс.
- public abstract boolean apply(int [] buffer, int [] controlbuffer, int channels, int channel, float samplingRate, double time, float drymix, float wetmix, EnvelopeInterface dryMixEnvelope, EnvelopeInterface wetMixEnvelope, double timeStart, double timeEnd): Тази функция взема входните аудио данни и ги променя за да приложи ефекта.
- buffer – този буфер съдържа входните данни с аудио и също така се използва за да се запазят изходните данни с аудио от ефекта. Това е поредица от 32-битови цели числа със знак (тоест, 32-битови данни PCM, които са цели числа със знак) със стойности между Integer.MIN_VALUE и Integer.MAX_VALUE.
- Този аргумент съдържа 32-битови данни PCM от цели числа със знак, независимо от пробната резолюция, която се използва в други части на Orinj. Това важи и за входните, и за изходните данни. Самият Orinj, а не ефекта, превръща данните с аудио в буфери с цели числа от 32 бита преди да изпрати тези данни с аудио към ефекта и превръща изхода от ефекта обратно в каквото се използва от звуковите устройства.
- Данните с аудио в buffer може да са за един или два канал, както е определено от аргумента channels. С два канала, първото цяло число е първата проба за левия канал, следващото цяло число е първата проба за десния канал, следващото цяло число е втората проба за левия канал и така нататък.
- buffer не съдържа всички аудио данни, които ще се изпратят към ефекта. По време на свиренето, данните с аудио се изпращат към ефекта на части. Самият ефект трябва да се справи с запазването и изхвърлянето на данни с аудио по начин, който работи за ефекта. Ехото в Orinj например използва предишни данни с аудио, защото текущите стойности на повторенията на ехото зависят от предишните стойности на сигнала. Това ехо запазва предишните аудио данни, използва ги и ги изхвърля, когато вече не са необходими (например, когато данните с аудио са доста далече в миналото за да се използват от ехото).
- controlbuffer – контролния буфер трябва да се използва от ефекти, които позволяват странична връзка, като например един компресор със странична връзка. Един компресор със странична връзка променя динамиката на една писта в зависимост от динамиката на една втора писта. Данните с аудио, които се съдържат във втората писта, ще се изпратят към ефекта в този аргумент.
- Един ефект, който използва този аргумент, трябва също да върне true в allowsSideChaining. Ако ефектът не позволява странични връзки, този аргумент може да е null и трябва да не се използва.
- Orinj контролира коя писта се използва като контролна писта. Няма нужда да се осъществяват контроли в графичния интерфейс на ефекта за това. Orinj ще достави тези контроли, ако ефектът върне true в allowsSideChaining.
- Ако controlbuffer не е null, тогава дължината на controlbuffer е същата, като дължината на buffer. Форматът на данните с аудио в controlbuffer е същият, като формата на данните с аудио в buffer.
- channels – броя на аудио каналите в buffer.
- channel – каналът, към който този ефект се прилага. Възможни стойности са определени в EffectChannels.java и могат да са двата канала, левия или десния.
- samplingRate – пробната честота на аудиото в buffer.
- time – началното време на буферите за аудио (buffer и controlbuffer) в сесията, вълната или лупинга, който се свири, в секунди. Ако свиренето например започва на десетата секунда в многопистовата сесия, time ще бъде равно на 10 при първото повикване на тази функция.
- Този аргумент обикновено се използва за да се изчислят амплитудите на сухия и мокрия микс (виж по-долу). Повечето ефекти няма да използват този аргумент за други неща. Едно ехо например ще произведе повторения на сигнала с едни и същи стойности на забавянията и затихванията, независимо от текущото време на свирене. Някои ефекти могат да се нуждаят от точното време на свирене. Един wah wah например ще звучи по добре ако трептенето му съвпада с ритъма на песента. Уа-уа-то тофава трябва да знае времето на свиренето. По принцип, ефекти, които използват нискочестотно трептене – трептене с честоти, които могат да се създават или възприемат от хората – вероятно се нуждаят от времето на свирене.
- drymix – сухият микс на ефекта. Това е число между 0 и 1. Това е допълнителната амплитуда, която трябва да се сложи на оригиналния сигнал, преди този оригинален сигнал да се сложи в изходния буфер, но обикновено след като този оригинален сигнал се използва при изчисленията на ефекта (тоест, сухият микс не трябва да променя повторенията на едно ехо, а само оригиналния сигнал). Ефектите, които не използват сух или мокър микс, трябва да не използват този аргумент.
- Не всички ефекти имат сух и мокър микс. Вземи например един прост дилей, който произвежда едно забавено и затихнало повторение на оригиналния сигнал. Осъществяването на сухия и мокрия микс е просто. Амплитудата на сухия микс се слага на оригиналния сигнал и амплитудата на мокрия микс се слага на забавеното и затихнало повторение. Обикновено, амплитудата на сухия микс се слага на оригиналния сигнал само след като забавеното и затихнало повторение е изчислено, така че тази амплитуда да не влияе на мократа част от получения сигнал. Подобно, амплитудата на мокрия микс не трябва да влияе на сухата, оригинална част на получения сигнал.
- Един компресор, от друга страна, обикновено няма да има сух и мокър микс. В един компресор, аргументите drymix, wetmix, dryMixEnvelope, and wetMixEnvelope трябва да се пренебрегнат.
- wetmix – мокрият микс на ефекта. Това е число между 0 и 1. Това е допълнителната амплитуда, сложена на преработения сигнал, след като този преработен сигнал е изчислен от оригиналния сигнал. Ефектите, които не използват сух или мокър микс, трябва да не използват този аргумент.
- dryMixEnvelope – амплитудна обвивка за сухия микс на ефекта (автоматизация на сухия микс). Тази обвивка е една допълнителна амплитуда за сухия микс, която трябва да се приложи към оригиналния сигнал, преди този оригинален сигнал да се сложи в buffer и след като този оригинален сигнал се използва за да се изчисли мокрия сигнал. Ефектите, които нямат сух и мокър микс, трябва да не използват този аргумент.
- Разликата между този аргумент и drymix е, че drymix е постоянен за аудио данните в buffer, а обвивката за сухия микс може да се промени. Едно повикване към функцията на този аргумент EnvelopeInterface::getValueAt ще даде стойността на амплитудата, която трябва да се сложи в определено време. Амплитудата е между 0 и 1 и трябва да се използва по същия начин, както drymix.
- wetMixEnvelope – амплитудна обвивка за мокрия микс на ефекта (автоматизация на мокрия микс). Тази обвивка е една допълнителна амплитуда за мокрия микс, която трябва да се приложи към преработения сигнал, след като този преработен сигнал е изчислен от оригиналния сигнал. Ефектите, които нямат сух и мокър микс, трябва да не използват този аргумент.
- timeStart – началното време, след което ефектът трябва да се приложи, в секунди, след началото на сесията или вълната. Тази функция няма да се извика, ако звуковите буфери са извън интервала между timeEnd и timeStart.
- timeEnd – крайното време, преди което ефектът трябва да се приложи, в секунди, от началото на сесията или вълната.
- Функцията връща true, ако няма грешки. Връща false, ако има грешки.
- public abstract boolean allowsDryWetMix(): За да осъществиш тази функция, просто върни true, ако твоя ефект позволява сух и мокър микс и false, ако не позволява.
- public abstract boolean allowsSideChaining(): За да осъществиш тази функция, върни true, ако твоя ефект позволява странична връзка и false, ако не позволява.
- public abstract boolean startPlay(): Използвай тази функция за каквито и да са подготовки в началото на свиренето. Графичният еквилайзер в Orinj например използва тази функция за да изчисли своите честотни филтри (въпреки че тези филтри могат пак да бъдат изчислени по време на свиренето, ако потребителят промени контролите на еквилайзера). Тази функция връща true, ако няма грешки и false, ако има.
- public abstract void stopPlay(): Използвай тази функция за действията, които трябва да се извършат, когато свиренето спре. Внимавай с тази функция, защото свиренето може да продължи за малко даже и след като тази функция свърши, в зависимост от размера на буферите за аудио в Orinj. Не трябва например да използваш тази функция за да махаш запазените входни данни с аудио (по-скоро, изтрий тези данни когато свиренето започне следващия път). Много малко от ефектите в инсталацията на Orinj използват тази функция.
- public abstract boolean hasData(): Тази функция трябва да върне true, ако ефектът не е свършил своята работа и false, ако е. Представи си един прост дилей (виж Orinj Структура на ефектите Примерен дилей Delay.java). Един такъв ефект създава едно повторение на оригиналния сигнал. Даже и когато оригиналният сигнал е свършил (например след края на последната вълна в пистата), повторението на сигнала, което е създадено от ефекта, може да продължи за кратко време след това. Orinj първо ще провери дали има още оригинален сигнал. Ако не, ще запита ефекта дали самият той ще произведе сигнал. Ако да, ефектът ще бъде използван. Ако не, ефектът няма да бъде използван. Това е просто един начин да се спестят изчисления за да се направят ефектите по-бързи. За да създаде своето повторение, Примерният Дилей запазва част от оригиналния сигнал. След като оригиналният сигнал е свършил, Примерен Дилей ще върне true с тази функция само ако все още има запазена част от оригиналния сигнал. Това означава, че все още може да произведе края на повторения сигнал. Когато и запазената част на оригиналния сигнал е свършена, Примерен Дилей няма вече да може да произвежда повторения сигнал и ще върне false тук. Забележи, че винаги е безопасно просто да се върне false. Ако направиш това обаче, мокрият сигнал от твоя ефект може да спре преждевременно на края на вълната. Повечето от ефектите всъщност произвеждат едно забавяне в сигнала, което трябва да се вземе предвид.
- public abstract boolean readObject(ReadInterface stream): Тази функция и следващата функцията позволяват на Orinj да запази контролите на ефектите във файловете на сесията, лупингите или при връщането на промени в ефекта. Orinj очаква, че всеки обект (писти, ефекти, обвивки за амплитудата) осъществява собственото си запазване (т.е., стандартните методи за запазвана в Java чрез Serializable не се използват). Следното е (една опростена версия на) readObject в дилея в Orinj.
- public boolean readObject(ReadInterface ar)
- {
- try
- {
- m_leftDelay = ar.readFloat();
- m_rightDelay = ar.readFloat();
- m_leftPolarity = ar.readBoolean();
- m_rightPolarity = ar.readBoolean();
- m_leftDecay = ar.readFloat();
- m_rightDecay = ar.readFloat();
- m_lockChannels = ar.readBoolean();
- }
- catch (IOException e)
- {
- System.out.println("Exception in Delay::readObject: " + e);
- return false;
- }
- return true;
- }
- Забележи, че можеш да запазиш и четеш не само контролите на ефектите, но и номера на версията на ефекта, която може да е независима от версията на Orinj. По този начин, можеш да имаш контрол върху версиите на твоя ефект.
- public abstract boolean writeObject(WriteInterface stream): Това е функцията, която Orinj използва за да чете ефектите в сесията на Orinj, в лупингите или за връщане на промени в ефектите. Тази функция трябва да бъде осъществена подобно на горната, но стойностите трябва да бъдат запазени, а не четени.
- public abstract void setEqual(EffectInterface effect): Тази функция трябва да направи данните на този ефект равни на данните на аргумента effect. Трябва да провериш дали двата ефекта са от един и същи клас. В текущата версия на Orinj, тази функция не се използва. Може да бъде използвана в бъдещите версии.
- public abstract void setLanguage(String languageCode): Осъществи тази функция, ако графиките на твоя ефект поддържат различни езици. Тази функция сменя текущия език на този, който е определен от languageCode. Аргументът languageCode е езика според ISO 639-2.
- В Orinj, текста на етикетите, подсказките и други подобни елементи се пази във файлове XML (виж например папката orinj/languages в твоята инсталация на Orinj). Можеш обаче да програмираш езиците по някакъв друг начин и не е задължително да използваш различни езици.
EffectPanelInterface.java
Графичния интерфейс за ефектите в Orinj обикновено развива класа javax.swing.JPanel и трябва да осъществи този интерфейс.
Ефектите в Orinj се изчисляват по време на свиренето и прозорците, които Orinj използва за да позволи на потребителя да промени контролите за ефектите не са модални. Тоест, потребителят може да работи с останалите части на Orinj докато прозореца за ефекта е отворен. В Orinj, всички прозорци за ефектите са стандартни. Самият прозорец (от клас JDialog) е вече осъществен и не трябва а го програмираш. Този прозорец съдържа:
- Един бутон Затвори (Close)
- Една контрола за пренебрегване на ефекта
- Една контрола за промяна на името на ефекта
- Една контрола за избор на предопределени настройки
- Бутони за запазване и изтриване на предопределени настройки
- Една контрола за избора на контролираща писта, ако ефекта позволява странична връзка.
Тези контроли са вече готови и не трябва да ги програмираш. Трябва само да създадеш контролите, които са специфични за ефекта. При дилея в Orinj например, тези контроли са забавянията, затихванията и полярността на левия и десния канал, както в примера по-горе. Когато създадеш самите контроли за ефекта, сложи ги в един клас, който удължава javax.swing.JPanel. Orinj ще вземе този панел и ще го сложи в един прозорец, който вече съдържа бутона Close и кутията за подминаване.
Интерфейсът EffectPanelInterface съдържа една единствена функция:
- public abstract void updateData(): Тази функция прави стойностите на контролите в панела за ефекта равни на стойностите на съответните данни в самия ефект.
EffectSkin.java
Този клас съдържа една статична променлива boolean – DARK. Не е задължително да я използваш в твоите ефекти. Някои ефекти в Orinj имат компоненти с граници (виж например панела с текст за праговете и отношенията за компресиране в компресора в Orinj), които се базират на тази стойност. Компонентите могат да определят цвета на своята граница да бъде по-светъл (виж java.awt.Color.brighter()) или по-тъмен (виж java.awt.Color.darker()) от фона, в зависимост от това, дали кожата на Orinj е тъмна или светла.
EnvelopeInterface.java
Една обвивка позволява промени в контролите за аудио с времето, когато свиренето или смесването продължава. Една амплитудна обвивка например, сложена на пистата в сесията, може да позволи на потребителя на Orinj да определи как амплитудата на пистата трябва плавно да спадне, така че пистата да затихне.
Orinj използва различни видове обвивки, които позволяват промени в амплитудата, панорамата, амплитудата на сухия микс на един ефект и амплитудата на мокрия микс на един ефект. Само амплитудните обвивки за сухия и мокрия микс се използват в структурата на ефектите в Orinj.
Обвивките в Orinj са осъществени в Orinj и не трябва да се осъществяват в ефектите. Този интерфейс позволява на ефектите достъп до обвивките в Orinj.
Този интерфейс съдържа само една функция.
- getValueAt(double time): Тази функция дава стойността на обвивката в определено време на свиренето или смесването. Стойността зависи от вида на обвивката. За амплитудните обвивки на сухия и мокрия микс, това ще е допълнителна амплитуда между 0 и 1. Аргументът time е в секунди.
- Правилният начин да се използва тази функция е във функцията apply в EffectInterface в един ефект. Аргументът time на тази функция apply е времето в началото на буфера с аудио (buffer). Времето на една определена проба в buffer във функцията apply зависи от пробната честота. Ако например началното време на buffer е 10 секунди, пробната честота е 44.1 kHz и работим с проба 100 в моно аудио, тогава времето на пробата ще бъде 10 + (100 / 44100) = 10.0022 секунди. getValueAt(10.0022) ще даде правилната стойност на обвивката при тази проба. Стойността може тогава да се използва при изчисляването на ефекта.
- Стойността на обвивката може да се взема при всяка проба. Тъй като обаче разликата между пробите представлява една много малка промяна във времето и тъй като обвивките обикновено не се променят драстично във времето, може и да се взема стойността на обвивките по-рядко, веднъж на всеки няколко проби.
ReadInterface.java
В Orinj има два класа, които осъществяват този интерфейс. Първият клас удължава java.io.ObjectInputStream. Забележи, че повечето от функциите в ReadInterface всъщност са просто функциите на ObjectInputStream. Можеш да използваш този клас и неговите функции по същия начин, по който използваш ObjectInputStream и функциите в този клас (виж парчетата код по-горе). Първият клас в Orinj, който осъществява този интерфейс и удължава ObjectInputStream, чете данните на ефекта след запазването на сесиите или лупингите. Вторият клас в Orinj, който осъществява този интерфейс, чете данните на ефекта когато потребителят връща промени в ефекта (т.е., при "undo"). Този клас е осъществен по един доста различен начин от ObjectInputStream. Не чете информация от диска например, а я чете от паметта. Това, че тези два класа осъществяват един и същи интерфейс означава, че трябва да създадем само една функция readObject за всеки ефект. Тази функция ще бъде извикана и когато ефекта се чете след запазването на сесията или лупинга и когато ефекта се чете при връщането на промените в него.
Undo.java
Този клас трябва да бъде удължен от всички ефекти, даже и ако тези ефекти не осъществяват undo (т.е., не изпращат събития за да запазят информация за промените, които могат да бъдат върнати). Този клас съдържа една поредица от слушатели за събития, които са уведомени, когато едно събитие undo е изпратено от един ефект. Следното са функциите в този клас.
- public void addUndoListener(UndoListener listener): Тази функция добавя един слушател към поредицата със слушатели в ефекта. Тази функция се използва от Orinj, компонентите на който стават слушатели за тези събития.
- public void removeUndoListener(UndoListener listener): Тази функция премахва слушатели от поредицата със слушатели в ефекта. Тази функция също се използва от Orinj.
- protected void fireUndoEvent(UndoEvent event): Тази функция уведомява всички слушатели за събитието undo. Тази функция ще се използва от ефекта за да изпрати събития undo, когато контролите на ефекта се променят. Събитията ще уведомят слушателите (компонентите на Orinj), че ефекта се променя и че данните му трябва да бъдат запазени, така че да могат да бъдат върнати, ако потребителят иска да го направи. Събитието също ще даде на Orinj съобщението, което трябва да бъде показано в менюто.
UndoEvent.java
Това е събитието undo, което трябва да бъде изпратено от ефектите, чрез класа Undo който е удължен от ефектите, така че Orinj да бъде уведомен, че ефектът се променя и така че Orinj да получи съобщението, което трябва да бъде сложено в менюто undo. Този клас съдържа просто една нещо – съобщението – и една функция освен конструктора – функцията, която позволява достъп до съобщението.
UndoListener.java
Това е слушателят за събития undo. Този интерфейс се използва от Orinj.
WorkingFolder.java
Това е пътя към orinj.jar, което е папката, в която Java работи.
WriteInterface.java
Подобно на ReadInterface, в Orinj има два класа, които осъществяват този интерфейс. Първият клас удължава java.io.ObjectOutputStream и се използва при запазването на сесии и лупинги (и ефектите, като част от сесиите и лупингите). Вторият клас запазва данните на ефекта за да може промените в него да се върнат от потребителя ("undo").
Виж също:
Orinj Структура на ефектите
Добави нов коментар