From 06d5929bea61951b9905aff435ac5446d30940e3 Mon Sep 17 00:00:00 2001 From: Niels Martin Hansen Date: Wed, 4 Apr 2007 20:33:37 +0000 Subject: [PATCH] Semi-complete kara-templater implementation, regular syl and line handling should be in place now (untested though) Originally committed to SVN as r1007. --- automation/autoload/kara-templater.lua | 247 +++++++++++++++++++++++-- automation/include/karaskel.lua | 6 + 2 files changed, 235 insertions(+), 18 deletions(-) diff --git a/automation/autoload/kara-templater.lua b/automation/autoload/kara-templater.lua index 48758f866..8681df4af 100644 --- a/automation/autoload/kara-templater.lua +++ b/automation/autoload/kara-templater.lua @@ -221,6 +221,23 @@ function parse_template(meta, styles, line, templates, mods) end end +-- Iterator function, return all templates that apply to the given line +function matching_templates(templates, line) + local lastkey = 0 + local function test_next() + lastkey = lastkey + 1 + local t = templates[lastkey] + if not t then + return nil + elseif t.style == line.style or not t.style then + return t + else + return test_next() + end + end + return test_next +end + -- Apply the templates function apply_templates(meta, styles, subs, templates) @@ -234,16 +251,7 @@ function apply_templates(meta, styles, subs, templates) -- run all run-once code snippets for k, t in pairs(templates.once) do assert(t.code, "WTF, a 'once' template without code?") - local f, err = loadstring(t.code, "template code once") - if not f then - aegisub.debug.out(2, "Failed to parse Lua code: %s\nCode that failed to parse: %s\n\n", err, t.code) - else - setfenv(f, tenv) - local res, err = pcall(f) - if not res then - aegisub.debug.out(2, "Runtime error in template code: %s\nCode producing error: %s\n\n", err, t.code) - end - end + run_template_code(t, tenv) end -- start processing lines @@ -256,6 +264,7 @@ function apply_templates(meta, styles, subs, templates) l.comment = true l.effect = "karaoke" subs[i] = l + l.i = i -- and then run it through the templates apply_line(meta, styles, subs, l, templates, tenv) end @@ -263,21 +272,223 @@ function apply_templates(meta, styles, subs, templates) end function apply_line(meta, styles, subs, line, templates, tenv) - -- apply all line templates - for k, t in pairs(templates.line) do + -- Tell whether any templates were applied to this line, needed to know whether the original line should be removed from input + local applied_templates = false + + -- General variable replacement context + local varctx = { + layer = line.layer, + lstart = line.start_time, + lend = line.end_time, + ldur = line.duration, + lmid = line.start_time + line.duration/2, + style = line.style, + actor = line.actor, + margin_l = ((line.margin_l > 0) and line.margin_l) or line.styleref.margin_l, + margin_r = ((line.margin_r > 0) and line.margin_r) or line.styleref.margin_r, + margin_t = ((line.margin_t > 0) and line.margin_t) or line.styleref.margin_t, + margin_b = ((line.margin_b > 0) and line.margin_b) or line.styleref.margin_b, + margin_v = ((line.margin_t > 0) and line.margin_t) or line.styleref.margin_t, + syln = line.karaoke.n, + li = line.i, + lleft = line.left, + lcenter = line.left + line.width/2, + lright = line.left + line.width, + lx = line.x, + ly = line.y + -- TODO: more positioning vars + } + + -- Specific for whole-line processing + varctx["start"] = varctx.lstart + varctx["end"] = varctx.lend + varctx.dur = varctx.ldur + varctx.mid = varctx.lmid + varctx.i = varctx.li + varctx.left = varctx.lleft + varctx.center = varctx.lcenter + varctx.right = varctx.lright + varctx.x = varctx.lx + varctx.y = varctx.ly + + local function set_ctx_syl(syl) + varctx.sstart = syl.start_time + varctx.send = syl.end_time + varctx.sdur = syl.duration + varctx.smid = syl.start_time + syl.duration / 2 + varctx["start"] = varctx.sstart + varctx["end"] = varctx.send + varctx.dur = varctx.sdur + varctx.mid = varctx.smid + varctx.si = syl.i + varctx.i = varctx.si + varctx.sleft = syl.left + varctx.scenter = syl.center + varctx.sright = syl.right + if line.halign == "left" then + varctx.sx = varctx.lleft + syl.left + elseif line.halign == "center" then + varctx.sx = varctx.lleft + syl.center + elseif line.halign == "right" then + varctx.sx = varctx.lleft + syl.right + end + varctx.sy = line.y + varctx.left = varctx.sleft + varctx.center = varctx.scenter + varctx.right = varctx.sright + varctx.x = varctx.sx + varctx.y = varctx.sy end - -- loop over syllables - for i = 0, line.karaoke.n do - local syl = line.karaoke[i] - -- apply syllable templates + + tenv.orgline = line + tenv.line = nil + tenv.syl = nil + tenv.char = nil + tenv.furi = nil + + -- Apply all line templates + for t in matching_templates(templates.line, line) do + if t.code then + run_template_code(t, tenv) + else + applied_templates = true + local newline = table.copy(line) + tenv.line = newline + newline.text = "" + if t.pre ~= "" then + newline.text = newline.text .. run_text_template(t.pre, tenv, varctx) + end + if t.t ~= "" then + for i = 1, line.kara.n do + local syl = line.kara[i] + tenv.syl = syl + set_ctx_syl(syl) + newline.text = newline.text .. run_text_template(t.t, tenv, varctx) + if t.addtext then + if t.keeptags then + newline.text = newline.text .. syl.text + else + newline.text = newline.text .. syl.text_stripped + end + end + end + else + -- hmm, no main template for the line... put original text in + newline.text = newline.text .. line.text + end + newline.effect = "fx" + subs.append(newline) + end + end + + -- Loop over syllables + for i = 0, line.kara.n do + local syl = line.kara[i] - -- apply character templates + -- Apply syllable templates + for t in matching_templates(templates.syl, line) do + tenv.syl = syl + set_ctx_syl(syl) + + if not t.inline_fx or t.inline_fx == syl.inline_fx then + if t.code then + run_code_template(t, tenv) + elseif t.multi then + for hl = 1, syl.highlights.n do + local hldata = syl.highlights[hl] + local hlsyl = table.copy(syl) + hlsyl.start_time = hldata.start_time + hlsyl.end_time = hldata.end_time + hlsyl.duration = hldata.duration + tenv.basesyl = syl + tenv.syl = hlsyl + set_ctx_syl(hlsyl) + + for j = 1, t.loops do + tenv.j = j + local newline = table.copy(line) + tenv.line = newline + newline.text = run_text_template(t, tenv, varctx) + if t.addtext then + newline.text = newline.text .. syl.text_stripped + end + newline.effect = "fx" + subs.append(newline) + end + end + else + for j = 1, t.loops do + tenv.j = j + local newline = table.copy(line) + tenv.line = newline + newline.text = run_text_template(t, tenv, varctx) + if t.addtext then + newline.text = newline.text .. syl.text_stripped + end + newline.effect = "fx" + subs.append(newline) + end + end + end + end + + -- Apply character templates + -- TODO end - -- loop over furigana + + -- Loop over furigana for i = 1, line.furi.n do + -- TODO end end +function run_code_template(template, tenv) + local f, err = loadstring(template.code, "template code") + if not f then + aegisub.debug.out(2, "Failed to parse Lua code: %s\nCode that failed to parse: %s\n\n", err, template.code) + else + setfenv(f, tenv) + for j = 1, template.loops do + tenv.j = j + local res, err = pcall(f) + if not res then + aegisub.debug.out(2, "Runtime error in template code: %s\nCode producing error: %s\n\n", err, template.code) + end + end + end +end + +function run_text_template(template, tenv, varctx) + local res = template + + -- Replace the variables in the string (this is probably faster than using a custom function, but doesn't provide error reporting) + if varctx then + res = string.gsub(res, "$([%a_]+)", varctx) + end + + -- Function for evaluating expressions + local function expression_evaluator(expression) + f, err = loadstring(string.format("return (%s)", expression)) + if (err) ~= nil then + aegisub.debug.out(2, "Error parsing expression: %s\nExpression producing error: %s\nTemplate with expression: %s\n\n", err, expression, template) + return "!" .. expression .. "!" + else + setfenv(f, tenv) + local res, val = pcall(f) + if res then + return val + else + aegisub.debug.out(2, "Runtime error in template expression: %s\nExpression producing error: %s\nTemplate with expression: %s\n\n", val, expression, template) + return "!" .. expression .. "!" + end + end + end + -- Find and evaluate expressions + res = string.gsub(res , "!(.-)!", expression_evaluator) + + return res +end + -- Main function to do the templating function filter_apply_templates(subs, config) diff --git a/automation/include/karaskel.lua b/automation/include/karaskel.lua index c2661c364..b486cd4c4 100644 --- a/automation/include/karaskel.lua +++ b/automation/include/karaskel.lua @@ -210,18 +210,21 @@ function karaskel.preproc_line(subs, meta, styles, line) line.center = line.left + line.width / 2 line.right = line.left + line.width line.x = line.left + line.halign = "left" elseif line.styleref.align == 2 or line.styleref.align == 5 or line.styleref.align == 8 then -- Centered line.left = (meta.res_x - line.eff_margin_l - line.eff_margin_r - line.width) / 2 + line.eff_margin_l line.center = line.left + line.width / 2 line.right = line.left + line.width line.x = line.center + line.halign = "center" elseif line.styleref.align == 3 or line.styleref.align == 6 or line.styleref.align == 9 then -- Right aligned line.left = meta.res_x - line.eff_margin_r - line.width line.center = line.left + line.width / 2 line.right = line.left + line.width line.x = line.right + line.halign = "right" end line.hcenter = line.center if line.styleref.align >=1 and line.styleref.align <= 3 then @@ -230,18 +233,21 @@ function karaskel.preproc_line(subs, meta, styles, line) line.middle = line.bottom - line.height / 2 line.top = line.bottom - line.height line.y = line.bottom + line.valign = "bottom" elseif line.styleref.align >= 4 and line.styleref.align <= 6 then -- Mid aligned line.top = (meta.res_y - line.eff_margin_t - line.eff_margin_b) / 2 + line.eff_margin_t line.middle = line.top + line.height / 2 line.bottom = line.top + line.height line.y = line.middle + line.valign = "middle" elseif line.styleref.align >= 7 and line.styleref.align <= 9 then -- Top aligned line.top = line.eff_margin_t line.middle = line.top + line.height / 2 line.bottom = line.top + line.height line.y = line.top + line.valign = "top" end line.vcenter = line.middle