Implemented new karaoke splitter. Still needs some work, especially graphically.

The timing postproc dialog is now properly centered.

Originally committed to SVN as r29.
This commit is contained in:
Niels Martin Hansen 2006-01-27 00:48:59 +00:00
parent c6fb2bdf42
commit 3ad6a9db43
7 changed files with 247 additions and 91 deletions

View file

@ -1,4 +1,4 @@
// Copyright (c) 2005, Rodrigo Braz Monteiro // Copyright (c) 2005, Rodrigo Braz Monteiro, Niels Martin Hansen
// All rights reserved. // All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without // Redistribution and use in source and binary forms, with or without
@ -171,9 +171,10 @@ wxPanel(parent,-1,wxDefaultPosition,wxDefaultSize,wxTAB_TRAVERSAL|wxBORDER_RAISE
JoinButton->SetToolTip(_("Join selected syllables")); JoinButton->SetToolTip(_("Join selected syllables"));
JoinButton->Enable(false); JoinButton->Enable(false);
karaokeSizer->Add(JoinButton,0,wxRIGHT,0); karaokeSizer->Add(JoinButton,0,wxRIGHT,0);
SplitButton = new wxButton(this,Audio_Button_Split,_("Split"),wxDefaultPosition,wxSize(-1,-1)); SplitButton = new wxToggleButton(this,Audio_Button_Split,_("Split"),wxDefaultPosition,wxSize(-1,-1));
SplitButton->SetToolTip(_("Split selected syllables")); SplitButton->SetToolTip(_("Toggle splitting-mode"));
SplitButton->Enable(false); SplitButton->Enable(false);
SplitButton->SetValue(false);
karaokeSizer->Add(SplitButton,0,wxRIGHT,5); karaokeSizer->Add(SplitButton,0,wxRIGHT,5);
karaokeSizer->Add(audioKaraoke,1,wxEXPAND,0); karaokeSizer->Add(audioKaraoke,1,wxEXPAND,0);
@ -234,12 +235,12 @@ BEGIN_EVENT_TABLE(AudioBox,wxPanel)
EVT_BUTTON(Audio_Button_Commit, AudioBox::OnCommit) EVT_BUTTON(Audio_Button_Commit, AudioBox::OnCommit)
EVT_BUTTON(Audio_Button_Goto, AudioBox::OnGoto) EVT_BUTTON(Audio_Button_Goto, AudioBox::OnGoto)
EVT_BUTTON(Audio_Button_Join,AudioBox::OnJoin) EVT_BUTTON(Audio_Button_Join,AudioBox::OnJoin)
EVT_BUTTON(Audio_Button_Split,AudioBox::OnSplit)
EVT_BUTTON(Audio_Button_Leadin,AudioBox::OnLeadIn) EVT_BUTTON(Audio_Button_Leadin,AudioBox::OnLeadIn)
EVT_BUTTON(Audio_Button_Leadout,AudioBox::OnLeadOut) EVT_BUTTON(Audio_Button_Leadout,AudioBox::OnLeadOut)
EVT_TOGGLEBUTTON(Audio_Button_Karaoke, AudioBox::OnKaraoke) EVT_TOGGLEBUTTON(Audio_Button_Karaoke, AudioBox::OnKaraoke)
EVT_TOGGLEBUTTON(Audio_Check_AutoGoto,AudioBox::OnAutoGoto) EVT_TOGGLEBUTTON(Audio_Check_AutoGoto,AudioBox::OnAutoGoto)
EVT_TOGGLEBUTTON(Audio_Button_Split,AudioBox::OnSplit)
EVT_TOGGLEBUTTON(Audio_Check_SSA,AudioBox::OnSSAMode) EVT_TOGGLEBUTTON(Audio_Check_SSA,AudioBox::OnSSAMode)
EVT_TOGGLEBUTTON(Audio_Check_Spectrum,AudioBox::OnSpectrumMode) EVT_TOGGLEBUTTON(Audio_Check_Spectrum,AudioBox::OnSpectrumMode)
EVT_TOGGLEBUTTON(Audio_Check_AutoCommit,AudioBox::OnAutoCommit) EVT_TOGGLEBUTTON(Audio_Check_AutoCommit,AudioBox::OnAutoCommit)
@ -427,6 +428,9 @@ void AudioBox::OnCommit(wxCommandEvent &event) {
void AudioBox::OnKaraoke(wxCommandEvent &event) { void AudioBox::OnKaraoke(wxCommandEvent &event) {
audioDisplay->SetFocus(); audioDisplay->SetFocus();
if (karaokeMode) { if (karaokeMode) {
if (audioKaraoke->splitting) {
audioKaraoke->EndSplit(false);
}
karaokeMode = false; karaokeMode = false;
audioKaraoke->enabled = false; audioKaraoke->enabled = false;
SetKaraokeButtons(false,false); SetKaraokeButtons(false,false);
@ -447,8 +451,14 @@ void AudioBox::OnKaraoke(wxCommandEvent &event) {
// Sets karaoke buttons // Sets karaoke buttons
void AudioBox::SetKaraokeButtons(bool join,bool split) { void AudioBox::SetKaraokeButtons(bool join,bool split) {
audioDisplay->SetFocus(); audioDisplay->SetFocus();
JoinButton->Enable(join); JoinButton->Enable(join && !audioKaraoke->splitting);
SplitButton->Enable(split); SplitButton->Enable(split);
SplitButton->SetValue(audioKaraoke->splitting);
if (audioKaraoke->splitting) {
SplitButton->SetLabel(_("Cancel Split"));
} else {
SplitButton->SetLabel(_("Split"));
}
} }
@ -464,7 +474,11 @@ void AudioBox::OnJoin(wxCommandEvent &event) {
// Split button // Split button
void AudioBox::OnSplit(wxCommandEvent &event) { void AudioBox::OnSplit(wxCommandEvent &event) {
audioDisplay->SetFocus(); audioDisplay->SetFocus();
audioKaraoke->Split(); if (!audioKaraoke->splitting) {
audioKaraoke->BeginSplit();
} else {
audioKaraoke->EndSplit(false);
}
} }

View file

@ -69,7 +69,7 @@ private:
wxSizer *DisplaySizer; wxSizer *DisplaySizer;
wxSashWindow *Sash; wxSashWindow *Sash;
wxButton *SplitButton; wxToggleButton *SplitButton;
wxButton *JoinButton; wxButton *JoinButton;
ToggleBitmap *AutoScroll; ToggleBitmap *AutoScroll;
ToggleBitmap *SSAMode; ToggleBitmap *SSAMode;

View file

@ -979,7 +979,9 @@ void AudioDisplay::CommitChanges () {
// Update karaoke // Update karaoke
int karSyl = 0; int karSyl = 0;
bool wasKaraSplitting = false;
if (karaoke->enabled) { if (karaoke->enabled) {
wasKaraSplitting = box->audioKaraoke->splitting;
karaoke->Commit(); karaoke->Commit();
karSyl = karaoke->curSyllable; karSyl = karaoke->curSyllable;
} }
@ -996,8 +998,8 @@ void AudioDisplay::CommitChanges () {
karaoke->curSyllable = karSyl; karaoke->curSyllable = karSyl;
blockUpdate = false; blockUpdate = false;
// If in SSA mode, select next line and "move timing forward" // If in SSA mode, select next line and "move timing forward" (unless the user was splitting karaoke)
if (Options.AsBool(_T("Audio SSA Mode")) && Options.AsBool(_T("Audio SSA Next Line on Commit"))) { if (Options.AsBool(_T("Audio SSA Mode")) && Options.AsBool(_T("Audio SSA Next Line on Commit")) && !wasKaraSplitting) {
dontReadTimes = true; dontReadTimes = true;
Next(); Next();
dontReadTimes = false; dontReadTimes = false;

View file

@ -1,4 +1,4 @@
// Copyright (c) 2005, Rodrigo Braz Monteiro // Copyright (c) 2005, Rodrigo Braz Monteiro, Niels Martin Hansen
// All rights reserved. // All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without // Redistribution and use in source and binary forms, with or without
@ -42,6 +42,7 @@
#include "audio_box.h" #include "audio_box.h"
#include "ass_dialogue.h" #include "ass_dialogue.h"
#include "ass_override.h" #include "ass_override.h"
#include <algorithm>
//////////////////////// ////////////////////////
@ -51,6 +52,7 @@ KaraokeSyllable::KaraokeSyllable() {
position = 0; position = 0;
display_w = 0; display_w = 0;
display_x = 0; display_x = 0;
pending_splits.clear();
selected = false; selected = false;
} }
@ -61,6 +63,8 @@ AudioKaraoke::AudioKaraoke(wxWindow *parent)
: wxWindow (parent,-1,wxDefaultPosition,wxSize(10,5),wxTAB_TRAVERSAL|wxBORDER_SUNKEN) : wxWindow (parent,-1,wxDefaultPosition,wxSize(10,5),wxTAB_TRAVERSAL|wxBORDER_SUNKEN)
{ {
enabled = false; enabled = false;
splitting = false;
split_cursor_syl = -1;
curSyllable = 0; curSyllable = 0;
diag = NULL; diag = NULL;
} }
@ -69,6 +73,11 @@ AudioKaraoke::AudioKaraoke(wxWindow *parent)
////////////////////// //////////////////////
// Load from dialogue // Load from dialogue
bool AudioKaraoke::LoadFromDialogue(AssDialogue *_diag) { bool AudioKaraoke::LoadFromDialogue(AssDialogue *_diag) {
// Make sure we're not in splitting-mode
if (splitting) {
EndSplit(false);
}
// Set dialogue // Set dialogue
diag = _diag; diag = _diag;
if (!diag) { if (!diag) {
@ -117,6 +126,9 @@ wxString AudioKaraoke::GetSyllableTag(AssDialogueBlockOverride *block,int n) {
//////////////////// ////////////////////
// Writes line back // Writes line back
void AudioKaraoke::Commit() { void AudioKaraoke::Commit() {
if (splitting) {
EndSplit(true);
}
wxString finalText = _T(""); wxString finalText = _T("");
KaraokeSyllable *syl; KaraokeSyllable *syl;
size_t n = syllables.size(); size_t n = syllables.size();
@ -255,7 +267,6 @@ void AudioKaraoke::OnPaint(wxPaintEvent &event) {
// Set syllable font // Set syllable font
wxFont curFont(9,wxFONTFAMILY_DEFAULT,wxFONTSTYLE_NORMAL,wxFONTWEIGHT_NORMAL,false,_T("Verdana"),wxFONTENCODING_SYSTEM); wxFont curFont(9,wxFONTFAMILY_DEFAULT,wxFONTSTYLE_NORMAL,wxFONTWEIGHT_NORMAL,false,_T("Verdana"),wxFONTENCODING_SYSTEM);
dc.SetFont(curFont); dc.SetFont(curFont);
dc.SetPen(wxPen(wxColour(0,0,0)));
// Draw syllables // Draw syllables
if (enabled) { if (enabled) {
@ -266,17 +277,22 @@ void AudioKaraoke::OnPaint(wxPaintEvent &event) {
int delta; int delta;
int dlen; int dlen;
for (size_t i=0;i<syln;i++) { for (size_t i=0;i<syln;i++) {
KaraokeSyllable &syl = syllables.at(i);
// Calculate text length // Calculate text length
temptext = syllables.at(i).contents; temptext = syl.contents;
// If we're splitting, every character must be drawn
if (!splitting) {
temptext.Trim(true); temptext.Trim(true);
temptext.Trim(false); temptext.Trim(false);
}
GetTextExtent(temptext,&tw,&th,NULL,NULL,&curFont); GetTextExtent(temptext,&tw,&th,NULL,NULL,&curFont);
delta = 0; delta = 0;
if (tw < 10) delta = 10 - tw; if (tw < 10) delta = 10 - tw;
dlen = tw + 8 + delta; dlen = tw + 8 + delta;
// Draw border // Draw border
if (syllables.at(i).selected) { dc.SetPen(wxPen(wxColour(0,0,0)));
if (syl.selected && !splitting) {
dc.SetBrush(wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT))); dc.SetBrush(wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT)));
dc.SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT)); dc.SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
} }
@ -287,11 +303,35 @@ void AudioKaraoke::OnPaint(wxPaintEvent &event) {
dc.DrawRectangle(dx,0,dlen,h); dc.DrawRectangle(dx,0,dlen,h);
// Draw text // Draw text
if (splitting) {
// Make sure the text position is more predictable in case of splitting
dc.DrawText(temptext,dx+4,(h-th)/2);
} else {
dc.DrawText(temptext,dx+(delta/2)+4,(h-th)/2); dc.DrawText(temptext,dx+(delta/2)+4,(h-th)/2);
}
// Draw pending splits
if (syl.pending_splits.size() > 0) {
wxArrayInt widths;
if (dc.GetPartialTextExtents(temptext, widths)) {
for (int i = 0; i < syl.pending_splits.size(); i++) {
dc.SetPen(wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT));
dc.DrawLine(dx+4+widths[syl.pending_splits[i]], 0, dx+4+widths[syl.pending_splits[i]], h);
}
} else {
wxLogError(_T("WTF? Failed to GetPartialTextExtents"));
}
}
if (splitting && split_cursor_syl == i && split_cursor_x > 0) {
dc.SetPen(*wxRED);
dc.DrawLine(dx+4+split_cursor_x, 0, dx+4+split_cursor_x, h);
dc.SetPen(wxPen(wxColour(0,0,0)));
}
// Set syllable data // Set syllable data
syllables.at(i).display_x = dx; syl.display_x = dx;
syllables.at(i).display_w = dlen; syl.display_w = dlen;
// Increment dx // Increment dx
dx += dlen; dx += dlen;
@ -319,6 +359,7 @@ void AudioKaraoke::OnMouse(wxMouseEvent &event) {
int y = event.GetY(); int y = event.GetY();
bool shift = event.m_shiftDown; bool shift = event.m_shiftDown;
if (!splitting) {
// Left button down // Left button down
if (event.LeftDown()) { if (event.LeftDown()) {
int syl = GetSylAtX(x); int syl = GetSylAtX(x);
@ -338,6 +379,81 @@ void AudioKaraoke::OnMouse(wxMouseEvent &event) {
} }
} }
} }
} else {
int syli = GetSylAtX(x);
if (syli != -1) {
KaraokeSyllable &syl = syllables.at(syli);
// Get the widths after each character in the text
wxClientDC dc(this);
wxFont curFont(9,wxFONTFAMILY_DEFAULT,wxFONTSTYLE_NORMAL,wxFONTWEIGHT_NORMAL,false,_T("Verdana"),wxFONTENCODING_SYSTEM);
dc.SetFont(curFont);
wxArrayInt widths;
dc.GetPartialTextExtents(syl.contents, widths);
// Find the character closest to the mouse
int rx = x - syl.display_x - 4;
int split_cursor_char = -1;
split_cursor_syl = -1;
split_cursor_x = -1;
if (syl.contents.Len() >= 2) {
int lastx = widths[0];
split_cursor_syl = syli;
for (int i = 1; i < widths.size()-1; i++) {
//wxLogDebug(_T("rx=%d, lastx=%d, widths[i]=%d, i=%d, widths.size()=%d, syli=%d"), rx, lastx, widths[i], i, widths.size(), syli);
if (lastx - rx < widths[i] - rx) {
if (rx - lastx < widths[i] - rx) {
//wxLogDebug(_T("Found at PREV!"));
split_cursor_x = lastx;
split_cursor_char = i - 1;
break;
} else if (rx < widths[i]) {
//wxLogDebug(_T("Found at CURRENT!"));
split_cursor_x = widths[i];
split_cursor_char = i;
break;
}
}
lastx = widths[i];
}
// If no split-point was caught by the for-loop, it must be over the last character,
// ie. split at next-to-last position
if (split_cursor_x < 0) {
//wxLogDebug(_T("Emergency picking LAST!"));
split_cursor_x = widths[widths.size()-2];
split_cursor_char = widths.size() - 2;
}
}
// Do something if there was a click and we're at a valid position
if (event.LeftDown() && split_cursor_char >= 0) {
//wxLogDebug(_T("A click!"));
int num_removed = 0;
std::vector<int>::iterator i = syl.pending_splits.begin();
while (i != syl.pending_splits.end()) {
if (split_cursor_char == *i) {
//wxLogDebug(_T("Erasing entry"));
num_removed++;
syl.pending_splits.erase(i);
} else {
i++;
}
}
if (num_removed == 0) {
syl.pending_splits.push_back(split_cursor_char);
}
}
} else {
split_cursor_syl = -1;
split_cursor_x = -1;
}
if (split_cursor_syl >= 0) {
Refresh(false);
}
}
} }
@ -424,84 +540,98 @@ void AudioKaraoke::Join() {
} }
/////////////////// ////////////////////////
// Split syllables // Enter splitting-mode
void AudioKaraoke::Split() { void AudioKaraoke::BeginSplit() {
// Variables splitting = true;
bool hasSplit = false; split_cursor_syl = -1;
split_cursor_x = -1;
box->SetKaraokeButtons(false, true);
Refresh(false);
}
// Loop
size_t syls = syllables.size(); ////////////////////////////////////////////
for (size_t i=0;i<syls;i++) { // Leave splitting-mode, committing changes
if (syllables.at(i).selected) { void AudioKaraoke::EndSplit(bool commit) {
int split = SplitSyl(i); splitting = false;
if (split > 0) { bool hasSplit = false;
syls += split; for (int i = 0; i < syllables.size(); i ++) {
i += split; if (syllables[i].pending_splits.size() > 0) {
if (commit) {
SplitSyl(i);
hasSplit = true; hasSplit = true;
} } else {
if (split == -1) break; syllables[i].pending_splits.clear();
} }
} }
}
SetSelection(0);
// Update // Update
if (hasSplit) { if (hasSplit) {
display->NeedCommit = true; display->NeedCommit = true;
display->Update(); display->Update();
}
// Always redraw, since the display is different in splitting mode
Refresh(false); Refresh(false);
} }
}
//////////////////// /////////////////////////////////////////////////
// Split a syllable // Split a syllable using the pending_slits data
int AudioKaraoke::SplitSyl (int n) { int AudioKaraoke::SplitSyl (int n) {
// Get split text syllables.reserve(syllables.size() + syllables[n].pending_splits.size());
KaraokeSyllable *curSyl = &syllables.at(n);
wxString result = wxGetTextFromUser(_("Enter pipes (\"|\") to split:"), _("Split syllable"), curSyl->contents);
if (result.IsEmpty()) return -1;
// Prepare parsing // Start by sorting the split points
const int splits = result.Freq(_T('|')); std::sort(syllables[n].pending_splits.begin(),syllables[n].pending_splits.end());
const int totalCharLen = curSyl->contents.Length() + splits + 1;
const int charRound = totalCharLen / 2;
const int totalTimeLen = curSyl->length;
int curpos = curSyl->position;
int curlen = 0;
wxStringTokenizer tkn(result,_T("|"),wxTOKEN_RET_EMPTY_ALL);
bool isFirst = true;
// Parse wxString originalText = syllables[n].contents;
for (int curn=n;tkn.HasMoreTokens();curn++) { int originalDuration = syllables[n].length;
// Prepare syllable
if (!isFirst) { // Fixup the first syllable
syllables[n].contents = originalText.Mid(0, syllables[n].pending_splits[0] + 1);
syllables[n].length = originalDuration * syllables[n].contents.Length() / originalText.Length();
int curpos = syllables[n].position + syllables[n].length;
// For each split, make a new syllable
for (int i = 0; i < syllables[n].pending_splits.size(); i++) {
KaraokeSyllable temp; KaraokeSyllable temp;
syllables.insert(syllables.begin()+curn,temp); if (i < syllables[n].pending_splits.size()-1) {
temp.selected = true; // in the middle
temp.contents = originalText.Mid(syllables[n].pending_splits[i]+1, syllables[n].pending_splits[i+1] - syllables[n].pending_splits[i]);
} else {
// the last one (take the rest)
temp.contents = originalText.Mid(syllables[n].pending_splits[i]+1);
} }
curSyl = &syllables.at(curn); temp.length = originalDuration * temp.contents.Length() / originalText.Length();
temp.position = curpos;
// Set text curpos += temp.length;
wxString token = tkn.GetNextToken(); syllables.insert(syllables.begin()+n+i+1, temp);
curSyl->contents = token;
// Set position
int len = (totalTimeLen * (token.Length() + 1) + charRound) / totalCharLen;
curlen += len;
if (curlen > totalTimeLen) {
len -= totalTimeLen - curlen;
curlen = totalTimeLen;
}
curSyl->length = len;
curSyl->position = curpos;
curpos += len;
// Done
isFirst = false;
} }
// Return splits // The total duration of the new syllables will be equal to or less than the original duration
return splits; // Fix this, so they'll always add up
// Use an unfair method, just adding 1 to each syllable one after another, until it's correct
int newDuration = 0;
for (int j = n; j < syllables[n].pending_splits.size()+n+1; j++) {
newDuration += syllables[j].length;
}
int k = n;
while (newDuration < originalDuration) {
syllables[k].length++;
k++;
if (k >= syllables.size()) {
k = n;
}
newDuration++;
}
// Prepare for return and clear pending splits
int numsplits = syllables[n].pending_splits.size();
syllables[n].pending_splits.clear();
return numsplits;
} }

View file

@ -64,6 +64,8 @@ public:
wxString tag; wxString tag;
bool selected; bool selected;
std::vector<int> pending_splits;
KaraokeSyllable(); KaraokeSyllable();
}; };
@ -80,6 +82,9 @@ private:
AssDialogue *diag; AssDialogue *diag;
int startClickSyl; int startClickSyl;
int split_cursor_syl;
int split_cursor_x;
int GetKaraokeLength(AssDialogueBlockOverride *block); int GetKaraokeLength(AssDialogueBlockOverride *block);
wxString GetSyllableTag(AssDialogueBlockOverride *block,int n); wxString GetSyllableTag(AssDialogueBlockOverride *block,int n);
void AutoSplit(); void AutoSplit();
@ -98,8 +103,8 @@ public:
int curSyllable; int curSyllable;
bool enabled; bool enabled;
bool splitting;
SylVector syllables; SylVector syllables;
SylVector origSyl;
AudioKaraoke(wxWindow *parent); AudioKaraoke(wxWindow *parent);
bool LoadFromDialogue(AssDialogue *diag); bool LoadFromDialogue(AssDialogue *diag);
@ -109,7 +114,8 @@ public:
bool SyllableDelta(int n,int delta,int mode); bool SyllableDelta(int n,int delta,int mode);
void Join(); void Join();
void Split(); void BeginSplit();
void EndSplit(bool commit=true);
DECLARE_EVENT_TABLE() DECLARE_EVENT_TABLE()
}; };

View file

@ -11,6 +11,8 @@ Please visit http://aegisub.net to download latest version
o Fixed bug, triggered when a line had a style not defined in the subs. A warning is now shown instead. o Fixed bug, triggered when a line had a style not defined in the subs. A warning is now shown instead.
- Fixed bug in parser that would cause Aegisub to crash if you had a \fn without parameters and tried to pick font. (AMZ) - Fixed bug in parser that would cause Aegisub to crash if you had a \fn without parameters and tried to pick font. (AMZ)
- Fixed crash when opening audio that appeared in 1.08 (Myrsloik) - Fixed crash when opening audio that appeared in 1.08 (Myrsloik)
- Implemented new graphical, mouse-controlled karaoke syllable splitter (jfs)
= 1.09 beta - 2006.01.16 =========================== = 1.09 beta - 2006.01.16 ===========================

View file

@ -157,6 +157,8 @@ DialogTimingProcessor::DialogTimingProcessor(wxWindow *parent,SubtitlesGrid *_gr
MainSizer->SetSizeHints(this); MainSizer->SetSizeHints(this);
SetSizer(MainSizer); SetSizer(MainSizer);
CenterOnParent();
// Update // Update
UpdateControls(); UpdateControls();
} }