/* ----------------------------------------------------------------------------- This source file is part of OpenSpace3D For the latest info, see http://www.openspace3d.com Copyright (c) 2024 I-maginer This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA, or go to http://www.gnu.org/copyleft/lesser.txt ----------------------------------------------------------------------------- */ struct ObjSubtitleStr = [ PSUBT_inst : PInstance, PSUBT_iWordTime : I, PSUBT_iMaxWords : I, PSUBT_lContent : [[I S] r1], PSUBT_trm : Timer, PSUBT_bReading : I, PSUBT_iLastTick : I, PSUBT_iLastPeriod : I, PSUBT_bState : I ]mkObjSubtitleStr;; proto sendNext = fun [ObjSubtitleStr] I;; fun deleteOb(inst, obstr)= if (obstr.PSUBT_trm == nil) then nil else ( _deltimer obstr.PSUBT_trm; set obstr.PSUBT_trm = nil; ); set obstr.PSUBT_lContent = nil; 0;; fun cbTimerSubtitle(trm, obstr)= _deltimer trm; set obstr.PSUBT_trm = nil; sendNext obstr; 0;; fun sendNext(obstr)= if (obstr.PSUBT_lContent == nil) then ( if (!obstr.PSUBT_bReading) then nil else SendPluginEvent obstr.PSUBT_inst "End" nil nil; set obstr.PSUBT_bReading = 0; set obstr.PSUBT_iLastTick = 0; set obstr.PSUBT_iLastPeriod = 0; 0; ) else ( let hd obstr.PSUBT_lContent -> [tm text] in ( if (obstr.PSUBT_bReading) then nil else SendPluginEvent obstr.PSUBT_inst "Begin" nil nil; set obstr.PSUBT_bReading = 1; SendPluginEvent obstr.PSUBT_inst "Out" text nil; set obstr.PSUBT_lContent = tl obstr.PSUBT_lContent; if (obstr.PSUBT_trm == nil) then nil else ( _deltimer obstr.PSUBT_trm; set obstr.PSUBT_trm = nil; ); set obstr.PSUBT_iLastPeriod = (max 10 (max obstr.PSUBT_iMaxWords tm)); set obstr.PSUBT_iLastTick = _tickcount; set obstr.PSUBT_trm = _rfltimer _starttimer _channel obstr.PSUBT_iLastPeriod @cbTimerSubtitle obstr; ); 0; ); 0;; //TODO priorise comma, dot... fun setMaxWordsPerLine(lw, nbw, wt, nw)= if (lw == nil) then nw else let 0 -> i in let "" -> line in ( let 0 -> override in while (((!override && (i < nbw)) || override) && (lw != nil)) do ( let hd lw -> w in set line = strcatn line::" "::w::nil; //keep small words "?" let hd tl lw -> nextw in if ((strlen nextw) < 3) then ( set line = strcatn line::" "::nextw::nil; set lw = tl tl lw; 0; ) else ( set lw = tl lw; let sizelist lw -> nsize in if (nsize > 3) then nil else ( //addLogMessage strcat "Override: " nextw; set override = 1; ); 0; ); set i = i + 1; ); //add \n when word number is large let strToWordList line -> lnw in let (sizelist lnw) + 1 -> nbword in let if ((nbword - 1) > (nbw / 2)) then let splitList lnw (nbword / 2) -> [aw bw] in if (sizelist bw) > 1 then strcatn (strcatnSep aw " ")::"\n"::(strcatnSep bw " ")::nil else line else line -> line in let wt * (nbword - 1) -> duration in setMaxWordsPerLine lw nbw wt lcat nw [duration line]::nil; );; fun filterText(l, nbw, wt, nl)= if (l == nil) then nl else let strToWordList hd l -> lw in let setMaxWordsPerLine lw nbw wt nil -> nw in filterText tl l nbw wt lcat nl nw;; fun cbSubtitle(inst, from, action, param, reply, obstr)= let getStringParameter param -> [text duration] in let sizelist strToWordList text -> nbwords in let if (((atoi duration) == nil) || (nbwords == 0)) then obstr.PSUBT_iWordTime else (atoi duration) / nbwords -> wt in let filterText strToList text obstr.PSUBT_iMaxWords wt nil -> lcont in set obstr.PSUBT_lContent = lcat obstr.PSUBT_lContent lcont; if ((obstr.PSUBT_bState != 1) || (obstr.PSUBT_trm != nil)) then nil else sendNext obstr; 0;; fun cbStart(inst, from, action, param, reply, obstr)= if ((obstr.PSUBT_bState != 1) || (obstr.PSUBT_trm != nil)) then nil else ( set obstr.PSUBT_bState = 1; sendNext obstr; ); 0;; fun cbStop(inst, from, action, param, reply, obstr)= if (!obstr.PSUBT_bState) then nil else ( if (obstr.PSUBT_trm == nil) then nil else ( _deltimer obstr.PSUBT_trm; set obstr.PSUBT_trm = nil; ); set obstr.PSUBT_bState = 0; set obstr.PSUBT_iLastTick = 0; set obstr.PSUBT_iLastPeriod = 0; ); 0;; fun cbClear(inst, from, action, param, reply, obstr)= set obstr.PSUBT_lContent = nil; if (!obstr.PSUBT_bReading) then nil else SendPluginEvent obstr.PSUBT_inst "End" nil nil; set obstr.PSUBT_bReading = 0; 0;; fun cbNext(inst, from, action, param, reply, obstr)= sendNext obstr; 0;; fun cbSetWordTime(inst, from, action, param, reply, obstr)= if ((atoi param) == nil) then nil else set obstr.PSUBT_iWordTime = atoi param; 0;; fun cbPause(inst, from, action, param, reply, obstr)= if (!obstr.PSUBT_bState || (obstr.PSUBT_trm == nil)) then nil else ( if (obstr.PSUBT_trm == nil) then nil else ( _deltimer obstr.PSUBT_trm; set obstr.PSUBT_trm = nil; ); set obstr.PSUBT_bState = 2; if (obstr.PSUBT_iLastTick == 0) then nil else set obstr.PSUBT_iLastPeriod = obstr.PSUBT_iLastPeriod - (_tickcount - obstr.PSUBT_iLastTick); ); 0;; fun cbResume(inst, from, action, param, reply, obstr)= if ((obstr.PSUBT_bState == 1) || (obstr.PSUBT_trm != nil)) then nil else ( set obstr.PSUBT_bState = 1; set obstr.PSUBT_iLastTick = _tickcount; if (obstr.PSUBT_iLastPeriod > 0) then ( set obstr.PSUBT_trm = _rfltimer _starttimer _channel obstr.PSUBT_iLastPeriod @cbTimerSubtitle obstr; 0; ) else ( sendNext obstr; 0; ); ); 0;; fun newOb(inst)= let (getPluginInstanceParam inst "text") -> text in let atoi (getPluginInstanceParam inst "wordtime") -> wordtime in let if (wordtime == nil) then 300 else wordtime -> wordtime in let atoi (getPluginInstanceParam inst "maxwords") -> maxwords in let if (maxwords == nil) then 9 else maxwords -> maxwords in let atoi (getPluginInstanceParam inst "init") -> init in let if (init == nil) then 1 else init -> init in let mkObjSubtitleStr [inst wordtime maxwords nil nil 0 0 0 init] -> obstr in ( let filterText strToList text obstr.PSUBT_iMaxWords obstr.PSUBT_iWordTime nil -> lcont in set obstr.PSUBT_lContent = lcont; PluginRegisterAction inst "In" mkfun6 @cbSubtitle obstr; PluginRegisterAction inst "Start" mkfun6 @cbStart obstr; PluginRegisterAction inst "Stop" mkfun6 @cbStop obstr; PluginRegisterAction inst "Pause" mkfun6 @cbPause obstr; PluginRegisterAction inst "Resume" mkfun6 @cbResume obstr; PluginRegisterAction inst "Clear subtitle" mkfun6 @cbClear obstr; PluginRegisterAction inst "Next input" mkfun6 @cbNext obstr; PluginRegisterAction inst "Set word time" mkfun6 @cbSetWordTime obstr; if (!init) then nil else sendNext obstr; setPluginInstanceCbDel inst mkfun2 @deleteOb obstr; ); 0;; fun IniPlug(file)= PlugRegister @newOb nil; setPluginEditor @dynamicedit; 0;;