2024-12-17 21:14:21 +10:30
|
|
|
# Substation Alpha (ASS) generation
|
|
|
|
|
|
|
|
# Colour values are &HAABBGGRR, &HBBGGRR, or &HAA.
|
|
|
|
# Alpha is actually inverted, i.e. transparency - FF is transparent, 00 is opaque.
|
|
|
|
|
|
|
|
ass_preamble = '''[Script Info]
|
|
|
|
ScriptType: v4.00+
|
|
|
|
WrapStyle: 0
|
|
|
|
ScaledBorderAndShadow: yes
|
|
|
|
YCbCr Matrix: TV.709
|
|
|
|
PlayResX: {PlayResX}
|
|
|
|
PlayResY: {PlayResY}
|
|
|
|
|
|
|
|
[V4+ Styles]
|
|
|
|
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
|
2024-12-18 20:54:50 +10:30
|
|
|
Style: Default,{LatinFont},72,&H002A0A00,&H000019FF,&H00FFFFFF,&H00000000,0,0,0,0,100,100,0,0,1,2.5,0,8,30,30,25,1
|
|
|
|
Style: Kanji,{JapaneseFont},{KanjiSize},&H{KaraokeColourPast},&H{KaraokeColourFuture},&H00FFFFFF,&H00000000,0,0,0,0,100,100,0,0,1,4.0,0,2,30,30,{KanjiVMargin},1
|
|
|
|
Style: Furigana,{JapaneseFont},{FuriSize},&H{KaraokeColourPast},&H{KaraokeColourFuture},&H00FFFFFF,&H00000000,0,0,0,0,100,100,0,0,1,2.5,0,2,0,0,{FuriVMargin},1
|
|
|
|
Style: Romaji,{LatinFont},{RomajiSize},&H{KaraokeColourPast},&H{KaraokeColourFuture},&H00FFFFFF,&H00000000,0,0,0,0,100,100,0,0,1,2.5,0,8,30,30,20,1
|
|
|
|
Style: Translation,{LatinFont},{TranslationSize},&H00FFFFFF,&H000019FF,&H00000000,&H00000000,0,1,0,0,100,100,0,0,1,1.0,3,8,30,30,20,1
|
2024-12-17 21:14:21 +10:30
|
|
|
|
|
|
|
[Events]
|
|
|
|
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
|
|
|
|
'''
|
|
|
|
|
|
|
|
format_defaults = {
|
|
|
|
'PlayResX': 1280,
|
|
|
|
'PlayResY': 720,
|
2024-12-18 20:54:50 +10:30
|
|
|
'LatinFont': 'Droid Sans',
|
|
|
|
'JapaneseFont': 'Droid Sans Japanese',
|
|
|
|
'TranslationSize': 36,
|
|
|
|
'RomajiSize': 48,
|
2024-12-17 21:14:21 +10:30
|
|
|
'KanjiSize': 72,
|
2024-12-18 00:53:00 +10:30
|
|
|
'KanjiVMargin': 20,
|
2024-12-17 21:14:21 +10:30
|
|
|
'FuriSize': 36,
|
2024-12-18 00:53:00 +10:30
|
|
|
'FuriVMargin': 85,
|
2024-12-17 21:14:21 +10:30
|
|
|
'KaraokeColourFuture': '000019FF',
|
|
|
|
'KaraokeColourPast': 'E02A0A00',
|
|
|
|
}
|
|
|
|
|
2024-12-17 23:55:12 +10:30
|
|
|
def seconds_to_timestamp(t: float) -> str:
|
|
|
|
minutes, seconds = divmod(t, 60)
|
|
|
|
hours, minutes = divmod(minutes, 60)
|
|
|
|
return f'{int(hours):02}:{int(minutes):02}:{seconds:05.2f}'
|
|
|
|
|
2024-12-17 21:14:21 +10:30
|
|
|
from format import LyricTrack
|
2024-12-17 23:55:12 +10:30
|
|
|
from japanese_converters import kana_to_syllable_list
|
2024-12-17 21:14:21 +10:30
|
|
|
def generate_ass(filename: str, lyric_track: LyricTrack, format_overloads: dict = None):
|
|
|
|
format_dict = format_defaults.copy()
|
|
|
|
if format_overloads:
|
|
|
|
format_dict.update(format_overloads)
|
2024-12-17 23:55:12 +10:30
|
|
|
preamble = ass_preamble.format(**format_dict)
|
2024-12-17 21:14:21 +10:30
|
|
|
|
|
|
|
# Kanji Furigana layout stuff
|
2024-12-18 00:53:00 +10:30
|
|
|
pt_to_fullwidth = 55.0/72.0 # This seems right for DroidSansJapanese, probably different for each font
|
|
|
|
size_kanji_x = format_dict['KanjiSize'] * pt_to_fullwidth
|
|
|
|
size_furi_x = format_dict['FuriSize'] * pt_to_fullwidth
|
2024-12-17 23:55:12 +10:30
|
|
|
res_x = format_dict['PlayResX']
|
2024-12-18 00:53:00 +10:30
|
|
|
res_xh = res_x/2
|
2024-12-17 21:14:21 +10:30
|
|
|
|
|
|
|
with open(filename, 'w') as file:
|
|
|
|
file.write(preamble)
|
2024-12-17 23:55:12 +10:30
|
|
|
t = 68.0 # placeholder
|
|
|
|
for line in lyric_track.lines:
|
|
|
|
t0 = seconds_to_timestamp(t)
|
|
|
|
timestamps = line.get_timestamps(0.5, t)
|
|
|
|
centiseconds = line.get_karaoke_centiseconds(0.5)
|
|
|
|
t = timestamps[-1] + 1.0 # placeholder
|
|
|
|
t1 = seconds_to_timestamp(t)
|
|
|
|
sub_preamble = f'Dialogue: 0,{t0},{t1}'
|
2024-12-18 00:53:00 +10:30
|
|
|
furi_preamble = f'Dialogue: 1,{t0},{t1}' # Different layer to avoid kanji collision detection
|
2024-12-17 23:55:12 +10:30
|
|
|
|
|
|
|
# Translation line is easy and static
|
|
|
|
file.write(f'{sub_preamble},Translation,,,,,,{line.translated_line}\n')
|
|
|
|
|
|
|
|
# Romaji line is also easy, just intersperse durations
|
|
|
|
romaji_line = f'{{\\k{centiseconds[0]}}}'
|
|
|
|
i = 0 # syllable counter
|
|
|
|
for syl in line.romaji_syllables:
|
|
|
|
if not syl.strip():
|
|
|
|
romaji_line += f'{{\\k0}}{syl}'
|
|
|
|
continue
|
|
|
|
romaji_line += f'{{\\K{centiseconds[i+1]-centiseconds[i]}}}{syl}'
|
|
|
|
i += 1
|
|
|
|
file.write(f'{sub_preamble},Romaji,,,,,,{romaji_line}\n')
|
|
|
|
|
|
|
|
# Now for the kanji and furi lines...
|
|
|
|
kanji_plain_str = ''.join([b.kanji for b in line.furi_blocks])
|
|
|
|
full_kanji_width = len(kanji_plain_str) * size_kanji_x
|
|
|
|
kanji_line = f'{{\\k{centiseconds[0]}}}'
|
|
|
|
kanji_line_progress = 0 # increment as we go, to track furi position
|
|
|
|
furi_lines = []
|
|
|
|
i = 0 # syllable counter
|
|
|
|
for furi_block in line.furi_blocks:
|
|
|
|
if len(furi_block.furi) == 0: # kana or punctuation, nice and simple!
|
|
|
|
syls = kana_to_syllable_list(furi_block.kanji)
|
|
|
|
for syl in syls:
|
|
|
|
if len(syl.strip()) == 0: # don't time spaces
|
|
|
|
kanji_line += f'{{\\k0}}{syl}'
|
|
|
|
kanji_line_progress += len(syl)
|
|
|
|
else:
|
|
|
|
kanji_line += f'{{\\K{centiseconds[i+1]-centiseconds[i]}}}{syl}'
|
|
|
|
kanji_line_progress += len(syl)
|
|
|
|
i += 1
|
|
|
|
else: # Kanji block
|
|
|
|
i0 = i # Store this to later calculate block time for the kanji
|
|
|
|
syls = kana_to_syllable_list(furi_block.furi)
|
|
|
|
furi_line = f'{{\\k{centiseconds[i]}}}'
|
|
|
|
furi_chars = 0
|
|
|
|
for syl in syls:
|
|
|
|
furi_line += f'{{\\K{centiseconds[i+1]-centiseconds[i]}}}{syl}'
|
|
|
|
furi_chars += len(syl)
|
|
|
|
i += 1
|
|
|
|
# Need to calculate kanji block position and span to typeset the furigana above it
|
|
|
|
k = furi_block.kanji
|
|
|
|
k_start = kanji_line_progress
|
|
|
|
kanji_line_progress += len(k)
|
|
|
|
k_end = kanji_line_progress
|
|
|
|
target_middle_x = (size_kanji_x * (k_end+k_start)/2) - (full_kanji_width/2) # x=0 at center
|
|
|
|
furi_width = furi_chars * size_furi_x
|
2024-12-18 00:53:00 +10:30
|
|
|
margin_l = int(res_xh+target_middle_x)
|
|
|
|
margin_r = int(res_xh-target_middle_x)
|
|
|
|
furi_lines.append(f'{furi_preamble},Furigana,,{margin_l},{margin_r},,,{furi_line}\n')
|
2024-12-17 23:55:12 +10:30
|
|
|
kanji_line += f'{{\\K{centiseconds[i]-centiseconds[i0]}}}{k}'
|
|
|
|
file.write(f'{sub_preamble},Kanji,,,,,,{kanji_line}\n')
|
|
|
|
for line in furi_lines:
|
|
|
|
file.write(line)
|