Aegisub/automation/demos/auto3/7-advanced-effect.lua

202 lines
7.8 KiB
Lua

-- 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
output.n = output.n + 1
output[output.n] = 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)
output.n = output.n + 1
output[output.n] = 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
output.n = output.n + 2
output[output.n-1] = bord
output[output.n] = 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)
output.n = output.n + 2
output[output.n-1] = tophalf
output[output.n] = 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
output.n = output.n + 1
output[output.n] = newlin
end
end
end