From bf1c00b5d1aa23db7992e15d8c9cb53081a566fd Mon Sep 17 00:00:00 2001 From: Flamefire Date: Tue, 8 Oct 2013 20:15:34 +0200 Subject: [PATCH 01/23] Some fixes and support for more than 2 voices --- Vocaluxe/Base/CMain.cs | 2 +- Vocaluxe/Screens/CScreenNames.cs | 8 +++-- Vocaluxe/Screens/CScreenScore.cs | 6 ++-- Vocaluxe/Screens/CScreenSing.cs | 9 ++--- VocaluxeLib/Extensions.cs | 52 +++++++++++++++++++++++++++ VocaluxeLib/IBasic.cs | 2 +- VocaluxeLib/Songs/CNotes.cs | 61 ++++++++++++++++++++++++++------ VocaluxeLib/Songs/CSong.cs | 56 ++++++++++++----------------- VocaluxeLib/Songs/CSongLine.cs | 7 ++-- VocaluxeLib/VocaluxeLib.csproj | 3 +- 10 files changed, 144 insertions(+), 62 deletions(-) create mode 100644 VocaluxeLib/Extensions.cs diff --git a/Vocaluxe/Base/CMain.cs b/Vocaluxe/Base/CMain.cs index 2d2a9ea9..8a40c555 100644 --- a/Vocaluxe/Base/CMain.cs +++ b/Vocaluxe/Base/CMain.cs @@ -110,7 +110,7 @@ namespace Vocaluxe.Base return CConfig.CoverSize; } - public IEnumerable GetSongFolder() + public IEnumerable GetSongFolders() { return CConfig.SongFolder; } diff --git a/Vocaluxe/Screens/CScreenNames.cs b/Vocaluxe/Screens/CScreenNames.cs index f1f548ab..62a99c37 100644 --- a/Vocaluxe/Screens/CScreenNames.cs +++ b/Vocaluxe/Screens/CScreenNames.cs @@ -22,6 +22,7 @@ using Vocaluxe.Base; using VocaluxeLib; using VocaluxeLib.Draw; using VocaluxeLib.Menu; +using VocaluxeLib.Songs; namespace Vocaluxe.Screens { @@ -597,6 +598,8 @@ namespace Vocaluxe.Screens _CheckMics(); _CheckPlayers(); + CSong firstSong = CGame.GetSong(0); + for (int i = 0; i < CSettings.MaxNumPlayer; i++) { _Statics[_StaticPlayerAvatar[i]].Texture = CProfiles.IsProfileIDValid(CGame.Players[i].ProfileID) ? @@ -607,8 +610,9 @@ namespace Vocaluxe.Screens { _SelectSlides[_SelectSlideDuetPlayer[i]].Clear(); _SelectSlides[_SelectSlideDuetPlayer[i]].Visible = i + 1 <= CGame.NumPlayer; - _SelectSlides[_SelectSlideDuetPlayer[i]].AddValue(CGame.GetSong(1).DuetPart1); - _SelectSlides[_SelectSlideDuetPlayer[i]].AddValue(CGame.GetSong(1).DuetPart2); + + for (int j = 0; j < firstSong.Notes.VoiceCount; j++) + _SelectSlides[_SelectSlideDuetPlayer[i]].AddValue(firstSong.Notes.VoiceNames[j]); _SelectSlides[_SelectSlideDuetPlayer[i]].Selection = i % 2; } else diff --git a/Vocaluxe/Screens/CScreenScore.cs b/Vocaluxe/Screens/CScreenScore.cs index fac4c499..b1e06b1e 100644 --- a/Vocaluxe/Screens/CScreenScore.cs +++ b/Vocaluxe/Screens/CScreenScore.cs @@ -297,10 +297,8 @@ namespace Vocaluxe.Screens string name = CProfiles.GetPlayerName(players[p].ProfileID, p); if (song != null && song.IsDuet) { - if (players[p].VoiceNr == 0 && song.DuetPart1 != "Part 1") - name += " (" + song.DuetPart1 + ")"; - else if (players[p].VoiceNr == 1 && song.DuetPart2 != "Part 2") - name += " (" + song.DuetPart2 + ")"; + if (song.Notes.VoiceNames.IsSet(players[p].VoiceNr)) + name += " (" + song.Notes.VoiceNames[players[p].VoiceNr] + ")"; } _Texts[_TextNames[p, CGame.NumPlayer - 1]].Text = name; diff --git a/Vocaluxe/Screens/CScreenSing.cs b/Vocaluxe/Screens/CScreenSing.cs index e485c684..08cf762a 100644 --- a/Vocaluxe/Screens/CScreenSing.cs +++ b/Vocaluxe/Screens/CScreenSing.cs @@ -634,8 +634,9 @@ namespace Vocaluxe.Screens if (song.IsDuet) { - _Texts[_TextDuetName1].Text = song.DuetPart1; - _Texts[_TextDuetName2].Text = song.DuetPart2; + //TODO: Show more than 2 voicenames + _Texts[_TextDuetName1].Text = song.Notes.VoiceNames[0]; + _Texts[_TextDuetName2].Text = song.Notes.VoiceNames[1]; //More than one song: Player is not assigned to line by user //Otherwise, this is done by CScreenNames if (CGame.GetNumSongs() > 1) @@ -781,7 +782,7 @@ namespace Vocaluxe.Screens _CurrentBeat = CGame.CurrentBeat; - for (int i = 0; i < song.Notes.LinesCount; i++) + for (int i = 0; i < song.Notes.VoiceCount; i++) { if (i > 1) break; // for later @@ -1093,7 +1094,7 @@ namespace Vocaluxe.Screens var alpha = new float[song.Notes.Voices.Length * 2]; float currentTime = _CurrentTime - song.Gap; - for (int i = 0; i < song.Notes.LinesCount; i++) + for (int i = 0; i < song.Notes.VoiceCount; i++) { CVoice voice = song.Notes.GetVoice(i); CSongLine[] lines = voice.Lines; diff --git a/VocaluxeLib/Extensions.cs b/VocaluxeLib/Extensions.cs new file mode 100644 index 00000000..b970b6bc --- /dev/null +++ b/VocaluxeLib/Extensions.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace VocaluxeLib +{ + public static class Extensions + { + /// + /// Resizes the given list to the given size. Removes elements or adds default values + /// + /// + /// + /// + /// + public static void Resize(this List list, int size, T defaultValue = default(T)) + { + int curSize = list.Count; + if (size < curSize) + list.RemoveRange(size, curSize - size); + else if (size > curSize) + list.AddRange(Enumerable.Repeat(defaultValue, size - curSize)); + } + + /// + /// Makes the list at least the given size adding the given default values. + /// + /// + /// + /// + /// + public static void EnsureSize(this List list, int size, T defaultValue) + { + int curSize = list.Count; + if (size > curSize) + list.AddRange(Enumerable.Repeat(defaultValue, size - curSize)); + } + + /// + /// Makes the list at least the given size adding a new instance of T + /// + /// + /// + /// + public static void EnsureSize(this List list, int size) where T : new() + { + if (size > list.Count) + list.Add(new T()); + } + } +} \ No newline at end of file diff --git a/VocaluxeLib/IBasic.cs b/VocaluxeLib/IBasic.cs index 2c34490b..c6893634 100644 --- a/VocaluxeLib/IBasic.cs +++ b/VocaluxeLib/IBasic.cs @@ -45,7 +45,7 @@ namespace VocaluxeLib int GetCoverSize(); - IEnumerable GetSongFolder(); + IEnumerable GetSongFolders(); ESongSorting GetSongSorting(); EOffOn GetTabs(); EOffOn GetIgnoreArticles(); diff --git a/VocaluxeLib/Songs/CNotes.cs b/VocaluxeLib/Songs/CNotes.cs index f1ab9cbb..66750715 100644 --- a/VocaluxeLib/Songs/CNotes.cs +++ b/VocaluxeLib/Songs/CNotes.cs @@ -21,26 +21,65 @@ namespace VocaluxeLib.Songs { public class CNotes { + public class CVoiceNames + { + private readonly List _Names = new List(); + + public CVoiceNames() {} + + public CVoiceNames(CVoiceNames names) + { + _Names.AddRange(names._Names); + } + + public string this[int index] + { + get + { + if (IsSet(index)) + return _Names[index]; + return "Part " + (index + 1); + } + set + { + _Names.EnsureSize(index + 1, null); + _Names[index] = value; + } + } + + public bool IsSet(int index) + { + return _Names.Count > index && !string.IsNullOrEmpty(_Names[index]); + } + + public void Reset() + { + _Names.Clear(); + } + } + private readonly List _Voices = new List(); + public readonly CVoiceNames VoiceNames = new CVoiceNames(); + + public CNotes() {} + + public CNotes(CNotes notes) + { + foreach (CVoice voice in notes._Voices) + _Voices.Add(new CVoice(voice)); + VoiceNames = new CVoiceNames(notes.VoiceNames); + } public CVoice[] Voices { get { return _Voices.ToArray(); } } - public CNotes() {} - - public int LinesCount + public int VoiceCount { get { return _Voices.Count; } } - public CNotes(CNotes notes) - { - foreach (CVoice voice in notes._Voices) - _Voices.Add(new CVoice(voice)); - } - public CVoice GetVoice(int index) { while (index >= _Voices.Count) @@ -79,9 +118,11 @@ namespace VocaluxeLib.Songs return true; } - public void Reset() + public void Reset(bool resetVoices = false) { _Voices.Clear(); + if (resetVoices) + VoiceNames.Reset(); } public void SetMedley(int startBeat, int endBeat) diff --git a/VocaluxeLib/Songs/CSong.cs b/VocaluxeLib/Songs/CSong.cs index 35e15d34..3a820b76 100644 --- a/VocaluxeLib/Songs/CSong.cs +++ b/VocaluxeLib/Songs/CSong.cs @@ -124,9 +124,6 @@ namespace VocaluxeLib.Songs public float Gap; public float VideoGap; - public string DuetPart1 = String.Empty; - public string DuetPart2 = String.Empty; - private readonly List _Comment = new List(); // Sorting @@ -192,8 +189,6 @@ namespace VocaluxeLib.Songs PreviewStart = song.PreviewStart; ShortEnd = song.ShortEnd; - DuetPart1 = song.DuetPart1; - DuetPart2 = song.DuetPart2; Encoding = song.Encoding; Folder = song.Folder; @@ -268,7 +263,7 @@ namespace VocaluxeLib.Songs if (Folder == null) return false; - foreach (string folder in CBase.Config.GetSongFolder()) + foreach (string folder in CBase.Config.GetSongFolders()) { if (!Folder.Contains(folder)) continue; @@ -318,7 +313,7 @@ namespace VocaluxeLib.Songs sr = null; sr = new StreamReader(filePath, Encoding); - //Scip everything till encoding + //Skip everything till encoding while (!sr.EndOfStream) { line = sr.ReadLine(); @@ -362,12 +357,12 @@ namespace VocaluxeLib.Songs case "DUETSINGERP1": case "P1": if (value != "") - DuetPart1 = value; + Notes.VoiceNames[0] = value; break; case "DUETSINGERP2": case "P2": if (value != "") - DuetPart2 = value; + Notes.VoiceNames[1] = value; break; case "MP3": if (File.Exists(Path.Combine(Folder, value))) @@ -462,7 +457,7 @@ namespace VocaluxeLib.Songs CalculateMedley = false; break; case "RELATIVE": - if (value.ToUpper() == "YES" && value.ToUpper() != "NO") + if (value.ToUpper() == "YES") Relative = EOffOn.TR_CONFIG_ON; break; } @@ -569,7 +564,7 @@ namespace VocaluxeLib.Songs int currentPos = 0; bool isNewSentence = false; - int player = 0; + int player = 1; int fileLineNo = 0; StreamReader sr = null; @@ -611,14 +606,7 @@ namespace VocaluxeLib.Songs char chr; while ((chr = (char)sr.Read()) == ' ') {} - int.TryParse(chr.ToString(), out param1); - if (param1 == 1) - player = 0; - else if (param1 == 2) - player = 1; - else if (param1 == 3) - player = 2; - else + if (!int.TryParse(chr.ToString(), out player)) { CBase.Log.LogError("Error loading song. Line No.: " + fileLineNo + ". Wrong or missing number after \"P\": " + filePath); return false; @@ -652,14 +640,19 @@ namespace VocaluxeLib.Songs if (Relative == EOffOn.TR_CONFIG_ON) param1 += currentPos; - if (player != 2) - // one singer - _ParseNote(player, noteType, param1, param2, param3, paramS); - else + int curPlayer = 0; + int tmpPlayer = player; + //Evaluate as bitset + while (tmpPlayer > 0) { - // both singer - _ParseNote(0, noteType, param1, param2, param3, paramS); - _ParseNote(1, noteType, param1, param2, param3, paramS); + if ((tmpPlayer & 1) != 0) + { + if (!_ParseNote(curPlayer, noteType, param1, param2, param3, paramS)) + CBase.Log.LogError("Warning! Ignored note for player " + (curPlayer + 1) + " in song because it overlaps with other note. Line No.: " + + fileLineNo + ": " + filePath); + } + tmpPlayer >>= 1; + curPlayer++; } isNewSentence = false; break; @@ -789,15 +782,10 @@ namespace VocaluxeLib.Songs private void _CheckDuet() { - if (DuetPart1 == "") + for (int i = 0; i < Notes.VoiceCount; i++) { - DuetPart1 = "Part 1"; - CBase.Log.LogError("Warning: Can't find #P1-tag for duets in \"" + Artist + " - " + Title + "\"."); - } - if (DuetPart2 == "") - { - DuetPart2 = "Part 2"; - CBase.Log.LogError("Warning: Can't find #P2-tag for duets in \"" + Artist + " - " + Title + "\"."); + if (!Notes.VoiceNames.IsSet(i)) + CBase.Log.LogError("Warning: Can't find #P" + (i + 1) + "-tag for duets in \"" + Artist + " - " + Title + "\"."); } } diff --git a/VocaluxeLib/Songs/CSongLine.cs b/VocaluxeLib/Songs/CSongLine.cs index 9f678a5c..19a94238 100644 --- a/VocaluxeLib/Songs/CSongLine.cs +++ b/VocaluxeLib/Songs/CSongLine.cs @@ -90,11 +90,8 @@ namespace VocaluxeLib.Songs public void SetMedley(int startBeat, int endBeat) { - foreach (CSongNote note in _Notes) - { - if (note.StartBeat < startBeat || note.EndBeat > endBeat) - note.NoteType = ENoteType.Freestyle; - } + foreach (CSongNote note in _Notes.Where(note => note.StartBeat < startBeat || note.EndBeat > endBeat)) + note.NoteType = ENoteType.Freestyle; VisibleInTimeLine = FirstNoteBeat >= startBeat && LastNoteBeat <= endBeat; } diff --git a/VocaluxeLib/VocaluxeLib.csproj b/VocaluxeLib/VocaluxeLib.csproj index 5b895271..0fb808a3 100644 --- a/VocaluxeLib/VocaluxeLib.csproj +++ b/VocaluxeLib/VocaluxeLib.csproj @@ -130,6 +130,7 @@ + @@ -195,4 +196,4 @@ --> - + \ No newline at end of file From 55d5c61a6656d1d1c0b381812d585e81c0400b46 Mon Sep 17 00:00:00 2001 From: Flamefire Date: Wed, 9 Oct 2013 12:23:10 +0200 Subject: [PATCH 02/23] Fix overlapping notes More consistent CLyric Some renaming for clearer meaning --- Vocaluxe/Base/CGame.cs | 6 +- Vocaluxe/Screens/CScreenSing.cs | 121 +++++++++--------- VocaluxeLib/Menu/CLyric.cs | 155 ++++++++--------------- VocaluxeLib/Menu/CText.cs | 2 +- VocaluxeLib/Menu/SingNotes/CSingNotes.cs | 6 +- VocaluxeLib/Songs/CBaseNote.cs | 4 + VocaluxeLib/Songs/CLineBase.cs | 6 +- VocaluxeLib/Songs/CSong.cs | 51 ++++---- VocaluxeLib/Songs/CSongLine.cs | 17 ++- VocaluxeLib/Songs/CSongNote.cs | 8 +- VocaluxeLib/Songs/CVoice.cs | 2 + 11 files changed, 177 insertions(+), 201 deletions(-) diff --git a/Vocaluxe/Base/CGame.cs b/Vocaluxe/Base/CGame.cs index dced2fa5..1b80d09f 100644 --- a/Vocaluxe/Base/CGame.cs +++ b/Vocaluxe/Base/CGame.cs @@ -290,7 +290,7 @@ namespace Vocaluxe.Base //CSound.RecordSetTone(p, Tone); double points = (CSettings.MaxScore - CSettings.LinebonusScore) * (double)notes[note].PointsForBeat / song.Notes.GetVoice(Players[p].VoiceNr).Points; - if (notes[note].NoteType == ENoteType.Golden) + if (notes[note].Type == ENoteType.Golden) Players[p].PointsGoldenNotes += points; Players[p].Points += points; @@ -384,7 +384,7 @@ namespace Vocaluxe.Base { if (note.StartBeat < nextStart) { - if (note.Hit && note.HitNote.NoteType==ENoteType.Golden) + if (note.Hit && note.HitNote.Type == ENoteType.Golden) Players[p].PointsGoldenNotes += note.Points; Players[p].Points += note.Points; } @@ -394,7 +394,7 @@ namespace Vocaluxe.Base } while (line.NoteCount > n && n >= 0) { - if (line.Notes[n].Hit && line.Notes[n].HitNote.NoteType == ENoteType.Golden) + if (line.Notes[n].Hit && line.Notes[n].HitNote.Type == ENoteType.Golden) Players[p].PointsGoldenNotes -= line.Notes[n].Points; Players[p].Points -= line.Notes[n].Points; line.DeleteNote(n); diff --git a/Vocaluxe/Screens/CScreenSing.cs b/Vocaluxe/Screens/CScreenSing.cs index 08cf762a..cde771bc 100644 --- a/Vocaluxe/Screens/CScreenSing.cs +++ b/Vocaluxe/Screens/CScreenSing.cs @@ -327,7 +327,7 @@ namespace Vocaluxe.Screens _LoadNextSong(); _UpdateSongText(); - _UpdateDuetText(); + _UpdateDuetNames(); if (_FadeOut) return true; @@ -387,12 +387,8 @@ namespace Vocaluxe.Screens for (int p = 0; p < CGame.NumPlayer; p++) { - // ReSharper disable ConvertIfStatementToConditionalTernaryExpression - if (CGame.Players[p].Points < 10000) - // ReSharper restore ConvertIfStatementToConditionalTernaryExpression - _Texts[_TextScores[p, CGame.NumPlayer - 1]].Text = CGame.Players[p].Points.ToString("0000"); - else - _Texts[_TextScores[p, CGame.NumPlayer - 1]].Text = CGame.Players[p].Points.ToString("00000"); + string fmtString = (CGame.Players[p].Points < 10000) ? "0000" : "00000"; + _Texts[_TextScores[p, CGame.NumPlayer - 1]].Text = CGame.Players[p].Points.ToString(fmtString); } if (_CurrentVideo != -1 && !_FadeOut && CConfig.VideosInSongs == EOffOn.TR_CONFIG_ON) @@ -607,12 +603,12 @@ namespace Vocaluxe.Screens _FinishTime = song.Finish; _TimeToFirstNote = 0f; _TimeToFirstNoteDuet = 0f; - var duetPlayer = new int[CGame.NumPlayer]; + var voiceAssignments = new int[CGame.NumPlayer]; if (song.IsDuet) { //Save duet-assignment before resetting - for (int i = 0; i < duetPlayer.Length; i++) - duetPlayer[i] = CGame.Players[i].VoiceNr; + for (int i = 0; i < voiceAssignments.Length; i++) + voiceAssignments[i] = CGame.Players[i].VoiceNr; } CGame.ResetPlayer(); @@ -647,7 +643,7 @@ namespace Vocaluxe.Screens else { for (int i = 0; i < CGame.NumPlayer; i++) - CGame.Players[i].VoiceNr = duetPlayer[i]; + CGame.Players[i].VoiceNr = voiceAssignments[i]; } } @@ -673,14 +669,11 @@ namespace Vocaluxe.Screens NoteLines[3] = SingNotes[SingBars].AddPlayer(new SRectF(640f, 350f, 590f, 200f, -0.5f), CTheme.ThemeColors.Player[3]); break; */ - _TimerSongText.Stop(); _TimerSongText.Reset(); - _TimerDuetText1.Stop(); _TimerDuetText1.Reset(); - _TimerDuetText2.Stop(); _TimerDuetText2.Reset(); - if (song.Notes.Voices.Length != 2) + if (!song.IsDuet) _TimerSongText.Start(); _StartSong(); @@ -1198,8 +1191,8 @@ namespace Vocaluxe.Screens } else { - _Statics[_StaticSongText].Color.A = (3f - (t - 7f)) / 3f; - _Texts[_TextSongName].Color.A = (3f - (t - 7f)) / 3f; + _Statics[_StaticSongText].Color.A = 10f - t / 3f; + _Texts[_TextSongName].Color.A = 10f - t / 3f; } } else @@ -1216,61 +1209,65 @@ namespace Vocaluxe.Screens } } - private void _UpdateDuetText() + private void _UpdateDuetNames(CText textName, Stopwatch timer) { - if (CGame.GetSong() != null) + if (timer.IsRunning) { - //Timer for first duet-part - if (_TimerDuetText1.IsRunning) + float t = timer.ElapsedMilliseconds / 1000f; + if (t < 10f) { - float t = _TimerDuetText1.ElapsedMilliseconds / 1000f; - if (t < 10f) - { - _Texts[_TextDuetName1].Visible = true; + textName.Visible = true; - if (t < 3f) - _Texts[_TextDuetName1].Color.A = (3f - (3f - t)) / 3f; - else if (t < 7f) - _Texts[_TextDuetName1].Color.A = 1f; - else - _Texts[_TextDuetName1].Color.A = (3f - (t - 7f)) / 3f; - } + if (t < 3f) + textName.Color.A = t / 3f; + else if (t < 7f) + textName.Color.A = 1f; else - { - _Texts[_TextDuetName1].Visible = false; - _TimerDuetText1.Stop(); - } + textName.Color.A = (10f - t) / 3f; } - else if (!_TimerDuetText1.IsRunning && _TimerDuetText1.ElapsedMilliseconds == 0 && _Lyrics[_LyricMainDuet].Alpha > 0 && CGame.GetSong().IsDuet) - _TimerDuetText1.Start(); else - _Texts[_TextDuetName1].Visible = false; - //Timer for second duet-part - if (_TimerDuetText2.IsRunning) { - float t = _TimerDuetText2.ElapsedMilliseconds / 1000f; - if (t < 10f) - { - _Texts[_TextDuetName2].Visible = true; - - if (t < 3f) - _Texts[_TextDuetName2].Color.A = (3f - (3f - t)) / 3f; - else if (t < 7f) - _Texts[_TextDuetName2].Color.A = 1f; - else - _Texts[_TextDuetName2].Color.A = (3f - (t - 7f)) / 3f; - } - else - { - _Texts[_TextDuetName2].Visible = false; - _TimerDuetText2.Stop(); - } + textName.Visible = false; + timer.Stop(); } - else if (!_TimerDuetText2.IsRunning && _TimerDuetText2.ElapsedMilliseconds == 0 && _Lyrics[_LyricMain].Alpha > 0 && CGame.GetSong().IsDuet) - _TimerDuetText2.Start(); - else - _Texts[_TextDuetName2].Visible = false; } + else if (!timer.IsRunning && timer.ElapsedMilliseconds == 0 && _Lyrics[_LyricMainDuet].Alpha > 0 && CGame.GetSong().IsDuet) + timer.Start(); + else + textName.Visible = false; + } + + private void _UpdateDuetNames() + { + if (CGame.GetSong() == null) + return; + //Timer for first duet-part + _UpdateDuetNames(_Texts[_TextDuetName1], _TimerDuetText1); + //Timer for second duet-part + if (_TimerDuetText2.IsRunning) + { + float t = _TimerDuetText2.ElapsedMilliseconds / 1000f; + if (t < 10f) + { + _Texts[_TextDuetName2].Visible = true; + + if (t < 3f) + _Texts[_TextDuetName2].Color.A = (3f - (3f - t)) / 3f; + else if (t < 7f) + _Texts[_TextDuetName2].Color.A = 1f; + else + _Texts[_TextDuetName2].Color.A = (3f - (t - 7f)) / 3f; + } + else + { + _Texts[_TextDuetName2].Visible = false; + _TimerDuetText2.Stop(); + } + } + else if (!_TimerDuetText2.IsRunning && _TimerDuetText2.ElapsedMilliseconds == 0 && _Lyrics[_LyricMain].Alpha > 0 && CGame.GetSong().IsDuet) + _TimerDuetText2.Start(); + else + _Texts[_TextDuetName2].Visible = false; } private void _UpdateTimeLine() diff --git a/VocaluxeLib/Menu/CLyric.cs b/VocaluxeLib/Menu/CLyric.cs index 0191be9e..8fbdc15c 100644 --- a/VocaluxeLib/Menu/CLyric.cs +++ b/VocaluxeLib/Menu/CLyric.cs @@ -28,7 +28,6 @@ namespace VocaluxeLib.Menu public string Text; public int StartBeat; public int EndBeat; - public int Duration; public ENoteType Type; } @@ -46,7 +45,10 @@ namespace VocaluxeLib.Menu private SThemeLyrics _Theme; private bool _ThemeLoaded; - private readonly List _Notes; + /// + /// Holds a reference to Songline. DO NOT MODIFY + /// + private CSongLine _Line; private CText _Text; private float _X; @@ -109,7 +111,7 @@ namespace VocaluxeLib.Menu _MaxW = 1f; _H = 1f; _Width = 1f; - _Notes = new List(); + _Line = new CSongLine(); _Text = new CText(_PartyModeID); LyricStyle = ELyricStyle.Fill; @@ -201,28 +203,27 @@ namespace VocaluxeLib.Menu public void SetLine(CSongLine line) { - _Notes.Clear(); - + _Line = line; _Width = 0f; foreach (CSongNote note in line.Notes) { - var n = new SNote {Text = note.Text, StartBeat = note.StartBeat, EndBeat = note.EndBeat, Duration = note.Duration, Type = note.NoteType}; - - _Text.Text = note.Text; - _Text.Style = EStyle.Bold; - - if (n.Type == ENoteType.Freestyle) - _Text.Style = EStyle.BoldItalic; - - RectangleF rect = CBase.Fonts.GetTextBounds(_Text); - _Width += rect.Width; - _Notes.Add(n); + _SetText(note); + _Width += _Text.Rect.W; } } + private void _SetText(CSongNote note) + { + _Text.Text = note.Text; + _Text.Style = EStyle.Bold; + + if (note.Type == ENoteType.Freestyle) + _Text.Style = EStyle.BoldItalic; + } + public void Clear() { - _Notes.Clear(); + _Line = new CSongLine(); } public float GetCurrentLyricPosX() @@ -260,15 +261,10 @@ namespace VocaluxeLib.Menu { float x = _X - _Width / 2; - foreach (SNote note in _Notes) + foreach (CSongNote note in _Line.Notes) { _Text.X = x; - _Text.Style = EStyle.Bold; - _Text.Text = note.Text; - RectangleF rect = CBase.Fonts.GetTextBounds(_Text); - - if (note.Type == ENoteType.Freestyle) - _Text.Style = EStyle.BoldItalic; + _SetText(note); if (currentBeat >= note.StartBeat) { @@ -276,9 +272,8 @@ namespace VocaluxeLib.Menu { _Text.Color = _ColorProcessed; - - float diff = note.EndBeat - note.StartBeat; - if (Math.Abs(diff) < float.Epsilon) + int diff = note.EndBeat - note.StartBeat; + if (diff <= 0) _Text.Draw(0f, 1f); else { @@ -299,7 +294,7 @@ namespace VocaluxeLib.Menu _Text.Draw(); } - x += rect.Width; + x += _Text.Rect.W; } } @@ -308,37 +303,27 @@ namespace VocaluxeLib.Menu float x = _X - _Width / 2; // most left position //find last active note - int lastNote = -1; - for (int i = 0; i < _Notes.Count; i++) - { - if (currentBeat >= _Notes[i].StartBeat) - lastNote = i; - } + int lastNote = _Line.FindPreviousNote((int)currentBeat); int zoomNote = -1; int endBeat = -1; float zoomx = 0f; - for (int note = 0; note < _Notes.Count; note++) + for (int note = 0; note < _Line.Notes.Length; note++) { _Text.X = x; - _Text.Style = EStyle.Bold; - _Text.Text = _Notes[note].Text; - RectangleF rect = CBase.Fonts.GetTextBounds(_Text); + _SetText(_Line.Notes[note]); - if (_Notes[note].Type == ENoteType.Freestyle) - _Text.Style = EStyle.BoldItalic; - - if (currentBeat >= _Notes[note].StartBeat) + if (currentBeat >= _Line.Notes[note].StartBeat) { - int endbeat = _Notes[note].EndBeat; - if (note < _Notes.Count - 1) - endbeat = _Notes[note + 1].StartBeat - 1; + int curEndBeat = _Line.Notes[note].EndBeat; + if (note < _Line.Notes.Length - 1) + curEndBeat = _Line.Notes[note + 1].StartBeat - 1; - if (currentBeat <= endbeat) + if (currentBeat <= curEndBeat) { zoomNote = note; - endBeat = endbeat; + endBeat = curEndBeat; zoomx = _Text.X; } else @@ -356,49 +341,37 @@ namespace VocaluxeLib.Menu _Text.Draw(); } - x += rect.Width; + x += _Text.Rect.W; } if (zoomNote > -1) { - if (_Notes[zoomNote].Duration == 0) - return; - _Text.X = zoomx; - _Text.Text = _Notes[zoomNote].Text; - _Text.Color = _ColorProcessed; - _Text.Style = EStyle.Bold; + _SetText(_Line.Notes[zoomNote]); - if (_Notes[zoomNote].Type == ENoteType.Freestyle) - _Text.Style = EStyle.BoldItalic; - - RectangleF rect = CBase.Fonts.GetTextBounds(_Text); - - float diff = endBeat - _Notes[zoomNote].StartBeat; + float diff = endBeat - _Line.Notes[zoomNote].StartBeat; if (diff <= 0f) diff = 1f; - float p = (currentBeat - _Notes[zoomNote].StartBeat) / diff; + float p = (currentBeat - _Line.Notes[zoomNote].StartBeat) / diff; if (p > 1f) p = 1f; p = 1f - p; float ty = _Text.Y; - float tx = _Text.X; float th = _Text.Height; float tz = _Text.Z; + SRectF normalRect = _Text.Rect; _Text.Height += _Text.Height * p * 0.4f; - RectangleF rectz = CBase.Fonts.GetTextBounds(_Text); - _Text.X -= (rectz.Width - rect.Width) / 2f; - _Text.Y -= (rectz.Height - rect.Height) / 2f; + _Text.X -= (_Text.Rect.W - normalRect.W) / 2f; + _Text.Y -= (_Text.Rect.W - normalRect.H) / 2f; _Text.Z -= 0.1f; _Text.Draw(); _Text.Y = ty; - _Text.X = tx; _Text.Height = th; _Text.Z = tz; } @@ -408,15 +381,10 @@ namespace VocaluxeLib.Menu { float x = _X - _Width / 2; // most left position - foreach (SNote note in _Notes) + foreach (CSongNote note in _Line.Notes) { _Text.X = x; - _Text.Style = EStyle.Bold; - _Text.Text = note.Text; - RectangleF rect = CBase.Fonts.GetTextBounds(_Text); - - if (note.Type == ENoteType.Freestyle) - _Text.Style = EStyle.BoldItalic; + _SetText(note); if (currentBeat >= note.StartBeat) { @@ -430,7 +398,7 @@ namespace VocaluxeLib.Menu _Text.Draw(); } - x += rect.Width; + x += _Text.Rect.W; } } @@ -439,29 +407,23 @@ namespace VocaluxeLib.Menu float x = _X - _Width / 2; // most left position //find last active note - int lastNote = -1; - for (int i = 0; i < _Notes.Count; i++) - { - if (currentBeat >= _Notes[i].StartBeat) - lastNote = i; - } + int lastNote = _Line.FindPreviousNote((int)currentBeat); int jumpNote = -1; float jumpx = 0f; - for (int note = 0; note < _Notes.Count; note++) + for (int note = 0; note < _Line.Notes.Length; note++) { _Text.X = x; - _Text.Style = EStyle.Bold; - _Text.Text = _Notes[note].Text; - RectangleF rect = CBase.Fonts.GetTextBounds(_Text); + _SetText(_Line.Notes[note]); - if (_Notes[note].Type == ENoteType.Freestyle) - _Text.Style = EStyle.BoldItalic; - - if (currentBeat >= _Notes[note].StartBeat) + if (currentBeat >= _Line.Notes[note].StartBeat) { - if (currentBeat <= _Notes[note].EndBeat) + int curEndBeat = _Line.Notes[note].EndBeat; + if (note < _Line.Notes.Length - 1) + curEndBeat = _Line.Notes[note + 1].StartBeat - 1; + + if (currentBeat <= curEndBeat) { jumpNote = note; jumpx = _Text.X; @@ -481,27 +443,20 @@ namespace VocaluxeLib.Menu _Text.Draw(); } - x += rect.Width; + x += _Text.Rect.W; } if (jumpNote < 0) return; - if (_Notes[jumpNote].Duration == 0) - return; _Text.X = jumpx; - _Text.Text = _Notes[jumpNote].Text; - _Text.Color = _ColorProcessed; - _Text.Style = EStyle.Bold; + _SetText(_Line.Notes[jumpNote]); - if (_Notes[jumpNote].Type == ENoteType.Freestyle) - _Text.Style = EStyle.BoldItalic; - - int diff = _Notes[jumpNote].EndBeat - _Notes[jumpNote].StartBeat; + int diff = _Line.Notes[jumpNote].EndBeat - _Line.Notes[jumpNote].StartBeat; if (diff <= 0) diff = 1; - float p = 1f - (currentBeat - _Notes[jumpNote].StartBeat) / diff; + float p = 1f - (currentBeat - _Line.Notes[jumpNote].StartBeat) / diff; if (Math.Abs(p) < float.Epsilon) _Text.Draw(); diff --git a/VocaluxeLib/Menu/CText.cs b/VocaluxeLib/Menu/CText.cs index 21c07a6f..94c2c01c 100644 --- a/VocaluxeLib/Menu/CText.cs +++ b/VocaluxeLib/Menu/CText.cs @@ -561,7 +561,7 @@ namespace VocaluxeLib.Menu float y = Y; RectangleF bounds = CBase.Fonts.GetTextBounds(this); - if (bounds.Width > MaxWidth && MaxWidth > 0f && bounds.Width > 0f) + if (MaxWidth > 0f && bounds.Width > MaxWidth && bounds.Width > 0f) { float factor = MaxWidth / bounds.Width; float step = h * (1 - factor); diff --git a/VocaluxeLib/Menu/SingNotes/CSingNotes.cs b/VocaluxeLib/Menu/SingNotes/CSingNotes.cs index 0904d121..a3a189a3 100644 --- a/VocaluxeLib/Menu/SingNotes/CSingNotes.cs +++ b/VocaluxeLib/Menu/SingNotes/CSingNotes.cs @@ -319,7 +319,7 @@ namespace VocaluxeLib.Menu.SingNotes int nr = 1; foreach (CSongNote note in line.Notes) { - if (note.NoteType != ENoteType.Freestyle) + if (note.Type != ENoteType.Freestyle) { float width = note.Duration / beats * w; @@ -334,7 +334,7 @@ namespace VocaluxeLib.Menu.SingNotes _DrawNoteBG(rect, color, 1f, _PlayerNotes[n].Timer); _DrawNote(rect, new SColorF(5f, 5f, 5f, 0.7f * _PlayerNotes[n].Alpha), 0.7f); - if (note.NoteType == ENoteType.Golden) + if (note.Type == ENoteType.Golden) { _AddGoldenNote(rect, n, nr); nr++; @@ -386,7 +386,7 @@ namespace VocaluxeLib.Menu.SingNotes _DrawNote(rect, color, f); - if (note.EndBeat >= CBase.Game.GetRecordedBeat() && note.Hit && note.HitNote.NoteType == ENoteType.Golden) + if (note.EndBeat >= CBase.Game.GetRecordedBeat() && note.Hit && note.HitNote.Type == ENoteType.Golden) { var re = new SRectF(rect) {W = (CBase.Game.GetMidRecordedBeat() - note.StartBeat) / beats * w}; _AddFlare(re, n); diff --git a/VocaluxeLib/Songs/CBaseNote.cs b/VocaluxeLib/Songs/CBaseNote.cs index e6fa193d..1c81038b 100644 --- a/VocaluxeLib/Songs/CBaseNote.cs +++ b/VocaluxeLib/Songs/CBaseNote.cs @@ -15,6 +15,8 @@ // along with Vocaluxe. If not, see . #endregion +using System; + namespace VocaluxeLib.Songs { /// @@ -38,6 +40,8 @@ namespace VocaluxeLib.Songs StartBeat = startBeat; Duration = duration; Tone = tone; + if (Duration < 1) + throw new Exception("Note to short. All notes should be at least 1 beat long!"); } #endregion Constructors diff --git a/VocaluxeLib/Songs/CLineBase.cs b/VocaluxeLib/Songs/CLineBase.cs index 0a819509..937f5647 100644 --- a/VocaluxeLib/Songs/CLineBase.cs +++ b/VocaluxeLib/Songs/CLineBase.cs @@ -79,7 +79,11 @@ namespace VocaluxeLib.Songs #endregion Properties #region Methods - //Find last note with StartBeat<=Beat + /// + /// Find last note with StartBeat<=Beat + /// + /// + /// Note index of last note with StartBeat<=Beat or -1 if all notes start after beat public int FindPreviousNote(int beat) { //If no notes -> No previous note diff --git a/VocaluxeLib/Songs/CSong.cs b/VocaluxeLib/Songs/CSong.cs index 3a820b76..17ea1848 100644 --- a/VocaluxeLib/Songs/CSong.cs +++ b/VocaluxeLib/Songs/CSong.cs @@ -628,33 +628,40 @@ namespace VocaluxeLib.Songs sr.Read(); string paramS = sr.ReadLine(); - ENoteType noteType; - - if (tempC.CompareTo('*') == 0) - noteType = ENoteType.Golden; - else if (tempC.CompareTo('F') == 0) - noteType = ENoteType.Freestyle; + if (param2 < 1) + CBase.Log.LogError("Warning! Ignored note in song because length is < 1. Line No.: " + fileLineNo + ": " + filePath); else - noteType = ENoteType.Normal; - - if (Relative == EOffOn.TR_CONFIG_ON) - param1 += currentPos; - - int curPlayer = 0; - int tmpPlayer = player; - //Evaluate as bitset - while (tmpPlayer > 0) { - if ((tmpPlayer & 1) != 0) + ENoteType noteType; + + if (tempC.CompareTo('*') == 0) + noteType = ENoteType.Golden; + else if (tempC.CompareTo('F') == 0) + noteType = ENoteType.Freestyle; + else + noteType = ENoteType.Normal; + + if (Relative == EOffOn.TR_CONFIG_ON) + param1 += currentPos; + + int curPlayer = 0; + int tmpPlayer = player; + //Evaluate as bitset + while (tmpPlayer > 0) { - if (!_ParseNote(curPlayer, noteType, param1, param2, param3, paramS)) - CBase.Log.LogError("Warning! Ignored note for player " + (curPlayer + 1) + " in song because it overlaps with other note. Line No.: " + - fileLineNo + ": " + filePath); + if ((tmpPlayer & 1) != 0) + { + if (!_ParseNote(curPlayer, noteType, param1, param2, param3, paramS)) + { + CBase.Log.LogError("Warning! Ignored note for player " + (curPlayer + 1) + " in song because it overlaps with other note. Line No.: " + + fileLineNo + ": " + filePath); + } + } + tmpPlayer >>= 1; + curPlayer++; } - tmpPlayer >>= 1; - curPlayer++; + isNewSentence = false; } - isNewSentence = false; break; case '-': if (isNewSentence) diff --git a/VocaluxeLib/Songs/CSongLine.cs b/VocaluxeLib/Songs/CSongLine.cs index 19a94238..1e98ac20 100644 --- a/VocaluxeLib/Songs/CSongLine.cs +++ b/VocaluxeLib/Songs/CSongLine.cs @@ -77,12 +77,19 @@ namespace VocaluxeLib.Songs _Notes.Add(note); else { + //Insert AFTER this note int insPos = FindPreviousNote(note.StartBeat); //Check for overlapping notes - if (insPos >= 0 && _Notes[insPos].EndBeat > note.StartBeat) - return false; - if (insPos < _Notes.Count - 1 && _Notes[insPos + 1].StartBeat > note.EndBeat) - return false; + if (insPos >= 0) //Check note before + { + if (_Notes[insPos].EndBeat > note.StartBeat) + return false; + } + if (insPos < _Notes.Count - 1) //Check note after + { + if (_Notes[insPos + 1].StartBeat < note.EndBeat) + return false; + } _Notes.Insert(insPos + 1, note); } return true; @@ -91,7 +98,7 @@ namespace VocaluxeLib.Songs public void SetMedley(int startBeat, int endBeat) { foreach (CSongNote note in _Notes.Where(note => note.StartBeat < startBeat || note.EndBeat > endBeat)) - note.NoteType = ENoteType.Freestyle; + note.Type = ENoteType.Freestyle; VisibleInTimeLine = FirstNoteBeat >= startBeat && LastNoteBeat <= endBeat; } diff --git a/VocaluxeLib/Songs/CSongNote.cs b/VocaluxeLib/Songs/CSongNote.cs index a300fa31..3079c61e 100644 --- a/VocaluxeLib/Songs/CSongNote.cs +++ b/VocaluxeLib/Songs/CSongNote.cs @@ -25,20 +25,20 @@ namespace VocaluxeLib.Songs #region Contructors public CSongNote(CSongNote note) : base(note) { - NoteType = note.NoteType; + Type = note.Type; Text = note.Text; } public CSongNote(int startBeat, int duration, int tone, string text, ENoteType noteType) : base(startBeat, duration, tone) { - NoteType = noteType; + Type = noteType; Text = text; } #endregion Constructors #region Properties - public ENoteType NoteType { get; set; } + public ENoteType Type { get; set; } public string Text { get; set; } @@ -46,7 +46,7 @@ namespace VocaluxeLib.Songs { get { - switch (NoteType) + switch (Type) { case ENoteType.Normal: return 1; diff --git a/VocaluxeLib/Songs/CVoice.cs b/VocaluxeLib/Songs/CVoice.cs index cee926c9..238ab9ea 100644 --- a/VocaluxeLib/Songs/CVoice.cs +++ b/VocaluxeLib/Songs/CVoice.cs @@ -122,6 +122,8 @@ namespace VocaluxeLib.Songs public bool AddNote(CSongNote note, bool updateTimings = false) { int lineIndex = FindPreviousLine(note.StartBeat); + if (lineIndex + 1 < _Lines.Count && _Lines[lineIndex + 1].FirstNoteBeat < note.EndBeat) //First note in next line starts before this one ends + return false; if (lineIndex < 0) { //Note is before ALL lines From ed220887280fa09bad0c373bc789cb2441177fa0 Mon Sep 17 00:00:00 2001 From: Flamefire Date: Wed, 9 Oct 2013 18:39:16 +0200 Subject: [PATCH 03/23] Split CSong to have single file for note and header reading. Some simpler assignments and more error checking --- VocaluxeLib/CHelper.cs | 2 +- VocaluxeLib/Songs/CSong.cs | 513 +------------------------ VocaluxeLib/Songs/SongNotesLoader.cs | 536 +++++++++++++++++++++++++++ VocaluxeLib/VocaluxeLib.csproj | 1 + 4 files changed, 544 insertions(+), 508 deletions(-) create mode 100644 VocaluxeLib/Songs/SongNotesLoader.cs diff --git a/VocaluxeLib/CHelper.cs b/VocaluxeLib/CHelper.cs index ad04b6db..97192ae1 100644 --- a/VocaluxeLib/CHelper.cs +++ b/VocaluxeLib/CHelper.cs @@ -214,7 +214,7 @@ namespace VocaluxeLib case "UTF8": return Encoding.UTF8; default: - return Encoding.UTF8; + return Encoding.Default; } } diff --git a/VocaluxeLib/Songs/CSong.cs b/VocaluxeLib/Songs/CSong.cs index 17ea1848..8957ffd2 100644 --- a/VocaluxeLib/Songs/CSong.cs +++ b/VocaluxeLib/Songs/CSong.cs @@ -66,7 +66,7 @@ namespace VocaluxeLib.Songs public float FadeOutTime; } - public class CSong + public partial class CSong { private CTexture _CoverTextureSmall; private CTexture _CoverTextureBig; @@ -168,7 +168,7 @@ namespace VocaluxeLib.Songs public static CSong LoadSong(string filePath) { var song = new CSong(); - return song.ReadHeader(filePath) ? song : null; + return song._InitPaths(filePath) && song._ReadHeader() ? song : null; } public CSong(CSong song) @@ -214,28 +214,20 @@ namespace VocaluxeLib.Songs Gap = song.Gap; VideoGap = song.VideoGap; - _Comment = new List(); - foreach (string value in song._Comment) - _Comment.Add(value); + _Comment = new List(song._Comment); ID = song.ID; _Visible = song._Visible; _CatIndex = song._CatIndex; _Selected = song._Selected; - Edition = new List(); - foreach (string value in song.Edition) - Edition.Add(value); + Edition = new List(song.Edition); - Genre = new List(); - foreach (string value in song.Genre) - Genre.Add(value); + Genre = new List(song.Genre); Year = song.Year; - Language = new List(); - foreach (string value in song.Language) - Language.Add(value); + Language = new List(song.Language); DataBaseSongID = song.DataBaseSongID; DateAdded = song.DateAdded; @@ -254,499 +246,6 @@ namespace VocaluxeLib.Songs return Path.Combine(Folder, VideoFileName); } - public bool ReadHeader(string filePath) - { - if (!File.Exists(filePath)) - return false; - - Folder = Path.GetDirectoryName(filePath); - if (Folder == null) - return false; - - foreach (string folder in CBase.Config.GetSongFolders()) - { - if (!Folder.Contains(folder)) - continue; - if (Folder.Length == folder.Length) - FolderName = "Songs"; - else - { - FolderName = Folder.Substring(folder.Length + 1, Folder.Length - folder.Length - 1); - - int pos = FolderName.IndexOf("\\", StringComparison.Ordinal); - if (pos >= 0) - FolderName = FolderName.Substring(0, pos); - } - break; - } - - FileName = Path.GetFileName(filePath); - - var headerFlags = new EHeaderFlags(); - StreamReader sr = null; - try - { - sr = new StreamReader(filePath, Encoding.Default, true); - - while (!sr.EndOfStream) - { - string line = sr.ReadLine(); - if (line == "") - continue; - if (!line[0].ToString().Equals("#")) - break; - - int pos = line.IndexOf(":", StringComparison.Ordinal); - - if (pos <= 0) - continue; - string identifier = line.Substring(1, pos - 1).Trim().ToUpper(); - string value = line.Substring(pos + 1, line.Length - pos - 1).Trim(); - - if (value != "") - { - switch (identifier) - { - case "ENCODING": - Encoding = CEncoding.GetEncoding(value); - sr.Dispose(); - sr = null; - sr = new StreamReader(filePath, Encoding); - - //Skip everything till encoding - while (!sr.EndOfStream) - { - line = sr.ReadLine(); - if (line == "") - continue; - if (!line[0].ToString().Equals("#")) - break; - - pos = line.IndexOf(":", StringComparison.Ordinal); - - if (pos > 0) - { - identifier = line.Substring(1, pos - 1).Trim().ToUpper(); - if (identifier == "ENCODING") - break; - } - } - break; - case "TITLE": - if (value != "") - { - Title = value; - headerFlags |= EHeaderFlags.Title; - } - break; - case "ARTIST": - if (value != "") - { - Artist = value; - headerFlags |= EHeaderFlags.Artist; - } - break; - case "TITLE-ON-SORTING": - if (value != "") - TitleSorting = value; - break; - case "ARTIST-ON-SORTING": - if (value != "") - ArtistSorting = value; - break; - case "DUETSINGERP1": - case "P1": - if (value != "") - Notes.VoiceNames[0] = value; - break; - case "DUETSINGERP2": - case "P2": - if (value != "") - Notes.VoiceNames[1] = value; - break; - case "MP3": - if (File.Exists(Path.Combine(Folder, value))) - { - MP3FileName = value; - headerFlags |= EHeaderFlags.MP3; - } - else - { - CBase.Log.LogError("Can't find audio file: " + Path.Combine(Folder, value)); - return false; - } - break; - case "BPM": - if (CHelper.TryParse(value, out BPM)) - { - BPM *= 4; - headerFlags |= EHeaderFlags.BPM; - } - break; - case "EDITION": - if (value.Length > 1) - Edition.Add(value); - break; - case "GENRE": - if (value.Length > 1) - Genre.Add(value); - break; - case "YEAR": - int num; - if (value.Length == 4 && int.TryParse(value, out num) && num != 0) - Year = value; - break; - case "LANGUAGE": - if (value.Length > 1) - Language.Add(value); - break; - case "COMMENT": - if (value.Length > 1) - _Comment.Add(value); - break; - case "GAP": - if (CHelper.TryParse(value, out Gap)) - Gap /= 1000f; - break; - case "COVER": - if (File.Exists(Path.Combine(Folder, value))) - CoverFileName = value; - break; - case "BACKGROUND": - if (File.Exists(Path.Combine(Folder, value))) - BackgroundFileName = value; - break; - case "VIDEO": - if (File.Exists(Path.Combine(Folder, value))) - VideoFileName = value; - else - CBase.Log.LogError("Can't find video file: " + Path.Combine(Folder, value)); - break; - case "VIDEOGAP": - CHelper.TryParse(value, out VideoGap); - break; - case "VIDEOASPECT": - CHelper.TryParse(value, out VideoAspect, true); - break; - case "START": - CHelper.TryParse(value, out Start); - break; - case "END": - if (CHelper.TryParse(value, out Finish)) - Finish /= 1000f; - break; - case "PREVIEWSTART": - if (CHelper.TryParse(value, out PreviewStart)) - { - if (PreviewStart < 0f) - PreviewStart = 0f; - else - headerFlags |= EHeaderFlags.PreviewStart; - } - break; - case "MEDLEYSTARTBEAT": - if (int.TryParse(value, out Medley.StartBeat)) - headerFlags |= EHeaderFlags.MedleyStartBeat; - break; - case "MEDLEYENDBEAT": - if (int.TryParse(value, out Medley.EndBeat)) - headerFlags |= EHeaderFlags.MedleyEndBeat; - break; - case "CALCMEDLEY": - if (value.ToUpper() == "OFF") - CalculateMedley = false; - break; - case "RELATIVE": - if (value.ToUpper() == "YES") - Relative = EOffOn.TR_CONFIG_ON; - break; - } - } - } //end of while - - if (sr.EndOfStream) - { - //No other data then header - CBase.Log.LogError("Lyrics/Notes missing: " + filePath); - return false; - } - - if ((headerFlags & EHeaderFlags.Title) == 0) - { - CBase.Log.LogError("Title tag missing: " + filePath); - return false; - } - - if ((headerFlags & EHeaderFlags.Artist) == 0) - { - CBase.Log.LogError("Artist tag missing: " + filePath); - return false; - } - - if ((headerFlags & EHeaderFlags.MP3) == 0) - { - CBase.Log.LogError("MP3 tag missing: " + filePath); - return false; - } - - if ((headerFlags & EHeaderFlags.BPM) == 0) - { - CBase.Log.LogError("BPM tag missing: " + filePath); - return false; - } - - #region check medley tags - if ((headerFlags & EHeaderFlags.MedleyStartBeat) != 0 && (headerFlags & EHeaderFlags.MedleyEndBeat) != 0) - { - if (Medley.StartBeat > Medley.EndBeat) - { - CBase.Log.LogError("MedleyStartBeat is bigger than MedleyEndBeat in file: " + filePath); - headerFlags = headerFlags - EHeaderFlags.MedleyStartBeat - EHeaderFlags.MedleyEndBeat; - } - } - - if ((headerFlags & EHeaderFlags.PreviewStart) == 0 || Math.Abs(PreviewStart) < float.Epsilon) - { - //PreviewStart is not set or <=0 - PreviewStart = (headerFlags & EHeaderFlags.MedleyStartBeat) != 0 ? CBase.Game.GetTimeFromBeats(Medley.StartBeat, BPM) : 0f; - } - - if ((headerFlags & EHeaderFlags.MedleyStartBeat) != 0 && (headerFlags & EHeaderFlags.MedleyEndBeat) != 0) - { - Medley.Source = EMedleySource.Tag; - Medley.FadeInTime = CBase.Settings.GetDefaultMedleyFadeInTime(); - Medley.FadeOutTime = CBase.Settings.GetDefaultMedleyFadeOutTime(); - } - #endregion check medley tags - - Encoding = sr.CurrentEncoding; - } - catch (Exception e) - { - if (sr != null) - sr.Dispose(); - CBase.Log.LogError("Error reading txt header in file \"" + filePath + "\": " + e.Message); - return false; - } - sr.Dispose(); - _CheckFiles(); - - CBase.DataBase.GetDataBaseSongInfos(Artist, Title, out NumPlayed, out DateAdded, out DataBaseSongID); - - //Before saving this tags to .txt: Check, if ArtistSorting and Artist are equal, then don't save this tag. - if (ArtistSorting == "") - ArtistSorting = Artist; - - if (TitleSorting == "") - TitleSorting = Title; - - return true; - } - - public bool ReadNotes() - { - return _ReadNotes(Path.Combine(Folder, FileName)); - } - - private bool _ReadNotes(string filePath, bool forceReload = false) - { - //Skip loading if already done and no reload is forced - if (NotesLoaded && !forceReload) - return true; - - if (!File.Exists(filePath)) - { - CBase.Log.LogError("Error loading song. The file does not exist: " + filePath); - return false; - } - - char tempC = char.MinValue; - int currentPos = 0; - bool isNewSentence = false; - - int player = 1; - int fileLineNo = 0; - - StreamReader sr = null; - try - { - sr = new StreamReader(filePath, Encoding, true); - - Notes.Reset(); - - //Search for Note Beginning - while (!sr.EndOfStream) - { - sr.ReadLine(); - fileLineNo++; - - tempC = (char)sr.Peek(); - if ((tempC == '#') || (tempC == '\r') || (tempC == '\n')) - continue; - if ((tempC == ':') || (tempC == 'F') || (tempC == '*') || (tempC == 'P')) - { - tempC = (char)sr.Read(); - break; - } - } - - if (sr.EndOfStream) - { - CBase.Log.LogError("Error loading song. Line No.: " + fileLineNo + ". No lyrics/notes found: " + filePath); - return false; - } - - do - { - int param2; - int param1; - switch (tempC) - { - case 'P': - char chr; - while ((chr = (char)sr.Read()) == ' ') {} - - if (!int.TryParse(chr.ToString(), out player)) - { - CBase.Log.LogError("Error loading song. Line No.: " + fileLineNo + ". Wrong or missing number after \"P\": " + filePath); - return false; - } - sr.ReadLine(); - break; - case ':': - case '*': - case 'F': - sr.Read(); - param1 = CHelper.TryReadInt(sr); - - sr.Read(); - param2 = CHelper.TryReadInt(sr); - - sr.Read(); - int param3 = CHelper.TryReadInt(sr); - - sr.Read(); - string paramS = sr.ReadLine(); - - if (param2 < 1) - CBase.Log.LogError("Warning! Ignored note in song because length is < 1. Line No.: " + fileLineNo + ": " + filePath); - else - { - ENoteType noteType; - - if (tempC.CompareTo('*') == 0) - noteType = ENoteType.Golden; - else if (tempC.CompareTo('F') == 0) - noteType = ENoteType.Freestyle; - else - noteType = ENoteType.Normal; - - if (Relative == EOffOn.TR_CONFIG_ON) - param1 += currentPos; - - int curPlayer = 0; - int tmpPlayer = player; - //Evaluate as bitset - while (tmpPlayer > 0) - { - if ((tmpPlayer & 1) != 0) - { - if (!_ParseNote(curPlayer, noteType, param1, param2, param3, paramS)) - { - CBase.Log.LogError("Warning! Ignored note for player " + (curPlayer + 1) + " in song because it overlaps with other note. Line No.: " + - fileLineNo + ": " + filePath); - } - } - tmpPlayer >>= 1; - curPlayer++; - } - isNewSentence = false; - } - break; - case '-': - if (isNewSentence) - { - CBase.Log.LogError("Error loading song. Line No.: " + fileLineNo + ". Double sentence break: " + filePath); - return false; - } - sr.Read(); - param1 = CHelper.TryReadInt(sr); - - if (Relative == EOffOn.TR_CONFIG_ON) - { - param1 += currentPos; - sr.Read(); - param2 = CHelper.TryReadInt(sr); - currentPos += param2; - } - - if (player != 2) - // one singer - _NewSentence(player, param1); - else - { - // both singer - _NewSentence(0, param1); - _NewSentence(1, param1); - } - - isNewSentence = true; - sr.ReadLine(); - break; - default: - CBase.Log.LogError("Error loading song. Line No.: " + fileLineNo + ". Unexpected or missing character (" + tempC + "): " + filePath); - return false; - } - int c; - do - { - c = sr.Read(); - } while (!sr.EndOfStream && (c == 19 || c == 16 || c == 13 || c == 10)); - tempC = (char)c; - fileLineNo++; - } while (!sr.EndOfStream && (tempC != 'E')); - - foreach (CVoice voice in Notes.Voices) - voice.UpdateTimings(); - } - catch (Exception e) - { - CBase.Log.LogError("Error loading song. Line No.: " + fileLineNo + ". An unhandled exception occured (" + e.Message + "): " + filePath); - if (sr != null) - sr.Dispose(); - return false; - } - sr.Dispose(); - try - { - _FindRefrain(); - _FindShortEnd(); - NotesLoaded = true; - if (IsDuet) - _CheckDuet(); - } - catch (Exception e) - { - CBase.Log.LogError("Error loading song. An unhandled exception occured (" + e.Message + "): " + filePath); - return false; - } - return true; - } - - private bool _ParseNote(int player, ENoteType noteType, int start, int length, int tone, string text) - { - var note = new CSongNote(start, length, tone, text, noteType); - CVoice voice = Notes.GetVoice(player); - return voice.AddNote(note); - } - - private void _NewSentence(int player, int start) - { - CVoice voice = Notes.GetVoice(player); - var line = new CSongLine {StartBeat = start}; - voice.AddLine(line); - } - public void LoadSmallCover() { if (_CoverTextureSmall != null) diff --git a/VocaluxeLib/Songs/SongNotesLoader.cs b/VocaluxeLib/Songs/SongNotesLoader.cs new file mode 100644 index 00000000..3e738b62 --- /dev/null +++ b/VocaluxeLib/Songs/SongNotesLoader.cs @@ -0,0 +1,536 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace VocaluxeLib.Songs +{ + /// + /// Part of CSong that is required for note loading + /// + public partial class CSong + { + private bool _InitPaths(string filePath) + { + if (!File.Exists(filePath)) + return false; + + Folder = Path.GetDirectoryName(filePath); + if (Folder == null) + return false; + + foreach (string folder in CBase.Config.GetSongFolders().Where(folder => Folder.StartsWith(folder))) + { + if (Folder.Length == folder.Length) + FolderName = "Songs"; + else + { + FolderName = Folder.Substring(folder.Length + 1); + + int pos = FolderName.IndexOf("\\", StringComparison.Ordinal); + if (pos >= 0) + FolderName = FolderName.Substring(0, pos); + } + break; + } + + FileName = Path.GetFileName(filePath); + return true; + } + + private void _LogReadError(string error, int lineNr) + { + CBase.Log.LogError(error + " in line #" + lineNr + "(" + Path.Combine(Folder, FileName) + ")"); + } + + private bool _ReadHeader(Encoding encoding = null) + { + string filePath = Path.Combine(Folder, FileName); + + if (!File.Exists(filePath)) + return false; + + Language.Clear(); + Genre.Clear(); + _Comment.Clear(); + + var headerFlags = new EHeaderFlags(); + StreamReader sr = null; + int lineNr = 0; + try + { + sr = new StreamReader(filePath, Encoding.Default, true); + while (!sr.EndOfStream) + { + string line = sr.ReadLine(); + lineNr++; + if (line == "") + continue; + if (!line[0].Equals('#')) + break; + + int pos = line.IndexOf(":", StringComparison.Ordinal); + + if (pos <= 1) + continue; + string identifier = line.Substring(1, pos - 1).Trim().ToUpper(); + string value = line.Substring(pos + 1).Trim(); + + if (value == "") + { + _LogReadError("Warning: Empty value skipped", lineNr); + continue; + } + + switch (identifier) + { + case "ENCODING": + if (encoding != null) + { + _LogReadError("Warning: Duplicate encoding ignored", lineNr); + continue; + } + Encoding newEncoding = CEncoding.GetEncoding(value); + if (!newEncoding.Equals(sr.CurrentEncoding)) + { + sr.Dispose(); + sr = null; + return _ReadHeader(Encoding); + } + break; + case "TITLE": + Title = value; + headerFlags |= EHeaderFlags.Title; + break; + case "ARTIST": + Artist = value; + headerFlags |= EHeaderFlags.Artist; + break; + case "TITLE-ON-SORTING": + TitleSorting = value; + break; + case "ARTIST-ON-SORTING": + ArtistSorting = value; + break; + case "DUETSINGERP1": + case "P1": + Notes.VoiceNames[0] = value; + break; + case "DUETSINGERP2": + case "P2": + Notes.VoiceNames[1] = value; + break; + case "MP3": + if (File.Exists(Path.Combine(Folder, value))) + { + MP3FileName = value; + headerFlags |= EHeaderFlags.MP3; + } + else + { + _LogReadError("Error: Can't find audio file: " + Path.Combine(Folder, value), lineNr); + return false; + } + break; + case "BPM": + if (CHelper.TryParse(value, out BPM)) + { + BPM *= 4; + headerFlags |= EHeaderFlags.BPM; + } + else + _LogReadError("Warning: Invalid BPM value", lineNr); + break; + case "EDITION": + if (value.Length > 1) + Edition.Add(value); + else + _LogReadError("Warning: Invalid edition", lineNr); + break; + case "GENRE": + if (value.Length > 1) + Genre.Add(value); + else + _LogReadError("Warning: Invalid genre", lineNr); + break; + case "YEAR": + int num; + if (value.Length == 4 && int.TryParse(value, out num) && num > 0) + Year = value; + else + _LogReadError("Warning: Invalid year", lineNr); + break; + case "LANGUAGE": + if (value.Length > 1) + Language.Add(value); + else + _LogReadError("Warning: Invalid language", lineNr); + break; + case "COMMENT": + if (value.Length > 1) + _Comment.Add(value); + else + _LogReadError("Warning: Invalid comment", lineNr); + break; + case "GAP": + if (CHelper.TryParse(value, out Gap)) + Gap /= 1000f; + else + _LogReadError("Warning: Invalid gap", lineNr); + break; + case "COVER": + if (File.Exists(Path.Combine(Folder, value))) + CoverFileName = value; + else + _LogReadError("Warning: Can't find cover file: " + Path.Combine(Folder, value), lineNr); + break; + case "BACKGROUND": + if (File.Exists(Path.Combine(Folder, value))) + BackgroundFileName = value; + else + _LogReadError("Warning: Can't find background file: " + Path.Combine(Folder, value), lineNr); + break; + case "VIDEO": + if (File.Exists(Path.Combine(Folder, value))) + VideoFileName = value; + else + CBase.Log.LogError("Warning: Can't find video file: " + Path.Combine(Folder, value)); + break; + case "VIDEOGAP": + if (!CHelper.TryParse(value, out VideoGap)) + _LogReadError("Warning: Invalid videogap", lineNr); + break; + case "VIDEOASPECT": + if (!CHelper.TryParse(value, out VideoAspect, true)) + _LogReadError("Warning: Invalid videoaspect", lineNr); + break; + case "START": + if (!CHelper.TryParse(value, out Start)) + _LogReadError("Warning: Invalid start", lineNr); + break; + case "END": + if (CHelper.TryParse(value, out Finish)) + Finish /= 1000f; + else + _LogReadError("Warning: Invalid end", lineNr); + break; + case "PREVIEWSTART": + if (CHelper.TryParse(value, out PreviewStart) && PreviewStart >= 0f) + headerFlags |= EHeaderFlags.PreviewStart; + else + _LogReadError("Warning: Invalid previewstart", lineNr); + break; + case "MEDLEYSTARTBEAT": + if (int.TryParse(value, out Medley.StartBeat)) + headerFlags |= EHeaderFlags.MedleyStartBeat; + else + _LogReadError("Warning: Invalid medleystartbeat", lineNr); + break; + case "MEDLEYENDBEAT": + if (int.TryParse(value, out Medley.EndBeat)) + headerFlags |= EHeaderFlags.MedleyEndBeat; + else + _LogReadError("Warning: Invalid medleyendbeat", lineNr); + break; + case "CALCMEDLEY": + if (value.ToUpper() == "OFF") + CalculateMedley = false; + break; + case "RELATIVE": + if (value.ToUpper() == "YES") + Relative = EOffOn.TR_CONFIG_ON; + break; + } + } //end of while + + if (sr.EndOfStream) + { + //No other data then header + CBase.Log.LogError("Lyrics/Notes missing: " + filePath); + return false; + } + + if ((headerFlags & EHeaderFlags.Title) == 0) + { + CBase.Log.LogError("Title tag missing: " + filePath); + return false; + } + + if ((headerFlags & EHeaderFlags.Artist) == 0) + { + CBase.Log.LogError("Artist tag missing: " + filePath); + return false; + } + + if ((headerFlags & EHeaderFlags.MP3) == 0) + { + CBase.Log.LogError("MP3 tag missing: " + filePath); + return false; + } + + if ((headerFlags & EHeaderFlags.BPM) == 0) + { + CBase.Log.LogError("BPM tag missing: " + filePath); + return false; + } + + #region check medley tags + if ((headerFlags & EHeaderFlags.MedleyStartBeat) != 0 && (headerFlags & EHeaderFlags.MedleyEndBeat) != 0) + { + if (Medley.StartBeat > Medley.EndBeat) + { + CBase.Log.LogError("MedleyStartBeat is bigger than MedleyEndBeat in file: " + filePath); + headerFlags = headerFlags - EHeaderFlags.MedleyStartBeat - EHeaderFlags.MedleyEndBeat; + } + } + + if ((headerFlags & EHeaderFlags.PreviewStart) == 0 || PreviewStart < 0) + { + //PreviewStart is not set or <=0 + PreviewStart = (headerFlags & EHeaderFlags.MedleyStartBeat) != 0 ? CBase.Game.GetTimeFromBeats(Medley.StartBeat, BPM) : 0f; + } + + if ((headerFlags & EHeaderFlags.MedleyStartBeat) != 0 && (headerFlags & EHeaderFlags.MedleyEndBeat) != 0) + { + Medley.Source = EMedleySource.Tag; + Medley.FadeInTime = CBase.Settings.GetDefaultMedleyFadeInTime(); + Medley.FadeOutTime = CBase.Settings.GetDefaultMedleyFadeOutTime(); + } + #endregion check medley tags + + Encoding = sr.CurrentEncoding; + } + catch (Exception e) + { + if (sr != null) + sr.Dispose(); + _LogReadError("Error reading txt header" + e.Message, lineNr); + return false; + } + sr.Dispose(); + _CheckFiles(); + + CBase.DataBase.GetDataBaseSongInfos(Artist, Title, out NumPlayed, out DateAdded, out DataBaseSongID); + + //Before saving this tags to .txt: Check, if ArtistSorting and Artist are equal, then don't save this tag. + if (ArtistSorting == "") + ArtistSorting = Artist; + + if (TitleSorting == "") + TitleSorting = Title; + + return true; + } + + public bool ReadNotes() + { + return _ReadNotes(Path.Combine(Folder, FileName)); + } + + private bool _ReadNotes(string filePath, bool forceReload = false) + { + //Skip loading if already done and no reload is forced + if (NotesLoaded && !forceReload) + return true; + + if (!File.Exists(filePath)) + { + CBase.Log.LogError("Error loading song. The file does not exist: " + filePath); + return false; + } + + char tempC = char.MinValue; + int currentPos = 0; + bool isNewSentence = false; + + int player = 1; + int fileLineNo = 0; + + StreamReader sr = null; + try + { + sr = new StreamReader(filePath, Encoding, true); + + Notes.Reset(); + + //Search for Note Beginning + while (!sr.EndOfStream) + { + sr.ReadLine(); + fileLineNo++; + + tempC = (char)sr.Peek(); + if ((tempC == '#') || (tempC == '\r') || (tempC == '\n')) + continue; + if ((tempC == ':') || (tempC == 'F') || (tempC == '*') || (tempC == 'P')) + { + tempC = (char)sr.Read(); + break; + } + } + + if (sr.EndOfStream) + { + CBase.Log.LogError("Error loading song. Line No.: " + fileLineNo + ". No lyrics/notes found: " + filePath); + return false; + } + + do + { + int param2; + int param1; + switch (tempC) + { + case 'P': + char chr; + while ((chr = (char)sr.Read()) == ' ') {} + + if (!int.TryParse(chr.ToString(), out player)) + { + CBase.Log.LogError("Error loading song. Line No.: " + fileLineNo + ". Wrong or missing number after \"P\": " + filePath); + return false; + } + sr.ReadLine(); + break; + case ':': + case '*': + case 'F': + sr.Read(); + param1 = CHelper.TryReadInt(sr); + + sr.Read(); + param2 = CHelper.TryReadInt(sr); + + sr.Read(); + int param3 = CHelper.TryReadInt(sr); + + sr.Read(); + string paramS = sr.ReadLine(); + + if (param2 < 1) + CBase.Log.LogError("Warning! Ignored note in song because length is < 1. Line No.: " + fileLineNo + ": " + filePath); + else + { + ENoteType noteType; + + if (tempC.CompareTo('*') == 0) + noteType = ENoteType.Golden; + else if (tempC.CompareTo('F') == 0) + noteType = ENoteType.Freestyle; + else + noteType = ENoteType.Normal; + + if (Relative == EOffOn.TR_CONFIG_ON) + param1 += currentPos; + + int curPlayer = 0; + int tmpPlayer = player; + //Evaluate as bitset + while (tmpPlayer > 0) + { + if ((tmpPlayer & 1) != 0) + { + if (!_ParseNote(curPlayer, noteType, param1, param2, param3, paramS)) + { + CBase.Log.LogError("Warning! Ignored note for player " + (curPlayer + 1) + " in song because it overlaps with other note. Line No.: " + + fileLineNo + ": " + filePath); + } + } + tmpPlayer >>= 1; + curPlayer++; + } + } + isNewSentence = false; + break; + case '-': + if (isNewSentence) + { + CBase.Log.LogError("Error loading song. Line No.: " + fileLineNo + ". Double sentence break: " + filePath); + return false; + } + sr.Read(); + param1 = CHelper.TryReadInt(sr); + + if (Relative == EOffOn.TR_CONFIG_ON) + { + param1 += currentPos; + sr.Read(); + param2 = CHelper.TryReadInt(sr); + currentPos += param2; + } + + if (param1 < 1) + CBase.Log.LogError("Warning! Ignored line break in song because position is < 1. Line No.: " + fileLineNo + ": " + filePath); + else + { + int curPlayer = 0; + int tmpPlayer = player; + //Evaluate as bitset + while (tmpPlayer > 0) + { + if ((tmpPlayer & 1) != 0) + _NewSentence(curPlayer, param1); + tmpPlayer >>= 1; + curPlayer++; + } + + isNewSentence = true; + } + sr.ReadLine(); + break; + default: + CBase.Log.LogError("Error loading song. Line No.: " + fileLineNo + ". Unexpected or missing character (" + tempC + "): " + filePath); + return false; + } + int c; + do + { + c = sr.Read(); + } while (!sr.EndOfStream && (c == 19 || c == 16 || c == 13 || c == 10)); + tempC = (char)c; + fileLineNo++; + } while (!sr.EndOfStream && (tempC != 'E')); + + foreach (CVoice voice in Notes.Voices) + voice.UpdateTimings(); + } + catch (Exception e) + { + CBase.Log.LogError("Error loading song. Line No.: " + fileLineNo + ". An unhandled exception occured (" + e.Message + "): " + filePath); + if (sr != null) + sr.Dispose(); + return false; + } + sr.Dispose(); + try + { + _FindRefrain(); + _FindShortEnd(); + NotesLoaded = true; + if (IsDuet) + _CheckDuet(); + } + catch (Exception e) + { + CBase.Log.LogError("Error loading song. An unhandled exception occured (" + e.Message + "): " + filePath); + return false; + } + return true; + } + + private bool _ParseNote(int player, ENoteType noteType, int start, int length, int tone, string text) + { + var note = new CSongNote(start, length, tone, text, noteType); + CVoice voice = Notes.GetVoice(player); + return voice.AddNote(note); + } + + private void _NewSentence(int player, int start) + { + CVoice voice = Notes.GetVoice(player); + var line = new CSongLine {StartBeat = start}; + voice.AddLine(line); + } + } +} \ No newline at end of file diff --git a/VocaluxeLib/VocaluxeLib.csproj b/VocaluxeLib/VocaluxeLib.csproj index 0fb808a3..0df713d5 100644 --- a/VocaluxeLib/VocaluxeLib.csproj +++ b/VocaluxeLib/VocaluxeLib.csproj @@ -178,6 +178,7 @@ + From ee6aaebfbc78d6a61515fd10bc1cf5da65123422 Mon Sep 17 00:00:00 2001 From: Flamefire Date: Wed, 9 Oct 2013 18:44:55 +0200 Subject: [PATCH 04/23] Use actual set encoding --- VocaluxeLib/Songs/SongNotesLoader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VocaluxeLib/Songs/SongNotesLoader.cs b/VocaluxeLib/Songs/SongNotesLoader.cs index 3e738b62..bb8540cd 100644 --- a/VocaluxeLib/Songs/SongNotesLoader.cs +++ b/VocaluxeLib/Songs/SongNotesLoader.cs @@ -350,7 +350,7 @@ namespace VocaluxeLib.Songs StreamReader sr = null; try { - sr = new StreamReader(filePath, Encoding, true); + sr = new StreamReader(filePath, Encoding); Notes.Reset(); From bbe8cfbe0859c6c872a9d940d9227dc887c20f69 Mon Sep 17 00:00:00 2001 From: Flamefire Date: Wed, 9 Oct 2013 19:45:49 +0200 Subject: [PATCH 05/23] Do not add line before first line Calculate start beat automatically More stable note loading with better warnings --- VocaluxeLib/Songs/CSongLine.cs | 6 +- VocaluxeLib/Songs/CVoice.cs | 20 +++- VocaluxeLib/Songs/SongNotesLoader.cs | 151 +++++++++++++++------------ 3 files changed, 100 insertions(+), 77 deletions(-) diff --git a/VocaluxeLib/Songs/CSongLine.cs b/VocaluxeLib/Songs/CSongLine.cs index 1e98ac20..dd6787f6 100644 --- a/VocaluxeLib/Songs/CSongLine.cs +++ b/VocaluxeLib/Songs/CSongLine.cs @@ -22,7 +22,7 @@ namespace VocaluxeLib.Songs { public class CSongLine : CLineBase { - private int _StartBeat = int.MinValue; + private int _StartBeat = int.MaxValue; private int _EndBeat = int.MinValue; #region Constructors @@ -46,7 +46,7 @@ namespace VocaluxeLib.Songs public int StartBeat { - get { return _StartBeat; } + get { return Math.Min(_StartBeat, FirstNoteBeat); } set { if (value <= FirstNoteBeat) @@ -56,7 +56,7 @@ namespace VocaluxeLib.Songs public int EndBeat { - get { return _EndBeat; } + get { return Math.Min(_EndBeat, LastNoteBeat); } set { if (value >= LastNoteBeat) diff --git a/VocaluxeLib/Songs/CVoice.cs b/VocaluxeLib/Songs/CVoice.cs index 238ab9ea..7252074d 100644 --- a/VocaluxeLib/Songs/CVoice.cs +++ b/VocaluxeLib/Songs/CVoice.cs @@ -127,9 +127,18 @@ namespace VocaluxeLib.Songs if (lineIndex < 0) { //Note is before ALL lines - var line = new CSongLine {StartBeat = note.StartBeat}; - line.AddNote(note); - _Lines.Insert(0, line); + if (_Lines.Count > 0) + { + //Add to first line + if (!_Lines[0].AddNote(note)) + return false; + } + else + { + var line = new CSongLine(); + line.AddNote(note); + _Lines.Add(line); + } } else { @@ -157,8 +166,9 @@ namespace VocaluxeLib.Songs { CSongNote lastNote; - if (_Lines.Count > 0) - _Lines[0].StartBeat = -10000; + if (_Lines.Count == 0) + return; + _Lines[0].StartBeat = -10000; for (int i = 1; i < _Lines.Count; i++) { diff --git a/VocaluxeLib/Songs/SongNotesLoader.cs b/VocaluxeLib/Songs/SongNotesLoader.cs index bb8540cd..71d85696 100644 --- a/VocaluxeLib/Songs/SongNotesLoader.cs +++ b/VocaluxeLib/Songs/SongNotesLoader.cs @@ -340,12 +340,15 @@ namespace VocaluxeLib.Songs return false; } - char tempC = char.MinValue; - int currentPos = 0; + int currentBeat = 0; //Used for relative songs bool isNewSentence = false; + bool endFound = false; int player = 1; - int fileLineNo = 0; + int lineNr = 0; + + char[] trimChars = {' ', ':'}; + char[] splitChars = {' '}; StreamReader sr = null; try @@ -355,40 +358,32 @@ namespace VocaluxeLib.Songs Notes.Reset(); //Search for Note Beginning - while (!sr.EndOfStream) + while (!sr.EndOfStream && !endFound) { - sr.ReadLine(); - fileLineNo++; + string line = sr.ReadLine(); + lineNr++; - tempC = (char)sr.Peek(); - if ((tempC == '#') || (tempC == '\r') || (tempC == '\n')) + if (String.IsNullOrEmpty(line)) continue; - if ((tempC == ':') || (tempC == 'F') || (tempC == '*') || (tempC == 'P')) - { - tempC = (char)sr.Read(); - break; - } - } - if (sr.EndOfStream) - { - CBase.Log.LogError("Error loading song. Line No.: " + fileLineNo + ". No lyrics/notes found: " + filePath); - return false; - } + char tag = line[0]; + //Remove tag and potential space + line = (line.Length >= 2 && line[1] == ' ') ? line.Substring(2) : line.Substring(1); - do - { - int param2; - int param1; - switch (tempC) + int beat, length; + switch (tag) { + case '#': + continue; + case 'E': + endFound = true; + break; case 'P': - char chr; - while ((chr = (char)sr.Read()) == ' ') {} + line = line.Trim(trimChars); - if (!int.TryParse(chr.ToString(), out player)) + if (!int.TryParse(line, out player)) { - CBase.Log.LogError("Error loading song. Line No.: " + fileLineNo + ". Wrong or missing number after \"P\": " + filePath); + _LogReadError("Error: Wrong or missing number after \"P\"", lineNr); return false; } sr.ReadLine(); @@ -396,33 +391,46 @@ namespace VocaluxeLib.Songs case ':': case '*': case 'F': - sr.Read(); - param1 = CHelper.TryReadInt(sr); - - sr.Read(); - param2 = CHelper.TryReadInt(sr); - - sr.Read(); - int param3 = CHelper.TryReadInt(sr); - - sr.Read(); - string paramS = sr.ReadLine(); - - if (param2 < 1) - CBase.Log.LogError("Warning! Ignored note in song because length is < 1. Line No.: " + fileLineNo + ": " + filePath); + string[] noteData = line.Split(splitChars, 4); + if (noteData.Length < 4) + { + if (noteData.Length == 3) + { + _LogReadError("Warning: Ignored note without text", lineNr); + continue; + } + _LogReadError("Error: Invalid note found", lineNr); + sr.Dispose(); + return false; + } + int tone; + if (!int.TryParse(noteData[0], out beat) || !int.TryParse(noteData[1], out length) || !int.TryParse(noteData[2], out tone)) + { + _LogReadError("Error: Invalid note found (non-numeric values)", lineNr); + sr.Dispose(); + return false; + } + string text = noteData[3]; + if (text.Trim() == "") + { + _LogReadError("Warning: Ignored note without text", lineNr); + continue; + } + if (length < 1) + _LogReadError("Warning: Ignored note with length < 1", lineNr); else { ENoteType noteType; - if (tempC.CompareTo('*') == 0) + if (tag.Equals('*')) noteType = ENoteType.Golden; - else if (tempC.CompareTo('F') == 0) + else if (tag.Equals('F')) noteType = ENoteType.Freestyle; else noteType = ENoteType.Normal; if (Relative == EOffOn.TR_CONFIG_ON) - param1 += currentPos; + beat += currentBeat; int curPlayer = 0; int tmpPlayer = player; @@ -431,10 +439,10 @@ namespace VocaluxeLib.Songs { if ((tmpPlayer & 1) != 0) { - if (!_ParseNote(curPlayer, noteType, param1, param2, param3, paramS)) + if (!_ParseNote(curPlayer, noteType, beat, length, tone, text)) { - CBase.Log.LogError("Warning! Ignored note for player " + (curPlayer + 1) + " in song because it overlaps with other note. Line No.: " + - fileLineNo + ": " + filePath); + CBase.Log.LogError("Warning! Ignored note for player " + (curPlayer + 1) + " because it overlaps with other note. Line No.: " + + lineNr + ": " + filePath); } } tmpPlayer >>= 1; @@ -446,22 +454,35 @@ namespace VocaluxeLib.Songs case '-': if (isNewSentence) { - CBase.Log.LogError("Error loading song. Line No.: " + fileLineNo + ". Double sentence break: " + filePath); + _LogReadError("Error: Double sentence break", lineNr); + sr.Dispose(); + return false; + } + string[] lineBreakData = line.Split(splitChars); + if (lineBreakData.Length < 1) + { + _LogReadError("Error: Invalid line break found (No beat)", lineNr); + sr.Dispose(); + return false; + } + if (!int.TryParse(lineBreakData[0], out beat)) + { + _LogReadError("Error: Invalid line break found (Non-numeric value)", lineNr); + sr.Dispose(); return false; } - sr.Read(); - param1 = CHelper.TryReadInt(sr); if (Relative == EOffOn.TR_CONFIG_ON) { - param1 += currentPos; - sr.Read(); - param2 = CHelper.TryReadInt(sr); - currentPos += param2; + beat += currentBeat; + if (lineBreakData.Length < 2 || !int.TryParse(lineBreakData[1], out length)) + _LogReadError("Warning: Missing line break length", lineNr); + else + currentBeat += length; } - if (param1 < 1) - CBase.Log.LogError("Warning! Ignored line break in song because position is < 1. Line No.: " + fileLineNo + ": " + filePath); + if (beat < 1) + _LogReadError("Warning: Ignored line break because position is < 1", lineNr); else { int curPlayer = 0; @@ -470,34 +491,26 @@ namespace VocaluxeLib.Songs while (tmpPlayer > 0) { if ((tmpPlayer & 1) != 0) - _NewSentence(curPlayer, param1); + _NewSentence(curPlayer, beat); tmpPlayer >>= 1; curPlayer++; } isNewSentence = true; } - sr.ReadLine(); break; default: - CBase.Log.LogError("Error loading song. Line No.: " + fileLineNo + ". Unexpected or missing character (" + tempC + "): " + filePath); + _LogReadError("Error loading song. Unexpected or missing character (" + tag + ")", lineNr); return false; } - int c; - do - { - c = sr.Read(); - } while (!sr.EndOfStream && (c == 19 || c == 16 || c == 13 || c == 10)); - tempC = (char)c; - fileLineNo++; - } while (!sr.EndOfStream && (tempC != 'E')); + } foreach (CVoice voice in Notes.Voices) voice.UpdateTimings(); } catch (Exception e) { - CBase.Log.LogError("Error loading song. Line No.: " + fileLineNo + ". An unhandled exception occured (" + e.Message + "): " + filePath); + _LogReadError("Error: An unhandled exception occured (" + e.Message + ")", lineNr); if (sr != null) sr.Dispose(); return false; From 31976ed498b672e0054417560b8d2c7c1e5bc36c Mon Sep 17 00:00:00 2001 From: Flamefire Date: Wed, 9 Oct 2013 23:32:55 +0200 Subject: [PATCH 06/23] Debug showcase for line break ordering --- VocaluxeLib/Songs/CVoice.cs | 72 ++++++++++++++++++++++++---- VocaluxeLib/Songs/SongNotesLoader.cs | 17 ++++--- 2 files changed, 71 insertions(+), 18 deletions(-) diff --git a/VocaluxeLib/Songs/CVoice.cs b/VocaluxeLib/Songs/CVoice.cs index 7252074d..ebf826f6 100644 --- a/VocaluxeLib/Songs/CVoice.cs +++ b/VocaluxeLib/Songs/CVoice.cs @@ -15,6 +15,7 @@ // along with Vocaluxe. If not, see . #endregion +using System; using System.Collections.Generic; using System.Linq; @@ -67,7 +68,11 @@ namespace VocaluxeLib.Songs } #region Methods - //Find last line with StartBeat<=Beat + /// + /// Find last line with StartBeat<=Beat + /// + /// + /// public int FindPreviousLine(int beat) { //If no line -> No previous line @@ -92,15 +97,49 @@ namespace VocaluxeLib.Songs return start; } - public void AddLine(CSongLine line) + public bool AddLine(CSongLine line, bool updateTimings = true) { if (_Lines.Count == 0) _Lines.Add(line); else { int insPos = FindPreviousLine(line.StartBeat); + //Check if previous line ends before this one + if (insPos >= 0 && _Lines[insPos].LastNoteBeat > line.StartBeat) + return false; + //Check if next line starts before this one ends + if (insPos + 1 < _Lines.Count && _Lines[insPos + 1].FirstNoteBeat < line.LastNoteBeat) + return false; _Lines.Insert(insPos + 1, line); } + if (updateTimings) + UpdateTimings(); + return true; + } + + public bool AddLine(int startBeat) + { + int insPos = FindPreviousLine(startBeat); + //Check for actual notes (startbeat may be lower than first note) + while (insPos >= 0 && _Lines[insPos].NoteCount > 0 && _Lines[insPos].FirstNoteBeat > startBeat) + insPos--; + CSongLine line = new CSongLine {StartBeat = startBeat}; + if (insPos >= 0) + { + CSongLine prevLine = _Lines[insPos]; + //We already have a line break here + if (prevLine.StartBeat == startBeat && prevLine.FirstNoteBeat == startBeat) + return false; + //Maybe we have to split the previous line + while (prevLine.NoteCount > 0 && prevLine.LastNote.StartBeat >= startBeat) + { + throw new Exception("This should not happen on song loading!"); + line.AddNote(prevLine.LastNote); + prevLine.DeleteNote(prevLine.NoteCount - 1); + } + } + _Lines.Insert(insPos + 1, line); + return true; } public bool DeleteLine(int index) @@ -119,7 +158,7 @@ namespace VocaluxeLib.Songs _Lines.Clear(); } - public bool AddNote(CSongNote note, bool updateTimings = false) + public bool AddNote(CSongNote note, bool updateTimings = true) { int lineIndex = FindPreviousLine(note.StartBeat); if (lineIndex + 1 < _Lines.Count && _Lines[lineIndex + 1].FirstNoteBeat < note.EndBeat) //First note in next line starts before this one ends @@ -150,17 +189,19 @@ namespace VocaluxeLib.Songs return true; } - public bool InsertNote(CSongNote note, int lineIndex, bool updateTimings = false) + /*Check this first for consistency (next line might be affected) + * public bool InsertNote(CSongNote note, int lineIndex, bool updateTimings = false) { if (_Lines.Count > lineIndex && _Lines[lineIndex].StartBeat <= note.StartBeat) { - _Lines[lineIndex].AddNote(note); + if (!_Lines[lineIndex].AddNote(note)) + return false; if (updateTimings) UpdateTimings(); return true; } return false; - } + }*/ public void UpdateTimings() { @@ -203,11 +244,24 @@ namespace VocaluxeLib.Songs _Lines[i].StartBeat = s; _Lines[i - 1].EndBeat = min; } + else if (firstNote != null) + { + _Lines[i - 1].EndBeat = Math.Min(_Lines[i - 1].StartBeat, firstNote.StartBeat - 2); + _Lines[i].StartBeat = Math.Max(_Lines[i - 1].EndBeat, firstNote.StartBeat - 2); + } + else if (lastNote != null) + { + _Lines[i - 1].EndBeat = lastNote.EndBeat; + _Lines[i].StartBeat = Math.Max(lastNote.EndBeat, _Lines[i].StartBeat); //Prefer current setting + } + else + { + //No note + _Lines[i - 1].EndBeat = _Lines[i - 1].StartBeat; //Assume at least startbeats were set right + } } - lastNote = _Lines[_Lines.Count - 1].LastNote; - if (lastNote != null) - _Lines[_Lines.Count - 1].EndBeat = lastNote.EndBeat; + _Lines[_Lines.Count - 1].EndBeat = Math.Max(_Lines[_Lines.Count - 1].LastNoteBeat, _Lines[_Lines.Count - 1].StartBeat); //Use note or (when not set) start beat } public void SetMedley(int startBeat, int endBeat) diff --git a/VocaluxeLib/Songs/SongNotesLoader.cs b/VocaluxeLib/Songs/SongNotesLoader.cs index 71d85696..89096042 100644 --- a/VocaluxeLib/Songs/SongNotesLoader.cs +++ b/VocaluxeLib/Songs/SongNotesLoader.cs @@ -440,10 +440,7 @@ namespace VocaluxeLib.Songs if ((tmpPlayer & 1) != 0) { if (!_ParseNote(curPlayer, noteType, beat, length, tone, text)) - { - CBase.Log.LogError("Warning! Ignored note for player " + (curPlayer + 1) + " because it overlaps with other note. Line No.: " + - lineNr + ": " + filePath); - } + _LogReadError("Warning: Ignored note for player " + (curPlayer + 1) + " because it overlaps with other note", lineNr); } tmpPlayer >>= 1; curPlayer++; @@ -491,7 +488,10 @@ namespace VocaluxeLib.Songs while (tmpPlayer > 0) { if ((tmpPlayer & 1) != 0) - _NewSentence(curPlayer, beat); + { + if (_NewSentence(curPlayer, beat)) + _LogReadError("Warning: Ignored line break for player " + (curPlayer + 1) + " (Overlapping or duplicate)", lineNr); + } tmpPlayer >>= 1; curPlayer++; } @@ -536,14 +536,13 @@ namespace VocaluxeLib.Songs { var note = new CSongNote(start, length, tone, text, noteType); CVoice voice = Notes.GetVoice(player); - return voice.AddNote(note); + return voice.AddNote(note, false); } - private void _NewSentence(int player, int start) + private bool _NewSentence(int player, int start) { CVoice voice = Notes.GetVoice(player); - var line = new CSongLine {StartBeat = start}; - voice.AddLine(line); + return voice.AddLine(start); } } } \ No newline at end of file From 3c2d7dcafd43866e15fc5b5ada24f8faab4aeade Mon Sep 17 00:00:00 2001 From: Flamefire Date: Fri, 11 Oct 2013 13:47:36 +0200 Subject: [PATCH 07/23] Introduce SongLoader class as some fields are only required while loading Change relative to be a bool --- Vocaluxe/Base/CSongs.cs | 2 +- VocaluxeLib/Songs/CSong.cs | 21 +- VocaluxeLib/Songs/CVoice.cs | 2 +- VocaluxeLib/Songs/SongNotesLoader.cs | 990 ++++++++++++++------------- 4 files changed, 516 insertions(+), 499 deletions(-) diff --git a/Vocaluxe/Base/CSongs.cs b/Vocaluxe/Base/CSongs.cs index aeed4b92..c45fc8a7 100644 --- a/Vocaluxe/Base/CSongs.cs +++ b/Vocaluxe/Base/CSongs.cs @@ -312,7 +312,7 @@ namespace Vocaluxe.Base if (song == null) continue; song.ID = _Songs.Count; - if (song.ReadNotes()) + if (song.LoadNotes()) _Songs.Add(song); } CLog.StopBenchmark(2, "Read TXTs"); diff --git a/VocaluxeLib/Songs/CSong.cs b/VocaluxeLib/Songs/CSong.cs index 8957ffd2..b021b182 100644 --- a/VocaluxeLib/Songs/CSong.cs +++ b/VocaluxeLib/Songs/CSong.cs @@ -82,7 +82,7 @@ namespace VocaluxeLib.Songs public string Folder = String.Empty; public string FolderName = String.Empty; public string FileName = String.Empty; - public EOffOn Relative = EOffOn.TR_CONFIG_OFF; + public bool Relative; public string MP3FileName = String.Empty; public string CoverFileName = String.Empty; @@ -165,12 +165,6 @@ namespace VocaluxeLib.Songs //No point creating a song without a text file --> Use factory method LoadSong private CSong() {} - public static CSong LoadSong(string filePath) - { - var song = new CSong(); - return song._InitPaths(filePath) && song._ReadHeader() ? song : null; - } - public CSong(CSong song) { _CoverTextureSmall = song._CoverTextureSmall; @@ -236,6 +230,19 @@ namespace VocaluxeLib.Songs Notes = new CNotes(song.Notes); } + public static CSong LoadSong(string filePath) + { + var song = new CSong(); + var loader = new CSongLoader(song); + return loader.InitPaths(filePath) && loader.ReadHeader() ? song : null; + } + + public bool LoadNotes() + { + var loader = new CSongLoader(this); + return loader.ReadNotes(); + } + public string GetMP3() { return Path.Combine(Folder, MP3FileName); diff --git a/VocaluxeLib/Songs/CVoice.cs b/VocaluxeLib/Songs/CVoice.cs index ebf826f6..3f1d9766 100644 --- a/VocaluxeLib/Songs/CVoice.cs +++ b/VocaluxeLib/Songs/CVoice.cs @@ -133,7 +133,7 @@ namespace VocaluxeLib.Songs //Maybe we have to split the previous line while (prevLine.NoteCount > 0 && prevLine.LastNote.StartBeat >= startBeat) { - throw new Exception("This should not happen on song loading!"); + //throw new Exception("This should not happen on song loading!"); line.AddNote(prevLine.LastNote); prevLine.DeleteNote(prevLine.NoteCount - 1); } diff --git a/VocaluxeLib/Songs/SongNotesLoader.cs b/VocaluxeLib/Songs/SongNotesLoader.cs index 89096042..8e160760 100644 --- a/VocaluxeLib/Songs/SongNotesLoader.cs +++ b/VocaluxeLib/Songs/SongNotesLoader.cs @@ -11,538 +11,548 @@ namespace VocaluxeLib.Songs /// public partial class CSong { - private bool _InitPaths(string filePath) + private class CSongLoader { - if (!File.Exists(filePath)) - return false; + private readonly CSong _Song; - Folder = Path.GetDirectoryName(filePath); - if (Folder == null) - return false; - - foreach (string folder in CBase.Config.GetSongFolders().Where(folder => Folder.StartsWith(folder))) + public CSongLoader(CSong song) { - if (Folder.Length == folder.Length) - FolderName = "Songs"; - else - { - FolderName = Folder.Substring(folder.Length + 1); - - int pos = FolderName.IndexOf("\\", StringComparison.Ordinal); - if (pos >= 0) - FolderName = FolderName.Substring(0, pos); - } - break; + _Song = song; } - FileName = Path.GetFileName(filePath); - return true; - } - - private void _LogReadError(string error, int lineNr) - { - CBase.Log.LogError(error + " in line #" + lineNr + "(" + Path.Combine(Folder, FileName) + ")"); - } - - private bool _ReadHeader(Encoding encoding = null) - { - string filePath = Path.Combine(Folder, FileName); - - if (!File.Exists(filePath)) - return false; - - Language.Clear(); - Genre.Clear(); - _Comment.Clear(); - - var headerFlags = new EHeaderFlags(); - StreamReader sr = null; - int lineNr = 0; - try + public bool InitPaths(string filePath) { - sr = new StreamReader(filePath, Encoding.Default, true); - while (!sr.EndOfStream) + if (!File.Exists(filePath)) + return false; + + _Song.Folder = Path.GetDirectoryName(filePath); + if (_Song.Folder == null) + return false; + + foreach (string folder in CBase.Config.GetSongFolders().Where(folder => _Song.Folder.StartsWith(folder))) { - string line = sr.ReadLine(); - lineNr++; - if (line == "") - continue; - if (!line[0].Equals('#')) - break; - - int pos = line.IndexOf(":", StringComparison.Ordinal); - - if (pos <= 1) - continue; - string identifier = line.Substring(1, pos - 1).Trim().ToUpper(); - string value = line.Substring(pos + 1).Trim(); - - if (value == "") + if (_Song.Folder.Length == folder.Length) + _Song.FolderName = "Songs"; + else { - _LogReadError("Warning: Empty value skipped", lineNr); - continue; + _Song.FolderName = _Song.Folder.Substring(folder.Length + 1); + + int pos = _Song.FolderName.IndexOf("\\", StringComparison.Ordinal); + if (pos >= 0) + _Song.FolderName = _Song.FolderName.Substring(0, pos); } - - switch (identifier) - { - case "ENCODING": - if (encoding != null) - { - _LogReadError("Warning: Duplicate encoding ignored", lineNr); - continue; - } - Encoding newEncoding = CEncoding.GetEncoding(value); - if (!newEncoding.Equals(sr.CurrentEncoding)) - { - sr.Dispose(); - sr = null; - return _ReadHeader(Encoding); - } - break; - case "TITLE": - Title = value; - headerFlags |= EHeaderFlags.Title; - break; - case "ARTIST": - Artist = value; - headerFlags |= EHeaderFlags.Artist; - break; - case "TITLE-ON-SORTING": - TitleSorting = value; - break; - case "ARTIST-ON-SORTING": - ArtistSorting = value; - break; - case "DUETSINGERP1": - case "P1": - Notes.VoiceNames[0] = value; - break; - case "DUETSINGERP2": - case "P2": - Notes.VoiceNames[1] = value; - break; - case "MP3": - if (File.Exists(Path.Combine(Folder, value))) - { - MP3FileName = value; - headerFlags |= EHeaderFlags.MP3; - } - else - { - _LogReadError("Error: Can't find audio file: " + Path.Combine(Folder, value), lineNr); - return false; - } - break; - case "BPM": - if (CHelper.TryParse(value, out BPM)) - { - BPM *= 4; - headerFlags |= EHeaderFlags.BPM; - } - else - _LogReadError("Warning: Invalid BPM value", lineNr); - break; - case "EDITION": - if (value.Length > 1) - Edition.Add(value); - else - _LogReadError("Warning: Invalid edition", lineNr); - break; - case "GENRE": - if (value.Length > 1) - Genre.Add(value); - else - _LogReadError("Warning: Invalid genre", lineNr); - break; - case "YEAR": - int num; - if (value.Length == 4 && int.TryParse(value, out num) && num > 0) - Year = value; - else - _LogReadError("Warning: Invalid year", lineNr); - break; - case "LANGUAGE": - if (value.Length > 1) - Language.Add(value); - else - _LogReadError("Warning: Invalid language", lineNr); - break; - case "COMMENT": - if (value.Length > 1) - _Comment.Add(value); - else - _LogReadError("Warning: Invalid comment", lineNr); - break; - case "GAP": - if (CHelper.TryParse(value, out Gap)) - Gap /= 1000f; - else - _LogReadError("Warning: Invalid gap", lineNr); - break; - case "COVER": - if (File.Exists(Path.Combine(Folder, value))) - CoverFileName = value; - else - _LogReadError("Warning: Can't find cover file: " + Path.Combine(Folder, value), lineNr); - break; - case "BACKGROUND": - if (File.Exists(Path.Combine(Folder, value))) - BackgroundFileName = value; - else - _LogReadError("Warning: Can't find background file: " + Path.Combine(Folder, value), lineNr); - break; - case "VIDEO": - if (File.Exists(Path.Combine(Folder, value))) - VideoFileName = value; - else - CBase.Log.LogError("Warning: Can't find video file: " + Path.Combine(Folder, value)); - break; - case "VIDEOGAP": - if (!CHelper.TryParse(value, out VideoGap)) - _LogReadError("Warning: Invalid videogap", lineNr); - break; - case "VIDEOASPECT": - if (!CHelper.TryParse(value, out VideoAspect, true)) - _LogReadError("Warning: Invalid videoaspect", lineNr); - break; - case "START": - if (!CHelper.TryParse(value, out Start)) - _LogReadError("Warning: Invalid start", lineNr); - break; - case "END": - if (CHelper.TryParse(value, out Finish)) - Finish /= 1000f; - else - _LogReadError("Warning: Invalid end", lineNr); - break; - case "PREVIEWSTART": - if (CHelper.TryParse(value, out PreviewStart) && PreviewStart >= 0f) - headerFlags |= EHeaderFlags.PreviewStart; - else - _LogReadError("Warning: Invalid previewstart", lineNr); - break; - case "MEDLEYSTARTBEAT": - if (int.TryParse(value, out Medley.StartBeat)) - headerFlags |= EHeaderFlags.MedleyStartBeat; - else - _LogReadError("Warning: Invalid medleystartbeat", lineNr); - break; - case "MEDLEYENDBEAT": - if (int.TryParse(value, out Medley.EndBeat)) - headerFlags |= EHeaderFlags.MedleyEndBeat; - else - _LogReadError("Warning: Invalid medleyendbeat", lineNr); - break; - case "CALCMEDLEY": - if (value.ToUpper() == "OFF") - CalculateMedley = false; - break; - case "RELATIVE": - if (value.ToUpper() == "YES") - Relative = EOffOn.TR_CONFIG_ON; - break; - } - } //end of while - - if (sr.EndOfStream) - { - //No other data then header - CBase.Log.LogError("Lyrics/Notes missing: " + filePath); - return false; + break; } - if ((headerFlags & EHeaderFlags.Title) == 0) - { - CBase.Log.LogError("Title tag missing: " + filePath); - return false; - } - - if ((headerFlags & EHeaderFlags.Artist) == 0) - { - CBase.Log.LogError("Artist tag missing: " + filePath); - return false; - } - - if ((headerFlags & EHeaderFlags.MP3) == 0) - { - CBase.Log.LogError("MP3 tag missing: " + filePath); - return false; - } - - if ((headerFlags & EHeaderFlags.BPM) == 0) - { - CBase.Log.LogError("BPM tag missing: " + filePath); - return false; - } - - #region check medley tags - if ((headerFlags & EHeaderFlags.MedleyStartBeat) != 0 && (headerFlags & EHeaderFlags.MedleyEndBeat) != 0) - { - if (Medley.StartBeat > Medley.EndBeat) - { - CBase.Log.LogError("MedleyStartBeat is bigger than MedleyEndBeat in file: " + filePath); - headerFlags = headerFlags - EHeaderFlags.MedleyStartBeat - EHeaderFlags.MedleyEndBeat; - } - } - - if ((headerFlags & EHeaderFlags.PreviewStart) == 0 || PreviewStart < 0) - { - //PreviewStart is not set or <=0 - PreviewStart = (headerFlags & EHeaderFlags.MedleyStartBeat) != 0 ? CBase.Game.GetTimeFromBeats(Medley.StartBeat, BPM) : 0f; - } - - if ((headerFlags & EHeaderFlags.MedleyStartBeat) != 0 && (headerFlags & EHeaderFlags.MedleyEndBeat) != 0) - { - Medley.Source = EMedleySource.Tag; - Medley.FadeInTime = CBase.Settings.GetDefaultMedleyFadeInTime(); - Medley.FadeOutTime = CBase.Settings.GetDefaultMedleyFadeOutTime(); - } - #endregion check medley tags - - Encoding = sr.CurrentEncoding; - } - catch (Exception e) - { - if (sr != null) - sr.Dispose(); - _LogReadError("Error reading txt header" + e.Message, lineNr); - return false; - } - sr.Dispose(); - _CheckFiles(); - - CBase.DataBase.GetDataBaseSongInfos(Artist, Title, out NumPlayed, out DateAdded, out DataBaseSongID); - - //Before saving this tags to .txt: Check, if ArtistSorting and Artist are equal, then don't save this tag. - if (ArtistSorting == "") - ArtistSorting = Artist; - - if (TitleSorting == "") - TitleSorting = Title; - - return true; - } - - public bool ReadNotes() - { - return _ReadNotes(Path.Combine(Folder, FileName)); - } - - private bool _ReadNotes(string filePath, bool forceReload = false) - { - //Skip loading if already done and no reload is forced - if (NotesLoaded && !forceReload) + _Song.FileName = Path.GetFileName(filePath); return true; - - if (!File.Exists(filePath)) - { - CBase.Log.LogError("Error loading song. The file does not exist: " + filePath); - return false; } - int currentBeat = 0; //Used for relative songs - bool isNewSentence = false; - bool endFound = false; - - int player = 1; - int lineNr = 0; - - char[] trimChars = {' ', ':'}; - char[] splitChars = {' '}; - - StreamReader sr = null; - try + private void _LogReadError(string error, int lineNr) { - sr = new StreamReader(filePath, Encoding); + CBase.Log.LogError(error + " in line #" + lineNr + "(" + Path.Combine(_Song.Folder, _Song.FileName) + ")"); + } - Notes.Reset(); + public bool ReadHeader(Encoding encoding = null) + { + string filePath = Path.Combine(_Song.Folder, _Song.FileName); - //Search for Note Beginning - while (!sr.EndOfStream && !endFound) + if (!File.Exists(filePath)) + return false; + + _Song.Language.Clear(); + _Song.Genre.Clear(); + _Song._Comment.Clear(); + + var headerFlags = new EHeaderFlags(); + StreamReader sr = null; + int lineNr = 0; + try { - string line = sr.ReadLine(); - lineNr++; - - if (String.IsNullOrEmpty(line)) - continue; - - char tag = line[0]; - //Remove tag and potential space - line = (line.Length >= 2 && line[1] == ' ') ? line.Substring(2) : line.Substring(1); - - int beat, length; - switch (tag) + sr = new StreamReader(filePath, Encoding.Default, true); + while (!sr.EndOfStream) { - case '#': + string line = sr.ReadLine(); + lineNr++; + if (line == "") continue; - case 'E': - endFound = true; + if (!line[0].Equals('#')) break; - case 'P': - line = line.Trim(trimChars); - if (!int.TryParse(line, out player)) - { - _LogReadError("Error: Wrong or missing number after \"P\"", lineNr); - return false; - } - sr.ReadLine(); - break; - case ':': - case '*': - case 'F': - string[] noteData = line.Split(splitChars, 4); - if (noteData.Length < 4) - { - if (noteData.Length == 3) + int pos = line.IndexOf(":", StringComparison.Ordinal); + + if (pos <= 1) + continue; + string identifier = line.Substring(1, pos - 1).Trim().ToUpper(); + string value = line.Substring(pos + 1).Trim(); + + if (value == "") + { + _LogReadError("Warning: Empty value skipped", lineNr); + continue; + } + + switch (identifier) + { + case "ENCODING": + if (encoding != null) + { + _LogReadError("Warning: Duplicate encoding ignored", lineNr); + continue; + } + Encoding newEncoding = CEncoding.GetEncoding(value); + if (!newEncoding.Equals(sr.CurrentEncoding)) + { + sr.Dispose(); + sr = null; + return ReadHeader(_Song.Encoding); + } + break; + case "TITLE": + _Song.Title = value; + headerFlags |= EHeaderFlags.Title; + break; + case "ARTIST": + _Song.Artist = value; + headerFlags |= EHeaderFlags.Artist; + break; + case "TITLE-ON-SORTING": + _Song.TitleSorting = value; + break; + case "ARTIST-ON-SORTING": + _Song.ArtistSorting = value; + break; + case "DUETSINGERP1": + case "P1": + _Song.Notes.VoiceNames[0] = value; + break; + case "DUETSINGERP2": + case "P2": + _Song.Notes.VoiceNames[1] = value; + break; + case "MP3": + if (File.Exists(Path.Combine(_Song.Folder, value))) + { + _Song.MP3FileName = value; + headerFlags |= EHeaderFlags.MP3; + } + else + { + _LogReadError("Error: Can't find audio file: " + Path.Combine(_Song.Folder, value), lineNr); + return false; + } + break; + case "BPM": + if (CHelper.TryParse(value, out _Song.BPM)) + { + _Song.BPM *= 4; + headerFlags |= EHeaderFlags.BPM; + } + else + _LogReadError("Warning: Invalid BPM value", lineNr); + break; + case "EDITION": + if (value.Length > 1) + _Song.Edition.Add(value); + else + _LogReadError("Warning: Invalid edition", lineNr); + break; + case "GENRE": + if (value.Length > 1) + _Song.Genre.Add(value); + else + _LogReadError("Warning: Invalid genre", lineNr); + break; + case "YEAR": + int num; + if (value.Length == 4 && int.TryParse(value, out num) && num > 0) + _Song.Year = value; + else + _LogReadError("Warning: Invalid year", lineNr); + break; + case "LANGUAGE": + if (value.Length > 1) + _Song.Language.Add(value); + else + _LogReadError("Warning: Invalid language", lineNr); + break; + case "COMMENT": + if (value.Length > 1) + _Song._Comment.Add(value); + else + _LogReadError("Warning: Invalid comment", lineNr); + break; + case "GAP": + if (CHelper.TryParse(value, out _Song.Gap)) + _Song.Gap /= 1000f; + else + _LogReadError("Warning: Invalid gap", lineNr); + break; + case "COVER": + if (File.Exists(Path.Combine(_Song.Folder, value))) + _Song.CoverFileName = value; + else + _LogReadError("Warning: Can't find cover file: " + Path.Combine(_Song.Folder, value), lineNr); + break; + case "BACKGROUND": + if (File.Exists(Path.Combine(_Song.Folder, value))) + _Song.BackgroundFileName = value; + else + _LogReadError("Warning: Can't find background file: " + Path.Combine(_Song.Folder, value), lineNr); + break; + case "VIDEO": + if (File.Exists(Path.Combine(_Song.Folder, value))) + _Song.VideoFileName = value; + else + CBase.Log.LogError("Warning: Can't find video file: " + Path.Combine(_Song.Folder, value)); + break; + case "VIDEOGAP": + if (!CHelper.TryParse(value, out _Song.VideoGap)) + _LogReadError("Warning: Invalid videogap", lineNr); + break; + case "VIDEOASPECT": + if (!CHelper.TryParse(value, out _Song.VideoAspect, true)) + _LogReadError("Warning: Invalid videoaspect", lineNr); + break; + case "START": + if (!CHelper.TryParse(value, out _Song.Start)) + _LogReadError("Warning: Invalid start", lineNr); + break; + case "END": + if (CHelper.TryParse(value, out _Song.Finish)) + _Song.Finish /= 1000f; + else + _LogReadError("Warning: Invalid end", lineNr); + break; + case "PREVIEWSTART": + if (CHelper.TryParse(value, out _Song.PreviewStart) && _Song.PreviewStart >= 0f) + headerFlags |= EHeaderFlags.PreviewStart; + else + _LogReadError("Warning: Invalid previewstart", lineNr); + break; + case "MEDLEYSTARTBEAT": + if (int.TryParse(value, out _Song.Medley.StartBeat)) + headerFlags |= EHeaderFlags.MedleyStartBeat; + else + _LogReadError("Warning: Invalid medleystartbeat", lineNr); + break; + case "MEDLEYENDBEAT": + if (int.TryParse(value, out _Song.Medley.EndBeat)) + headerFlags |= EHeaderFlags.MedleyEndBeat; + else + _LogReadError("Warning: Invalid medleyendbeat", lineNr); + break; + case "CALCMEDLEY": + if (value.ToUpper() == "OFF") + _Song.CalculateMedley = false; + break; + case "RELATIVE": + if (value.ToUpper() == "YES") + _Song.Relative = true; + break; + } + } //end of while + + if (sr.EndOfStream) + { + //No other data then header + CBase.Log.LogError("Lyrics/Notes missing: " + filePath); + return false; + } + + if ((headerFlags & EHeaderFlags.Title) == 0) + { + CBase.Log.LogError("Title tag missing: " + filePath); + return false; + } + + if ((headerFlags & EHeaderFlags.Artist) == 0) + { + CBase.Log.LogError("Artist tag missing: " + filePath); + return false; + } + + if ((headerFlags & EHeaderFlags.MP3) == 0) + { + CBase.Log.LogError("MP3 tag missing: " + filePath); + return false; + } + + if ((headerFlags & EHeaderFlags.BPM) == 0) + { + CBase.Log.LogError("BPM tag missing: " + filePath); + return false; + } + + #region check medley tags + if ((headerFlags & EHeaderFlags.MedleyStartBeat) != 0 && (headerFlags & EHeaderFlags.MedleyEndBeat) != 0) + { + if (_Song.Medley.StartBeat > _Song.Medley.EndBeat) + { + CBase.Log.LogError("MedleyStartBeat is bigger than MedleyEndBeat in file: " + filePath); + headerFlags = headerFlags - EHeaderFlags.MedleyStartBeat - EHeaderFlags.MedleyEndBeat; + } + } + + if ((headerFlags & EHeaderFlags.PreviewStart) == 0 || _Song.PreviewStart < 0) + { + //PreviewStart is not set or <=0 + _Song.PreviewStart = (headerFlags & EHeaderFlags.MedleyStartBeat) != 0 ? CBase.Game.GetTimeFromBeats(_Song.Medley.StartBeat, _Song.BPM) : 0f; + } + + if ((headerFlags & EHeaderFlags.MedleyStartBeat) != 0 && (headerFlags & EHeaderFlags.MedleyEndBeat) != 0) + { + _Song.Medley.Source = EMedleySource.Tag; + _Song.Medley.FadeInTime = CBase.Settings.GetDefaultMedleyFadeInTime(); + _Song.Medley.FadeOutTime = CBase.Settings.GetDefaultMedleyFadeOutTime(); + } + #endregion check medley tags + + _Song.Encoding = sr.CurrentEncoding; + } + catch (Exception e) + { + if (sr != null) + sr.Dispose(); + _LogReadError("Error reading txt header" + e.Message, lineNr); + return false; + } + sr.Dispose(); + _Song._CheckFiles(); + + CBase.DataBase.GetDataBaseSongInfos(_Song.Artist, _Song.Title, out _Song.NumPlayed, out _Song.DateAdded, out _Song.DataBaseSongID); + + //Before saving this tags to .txt: Check, if ArtistSorting and Artist are equal, then don't save this tag. + if (_Song.ArtistSorting == "") + _Song.ArtistSorting = _Song.Artist; + + if (_Song.TitleSorting == "") + _Song.TitleSorting = _Song.Title; + + return true; + } + + public bool ReadNotes() + { + return _ReadNotes(); + } + + private bool _ReadNotes(bool forceReload = false) + { + //Skip loading if already done and no reload is forced + if (_Song.NotesLoaded && !forceReload) + return true; + + string filePath = Path.Combine(_Song.Folder, _Song.FileName); + + if (!File.Exists(filePath)) + { + CBase.Log.LogError("Error loading song. The file does not exist: " + filePath); + return false; + } + + int currentBeat = 0; //Used for relative songs + int lastNoteEnd = 0; + bool endFound = false; + + int player = 1; + int lineNr = 0; + + char[] trimChars = {' ', ':'}; + char[] splitChars = {' '}; + + StreamReader sr = null; + try + { + sr = new StreamReader(filePath, _Song.Encoding); + + _Song.Notes.Reset(); + + //Search for Note Beginning + while (!sr.EndOfStream && !endFound) + { + string line = sr.ReadLine(); + lineNr++; + + if (String.IsNullOrEmpty(line)) + continue; + + char tag = line[0]; + //Remove tag and potential space + line = (line.Length >= 2 && line[1] == ' ') ? line.Substring(2) : line.Substring(1); + + int beat, length; + switch (tag) + { + case '#': + continue; + case 'E': + endFound = true; + break; + case 'P': + line = line.Trim(trimChars); + + if (!int.TryParse(line, out player)) + { + _LogReadError("Error: Wrong or missing number after \"P\"", lineNr); + return false; + } + sr.ReadLine(); + break; + case ':': + case '*': + case 'F': + string[] noteData = line.Split(splitChars, 4); + if (noteData.Length < 4) + { + if (noteData.Length == 3) + { + _LogReadError("Warning: Ignored note without text", lineNr); + continue; + } + _LogReadError("Error: Invalid note found", lineNr); + sr.Dispose(); + return false; + } + int tone; + if (!int.TryParse(noteData[0], out beat) || !int.TryParse(noteData[1], out length) || !int.TryParse(noteData[2], out tone)) + { + _LogReadError("Error: Invalid note found (non-numeric values)", lineNr); + sr.Dispose(); + return false; + } + string text = noteData[3]; + if (text.Trim() == "") { _LogReadError("Warning: Ignored note without text", lineNr); continue; } - _LogReadError("Error: Invalid note found", lineNr); - sr.Dispose(); - return false; - } - int tone; - if (!int.TryParse(noteData[0], out beat) || !int.TryParse(noteData[1], out length) || !int.TryParse(noteData[2], out tone)) - { - _LogReadError("Error: Invalid note found (non-numeric values)", lineNr); - sr.Dispose(); - return false; - } - string text = noteData[3]; - if (text.Trim() == "") - { - _LogReadError("Warning: Ignored note without text", lineNr); - continue; - } - if (length < 1) - _LogReadError("Warning: Ignored note with length < 1", lineNr); - else - { - ENoteType noteType; - - if (tag.Equals('*')) - noteType = ENoteType.Golden; - else if (tag.Equals('F')) - noteType = ENoteType.Freestyle; + if (length < 1) + _LogReadError("Warning: Ignored note with length < 1", lineNr); else - noteType = ENoteType.Normal; + { + ENoteType noteType; - if (Relative == EOffOn.TR_CONFIG_ON) + if (tag.Equals('*')) + noteType = ENoteType.Golden; + else if (tag.Equals('F')) + noteType = ENoteType.Freestyle; + else + noteType = ENoteType.Normal; + + if (_Song.Relative) + beat += currentBeat; + + int curPlayer = 0; + int tmpPlayer = player; + //Evaluate as bitset + while (tmpPlayer > 0) + { + if ((tmpPlayer & 1) != 0) + { + if (!_ParseNote(curPlayer, noteType, beat, length, tone, text)) + _LogReadError("Warning: Ignored note for player " + (curPlayer + 1) + " because it overlaps with other note", lineNr); + } + tmpPlayer >>= 1; + curPlayer++; + } + } + lastNoteEnd = beat + length; + break; + case '-': + string[] lineBreakData = line.Split(splitChars); + if (lineBreakData.Length < 1) + { + _LogReadError("Error: Invalid line break found (No beat)", lineNr); + sr.Dispose(); + return false; + } + if (!int.TryParse(lineBreakData[0], out beat)) + { + _LogReadError("Error: Invalid line break found (Non-numeric value)", lineNr); + sr.Dispose(); + return false; + } + + if (_Song.Relative) + { beat += currentBeat; - - int curPlayer = 0; - int tmpPlayer = player; - //Evaluate as bitset - while (tmpPlayer > 0) - { - if ((tmpPlayer & 1) != 0) - { - if (!_ParseNote(curPlayer, noteType, beat, length, tone, text)) - _LogReadError("Warning: Ignored note for player " + (curPlayer + 1) + " because it overlaps with other note", lineNr); - } - tmpPlayer >>= 1; - curPlayer++; + if (lineBreakData.Length < 2 || !int.TryParse(lineBreakData[1], out length)) + _LogReadError("Warning: Missing line break length", lineNr); + else + currentBeat += length; } - } - isNewSentence = false; - break; - case '-': - if (isNewSentence) - { - _LogReadError("Error: Double sentence break", lineNr); - sr.Dispose(); - return false; - } - string[] lineBreakData = line.Split(splitChars); - if (lineBreakData.Length < 1) - { - _LogReadError("Error: Invalid line break found (No beat)", lineNr); - sr.Dispose(); - return false; - } - if (!int.TryParse(lineBreakData[0], out beat)) - { - _LogReadError("Error: Invalid line break found (Non-numeric value)", lineNr); - sr.Dispose(); - return false; - } - if (Relative == EOffOn.TR_CONFIG_ON) - { - beat += currentBeat; - if (lineBreakData.Length < 2 || !int.TryParse(lineBreakData[1], out length)) - _LogReadError("Warning: Missing line break length", lineNr); + if (beat < lastNoteEnd) + { + _LogReadError("Warning: Line break is before previous note end. Adjusted it (might not work for relative songs)", lineNr); + beat = lastNoteEnd; + } + + if (beat < 1) + _LogReadError("Warning: Ignored line break because position is < 1", lineNr); else - currentBeat += length; - } - - if (beat < 1) - _LogReadError("Warning: Ignored line break because position is < 1", lineNr); - else - { - int curPlayer = 0; - int tmpPlayer = player; - //Evaluate as bitset - while (tmpPlayer > 0) { - if ((tmpPlayer & 1) != 0) + int curPlayer = 0; + int tmpPlayer = player; + //Evaluate as bitset + while (tmpPlayer > 0) { - if (_NewSentence(curPlayer, beat)) - _LogReadError("Warning: Ignored line break for player " + (curPlayer + 1) + " (Overlapping or duplicate)", lineNr); + if ((tmpPlayer & 1) != 0) + { + if (_NewSentence(curPlayer, beat)) + _LogReadError("Warning: Ignored line break for player " + (curPlayer + 1) + " (Overlapping or duplicate)", lineNr); + } + tmpPlayer >>= 1; + curPlayer++; } - tmpPlayer >>= 1; - curPlayer++; } - - isNewSentence = true; - } - break; - default: - _LogReadError("Error loading song. Unexpected or missing character (" + tag + ")", lineNr); - return false; + break; + default: + _LogReadError("Error loading song. Unexpected or missing character (" + tag + ")", lineNr); + return false; + } } + + foreach (CVoice voice in _Song.Notes.Voices) + voice.UpdateTimings(); } + catch (Exception e) + { + _LogReadError("Error: An unhandled exception occured (" + e.Message + ")", lineNr); + if (sr != null) + sr.Dispose(); + return false; + } + sr.Dispose(); + try + { + _Song._FindRefrain(); + _Song._FindShortEnd(); + _Song.NotesLoaded = true; + if (_Song.IsDuet) + _Song._CheckDuet(); + } + catch (Exception e) + { + CBase.Log.LogError("Error loading song. An unhandled exception occured (" + e.Message + "): " + filePath); + return false; + } + return true; + } - foreach (CVoice voice in Notes.Voices) - voice.UpdateTimings(); - } - catch (Exception e) + private bool _ParseNote(int player, ENoteType noteType, int start, int length, int tone, string text) { - _LogReadError("Error: An unhandled exception occured (" + e.Message + ")", lineNr); - if (sr != null) - sr.Dispose(); - return false; + var note = new CSongNote(start, length, tone, text, noteType); + CVoice voice = _Song.Notes.GetVoice(player); + return voice.AddNote(note, false); } - sr.Dispose(); - try - { - _FindRefrain(); - _FindShortEnd(); - NotesLoaded = true; - if (IsDuet) - _CheckDuet(); - } - catch (Exception e) - { - CBase.Log.LogError("Error loading song. An unhandled exception occured (" + e.Message + "): " + filePath); - return false; - } - return true; - } - private bool _ParseNote(int player, ENoteType noteType, int start, int length, int tone, string text) - { - var note = new CSongNote(start, length, tone, text, noteType); - CVoice voice = Notes.GetVoice(player); - return voice.AddNote(note, false); - } - - private bool _NewSentence(int player, int start) - { - CVoice voice = Notes.GetVoice(player); - return voice.AddLine(start); + private bool _NewSentence(int player, int start) + { + CVoice voice = _Song.Notes.GetVoice(player); + return voice.AddLine(start); + } } } } \ No newline at end of file From 05e170e91a24333623c634f035a1f4c93b0ca120 Mon Sep 17 00:00:00 2001 From: Flamefire Date: Fri, 11 Oct 2013 13:48:23 +0200 Subject: [PATCH 08/23] Rename SongLoader file --- VocaluxeLib/Songs/{SongNotesLoader.cs => CSongLoader.cs} | 0 VocaluxeLib/VocaluxeLib.csproj | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename VocaluxeLib/Songs/{SongNotesLoader.cs => CSongLoader.cs} (100%) diff --git a/VocaluxeLib/Songs/SongNotesLoader.cs b/VocaluxeLib/Songs/CSongLoader.cs similarity index 100% rename from VocaluxeLib/Songs/SongNotesLoader.cs rename to VocaluxeLib/Songs/CSongLoader.cs diff --git a/VocaluxeLib/VocaluxeLib.csproj b/VocaluxeLib/VocaluxeLib.csproj index 0df713d5..3a69d742 100644 --- a/VocaluxeLib/VocaluxeLib.csproj +++ b/VocaluxeLib/VocaluxeLib.csproj @@ -178,7 +178,7 @@ - + From 2ec094e7349a28cec910f0660d7d01919862109f Mon Sep 17 00:00:00 2001 From: Flamefire Date: Fri, 11 Oct 2013 14:22:33 +0200 Subject: [PATCH 09/23] Easier logging in SongLoader --- VocaluxeLib/Extensions.cs | 20 ++++ VocaluxeLib/Menu/CLyric.cs | 8 -- VocaluxeLib/Songs/CSong.cs | 8 +- VocaluxeLib/Songs/CSongLoader.cs | 161 +++++++++++++++++-------------- VocaluxeLib/Songs/CSungNote.cs | 2 +- 5 files changed, 112 insertions(+), 87 deletions(-) diff --git a/VocaluxeLib/Extensions.cs b/VocaluxeLib/Extensions.cs index b970b6bc..f19e4b2b 100644 --- a/VocaluxeLib/Extensions.cs +++ b/VocaluxeLib/Extensions.cs @@ -48,5 +48,25 @@ namespace VocaluxeLib if (size > list.Count) list.Add(new T()); } + + /// + /// Gets all set bits starting from lowest (Bit 0) + /// + /// + /// + public static List GetSetBits(this int value) + { + var result = new List(); + int curBit = 0; + //Evaluate as bitset + while (value > 0) + { + if ((value & 1) != 0) + result.Add(curBit); + value >>= 1; + curBit++; + } + return result; + } } } \ No newline at end of file diff --git a/VocaluxeLib/Menu/CLyric.cs b/VocaluxeLib/Menu/CLyric.cs index 8fbdc15c..7c1376a5 100644 --- a/VocaluxeLib/Menu/CLyric.cs +++ b/VocaluxeLib/Menu/CLyric.cs @@ -23,14 +23,6 @@ using VocaluxeLib.Songs; namespace VocaluxeLib.Menu { - struct SNote - { - public string Text; - public int StartBeat; - public int EndBeat; - public ENoteType Type; - } - struct SThemeLyrics { public string Name; diff --git a/VocaluxeLib/Songs/CSong.cs b/VocaluxeLib/Songs/CSong.cs index b021b182..9833e963 100644 --- a/VocaluxeLib/Songs/CSong.cs +++ b/VocaluxeLib/Songs/CSong.cs @@ -137,10 +137,10 @@ namespace VocaluxeLib.Songs } public readonly List Edition = new List(); - public readonly List Genre = new List(); + public readonly List Genres = new List(); public string Year = ""; - public readonly List Language = new List(); + public readonly List Languages = new List(); public int DataBaseSongID = -1; public string DateAdded = ""; @@ -217,11 +217,11 @@ namespace VocaluxeLib.Songs Edition = new List(song.Edition); - Genre = new List(song.Genre); + Genres = new List(song.Genres); Year = song.Year; - Language = new List(song.Language); + Languages = new List(song.Languages); DataBaseSongID = song.DataBaseSongID; DateAdded = song.DateAdded; diff --git a/VocaluxeLib/Songs/CSongLoader.cs b/VocaluxeLib/Songs/CSongLoader.cs index 8e160760..cef6e4dd 100644 --- a/VocaluxeLib/Songs/CSongLoader.cs +++ b/VocaluxeLib/Songs/CSongLoader.cs @@ -14,6 +14,7 @@ namespace VocaluxeLib.Songs private class CSongLoader { private readonly CSong _Song; + private int _LineNr; public CSongLoader(CSong song) { @@ -48,9 +49,27 @@ namespace VocaluxeLib.Songs return true; } - private void _LogReadError(string error, int lineNr) + /// + /// Logs a given message with file name and line# + /// + /// Message + /// True prepends "Error: "; False prepends "Warning: " + private void _LogMsg(string msg, bool error, bool withLineNr) { - CBase.Log.LogError(error + " in line #" + lineNr + "(" + Path.Combine(_Song.Folder, _Song.FileName) + ")"); + msg = (error ? "Error: " : "Warning: ") + msg; + if (withLineNr) + msg += " in line #" + _LineNr; + CBase.Log.LogError(msg + " (" + Path.Combine(_Song.Folder, _Song.FileName) + ")"); + } + + private void _LogError(string msg, bool withLineNr = true) + { + _LogMsg(msg, true, withLineNr); + } + + private void _LogWarning(string msg, bool withLineNr = true) + { + _LogMsg(msg, false, withLineNr); } public bool ReadHeader(Encoding encoding = null) @@ -60,20 +79,20 @@ namespace VocaluxeLib.Songs if (!File.Exists(filePath)) return false; - _Song.Language.Clear(); - _Song.Genre.Clear(); + _Song.Languages.Clear(); + _Song.Genres.Clear(); _Song._Comment.Clear(); var headerFlags = new EHeaderFlags(); StreamReader sr = null; - int lineNr = 0; + _LineNr = 0; try { sr = new StreamReader(filePath, Encoding.Default, true); while (!sr.EndOfStream) { string line = sr.ReadLine(); - lineNr++; + _LineNr++; if (line == "") continue; if (!line[0].Equals('#')) @@ -88,7 +107,7 @@ namespace VocaluxeLib.Songs if (value == "") { - _LogReadError("Warning: Empty value skipped", lineNr); + _LogWarning("Empty value skipped"); continue; } @@ -97,7 +116,7 @@ namespace VocaluxeLib.Songs case "ENCODING": if (encoding != null) { - _LogReadError("Warning: Duplicate encoding ignored", lineNr); + _LogWarning("Duplicate encoding ignored"); continue; } Encoding newEncoding = CEncoding.GetEncoding(value); @@ -138,7 +157,7 @@ namespace VocaluxeLib.Songs } else { - _LogReadError("Error: Can't find audio file: " + Path.Combine(_Song.Folder, value), lineNr); + _LogError("Can't find audio file: " + Path.Combine(_Song.Folder, value)); return false; } break; @@ -149,98 +168,98 @@ namespace VocaluxeLib.Songs headerFlags |= EHeaderFlags.BPM; } else - _LogReadError("Warning: Invalid BPM value", lineNr); + _LogWarning("Invalid BPM value"); break; case "EDITION": if (value.Length > 1) _Song.Edition.Add(value); else - _LogReadError("Warning: Invalid edition", lineNr); + _LogWarning("Invalid edition"); break; case "GENRE": if (value.Length > 1) - _Song.Genre.Add(value); + _Song.Genres.Add(value); else - _LogReadError("Warning: Invalid genre", lineNr); + _LogWarning("Invalid genre"); break; case "YEAR": int num; if (value.Length == 4 && int.TryParse(value, out num) && num > 0) _Song.Year = value; else - _LogReadError("Warning: Invalid year", lineNr); + _LogWarning("Invalid year"); break; case "LANGUAGE": if (value.Length > 1) - _Song.Language.Add(value); + _Song.Languages.Add(value); else - _LogReadError("Warning: Invalid language", lineNr); + _LogWarning("Invalid language"); break; case "COMMENT": if (value.Length > 1) _Song._Comment.Add(value); else - _LogReadError("Warning: Invalid comment", lineNr); + _LogWarning("Invalid comment"); break; case "GAP": if (CHelper.TryParse(value, out _Song.Gap)) _Song.Gap /= 1000f; else - _LogReadError("Warning: Invalid gap", lineNr); + _LogWarning("Invalid gap"); break; case "COVER": if (File.Exists(Path.Combine(_Song.Folder, value))) _Song.CoverFileName = value; else - _LogReadError("Warning: Can't find cover file: " + Path.Combine(_Song.Folder, value), lineNr); + _LogWarning("Can't find cover file: " + Path.Combine(_Song.Folder, value)); break; case "BACKGROUND": if (File.Exists(Path.Combine(_Song.Folder, value))) _Song.BackgroundFileName = value; else - _LogReadError("Warning: Can't find background file: " + Path.Combine(_Song.Folder, value), lineNr); + _LogWarning("Can't find background file: " + Path.Combine(_Song.Folder, value)); break; case "VIDEO": if (File.Exists(Path.Combine(_Song.Folder, value))) _Song.VideoFileName = value; else - CBase.Log.LogError("Warning: Can't find video file: " + Path.Combine(_Song.Folder, value)); + _LogWarning("Can't find video file: " + Path.Combine(_Song.Folder, value)); break; case "VIDEOGAP": if (!CHelper.TryParse(value, out _Song.VideoGap)) - _LogReadError("Warning: Invalid videogap", lineNr); + _LogWarning("Invalid videogap"); break; case "VIDEOASPECT": if (!CHelper.TryParse(value, out _Song.VideoAspect, true)) - _LogReadError("Warning: Invalid videoaspect", lineNr); + _LogWarning("Invalid videoaspect"); break; case "START": if (!CHelper.TryParse(value, out _Song.Start)) - _LogReadError("Warning: Invalid start", lineNr); + _LogWarning("Invalid start"); break; case "END": if (CHelper.TryParse(value, out _Song.Finish)) _Song.Finish /= 1000f; else - _LogReadError("Warning: Invalid end", lineNr); + _LogWarning("Invalid end"); break; case "PREVIEWSTART": if (CHelper.TryParse(value, out _Song.PreviewStart) && _Song.PreviewStart >= 0f) headerFlags |= EHeaderFlags.PreviewStart; else - _LogReadError("Warning: Invalid previewstart", lineNr); + _LogWarning("Invalid previewstart"); break; case "MEDLEYSTARTBEAT": if (int.TryParse(value, out _Song.Medley.StartBeat)) headerFlags |= EHeaderFlags.MedleyStartBeat; else - _LogReadError("Warning: Invalid medleystartbeat", lineNr); + _LogWarning("Invalid medleystartbeat"); break; case "MEDLEYENDBEAT": if (int.TryParse(value, out _Song.Medley.EndBeat)) headerFlags |= EHeaderFlags.MedleyEndBeat; else - _LogReadError("Warning: Invalid medleyendbeat", lineNr); + _LogWarning("Invalid medleyendbeat"); break; case "CALCMEDLEY": if (value.ToUpper() == "OFF") @@ -250,37 +269,47 @@ namespace VocaluxeLib.Songs if (value.ToUpper() == "YES") _Song.Relative = true; break; + default: + if (identifier.StartsWith("DUETSINGER")) + identifier = identifier.Substring(10); + if (identifier.StartsWith("P")) + { + int player; + if (int.TryParse(identifier.Substring(1).Trim(), out player)) + foreach (int curPlayer in player.GetSetBits()) {} + } + break; } } //end of while if (sr.EndOfStream) { //No other data then header - CBase.Log.LogError("Lyrics/Notes missing: " + filePath); + _LogError("Lyrics/Notes missing", false); return false; } if ((headerFlags & EHeaderFlags.Title) == 0) { - CBase.Log.LogError("Title tag missing: " + filePath); + _LogError("Title tag missing", false); return false; } if ((headerFlags & EHeaderFlags.Artist) == 0) { - CBase.Log.LogError("Artist tag missing: " + filePath); + _LogError("Artist tag missing", false); return false; } if ((headerFlags & EHeaderFlags.MP3) == 0) { - CBase.Log.LogError("MP3 tag missing: " + filePath); + _LogError("MP3 tag missing", false); return false; } if ((headerFlags & EHeaderFlags.BPM) == 0) { - CBase.Log.LogError("BPM tag missing: " + filePath); + _LogError("BPM tag missing", false); return false; } @@ -289,7 +318,7 @@ namespace VocaluxeLib.Songs { if (_Song.Medley.StartBeat > _Song.Medley.EndBeat) { - CBase.Log.LogError("MedleyStartBeat is bigger than MedleyEndBeat in file: " + filePath); + _LogError("MedleyStartBeat is bigger than MedleyEndBeat in file", false); headerFlags = headerFlags - EHeaderFlags.MedleyStartBeat - EHeaderFlags.MedleyEndBeat; } } @@ -314,7 +343,7 @@ namespace VocaluxeLib.Songs { if (sr != null) sr.Dispose(); - _LogReadError("Error reading txt header" + e.Message, lineNr); + _LogError("Error reading txt header" + e.Message, false); return false; } sr.Dispose(); @@ -347,7 +376,7 @@ namespace VocaluxeLib.Songs if (!File.Exists(filePath)) { - CBase.Log.LogError("Error loading song. The file does not exist: " + filePath); + _LogError("The file does not exist", false); return false; } @@ -356,7 +385,7 @@ namespace VocaluxeLib.Songs bool endFound = false; int player = 1; - int lineNr = 0; + _LineNr = 0; char[] trimChars = {' ', ':'}; char[] splitChars = {' '}; @@ -372,7 +401,7 @@ namespace VocaluxeLib.Songs while (!sr.EndOfStream && !endFound) { string line = sr.ReadLine(); - lineNr++; + _LineNr++; if (String.IsNullOrEmpty(line)) continue; @@ -394,7 +423,7 @@ namespace VocaluxeLib.Songs if (!int.TryParse(line, out player)) { - _LogReadError("Error: Wrong or missing number after \"P\"", lineNr); + _LogError("Wrong or missing number after \"P\""); return false; } sr.ReadLine(); @@ -407,28 +436,28 @@ namespace VocaluxeLib.Songs { if (noteData.Length == 3) { - _LogReadError("Warning: Ignored note without text", lineNr); + _LogWarning("Ignored note without text"); continue; } - _LogReadError("Error: Invalid note found", lineNr); + _LogError("Invalid note found"); sr.Dispose(); return false; } int tone; if (!int.TryParse(noteData[0], out beat) || !int.TryParse(noteData[1], out length) || !int.TryParse(noteData[2], out tone)) { - _LogReadError("Error: Invalid note found (non-numeric values)", lineNr); + _LogError("Invalid note found (non-numeric values)"); sr.Dispose(); return false; } string text = noteData[3]; if (text.Trim() == "") { - _LogReadError("Warning: Ignored note without text", lineNr); + _LogWarning("Ignored note without text"); continue; } if (length < 1) - _LogReadError("Warning: Ignored note with length < 1", lineNr); + _LogWarning("Ignored note with length < 1"); else { ENoteType noteType; @@ -443,18 +472,10 @@ namespace VocaluxeLib.Songs if (_Song.Relative) beat += currentBeat; - int curPlayer = 0; - int tmpPlayer = player; - //Evaluate as bitset - while (tmpPlayer > 0) + foreach (int curPlayer in player.GetSetBits()) { - if ((tmpPlayer & 1) != 0) - { - if (!_ParseNote(curPlayer, noteType, beat, length, tone, text)) - _LogReadError("Warning: Ignored note for player " + (curPlayer + 1) + " because it overlaps with other note", lineNr); - } - tmpPlayer >>= 1; - curPlayer++; + if (!_ParseNote(curPlayer, noteType, beat, length, tone, text)) + _LogWarning("Ignored note for player " + (curPlayer + 1) + " because it overlaps with other note"); } } lastNoteEnd = beat + length; @@ -463,13 +484,13 @@ namespace VocaluxeLib.Songs string[] lineBreakData = line.Split(splitChars); if (lineBreakData.Length < 1) { - _LogReadError("Error: Invalid line break found (No beat)", lineNr); + _LogError("Invalid line break found (No beat)"); sr.Dispose(); return false; } if (!int.TryParse(lineBreakData[0], out beat)) { - _LogReadError("Error: Invalid line break found (Non-numeric value)", lineNr); + _LogError("Invalid line break found (Non-numeric value)"); sr.Dispose(); return false; } @@ -478,38 +499,30 @@ namespace VocaluxeLib.Songs { beat += currentBeat; if (lineBreakData.Length < 2 || !int.TryParse(lineBreakData[1], out length)) - _LogReadError("Warning: Missing line break length", lineNr); + _LogWarning("Missing line break length"); else currentBeat += length; } if (beat < lastNoteEnd) { - _LogReadError("Warning: Line break is before previous note end. Adjusted it (might not work for relative songs)", lineNr); + _LogWarning("Line break is before previous note end. Adjusted it (might not work for relative songs)"); beat = lastNoteEnd; } if (beat < 1) - _LogReadError("Warning: Ignored line break because position is < 1", lineNr); + _LogWarning("Ignored line break because position is < 1"); else { - int curPlayer = 0; - int tmpPlayer = player; - //Evaluate as bitset - while (tmpPlayer > 0) + foreach (int curPlayer in player.GetSetBits()) { - if ((tmpPlayer & 1) != 0) - { - if (_NewSentence(curPlayer, beat)) - _LogReadError("Warning: Ignored line break for player " + (curPlayer + 1) + " (Overlapping or duplicate)", lineNr); - } - tmpPlayer >>= 1; - curPlayer++; + if (_NewSentence(curPlayer, beat)) + _LogWarning("Ignored line break for player " + (curPlayer + 1) + " (Overlapping or duplicate)"); } } break; default: - _LogReadError("Error loading song. Unexpected or missing character (" + tag + ")", lineNr); + _LogError("Error loading song. Unexpected or missing character (" + tag + ")"); return false; } } @@ -519,7 +532,7 @@ namespace VocaluxeLib.Songs } catch (Exception e) { - _LogReadError("Error: An unhandled exception occured (" + e.Message + ")", lineNr); + _LogError("An unhandled exception occured (" + e.Message + ")"); if (sr != null) sr.Dispose(); return false; @@ -535,7 +548,7 @@ namespace VocaluxeLib.Songs } catch (Exception e) { - CBase.Log.LogError("Error loading song. An unhandled exception occured (" + e.Message + "): " + filePath); + _LogError("An unhandled exception occured (" + e.Message + ")", false); return false; } return true; diff --git a/VocaluxeLib/Songs/CSungNote.cs b/VocaluxeLib/Songs/CSungNote.cs index b84bfe05..a624d4c5 100644 --- a/VocaluxeLib/Songs/CSungNote.cs +++ b/VocaluxeLib/Songs/CSungNote.cs @@ -48,7 +48,7 @@ namespace VocaluxeLib.Songs } #region Properties - public double Points; + public new double Points; // for drawing player notes public bool Hit { From a9b3e1d3f8a2475ed4d10788ade72eca8b89e378 Mon Sep 17 00:00:00 2001 From: Flamefire Date: Fri, 11 Oct 2013 14:39:23 +0200 Subject: [PATCH 10/23] Simplify song-image detection Store comment as 1 string --- VocaluxeLib/Extensions.cs | 7 ++++++- VocaluxeLib/Songs/CSong.cs | 14 ++++++++------ VocaluxeLib/Songs/CSongLoader.cs | 9 ++++----- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/VocaluxeLib/Extensions.cs b/VocaluxeLib/Extensions.cs index f19e4b2b..8f2be91e 100644 --- a/VocaluxeLib/Extensions.cs +++ b/VocaluxeLib/Extensions.cs @@ -54,7 +54,7 @@ namespace VocaluxeLib /// /// /// - public static List GetSetBits(this int value) + public static IEnumerable GetSetBits(this int value) { var result = new List(); int curBit = 0; @@ -68,5 +68,10 @@ namespace VocaluxeLib } return result; } + + public static bool ContainsIgnoreCase(this string value, string other) + { + return value.IndexOf(other, StringComparison.CurrentCultureIgnoreCase) >= 0; + } } } \ No newline at end of file diff --git a/VocaluxeLib/Songs/CSong.cs b/VocaluxeLib/Songs/CSong.cs index 9833e963..6fab1f3b 100644 --- a/VocaluxeLib/Songs/CSong.cs +++ b/VocaluxeLib/Songs/CSong.cs @@ -124,7 +124,7 @@ namespace VocaluxeLib.Songs public float Gap; public float VideoGap; - private readonly List _Comment = new List(); + private string _Comment = ""; // Sorting public int ID; @@ -208,7 +208,7 @@ namespace VocaluxeLib.Songs Gap = song.Gap; VideoGap = song.VideoGap; - _Comment = new List(song._Comment); + _Comment = song._Comment; ID = song.ID; _Visible = song._Visible; @@ -271,11 +271,12 @@ namespace VocaluxeLib.Songs if (CoverFileName == "") { List files = CHelper.ListFiles(Folder, "*.jpg"); + files.AddRange(CHelper.ListFiles(Folder, "*.jpeg")); files.AddRange(CHelper.ListFiles(Folder, "*.png")); foreach (String file in files) { - if (Regex.IsMatch(file, @".[CO].", RegexOptions.IgnoreCase) && - (Regex.IsMatch(file, @"" + Regex.Escape(Title), RegexOptions.IgnoreCase) || Regex.IsMatch(file, @"" + Regex.Escape(Artist), RegexOptions.IgnoreCase))) + if (file.ContainsIgnoreCase("[CO]") && + (file.ContainsIgnoreCase(Title) || file.ContainsIgnoreCase(Artist))) CoverFileName = file; } } @@ -283,11 +284,12 @@ namespace VocaluxeLib.Songs if (BackgroundFileName == "") { List files = CHelper.ListFiles(Folder, "*.jpg"); + files.AddRange(CHelper.ListFiles(Folder, "*.jpeg")); files.AddRange(CHelper.ListFiles(Folder, "*.png")); foreach (String file in files) { - if (Regex.IsMatch(file, @".[BG].", RegexOptions.IgnoreCase) && - (Regex.IsMatch(file, @"" + Regex.Escape(Title), RegexOptions.IgnoreCase) || Regex.IsMatch(file, @"" + Regex.Escape(Artist), RegexOptions.IgnoreCase))) + if (file.ContainsIgnoreCase("[BG]") && + (file.ContainsIgnoreCase(Title) || file.ContainsIgnoreCase(Artist))) BackgroundFileName = file; } } diff --git a/VocaluxeLib/Songs/CSongLoader.cs b/VocaluxeLib/Songs/CSongLoader.cs index cef6e4dd..80395065 100644 --- a/VocaluxeLib/Songs/CSongLoader.cs +++ b/VocaluxeLib/Songs/CSongLoader.cs @@ -81,7 +81,7 @@ namespace VocaluxeLib.Songs _Song.Languages.Clear(); _Song.Genres.Clear(); - _Song._Comment.Clear(); + _Song._Comment = ""; var headerFlags = new EHeaderFlags(); StreamReader sr = null; @@ -196,10 +196,9 @@ namespace VocaluxeLib.Songs _LogWarning("Invalid language"); break; case "COMMENT": - if (value.Length > 1) - _Song._Comment.Add(value); - else - _LogWarning("Invalid comment"); + if (!String.IsNullOrEmpty(_Song._Comment)) + _Song._Comment += "\r\n"; + _Song._Comment += value; break; case "GAP": if (CHelper.TryParse(value, out _Song.Gap)) From d498e0b9353217d7515597d53a9c0ba1575540ad Mon Sep 17 00:00:00 2001 From: Flamefire Date: Fri, 11 Oct 2013 19:19:42 +0200 Subject: [PATCH 11/23] Remove empty lines from Songs (causes problems otherwise!) Compatibility reading mode for songs with 0-based lengths Give Lines as an enumerable instead of creating an array --- Vocaluxe/Base/CGame.cs | 6 +- Vocaluxe/Screens/CScreenSing.cs | 2 +- VocaluxeLib/Songs/CNotes.cs | 4 +- VocaluxeLib/Songs/CSong.cs | 2 +- VocaluxeLib/Songs/CSongLoader.cs | 131 ++++++++++++++++++++++++++----- VocaluxeLib/Songs/CVoice.cs | 5 ++ 6 files changed, 125 insertions(+), 25 deletions(-) diff --git a/Vocaluxe/Base/CGame.cs b/Vocaluxe/Base/CGame.cs index 1b80d09f..7f47fbf5 100644 --- a/Vocaluxe/Base/CGame.cs +++ b/Vocaluxe/Base/CGame.cs @@ -406,9 +406,9 @@ namespace Vocaluxe.Base } Players[p].SungLines.RemoveRange(deleteLine, Players[p].SungLines.Count - deleteLine); } - CSong song = GetSong(); - for (int i = 0; i < GetSong().Notes.Voices[0].Lines.Length; i++) - CLog.LogError((i + 1) + ". " + song.Notes.Voices[0].Lines[i].NoteCount + " - " + Players[0].SungLines[i].NoteCount); + //CSong song = GetSong(); + //for (int i = 0; i < GetSong().Notes.Voices[0].Lines.Length; i++) + // CLog.LogError((i + 1) + ". " + song.Notes.Voices[0].Lines[i].NoteCount + " - " + Players[0].SungLines[i].NoteCount); CSound.SetPosition(soundStream, time); CVideo.Skip(vidStream, time, GetSong().VideoGap); diff --git a/Vocaluxe/Screens/CScreenSing.cs b/Vocaluxe/Screens/CScreenSing.cs index cde771bc..e792ba4e 100644 --- a/Vocaluxe/Screens/CScreenSing.cs +++ b/Vocaluxe/Screens/CScreenSing.cs @@ -1084,7 +1084,7 @@ namespace Vocaluxe.Screens if (song == null || !song.NotesLoaded) return null; - var alpha = new float[song.Notes.Voices.Length * 2]; + var alpha = new float[song.Notes.VoiceCount * 2]; float currentTime = _CurrentTime - song.Gap; for (int i = 0; i < song.Notes.VoiceCount; i++) diff --git a/VocaluxeLib/Songs/CNotes.cs b/VocaluxeLib/Songs/CNotes.cs index 66750715..79598dcd 100644 --- a/VocaluxeLib/Songs/CNotes.cs +++ b/VocaluxeLib/Songs/CNotes.cs @@ -70,9 +70,9 @@ namespace VocaluxeLib.Songs VoiceNames = new CVoiceNames(notes.VoiceNames); } - public CVoice[] Voices + public IEnumerable Voices { - get { return _Voices.ToArray(); } + get { return _Voices; } } public int VoiceCount diff --git a/VocaluxeLib/Songs/CSong.cs b/VocaluxeLib/Songs/CSong.cs index 6fab1f3b..6dd6e33f 100644 --- a/VocaluxeLib/Songs/CSong.cs +++ b/VocaluxeLib/Songs/CSong.cs @@ -133,7 +133,7 @@ namespace VocaluxeLib.Songs private readonly bool _Selected; public bool IsDuet { - get { return Notes.Voices.Length > 1; } + get { return Notes.VoiceCount > 1; } } public readonly List Edition = new List(); diff --git a/VocaluxeLib/Songs/CSongLoader.cs b/VocaluxeLib/Songs/CSongLoader.cs index 80395065..9c2bd0f2 100644 --- a/VocaluxeLib/Songs/CSongLoader.cs +++ b/VocaluxeLib/Songs/CSongLoader.cs @@ -351,21 +351,63 @@ namespace VocaluxeLib.Songs CBase.DataBase.GetDataBaseSongInfos(_Song.Artist, _Song.Title, out _Song.NumPlayed, out _Song.DateAdded, out _Song.DataBaseSongID); //Before saving this tags to .txt: Check, if ArtistSorting and Artist are equal, then don't save this tag. - if (_Song.ArtistSorting == "") + if (String.IsNullOrEmpty(_Song.ArtistSorting)) _Song.ArtistSorting = _Song.Artist; - if (_Song.TitleSorting == "") + if (String.IsNullOrEmpty(_Song.TitleSorting)) _Song.TitleSorting = _Song.Title; return true; } - public bool ReadNotes() + private enum ENoteReadMode { - return _ReadNotes(); + Normal, + ZeroBased, + OneBased } - private bool _ReadNotes(bool forceReload = false) + private class CAutoChanges + { + public int ZeroLengthNoteCt; + public int OverlapNoteCt; + public int NoTextNoteCt; + public int NoLengthBreakCt; + public int AjustedBreakCt; + public int InvalidPosBreakCt; + + public bool IsModified + { + get { return ZeroLengthNoteCt + OverlapNoteCt + NoTextNoteCt + NoLengthBreakCt + AjustedBreakCt + InvalidPosBreakCt > 0; } + } + + public override string ToString() + { + string result = ""; + int skippedNotesCt = ZeroLengthNoteCt + OverlapNoteCt + NoTextNoteCt; + if (skippedNotesCt > 0) + result += "Skipped " + skippedNotesCt + " notes (0-Length: " + ZeroLengthNoteCt + ", Overlapping: " + OverlapNoteCt + ", No text: " + NoTextNoteCt + ")\r\n"; + if (InvalidPosBreakCt > 0) + result += "Skipped " + InvalidPosBreakCt + " line breaks (Invalid position)\r\n"; + int adjustedBreakCt = AjustedBreakCt + NoLengthBreakCt; + if (adjustedBreakCt > 0) + result += "Adjusted " + adjustedBreakCt + " line breaks (Overlapping previous note: " + AjustedBreakCt + ", No length: " + NoLengthBreakCt + ")\r\n"; + return result; + } + } + + private ENoteReadMode _CurrentReadMode = ENoteReadMode.Normal; + private const int _MaxZeroNoteCt = 1; + private const int _MaxOverlapNoteCt = 3; + + /// + /// Read notes. First try to read notes normally (assume standard)
+ /// If there are more than _MaxZeroNoteCt with length < 1, try to read notes adding 1 to length
+ /// Then if there are more than _MaxOverlapNoteCt fallback to first version ignoring notes with length < 1 + ///
+ /// + /// + public bool ReadNotes(bool forceReload = false) { //Skip loading if already done and no reload is forced if (_Song.NotesLoaded && !forceReload) @@ -380,8 +422,8 @@ namespace VocaluxeLib.Songs } int currentBeat = 0; //Used for relative songs - int lastNoteEnd = 0; - bool endFound = false; + CSongNote lastNote = null; //Holds last parsed note. Get's reset on player change + bool endFound = false; // True if end tag was found int player = 1; _LineNr = 0; @@ -389,6 +431,8 @@ namespace VocaluxeLib.Songs char[] trimChars = {' ', ':'}; char[] splitChars = {' '}; + var changesMade = new CAutoChanges(); + StreamReader sr = null; try { @@ -425,7 +469,7 @@ namespace VocaluxeLib.Songs _LogError("Wrong or missing number after \"P\""); return false; } - sr.ReadLine(); + lastNote = null; break; case ':': case '*': @@ -436,6 +480,7 @@ namespace VocaluxeLib.Songs if (noteData.Length == 3) { _LogWarning("Ignored note without text"); + changesMade.NoTextNoteCt++; continue; } _LogError("Invalid note found"); @@ -453,10 +498,23 @@ namespace VocaluxeLib.Songs if (text.Trim() == "") { _LogWarning("Ignored note without text"); + changesMade.NoTextNoteCt++; continue; } + if (_CurrentReadMode == ENoteReadMode.ZeroBased) + length++; if (length < 1) + { + changesMade.ZeroLengthNoteCt++; + if (_CurrentReadMode == ENoteReadMode.Normal && changesMade.ZeroLengthNoteCt > _MaxZeroNoteCt && changesMade.OverlapNoteCt <= _MaxOverlapNoteCt) + { + _LogWarning("Found more than " + _MaxZeroNoteCt + " note with length < 1. Trying alternative read mode."); + _CurrentReadMode = ENoteReadMode.ZeroBased; + sr.Dispose(); + return ReadNotes(true); + } _LogWarning("Ignored note with length < 1"); + } else { ENoteType noteType; @@ -471,13 +529,30 @@ namespace VocaluxeLib.Songs if (_Song.Relative) beat += currentBeat; + + bool ignored = false; foreach (int curPlayer in player.GetSetBits()) { - if (!_ParseNote(curPlayer, noteType, beat, length, tone, text)) + //Create the note here as we want independent instances in the lines. Otherwhise we can't modify them later + lastNote = new CSongNote(beat, length, tone, text, noteType); + if (!_AddNote(curPlayer, lastNote)) + { + if (!ignored) + { + ignored = true; + changesMade.OverlapNoteCt++; + if (changesMade.OverlapNoteCt > _MaxOverlapNoteCt && _CurrentReadMode == ENoteReadMode.ZeroBased) + { + _LogWarning("Found more than " + _MaxOverlapNoteCt + " overlapping notes. Using standard mode."); + _CurrentReadMode = ENoteReadMode.OneBased; + sr.Dispose(); + return ReadNotes(true); + } + } _LogWarning("Ignored note for player " + (curPlayer + 1) + " because it overlaps with other note"); + } } } - lastNoteEnd = beat + length; break; case '-': string[] lineBreakData = line.Split(splitChars); @@ -498,36 +573,51 @@ namespace VocaluxeLib.Songs { beat += currentBeat; if (lineBreakData.Length < 2 || !int.TryParse(lineBreakData[1], out length)) + { _LogWarning("Missing line break length"); + changesMade.NoLengthBreakCt++; + } else currentBeat += length; } - if (beat < lastNoteEnd) + if (lastNote != null && beat < lastNote.EndBeat) { - _LogWarning("Line break is before previous note end. Adjusted it (might not work for relative songs)"); - beat = lastNoteEnd; + _LogWarning("Line break is before previous note end. Adjusted."); + changesMade.AjustedBreakCt++; + if (_Song.Relative) + currentBeat += lastNote.EndBeat - beat; + beat = lastNote.EndBeat; } if (beat < 1) + { _LogWarning("Ignored line break because position is < 1"); + changesMade.InvalidPosBreakCt++; + } else { foreach (int curPlayer in player.GetSetBits()) { - if (_NewSentence(curPlayer, beat)) + if (!_NewSentence(curPlayer, beat)) _LogWarning("Ignored line break for player " + (curPlayer + 1) + " (Overlapping or duplicate)"); } } break; default: - _LogError("Error loading song. Unexpected or missing character (" + tag + ")"); + _LogError("Unexpected or missing character (" + tag + ")"); return false; } } - foreach (CVoice voice in _Song.Notes.Voices) + for (int i = 0; i < _Song.Notes.VoiceCount; i++) + { + CVoice voice = _Song.Notes.GetVoice(i); + int emptyLines = voice.RemoveEmptyLines(); + if (emptyLines > 0) + _LogWarning("Removed " + emptyLines + " empty lines from P" + ". This often indicates a problem with the line breaks in the file", false); voice.UpdateTimings(); + } } catch (Exception e) { @@ -550,12 +640,17 @@ namespace VocaluxeLib.Songs _LogError("An unhandled exception occured (" + e.Message + ")", false); return false; } + + if (changesMade.IsModified) + { + string msg = "Automatic changes have been made to " + filePath + " Please check result!\r\n" + changesMade; + CBase.Log.LogError("Warning:" + msg); + } return true; } - private bool _ParseNote(int player, ENoteType noteType, int start, int length, int tone, string text) + private bool _AddNote(int player, CSongNote note) { - var note = new CSongNote(start, length, tone, text, noteType); CVoice voice = _Song.Notes.GetVoice(player); return voice.AddNote(note, false); } diff --git a/VocaluxeLib/Songs/CVoice.cs b/VocaluxeLib/Songs/CVoice.cs index 3f1d9766..b2d962b3 100644 --- a/VocaluxeLib/Songs/CVoice.cs +++ b/VocaluxeLib/Songs/CVoice.cs @@ -270,5 +270,10 @@ namespace VocaluxeLib.Songs line.SetMedley(startBeat, endBeat); } #endregion Methods + + public int RemoveEmptyLines() + { + return _Lines.RemoveAll(line => line.NoteCount == 0); + } } } \ No newline at end of file From 8e80cf93e2f1ddb747abe75082c7be3230556c66 Mon Sep 17 00:00:00 2001 From: Flamefire Date: Fri, 11 Oct 2013 20:25:05 +0200 Subject: [PATCH 12/23] Add Songwriter and #PREVIEW tag --- VocaluxeLib/Songs/CSong.cs | 12 +++++ VocaluxeLib/Songs/CSongLoader.cs | 22 ++++++++- VocaluxeLib/Songs/CSongWriter.cs | 78 ++++++++++++++++++++++++++++++++ VocaluxeLib/VocaluxeLib.csproj | 1 + 4 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 VocaluxeLib/Songs/CSongWriter.cs diff --git a/VocaluxeLib/Songs/CSong.cs b/VocaluxeLib/Songs/CSong.cs index 6dd6e33f..1db59d29 100644 --- a/VocaluxeLib/Songs/CSong.cs +++ b/VocaluxeLib/Songs/CSong.cs @@ -117,11 +117,23 @@ namespace VocaluxeLib.Songs public string TitleSorting = String.Empty; public string ArtistSorting = String.Empty; + /// + /// Start of the song in s (s in txt) + /// public float Start; + /// + /// End of the song in s (ms in txt) + /// public float Finish; public float BPM = 1f; + /// + /// Gap of the mp3 in s (ms in txt) + /// public float Gap; + /// + /// Gap of the video in s (s in txt) + /// public float VideoGap; private string _Comment = ""; diff --git a/VocaluxeLib/Songs/CSongLoader.cs b/VocaluxeLib/Songs/CSongLoader.cs index 9c2bd0f2..3c29dcde 100644 --- a/VocaluxeLib/Songs/CSongLoader.cs +++ b/VocaluxeLib/Songs/CSongLoader.cs @@ -11,6 +11,11 @@ namespace VocaluxeLib.Songs /// public partial class CSong { + /// + /// Factor the bpm given in the txt is multiplied with + /// + private const int _BPMFactor = 4; + private class CSongLoader { private readonly CSong _Song; @@ -54,6 +59,7 @@ namespace VocaluxeLib.Songs /// /// Message /// True prepends "Error: "; False prepends "Warning: " + /// Adds line# private void _LogMsg(string msg, bool error, bool withLineNr) { msg = (error ? "Error: " : "Warning: ") + msg; @@ -164,7 +170,7 @@ namespace VocaluxeLib.Songs case "BPM": if (CHelper.TryParse(value, out _Song.BPM)) { - _Song.BPM *= 4; + _Song.BPM *= _BPMFactor; headerFlags |= EHeaderFlags.BPM; } else @@ -248,6 +254,16 @@ namespace VocaluxeLib.Songs else _LogWarning("Invalid previewstart"); break; + case "PREVIEW": + if (CHelper.TryParse(value, out _Song.PreviewStart) && _Song.PreviewStart >= 0f) + { + //This is stored in ms not like PREVIEWSTART! + _Song.PreviewStart /= 1000f; + headerFlags |= EHeaderFlags.PreviewStart; + } + else + _LogWarning("Invalid previewstart"); + break; case "MEDLEYSTARTBEAT": if (int.TryParse(value, out _Song.Medley.StartBeat)) headerFlags |= EHeaderFlags.MedleyStartBeat; @@ -268,6 +284,9 @@ namespace VocaluxeLib.Songs if (value.ToUpper() == "YES") _Song.Relative = true; break; + case "RESULTION": + _LogWarning("#RESOLUTION tag is outdated and ignored. Test the song to see if note timing is still correct!"); + break; default: if (identifier.StartsWith("DUETSINGER")) identifier = identifier.Substring(10); @@ -277,6 +296,7 @@ namespace VocaluxeLib.Songs if (int.TryParse(identifier.Substring(1).Trim(), out player)) foreach (int curPlayer in player.GetSetBits()) {} } + break; } } //end of while diff --git a/VocaluxeLib/Songs/CSongWriter.cs b/VocaluxeLib/Songs/CSongWriter.cs new file mode 100644 index 00000000..1daf89ff --- /dev/null +++ b/VocaluxeLib/Songs/CSongWriter.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace VocaluxeLib.Songs +{ + public partial class CSong + { + private class CSongWriter + { + private readonly CSong _Song; + + public CSongWriter(CSong song) + { + _Song = song; + } + + private void _WriteHeaderEntry(StreamWriter sw, string id, T value) + { + if (!value.Equals(default(T))) + sw.WriteLine("#" + id.ToUpper() + ":" + value); + } + + private void _WriteHeaderEntry(StreamWriter sw, string id, bool value) + { + if (value) + _WriteHeaderEntry(sw, id, "YES"); + } + + private void _WriteHeaderEntry(StreamWriter sw, string id, IList value) + { + if (value!=null && value.Count>0) + foreach (var val in value) + _WriteHeaderEntry(sw, id, val); + } + + private void _WriteHeader(StreamWriter sw) + { + _WriteHeaderEntry(sw, "ENCODING", _Song.Encoding); + _WriteHeaderEntry(sw, "TITLE", _Song.Title); + _WriteHeaderEntry(sw, "ARTIST", _Song.Artist); + if (!_Song.Title.Equals(_Song.TitleSorting)) _WriteHeaderEntry(sw, "TITLE-ON-SORTING", _Song.TitleSorting); + if (!_Song.Artist.Equals(_Song.ArtistSorting)) _WriteHeaderEntry(sw, "ARTIST-ON-SORTING", _Song.ArtistSorting); + _WriteHeaderEntry(sw, "LANGUAGE", _Song.Languages); + _WriteHeaderEntry(sw, "EDITION", _Song.Edition); + _WriteHeaderEntry(sw, "GENRE", _Song.Genres); + _WriteHeaderEntry(sw, "YEAR", _Song.Year); + _WriteHeaderEntry(sw, "COMMENT", _Song._Comment); + _WriteHeaderEntry(sw, "MP3", _Song.MP3FileName); + _WriteHeaderEntry(sw, "COVER", _Song.CoverFileName); + _WriteHeaderEntry(sw, "BACKGROUND", _Song.BackgroundFileName); + _WriteHeaderEntry(sw, "VIDEO", _Song.VideoFileName); + _WriteHeaderEntry(sw, "VIDEOGAP", _Song.VideoGap); + _WriteHeaderEntry(sw, "VIDEOASPECT", _Song.VideoAspect); + _WriteHeaderEntry(sw, "RELATIVE", _Song.Relative); + _WriteHeaderEntry(sw, "BPM", _Song.BPM); + _WriteHeaderEntry(sw, "GAP", _Song.Gap); + _WriteHeaderEntry(sw, "PREVIEWSTART", _Song.PreviewStart); + _WriteHeaderEntry(sw, "START", _Song.Start); + _WriteHeaderEntry(sw, "END", _Song.Finish); + if(!_Song.CalculateMedley) + _WriteHeaderEntry(sw, "CALCMEDLEY", "OFF"); + if (_Song.Medley.Source == EMedleySource.Tag) + { + _WriteHeaderEntry(sw, "MEDLEYSTARTBEAT", _Song.Medley.StartBeat); + _WriteHeaderEntry(sw, "MEDLEYENDBEAT", _Song.Medley.EndBeat); + } + for (int i = 0; i < _Song.Notes.VoiceCount; i++) + { + if (_Song.Notes.VoiceNames.IsSet(i)) + _WriteHeaderEntry(sw, "P" + i, _Song.Notes.VoiceNames[i]); + } + } + } + } +} \ No newline at end of file diff --git a/VocaluxeLib/VocaluxeLib.csproj b/VocaluxeLib/VocaluxeLib.csproj index 3a69d742..8a784a53 100644 --- a/VocaluxeLib/VocaluxeLib.csproj +++ b/VocaluxeLib/VocaluxeLib.csproj @@ -175,6 +175,7 @@ + From da91f177eb20f4907941adfc1c4c5f855745478d Mon Sep 17 00:00:00 2001 From: Flamefire Date: Fri, 11 Oct 2013 20:25:56 +0200 Subject: [PATCH 13/23] Read more than 2 voice names --- VocaluxeLib/Songs/CSongLoader.cs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/VocaluxeLib/Songs/CSongLoader.cs b/VocaluxeLib/Songs/CSongLoader.cs index 3c29dcde..5236b823 100644 --- a/VocaluxeLib/Songs/CSongLoader.cs +++ b/VocaluxeLib/Songs/CSongLoader.cs @@ -147,14 +147,6 @@ namespace VocaluxeLib.Songs case "ARTIST-ON-SORTING": _Song.ArtistSorting = value; break; - case "DUETSINGERP1": - case "P1": - _Song.Notes.VoiceNames[0] = value; - break; - case "DUETSINGERP2": - case "P2": - _Song.Notes.VoiceNames[1] = value; - break; case "MP3": if (File.Exists(Path.Combine(_Song.Folder, value))) { @@ -294,7 +286,10 @@ namespace VocaluxeLib.Songs { int player; if (int.TryParse(identifier.Substring(1).Trim(), out player)) - foreach (int curPlayer in player.GetSetBits()) {} + { + foreach (int curPlayer in player.GetSetBits()) + _Song.Notes.VoiceNames[curPlayer] = value; + } } break; From 0a97a2e46d5542532f661efeb56281aec8824b0d Mon Sep 17 00:00:00 2001 From: Flamefire Date: Sat, 12 Oct 2013 14:50:10 +0200 Subject: [PATCH 14/23] Fix renaming of Editions etc. --- Vocaluxe.sln | 5 +---- Vocaluxe/Base/CSongSorter.cs | 6 +++--- VocaluxeLib/Songs/CSong.cs | 7 +++++-- VocaluxeLib/Songs/CSongLoader.cs | 14 ++++++++++++-- VocaluxeLib/Songs/CSongWriter.cs | 22 ++++++++++++++-------- 5 files changed, 35 insertions(+), 19 deletions(-) diff --git a/Vocaluxe.sln b/Vocaluxe.sln index d894246e..0896a6f6 100644 --- a/Vocaluxe.sln +++ b/Vocaluxe.sln @@ -1,6 +1,6 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Express 2012 for Windows Desktop +# Visual Studio 2012 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Vocaluxe", "Vocaluxe\Vocaluxe.csproj", "{2A1E0F2D-5392-4BB7-B0A1-A9DFA13E79FF}" ProjectSection(ProjectDependencies) = postProject {6F3E1874-F28A-40E9-AFBD-31934D593678} = {6F3E1874-F28A-40E9-AFBD-31934D593678} @@ -154,7 +154,4 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection - GlobalSection(Performance) = preSolution - HasPerformanceSessions = true - EndGlobalSection EndGlobal diff --git a/Vocaluxe/Base/CSongSorter.cs b/Vocaluxe/Base/CSongSorter.cs index 9526cedb..eefb796f 100644 --- a/Vocaluxe/Base/CSongSorter.cs +++ b/Vocaluxe/Base/CSongSorter.cs @@ -157,10 +157,10 @@ namespace Vocaluxe.Base switch (_SongSorting) { case ESongSorting.TR_CONFIG_EDITION: - fieldName = "Edition"; + fieldName = "Editions"; break; case ESongSorting.TR_CONFIG_GENRE: - fieldName = "Genre"; + fieldName = "Genres"; break; case ESongSorting.TR_CONFIG_FOLDER: fieldName = "FolderName"; @@ -177,7 +177,7 @@ namespace Vocaluxe.Base fieldName = "Year"; break; case ESongSorting.TR_CONFIG_LANGUAGE: - fieldName = "Language"; + fieldName = "Languages"; break; case ESongSorting.TR_CONFIG_DATEADDED: fieldName = "DateAdded"; diff --git a/VocaluxeLib/Songs/CSong.cs b/VocaluxeLib/Songs/CSong.cs index 1db59d29..b8d19405 100644 --- a/VocaluxeLib/Songs/CSong.cs +++ b/VocaluxeLib/Songs/CSong.cs @@ -117,6 +117,9 @@ namespace VocaluxeLib.Songs public string TitleSorting = String.Empty; public string ArtistSorting = String.Empty; + public string Creator = ""; + public string Version = ""; + /// /// Start of the song in s (s in txt) /// @@ -148,7 +151,7 @@ namespace VocaluxeLib.Songs get { return Notes.VoiceCount > 1; } } - public readonly List Edition = new List(); + public readonly List Editions = new List(); public readonly List Genres = new List(); public string Year = ""; @@ -227,7 +230,7 @@ namespace VocaluxeLib.Songs _CatIndex = song._CatIndex; _Selected = song._Selected; - Edition = new List(song.Edition); + Editions = new List(song.Editions); Genres = new List(song.Genres); diff --git a/VocaluxeLib/Songs/CSongLoader.cs b/VocaluxeLib/Songs/CSongLoader.cs index 5236b823..db0445b6 100644 --- a/VocaluxeLib/Songs/CSongLoader.cs +++ b/VocaluxeLib/Songs/CSongLoader.cs @@ -109,6 +109,8 @@ namespace VocaluxeLib.Songs if (pos <= 1) continue; string identifier = line.Substring(1, pos - 1).Trim().ToUpper(); + if (identifier.Contains(" ")) + continue; string value = line.Substring(pos + 1).Trim(); if (value == "") @@ -147,6 +149,12 @@ namespace VocaluxeLib.Songs case "ARTIST-ON-SORTING": _Song.ArtistSorting = value; break; + case "CREATOR": + _Song.Creator = value; + break; + case "VERSION": + _Song.Version = value; + break; case "MP3": if (File.Exists(Path.Combine(_Song.Folder, value))) { @@ -170,7 +178,7 @@ namespace VocaluxeLib.Songs break; case "EDITION": if (value.Length > 1) - _Song.Edition.Add(value); + _Song.Editions.Add(value); else _LogWarning("Invalid edition"); break; @@ -276,7 +284,7 @@ namespace VocaluxeLib.Songs if (value.ToUpper() == "YES") _Song.Relative = true; break; - case "RESULTION": + case "RESOULTION": _LogWarning("#RESOLUTION tag is outdated and ignored. Test the song to see if note timing is still correct!"); break; default: @@ -291,6 +299,8 @@ namespace VocaluxeLib.Songs _Song.Notes.VoiceNames[curPlayer] = value; } } + else + _LogWarning("Unknown tag: #" + identifier); break; } diff --git a/VocaluxeLib/Songs/CSongWriter.cs b/VocaluxeLib/Songs/CSongWriter.cs index 1daf89ff..34cbccdd 100644 --- a/VocaluxeLib/Songs/CSongWriter.cs +++ b/VocaluxeLib/Songs/CSongWriter.cs @@ -31,23 +31,29 @@ namespace VocaluxeLib.Songs private void _WriteHeaderEntry(StreamWriter sw, string id, IList value) { - if (value!=null && value.Count>0) - foreach (var val in value) + if (value != null && value.Count > 0) + { + foreach (T val in value) _WriteHeaderEntry(sw, id, val); + } } private void _WriteHeader(StreamWriter sw) { _WriteHeaderEntry(sw, "ENCODING", _Song.Encoding); + _WriteHeaderEntry(sw, "CREATOR", _Song.Creator); + _WriteHeaderEntry(sw, "VERSION", _Song.Version); + _WriteHeaderEntry(sw, "COMMENT", _Song._Comment); _WriteHeaderEntry(sw, "TITLE", _Song.Title); _WriteHeaderEntry(sw, "ARTIST", _Song.Artist); - if (!_Song.Title.Equals(_Song.TitleSorting)) _WriteHeaderEntry(sw, "TITLE-ON-SORTING", _Song.TitleSorting); - if (!_Song.Artist.Equals(_Song.ArtistSorting)) _WriteHeaderEntry(sw, "ARTIST-ON-SORTING", _Song.ArtistSorting); - _WriteHeaderEntry(sw, "LANGUAGE", _Song.Languages); - _WriteHeaderEntry(sw, "EDITION", _Song.Edition); + if (!_Song.Title.Equals(_Song.TitleSorting)) + _WriteHeaderEntry(sw, "TITLE-ON-SORTING", _Song.TitleSorting); + if (!_Song.Artist.Equals(_Song.ArtistSorting)) + _WriteHeaderEntry(sw, "ARTIST-ON-SORTING", _Song.ArtistSorting); + _WriteHeaderEntry(sw, "EDITION", _Song.Editions); _WriteHeaderEntry(sw, "GENRE", _Song.Genres); + _WriteHeaderEntry(sw, "LANGUAGE", _Song.Languages); _WriteHeaderEntry(sw, "YEAR", _Song.Year); - _WriteHeaderEntry(sw, "COMMENT", _Song._Comment); _WriteHeaderEntry(sw, "MP3", _Song.MP3FileName); _WriteHeaderEntry(sw, "COVER", _Song.CoverFileName); _WriteHeaderEntry(sw, "BACKGROUND", _Song.BackgroundFileName); @@ -60,7 +66,7 @@ namespace VocaluxeLib.Songs _WriteHeaderEntry(sw, "PREVIEWSTART", _Song.PreviewStart); _WriteHeaderEntry(sw, "START", _Song.Start); _WriteHeaderEntry(sw, "END", _Song.Finish); - if(!_Song.CalculateMedley) + if (!_Song.CalculateMedley) _WriteHeaderEntry(sw, "CALCMEDLEY", "OFF"); if (_Song.Medley.Source == EMedleySource.Tag) { From b0cb402dc334aa35927dec0370cf0375bc6e40b9 Mon Sep 17 00:00:00 2001 From: Flamefire Date: Sat, 12 Oct 2013 16:10:24 +0200 Subject: [PATCH 15/23] Support #SOURCE, #ALBUM Write #COMMENT Keep unknown tags/lines --- VocaluxeLib/Songs/CSong.cs | 3 +++ VocaluxeLib/Songs/CSongLoader.cs | 30 +++++++++++++++++++++++++++--- VocaluxeLib/Songs/CSongWriter.cs | 11 ++++++++++- VocaluxeLib/Songs/CVoice.cs | 2 +- 4 files changed, 41 insertions(+), 5 deletions(-) diff --git a/VocaluxeLib/Songs/CSong.cs b/VocaluxeLib/Songs/CSong.cs index b8d19405..ece23c2c 100644 --- a/VocaluxeLib/Songs/CSong.cs +++ b/VocaluxeLib/Songs/CSong.cs @@ -119,6 +119,8 @@ namespace VocaluxeLib.Songs public string Creator = ""; public string Version = ""; + public string Source = ""; + public readonly List UnknownTags = new List(); /// /// Start of the song in s (s in txt) @@ -153,6 +155,7 @@ namespace VocaluxeLib.Songs public readonly List Editions = new List(); public readonly List Genres = new List(); + public string Album = ""; public string Year = ""; public readonly List Languages = new List(); diff --git a/VocaluxeLib/Songs/CSongLoader.cs b/VocaluxeLib/Songs/CSongLoader.cs index db0445b6..f93a2368 100644 --- a/VocaluxeLib/Songs/CSongLoader.cs +++ b/VocaluxeLib/Songs/CSongLoader.cs @@ -87,6 +87,7 @@ namespace VocaluxeLib.Songs _Song.Languages.Clear(); _Song.Genres.Clear(); + _Song.UnknownTags.Clear(); _Song._Comment = ""; var headerFlags = new EHeaderFlags(); @@ -107,14 +108,21 @@ namespace VocaluxeLib.Songs int pos = line.IndexOf(":", StringComparison.Ordinal); if (pos <= 1) + { + _Song.UnknownTags.Add(line); continue; + } string identifier = line.Substring(1, pos - 1).Trim().ToUpper(); if (identifier.Contains(" ")) + { + _Song.UnknownTags.Add(line); continue; + } string value = line.Substring(pos + 1).Trim(); if (value == "") { + _Song.UnknownTags.Add(line); _LogWarning("Empty value skipped"); continue; } @@ -150,11 +158,17 @@ namespace VocaluxeLib.Songs _Song.ArtistSorting = value; break; case "CREATOR": + case "AUTHOR": + case "AUTOR": _Song.Creator = value; break; case "VERSION": _Song.Version = value; break; + case "SOURCE": + case "YOUTUBE": + _Song.Source = value; + break; case "MP3": if (File.Exists(Path.Combine(_Song.Folder, value))) { @@ -188,6 +202,9 @@ namespace VocaluxeLib.Songs else _LogWarning("Invalid genre"); break; + case "ALBUM": + _Song.Album = value; + break; case "YEAR": int num; if (value.Length == 4 && int.TryParse(value, out num) && num > 0) @@ -289,7 +306,11 @@ namespace VocaluxeLib.Songs break; default: if (identifier.StartsWith("DUETSINGER")) + { identifier = identifier.Substring(10); + if (identifier.StartsWith("P")) // Possible fix for missing "P" + identifier = "P" + identifier; + } if (identifier.StartsWith("P")) { int player; @@ -300,7 +321,10 @@ namespace VocaluxeLib.Songs } } else + { + _Song.UnknownTags.Add(line); _LogWarning("Unknown tag: #" + identifier); + } break; } @@ -606,13 +630,13 @@ namespace VocaluxeLib.Songs currentBeat += length; } - if (lastNote != null && beat < lastNote.EndBeat) + if (lastNote != null && beat <= lastNote.EndBeat) { _LogWarning("Line break is before previous note end. Adjusted."); changesMade.AjustedBreakCt++; if (_Song.Relative) - currentBeat += lastNote.EndBeat - beat; - beat = lastNote.EndBeat; + currentBeat += lastNote.EndBeat - beat + 1; + beat = lastNote.EndBeat + 1; } if (beat < 1) diff --git a/VocaluxeLib/Songs/CSongWriter.cs b/VocaluxeLib/Songs/CSongWriter.cs index 34cbccdd..59d1cbe1 100644 --- a/VocaluxeLib/Songs/CSongWriter.cs +++ b/VocaluxeLib/Songs/CSongWriter.cs @@ -43,7 +43,13 @@ namespace VocaluxeLib.Songs _WriteHeaderEntry(sw, "ENCODING", _Song.Encoding); _WriteHeaderEntry(sw, "CREATOR", _Song.Creator); _WriteHeaderEntry(sw, "VERSION", _Song.Version); - _WriteHeaderEntry(sw, "COMMENT", _Song._Comment); + _WriteHeaderEntry(sw, "SOURCE", _Song.Source); + if (!String.IsNullOrEmpty(_Song._Comment)) + { + string comment = _Song._Comment.Replace("\r\n", "\n").Replace('\r', '\n'); + char[] splitChar = {'\n'}; + _WriteHeaderEntry(sw, "COMMENT", comment.Split(splitChar)); + } _WriteHeaderEntry(sw, "TITLE", _Song.Title); _WriteHeaderEntry(sw, "ARTIST", _Song.Artist); if (!_Song.Title.Equals(_Song.TitleSorting)) @@ -53,6 +59,7 @@ namespace VocaluxeLib.Songs _WriteHeaderEntry(sw, "EDITION", _Song.Editions); _WriteHeaderEntry(sw, "GENRE", _Song.Genres); _WriteHeaderEntry(sw, "LANGUAGE", _Song.Languages); + _WriteHeaderEntry(sw, "ALBUM", _Song.Album); _WriteHeaderEntry(sw, "YEAR", _Song.Year); _WriteHeaderEntry(sw, "MP3", _Song.MP3FileName); _WriteHeaderEntry(sw, "COVER", _Song.CoverFileName); @@ -78,6 +85,8 @@ namespace VocaluxeLib.Songs if (_Song.Notes.VoiceNames.IsSet(i)) _WriteHeaderEntry(sw, "P" + i, _Song.Notes.VoiceNames[i]); } + foreach (string addLine in _Song.UnknownTags) + sw.WriteLine(addLine); } } } diff --git a/VocaluxeLib/Songs/CVoice.cs b/VocaluxeLib/Songs/CVoice.cs index b2d962b3..61eac5a4 100644 --- a/VocaluxeLib/Songs/CVoice.cs +++ b/VocaluxeLib/Songs/CVoice.cs @@ -128,7 +128,7 @@ namespace VocaluxeLib.Songs { CSongLine prevLine = _Lines[insPos]; //We already have a line break here - if (prevLine.StartBeat == startBeat && prevLine.FirstNoteBeat == startBeat) + if (prevLine.StartBeat == startBeat && (prevLine.NoteCount==0 || prevLine.FirstNoteBeat == startBeat)) return false; //Maybe we have to split the previous line while (prevLine.NoteCount > 0 && prevLine.LastNote.StartBeat >= startBeat) From cc6d921f034ad3dfb16bb03e2de4aa3608de9107 Mon Sep 17 00:00:00 2001 From: Flamefire Date: Sat, 12 Oct 2013 17:32:24 +0200 Subject: [PATCH 16/23] Implement config var Implement SongWriter and Song.Save() --- Vocaluxe/Base/CConfig.cs | 5 ++ Vocaluxe/Base/CMain.cs | 5 ++ VocaluxeLib/IBasic.cs | 2 + VocaluxeLib/Songs/CBaseNote.cs | 3 ++ VocaluxeLib/Songs/CSong.cs | 13 +++-- VocaluxeLib/Songs/CSongLoader.cs | 10 +++- VocaluxeLib/Songs/CSongWriter.cs | 82 +++++++++++++++++++++++++++----- 7 files changed, 103 insertions(+), 17 deletions(-) diff --git a/Vocaluxe/Base/CConfig.cs b/Vocaluxe/Base/CConfig.cs index 00d8b55a..f4074ad1 100644 --- a/Vocaluxe/Base/CConfig.cs +++ b/Vocaluxe/Base/CConfig.cs @@ -37,6 +37,7 @@ namespace Vocaluxe.Base // Debug public static EDebugLevel DebugLevel = EDebugLevel.TR_CONFIG_OFF; + public static EOffOn SaveModifiedSongs = EOffOn.TR_CONFIG_OFF; // Graphics #if WIN @@ -169,6 +170,7 @@ namespace Vocaluxe.Base return; xmlReader.TryGetEnumValue("//root/Debug/DebugLevel", ref DebugLevel); + xmlReader.TryGetEnumValue("//root/Debug/SaveModifiedSongs", ref SaveModifiedSongs); #region Graphics xmlReader.TryGetEnumValue("//root/Graphics/Renderer", ref Renderer); @@ -328,6 +330,9 @@ namespace Vocaluxe.Base writer.WriteComment("DebugLevel: " + CHelper.ListStrings(Enum.GetNames(typeof(EDebugLevel)))); writer.WriteElementString("DebugLevel", Enum.GetName(typeof(EDebugLevel), DebugLevel)); + writer.WriteComment("SaveModifiedSongs: " + CHelper.ListStrings(Enum.GetNames(typeof(EOffOn)))); + writer.WriteElementString("SaveModifiedSongs", Enum.GetName(typeof(EOffOn), SaveModifiedSongs)); + writer.WriteEndElement(); #endregion Debug diff --git a/Vocaluxe/Base/CMain.cs b/Vocaluxe/Base/CMain.cs index 8a40c555..84523016 100644 --- a/Vocaluxe/Base/CMain.cs +++ b/Vocaluxe/Base/CMain.cs @@ -60,6 +60,11 @@ namespace Vocaluxe.Base class CBconfig : IConfig { + public EOffOn GetSaveModifiedSongs() + { + return CConfig.SaveModifiedSongs; + } + public void SetBackgroundMusicVolume(int newVolume) { CConfig.BackgroundMusicVolume = newVolume; diff --git a/VocaluxeLib/IBasic.cs b/VocaluxeLib/IBasic.cs index c6893634..6543482a 100644 --- a/VocaluxeLib/IBasic.cs +++ b/VocaluxeLib/IBasic.cs @@ -29,6 +29,8 @@ namespace VocaluxeLib { public interface IConfig { + EOffOn GetSaveModifiedSongs(); + void SetBackgroundMusicVolume(int newVolume); int GetBackgroundMusicVolume(); diff --git a/VocaluxeLib/Songs/CBaseNote.cs b/VocaluxeLib/Songs/CBaseNote.cs index 1c81038b..45043aa7 100644 --- a/VocaluxeLib/Songs/CBaseNote.cs +++ b/VocaluxeLib/Songs/CBaseNote.cs @@ -48,6 +48,9 @@ namespace VocaluxeLib.Songs #region Properties public int StartBeat { get; set; } + /// + /// Last beat of this note. First valid beat for a note after that is at EndBeat+1 + /// public int EndBeat { get { return StartBeat + _Duration - 1; } diff --git a/VocaluxeLib/Songs/CSong.cs b/VocaluxeLib/Songs/CSong.cs index ece23c2c..688d2d28 100644 --- a/VocaluxeLib/Songs/CSong.cs +++ b/VocaluxeLib/Songs/CSong.cs @@ -20,7 +20,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; -using System.Text.RegularExpressions; using VocaluxeLib.Draw; namespace VocaluxeLib.Songs @@ -73,7 +72,7 @@ namespace VocaluxeLib.Songs public SMedley Medley; - public bool CalculateMedley = true; + private bool _CalculateMedley = true; public float PreviewStart; public int ShortEnd; @@ -197,7 +196,7 @@ namespace VocaluxeLib.Songs FadeOutTime = song.Medley.FadeOutTime }; - CalculateMedley = song.CalculateMedley; + _CalculateMedley = song._CalculateMedley; PreviewStart = song.PreviewStart; ShortEnd = song.ShortEnd; @@ -261,6 +260,12 @@ namespace VocaluxeLib.Songs return loader.ReadNotes(); } + public bool Save(string filePath) + { + var writer = new CSongWriter(this); + return writer.SaveFile(filePath); + } + public string GetMP3() { return Path.Combine(Folder, MP3FileName); @@ -381,7 +386,7 @@ namespace VocaluxeLib.Songs if (Medley.Source == EMedleySource.Tag) return; - if (!CalculateMedley) + if (!_CalculateMedley) return; List series = _GetSeries(); diff --git a/VocaluxeLib/Songs/CSongLoader.cs b/VocaluxeLib/Songs/CSongLoader.cs index f93a2368..98cc839b 100644 --- a/VocaluxeLib/Songs/CSongLoader.cs +++ b/VocaluxeLib/Songs/CSongLoader.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; @@ -295,7 +294,7 @@ namespace VocaluxeLib.Songs break; case "CALCMEDLEY": if (value.ToUpper() == "OFF") - _Song.CalculateMedley = false; + _Song._CalculateMedley = false; break; case "RELATIVE": if (value.ToUpper() == "YES") @@ -518,6 +517,7 @@ namespace VocaluxeLib.Songs _LogError("Wrong or missing number after \"P\""); return false; } + currentBeat = 0; lastNote = null; break; case ':': @@ -625,6 +625,7 @@ namespace VocaluxeLib.Songs { _LogWarning("Missing line break length"); changesMade.NoLengthBreakCt++; + currentBeat = beat; } else currentBeat += length; @@ -694,6 +695,11 @@ namespace VocaluxeLib.Songs { string msg = "Automatic changes have been made to " + filePath + " Please check result!\r\n" + changesMade; CBase.Log.LogError("Warning:" + msg); + if (CBase.Config.GetSaveModifiedSongs() == EOffOn.TR_CONFIG_ON) + { + string name = Path.GetFileNameWithoutExtension(_Song.FileName); + _Song.Save(Path.Combine(_Song.Folder, name + ".fix.txt")); + } } return true; } diff --git a/VocaluxeLib/Songs/CSongWriter.cs b/VocaluxeLib/Songs/CSongWriter.cs index 59d1cbe1..a108c685 100644 --- a/VocaluxeLib/Songs/CSongWriter.cs +++ b/VocaluxeLib/Songs/CSongWriter.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; namespace VocaluxeLib.Songs { @@ -17,19 +15,19 @@ namespace VocaluxeLib.Songs _Song = song; } - private void _WriteHeaderEntry(StreamWriter sw, string id, T value) + private void _WriteHeaderEntry(TextWriter sw, string id, T value) { if (!value.Equals(default(T))) sw.WriteLine("#" + id.ToUpper() + ":" + value); } - private void _WriteHeaderEntry(StreamWriter sw, string id, bool value) + private void _WriteHeaderEntry(TextWriter sw, string id, bool value) { if (value) _WriteHeaderEntry(sw, id, "YES"); } - private void _WriteHeaderEntry(StreamWriter sw, string id, IList value) + private void _WriteHeaderEntrys(TextWriter sw, string id, IList value) { if (value != null && value.Count > 0) { @@ -38,7 +36,7 @@ namespace VocaluxeLib.Songs } } - private void _WriteHeader(StreamWriter sw) + private void _WriteHeader(TextWriter sw) { _WriteHeaderEntry(sw, "ENCODING", _Song.Encoding); _WriteHeaderEntry(sw, "CREATOR", _Song.Creator); @@ -48,7 +46,7 @@ namespace VocaluxeLib.Songs { string comment = _Song._Comment.Replace("\r\n", "\n").Replace('\r', '\n'); char[] splitChar = {'\n'}; - _WriteHeaderEntry(sw, "COMMENT", comment.Split(splitChar)); + _WriteHeaderEntrys(sw, "COMMENT", comment.Split(splitChar)); } _WriteHeaderEntry(sw, "TITLE", _Song.Title); _WriteHeaderEntry(sw, "ARTIST", _Song.Artist); @@ -56,9 +54,9 @@ namespace VocaluxeLib.Songs _WriteHeaderEntry(sw, "TITLE-ON-SORTING", _Song.TitleSorting); if (!_Song.Artist.Equals(_Song.ArtistSorting)) _WriteHeaderEntry(sw, "ARTIST-ON-SORTING", _Song.ArtistSorting); - _WriteHeaderEntry(sw, "EDITION", _Song.Editions); - _WriteHeaderEntry(sw, "GENRE", _Song.Genres); - _WriteHeaderEntry(sw, "LANGUAGE", _Song.Languages); + _WriteHeaderEntrys(sw, "EDITION", _Song.Editions); + _WriteHeaderEntrys(sw, "GENRE", _Song.Genres); + _WriteHeaderEntrys(sw, "LANGUAGE", _Song.Languages); _WriteHeaderEntry(sw, "ALBUM", _Song.Album); _WriteHeaderEntry(sw, "YEAR", _Song.Year); _WriteHeaderEntry(sw, "MP3", _Song.MP3FileName); @@ -73,7 +71,7 @@ namespace VocaluxeLib.Songs _WriteHeaderEntry(sw, "PREVIEWSTART", _Song.PreviewStart); _WriteHeaderEntry(sw, "START", _Song.Start); _WriteHeaderEntry(sw, "END", _Song.Finish); - if (!_Song.CalculateMedley) + if (!_Song._CalculateMedley) _WriteHeaderEntry(sw, "CALCMEDLEY", "OFF"); if (_Song.Medley.Source == EMedleySource.Tag) { @@ -88,6 +86,68 @@ namespace VocaluxeLib.Songs foreach (string addLine in _Song.UnknownTags) sw.WriteLine(addLine); } + + private void _WriteNotes(TextWriter sw) + { + for (int i = 0; i < _Song.Notes.VoiceCount; i++) + { + CVoice voice = _Song.Notes.GetVoice(i); + if (_Song.Notes.VoiceCount > 1) + sw.WriteLine("P" + Math.Pow(2, i)); + bool firstLine = true; + int currentBeat = 0; + foreach (CSongLine line in voice.Lines) + { + if (!firstLine) + { + string lineTxt = "- " + (line.StartBeat - currentBeat); + if (_Song.Relative) + { + lineTxt += " " + (line.FirstNoteBeat - currentBeat); + currentBeat = line.FirstNoteBeat; + } + sw.WriteLine(lineTxt); + } + else + firstLine = false; + foreach (CSongNote note in line.Notes) + { + string tag; + switch (note.Type) + { + case ENoteType.Normal: + tag = ":"; + break; + case ENoteType.Golden: + tag = "*"; + break; + case ENoteType.Freestyle: + tag = "F"; + break; + default: + throw new NotImplementedException("Note type " + note.Type); + } + sw.WriteLine(tag + " " + (note.StartBeat - currentBeat) + " " + note.Duration + " " + note.Tone + " " + note.Text); + } + } + } + } + + public bool SaveFile(string filePath) + { + try + { + TextWriter sw = new StreamWriter(filePath, false, _Song.Encoding); + _WriteHeader(sw); + _WriteNotes(sw); + } + catch (Exception e) + { + CBase.Log.LogError("Unhandled exception while writing " + filePath + ": " + e); + return false; + } + return true; + } } } } \ No newline at end of file From 87ba525811500cddbdea2398aa9d6b0964ae4915 Mon Sep 17 00:00:00 2001 From: Flamefire Date: Sat, 12 Oct 2013 18:08:46 +0200 Subject: [PATCH 17/23] Fix for SongWriter --- VocaluxeLib/CHelper.cs | 4 +- VocaluxeLib/Songs/CSong.cs | 21 +++--- VocaluxeLib/Songs/CSongLoader.cs | 25 +++---- VocaluxeLib/Songs/CSongWriter.cs | 108 +++++++++++++++++-------------- 4 files changed, 84 insertions(+), 74 deletions(-) diff --git a/VocaluxeLib/CHelper.cs b/VocaluxeLib/CHelper.cs index 97192ae1..7b441caa 100644 --- a/VocaluxeLib/CHelper.cs +++ b/VocaluxeLib/CHelper.cs @@ -199,7 +199,7 @@ namespace VocaluxeLib static class CEncoding { - public static Encoding GetEncoding(string encodingName) + public static Encoding GetEncoding(this string encodingName) { switch (encodingName) { @@ -218,7 +218,7 @@ namespace VocaluxeLib } } - public static string GetEncodingName(Encoding enc) + public static string GetEncodingName(this Encoding enc) { string result = "UTF8"; diff --git a/VocaluxeLib/Songs/CSong.cs b/VocaluxeLib/Songs/CSong.cs index 688d2d28..ac528889 100644 --- a/VocaluxeLib/Songs/CSong.cs +++ b/VocaluxeLib/Songs/CSong.cs @@ -78,6 +78,7 @@ namespace VocaluxeLib.Songs public int ShortEnd; public Encoding Encoding = Encoding.Default; + public bool ManualEncoding = false; public string Folder = String.Empty; public string FolderName = String.Empty; public string FileName = String.Empty; @@ -187,14 +188,7 @@ namespace VocaluxeLib.Songs _CoverTextureSmall = song._CoverTextureSmall; _CoverTextureBig = song._CoverTextureBig; - Medley = new SMedley - { - Source = song.Medley.Source, - StartBeat = song.Medley.StartBeat, - EndBeat = song.Medley.EndBeat, - FadeInTime = song.Medley.FadeInTime, - FadeOutTime = song.Medley.FadeOutTime - }; + Medley = song.Medley; _CalculateMedley = song._CalculateMedley; PreviewStart = song.PreviewStart; @@ -202,6 +196,7 @@ namespace VocaluxeLib.Songs ShortEnd = song.ShortEnd; Encoding = song.Encoding; + ManualEncoding = song.ManualEncoding; Folder = song.Folder; FolderName = song.FolderName; FileName = song.FileName; @@ -217,6 +212,13 @@ namespace VocaluxeLib.Songs Artist = song.Artist; Title = song.Title; + ArtistSorting = song.ArtistSorting; + TitleSorting = song.TitleSorting; + + Creator = song.Creator; + Version = song.Version; + Source = song.Source; + UnknownTags = new List(song.UnknownTags); Start = song.Start; Finish = song.Finish; @@ -233,9 +235,8 @@ namespace VocaluxeLib.Songs _Selected = song._Selected; Editions = new List(song.Editions); - Genres = new List(song.Genres); - + Album = song.Album; Year = song.Year; Languages = new List(song.Languages); diff --git a/VocaluxeLib/Songs/CSongLoader.cs b/VocaluxeLib/Songs/CSongLoader.cs index 98cc839b..0720be1a 100644 --- a/VocaluxeLib/Songs/CSongLoader.cs +++ b/VocaluxeLib/Songs/CSongLoader.cs @@ -77,7 +77,7 @@ namespace VocaluxeLib.Songs _LogMsg(msg, false, withLineNr); } - public bool ReadHeader(Encoding encoding = null) + public bool ReadHeader(bool ignoreSetEncoding = false) { string filePath = Path.Combine(_Song.Folder, _Song.FileName); @@ -88,13 +88,14 @@ namespace VocaluxeLib.Songs _Song.Genres.Clear(); _Song.UnknownTags.Clear(); _Song._Comment = ""; + _Song.ManualEncoding = false; var headerFlags = new EHeaderFlags(); StreamReader sr = null; _LineNr = 0; try { - sr = new StreamReader(filePath, Encoding.Default, true); + sr = new StreamReader(filePath, _Song.Encoding, true); while (!sr.EndOfStream) { string line = sr.ReadLine(); @@ -129,17 +130,19 @@ namespace VocaluxeLib.Songs switch (identifier) { case "ENCODING": - if (encoding != null) - { - _LogWarning("Duplicate encoding ignored"); - continue; - } - Encoding newEncoding = CEncoding.GetEncoding(value); + Encoding newEncoding = value.GetEncoding(); + _Song.ManualEncoding = true; if (!newEncoding.Equals(sr.CurrentEncoding)) { + if (ignoreSetEncoding) + { + _LogWarning("Duplicate encoding ignored"); + continue; + } sr.Dispose(); sr = null; - return ReadHeader(_Song.Encoding); + _Song.Encoding = newEncoding; + return ReadHeader(true); } break; case "TITLE": @@ -300,7 +303,7 @@ namespace VocaluxeLib.Songs if (value.ToUpper() == "YES") _Song.Relative = true; break; - case "RESOULTION": + case "RESOLUTION": _LogWarning("#RESOLUTION tag is outdated and ignored. Test the song to see if note timing is still correct!"); break; default: @@ -383,8 +386,6 @@ namespace VocaluxeLib.Songs _Song.Medley.FadeOutTime = CBase.Settings.GetDefaultMedleyFadeOutTime(); } #endregion check medley tags - - _Song.Encoding = sr.CurrentEncoding; } catch (Exception e) { diff --git a/VocaluxeLib/Songs/CSongWriter.cs b/VocaluxeLib/Songs/CSongWriter.cs index a108c685..73c2ecc5 100644 --- a/VocaluxeLib/Songs/CSongWriter.cs +++ b/VocaluxeLib/Songs/CSongWriter.cs @@ -9,91 +9,99 @@ namespace VocaluxeLib.Songs private class CSongWriter { private readonly CSong _Song; + private TextWriter tw; public CSongWriter(CSong song) { _Song = song; } - private void _WriteHeaderEntry(TextWriter sw, string id, T value) + private void _WriteHeaderEntry(string id, string value) { - if (!value.Equals(default(T))) - sw.WriteLine("#" + id.ToUpper() + ":" + value); + if (!String.IsNullOrEmpty(value)) + tw.WriteLine("#" + id.ToUpper() + ":" + value); } - private void _WriteHeaderEntry(TextWriter sw, string id, bool value) + private void _WriteHeaderEntry(string id, bool value) { if (value) - _WriteHeaderEntry(sw, id, "YES"); + _WriteHeaderEntry(id, "YES"); } - private void _WriteHeaderEntrys(TextWriter sw, string id, IList value) + private void _WriteHeaderEntry(string id, float value, float def = 0f) { - if (value != null && value.Count > 0) - { - foreach (T val in value) - _WriteHeaderEntry(sw, id, val); - } + if (Math.Abs(value - def) > 0.0001) + _WriteHeaderEntry(id, value.ToString()); } - private void _WriteHeader(TextWriter sw) + private void _WriteHeaderEntrys(string id, ICollection value) { - _WriteHeaderEntry(sw, "ENCODING", _Song.Encoding); - _WriteHeaderEntry(sw, "CREATOR", _Song.Creator); - _WriteHeaderEntry(sw, "VERSION", _Song.Version); - _WriteHeaderEntry(sw, "SOURCE", _Song.Source); + if (value == null || value.Count <= 0) + return; + foreach (String val in value) + _WriteHeaderEntry(id, val); + } + + private void _WriteHeader() + { + if (_Song.ManualEncoding) + _WriteHeaderEntry("ENCODING", _Song.Encoding.GetEncodingName()); + _WriteHeaderEntry("CREATOR", _Song.Creator); + _WriteHeaderEntry("VERSION", _Song.Version); + _WriteHeaderEntry("SOURCE", _Song.Source); if (!String.IsNullOrEmpty(_Song._Comment)) { string comment = _Song._Comment.Replace("\r\n", "\n").Replace('\r', '\n'); char[] splitChar = {'\n'}; - _WriteHeaderEntrys(sw, "COMMENT", comment.Split(splitChar)); + _WriteHeaderEntrys("COMMENT", comment.Split(splitChar)); } - _WriteHeaderEntry(sw, "TITLE", _Song.Title); - _WriteHeaderEntry(sw, "ARTIST", _Song.Artist); + _WriteHeaderEntry("TITLE", _Song.Title); + _WriteHeaderEntry("ARTIST", _Song.Artist); if (!_Song.Title.Equals(_Song.TitleSorting)) - _WriteHeaderEntry(sw, "TITLE-ON-SORTING", _Song.TitleSorting); + _WriteHeaderEntry("TITLE-ON-SORTING", _Song.TitleSorting); if (!_Song.Artist.Equals(_Song.ArtistSorting)) - _WriteHeaderEntry(sw, "ARTIST-ON-SORTING", _Song.ArtistSorting); - _WriteHeaderEntrys(sw, "EDITION", _Song.Editions); - _WriteHeaderEntrys(sw, "GENRE", _Song.Genres); - _WriteHeaderEntrys(sw, "LANGUAGE", _Song.Languages); - _WriteHeaderEntry(sw, "ALBUM", _Song.Album); - _WriteHeaderEntry(sw, "YEAR", _Song.Year); - _WriteHeaderEntry(sw, "MP3", _Song.MP3FileName); - _WriteHeaderEntry(sw, "COVER", _Song.CoverFileName); - _WriteHeaderEntry(sw, "BACKGROUND", _Song.BackgroundFileName); - _WriteHeaderEntry(sw, "VIDEO", _Song.VideoFileName); - _WriteHeaderEntry(sw, "VIDEOGAP", _Song.VideoGap); - _WriteHeaderEntry(sw, "VIDEOASPECT", _Song.VideoAspect); - _WriteHeaderEntry(sw, "RELATIVE", _Song.Relative); - _WriteHeaderEntry(sw, "BPM", _Song.BPM); - _WriteHeaderEntry(sw, "GAP", _Song.Gap); - _WriteHeaderEntry(sw, "PREVIEWSTART", _Song.PreviewStart); - _WriteHeaderEntry(sw, "START", _Song.Start); - _WriteHeaderEntry(sw, "END", _Song.Finish); + _WriteHeaderEntry("ARTIST-ON-SORTING", _Song.ArtistSorting); + _WriteHeaderEntrys("EDITION", _Song.Editions); + _WriteHeaderEntrys("GENRE", _Song.Genres); + _WriteHeaderEntrys("LANGUAGE", _Song.Languages); + _WriteHeaderEntry("ALBUM", _Song.Album); + _WriteHeaderEntry("YEAR", _Song.Year); + _WriteHeaderEntry("MP3", _Song.MP3FileName); + _WriteHeaderEntry("COVER", _Song.CoverFileName); + _WriteHeaderEntry("BACKGROUND", _Song.BackgroundFileName); + _WriteHeaderEntry("VIDEO", _Song.VideoFileName); + _WriteHeaderEntry("VIDEOGAP", _Song.VideoGap); + if (_Song.VideoAspect != EAspect.Crop) + _WriteHeaderEntry("VIDEOASPECT", _Song.VideoAspect.ToString()); + _WriteHeaderEntry("RELATIVE", _Song.Relative); + _WriteHeaderEntry("BPM", _Song.BPM / _BPMFactor); + _WriteHeaderEntry("GAP", _Song.Gap * 1000f); + _WriteHeaderEntry("PREVIEWSTART", _Song.PreviewStart); + _WriteHeaderEntry("START", _Song.Start); + _WriteHeaderEntry("END", _Song.Finish * 1000f); if (!_Song._CalculateMedley) - _WriteHeaderEntry(sw, "CALCMEDLEY", "OFF"); + _WriteHeaderEntry("CALCMEDLEY", "OFF"); if (_Song.Medley.Source == EMedleySource.Tag) { - _WriteHeaderEntry(sw, "MEDLEYSTARTBEAT", _Song.Medley.StartBeat); - _WriteHeaderEntry(sw, "MEDLEYENDBEAT", _Song.Medley.EndBeat); + _WriteHeaderEntry("MEDLEYSTARTBEAT", _Song.Medley.StartBeat); + _WriteHeaderEntry("MEDLEYENDBEAT", _Song.Medley.EndBeat); } for (int i = 0; i < _Song.Notes.VoiceCount; i++) { if (_Song.Notes.VoiceNames.IsSet(i)) - _WriteHeaderEntry(sw, "P" + i, _Song.Notes.VoiceNames[i]); + _WriteHeaderEntry("P" + i, _Song.Notes.VoiceNames[i]); } foreach (string addLine in _Song.UnknownTags) - sw.WriteLine(addLine); + tw.WriteLine(addLine); } - private void _WriteNotes(TextWriter sw) + private void _WriteNotes() { for (int i = 0; i < _Song.Notes.VoiceCount; i++) { CVoice voice = _Song.Notes.GetVoice(i); if (_Song.Notes.VoiceCount > 1) - sw.WriteLine("P" + Math.Pow(2, i)); + tw.WriteLine("P" + Math.Pow(2, i)); bool firstLine = true; int currentBeat = 0; foreach (CSongLine line in voice.Lines) @@ -106,7 +114,7 @@ namespace VocaluxeLib.Songs lineTxt += " " + (line.FirstNoteBeat - currentBeat); currentBeat = line.FirstNoteBeat; } - sw.WriteLine(lineTxt); + tw.WriteLine(lineTxt); } else firstLine = false; @@ -127,7 +135,7 @@ namespace VocaluxeLib.Songs default: throw new NotImplementedException("Note type " + note.Type); } - sw.WriteLine(tag + " " + (note.StartBeat - currentBeat) + " " + note.Duration + " " + note.Tone + " " + note.Text); + tw.WriteLine(tag + " " + (note.StartBeat - currentBeat) + " " + note.Duration + " " + note.Tone + " " + note.Text); } } } @@ -137,9 +145,9 @@ namespace VocaluxeLib.Songs { try { - TextWriter sw = new StreamWriter(filePath, false, _Song.Encoding); - _WriteHeader(sw); - _WriteNotes(sw); + tw = new StreamWriter(filePath, false, _Song.Encoding); + _WriteHeader(); + _WriteNotes(); } catch (Exception e) { From b12e4156faa2b15c18d389790afc91f98c934ad1 Mon Sep 17 00:00:00 2001 From: Flamefire Date: Sat, 12 Oct 2013 21:42:03 +0200 Subject: [PATCH 18/23] Allow #ENDSHORT tags Detect where Preview and ShortEnd came from Introduce a function to check whether a given GameMode is available for a song --- .../CPartyScreenTicTacToeMain.cs | 4 +- Vocaluxe/Base/CGame.cs | 2 +- Vocaluxe/Base/CLog.cs | 1 - Vocaluxe/GameModes/CGameModes.cs | 4 +- Vocaluxe/Screens/CScreenSong.cs | 33 ++++--- VocaluxeLib/Extensions.cs | 24 ++++++ VocaluxeLib/IBasic.cs | 1 + VocaluxeLib/Menu/CPlaylist.cs | 4 +- .../Menu/SongMenu/CSongMenuFramework.cs | 30 ++++--- .../Menu/SongMenu/CSongMenuTileBoard.cs | 4 +- VocaluxeLib/Songs/CSong.cs | 85 ++++++++++++------- VocaluxeLib/Songs/CSongLine.cs | 4 +- VocaluxeLib/Songs/CSongLoader.cs | 52 +++++++----- VocaluxeLib/Songs/CSongWriter.cs | 16 +++- 14 files changed, 167 insertions(+), 97 deletions(-) diff --git a/PartyModes/PartyModeTicTacToe/CPartyScreenTicTacToeMain.cs b/PartyModes/PartyModeTicTacToe/CPartyScreenTicTacToeMain.cs index b50c51ad..6cfad473 100644 --- a/PartyModes/PartyModeTicTacToe/CPartyScreenTicTacToeMain.cs +++ b/PartyModes/PartyModeTicTacToe/CPartyScreenTicTacToeMain.cs @@ -504,7 +504,7 @@ namespace VocaluxeLib.PartyModes.TicTacToe CBase.BackgroundMusic.SetStatus(true); _PreviewStream = CBase.Sound.Load(song.GetMP3(), false); - CBase.Sound.SetPosition(_PreviewStream, song.PreviewStart); + CBase.Sound.SetPosition(_PreviewStream, song.Preview.StartTime); CBase.Sound.SetStreamVolume(_PreviewStream, 0f); CBase.Sound.Play(_PreviewStream); CBase.Sound.Fade(_PreviewStream, CBase.Config.GetBackgroundMusicVolume(), 1f); @@ -528,7 +528,7 @@ namespace VocaluxeLib.PartyModes.TicTacToe CBase.BackgroundMusic.SetStatus(true); _PreviewStream = CBase.Sound.Load(song.GetMP3(), false); - CBase.Sound.SetPosition(_PreviewStream, song.PreviewStart); + CBase.Sound.SetPosition(_PreviewStream, song.Preview.StartTime); CBase.Sound.SetStreamVolume(_PreviewStream, 0f); CBase.Sound.Play(_PreviewStream); CBase.Sound.Fade(_PreviewStream, CBase.Config.GetBackgroundMusicVolume(), 1f); diff --git a/Vocaluxe/Base/CGame.cs b/Vocaluxe/Base/CGame.cs index 7f47fbf5..6eb98838 100644 --- a/Vocaluxe/Base/CGame.cs +++ b/Vocaluxe/Base/CGame.cs @@ -234,7 +234,7 @@ namespace Vocaluxe.Base for (int beat = _LastEvalBeat + 1; beat <= RecordedBeat; beat++) { if ((_SongQueue.GetCurrentGameMode() == EGameMode.TR_GAMEMODE_MEDLEY && song.Medley.EndBeat == beat) || - (_SongQueue.GetCurrentGameMode() == EGameMode.TR_GAMEMODE_SHORTSONG && song.ShortEnd == beat)) + (_SongQueue.GetCurrentGameMode() == EGameMode.TR_GAMEMODE_SHORTSONG && song.ShortEnd.EndBeat == beat)) Players[p].SongFinished = true; CSongLine[] lines = song.Notes.GetVoice(Players[p].VoiceNr).Lines; diff --git a/Vocaluxe/Base/CLog.cs b/Vocaluxe/Base/CLog.cs index 350950bb..0dd53fe2 100644 --- a/Vocaluxe/Base/CLog.cs +++ b/Vocaluxe/Base/CLog.cs @@ -118,7 +118,6 @@ namespace Vocaluxe.Base if (e != null) errorText += ": " + e; _ErrorLog.Add(_NumErrors + ") " + errorText + "\r\n"); - _ErrorLog.Add(String.Empty); if (exit) Environment.Exit(Environment.ExitCode); } diff --git a/Vocaluxe/GameModes/CGameModes.cs b/Vocaluxe/GameModes/CGameModes.cs index 460943ea..c49ff93e 100644 --- a/Vocaluxe/GameModes/CGameModes.cs +++ b/Vocaluxe/GameModes/CGameModes.cs @@ -68,9 +68,9 @@ namespace Vocaluxe.GameModes { protected override CSong _PrepareSong(CSong song) { - var newSong = new CSong(song) {Finish = CGame.GetTimeFromBeats(song.ShortEnd, song.BPM) + CSettings.DefaultMedleyFadeOutTime + song.Gap}; + var newSong = new CSong(song) {Finish = CGame.GetTimeFromBeats(song.ShortEnd.EndBeat, song.BPM) + CSettings.DefaultMedleyFadeOutTime + song.Gap}; // set lines to short mode - newSong.Notes.SetMedley(song.Notes.GetVoice(0).Lines[0].FirstNoteBeat, song.ShortEnd); + newSong.Notes.SetMedley(0, song.ShortEnd.EndBeat); return newSong; } diff --git a/Vocaluxe/Screens/CScreenSong.cs b/Vocaluxe/Screens/CScreenSong.cs index 17586fe5..70834067 100644 --- a/Vocaluxe/Screens/CScreenSong.cs +++ b/Vocaluxe/Screens/CScreenSong.cs @@ -844,7 +844,7 @@ namespace Vocaluxe.Screens { if ((CSongs.Category >= 0) && (songNr >= 0)) { - if (CSongs.VisibleSongs[songNr].Medley.Source == EMedleySource.None) + if (CSongs.VisibleSongs[songNr].IsGameModeAvailable(EGameMode.TR_GAMEMODE_MEDLEY)) return; CGame.Reset(); @@ -1165,6 +1165,15 @@ namespace Vocaluxe.Screens } } + private void _CheckAndAddGameMode(EGameMode gameMode) + { + if (CSongs.VisibleSongs[_SongMenus[_SongMenu].GetSelectedSong()].IsGameModeAvailable(gameMode)) + { + _SelectSlides[_SelectSlideOptionsMode].AddValue(Enum.GetName(typeof(EGameMode), gameMode)); + _AvailableGameModes.Add(gameMode); + } + } + private void _ShowSongOptionsSong() { var lastMode = EGameMode.TR_GAMEMODE_NORMAL; @@ -1172,23 +1181,11 @@ namespace Vocaluxe.Screens lastMode = _AvailableGameModes[_SelectSlides[_SelectSlideOptionsMode].Selection]; _AvailableGameModes.Clear(); _SelectSlides[_SelectSlideOptionsMode].Clear(); - if (CSongs.VisibleSongs[_SongMenus[_SongMenu].GetSelectedSong()].IsDuet) - { - _SelectSlides[_SelectSlideOptionsMode].AddValue(Enum.GetName(typeof(EGameMode), EGameMode.TR_GAMEMODE_DUET)); - _AvailableGameModes.Add(EGameMode.TR_GAMEMODE_DUET); - } - else - { - _SelectSlides[_SelectSlideOptionsMode].AddValue(Enum.GetName(typeof(EGameMode), EGameMode.TR_GAMEMODE_NORMAL)); - _AvailableGameModes.Add(EGameMode.TR_GAMEMODE_NORMAL); - _SelectSlides[_SelectSlideOptionsMode].AddValue(Enum.GetName(typeof(EGameMode), EGameMode.TR_GAMEMODE_SHORTSONG)); - _AvailableGameModes.Add(EGameMode.TR_GAMEMODE_SHORTSONG); - } - if (CSongs.VisibleSongs[_SongMenus[_SongMenu].GetSelectedSong()].Medley.Source != EMedleySource.None) - { - _SelectSlides[_SelectSlideOptionsMode].AddValue(Enum.GetName(typeof(EGameMode), EGameMode.TR_GAMEMODE_MEDLEY)); - _AvailableGameModes.Add(EGameMode.TR_GAMEMODE_MEDLEY); - } + _CheckAndAddGameMode(EGameMode.TR_GAMEMODE_NORMAL); + _CheckAndAddGameMode(EGameMode.TR_GAMEMODE_DUET); + _CheckAndAddGameMode(EGameMode.TR_GAMEMODE_SHORTSONG); + _CheckAndAddGameMode(EGameMode.TR_GAMEMODE_MEDLEY); + //Set SelectSlide-Selection to last selected game-mode if possible for (int i = 0; i < _AvailableGameModes.Count; i++) { diff --git a/VocaluxeLib/Extensions.cs b/VocaluxeLib/Extensions.cs index 8f2be91e..a035f869 100644 --- a/VocaluxeLib/Extensions.cs +++ b/VocaluxeLib/Extensions.cs @@ -73,5 +73,29 @@ namespace VocaluxeLib { return value.IndexOf(other, StringComparison.CurrentCultureIgnoreCase) >= 0; } + + public static string TrimMultipleWs(this string value) + { + if (string.IsNullOrEmpty(value)) + return ""; + int start = 0; + int len = value.Length; + while (value[start] == ' ') + { + start++; + if (start >= len) + return ""; + } + if (start > 0) + start--; + int end = --len; + while (value[end] == ' ') + end--; + if (end < len) + end++; + if (end - start < len) + value = value.Substring(start, end - start + 1); + return value; + } } } \ No newline at end of file diff --git a/VocaluxeLib/IBasic.cs b/VocaluxeLib/IBasic.cs index 6543482a..efdb45df 100644 --- a/VocaluxeLib/IBasic.cs +++ b/VocaluxeLib/IBasic.cs @@ -181,6 +181,7 @@ namespace VocaluxeLib double GetRandomDouble(); float GetTimeFromBeats(float beat, float bpm); + float GetBeatFromTime(float time, float bpm, float gap); void AddSong(int songID, EGameMode gameMode); void Reset(); diff --git a/VocaluxeLib/Menu/CPlaylist.cs b/VocaluxeLib/Menu/CPlaylist.cs index 82c5f93a..1eecc9f2 100644 --- a/VocaluxeLib/Menu/CPlaylist.cs +++ b/VocaluxeLib/Menu/CPlaylist.cs @@ -84,7 +84,7 @@ namespace VocaluxeLib.Menu { private class CPlaylistElementContent { - public EGameMode[] Modes; + public IList Modes; public int SongID; public EGameMode Mode; } @@ -1220,7 +1220,7 @@ namespace VocaluxeLib.Menu string t1 = CBase.Language.Translate(_Theme.Text1.Text).Replace("%a", song.Artist).Replace("%t", song.Title); _PlaylistElements[i].Text1.Text = /*(Offset + i + 1) + ") " + */ t1; //TODO: Add text field for the number _PlaylistElements[i].SelectSlide.Clear(); - for (int g = 0; g < pec.Modes.Length; g++) + for (int g = 0; g < pec.Modes.Count; g++) { _PlaylistElements[i].SelectSlide.AddValue(Enum.GetName(typeof(EGameMode), pec.Modes[g])); if (pec.Modes[g] == pec.Mode) diff --git a/VocaluxeLib/Menu/SongMenu/CSongMenuFramework.cs b/VocaluxeLib/Menu/SongMenu/CSongMenuFramework.cs index c1e6ea5b..7a717f7d 100644 --- a/VocaluxeLib/Menu/SongMenu/CSongMenuFramework.cs +++ b/VocaluxeLib/Menu/SongMenu/CSongMenuFramework.cs @@ -22,6 +22,7 @@ using System.IO; using System.Xml; using VocaluxeLib.Draw; using VocaluxeLib.PartyModes; +using VocaluxeLib.Songs; namespace VocaluxeLib.Menu.SongMenu { @@ -101,7 +102,7 @@ namespace VocaluxeLib.Menu.SongMenu private readonly Stopwatch _Timer = new Stopwatch(); private readonly Stopwatch _VideoFadeTimer = new Stopwatch(); private readonly List _Streams = new List(); - private int _Actsong = -1; + private int _ActSongId = -1; private int _Actsongstream = -1; protected CTexture _Vidtex; @@ -186,7 +187,7 @@ namespace VocaluxeLib.Menu.SongMenu protected int _ActSong { - get { return _Actsong; } + get { return _ActSongId; } } public SRectF Rect @@ -206,7 +207,7 @@ namespace VocaluxeLib.Menu.SongMenu public virtual int GetActualSelection() { - return _Actsong; + return _ActSongId; } private bool _Selected; @@ -434,7 +435,7 @@ namespace VocaluxeLib.Menu.SongMenu if (!_Initialized) return; - if (_Actsong != _PreviewSelected) + if (_ActSongId != _PreviewSelected) _SelectSong(_PreviewSelected); if (_Streams.Count <= 0 || _Video == -1) @@ -607,7 +608,7 @@ namespace VocaluxeLib.Menu.SongMenu protected void _SelectSong(int nr) { - if (CBase.Songs.IsInCategory() && (CBase.Songs.GetNumSongsVisible() > 0) && (nr >= 0) && ((_Actsong != nr) || (_Streams.Count == 0))) + if (CBase.Songs.IsInCategory() && (CBase.Songs.GetNumSongsVisible() > 0) && (nr >= 0) && ((_ActSongId != nr) || (_Streams.Count == 0))) { _Streams.ForEach(soundStream => CBase.Sound.FadeAndStop(soundStream, 0f, 1f)); _Streams.Clear(); @@ -617,18 +618,19 @@ namespace VocaluxeLib.Menu.SongMenu CBase.Drawing.RemoveTexture(ref _Vidtex); - _Actsong = nr; - if (_Actsong >= CBase.Songs.GetNumSongsVisible()) - _Actsong = 0; + _ActSongId = nr; + if (_ActSongId >= CBase.Songs.GetNumSongsVisible()) + _ActSongId = 0; - int stream = CBase.Sound.Load(Path.Combine(CBase.Songs.GetVisibleSong(_Actsong).Folder, CBase.Songs.GetVisibleSong(_Actsong).MP3FileName), true); + CSong actSong = CBase.Songs.GetVisibleSong(_ActSongId); + int stream = CBase.Sound.Load(Path.Combine(actSong.Folder, actSong.MP3FileName), true); CBase.Sound.SetStreamVolumeMax(stream, _MaxVolume); CBase.Sound.SetStreamVolume(stream, 0f); - float startposition = CBase.Songs.GetVisibleSong(_Actsong).PreviewStart; + float startposition = actSong.Preview.StartTime; - if (Math.Abs(startposition) < 0.001) + if (actSong.Preview.Source == EDataSource.None) startposition = CBase.Sound.GetLength(stream) / 4f; if (startposition >= 0.5f) @@ -640,12 +642,12 @@ namespace VocaluxeLib.Menu.SongMenu _Streams.Add(stream); _Actsongstream = stream; - if (CBase.Songs.GetVisibleSong(_Actsong).VideoFileName != "" && CBase.Config.GetVideoPreview() == EOffOn.TR_CONFIG_ON) + if (actSong.VideoFileName != "" && CBase.Config.GetVideoPreview() == EOffOn.TR_CONFIG_ON) { - _Video = CBase.Video.Load(Path.Combine(CBase.Songs.GetVisibleSong(_Actsong).Folder, CBase.Songs.GetVisibleSong(_Actsong).VideoFileName)); + _Video = CBase.Video.Load(Path.Combine(actSong.Folder, actSong.VideoFileName)); if (_Video == -1) return; - CBase.Video.Skip(_Video, startposition, CBase.Songs.GetVisibleSong(_Actsong).VideoGap); + CBase.Video.Skip(_Video, startposition, actSong.VideoGap); _VideoFadeTimer.Stop(); _VideoFadeTimer.Reset(); _VideoFadeTimer.Start(); diff --git a/VocaluxeLib/Menu/SongMenu/CSongMenuTileBoard.cs b/VocaluxeLib/Menu/SongMenu/CSongMenuTileBoard.cs index a0ba7fe1..dc18e486 100644 --- a/VocaluxeLib/Menu/SongMenu/CSongMenuTileBoard.cs +++ b/VocaluxeLib/Menu/SongMenu/CSongMenuTileBoard.cs @@ -413,8 +413,8 @@ namespace VocaluxeLib.Menu.SongMenu _Title.Text = song.Title; _DuetIcon.Visible = song.IsDuet; _VideoIcon.Visible = song.VideoFileName != ""; - _MedleyCalcIcon.Visible = song.Medley.Source == EMedleySource.Calculated; - _MedleyTagIcon.Visible = song.Medley.Source == EMedleySource.Tag; + _MedleyCalcIcon.Visible = song.Medley.Source == EDataSource.Calculated; + _MedleyTagIcon.Visible = song.Medley.Source == EDataSource.Tag; float time = CBase.Sound.GetLength(_SongStream); if (Math.Abs(song.Finish) > 0.001) diff --git a/VocaluxeLib/Songs/CSong.cs b/VocaluxeLib/Songs/CSong.cs index ac528889..45f5af1f 100644 --- a/VocaluxeLib/Songs/CSong.cs +++ b/VocaluxeLib/Songs/CSong.cs @@ -44,12 +44,11 @@ namespace VocaluxeLib.Songs Artist = 2, MP3 = 4, BPM = 8, - PreviewStart = 16, - MedleyStartBeat = 32, - MedleyEndBeat = 64 + MedleyStartBeat = 16, + MedleyEndBeat = 32 } - public enum EMedleySource + public enum EDataSource { None = 0, Calculated, @@ -58,13 +57,25 @@ namespace VocaluxeLib.Songs public struct SMedley { - public EMedleySource Source; + public EDataSource Source; public int StartBeat; public int EndBeat; public float FadeInTime; public float FadeOutTime; } + public struct SShortEnd + { + public EDataSource Source; + public int EndBeat; + } + + public struct SPreview + { + public EDataSource Source; + public float StartTime; + } + public partial class CSong { private CTexture _CoverTextureSmall; @@ -73,12 +84,12 @@ namespace VocaluxeLib.Songs public SMedley Medley; private bool _CalculateMedley = true; - public float PreviewStart; + public SPreview Preview; - public int ShortEnd; + public SShortEnd ShortEnd; public Encoding Encoding = Encoding.Default; - public bool ManualEncoding = false; + public bool ManualEncoding; public string Folder = String.Empty; public string FolderName = String.Empty; public string FileName = String.Empty; @@ -167,19 +178,30 @@ namespace VocaluxeLib.Songs // Notes public readonly CNotes Notes = new CNotes(); - public EGameMode[] AvailableGameModes + public IList AvailableGameModes { get { var gms = new List {IsDuet ? EGameMode.TR_GAMEMODE_DUET : EGameMode.TR_GAMEMODE_NORMAL}; - if (Medley.Source != EMedleySource.None) + if (Medley.Source != EDataSource.None) gms.Add(EGameMode.TR_GAMEMODE_MEDLEY); - gms.Add(EGameMode.TR_GAMEMODE_SHORTSONG); + if (ShortEnd.Source != EDataSource.None) + gms.Add(EGameMode.TR_GAMEMODE_SHORTSONG); - return gms.ToArray(); + return gms; } } + /// + /// Returns true if the requested game mode is available + /// + /// + /// true if the requested game mode is available + public bool IsGameModeAvailable(EGameMode gameMode) + { + return AvailableGameModes.Any(gm => gm == gameMode); + } + //No point creating a song without a text file --> Use factory method LoadSong private CSong() {} @@ -191,7 +213,7 @@ namespace VocaluxeLib.Songs Medley = song.Medley; _CalculateMedley = song._CalculateMedley; - PreviewStart = song.PreviewStart; + Preview = song.Preview; ShortEnd = song.ShortEnd; @@ -380,13 +402,10 @@ namespace VocaluxeLib.Songs { if (IsDuet) { - Medley.Source = EMedleySource.None; + Medley.Source = EDataSource.None; return; } - if (Medley.Source == EMedleySource.Tag) - return; - if (!_CalculateMedley) return; @@ -410,41 +429,44 @@ namespace VocaluxeLib.Songs Medley.StartBeat = voice.Lines[series[longest].Start].FirstNoteBeat; Medley.EndBeat = voice.Lines[series[longest].End].LastNoteBeat; - bool foundEnd = CBase.Game.GetTimeFromBeats(Medley.StartBeat, BPM) + CBase.Settings.GetMedleyMinDuration() > - CBase.Game.GetTimeFromBeats(Medley.EndBeat, BPM); + bool foundEnd = CBase.Game.GetTimeFromBeats(Medley.EndBeat, BPM) - CBase.Game.GetTimeFromBeats(Medley.StartBeat, BPM) < CBase.Settings.GetMedleyMinDuration(); - // set end if duration > MedleyMinDuration + // set end if duration < MedleyMinDuration if (!foundEnd) { - for (int i = series[longest].Start + 1; i < voice.NumLines - 1; i++) + for (int i = series[longest].End + 1; i < voice.NumLines - 1; i++) { - if (CBase.Game.GetTimeFromBeats(Medley.StartBeat, BPM) + CBase.Settings.GetMedleyMinDuration() > - CBase.Game.GetTimeFromBeats(voice.Lines[i].LastNoteBeat, BPM)) + if (CBase.Game.GetTimeFromBeats(voice.Lines[i].LastNoteBeat, BPM) - CBase.Game.GetTimeFromBeats(Medley.StartBeat, BPM) < + CBase.Settings.GetMedleyMinDuration()) { foundEnd = true; Medley.EndBeat = voice.Lines[i].LastNoteBeat; + break; } } } if (foundEnd) { - Medley.Source = EMedleySource.Calculated; + Medley.Source = EDataSource.Calculated; Medley.FadeInTime = CBase.Settings.GetDefaultMedleyFadeInTime(); Medley.FadeOutTime = CBase.Settings.GetDefaultMedleyFadeOutTime(); } } - if (Math.Abs(PreviewStart) < 0.001) + if (Preview.Source == EDataSource.None) { - if (Medley.Source == EMedleySource.Calculated) - PreviewStart = CBase.Game.GetTimeFromBeats(Medley.StartBeat, BPM); + if (Medley.Source != EDataSource.None) + Preview.StartTime = CBase.Game.GetTimeFromBeats(Medley.StartBeat, BPM); } } private void _FindShortEnd() { + if (ShortEnd.Source != EDataSource.None) + return; + List series = _GetSeries(); if (series == null) return; @@ -461,10 +483,10 @@ namespace VocaluxeLib.Songs { if (stop < (voice.Lines[series[i].Start].FirstNoteBeat + ((voice.Lines[series[i].End].LastNoteBeat - voice.Lines[series[i].Start].FirstNoteBeat) / 2))) { - ShortEnd = voice.Lines[series[i].Start - 1].LastNote.EndBeat; + ShortEnd.EndBeat = voice.Lines[series[i].Start - 1].LastNote.EndBeat; return; } - ShortEnd = voice.Lines[series[i].End].LastNote.EndBeat; + ShortEnd.EndBeat = voice.Lines[series[i].End].LastNote.EndBeat; return; } } @@ -474,12 +496,13 @@ namespace VocaluxeLib.Songs { if (line.FirstNoteBeat < stop && line.LastNoteBeat > stop) { - ShortEnd = line.LastNoteBeat; + ShortEnd.EndBeat = line.LastNoteBeat; return; } } - ShortEnd = stop; + ShortEnd.EndBeat = stop; + ShortEnd.Source = EDataSource.Calculated; } } } \ No newline at end of file diff --git a/VocaluxeLib/Songs/CSongLine.cs b/VocaluxeLib/Songs/CSongLine.cs index dd6787f6..4dffe501 100644 --- a/VocaluxeLib/Songs/CSongLine.cs +++ b/VocaluxeLib/Songs/CSongLine.cs @@ -82,12 +82,12 @@ namespace VocaluxeLib.Songs //Check for overlapping notes if (insPos >= 0) //Check note before { - if (_Notes[insPos].EndBeat > note.StartBeat) + if (_Notes[insPos].EndBeat >= note.StartBeat) return false; } if (insPos < _Notes.Count - 1) //Check note after { - if (_Notes[insPos + 1].StartBeat < note.EndBeat) + if (_Notes[insPos + 1].StartBeat <= note.EndBeat) return false; } _Notes.Insert(insPos + 1, note); diff --git a/VocaluxeLib/Songs/CSongLoader.cs b/VocaluxeLib/Songs/CSongLoader.cs index 0720be1a..a3b2735a 100644 --- a/VocaluxeLib/Songs/CSongLoader.cs +++ b/VocaluxeLib/Songs/CSongLoader.cs @@ -77,7 +77,7 @@ namespace VocaluxeLib.Songs _LogMsg(msg, false, withLineNr); } - public bool ReadHeader(bool ignoreSetEncoding = false) + public bool ReadHeader(bool useSetEncoding = false) { string filePath = Path.Combine(_Song.Folder, _Song.FileName); @@ -89,13 +89,17 @@ namespace VocaluxeLib.Songs _Song.UnknownTags.Clear(); _Song._Comment = ""; _Song.ManualEncoding = false; + _Song.Medley.Source = EDataSource.None; + _Song._CalculateMedley = false; + _Song.Preview.Source = EDataSource.None; + _Song.ShortEnd.Source = EDataSource.None; var headerFlags = new EHeaderFlags(); StreamReader sr = null; _LineNr = 0; try { - sr = new StreamReader(filePath, _Song.Encoding, true); + sr = useSetEncoding ? new StreamReader(filePath, _Song.Encoding, true) : new StreamReader(filePath, true); while (!sr.EndOfStream) { string line = sr.ReadLine(); @@ -134,7 +138,7 @@ namespace VocaluxeLib.Songs _Song.ManualEncoding = true; if (!newEncoding.Equals(sr.CurrentEncoding)) { - if (ignoreSetEncoding) + if (useSetEncoding) { _LogWarning("Duplicate encoding ignored"); continue; @@ -268,17 +272,17 @@ namespace VocaluxeLib.Songs _LogWarning("Invalid end"); break; case "PREVIEWSTART": - if (CHelper.TryParse(value, out _Song.PreviewStart) && _Song.PreviewStart >= 0f) - headerFlags |= EHeaderFlags.PreviewStart; + if (CHelper.TryParse(value, out _Song.Preview.StartTime) && _Song.Preview.StartTime >= 0f) + _Song.Preview.Source = EDataSource.Tag; else _LogWarning("Invalid previewstart"); break; case "PREVIEW": - if (CHelper.TryParse(value, out _Song.PreviewStart) && _Song.PreviewStart >= 0f) + if (CHelper.TryParse(value, out _Song.Preview.StartTime) && _Song.Preview.StartTime >= 0f) { //This is stored in ms not like PREVIEWSTART! - _Song.PreviewStart /= 1000f; - headerFlags |= EHeaderFlags.PreviewStart; + _Song.Preview.StartTime /= 1000f; + _Song.Preview.Source = EDataSource.Tag; } else _LogWarning("Invalid previewstart"); @@ -295,6 +299,19 @@ namespace VocaluxeLib.Songs else _LogWarning("Invalid medleyendbeat"); break; + case "ENDSHORT": + if ((headerFlags & EHeaderFlags.BPM) != 0) + { + int endTime; + if (int.TryParse(value, out endTime) || endTime < 0) + { + _Song.ShortEnd.EndBeat = (int)CBase.Game.GetBeatFromTime(endTime / 1000f, _Song.BPM, _Song.Gap); + _Song.ShortEnd.Source = EDataSource.Tag; + } + else + _LogWarning("Invalid medleyendbeat"); + } + break; case "CALCMEDLEY": if (value.ToUpper() == "OFF") _Song._CalculateMedley = false; @@ -373,15 +390,15 @@ namespace VocaluxeLib.Songs } } - if ((headerFlags & EHeaderFlags.PreviewStart) == 0 || _Song.PreviewStart < 0) + if (_Song.Preview.Source == EDataSource.None) { //PreviewStart is not set or <=0 - _Song.PreviewStart = (headerFlags & EHeaderFlags.MedleyStartBeat) != 0 ? CBase.Game.GetTimeFromBeats(_Song.Medley.StartBeat, _Song.BPM) : 0f; + _Song.Preview.StartTime = (headerFlags & EHeaderFlags.MedleyStartBeat) != 0 ? CBase.Game.GetTimeFromBeats(_Song.Medley.StartBeat, _Song.BPM) : 0f; } if ((headerFlags & EHeaderFlags.MedleyStartBeat) != 0 && (headerFlags & EHeaderFlags.MedleyEndBeat) != 0) { - _Song.Medley.Source = EMedleySource.Tag; + _Song.Medley.Source = EDataSource.Tag; _Song.Medley.FadeInTime = CBase.Settings.GetDefaultMedleyFadeInTime(); _Song.Medley.FadeOutTime = CBase.Settings.GetDefaultMedleyFadeOutTime(); } @@ -394,6 +411,7 @@ namespace VocaluxeLib.Songs _LogError("Error reading txt header" + e.Message, false); return false; } + _Song.Encoding = sr.CurrentEncoding; sr.Dispose(); _Song._CheckFiles(); @@ -485,7 +503,7 @@ namespace VocaluxeLib.Songs StreamReader sr = null; try { - sr = new StreamReader(filePath, _Song.Encoding); + sr = new StreamReader(filePath, _Song.Encoding, true); _Song.Notes.Reset(); @@ -544,8 +562,8 @@ namespace VocaluxeLib.Songs sr.Dispose(); return false; } - string text = noteData[3]; - if (text.Trim() == "") + string text = noteData[3].TrimMultipleWs(); + if (text == "") { _LogWarning("Ignored note without text"); changesMade.NoTextNoteCt++; @@ -579,7 +597,6 @@ namespace VocaluxeLib.Songs if (_Song.Relative) beat += currentBeat; - bool ignored = false; foreach (int curPlayer in player.GetSetBits()) { @@ -697,10 +714,7 @@ namespace VocaluxeLib.Songs string msg = "Automatic changes have been made to " + filePath + " Please check result!\r\n" + changesMade; CBase.Log.LogError("Warning:" + msg); if (CBase.Config.GetSaveModifiedSongs() == EOffOn.TR_CONFIG_ON) - { - string name = Path.GetFileNameWithoutExtension(_Song.FileName); - _Song.Save(Path.Combine(_Song.Folder, name + ".fix.txt")); - } + _Song.Save(filePath + ".fix"); } return true; } diff --git a/VocaluxeLib/Songs/CSongWriter.cs b/VocaluxeLib/Songs/CSongWriter.cs index 73c2ecc5..89d628d6 100644 --- a/VocaluxeLib/Songs/CSongWriter.cs +++ b/VocaluxeLib/Songs/CSongWriter.cs @@ -34,6 +34,12 @@ namespace VocaluxeLib.Songs _WriteHeaderEntry(id, value.ToString()); } + private void _WriteHeaderEntry(string id, int value, int def = 0) + { + if (value != def) + _WriteHeaderEntry(id, value.ToString()); + } + private void _WriteHeaderEntrys(string id, ICollection value) { if (value == null || value.Count <= 0) @@ -76,12 +82,15 @@ namespace VocaluxeLib.Songs _WriteHeaderEntry("RELATIVE", _Song.Relative); _WriteHeaderEntry("BPM", _Song.BPM / _BPMFactor); _WriteHeaderEntry("GAP", _Song.Gap * 1000f); - _WriteHeaderEntry("PREVIEWSTART", _Song.PreviewStart); + if (_Song.Preview.Source == EDataSource.Tag) + _WriteHeaderEntry("PREVIEWSTART", _Song.Preview.StartTime); _WriteHeaderEntry("START", _Song.Start); _WriteHeaderEntry("END", _Song.Finish * 1000f); - if (!_Song._CalculateMedley) + if (_Song.ShortEnd.Source == EDataSource.Tag) + _WriteHeaderEntry("ENDSHORT", (int)(CBase.Game.GetTimeFromBeats(_Song.ShortEnd.EndBeat, _Song.BPM) + _Song.Gap) * 1000); + if (_Song.Medley.Source == EDataSource.None) _WriteHeaderEntry("CALCMEDLEY", "OFF"); - if (_Song.Medley.Source == EMedleySource.Tag) + if (_Song.Medley.Source == EDataSource.Tag) { _WriteHeaderEntry("MEDLEYSTARTBEAT", _Song.Medley.StartBeat); _WriteHeaderEntry("MEDLEYENDBEAT", _Song.Medley.EndBeat); @@ -139,6 +148,7 @@ namespace VocaluxeLib.Songs } } } + tw.WriteLine("E"); } public bool SaveFile(string filePath) From 6de23ea42829cef48704e50d504e81884854018f Mon Sep 17 00:00:00 2001 From: Flamefire Date: Sat, 12 Oct 2013 21:50:39 +0200 Subject: [PATCH 19/23] Use IsGameModeAvail for duet check --- Vocaluxe/Base/CBackgroundMusic.cs | 16 ---------------- Vocaluxe/Base/CMain.cs | 5 +++++ Vocaluxe/GameModes/CGameModes.cs | 8 ++++++-- Vocaluxe/Lib/Playlist/CPlaylistFile.cs | 6 +++++- Vocaluxe/Screens/CPopupScreenPlayerControl.cs | 2 +- Vocaluxe/SongQueue/CSongQueue.cs | 2 +- VocaluxeLib/Menu/CPlaylist.cs | 2 +- 7 files changed, 19 insertions(+), 22 deletions(-) diff --git a/Vocaluxe/Base/CBackgroundMusic.cs b/Vocaluxe/Base/CBackgroundMusic.cs index 20a1c8ea..4f315e97 100644 --- a/Vocaluxe/Base/CBackgroundMusic.cs +++ b/Vocaluxe/Base/CBackgroundMusic.cs @@ -90,11 +90,6 @@ namespace Vocaluxe.Base get { return _CurrentPlaylistElement.SongID; } } - public static bool Duet - { - get { return _CurrentPlaylistElement.Duet; } - } - public static bool SongHasVideo { get { return File.Exists(_CurrentPlaylistElement.VideoFilePath); } @@ -581,17 +576,6 @@ namespace Vocaluxe.Base } } - public bool Duet - { - get - { - if (_Song != null) - return _Song.IsDuet; - - return false; - } - } - public CPlaylistElement(CSong song) { MusicFilePath = string.Empty; diff --git a/Vocaluxe/Base/CMain.cs b/Vocaluxe/Base/CMain.cs index 84523016..6ff4068a 100644 --- a/Vocaluxe/Base/CMain.cs +++ b/Vocaluxe/Base/CMain.cs @@ -532,6 +532,11 @@ namespace Vocaluxe.Base return CGame.GetTimeFromBeats(beat, bpm); } + public float GetBeatFromTime(float time, float bpm, float gap) + { + return CGame.GetBeatFromTime(time, bpm, gap); + } + public void AddSong(int songID, EGameMode gameMode) { CGame.AddSong(songID, gameMode); diff --git a/Vocaluxe/GameModes/CGameModes.cs b/Vocaluxe/GameModes/CGameModes.cs index c49ff93e..a7e7d4b2 100644 --- a/Vocaluxe/GameModes/CGameModes.cs +++ b/Vocaluxe/GameModes/CGameModes.cs @@ -52,7 +52,7 @@ namespace Vocaluxe.GameModes { protected override CSong _PrepareSong(CSong song) { - return song; + return (song.IsGameModeAvailable(EGameMode.TR_GAMEMODE_NORMAL)) ? song : null; } } @@ -60,7 +60,7 @@ namespace Vocaluxe.GameModes { protected override CSong _PrepareSong(CSong song) { - return (song.IsDuet) ? song : null; + return (song.IsGameModeAvailable(EGameMode.TR_GAMEMODE_DUET)) ? song : null; } } @@ -68,6 +68,8 @@ namespace Vocaluxe.GameModes { protected override CSong _PrepareSong(CSong song) { + if (!song.IsGameModeAvailable(EGameMode.TR_GAMEMODE_SHORTSONG)) + return null; var newSong = new CSong(song) {Finish = CGame.GetTimeFromBeats(song.ShortEnd.EndBeat, song.BPM) + CSettings.DefaultMedleyFadeOutTime + song.Gap}; // set lines to short mode newSong.Notes.SetMedley(0, song.ShortEnd.EndBeat); @@ -80,6 +82,8 @@ namespace Vocaluxe.GameModes { protected override CSong _PrepareSong(CSong song) { + if (!song.IsGameModeAvailable(EGameMode.TR_GAMEMODE_MEDLEY)) + return null; var newSong = new CSong(song) {Start = CGame.GetTimeFromBeats(song.Medley.StartBeat, song.BPM) - song.Medley.FadeInTime + song.Gap}; if (newSong.Start < 0f) newSong.Start = 0f; diff --git a/Vocaluxe/Lib/Playlist/CPlaylistFile.cs b/Vocaluxe/Lib/Playlist/CPlaylistFile.cs index 3c17a424..6c7f5228 100644 --- a/Vocaluxe/Lib/Playlist/CPlaylistFile.cs +++ b/Vocaluxe/Lib/Playlist/CPlaylistFile.cs @@ -180,7 +180,11 @@ namespace Vocaluxe.Lib.Playlist public void AddSong(int songID) { - var song = new CPlaylistSong {SongID = songID, GameMode = CSongs.GetSong(songID).IsDuet ? EGameMode.TR_GAMEMODE_DUET : EGameMode.TR_GAMEMODE_NORMAL}; + var song = new CPlaylistSong + { + SongID = songID, + GameMode = CSongs.GetSong(songID).IsGameModeAvailable(EGameMode.TR_GAMEMODE_DUET) ? EGameMode.TR_GAMEMODE_DUET : EGameMode.TR_GAMEMODE_NORMAL + }; Songs.Add(song); } diff --git a/Vocaluxe/Screens/CPopupScreenPlayerControl.cs b/Vocaluxe/Screens/CPopupScreenPlayerControl.cs index c48b2f08..9ffe1779 100644 --- a/Vocaluxe/Screens/CPopupScreenPlayerControl.cs +++ b/Vocaluxe/Screens/CPopupScreenPlayerControl.cs @@ -200,7 +200,7 @@ namespace Vocaluxe.Screens CGame.ClearSongs(); var gm = EGameMode.TR_GAMEMODE_NORMAL; - if (CSongs.AllSongs[songNr].IsDuet) + if (CSongs.AllSongs[songNr].IsGameModeAvailable(EGameMode.TR_GAMEMODE_DUET)) gm = EGameMode.TR_GAMEMODE_DUET; CGame.AddSong(songNr, gm); diff --git a/Vocaluxe/SongQueue/CSongQueue.cs b/Vocaluxe/SongQueue/CSongQueue.cs index b23f1288..6bbdc664 100644 --- a/Vocaluxe/SongQueue/CSongQueue.cs +++ b/Vocaluxe/SongQueue/CSongQueue.cs @@ -69,7 +69,7 @@ namespace Vocaluxe.SongQueue private bool _AddSong(int songID, EGameMode gameMode) { - if (gameMode == EGameMode.TR_GAMEMODE_DUET && !CSongs.GetSong(songID).IsDuet) + if (CSongs.GetSong(songID).IsGameModeAvailable(gameMode)) return false; _SongQueue.Add(new SSongQueueEntry(songID, gameMode)); diff --git a/VocaluxeLib/Menu/CPlaylist.cs b/VocaluxeLib/Menu/CPlaylist.cs index 1eecc9f2..1610fd52 100644 --- a/VocaluxeLib/Menu/CPlaylist.cs +++ b/VocaluxeLib/Menu/CPlaylist.cs @@ -971,11 +971,11 @@ namespace VocaluxeLib.Menu if (!mouseEvent.LBH && DragAndDropSongID != -1) { - var gm = EGameMode.TR_GAMEMODE_NORMAL; CSong song = CBase.Songs.GetSongByID(DragAndDropSongID); if (song != null) { + var gm = EGameMode.TR_GAMEMODE_NORMAL; if (song.IsDuet) gm = EGameMode.TR_GAMEMODE_DUET; From 6e9db6ed9890f4e100ff14c12e1747a3d3c98e72 Mon Sep 17 00:00:00 2001 From: Flamefire Date: Sat, 12 Oct 2013 22:47:08 +0200 Subject: [PATCH 20/23] Write correct tag No newline in LogError (could do it manually) --- Vocaluxe/Base/CLog.cs | 2 +- VocaluxeLib/Songs/CSongLoader.cs | 2 +- VocaluxeLib/Songs/CSongWriter.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Vocaluxe/Base/CLog.cs b/Vocaluxe/Base/CLog.cs index 0dd53fe2..6f87a74c 100644 --- a/Vocaluxe/Base/CLog.cs +++ b/Vocaluxe/Base/CLog.cs @@ -117,7 +117,7 @@ namespace Vocaluxe.Base MessageBox.Show(errorText, CSettings.ProgramName, MessageBoxButtons.OK, MessageBoxIcon.Error); if (e != null) errorText += ": " + e; - _ErrorLog.Add(_NumErrors + ") " + errorText + "\r\n"); + _ErrorLog.Add(_NumErrors + ") " + errorText); if (exit) Environment.Exit(Environment.ExitCode); } diff --git a/VocaluxeLib/Songs/CSongLoader.cs b/VocaluxeLib/Songs/CSongLoader.cs index a3b2735a..b0037ee3 100644 --- a/VocaluxeLib/Songs/CSongLoader.cs +++ b/VocaluxeLib/Songs/CSongLoader.cs @@ -90,7 +90,7 @@ namespace VocaluxeLib.Songs _Song._Comment = ""; _Song.ManualEncoding = false; _Song.Medley.Source = EDataSource.None; - _Song._CalculateMedley = false; + _Song._CalculateMedley = true; _Song.Preview.Source = EDataSource.None; _Song.ShortEnd.Source = EDataSource.None; diff --git a/VocaluxeLib/Songs/CSongWriter.cs b/VocaluxeLib/Songs/CSongWriter.cs index 89d628d6..9ecf29a1 100644 --- a/VocaluxeLib/Songs/CSongWriter.cs +++ b/VocaluxeLib/Songs/CSongWriter.cs @@ -88,7 +88,7 @@ namespace VocaluxeLib.Songs _WriteHeaderEntry("END", _Song.Finish * 1000f); if (_Song.ShortEnd.Source == EDataSource.Tag) _WriteHeaderEntry("ENDSHORT", (int)(CBase.Game.GetTimeFromBeats(_Song.ShortEnd.EndBeat, _Song.BPM) + _Song.Gap) * 1000); - if (_Song.Medley.Source == EDataSource.None) + if (!_Song._CalculateMedley) _WriteHeaderEntry("CALCMEDLEY", "OFF"); if (_Song.Medley.Source == EDataSource.Tag) { From 42ac648fc11fac4eb0c62d13b103b60ffbe3e63c Mon Sep 17 00:00:00 2001 From: Flamefire Date: Sat, 12 Oct 2013 23:10:11 +0200 Subject: [PATCH 21/23] Remove txt on saved songs Detect also gif images automatically --- VocaluxeLib/Songs/CSong.cs | 2 ++ VocaluxeLib/Songs/CSongLoader.cs | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/VocaluxeLib/Songs/CSong.cs b/VocaluxeLib/Songs/CSong.cs index 45f5af1f..aa0ced4b 100644 --- a/VocaluxeLib/Songs/CSong.cs +++ b/VocaluxeLib/Songs/CSong.cs @@ -319,6 +319,7 @@ namespace VocaluxeLib.Songs List files = CHelper.ListFiles(Folder, "*.jpg"); files.AddRange(CHelper.ListFiles(Folder, "*.jpeg")); files.AddRange(CHelper.ListFiles(Folder, "*.png")); + files.AddRange(CHelper.ListFiles(Folder, "*.gif")); foreach (String file in files) { if (file.ContainsIgnoreCase("[CO]") && @@ -332,6 +333,7 @@ namespace VocaluxeLib.Songs List files = CHelper.ListFiles(Folder, "*.jpg"); files.AddRange(CHelper.ListFiles(Folder, "*.jpeg")); files.AddRange(CHelper.ListFiles(Folder, "*.png")); + files.AddRange(CHelper.ListFiles(Folder, "*.gif")); foreach (String file in files) { if (file.ContainsIgnoreCase("[BG]") && diff --git a/VocaluxeLib/Songs/CSongLoader.cs b/VocaluxeLib/Songs/CSongLoader.cs index b0037ee3..459b554b 100644 --- a/VocaluxeLib/Songs/CSongLoader.cs +++ b/VocaluxeLib/Songs/CSongLoader.cs @@ -714,7 +714,10 @@ namespace VocaluxeLib.Songs string msg = "Automatic changes have been made to " + filePath + " Please check result!\r\n" + changesMade; CBase.Log.LogError("Warning:" + msg); if (CBase.Config.GetSaveModifiedSongs() == EOffOn.TR_CONFIG_ON) - _Song.Save(filePath + ".fix"); + { + string name = Path.GetFileNameWithoutExtension(_Song.FileName); + _Song.Save(Path.Combine(_Song.Folder, name + ".fix")); + } } return true; } From d41779b6f2b6cd58348295a793a6ef4a4acd3c72 Mon Sep 17 00:00:00 2001 From: Flamefire Date: Sat, 12 Oct 2013 23:31:36 +0200 Subject: [PATCH 22/23] Fix wrong comparisons --- Vocaluxe/Screens/CScreenSong.cs | 17 +++++++---------- Vocaluxe/SongQueue/CSongQueue.cs | 2 +- VocaluxeLib/Songs/CSongLoader.cs | 4 ++-- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/Vocaluxe/Screens/CScreenSong.cs b/Vocaluxe/Screens/CScreenSong.cs index 70834067..5c27e577 100644 --- a/Vocaluxe/Screens/CScreenSong.cs +++ b/Vocaluxe/Screens/CScreenSong.cs @@ -819,9 +819,8 @@ namespace Vocaluxe.Screens CGame.Reset(); CGame.ClearSongs(); - CGame.AddVisibleSong(songNr, gm); - - CGraphics.FadeTo(EScreens.ScreenNames); + if (CGame.AddVisibleSong(songNr, gm)) + CGraphics.FadeTo(EScreens.ScreenNames); } } @@ -834,9 +833,8 @@ namespace Vocaluxe.Screens CGame.Reset(); CGame.ClearSongs(); - CGame.AddVisibleSong(songNr, gm); - - CGraphics.FadeTo(EScreens.ScreenNames); + if (CGame.AddVisibleSong(songNr, gm)) + CGraphics.FadeTo(EScreens.ScreenNames); } } @@ -844,14 +842,13 @@ namespace Vocaluxe.Screens { if ((CSongs.Category >= 0) && (songNr >= 0)) { - if (CSongs.VisibleSongs[songNr].IsGameModeAvailable(EGameMode.TR_GAMEMODE_MEDLEY)) + if (!CSongs.VisibleSongs[songNr].IsGameModeAvailable(EGameMode.TR_GAMEMODE_MEDLEY)) return; CGame.Reset(); CGame.ClearSongs(); - CGame.AddVisibleSong(songNr, EGameMode.TR_GAMEMODE_MEDLEY); - - CGraphics.FadeTo(EScreens.ScreenNames); + if (CGame.AddVisibleSong(songNr, EGameMode.TR_GAMEMODE_MEDLEY)) + CGraphics.FadeTo(EScreens.ScreenNames); } } diff --git a/Vocaluxe/SongQueue/CSongQueue.cs b/Vocaluxe/SongQueue/CSongQueue.cs index 6bbdc664..e2a461cf 100644 --- a/Vocaluxe/SongQueue/CSongQueue.cs +++ b/Vocaluxe/SongQueue/CSongQueue.cs @@ -69,7 +69,7 @@ namespace Vocaluxe.SongQueue private bool _AddSong(int songID, EGameMode gameMode) { - if (CSongs.GetSong(songID).IsGameModeAvailable(gameMode)) + if (!CSongs.GetSong(songID).IsGameModeAvailable(gameMode)) return false; _SongQueue.Add(new SSongQueueEntry(songID, gameMode)); diff --git a/VocaluxeLib/Songs/CSongLoader.cs b/VocaluxeLib/Songs/CSongLoader.cs index 459b554b..ec93c156 100644 --- a/VocaluxeLib/Songs/CSongLoader.cs +++ b/VocaluxeLib/Songs/CSongLoader.cs @@ -99,7 +99,7 @@ namespace VocaluxeLib.Songs _LineNr = 0; try { - sr = useSetEncoding ? new StreamReader(filePath, _Song.Encoding, true) : new StreamReader(filePath, true); + sr = new StreamReader(filePath, _Song.Encoding, true); while (!sr.EndOfStream) { string line = sr.ReadLine(); @@ -712,7 +712,7 @@ namespace VocaluxeLib.Songs if (changesMade.IsModified) { string msg = "Automatic changes have been made to " + filePath + " Please check result!\r\n" + changesMade; - CBase.Log.LogError("Warning:" + msg); + CBase.Log.LogError("Warning: " + msg); if (CBase.Config.GetSaveModifiedSongs() == EOffOn.TR_CONFIG_ON) { string name = Path.GetFileNameWithoutExtension(_Song.FileName); From 1702859e790f79e2d4b2d7fed76f0991d719ef73 Mon Sep 17 00:00:00 2001 From: Flamefire Date: Mon, 14 Oct 2013 21:55:26 +0200 Subject: [PATCH 23/23] Write decimals in international format No warning on #RESOLUTION tag --- VocaluxeLib/CHelper.cs | 9 +-------- VocaluxeLib/Extensions.cs | 11 +++++++++++ VocaluxeLib/Songs/CSongLoader.cs | 6 ++++-- VocaluxeLib/Songs/CSongWriter.cs | 22 +++++++++++----------- 4 files changed, 27 insertions(+), 21 deletions(-) diff --git a/VocaluxeLib/CHelper.cs b/VocaluxeLib/CHelper.cs index 7b441caa..bd06cecb 100644 --- a/VocaluxeLib/CHelper.cs +++ b/VocaluxeLib/CHelper.cs @@ -53,7 +53,6 @@ namespace VocaluxeLib public static string ListStrings(string[] str) { string result = string.Empty; - for (int i = 0; i < str.Length; i++) { result += str[i]; @@ -160,13 +159,7 @@ namespace VocaluxeLib return files; } - public static bool TryParse(string value, out T result) - where T : struct - { - return TryParse(value, out result, false); - } - - public static bool TryParse(string value, out T result, bool ignoreCase) + public static bool TryParse(string value, out T result, bool ignoreCase=false) where T : struct { result = default(T); diff --git a/VocaluxeLib/Extensions.cs b/VocaluxeLib/Extensions.cs index a035f869..d6c78529 100644 --- a/VocaluxeLib/Extensions.cs +++ b/VocaluxeLib/Extensions.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Text; @@ -97,5 +98,15 @@ namespace VocaluxeLib value = value.Substring(start, end - start + 1); return value; } + + /// + /// Converts value to a string in fixed point notation using invariant formating (english style decimal point) + /// + /// Value to be converted + /// String representation of value + public static string ToInvariantString(this float value) + { + return value.ToString("F", NumberFormatInfo.InvariantInfo); + } } } \ No newline at end of file diff --git a/VocaluxeLib/Songs/CSongLoader.cs b/VocaluxeLib/Songs/CSongLoader.cs index ec93c156..597398d4 100644 --- a/VocaluxeLib/Songs/CSongLoader.cs +++ b/VocaluxeLib/Songs/CSongLoader.cs @@ -321,13 +321,15 @@ namespace VocaluxeLib.Songs _Song.Relative = true; break; case "RESOLUTION": - _LogWarning("#RESOLUTION tag is outdated and ignored. Test the song to see if note timing is still correct!"); + case "NOTESGAP": + //Outdated/not used + _Song.UnknownTags.Add(line); break; default: if (identifier.StartsWith("DUETSINGER")) { identifier = identifier.Substring(10); - if (identifier.StartsWith("P")) // Possible fix for missing "P" + if (identifier.StartsWith("P")) // fix for missing "P" identifier = "P" + identifier; } if (identifier.StartsWith("P")) diff --git a/VocaluxeLib/Songs/CSongWriter.cs b/VocaluxeLib/Songs/CSongWriter.cs index 9ecf29a1..906c358f 100644 --- a/VocaluxeLib/Songs/CSongWriter.cs +++ b/VocaluxeLib/Songs/CSongWriter.cs @@ -9,7 +9,7 @@ namespace VocaluxeLib.Songs private class CSongWriter { private readonly CSong _Song; - private TextWriter tw; + private TextWriter _Tw; public CSongWriter(CSong song) { @@ -19,7 +19,7 @@ namespace VocaluxeLib.Songs private void _WriteHeaderEntry(string id, string value) { if (!String.IsNullOrEmpty(value)) - tw.WriteLine("#" + id.ToUpper() + ":" + value); + _Tw.WriteLine("#" + id.ToUpper() + ":" + value); } private void _WriteHeaderEntry(string id, bool value) @@ -31,7 +31,7 @@ namespace VocaluxeLib.Songs private void _WriteHeaderEntry(string id, float value, float def = 0f) { if (Math.Abs(value - def) > 0.0001) - _WriteHeaderEntry(id, value.ToString()); + _WriteHeaderEntry(id, value.ToInvariantString()); } private void _WriteHeaderEntry(string id, int value, int def = 0) @@ -81,11 +81,11 @@ namespace VocaluxeLib.Songs _WriteHeaderEntry("VIDEOASPECT", _Song.VideoAspect.ToString()); _WriteHeaderEntry("RELATIVE", _Song.Relative); _WriteHeaderEntry("BPM", _Song.BPM / _BPMFactor); - _WriteHeaderEntry("GAP", _Song.Gap * 1000f); + _WriteHeaderEntry("GAP", (int)(_Song.Gap * 1000f)); if (_Song.Preview.Source == EDataSource.Tag) _WriteHeaderEntry("PREVIEWSTART", _Song.Preview.StartTime); _WriteHeaderEntry("START", _Song.Start); - _WriteHeaderEntry("END", _Song.Finish * 1000f); + _WriteHeaderEntry("END", (int)(_Song.Finish * 1000f)); if (_Song.ShortEnd.Source == EDataSource.Tag) _WriteHeaderEntry("ENDSHORT", (int)(CBase.Game.GetTimeFromBeats(_Song.ShortEnd.EndBeat, _Song.BPM) + _Song.Gap) * 1000); if (!_Song._CalculateMedley) @@ -101,7 +101,7 @@ namespace VocaluxeLib.Songs _WriteHeaderEntry("P" + i, _Song.Notes.VoiceNames[i]); } foreach (string addLine in _Song.UnknownTags) - tw.WriteLine(addLine); + _Tw.WriteLine(addLine); } private void _WriteNotes() @@ -110,7 +110,7 @@ namespace VocaluxeLib.Songs { CVoice voice = _Song.Notes.GetVoice(i); if (_Song.Notes.VoiceCount > 1) - tw.WriteLine("P" + Math.Pow(2, i)); + _Tw.WriteLine("P" + Math.Pow(2, i)); bool firstLine = true; int currentBeat = 0; foreach (CSongLine line in voice.Lines) @@ -123,7 +123,7 @@ namespace VocaluxeLib.Songs lineTxt += " " + (line.FirstNoteBeat - currentBeat); currentBeat = line.FirstNoteBeat; } - tw.WriteLine(lineTxt); + _Tw.WriteLine(lineTxt); } else firstLine = false; @@ -144,18 +144,18 @@ namespace VocaluxeLib.Songs default: throw new NotImplementedException("Note type " + note.Type); } - tw.WriteLine(tag + " " + (note.StartBeat - currentBeat) + " " + note.Duration + " " + note.Tone + " " + note.Text); + _Tw.WriteLine(tag + " " + (note.StartBeat - currentBeat) + " " + note.Duration + " " + note.Tone + " " + note.Text); } } } - tw.WriteLine("E"); + _Tw.WriteLine("E"); } public bool SaveFile(string filePath) { try { - tw = new StreamWriter(filePath, false, _Song.Encoding); + _Tw = new StreamWriter(filePath, false, _Song.Encoding); _WriteHeader(); _WriteNotes(); }