mirror of https://github.com/odrling/Aegisub
190 lines
7.3 KiB
Lua
190 lines
7.3 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.
|
|
|
|
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
|