diff --git a/TJAPlayer3/Songs/CDTX.cs b/TJAPlayer3/Songs/CDTX.cs index c4bccef6..f03e874d 100644 --- a/TJAPlayer3/Songs/CDTX.cs +++ b/TJAPlayer3/Songs/CDTX.cs @@ -1309,6 +1309,8 @@ namespace TJAPlayer3 public List listLyric; //歌詞を格納していくリスト。スペル忘れた(ぉい public List listLyric2; + public bool usingLyricsFile; //If lyric file is used (VTT/LRC), ignore #LYRIC tags & do not parse other lyric file tags + private int listBalloon_Normal_数値管理; private int listBalloon_Expert_数値管理; private int listBalloon_Master_数値管理; @@ -1379,6 +1381,7 @@ namespace TJAPlayer3 this.MAKER = ""; this.SELECTBG = ""; this.bLyrics = false; + this.usingLyricsFile = false; this.eジャンル = Eジャンル.None; this.PREVIEW = ""; this.PREIMAGE = ""; @@ -4001,7 +4004,7 @@ namespace TJAPlayer3 this.listChip.Add(chip); } - else if (command == "#LYRIC") + else if (command == "#LYRIC" && !usingLyricsFile) // Do not parse LYRIC tags if a lyric file is already loaded { if (TJAPlayer3.r現在のステージ.eステージID == CStage.Eステージ.曲読み込み)//起動時に重たくなってしまう問題の修正用 this.listLyric.Add(this.pf歌詞フォント.DrawPrivateFont(argument, TJAPlayer3.Skin.Game_Lyric_ForeColor, TJAPlayer3.Skin.Game_Lyric_BackColor)); @@ -5419,7 +5422,48 @@ namespace TJAPlayer3 this.bHIDDENBRANCH = true; } } - else if (strCommandName.Equals("LYRICFILE")) + else if (strCommandName.Equals("LYRICS") && !usingLyricsFile) + { + if (!string.IsNullOrEmpty(strCommandParam)) + { + string[] files = SplitComma(strCommandParam); + string[] filePaths = new string[files.Length]; + for (int i = 0; i < files.Length; i++) + { + filePaths[i] = this.strフォルダ名 + files[i]; + + if (File.Exists(filePaths[i])) + { + try + { + if (TJAPlayer3.r現在のステージ.eステージID == CStage.Eステージ.曲読み込み) + { + if (filePaths[i].EndsWith(".vtt")) + { + using (VTTParser parser = new VTTParser()) + { + this.listLyric2.AddRange(parser.ParseVTTFile(filePaths[i], i, this.pf歌詞フォント)); + } + this.bLyrics = true; + this.usingLyricsFile = true; + } + else if (filePaths[i].EndsWith(".lrc")) + { + this.LyricFileParser(filePaths[i], i); + this.bLyrics = true; + this.usingLyricsFile = true; + } + } + } + catch (Exception e) + { + Debug.WriteLine("Something went wrong while parsing a lyric file. More details : {0}", e); + } + } + } + } + } + else if (strCommandName.Equals("LYRICFILE") && !usingLyricsFile) { if (!string.IsNullOrEmpty(strCommandParam)) { @@ -5435,6 +5479,7 @@ namespace TJAPlayer3 if (TJAPlayer3.r現在のステージ.eステージID == CStage.Eステージ.曲読み込み)//起動時に重たくなってしまう問題の修正用 this.LyricFileParser(strFilePath[index], index); this.bLyrics = true; + this.usingLyricsFile = true; } catch { diff --git a/TJAPlayer3/Songs/VTTParser.cs b/TJAPlayer3/Songs/VTTParser.cs new file mode 100644 index 00000000..bc42c155 --- /dev/null +++ b/TJAPlayer3/Songs/VTTParser.cs @@ -0,0 +1,235 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using static TJAPlayer3.CDTX; + +namespace TJAPlayer3 +{ + public class VTTParser : IDisposable + { + /* + + TO-DO : + - Tag support (lang, timestamp, color(?), ruby/rt) + - Adjust bitmap position/anchor when lyric contains newlines (currently snapped to top-center, gets trimmed going down) (also how on earth do i do this lmao) + - NOTE support (for comments within file) + + */ + + private static string[] _vttdelimiter; + + private static Regex regexTimestamp; + private static Regex regexTag; + + private static Regex regexOffset; + private static Regex regexLang; + + private bool _isDisposed; + + public VTTParser() + { + _vttdelimiter = new[] { "-->", "- >", "->" }; + + regexTimestamp = new Regex(@"(-)?(([0-9]+):)?([0-9]+):([0-9]+)[,\\.]([0-9]+)"); + regexTag = new Regex(@"<[^>]*>"); // For now, all tags will be ignored & removed. + + regexOffset = new Regex(@"Offset:\s*((-)?(([0-9]+):)?([0-9]+):([0-9]+)[,\\.]([0-9]+));?"); // i.e. "WebVTT Offset: 00:01.001;" + regexLang = new Regex(@"Language:\s*([A-Za-z]+);?"); // i.e. "WebVTT Language: ja;" + } + + #region Dispose stuff + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (!_isDisposed && disposing) + { + _vttdelimiter = null; + regexTimestamp = null; + regexTag = null; + + regexOffset = null; + regexLang = null; + + _isDisposed = true; + } + + } + #endregion + + internal List ParseVTTFile(string filepath, int order, CPrivateFastFont drawer) + { + List lrclist = new List() { }; + List lines = File.ReadAllLines(filepath).ToList(); + long offset = 0; + + // Header stuff + if (lines[0].StartsWith("WEBVTT")) + { + Match languageMatch = regexLang.Match(lines[0]); + if (languageMatch.Success) + { + if (!(languageMatch.Groups[1].Value.ToLower() == CLangManager.fetchLang())) + { + Debug.WriteLine("WebVTT header language does not match user's selected language. Aborting VTT parse."); + return lrclist; + } + } + Match offsetMatch = regexOffset.Match(lines[0]); + if (offsetMatch.Success) + { + offset = ParseTimestamp(offsetMatch.Groups[1].Value); + } + } + else + { + Debug.WriteLine("WebVTT lyric file at {0} does not start with \"WEBVTT\". Aborting VTT parse."); + return lrclist; + } + + List<(string, long)> lyrics = new List<(string, long)>() { }; + + long startTime = -1; + long endTime = -1; + string line = String.Empty; + + for (int i = 1; i < lines.Count; i++) // Skip header line (line 0) + { + long start; + long end; + + if (TryParseTimestamp(lines[i], out start, out end)) + { + if (start > endTime && endTime != -1) + { + lyrics.Add((string.Empty, endTime + offset)); + // If new start timestamp is greater than old end timestamp, + // it is assumed there is a gap in-between displaying lyrics. + } + + if (startTime != -1) + { + lyrics.Add((line, startTime + offset)); + // When new timestamp is found, + // create lyrics with existing timestamp. + } + + startTime = start; + endTime = end; + + line = string.Empty; + } + else // If timestamp parse failed, let's assume it's a lyric. ¯\_(ツ)_/¯ + { + if (line != "") line += Environment.NewLine; + line += String.Join(String.Empty, regexTag.Split(lines[i])); // Remove tags by splitting, then join back into single string + } + } + lyrics.Add((line, startTime + offset)); + lyrics.Add((String.Empty, endTime + offset)); + + foreach ((string, long) lyric in lyrics) + { + lrclist.Add(CreateLyric(lyric, drawer, order)); + } + + return lrclist; + + } + internal bool TryParseTimestamp(string input, out long startTime, out long endTime) + { + var split = input.Split(_vttdelimiter, StringSplitOptions.None); + if (split.Length == 2 && regexTimestamp.IsMatch(split[0]) && regexTimestamp.IsMatch(split[1])) + { + startTime = ParseTimestamp(split[0]); + endTime = ParseTimestamp(split[1]); + return true; + } + else + { + startTime = -1; + endTime = -1; + return false; + } + } + internal long ParseTimestamp(string input) + { + int hours; + int minutes; + int seconds; + int milliseconds = -1; + + Match match = regexTimestamp.Match(input); + if (match.Success) + { + hours = !string.IsNullOrEmpty(match.Groups[3].Value) ? int.Parse(match.Groups[3].Value) : 0; // Hours are sometimes not included in timestamps. + minutes = int.Parse(match.Groups[4].Value); + seconds = int.Parse(match.Groups[5].Value); + milliseconds = int.Parse(match.Groups[6].Value); + + TimeSpan result = new TimeSpan(0, hours, minutes, seconds, milliseconds); + return (long)result.TotalMilliseconds * (match.Groups[1].Value == "-" ? -1 : 1); + } + + return -1; + } + internal STLYRIC CreateLyric(string lyric, long time, CPrivateFastFont draw, int order) + { + return CreateLyric((lyric, time), draw, order); + } + internal STLYRIC CreateLyric((string, long) lyric, CPrivateFastFont draw, int order) + { + string[] split = lyric.Item1.Split(new string[]{"\\r\\n", "\\n", "\r\n", "\n"}, StringSplitOptions.RemoveEmptyEntries); + + List textures = new List(); + int width = 1; + int height = 1; + + for (int i = 0; i < split.Length; i++) + { + textures.Add(draw.DrawPrivateFont(split[i], TJAPlayer3.Skin.Game_Lyric_ForeColor, TJAPlayer3.Skin.Game_Lyric_BackColor)); + width = width < textures[i].Width ? textures[i].Width : width; + height += textures[i].Height; + } + + Bitmap texture = new Bitmap(width, height); + + if (textures.Count != 0) + { + using (Graphics canvas = Graphics.FromImage(texture)) + { + canvas.Clear(Color.Transparent); + + Point pos = new Point(0, 0); + foreach (Bitmap bitmap in textures) + { + pos.X = (width - bitmap.Width) / 2; + canvas.DrawImage(bitmap, pos); + pos.Y += bitmap.Height; + } + } + } + + STLYRIC stlrc = new STLYRIC() + { + + Text = lyric.Item1, + TextTex = texture, + Time = lyric.Item2, + index = order + }; + + return stlrc; + } + } +} diff --git a/TJAPlayer3/TJAPlayer3.csproj b/TJAPlayer3/TJAPlayer3.csproj index c22460ae..33c3e459 100644 --- a/TJAPlayer3/TJAPlayer3.csproj +++ b/TJAPlayer3/TJAPlayer3.csproj @@ -152,6 +152,7 @@ + diff --git a/Test/Songs/08 OpenTaiko Original/008 - DONT_LOOK_BACK/DON'T LOOK BACK.tja b/Test/Songs/08 OpenTaiko Original/008 - DONT_LOOK_BACK/DON'T LOOK BACK.tja index 65623887..4f56b31c 100644 --- a/Test/Songs/08 OpenTaiko Original/008 - DONT_LOOK_BACK/DON'T LOOK BACK.tja +++ b/Test/Songs/08 OpenTaiko Original/008 - DONT_LOOK_BACK/DON'T LOOK BACK.tja @@ -4,6 +4,7 @@ BPM:200 WAVE:DON'T LOOK BACK.ogg OFFSET:-0.0095 DEMOSTART:44.082 +LYRICS:DON'T LOOK BACK.vtt SCOREMODE:2 PREIMAGE:DLB.png MAKER:bol diff --git a/Test/Songs/08 OpenTaiko Original/008 - DONT_LOOK_BACK/DON'T LOOK BACK.vtt b/Test/Songs/08 OpenTaiko Original/008 - DONT_LOOK_BACK/DON'T LOOK BACK.vtt new file mode 100644 index 00000000..73f2326e --- /dev/null +++ b/Test/Songs/08 OpenTaiko Original/008 - DONT_LOOK_BACK/DON'T LOOK BACK.vtt @@ -0,0 +1,43 @@ +WEBVTT + +01:18.205 --> 01:20.338 +Coming through straight, I rest my case (Do it, do it) + +01:20.338 --> 01:22.472 +Hands up in the sunshine when we (Do it, do it) + +01:22.472 --> 01:24.338 +You know that when the beat drop, I always need more + +01:24.338 --> 01:25.672 +But that shawty says she with the glock so + +01:25.672 --> 01:25.938 +DON'T + +01:25.938 --> 01:26.205 +DON'T LOOK + +01:26.205 --> 01:26.738 +DON'T LOOK BACK + +01:26.738 --> 01:28.872 +Coming through straight, I rest my case (Do it, do it) + +01:28.872 --> 01:31.005 +Hands up in the sunshine when we (Do it, do it) + +01:31.005 --> 01:32.872 +You know that when the beat drop, I always need more + +01:32.872 --> 01:34.205 +But that shawty says she with the glock so + +01:34.205 --> 01:34.472 +DON'T + +01:34.472 --> 01:34.738 +DON'T LOOK + +01:34.738 --> 01:35.005 +DON'T LOOK BACK \ No newline at end of file diff --git a/Test/Songs/08 OpenTaiko Original/038 - Dear Stars/stars.tja b/Test/Songs/08 OpenTaiko Original/038 - Dear Stars/stars.tja index 776eaff1..54c3b921 100644 --- a/Test/Songs/08 OpenTaiko Original/038 - Dear Stars/stars.tja +++ b/Test/Songs/08 OpenTaiko Original/038 - Dear Stars/stars.tja @@ -5,6 +5,7 @@ WAVE:stars.ogg OFFSET:-0.028 MAKER:Dashy DEMOSTART:59.472 +LYRICS:stars.vtt PREIMAGE:stars.png BGMOVIE:stars.mp4 diff --git a/Test/Songs/08 OpenTaiko Original/038 - Dear Stars/stars.vtt b/Test/Songs/08 OpenTaiko Original/038 - Dear Stars/stars.vtt new file mode 100644 index 00000000..551291ad --- /dev/null +++ b/Test/Songs/08 OpenTaiko Original/038 - Dear Stars/stars.vtt @@ -0,0 +1,90 @@ +WEBVTT + +00:00.732 --> 00:07.683 +another day has passed + +00:07.683 --> 00:13.902 +and you've awakened as always + +00:13.902 --> 00:20.488 +each and every million of stars, you're all + +00:20.488 --> 00:25.427 +finally with me at last + +00:25.427 --> 00:33.659 +so i ask to you, dear stars... + +00:36.585 --> 00:39.146 +"why must this happen? + +00:39.146 --> 00:42.439 +these little mistakes hurt so much + +00:42.439 --> 00:45.183 +all of these fake words + +00:45.183 --> 00:48.110 +all i did today... hurt so much" + +00:48.110 --> 00:51.036 +"i can only hide these feelings each day + +00:51.036 --> 00:53.780 +and stay tainted with regret + +00:53.780 --> 00:56.890 +and perhaps you're the only ones + +00:56.890 --> 01:00.000 +who understand how i feel" + +01:00.000 --> 01:05.487 +"shining above me, watching over me + +01:05.487 --> 01:08.597 +i can only wish, dear stars + +01:08.597 --> 01:11.524 +that you could tell me what i could do" + +01:11.524 --> 01:14.634 +"you saw everything, you felt everything, + +01:14.634 --> 01:17.378 +you've been through it all, you know it all" + +01:17.378 --> 01:18.841 +why can't i just stay here + +01:18.841 --> 01:20.304 +and find you all again + +01:20.304 --> 01:22.682 +until i fall asleep? + +01:22.682 --> 01:25.609 +dear twinkling, shining stars, + +01:25.609 --> 01:29.268 +oh, whatever could i do...? + +01:52.682 --> 01:58.170 +i don't want to go on +i don't want to, i don't want to, i don't want to, i don't want to, + +01:58.170 --> 02:03.292 +i can't keep going anymore +not anymore, not anymore, not anymore... + +02:03.292 --> 02:06.036 +so, dear stars, +so, dear stars, + +02:06.036 --> 02:13.170 +let me stay with you forever +let me stay with you forever + +02:13.170 --> 02:16.096 +please +please diff --git a/Test/Songs/08 OpenTaiko Original/039 - Forever Fading Away/Forever Fading Away.tja b/Test/Songs/08 OpenTaiko Original/039 - Forever Fading Away/Forever Fading Away.tja index 081391e9..26cdfb28 100644 --- a/Test/Songs/08 OpenTaiko Original/039 - Forever Fading Away/Forever Fading Away.tja +++ b/Test/Songs/08 OpenTaiko Original/039 - Forever Fading Away/Forever Fading Away.tja @@ -4,6 +4,7 @@ BPM:200 WAVE:Forever Fading Away.ogg OFFSET:-0.029 DEMOSTART:95.529 +LYRICS:Forever Fading Away.vtt PREIMAGE:Forever Fading Away.png BGMOVIE:Forever Fading Away.mp4 MAKER:DashyDesu & Colin diff --git a/Test/Songs/08 OpenTaiko Original/039 - Forever Fading Away/Forever Fading Away.vtt b/Test/Songs/08 OpenTaiko Original/039 - Forever Fading Away/Forever Fading Away.vtt new file mode 100644 index 00000000..56aa151e --- /dev/null +++ b/Test/Songs/08 OpenTaiko Original/039 - Forever Fading Away/Forever Fading Away.vtt @@ -0,0 +1,142 @@ +WEBVTT + +00:38.400 --> 00:44.100 +Cloudless faded light blue skies + +00:44.100 --> 00:48.000 +surrounded the world he saw + +00:48.000 --> 00:52.800 +an urgency was suddenly born + +00:52.800 --> 00:57.600 +as that memory began to disappear... + +00:57.600 --> 01:01.800 +place, faces, sounds and tastes + +01:01.800 --> 01:06.900 +empty streets, and light gray drizzling clouds + +01:06.900 --> 01:11.400 +He seeked to discover the reason + +01:11.400 --> 01:16.800 +for such settings being so familiar + +01:16.800 --> 01:17.400 +mem + +01:17.400 --> 01:18.000 +memor + +01:18.000 --> 01:18.600 +memories + +01:18.600 --> 01:19.200 +memories and + +01:19.200 --> 01:19.800 +memories and frag + +01:19.800 --> 01:20.400 +memories and fragments + +01:20.400 --> 01:21.000 +memories and fragments of + +01:21.000 --> 01:21.600 +memories and fragments of that + +01:21.600 --> 01:22.800 +memories and fragments of that time + +01:22.800 --> 01:23.400 +he + +01:23.400 --> 01:24.000 +he re + +01:24.000 --> 01:25.200 +he remem + +01:25.200 --> 01:26.400 +he remembered + +01:26.400 --> 01:27.000 +that + +01:27.000 --> 01:27.600 +that mom + +01:27.600 --> 01:28.200 +that moment + +01:28.200 --> 01:28.800 +that moment breezed + +01:28.800 --> 01:29.400 +that moment breezed by + +01:29.400 --> 01:30.000 +that moment breezed by from + +01:30.000 --> 01:30.600 +that moment breezed by from long + +01:30.600 --> 01:31.200 +that moment breezed by from long a + +01:31.200 --> 01:32.400 +that moment breezed by from long ago + +01:32.400 --> 01:33.000 +he + +01:33.000 --> 01:33.600 +he re + +01:33.600 --> 01:34.200 +he real + +01:34.200 --> 01:35.400 +he realized... + +01:35.400 --> 01:37.800 +as all those moments dissolve away, + +01:37.800 --> 01:40.200 +just like always... + +01:40.200 --> 01:45.000 +just like always, he'll continue living life his way... + +01:45.000 --> 01:51.600 +only covered in thoughts as time fades away + +01:51.600 --> 01:54.750 +time and time again... + +01:54.750 --> 01:59.400 +no matter hours, seconds, weeks, they disappear + +01:59.400 --> 02:04.200 +they disappear and are left with not any meaning + +02:04.200 --> 02:10.800 +the time passes and then never reappears... + +02:10.800 --> 02:19.200 +he stays in that pure white world... + +02:33.600 --> 02:43.200 +now this time that was once at his fingertips + +02:43.200 --> 02:49.200 +had began to drift away + +02:49.200 --> 02:52.800 +so he remains in the pure white world... + +02:52.800 --> 03:02.400 +forever, endless, ilimitable. \ No newline at end of file diff --git a/Test/Songs/08 OpenTaiko Original/043 - TRIPLE HELIX/TRIPLE HELIX.tja b/Test/Songs/08 OpenTaiko Original/043 - TRIPLE HELIX/TRIPLE HELIX.tja index c7a0183f..901e0c27 100644 --- a/Test/Songs/08 OpenTaiko Original/043 - TRIPLE HELIX/TRIPLE HELIX.tja +++ b/Test/Songs/08 OpenTaiko Original/043 - TRIPLE HELIX/TRIPLE HELIX.tja @@ -4,6 +4,7 @@ BPM:160 WAVE:TRIPLE HELIX.ogg OFFSET:-0.001 DEMOSTART:120.001 +LYRICS:TRIPLE HELIX.vtt MAKER:Colin COURSE:Oni diff --git a/Test/Songs/08 OpenTaiko Original/043 - TRIPLE HELIX/TRIPLE HELIX.vtt b/Test/Songs/08 OpenTaiko Original/043 - TRIPLE HELIX/TRIPLE HELIX.vtt new file mode 100644 index 00000000..26815618 --- /dev/null +++ b/Test/Songs/08 OpenTaiko Original/043 - TRIPLE HELIX/TRIPLE HELIX.vtt @@ -0,0 +1,14 @@ +WEBVTT + +00:42.000 --> 00:57.000 +(rapping) + +01:00.000 --> 01:03.375 +(rapping) + +01:06.000 --> 01:09.000 +(rapping) + +01:11.813 --> 01:15.000 +(rapping) + \ No newline at end of file