void AssKaraoke::RemoveSplit(size_t syl_idx) { // Don't allow removing the first syllable if (syl_idx == 0) return; Syllable &syl = syls[syl_idx]; Syllable &prev = syls[syl_idx - 1]; prev.duration += syl.duration; for (auto const& tag : syl.ovr_tags) prev.ovr_tags[tag.first + prev.text.size()] = tag.second; prev.text += syl.text; syls.erase(syls.begin() + syl_idx); if (!no_announce) AnnounceSyllablesChanged(); }
void AssKaraoke::SetLine(AssDialogue *line, bool auto_split, bool normalize) { active_line = line; syls.clear(); Syllable syl; syl.start_time = line->Start; syl.duration = 0; syl.tag_type = "\\k"; ParseSyllables(line, syl); if (normalize) { // Normalize the syllables so that the total duration is equal to the line length int end_time = active_line->End; int last_end = syl.start_time + syl.duration; // Total duration is shorter than the line length so just extend the last // syllable; this has no effect on rendering but is easier to work with if (last_end < end_time) syls.back().duration += end_time - last_end; else if (last_end > end_time) { // Truncate any syllables that extend past the end of the line for (auto& syl : syls) { if (syl.start_time > end_time) { syl.start_time = end_time; syl.duration = 0; } else { syl.duration = std::min(syl.duration, end_time - syl.start_time); } } } } // Add karaoke splits at each space if (auto_split && syls.size() == 1) { size_t pos; no_announce = true; while ((pos = syls.back().text.find(' ')) != std::string::npos) AddSplit(syls.size() - 1, pos + 1); no_announce = false; } AnnounceSyllablesChanged(); }
void AssKaraoke::AddSplit(size_t syl_idx, size_t pos) { syls.insert(syls.begin() + syl_idx + 1, Syllable()); Syllable &syl = syls[syl_idx]; Syllable &new_syl = syls[syl_idx + 1]; // If the syl is empty or the user is adding a syllable past the last // character then pos will be out of bounds. Doing this is a bit goofy, // but it's sometimes required for complex karaoke scripts if (pos < syl.text.size()) { new_syl.text = syl.text.substr(pos); syl.text = syl.text.substr(0, pos); } if (new_syl.text.empty()) new_syl.duration = 0; else if (syl.text.empty()) { new_syl.duration = syl.duration; syl.duration = 0; } else { new_syl.duration = (syl.duration * new_syl.text.size() / (syl.text.size() + new_syl.text.size()) + 5) / 10 * 10; syl.duration -= new_syl.duration; } assert(syl.duration >= 0); new_syl.start_time = syl.start_time + syl.duration; new_syl.tag_type = syl.tag_type; // Move all override tags after the split to the new syllable and fix the indices size_t text_len = syl.text.size(); for (auto it = syl.ovr_tags.begin(); it != syl.ovr_tags.end(); ) { if (it->first < text_len) ++it; else { new_syl.ovr_tags[it->first - text_len] = it->second; syl.ovr_tags.erase(it++); } } if (!no_announce) AnnounceSyllablesChanged(); }