diff options
Diffstat (limited to 'Medley/src/screens/UScreenEditSub.pas')
-rw-r--r-- | Medley/src/screens/UScreenEditSub.pas | 1520 |
1 files changed, 1520 insertions, 0 deletions
diff --git a/Medley/src/screens/UScreenEditSub.pas b/Medley/src/screens/UScreenEditSub.pas new file mode 100644 index 00000000..609a689b --- /dev/null +++ b/Medley/src/screens/UScreenEditSub.pas @@ -0,0 +1,1520 @@ +{* UltraStar Deluxe - Karaoke Game + * + * UltraStar Deluxe is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + *} + +unit UScreenEditSub; + +interface + +{$IFDEF FPC} + {$MODE Delphi} +{$ENDIF} +{$I switches.inc} + +uses + UMenu, + UMusic, + SDL, + SysUtils, + UFiles, + UTime, + USongs, + USong, + UIni, + ULog, + UTexture, + UMenuText, + UEditorLyrics, + Math, + gl, + {$IFDEF UseMIDIPort} + MidiOut, + {$ENDIF} + UThemes; + +type + TScreenEditSub = class(TMenu) + private + //Variable is True if no Song is loaded + Error: boolean; + + TextNote: integer; + TextSentence: integer; + TextTitle: integer; + TextArtist: integer; + TextMp3: integer; + TextBPM: integer; + TextGAP: integer; + TextDebug: integer; + TextNStart: integer; + TextNLength: integer; + TextNTon: integer; + TextNText: integer; + CurrentNote: integer; + PlaySentence: boolean; + PlaySentenceMidi: boolean; + PlayStopTime: real; + LastClick: integer; + Click: boolean; + CopySrc: integer; + + {$IFDEF UseMIDIPort} + MidiOut: TMidiOutput; + {$endif} + + MidiStart: real; + MidiStop: real; + MidiTime: real; + MidiPos: real; + MidiLastNote: integer; + + TextEditMode: boolean; + + Lyric: TEditorLyrics; + + procedure DivideBPM; + procedure MultiplyBPM; + procedure LyricsCapitalize; + procedure LyricsCorrectSpaces; + procedure FixTimings; + procedure DivideSentence; + procedure JoinSentence; + procedure DivideNote; + procedure DeleteNote; + procedure TransposeNote(Transpose: integer); + procedure ChangeWholeTone(Tone: integer); + procedure MoveAllToEnd(Move: integer); + procedure MoveTextToRight; + procedure MarkSrc; + procedure PasteText; + procedure CopySentence(Src, Dst: integer); + procedure CopySentences(Src, Dst, Num: integer); + //Note Name Mod + function GetNoteName(Note: integer): string; + public + Tex_Background: TTexture; + FadeOut: boolean; + constructor Create; override; + procedure OnShow; override; + function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override; + function ParseInputEditText(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; + function Draw: boolean; override; + procedure OnHide; override; + end; + +implementation + +uses + UGraphic, + UDraw, + UNote, + USkins, + ULanguage, + UTextEncoding, + UUnicodeUtils, + UPath; + + +procedure OnSaveEncodingError(Value: boolean; Data: Pointer); +var + SResult: TSaveSongResult; + FilePath: IPath; + Success: boolean; +begin + Success := false; + if (Value) then + begin + CurrentSong.Encoding := encUTF8; + FilePath := CurrentSong.Path.Append(CurrentSong.FileName); + // create backup file + FilePath.CopyFile(Path(FilePath.ToUTF8 + '.ansi.bak'), false); + // store in UTF-8 encoding + SResult := SaveSong(CurrentSong, Lines[0], FilePath, + boolean(Data)); + Success := (SResult = ssrOK); + end; + + if (Success) then + ScreenPopupInfo.ShowPopup(Language.Translate('INFO_FILE_SAVED')) + else + ScreenPopupError.ShowPopup(Language.Translate('ERROR_SAVE_FILE_FAILED')); +end; + +// Method for input parsing. If false is returned, GetNextWindow +// should be checked to know the next window to load; +function TScreenEditSub.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; +var + SDL_ModState: word; + R: real; + SResult: TSaveSongResult; +begin + Result := true; + + if TextEditMode then + begin + Result := ParseInputEditText(PressedKey, CharCode, PressedDown); + end + else + begin + + SDL_ModState := SDL_GetModState and (KMOD_LSHIFT + KMOD_RSHIFT + + KMOD_LCTRL + KMOD_RCTRL + KMOD_LALT + KMOD_RALT {+ KMOD_CAPS}); + + if (PressedDown) then // Key Down + begin + // check normal keys + case UCS4UpperCase(CharCode) of + Ord('Q'): + begin + Result := false; + Exit; + end; + Ord('S'): + begin + // Save Song + SResult := SaveSong(CurrentSong, Lines[0], CurrentSong.Path.Append(CurrentSong.FileName), + (SDL_ModState = KMOD_LSHIFT)); + if (SResult = ssrOK) then + begin + ScreenPopupInfo.ShowPopup(Language.Translate('INFO_FILE_SAVED')); + end + else if (SResult = ssrEncodingError) then + begin + ScreenPopupCheck.ShowPopup(Language.Translate('ENCODING_ERROR_ASK_FOR_UTF8'), OnSaveEncodingError, + Pointer(SDL_ModState = KMOD_LSHIFT), true); + end + else + begin + ScreenPopupError.ShowPopup(Language.Translate('ERROR_SAVE_FILE_FAILED')); + end; + Exit; + end; + Ord('D'): + begin + // Divide lengths by 2 + DivideBPM; + Exit; + end; + Ord('M'): + begin + // Multiply lengths by 2 + MultiplyBPM; + Exit; + end; + Ord('C'): + begin + // Capitalize letter at the beginning of line + if SDL_ModState = 0 then + LyricsCapitalize; + + // Correct spaces + if SDL_ModState = KMOD_LSHIFT then + LyricsCorrectSpaces; + + // Copy sentence + if SDL_ModState = KMOD_LCTRL then + MarkSrc; + + Exit; + end; + Ord('V'): + begin + // Paste text + if SDL_ModState = KMOD_LCTRL then + begin + if Lines[0].Line[Lines[0].Current].HighNote >= Lines[0].Line[CopySrc].HighNote then + PasteText + else + Log.LogStatus('PasteText: invalid range', 'TScreenEditSub.ParseInput'); + end; + + if SDL_ModState = KMOD_LCTRL + KMOD_LSHIFT then + begin + CopySentence(CopySrc, Lines[0].Current); + end; + end; + Ord('T'): + begin + // Fixes timings between sentences + FixTimings; + Exit; + end; + Ord('P'): + begin + if SDL_ModState = 0 then + begin + // Play Sentence + Click := true; + AudioPlayback.Stop; + R := GetTimeFromBeat(Lines[0].Line[Lines[0].Current].Note[0].Start); + if R <= AudioPlayback.Length then + begin + AudioPlayback.Position := R; + PlayStopTime := GetTimeFromBeat(Lines[0].Line[Lines[0].Current].End_); + PlaySentence := true; + AudioPlayback.Play; + LastClick := -100; + end; + end + else if SDL_ModState = KMOD_LSHIFT then + begin + PlaySentenceMidi := true; + + MidiTime := USTime.GetTime; + MidiStart := GetTimeFromBeat(Lines[0].Line[Lines[0].Current].Note[0].Start); + MidiStop := GetTimeFromBeat(Lines[0].Line[Lines[0].Current].End_); + + LastClick := -100; + end + else if SDL_ModState = KMOD_LSHIFT or KMOD_LCTRL then + begin + PlaySentenceMidi := true; + MidiTime := USTime.GetTime; + MidiStart := GetTimeFromBeat(Lines[0].Line[Lines[0].Current].Note[0].Start); + MidiStop := GetTimeFromBeat(Lines[0].Line[Lines[0].Current].End_); + LastClick := -100; + + PlaySentence := true; + Click := true; + AudioPlayback.Stop; + AudioPlayback.Position := GetTimeFromBeat(Lines[0].Line[Lines[0].Current].Note[0].Start)+0{-0.10}; + PlayStopTime := GetTimeFromBeat(Lines[0].Line[Lines[0].Current].End_)+0; + AudioPlayback.Play; + LastClick := -100; + end; + Exit; + end; + + // Golden Note + Ord('G'): + begin + if (Lines[0].Line[Lines[0].Current].Note[CurrentNote].NoteType = ntGolden) then + Lines[0].Line[Lines[0].Current].Note[CurrentNote].NoteType := ntNormal + else + Lines[0].Line[Lines[0].Current].Note[CurrentNote].NoteType := ntGolden; + + Exit; + end; + + // Freestyle Note + Ord('F'): + begin + if (Lines[0].Line[Lines[0].Current].Note[CurrentNote].NoteType = ntFreestyle) then + Lines[0].Line[Lines[0].Current].Note[CurrentNote].NoteType := ntNormal + else + Lines[0].Line[Lines[0].Current].Note[CurrentNote].NoteType := ntFreestyle; + + Exit; + end; + end; + + // check special keys + case PressedKey of + SDLK_ESCAPE, + SDLK_BACKSPACE : + begin + FadeTo(@ScreenSong); + end; + + SDLK_BACKQUOTE: + begin + // Increase Note Length (same as Alt + Right) + Inc(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Length); + if CurrentNote = Lines[0].Line[Lines[0].Current].HighNote then + Inc(Lines[0].Line[Lines[0].Current].End_); + end; + + SDLK_EQUALS: + begin + // Increase BPM + if SDL_ModState = 0 then + CurrentSong.BPM[0].BPM := Round((CurrentSong.BPM[0].BPM * 5) + 1) / 5; // (1/20) + if SDL_ModState = KMOD_LSHIFT then + CurrentSong.BPM[0].BPM := CurrentSong.BPM[0].BPM + 4; // (1/1) + if SDL_ModState = KMOD_LCTRL then + CurrentSong.BPM[0].BPM := Round((CurrentSong.BPM[0].BPM * 25) + 1) / 25; // (1/100) + end; + + SDLK_MINUS: + begin + // Decrease BPM + if SDL_ModState = 0 then + CurrentSong.BPM[0].BPM := Round((CurrentSong.BPM[0].BPM * 5) - 1) / 5; + if SDL_ModState = KMOD_LSHIFT then + CurrentSong.BPM[0].BPM := CurrentSong.BPM[0].BPM - 4; + if SDL_ModState = KMOD_LCTRL then + CurrentSong.BPM[0].BPM := Round((CurrentSong.BPM[0].BPM * 25) - 1) / 25; + end; + + SDLK_4: + begin + if SDL_ModState = KMOD_LCTRL + KMOD_LSHIFT then + begin + CopySentence(CopySrc, Lines[0].Current); + CopySentence(CopySrc+1, Lines[0].Current+1); + CopySentence(CopySrc+2, Lines[0].Current+2); + CopySentence(CopySrc+3, Lines[0].Current+3); + end; + + if SDL_ModState = KMOD_LCTRL + KMOD_LSHIFT + KMOD_LALT then + begin + CopySentences(CopySrc, Lines[0].Current, 4); + end; + end; + SDLK_5: + begin + if SDL_ModState = KMOD_LCTRL + KMOD_LSHIFT then + begin + CopySentence(CopySrc, Lines[0].Current); + CopySentence(CopySrc+1, Lines[0].Current+1); + CopySentence(CopySrc+2, Lines[0].Current+2); + CopySentence(CopySrc+3, Lines[0].Current+3); + CopySentence(CopySrc+4, Lines[0].Current+4); + end; + + if SDL_ModState = KMOD_LCTRL + KMOD_LSHIFT + KMOD_LALT then + begin + CopySentences(CopySrc, Lines[0].Current, 5); + end; + end; + + SDLK_9: + begin + // Decrease GAP + if SDL_ModState = 0 then + CurrentSong.GAP := CurrentSong.GAP - 10; + if SDL_ModState = KMOD_LSHIFT then + CurrentSong.GAP := CurrentSong.GAP - 1000; + end; + SDLK_0: + begin + // Increase GAP + if SDL_ModState = 0 then + CurrentSong.GAP := CurrentSong.GAP + 10; + if SDL_ModState = KMOD_LSHIFT then + CurrentSong.GAP := CurrentSong.GAP + 1000; + end; + + SDLK_KP_PLUS: + begin + // Increase tone of all notes + if SDL_ModState = 0 then + ChangeWholeTone(1); + if SDL_ModState = KMOD_LSHIFT then + ChangeWholeTone(12); + end; + + SDLK_KP_MINUS: + begin + // Decrease tone of all notes + if SDL_ModState = 0 then + ChangeWholeTone(-1); + if SDL_ModState = KMOD_LSHIFT then + ChangeWholeTone(-12); + end; + + SDLK_SLASH: + begin + if SDL_ModState = 0 then + begin + // Insert start of sentece + if CurrentNote > 0 then + DivideSentence; + end; + + if SDL_ModState = KMOD_LSHIFT then + begin + // Join next sentence with current + if Lines[0].Current < Lines[0].High then + JoinSentence; + end; + + if SDL_ModState = KMOD_LCTRL then + begin + // divide note + DivideNote; + end; + + end; + + SDLK_F4: + begin + // Enter Text Edit Mode + TextEditMode := true; + end; + + SDLK_SPACE: + begin + // Play Sentence + PlaySentenceMidi := false; // stop midi + PlaySentence := true; + Click := false; + AudioPlayback.Stop; + AudioPlayback.Position := GetTimeFromBeat(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Start); + PlayStopTime := (GetTimeFromBeat( + Lines[0].Line[Lines[0].Current].Note[CurrentNote].Start + + Lines[0].Line[Lines[0].Current].Note[CurrentNote].Length)); + AudioPlayback.Play; + LastClick := -100; + end; + + SDLK_RETURN: + begin + end; + + SDLK_DELETE: + begin + if SDL_ModState = KMOD_LCTRL then + begin + // moves text to right in current sentence + DeleteNote; + end; + end; + + SDLK_PERIOD: + begin + // moves text to right in current sentence + MoveTextToRight; + end; + + SDLK_RIGHT: + begin + // right + if SDL_ModState = 0 then + begin + Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 1; + Inc(CurrentNote); + if CurrentNote > Lines[0].Line[Lines[0].Current].HighNote then + CurrentNote := 0; + Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 2; + Lyric.Selected := CurrentNote; + end; + + // ctrl + right + if SDL_ModState = KMOD_LCTRL then + begin + if Lines[0].Line[Lines[0].Current].Note[CurrentNote].Length > 1 then + begin + Dec(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Length); + Inc(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Start); + if CurrentNote = 0 then + begin + Inc(Lines[0].Line[Lines[0].Current].Start); + end; + end; + end; + + // shift + right + if SDL_ModState = KMOD_LSHIFT then + begin + Inc(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Start); + if CurrentNote = 0 then + begin + Inc(Lines[0].Line[Lines[0].Current].Start); + end; + if CurrentNote = Lines[0].Line[Lines[0].Current].HighNote then + Inc(Lines[0].Line[Lines[0].Current].End_); + end; + + // alt + right + if SDL_ModState = KMOD_LALT then + begin + Inc(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Length); + if CurrentNote = Lines[0].Line[Lines[0].Current].HighNote then + Inc(Lines[0].Line[Lines[0].Current].End_); + end; + + // alt + ctrl + shift + right = move all from cursor to right + if SDL_ModState = KMOD_LALT + KMOD_LCTRL + KMOD_LSHIFT then + begin + MoveAllToEnd(1); + end; + + end; + + SDLK_LEFT: + begin + // left + if SDL_ModState = 0 then + begin + Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 1; + Dec(CurrentNote); + if CurrentNote = -1 then + CurrentNote := Lines[0].Line[Lines[0].Current].HighNote; + Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 2; + Lyric.Selected := CurrentNote; + end; + + // ctrl + left + if SDL_ModState = KMOD_LCTRL then + begin + Dec(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Start); + Inc(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Length); + if CurrentNote = 0 then + begin + Dec(Lines[0].Line[Lines[0].Current].Start); + end; + end; + + // shift + left + if SDL_ModState = KMOD_LSHIFT then + begin + Dec(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Start); + + // resizing sentences + if CurrentNote = 0 then + begin + Dec(Lines[0].Line[Lines[0].Current].Start); + end; + + if CurrentNote = Lines[0].Line[Lines[0].Current].HighNote then + Dec(Lines[0].Line[Lines[0].Current].End_); + + end; + + // alt + left + if SDL_ModState = KMOD_LALT then + begin + if Lines[0].Line[Lines[0].Current].Note[CurrentNote].Length > 1 then + begin + Dec(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Length); + if CurrentNote = Lines[0].Line[Lines[0].Current].HighNote then + Dec(Lines[0].Line[Lines[0].Current].End_); + end; + end; + + // alt + ctrl + shift + right = move all from cursor to left + if SDL_ModState = KMOD_LALT + KMOD_LCTRL + KMOD_LSHIFT then + begin + MoveAllToEnd(-1); + end; + + end; + + SDLK_DOWN: + begin + + // skip to next sentence + if SDL_ModState = 0 then + begin + {$IFDEF UseMIDIPort} + MidiOut.PutShort($81, Lines[0].Line[Lines[0].Current].Note[MidiLastNote].Tone + 60, 127); + PlaySentenceMidi := false; + {$ENDIF} + + Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 1; + Inc(Lines[0].Current); + CurrentNote := 0; + if Lines[0].Current > Lines[0].High then + Lines[0].Current := 0; + Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 2; + + Lyric.AddLine(Lines[0].Current); + Lyric.Selected := 0; + AudioPlayback.Stop; + PlaySentence := false; + end; + + // decrease tone + if SDL_ModState = KMOD_LCTRL then + begin + TransposeNote(-1); + end; + + end; + + SDLK_UP: + begin + + // skip to previous sentence + if SDL_ModState = 0 then + begin + {$IFDEF UseMIDIPort} + MidiOut.PutShort($81, Lines[0].Line[Lines[0].Current].Note[MidiLastNote].Tone + 60, 127); + PlaySentenceMidi := false; + {$endif} + + Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 1; + Dec(Lines[0].Current); + CurrentNote := 0; + if Lines[0].Current = -1 then + Lines[0].Current := Lines[0].High; + Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 2; + + Lyric.AddLine(Lines[0].Current); + Lyric.Selected := 0; + AudioPlayback.Stop; + PlaySentence := false; + end; + + // increase tone + if SDL_ModState = KMOD_LCTRL then + begin + TransposeNote(1); + end; + end; + + end; // case + end; + end; // if +end; + +function TScreenEditSub.ParseInputEditText(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; +var + SDL_ModState: word; +begin + // used when in Text Edit Mode + Result := true; + + SDL_ModState := SDL_GetModState and (KMOD_LSHIFT + KMOD_RSHIFT + + KMOD_LCTRL + KMOD_RCTRL + KMOD_LALT + KMOD_RALT {+ KMOD_CAPS}); + + if (PressedDown) then + begin + // check normal keys + if (IsPrintableChar(CharCode)) then + begin + Lines[0].Line[Lines[0].Current].Note[CurrentNote].Text := + Lines[0].Line[Lines[0].Current].Note[CurrentNote].Text + UCS4ToUTF8String(CharCode); + Exit; + end; + + // check special keys + case PressedKey of + + SDLK_ESCAPE: + begin + FadeTo(@ScreenSong); + end; + SDLK_F4, SDLK_RETURN: + begin + // Exit Text Edit Mode + TextEditMode := false; + end; + SDLK_BACKSPACE: + begin + UTF8Delete(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Text, + LengthUTF8(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Text), 1); + end; + SDLK_RIGHT: + begin + // right + if SDL_ModState = 0 then + begin + Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 1; + Inc(CurrentNote); + if CurrentNote > Lines[0].Line[Lines[0].Current].HighNote then + CurrentNote := 0; + Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 2; + Lyric.Selected := CurrentNote; + end; + end; + SDLK_LEFT: + begin + // left + if SDL_ModState = 0 then + begin + Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 1; + Dec(CurrentNote); + if CurrentNote = -1 then + CurrentNote := Lines[0].Line[Lines[0].Current].HighNote; + Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 2; + Lyric.Selected := CurrentNote; + end; + end; + end; + end; +end; + +{ +procedure TScreenEditSub.NewBeat; +begin + // click + for Pet := 0 to Lines[0].Line[Lines[0].Current].HighNut do + if (Lines[0].Line[Lines[0].Current].Note[Pet].Start = Czas.AktBeat) then + Music.PlayClick; +end; +} + +procedure TScreenEditSub.DivideBPM; +var + C: integer; + N: integer; +begin + CurrentSong.BPM[0].BPM := CurrentSong.BPM[0].BPM / 2; + for C := 0 to Lines[0].High do + begin + Lines[0].Line[C].Start := Lines[0].Line[C].Start div 2; + Lines[0].Line[C].End_ := Lines[0].Line[C].End_ div 2; + for N := 0 to Lines[0].Line[C].HighNote do + begin + Lines[0].Line[C].Note[N].Start := Lines[0].Line[C].Note[N].Start div 2; + Lines[0].Line[C].Note[N].Length := Round(Lines[0].Line[C].Note[N].Length / 2); + end; // N + end; // C +end; + +procedure TScreenEditSub.MultiplyBPM; +var + C: integer; + N: integer; +begin + CurrentSong.BPM[0].BPM := CurrentSong.BPM[0].BPM * 2; + for C := 0 to Lines[0].High do + begin + Lines[0].Line[C].Start := Lines[0].Line[C].Start * 2; + Lines[0].Line[C].End_ := Lines[0].Line[C].End_ * 2; + for N := 0 to Lines[0].Line[C].HighNote do + begin + Lines[0].Line[C].Note[N].Start := Lines[0].Line[C].Note[N].Start * 2; + Lines[0].Line[C].Note[N].Length := Lines[0].Line[C].Note[N].Length * 2; + end; // N + end; // C +end; + +procedure TScreenEditSub.LyricsCapitalize; +var + C: integer; + //N: integer; // temporary + S: string; +begin + // temporary + { + for C := 0 to Lines[0].High do + for N := 0 to Lines[0].Line[C].HighNut do + Lines[0].Line[C].Note[N].Text := UTF8LowerCase(Lines[0].Line[C].Note[N].Text); + } + + for C := 0 to Lines[0].High do + begin + S := AnsiUpperCase(Copy(Lines[0].Line[C].Note[0].Text, 1, 1)); + S := S + Copy(Lines[0].Line[C].Note[0].Text, 2, Length(Lines[0].Line[C].Note[0].Text)-1); + Lines[0].Line[C].Note[0].Text := S; + end; // C +end; + +procedure TScreenEditSub.LyricsCorrectSpaces; +var + C: integer; + N: integer; +begin + for C := 0 to Lines[0].High do + begin + // correct starting spaces in the first word + while Copy(Lines[0].Line[C].Note[0].Text, 1, 1) = ' ' do + Lines[0].Line[C].Note[0].Text := Copy(Lines[0].Line[C].Note[0].Text, 2, 100); + + // move spaces on the start to the end of the previous note + for N := 1 to Lines[0].Line[C].HighNote do + begin + while (Copy(Lines[0].Line[C].Note[N].Text, 1, 1) = ' ') do + begin + Lines[0].Line[C].Note[N].Text := Copy(Lines[0].Line[C].Note[N].Text, 2, 100); + Lines[0].Line[C].Note[N-1].Text := Lines[0].Line[C].Note[N-1].Text + ' '; + end; + end; // N + + // correct '-' to '- ' + for N := 0 to Lines[0].Line[C].HighNote do + begin + if Lines[0].Line[C].Note[N].Text = '-' then + Lines[0].Line[C].Note[N].Text := '- '; + end; // N + + // add space to the previous note when the current word is '- ' + for N := 1 to Lines[0].Line[C].HighNote do + begin + if Lines[0].Line[C].Note[N].Text = '- ' then + Lines[0].Line[C].Note[N-1].Text := Lines[0].Line[C].Note[N-1].Text + ' '; + end; // N + + // correct too many spaces at the end of note + for N := 0 to Lines[0].Line[C].HighNote do + begin + while Copy(Lines[0].Line[C].Note[N].Text, Length(Lines[0].Line[C].Note[N].Text)-1, 2) = ' ' do + Lines[0].Line[C].Note[N].Text := Copy(Lines[0].Line[C].Note[N].Text, 1, Length(Lines[0].Line[C].Note[N].Text)-1); + end; // N + + // and correct if there is no space at the end of sentence + N := Lines[0].Line[C].HighNote; + if Copy(Lines[0].Line[C].Note[N].Text, Length(Lines[0].Line[C].Note[N].Text), 1) <> ' ' then + Lines[0].Line[C].Note[N].Text := Lines[0].Line[C].Note[N].Text + ' '; + + end; // C +end; + +procedure TScreenEditSub.FixTimings; +var + C: integer; + S: integer; + Min: integer; + Max: integer; +begin + for C := 1 to Lines[0].High do + begin + with Lines[0].Line[C-1] do + begin + Min := Note[HighNote].Start + Note[HighNote].Length; + Max := Lines[0].Line[C].Note[0].Start; + case (Max - Min) of + 0: S := Max; + 1: S := Max; + 2: S := Max - 1; + 3: S := Max - 2; + else + if ((Max - Min) > 4) then + S := Min + 2 + else + S := Max; + end; // case + + Lines[0].Line[C].Start := S; + end; // with + end; // for +end; + +procedure TScreenEditSub.DivideSentence; +var + C: integer; + CStart: integer; + CNew: integer; + CLen: integer; + N: integer; + NStart: integer; + NHigh: integer; +begin + // increase sentence length by 1 + CLen := Length(Lines[0].Line); + SetLength(Lines[0].Line, CLen + 1); + Inc(Lines[0].Number); + Inc(Lines[0].High); + + // move needed sentences to one forward. newly has the copy of divided sentence + CStart := Lines[0].Current; + for C := CLen-1 downto CStart do + Lines[0].Line[C+1] := Lines[0].Line[C]; + + // clear and set new sentence + CNew := CStart + 1; + NStart := CurrentNote; + Lines[0].Line[CNew].Start := Lines[0].Line[CStart].Note[NStart].Start; + Lines[0].Line[CNew].Lyric := ''; + Lines[0].Line[CNew].End_ := 0; + Lines[0].Line[CNew].BaseNote := 0;//High(integer); // TODO: High (integer) will causes a memory exception later in this procedure. Weird! + Lines[0].Line[CNew].HighNote := -1; + SetLength(Lines[0].Line[CNew].Note, 0); + + // move right notes to new sentences + NHigh := Lines[0].Line[CStart].HighNote; + for N := NStart to NHigh do + begin + // increase sentence counters + with Lines[0].Line[CNew] do + begin + Inc(HighNote); + SetLength(Note, HighNote + 1); + Note[HighNote] := Note[N]; + End_ := Note[HighNote].Start + Note[HighNote].Length; + + if Note[HighNote].Tone < BaseNote then + BaseNote := Note[HighNote].Tone; + end; + end; + + // clear old notes and set sentence counters + Lines[0].Line[CStart].HighNote := NStart - 1; + Lines[0].Line[CStart].End_ := Lines[0].Line[CStart].Note[NStart-1].Start + + Lines[0].Line[CStart].Note[NStart-1].Length; + SetLength(Lines[0].Line[CStart].Note, Lines[0].Line[CStart].HighNote + 1); + + //recalculate BaseNote of the divided Sentence + with Lines[0].Line[CStart] do + begin + BaseNote := High(integer); + + for N := 0 to HighNote do + if Note[N].Tone < BaseNote then + BaseNote := Note[N].Tone; + end; + + Lines[0].Current := Lines[0].Current + 1; + CurrentNote := 0; + Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 2; + Lyric.AddLine(Lines[0].Current); +end; + +procedure TScreenEditSub.JoinSentence; +var + C: integer; + N: integer; + NStart: integer; + NDst: integer; +begin + C := Lines[0].Current; + + // set new sentence + NStart := Lines[0].Line[C].HighNote + 1; + Lines[0].Line[C].HighNote := Lines[0].Line[C].HighNote + Lines[0].Line[C+1].HighNote + 1; + SetLength(Lines[0].Line[C].Note, Lines[0].Line[C].HighNote + 1); + + // move right notes to new sentences + for N := 0 to Lines[0].Line[C+1].HighNote do + begin + NDst := NStart + N; + Lines[0].Line[C].Note[NDst] := Lines[0].Line[C+1].Note[N]; + end; + + // increase sentence counters + NDst := Lines[0].Line[C].HighNote; + Lines[0].Line[C].End_ := Lines[0].Line[C].Note[NDst].Start + + Lines[0].Line[C].Note[NDst].Length; + + // move needed sentences to one backward. + for C := Lines[0].Current + 1 to Lines[0].High - 1 do + Lines[0].Line[C] := Lines[0].Line[C+1]; + + // increase sentence length by 1 + SetLength(Lines[0].Line, Length(Lines[0].Line) - 1); + Dec(Lines[0].Number); + Dec(Lines[0].High); +end; + +procedure TScreenEditSub.DivideNote; +var + C: integer; + N: integer; +begin + C := Lines[0].Current; + + with Lines[0].Line[C] do + begin + Inc(HighNote); + SetLength(Note, HighNote + 1); + + // we copy all notes including selected one + for N := HighNote downto CurrentNote+1 do + begin + Note[N] := Note[N-1]; + end; + + // me slightly modify new note + Note[CurrentNote].Length := 1; + Inc(Note[CurrentNote+1].Start); + Dec(Note[CurrentNote+1].Length); + Note[CurrentNote+1].Text := '- '; + Note[CurrentNote+1].Color := 1; + end; +end; + +procedure TScreenEditSub.DeleteNote; +var + C: integer; + N: integer; +begin + C := Lines[0].Current; + + //Do Not delete Last Note + if (Lines[0].High > 0) or (Lines[0].Line[C].HighNote > 0) then + begin + + // we copy all notes from the next to the selected one + for N := CurrentNote+1 to Lines[0].Line[C].HighNote do + begin + Lines[0].Line[C].Note[N-1] := Lines[0].Line[C].Note[N]; + end; + + Dec(Lines[0].Line[C].HighNote); + if (Lines[0].Line[C].HighNote >= 0) then + begin + SetLength(Lines[0].Line[C].Note, Lines[0].Line[C].HighNote + 1); + + // me slightly modify new note + if CurrentNote > Lines[0].Line[C].HighNote then + Dec(CurrentNote); + + Lines[0].Line[C].Note[CurrentNote].Color := 2; + end + //Last Note of current Sentence Deleted - > Delete Sentence + else + begin + //Move all Sentences after the current to the Left + for N := C+1 to Lines[0].High do + Lines[0].Line[N-1] := Lines[0].Line[N]; + + //Delete Last Sentence + SetLength(Lines[0].Line, Lines[0].High); + Lines[0].High := High(Lines[0].Line); + Lines[0].Number := Length(Lines[0].Line); + + CurrentNote := 0; + if (C > 0) then + Lines[0].Current := C - 1 + else + Lines[0].Current := 0; + + Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 2; + end; + end; +end; + +procedure TScreenEditSub.TransposeNote(Transpose: integer); +begin + Inc(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Tone, Transpose); +end; + +procedure TScreenEditSub.ChangeWholeTone(Tone: integer); +var + C: integer; + N: integer; +begin + for C := 0 to Lines[0].High do + begin + Lines[0].Line[C].BaseNote := Lines[0].Line[C].BaseNote + Tone; + for N := 0 to Lines[0].Line[C].HighNote do + Lines[0].Line[C].Note[N].Tone := Lines[0].Line[C].Note[N].Tone + Tone; + end; +end; + +procedure TScreenEditSub.MoveAllToEnd(Move: integer); +var + C: integer; + N: integer; + NStart: integer; +begin + for C := Lines[0].Current to Lines[0].High do + begin + NStart := 0; + if C = Lines[0].Current then + NStart := CurrentNote; + for N := NStart to Lines[0].Line[C].HighNote do + begin + Inc(Lines[0].Line[C].Note[N].Start, Move); // move note start + + if N = 0 then + begin // fix beginning + Inc(Lines[0].Line[C].Start, Move); + end; + + if N = Lines[0].Line[C].HighNote then // fix ending + Inc(Lines[0].Line[C].End_, Move); + + end; // for + end; // for +end; + +procedure TScreenEditSub.MoveTextToRight; +var + C: integer; + N: integer; + NHigh: integer; +begin + { + C := Lines[0].Current; + + for N := Lines[0].Line[C].HighNut downto 1 do + begin + Lines[0].Line[C].Note[N].Text := Lines[0].Line[C].Note[N-1].Text; + end; // for + + Lines[0].Line[C].Note[0].Text := '- '; + } + + C := Lines[0].Current; + NHigh := Lines[0].Line[C].HighNote; + + // last word + Lines[0].Line[C].Note[NHigh].Text := Lines[0].Line[C].Note[NHigh-1].Text + Lines[0].Line[C].Note[NHigh].Text; + + // other words + for N := NHigh - 1 downto CurrentNote + 1 do + begin + Lines[0].Line[C].Note[N].Text := Lines[0].Line[C].Note[N-1].Text; + end; // for + Lines[0].Line[C].Note[CurrentNote].Text := '- '; +end; + +procedure TScreenEditSub.MarkSrc; +begin + CopySrc := Lines[0].Current; +end; + +procedure TScreenEditSub.PasteText; +var + C: integer; + N: integer; +begin + C := Lines[0].Current; + + for N := 0 to Lines[0].Line[CopySrc].HighNote do + Lines[0].Line[C].Note[N].Text := Lines[0].Line[CopySrc].Note[N].Text; +end; + +procedure TScreenEditSub.CopySentence(Src, Dst: integer); +var + N: integer; + Time1: integer; + Time2: integer; + TD: integer; +begin + Time1 := Lines[0].Line[Src].Note[0].Start; + Time2 := Lines[0].Line[Dst].Note[0].Start; + TD := Time2-Time1; + + SetLength(Lines[0].Line[Dst].Note, Lines[0].Line[Src].HighNote + 1); + Lines[0].Line[Dst].HighNote := Lines[0].Line[Src].HighNote; + for N := 0 to Lines[0].Line[Src].HighNote do + begin + Lines[0].Line[Dst].Note[N].Text := Lines[0].Line[Src].Note[N].Text; + Lines[0].Line[Dst].Note[N].Length := Lines[0].Line[Src].Note[N].Length; + Lines[0].Line[Dst].Note[N].Tone := Lines[0].Line[Src].Note[N].Tone; + Lines[0].Line[Dst].Note[N].Start := Lines[0].Line[Src].Note[N].Start + TD; + end; + N := Lines[0].Line[Src].HighNote; + Lines[0].Line[Dst].End_ := Lines[0].Line[Dst].Note[N].Start + Lines[0].Line[Dst].Note[N].Length; +end; + +procedure TScreenEditSub.CopySentences(Src, Dst, Num: integer); +var + C: integer; +begin + // create place for new sentences + SetLength(Lines[0].Line, Lines[0].Number + Num - 1); + + // moves sentences next to the destination + for C := Lines[0].High downto Dst + 1 do + begin + Lines[0].Line[C + Num - 1] := Lines[0].Line[C]; + end; + + // prepares new sentences: sets sentence start and create first note + for C := 1 to Num-1 do + begin + Lines[0].Line[Dst + C].Start := Lines[0].Line[Dst + C - 1].Note[0].Start + + (Lines[0].Line[Src + C].Note[0].Start - Lines[0].Line[Src + C - 1].Note[0].Start); + SetLength(Lines[0].Line[Dst + C].Note, 1); + Lines[0].Line[Dst + C].HighNote := 0; + Lines[0].Line[Dst + C].Note[0].Start := Lines[0].Line[Dst + C].Start; + Lines[0].Line[Dst + C].Note[0].Length := 1; + Lines[0].Line[Dst + C].End_ := Lines[0].Line[Dst + C].Start + 1; + end; + + // increase counters + Lines[0].Number := Lines[0].Number + Num - 1; + Lines[0].High := Lines[0].High + Num - 1; + + for C := 0 to Num-1 do + CopySentence(Src + C, Dst + C); +end; + +constructor TScreenEditSub.Create; +begin + inherited Create; + SetLength(Player, 1); + + // line + AddStatic(20, 10, 80, 30, 0, 0, 0, Skin.GetTextureFileName('ButtonF'), TEXTURE_TYPE_COLORIZED); + AddText(40, 17, 1, 18, 1, 1, 1, 'Line'); + TextSentence := AddText(120, 14, 1, 24, 0, 0, 0, '0 / 0'); + + // Note + AddStatic(220, 10, 80, 30, 0, 0, 0, Skin.GetTextureFileName('ButtonF'), TEXTURE_TYPE_COLORIZED); + AddText(242, 17, 1, 18, 1, 1, 1, 'Note'); + TextNote := AddText(320, 14, 1, 24, 0, 0, 0, '0 / 0'); + + // file info + AddStatic(150, 50, 500, 150, 0, 0, 0, Skin.GetTextureFileName('MainBar'), TEXTURE_TYPE_COLORIZED); + AddStatic(151, 52, 498, 146, 1, 1, 1, Skin.GetTextureFileName('MainBar'), TEXTURE_TYPE_COLORIZED); + AddText(180, 65, 0, 24, 0, 0, 0, 'Title:'); + AddText(180, 90, 0, 24, 0, 0, 0, 'Artist:'); + AddText(180, 115, 0, 24, 0, 0, 0, 'Mp3:'); + AddText(180, 140, 0, 24, 0, 0, 0, 'BPM:'); + AddText(180, 165, 0, 24, 0, 0, 0, 'GAP:'); + + TextTitle := AddText(250, 65, 0, 24, 0, 0, 0, 'a'); + TextArtist := AddText(250, 90, 0, 24, 0, 0, 0, 'b'); + TextMp3 := AddText(250, 115, 0, 24, 0, 0, 0, 'c'); + TextBPM := AddText(250, 140, 0, 24, 0, 0, 0, 'd'); + TextGAP := AddText(250, 165, 0, 24, 0, 0, 0, 'e'); + +{ AddInteraction(2, TextTitle); + AddInteraction(2, TextArtist); + AddInteraction(2, TextMp3); + AddInteraction(2, TextBPM); + AddInteraction(2, TextGAP);} + + // note info + AddText(20, 190, 0, 24, 0, 0, 0, 'Start:'); + AddText(20, 215, 0, 24, 0, 0, 0, 'Duration:'); + AddText(20, 240, 0, 24, 0, 0, 0, 'Tone:'); + AddText(20, 265, 0, 24, 0, 0, 0, 'Text:'); + + TextNStart := AddText(120, 190, 0, 24, 0, 0, 0, 'a'); + TextNLength := AddText(120, 215, 0, 24, 0, 0, 0, 'b'); + TextNTon := AddText(120, 240, 0, 24, 0, 0, 0, 'c'); + TextNText := AddText(120, 265, 0, 24, 0, 0, 0, 'd'); + + // debug + TextDebug := AddText(30, 550, 0, 8, 0, 0, 0, ''); + +end; + +procedure TScreenEditSub.OnShow; +var + FileExt: IPath; +begin + inherited; + + Log.LogStatus('Initializing', 'TEditScreen.OnShow'); + Lyric := TEditorLyrics.Create; + + ResetSingTemp; + + try + //Check if File is XML + FileExt := CurrentSong.FileName.GetExtension; + if FileExt.ToUTF8 = '.xml' then + Error := not CurrentSong.LoadXMLSong() + else + begin + // reread header with custom tags + Error := not CurrentSong.Analyse(true); + if not Error then + Error := not CurrentSong.LoadSong; + end; + except + Error := true; + end; + + if Error then + begin + //Error Loading Song -> Go back to Song Screen and Show some Error Message + FadeTo(@ScreenSong); + ScreenPopupError.ShowPopup (Language.Translate('ERROR_CORRUPT_SONG')); + Exit; + end + else + begin + {$IFDEF UseMIDIPort} + MidiOut := TMidiOutput.Create(nil); + if Ini.Debug = 1 then + MidiOut.ProductName := 'Microsoft GS Wavetable SW Synth'; // for my kxproject without midi table + MidiOut.Open; + {$ENDIF} + Text[TextTitle].Text := CurrentSong.Title; + Text[TextArtist].Text := CurrentSong.Artist; + Text[TextMp3].Text := CurrentSong.Mp3.ToUTF8; + + Lines[0].Current := 0; + CurrentNote := 0; + Lines[0].Line[0].Note[0].Color := 2; + AudioPlayback.Open(CurrentSong.Path.Append(CurrentSong.Mp3)); + //Set Down Music Volume for Better hearability of Midi Sounds + //Music.SetVolume(0.4); + + Lyric.Clear; + Lyric.X := 400; + Lyric.Y := 500; + Lyric.Align := atCenter; + Lyric.Size := 42; + Lyric.ColR := 0; + Lyric.ColG := 0; + Lyric.ColB := 0; + Lyric.ColSR := Skin_FontHighlightR; + Lyric.ColSG := Skin_FontHighlightG; + Lyric.ColSB := Skin_FontHighlightB; + Lyric.AddLine(0); + Lyric.Selected := 0; + + NotesH := 7; + NotesW := 4; + + end; + +// Interaction := 0; + TextEditMode := false; +end; + +function TScreenEditSub.Draw: boolean; +var + Pet: integer; + AktBeat: integer; +begin + glClearColor(1,1,1,1); + + // midi music + if PlaySentenceMidi then + begin + {$IFDEF UseMIDIPort} + MidiPos := USTime.GetTime - MidiTime + MidiStart; + + // stop the music + if (MidiPos > MidiStop) then + begin + MidiOut.PutShort($81, Lines[0].Line[Lines[0].Current].Note[MidiLastNote].Tone + 60, 127); + PlaySentenceMidi := false; + end; + {$ENDIF} + + // click + AktBeat := Floor(GetMidBeat(MidiPos - CurrentSong.GAP / 1000)); + Text[TextDebug].Text := IntToStr(AktBeat); + + if AktBeat <> LastClick then + begin + for Pet := 0 to Lines[0].Line[Lines[0].Current].HighNote do + if (Lines[0].Line[Lines[0].Current].Note[Pet].Start = AktBeat) then + begin + + LastClick := AktBeat; + {$IFDEF UseMIDIPort} + if Pet > 0 then + MidiOut.PutShort($81, Lines[0].Line[Lines[0].Current].Note[Pet-1].Tone + 60, 127); + MidiOut.PutShort($91, Lines[0].Line[Lines[0].Current].Note[Pet].Tone + 60, 127); + MidiLastNote := Pet; + {$ENDIF} + + end; + end; + end; // if PlaySentenceMidi + + // mp3 music + if PlaySentence then + begin + // stop the music + if (AudioPlayback.Position > PlayStopTime) then + begin + AudioPlayback.Stop; + PlaySentence := false; + end; + + // click + if (Click) and (PlaySentence) then + begin +// AktBeat := Floor(CurrentSong.BPM[0].BPM * (Music.Position - CurrentSong.GAP / 1000) / 60); + AktBeat := Floor(GetMidBeat(AudioPlayback.Position - CurrentSong.GAP / 1000)); + Text[TextDebug].Text := IntToStr(AktBeat); + if AktBeat <> LastClick then + begin + for Pet := 0 to Lines[0].Line[Lines[0].Current].HighNote do + if (Lines[0].Line[Lines[0].Current].Note[Pet].Start = AktBeat) then + begin + AudioPlayback.PlaySound( SoundLib.Click ); + LastClick := AktBeat; + end; + end; + end; // click + end; // if PlaySentence + + + Text[TextSentence].Text := IntToStr(Lines[0].Current + 1) + ' / ' + IntToStr(Lines[0].Number); + Text[TextNote].Text := IntToStr(CurrentNote + 1) + ' / ' + IntToStr(Lines[0].Line[Lines[0].Current].HighNote + 1); + + // Song info + Text[TextBPM].Text := FloatToStr(CurrentSong.BPM[0].BPM / 4); + Text[TextGAP].Text := FloatToStr(CurrentSong.GAP); + + //Error reading Variables when no Song is loaded + if not Error then + begin + // Note info + Text[TextNStart].Text := IntToStr(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Start); + Text[TextNLength].Text := IntToStr(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Length); + Text[TextNTon].Text := IntToStr(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Tone) + ' ( ' + GetNoteName(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Tone) + ' )'; + Text[TextNText].Text := Lines[0].Line[Lines[0].Current].Note[CurrentNote].Text; + end; + + // Text Edit Mode + if TextEditMode then + Text[TextNText].Text := Text[TextNText].Text + '|'; + + // draw static menu + inherited Draw; + + // draw notes + SingDrawNoteLines(20, 300, 780, 15); + //Error Drawing when no Song is loaded + if not Error then + begin + SingDrawBeatDelimeters(40, 300, 760, 0); + EditDrawLine(40, 405, 760, 0, 15); + end; + + // draw text + Lyric.Draw; + + Result := true; +end; + +procedure TScreenEditSub.OnHide; +begin + {$IFDEF UseMIDIPort} + MidiOut.Close; + MidiOut.Free; + {$ENDIF} + Lyric.Free; + //Music.SetVolume(1.0); +end; + +function TScreenEditSub.GetNoteName(Note: integer): string; +var + N1, N2: integer; +begin + if (Note > 0) then + begin + N1 := Note mod 12; + N2 := Note div 12; + end + else + begin + N1 := (Note + (-Trunc(Note/12)+1)*12) mod 12; + N2 := -1; + end; + + case N1 of + 0: Result := 'c'; + 1: Result := 'c#'; + 2: Result := 'd'; + 3: Result := 'd#'; + 4: Result := 'e'; + 5: Result := 'f'; + 6: Result := 'f#'; + 7: Result := 'g'; + 8: Result := 'g#'; + 9: Result := 'a'; + 10: Result := 'b'; + 11: Result := 'h'; + end; + + case N2 of + 0: Result := UpperCase(Result); //Normal Uppercase Note, 1: Normal lowercase Note + 2: Result := Result + ''''; //One Striped + 3: Result := Result + ''''''; //Two Striped + 4: Result := Result + ''''''''; //etc. + 5: Result := Result + ''''''''''; + 6: Result := Result + ''''''''''''; + 7: Result := Result + ''''''''''''''; + end; +end; + +end. |