Subtitle pre-empting and crossfades. I'm happy with this output for now.
This commit is contained in:
parent
ece3fab18e
commit
4b1af657f7
|
@ -248,6 +248,7 @@ function load_song(data) {
|
|||
|
||||
// lyrics_input_updated()
|
||||
lyrics_input_element.dispatchEvent(new Event('change', {}));
|
||||
// generate_subtitles();
|
||||
}
|
||||
document.getElementById('load_song').addEventListener('change', event => {
|
||||
console.log('Attempting to load json file');
|
||||
|
@ -265,24 +266,34 @@ btn_download_subtitles.addEventListener('click', () => {
|
|||
import generate_subtitles_from_data from './subtitle_generator.js'
|
||||
function generate_subtitles() {
|
||||
console.log('Attempting to generate subtitles');
|
||||
// Strip out empties/labels from line numbers, only looking at lines and rests
|
||||
let arr_line_numbers = arrangement_line_numbers.filter(l => {
|
||||
if (l < 0) return true; // Rest
|
||||
if ((typeof tokenized_lyric_lines?.[l] ?? '') == "string") return false; // Label line
|
||||
return true
|
||||
});
|
||||
|
||||
// Grab manual line timestamps
|
||||
const manual_line_timecodes = line_timer_element.value.split('\n').filter(s=>s.trim()).map(s=>parseFloat(s.split('\t')[0]));
|
||||
const placeholder_t0 = 72.0;
|
||||
const seconds_per_line = 10.0;
|
||||
const placeholder_line_timecodes = Array(arrangement_line_numbers.length+1).fill().map((_,i) => Math.floor(i*seconds_per_line + placeholder_t0));
|
||||
const placeholder_line_timecodes = Array(arr_line_numbers.length+1).fill().map((_,i) => Math.floor(i*seconds_per_line + placeholder_t0));
|
||||
// Grab syllable beat counts
|
||||
const line_syllable_timings = lyrics_timing_input_element.value.split('\n').map(l => l.split(' ').filter(c=>c).map(c=>parseInt(c)));
|
||||
const line_syllable_total_beats = line_syllable_timings.map(l=>l.reduce((acc,x)=>acc+x));
|
||||
|
||||
let lines = [];
|
||||
// let t0 = manual_line_timecodes?.[0] ?? placeholder_line_timecodes[0];
|
||||
const early_fade_beats = 2.4;
|
||||
const early_fadein_beats = 3.0;
|
||||
|
||||
const tl_lines = lyrics_tl_input_element.value.split('\n');
|
||||
let out_idx = 0;
|
||||
arrangement_line_numbers.forEach(src_line_idx => {
|
||||
const t0 = manual_line_timecodes?.[out_idx] ?? placeholder_line_timecodes[out_idx];
|
||||
const t1 = manual_line_timecodes?.[out_idx+1] ?? placeholder_line_timecodes[out_idx+1];
|
||||
const N = arr_line_numbers.length;
|
||||
for (let i = 0; i < N; i++) {
|
||||
const src_line_idx = arr_line_numbers[i];
|
||||
let t0 = manual_line_timecodes?.[out_idx] ?? placeholder_line_timecodes[out_idx];
|
||||
let t1 = manual_line_timecodes?.[out_idx+1] ?? placeholder_line_timecodes[out_idx+1];
|
||||
const duration = t1-t0;
|
||||
const total_beats = (src_line_idx<0) ? (-src_line_idx) : (line_syllable_total_beats?.[src_line_idx] ?? 1);
|
||||
const bpm = 60.0*total_beats/duration;
|
||||
|
@ -290,14 +301,28 @@ function generate_subtitles() {
|
|||
console.log(src_line_idx, out_idx, t0, t1, total_beats, bpm, spb);
|
||||
|
||||
const tokenized_line = tokenized_lyric_lines[src_line_idx];
|
||||
if ((typeof tokenized_line) == "string") return; // Label line
|
||||
if ((typeof tokenized_line) == "string") continue; // Label line
|
||||
if (src_line_idx < 0) { // Rest beats - still increment the output line
|
||||
out_idx++;
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Peek next line to see if we should cut out early or extend into a rest/instrumental
|
||||
const next_line_num = arr_line_numbers?.[i+1] ?? -16; // Default to nice long rest at end of song
|
||||
if (next_line_num < 0) {
|
||||
t1 += (-next_line_num)*spb/4; // Extend 1/4 into the rest
|
||||
} else {
|
||||
t1 -= spb*early_fade_beats; // Back off early to make room
|
||||
}
|
||||
// Peek previous line to see how early we can fade in
|
||||
const prev_line_num = arr_line_numbers?.[i-1] ?? -16; // Default to nice long rest at start of song
|
||||
const early_fade_in_b = (prev_line_num < 0)
|
||||
? ((-prev_line_num)/8) // Extend 1/8 into the rest
|
||||
: (early_fadein_beats) // Jump in early
|
||||
t0 -= early_fade_in_b * spb;
|
||||
|
||||
let arr_syllable_cs = [];
|
||||
let acc = 0;
|
||||
let acc = early_fade_in_b;
|
||||
line_syllable_timings?.[src_line_idx]?.forEach(b => {
|
||||
acc += b;
|
||||
arr_syllable_cs.push(Math.floor(100*acc*spb));
|
||||
|
@ -310,7 +335,7 @@ function generate_subtitles() {
|
|||
translated_line: tl_lines?.[src_line_idx] ?? '',
|
||||
}, tokenized_line));
|
||||
out_idx++;
|
||||
});
|
||||
}
|
||||
|
||||
const subtitles = generate_subtitles_from_data({
|
||||
song_title: document.getElementById('song_title').value,
|
||||
|
@ -319,7 +344,8 @@ function generate_subtitles() {
|
|||
song_lyricist_en: document.getElementById('song_lyricist_en').value,
|
||||
song_composer: document.getElementById('song_composer').value,
|
||||
song_composer_en: document.getElementById('song_composer_en').value,
|
||||
title_fade_duration_ms: 2500,
|
||||
title_fade_duration_ms: 4000,
|
||||
lyric_fade_duration_ms: 275,
|
||||
lines: lines,
|
||||
});
|
||||
subtitle_editor_textarea.value = subtitles;
|
||||
|
|
|
@ -87,7 +87,9 @@ Dialogue: 0,${t0s},${t1s},Title,,,,,,${title_fade}${data.song_title_en}\\NMusic:
|
|||
const res_xh = res_x/2;
|
||||
|
||||
// Add the lines
|
||||
let layer = 0; // Flip this every line so we can crossfade them instead of push them away
|
||||
data.lines?.forEach(line => {
|
||||
layer = 1-layer;
|
||||
const t0 = line.t0;
|
||||
const t1 = line.t1;
|
||||
const t0s = timecode(t0);
|
||||
|
@ -98,8 +100,8 @@ Dialogue: 0,${t0s},${t1s},Title,,,,,,${title_fade}${data.song_title_en}\\NMusic:
|
|||
const fallback_syl_duration_cs = duration_cs/num_syllables;
|
||||
const centiseconds = line.arr_syllables_cs ?? Array(num_syllables+1).fill().map((_,i) => Math.floor(i*fallback_syl_duration_cs));
|
||||
|
||||
const sub_preamble = `Dialogue: 0,${t0s},${t1s}`;
|
||||
const furi_preamble = `Dialogue: 1,${t0s},${t1s}`; // Different layer to avoid kanji collision detection
|
||||
const sub_preamble = `Dialogue: ${layer},${t0s},${t1s}`;
|
||||
const furi_preamble = `Dialogue: ${layer+2},${t0s},${t1s}`; // Different layer to avoid kanji collision detection
|
||||
|
||||
// Translation line is easy and static
|
||||
s += `${sub_preamble},Translation,,,,,,${lyric_fade}${line.translated_line}\n`;
|
||||
|
|
Loading…
Reference in New Issue