-- Aegisub Automation demonstration script -- Original written by Niels Martin Hansen -- Given into the public domain -- But still, please don't use the effect generated by this script -- (unchanged) for your own works. -- Note that many of the techniques used in this script are heavily deprecated, much of the processing done -- is also done by the karaskel scripts. -- In fact, this can almost be viewed as an example of how *not* to do things... -- Since this script assumes some rather specific things about the ASS input, it might not be very useful at first. -- The original ASS file this was written for is available upon request. include("utils.lua") name = "Advanced karaoke effect" description = "An advanced karaoke effect, making heavy use of both line-copying and per-syllable text placement. Also demonstrates how to treat lines differently, based on their style, and syllables differently based on the timing tag used." version, kind, configuration = 3, 'basic_ass', {} function process_lines(meta, styles, lines, config) local output = { n=0 } math.randomseed(5922) -- just to make sure it's initialised the same every time for curline = 0, lines.n-1 do aegisub.report_progress(curline/lines.n*100) local lin = lines[curline] if lin.kind == "dialogue" and lin.style == "op romaji" then doromaji(lin, output, styles["op romaji"], 30) elseif lin.kind == "dialogue" and lin.style == "op kanji" then -- Just one of these lines should be uncommented. -- This script was written before configuration worked, otherwise it would use configuration -- to select which of the two effects to use for "op kanji" style lines. --doromaji(lin, output, styles["op kanji"], 20) dokanji(lin, output, styles["op kanji"]) else -- Unknown lines are copied verbatim table.insert(output, lin) end end return output end function doromaji(lin, output, sty, linetop) aegisub.set_status(lin.text_stripped) --local linetop = 50 -- prepare syllable data local linewidth = 0 local syltime = 0 local syls = {n=0} for i = 1, lin.karaoke.n-1 do local syl = lin.karaoke[i] syl.width, syl.height, syl.descent, syl.extlead = aegisub.text_extents(sty, syl.text_stripped) syl.left = linewidth syl.start_time = syltime syl.end_time = syltime + syl.duration syltime = syltime + syl.duration if syl.kind == "kf" and (syl.text == " " or syl.text == " ") then -- The font this effect was made to work with has too wide spaces, so make those half width syl.width = math.floor(syl.width / 2) syl.kind = "space" elseif syl.kind == "kf" then syl.kind = "deco" elseif syl.kind == "k" then syl.kind = "reg" elseif syl.kind == "ko" then syl.kind = "dance" else syl.kind = "ignore" end if syl.text == "#" then syls[syls.n-1].duration = syls[syls.n-1].duration + syl.duration syls[syls.n-1].end_time = syls[syls.n-1].end_time + syl.duration else linewidth = linewidth + syl.width syls[syls.n] = syl syls.n = syls.n + 1 end end local lineofs = math.floor((640 - linewidth) / 2) for i = 0, syls.n-1 do local syl = syls[i], copy_line(lin) if syl.kind == "space" then -- Spaces are skipped. Since the width of them is already incorporated in the position calculations -- for the following syllables, they can safely be stripped from output, without losing the spaces. elseif syl.kind == "ignore" then -- These are actually syllable kinds we don't know what to do about. They are not included in the output. else local startx, starty, shakex, shakey, enterangle -- angle to enter from enterangle = math.rad((180 / lin.karaoke.n) * i - 90) -- position to enter from (350 pixels from (320, 50)) startx = math.sin(enterangle)*350 + 320 starty = linetop - math.cos(enterangle)*350 if syl.kind == "reg" then shakex = (syl.left+lineofs) + math.random(-5, 5) shakey = linetop + math.random(-5, 5) elseif syl.kind == "deco" then shakex, shakey = syl.left+lineofs, linetop elseif syl.kind == "dance" then shakex = (syl.left+lineofs) + math.random(-5, 5) shakey = linetop + math.random(-5, 5) end -- origin for rotation local orgx = syl.left + lineofs + syl.width/2 local orgy = linetop + syl.height/2 -- entry effect local enterlin = copy_line(lin) enterlin.start_time = lin.start_time - 40 enterlin.end_time = lin.start_time enterlin.text = string.format("{\\move(%d,%d,%d,%d)\\fr%d\\t(\\fr0)\\an7}%s", startx, starty, shakex, shakey, -math.deg(enterangle), syl.text) table.insert(output, enterlin) -- main highlight effect local newlin = copy_line(lin) local hilistart, hilimid, hiliend = syl.start_time*10, (syl.start_time+syl.duration/2)*10, (syl.start_time+syl.duration)*10 newlin.text = string.format("\\move(%d,%d,%d,%d,%d,%d)\\an7}%s", shakex, shakey, syl.left+lineofs, linetop, hilistart, hiliend, syl.text) newlin.layer = 1 if syl.kind == "dance" then local fx = string.format("\\org(%d,%d)\\t(%d,%d,\\fr30)\\t(%d,%d,\\fr-30)\\t(%d,%d,\\3c&H0000ff&)\\t(%d,%d,\\3c&H000080&)\\t(%d,%d,\\fr0)", orgx, orgy, hilistart, hilistart+20, hilistart+20, hiliend, hilistart, hilimid, hilimid, hiliend, hiliend, hiliend+syl.duration*10) newlin.text = fx .. newlin.text else local fx = string.format("\\t(%d,%d,\\3c&H0000ff&)\\t(%d,%d,\\3c&H000080&)", hilistart, hilimid, hilimid, hiliend) newlin.text = fx .. newlin.text end local bord = copy_line(newlin) bord.layer = 0 bord.text = "{" .. bord.text newlin.text = "{\\bord0" .. newlin.text table.insert(output, bord) table.insert(output, newlin) -- leave effect -- cut the line over in two, lower half "drops down", upper just fades away local tophalf = copy_line(lin) tophalf.start_time = lin.end_time tophalf.end_time = lin.end_time + 100 tophalf.text = string.format("\\3c&H000080&\\fad(0,700)\\pos(%d,%d)\\an7}%s", syl.left+lineofs, linetop, syl.text_stripped) local bottomhalf = copy_line(tophalf) tophalf.text = string.format("{\\t(0,200,\\1c&H000080&)\\clip(0,0,640,%d)%s", linetop+syl.height/2, tophalf.text) bottomhalf.text = string.format("{\\org(%d,%d)\\clip(0,%d,640,480)\\t(0,200,\\1c&H000080&)\\t(200,1000,1.2,\\frx90\\clip(0,%d,640,480)%s", 320, linetop+syl.height, linetop+syl.height/2, linetop+syl.height, bottomhalf.text) table.insert(output, tophalf) table.insert(output, bottomhalf) end end end function dokanji(lin, output, sty) aegisub.set_status(lin.text_stripped) local fontname = "@HGSHigemoji" local lineheight = 0 local syltime = 0 local syls = {n=0} for i = 1, lin.karaoke.n-1 do local syl = lin.karaoke[i] syl.height, syl.width, syl.descent, syl.extlead = aegisub.text_extents(sty, syl.text_stripped) syl.start_time = syltime syl.end_time = syltime + syl.duration syltime = syltime + syl.duration if syl.text == "#" then syls[syls.n-1].duration = syls[syls.n-1].duration + syl.duration syls[syls.n-1].end_time = syls[syls.n-1].end_time + syl.duration --elseif syl.text == "" then -- skip else lineheight = lineheight + syl.height syls[syls.n] = syl syls.n = syls.n + 1 end end for i = 0, syls.n-1 do local syl = syls[i] local top = 170 for j = 0, 8 do if i+j >= syls.n then break end local newlin = copy_line(lin) newlin.start_time = lin.start_time + syl.start_time newlin.end_time = newlin.start_time + syl.duration local startalpha, targetalpha if j == 0 then startalpha = 0 targetalpha = 255 else startalpha = j * 255 / 8 targetalpha = (j-1) * 255 / 8 end newlin.text = string.format("{\\fn%s\\an7\\fr-90\\move(%d,%d,%d,%d)\\1a&H%2x&\\3a&H%2x&\\t(\\1a&H%2x&\\3a&H%2x&)}%s", fontname, 620, top, 620, top-syl.height, startalpha, startalpha, targetalpha, targetalpha, syls[i+j].text) top = top + syls[i+j].height table.insert(output, newlin) end end end