Merge branch 'SongLoader' into develop

This commit is contained in:
Flamefire committed 2013-10-14 22:02:18 +02:00
commit 91afb830b5
37 files changed
+1550 -903

No files matched your search

@@ -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);
+1 -4
View File
@@ -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
-16
View File
@@ -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;
+5
View File
@@ -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
+7 -7
View File
@@ -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;
@@ -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);
@@ -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);
+1 -2
View File
@@ -117,8 +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(String.Empty);
_ErrorLog.Add(_NumErrors + ") " + errorText);
if (exit)
Environment.Exit(Environment.ExitCode);
}
+11 -1
View File
@@ -60,6 +60,11 @@ namespace Vocaluxe.Base
class CBconfig : IConfig
{
public EOffOn GetSaveModifiedSongs()
{
return CConfig.SaveModifiedSongs;
}
public void SetBackgroundMusicVolume(int newVolume)
{
CConfig.BackgroundMusicVolume = newVolume;
@@ -110,7 +115,7 @@ namespace Vocaluxe.Base
return CConfig.CoverSize;
}
public IEnumerable<string> GetSongFolder()
public IEnumerable<string> GetSongFolders()
{
return CConfig.SongFolder;
}
@@ -527,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);
+3 -3
View File
@@ -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";
+1 -1
View File
@@ -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");
+8 -4
View File
@@ -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,9 +68,11 @@ 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};
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(song.Notes.GetVoice(0).Lines[0].FirstNoteBeat, song.ShortEnd);
newSong.Notes.SetMedley(0, song.ShortEnd.EndBeat);
return newSong;
}
@@ -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;
+5 -1
View File
@@ -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);
}
@@ -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);
+6 -2
View File
@@ -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
+2 -4
View File
@@ -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;
+65 -67
View File
@@ -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();
@@ -634,8 +630,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)
@@ -646,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];
}
}
@@ -672,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();
@@ -781,7 +775,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
@@ -1090,10 +1084,10 @@ 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.LinesCount; i++)
for (int i = 0; i < song.Notes.VoiceCount; i++)
{
CVoice voice = song.Notes.GetVoice(i);
CSongLine[] lines = voice.Lines;
@@ -1197,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
@@ -1215,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()
+21 -27
View File
@@ -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].Medley.Source == EMedleySource.None)
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);
}
}
@@ -1165,6 +1162,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 +1178,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++)
{
+1 -1
View File
@@ -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));
+4 -11
View File
@@ -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<T>(string value, out T result)
where T : struct
{
return TryParse(value, out result, false);
}
public static bool TryParse<T>(string value, out T result, bool ignoreCase)
public static bool TryParse<T>(string value, out T result, bool ignoreCase=false)
where T : struct
{
result = default(T);
@@ -199,7 +192,7 @@ namespace VocaluxeLib
static class CEncoding
{
public static Encoding GetEncoding(string encodingName)
public static Encoding GetEncoding(this string encodingName)
{
switch (encodingName)
{
@@ -214,11 +207,11 @@ namespace VocaluxeLib
case "UTF8":
return Encoding.UTF8;
default:
return Encoding.UTF8;
return Encoding.Default;
}
}
public static string GetEncodingName(Encoding enc)
public static string GetEncodingName(this Encoding enc)
{
string result = "UTF8";
+112
View File
@@ -0,0 +1,112 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
namespace VocaluxeLib
{
public static class Extensions
{
/// <summary>
/// Resizes the given list to the given size. Removes elements or adds default values
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="list"></param>
/// <param name="size"></param>
/// <param name="defaultValue"></param>
public static void Resize<T>(this List<T> 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));
}
/// <summary>
/// Makes the list at least the given size adding the given default values.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="list"></param>
/// <param name="size"></param>
/// <param name="defaultValue"></param>
public static void EnsureSize<T>(this List<T> list, int size, T defaultValue)
{
int curSize = list.Count;
if (size > curSize)
list.AddRange(Enumerable.Repeat(defaultValue, size - curSize));
}
/// <summary>
/// Makes the list at least the given size adding a new instance of T
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="list"></param>
/// <param name="size"></param>
public static void EnsureSize<T>(this List<T> list, int size) where T : new()
{
if (size > list.Count)
list.Add(new T());
}
/// <summary>
/// Gets all set bits starting from lowest (Bit 0)
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static IEnumerable<int> GetSetBits(this int value)
{
var result = new List<int>();
int curBit = 0;
//Evaluate as bitset
while (value > 0)
{
if ((value & 1) != 0)
result.Add(curBit);
value >>= 1;
curBit++;
}
return result;
}
public static bool ContainsIgnoreCase(this string value, string other)
{
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;
}
/// <summary>
/// Converts value to a string in fixed point notation using invariant formating (english style decimal point)
/// </summary>
/// <param name="value">Value to be converted</param>
/// <returns>String representation of value</returns>
public static string ToInvariantString(this float value)
{
return value.ToString("F", NumberFormatInfo.InvariantInfo);
}
}
}
+4 -1
View File
@@ -29,6 +29,8 @@ namespace VocaluxeLib
{
public interface IConfig
{
EOffOn GetSaveModifiedSongs();
void SetBackgroundMusicVolume(int newVolume);
int GetBackgroundMusicVolume();
@@ -45,7 +47,7 @@ namespace VocaluxeLib
int GetCoverSize();
IEnumerable<string> GetSongFolder();
IEnumerable<string> GetSongFolders();
ESongSorting GetSongSorting();
EOffOn GetTabs();
EOffOn GetIgnoreArticles();
@@ -179,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();
+55 -108
View File
@@ -23,15 +23,6 @@ using VocaluxeLib.Songs;
namespace VocaluxeLib.Menu
{
struct SNote
{
public string Text;
public int StartBeat;
public int EndBeat;
public int Duration;
public ENoteType Type;
}
struct SThemeLyrics
{
public string Name;
@@ -46,7 +37,10 @@ namespace VocaluxeLib.Menu
private SThemeLyrics _Theme;
private bool _ThemeLoaded;
private readonly List<SNote> _Notes;
/// <summary>
/// Holds a reference to Songline. DO NOT MODIFY
/// </summary>
private CSongLine _Line;
private CText _Text;
private float _X;
@@ -109,7 +103,7 @@ namespace VocaluxeLib.Menu
_MaxW = 1f;
_H = 1f;
_Width = 1f;
_Notes = new List<SNote>();
_Line = new CSongLine();
_Text = new CText(_PartyModeID);
LyricStyle = ELyricStyle.Fill;
@@ -201,28 +195,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 +253,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 +264,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 +286,7 @@ namespace VocaluxeLib.Menu
_Text.Draw();
}
x += rect.Width;
x += _Text.Rect.W;
}
}
@@ -308,37 +295,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 +333,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 +373,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 +390,7 @@ namespace VocaluxeLib.Menu
_Text.Draw();
}
x += rect.Width;
x += _Text.Rect.W;
}
}
@@ -439,29 +399,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 +435,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();
+3 -3
View File
@@ -84,7 +84,7 @@ namespace VocaluxeLib.Menu
{
private class CPlaylistElementContent
{
public EGameMode[] Modes;
public IList<EGameMode> Modes;
public int SongID;
public EGameMode Mode;
}
@@ -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;
@@ -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)
+1 -1
View File
@@ -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);
+3 -3
View File
@@ -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);
+16 -14
View File
@@ -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<int> _Streams = new List<int>();
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();
@@ -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)
+7
View File
@@ -15,6 +15,8 @@
// along with Vocaluxe. If not, see <http://www.gnu.org/licenses/>.
#endregion
using System;
namespace VocaluxeLib.Songs
{
/// <summary>
@@ -38,12 +40,17 @@ 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
#region Properties
public int StartBeat { get; set; }
/// <summary>
/// Last beat of this note. First valid beat for a note after that is at EndBeat+1
/// </summary>
public int EndBeat
{
get { return StartBeat + _Duration - 1; }
+5 -1
View File
@@ -79,7 +79,11 @@ namespace VocaluxeLib.Songs
#endregion Properties
#region Methods
//Find last note with StartBeat<=Beat
/// <summary>
/// Find last note with StartBeat<=Beat
/// </summary>
/// <param name="beat"></param>
/// <returns>Note index of last note with StartBeat<=Beat or -1 if all notes start after beat</returns>
public int FindPreviousNote(int beat)
{
//If no notes -> No previous note
+51 -10
View File
@@ -21,24 +21,63 @@ namespace VocaluxeLib.Songs
{
public class CNotes
{
private readonly List<CVoice> _Voices = new List<CVoice>();
public CVoice[] Voices
public class CVoiceNames
{
get { return _Voices.ToArray(); }
private readonly List<string> _Names = new List<string>();
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<CVoice> _Voices = new List<CVoice>();
public readonly CVoiceNames VoiceNames = new CVoiceNames();
public CNotes() {}
public int LinesCount
{
get { return _Voices.Count; }
}
public CNotes(CNotes notes)
{
foreach (CVoice voice in notes._Voices)
_Voices.Add(new CVoice(voice));
VoiceNames = new CVoiceNames(notes.VoiceNames);
}
public IEnumerable<CVoice> Voices
{
get { return _Voices; }
}
public int VoiceCount
{
get { return _Voices.Count; }
}
public CVoice GetVoice(int index)
@@ -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)
+126 -574
View File
@@ -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
@@ -45,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,
@@ -59,30 +57,43 @@ 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 class CSong
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;
private CTexture _CoverTextureBig;
public SMedley Medley;
public bool CalculateMedley = true;
public float PreviewStart;
private bool _CalculateMedley = true;
public SPreview Preview;
public int ShortEnd;
public SShortEnd ShortEnd;
public Encoding Encoding = Encoding.Default;
public bool ManualEncoding;
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;
@@ -117,17 +128,31 @@ namespace VocaluxeLib.Songs
public string TitleSorting = String.Empty;
public string ArtistSorting = String.Empty;
public string Creator = "";
public string Version = "";
public string Source = "";
public readonly List<String> UnknownTags = new List<string>();
/// <summary>
/// Start of the song in s (s in txt)
/// </summary>
public float Start;
/// <summary>
/// End of the song in s (ms in txt)
/// </summary>
public float Finish;
public float BPM = 1f;
/// <summary>
/// Gap of the mp3 in s (ms in txt)
/// </summary>
public float Gap;
/// <summary>
/// Gap of the video in s (s in txt)
/// </summary>
public float VideoGap;
public string DuetPart1 = String.Empty;
public string DuetPart2 = String.Empty;
private readonly List<string> _Comment = new List<string>();
private string _Comment = "";
// Sorting
public int ID;
@@ -136,14 +161,15 @@ namespace VocaluxeLib.Songs
private readonly bool _Selected;
public bool IsDuet
{
get { return Notes.Voices.Length > 1; }
get { return Notes.VoiceCount > 1; }
}
public readonly List<string> Edition = new List<string>();
public readonly List<string> Genre = new List<string>();
public readonly List<string> Editions = new List<string>();
public readonly List<string> Genres = new List<string>();
public string Album = "";
public string Year = "";
public readonly List<string> Language = new List<string>();
public readonly List<string> Languages = new List<string>();
public int DataBaseSongID = -1;
public string DateAdded = "";
@@ -152,50 +178,47 @@ namespace VocaluxeLib.Songs
// Notes
public readonly CNotes Notes = new CNotes();
public EGameMode[] AvailableGameModes
public IList<EGameMode> AvailableGameModes
{
get
{
var gms = new List<EGameMode> {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;
}
}
/// <summary>
/// Returns true if the requested game mode is available
/// </summary>
/// <param name="gameMode"></param>
/// <returns>true if the requested game mode is available</returns>
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() {}
public static CSong LoadSong(string filePath)
{
var song = new CSong();
return song.ReadHeader(filePath) ? song : null;
}
public CSong(CSong song)
{
_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;
_CalculateMedley = song._CalculateMedley;
Preview = song.Preview;
ShortEnd = song.ShortEnd;
DuetPart1 = song.DuetPart1;
DuetPart2 = song.DuetPart2;
Encoding = song.Encoding;
ManualEncoding = song.ManualEncoding;
Folder = song.Folder;
FolderName = song.FolderName;
FileName = song.FileName;
@@ -211,6 +234,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<string>(song.UnknownTags);
Start = song.Start;
Finish = song.Finish;
@@ -219,28 +249,19 @@ namespace VocaluxeLib.Songs
Gap = song.Gap;
VideoGap = song.VideoGap;
_Comment = new List<string>();
foreach (string value in song._Comment)
_Comment.Add(value);
_Comment = song._Comment;
ID = song.ID;
_Visible = song._Visible;
_CatIndex = song._CatIndex;
_Selected = song._Selected;
Edition = new List<string>();
foreach (string value in song.Edition)
Edition.Add(value);
Genre = new List<string>();
foreach (string value in song.Genre)
Genre.Add(value);
Editions = new List<string>(song.Editions);
Genres = new List<string>(song.Genres);
Album = song.Album;
Year = song.Year;
Language = new List<string>();
foreach (string value in song.Language)
Language.Add(value);
Languages = new List<string>(song.Languages);
DataBaseSongID = song.DataBaseSongID;
DateAdded = song.DateAdded;
@@ -249,6 +270,25 @@ 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 bool Save(string filePath)
{
var writer = new CSongWriter(this);
return writer.SaveFile(filePath);
}
public string GetMP3()
{
return Path.Combine(Folder, MP3FileName);
@@ -259,494 +299,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.GetSongFolder())
{
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);
//Scip 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 != "")
DuetPart1 = value;
break;
case "DUETSINGERP2":
case "P2":
if (value != "")
DuetPart2 = 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" && value.ToUpper() != "NO")
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 = 0;
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()) == ' ') {}
int.TryParse(chr.ToString(), out param1);
if (param1 == 1)
player = 0;
else if (param1 == 2)
player = 1;
else if (param1 == 3)
player = 2;
else
{
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();
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;
if (player != 2)
// one singer
_ParseNote(player, noteType, param1, param2, param3, paramS);
else
{
// both singer
_ParseNote(0, noteType, param1, param2, param3, paramS);
_ParseNote(1, noteType, param1, param2, param3, paramS);
}
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)
@@ -765,11 +317,13 @@ namespace VocaluxeLib.Songs
if (CoverFileName == "")
{
List<string> 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 (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;
}
}
@@ -777,11 +331,13 @@ namespace VocaluxeLib.Songs
if (BackgroundFileName == "")
{
List<string> 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 (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;
}
}
@@ -789,15 +345,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 + "\".");
}
}
@@ -853,14 +404,11 @@ namespace VocaluxeLib.Songs
{
if (IsDuet)
{
Medley.Source = EMedleySource.None;
Medley.Source = EDataSource.None;
return;
}
if (Medley.Source == EMedleySource.Tag)
return;
if (!CalculateMedley)
if (!_CalculateMedley)
return;
List<SSeries> series = _GetSeries();
@@ -883,41 +431,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<SSeries> series = _GetSeries();
if (series == null)
return;
@@ -934,10 +485,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;
}
}
@@ -947,12 +498,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;
}
}
}
+16 -12
View File
@@ -22,7 +22,7 @@ namespace VocaluxeLib.Songs
{
public class CSongLine : CLineBase<CSongNote>
{
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)
@@ -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;
@@ -90,11 +97,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.Type = ENoteType.Freestyle;
VisibleInTimeLine = FirstNoteBeat >= startBeat && LastNoteBeat <= endBeat;
}
+740
View File
@@ -0,0 +1,740 @@
using System;
using System.IO;
using System.Linq;
using System.Text;
namespace VocaluxeLib.Songs
{
/// <summary>
/// Part of CSong that is required for note loading
/// </summary>
public partial class CSong
{
/// <summary>
/// Factor the bpm given in the txt is multiplied with
/// </summary>
private const int _BPMFactor = 4;
private class CSongLoader
{
private readonly CSong _Song;
private int _LineNr;
public CSongLoader(CSong song)
{
_Song = song;
}
public bool InitPaths(string filePath)
{
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)))
{
if (_Song.Folder.Length == folder.Length)
_Song.FolderName = "Songs";
else
{
_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);
}
break;
}
_Song.FileName = Path.GetFileName(filePath);
return true;
}
/// <summary>
/// Logs a given message with file name and line#
/// </summary>
/// <param name="msg">Message</param>
/// <param name="error">True prepends "Error: "; False prepends "Warning: "</param>
/// <param name="withLineNr">Adds line#</param>
private void _LogMsg(string msg, bool error, bool withLineNr)
{
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(bool useSetEncoding = false)
{
string filePath = Path.Combine(_Song.Folder, _Song.FileName);
if (!File.Exists(filePath))
return false;
_Song.Languages.Clear();
_Song.Genres.Clear();
_Song.UnknownTags.Clear();
_Song._Comment = "";
_Song.ManualEncoding = false;
_Song.Medley.Source = EDataSource.None;
_Song._CalculateMedley = true;
_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);
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)
{
_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;
}
switch (identifier)
{
case "ENCODING":
Encoding newEncoding = value.GetEncoding();
_Song.ManualEncoding = true;
if (!newEncoding.Equals(sr.CurrentEncoding))
{
if (useSetEncoding)
{
_LogWarning("Duplicate encoding ignored");
continue;
}
sr.Dispose();
sr = null;
_Song.Encoding = newEncoding;
return ReadHeader(true);
}
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 "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)))
{
_Song.MP3FileName = value;
headerFlags |= EHeaderFlags.MP3;
}
else
{
_LogError("Can't find audio file: " + Path.Combine(_Song.Folder, value));
return false;
}
break;
case "BPM":
if (CHelper.TryParse(value, out _Song.BPM))
{
_Song.BPM *= _BPMFactor;
headerFlags |= EHeaderFlags.BPM;
}
else
_LogWarning("Invalid BPM value");
break;
case "EDITION":
if (value.Length > 1)
_Song.Editions.Add(value);
else
_LogWarning("Invalid edition");
break;
case "GENRE":
if (value.Length > 1)
_Song.Genres.Add(value);
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)
_Song.Year = value;
else
_LogWarning("Invalid year");
break;
case "LANGUAGE":
if (value.Length > 1)
_Song.Languages.Add(value);
else
_LogWarning("Invalid language");
break;
case "COMMENT":
if (!String.IsNullOrEmpty(_Song._Comment))
_Song._Comment += "\r\n";
_Song._Comment += value;
break;
case "GAP":
if (CHelper.TryParse(value, out _Song.Gap))
_Song.Gap /= 1000f;
else
_LogWarning("Invalid gap");
break;
case "COVER":
if (File.Exists(Path.Combine(_Song.Folder, value)))
_Song.CoverFileName = value;
else
_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
_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
_LogWarning("Can't find video file: " + Path.Combine(_Song.Folder, value));
break;
case "VIDEOGAP":
if (!CHelper.TryParse(value, out _Song.VideoGap))
_LogWarning("Invalid videogap");
break;
case "VIDEOASPECT":
if (!CHelper.TryParse(value, out _Song.VideoAspect, true))
_LogWarning("Invalid videoaspect");
break;
case "START":
if (!CHelper.TryParse(value, out _Song.Start))
_LogWarning("Invalid start");
break;
case "END":
if (CHelper.TryParse(value, out _Song.Finish))
_Song.Finish /= 1000f;
else
_LogWarning("Invalid end");
break;
case "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.Preview.StartTime) && _Song.Preview.StartTime >= 0f)
{
//This is stored in ms not like PREVIEWSTART!
_Song.Preview.StartTime /= 1000f;
_Song.Preview.Source = EDataSource.Tag;
}
else
_LogWarning("Invalid previewstart");
break;
case "MEDLEYSTARTBEAT":
if (int.TryParse(value, out _Song.Medley.StartBeat))
headerFlags |= EHeaderFlags.MedleyStartBeat;
else
_LogWarning("Invalid medleystartbeat");
break;
case "MEDLEYENDBEAT":
if (int.TryParse(value, out _Song.Medley.EndBeat))
headerFlags |= EHeaderFlags.MedleyEndBeat;
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;
break;
case "RELATIVE":
if (value.ToUpper() == "YES")
_Song.Relative = true;
break;
case "RESOLUTION":
case "NOTESGAP":
//Outdated/not used
_Song.UnknownTags.Add(line);
break;
default:
if (identifier.StartsWith("DUETSINGER"))
{
identifier = identifier.Substring(10);
if (identifier.StartsWith("P")) // fix for missing "P"
identifier = "P" + identifier;
}
if (identifier.StartsWith("P"))
{
int player;
if (int.TryParse(identifier.Substring(1).Trim(), out player))
{
foreach (int curPlayer in player.GetSetBits())
_Song.Notes.VoiceNames[curPlayer] = value;
}
}
else
{
_Song.UnknownTags.Add(line);
_LogWarning("Unknown tag: #" + identifier);
}
break;
}
} //end of while
if (sr.EndOfStream)
{
//No other data then header
_LogError("Lyrics/Notes missing", false);
return false;
}
if ((headerFlags & EHeaderFlags.Title) == 0)
{
_LogError("Title tag missing", false);
return false;
}
if ((headerFlags & EHeaderFlags.Artist) == 0)
{
_LogError("Artist tag missing", false);
return false;
}
if ((headerFlags & EHeaderFlags.MP3) == 0)
{
_LogError("MP3 tag missing", false);
return false;
}
if ((headerFlags & EHeaderFlags.BPM) == 0)
{
_LogError("BPM tag missing", false);
return false;
}
#region check medley tags
if ((headerFlags & EHeaderFlags.MedleyStartBeat) != 0 && (headerFlags & EHeaderFlags.MedleyEndBeat) != 0)
{
if (_Song.Medley.StartBeat > _Song.Medley.EndBeat)
{
_LogError("MedleyStartBeat is bigger than MedleyEndBeat in file", false);
headerFlags = headerFlags - EHeaderFlags.MedleyStartBeat - EHeaderFlags.MedleyEndBeat;
}
}
if (_Song.Preview.Source == EDataSource.None)
{
//PreviewStart is not set or <=0
_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 = EDataSource.Tag;
_Song.Medley.FadeInTime = CBase.Settings.GetDefaultMedleyFadeInTime();
_Song.Medley.FadeOutTime = CBase.Settings.GetDefaultMedleyFadeOutTime();
}
#endregion check medley tags
}
catch (Exception e)
{
if (sr != null)
sr.Dispose();
_LogError("Error reading txt header" + e.Message, false);
return false;
}
_Song.Encoding = sr.CurrentEncoding;
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 (String.IsNullOrEmpty(_Song.ArtistSorting))
_Song.ArtistSorting = _Song.Artist;
if (String.IsNullOrEmpty(_Song.TitleSorting))
_Song.TitleSorting = _Song.Title;
return true;
}
private enum ENoteReadMode
{
Normal,
ZeroBased,
OneBased
}
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;
/// <summary>
/// Read notes. First try to read notes normally (assume standard)<br/>
/// If there are more than _MaxZeroNoteCt with length &lt; 1, try to read notes adding 1 to length<br/>
/// Then if there are more than _MaxOverlapNoteCt fallback to first version ignoring notes with length &lt; 1
/// </summary>
/// <param name="forceReload"></param>
/// <returns></returns>
public 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))
{
_LogError("The file does not exist", false);
return false;
}
int currentBeat = 0; //Used for relative songs
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;
char[] trimChars = {' ', ':'};
char[] splitChars = {' '};
var changesMade = new CAutoChanges();
StreamReader sr = null;
try
{
sr = new StreamReader(filePath, _Song.Encoding, true);
_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))
{
_LogError("Wrong or missing number after \"P\"");
return false;
}
currentBeat = 0;
lastNote = null;
break;
case ':':
case '*':
case 'F':
string[] noteData = line.Split(splitChars, 4);
if (noteData.Length < 4)
{
if (noteData.Length == 3)
{
_LogWarning("Ignored note without text");
changesMade.NoTextNoteCt++;
continue;
}
_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))
{
_LogError("Invalid note found (non-numeric values)");
sr.Dispose();
return false;
}
string text = noteData[3].TrimMultipleWs();
if (text == "")
{
_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;
if (tag.Equals('*'))
noteType = ENoteType.Golden;
else if (tag.Equals('F'))
noteType = ENoteType.Freestyle;
else
noteType = ENoteType.Normal;
if (_Song.Relative)
beat += currentBeat;
bool ignored = false;
foreach (int curPlayer in player.GetSetBits())
{
//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");
}
}
}
break;
case '-':
string[] lineBreakData = line.Split(splitChars);
if (lineBreakData.Length < 1)
{
_LogError("Invalid line break found (No beat)");
sr.Dispose();
return false;
}
if (!int.TryParse(lineBreakData[0], out beat))
{
_LogError("Invalid line break found (Non-numeric value)");
sr.Dispose();
return false;
}
if (_Song.Relative)
{
beat += currentBeat;
if (lineBreakData.Length < 2 || !int.TryParse(lineBreakData[1], out length))
{
_LogWarning("Missing line break length");
changesMade.NoLengthBreakCt++;
currentBeat = beat;
}
else
currentBeat += length;
}
if (lastNote != null && beat <= lastNote.EndBeat)
{
_LogWarning("Line break is before previous note end. Adjusted.");
changesMade.AjustedBreakCt++;
if (_Song.Relative)
currentBeat += lastNote.EndBeat - beat + 1;
beat = lastNote.EndBeat + 1;
}
if (beat < 1)
{
_LogWarning("Ignored line break because position is < 1");
changesMade.InvalidPosBreakCt++;
}
else
{
foreach (int curPlayer in player.GetSetBits())
{
if (!_NewSentence(curPlayer, beat))
_LogWarning("Ignored line break for player " + (curPlayer + 1) + " (Overlapping or duplicate)");
}
}
break;
default:
_LogError("Unexpected or missing character (" + tag + ")");
return false;
}
}
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)
{
_LogError("An unhandled exception occured (" + e.Message + ")");
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)
{
_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);
if (CBase.Config.GetSaveModifiedSongs() == EOffOn.TR_CONFIG_ON)
{
string name = Path.GetFileNameWithoutExtension(_Song.FileName);
_Song.Save(Path.Combine(_Song.Folder, name + ".fix"));
}
}
return true;
}
private bool _AddNote(int player, CSongNote note)
{
CVoice voice = _Song.Notes.GetVoice(player);
return voice.AddNote(note, false);
}
private bool _NewSentence(int player, int start)
{
CVoice voice = _Song.Notes.GetVoice(player);
return voice.AddLine(start);
}
}
}
}
+4 -4
View File
@@ -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;
+171
View File
@@ -0,0 +1,171 @@
using System;
using System.Collections.Generic;
using System.IO;
namespace VocaluxeLib.Songs
{
public partial class CSong
{
private class CSongWriter
{
private readonly CSong _Song;
private TextWriter _Tw;
public CSongWriter(CSong song)
{
_Song = song;
}
private void _WriteHeaderEntry(string id, string value)
{
if (!String.IsNullOrEmpty(value))
_Tw.WriteLine("#" + id.ToUpper() + ":" + value);
}
private void _WriteHeaderEntry(string id, bool value)
{
if (value)
_WriteHeaderEntry(id, "YES");
}
private void _WriteHeaderEntry(string id, float value, float def = 0f)
{
if (Math.Abs(value - def) > 0.0001)
_WriteHeaderEntry(id, value.ToInvariantString());
}
private void _WriteHeaderEntry(string id, int value, int def = 0)
{
if (value != def)
_WriteHeaderEntry(id, value.ToString());
}
private void _WriteHeaderEntrys(string id, ICollection<string> value)
{
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("COMMENT", comment.Split(splitChar));
}
_WriteHeaderEntry("TITLE", _Song.Title);
_WriteHeaderEntry("ARTIST", _Song.Artist);
if (!_Song.Title.Equals(_Song.TitleSorting))
_WriteHeaderEntry("TITLE-ON-SORTING", _Song.TitleSorting);
if (!_Song.Artist.Equals(_Song.ArtistSorting))
_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", (int)(_Song.Gap * 1000f));
if (_Song.Preview.Source == EDataSource.Tag)
_WriteHeaderEntry("PREVIEWSTART", _Song.Preview.StartTime);
_WriteHeaderEntry("START", _Song.Start);
_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)
_WriteHeaderEntry("CALCMEDLEY", "OFF");
if (_Song.Medley.Source == EDataSource.Tag)
{
_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("P" + i, _Song.Notes.VoiceNames[i]);
}
foreach (string addLine in _Song.UnknownTags)
_Tw.WriteLine(addLine);
}
private void _WriteNotes()
{
for (int i = 0; i < _Song.Notes.VoiceCount; i++)
{
CVoice voice = _Song.Notes.GetVoice(i);
if (_Song.Notes.VoiceCount > 1)
_Tw.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;
}
_Tw.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);
}
_Tw.WriteLine(tag + " " + (note.StartBeat - currentBeat) + " " + note.Duration + " " + note.Tone + " " + note.Text);
}
}
}
_Tw.WriteLine("E");
}
public bool SaveFile(string filePath)
{
try
{
_Tw = new StreamWriter(filePath, false, _Song.Encoding);
_WriteHeader();
_WriteNotes();
}
catch (Exception e)
{
CBase.Log.LogError("Unhandled exception while writing " + filePath + ": " + e);
return false;
}
return true;
}
}
}
}
+1 -1
View File
@@ -48,7 +48,7 @@ namespace VocaluxeLib.Songs
}
#region Properties
public double Points;
public new double Points;
// for drawing player notes
public bool Hit
{
+85 -14
View File
@@ -15,6 +15,7 @@
// along with Vocaluxe. If not, see <http://www.gnu.org/licenses/>.
#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
/// <summary>
/// Find last line with StartBeat<=Beat
/// </summary>
/// <param name="beat"></param>
/// <returns></returns>
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.NoteCount==0 || 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,15 +158,26 @@ 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
return false;
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
{
@@ -139,24 +189,27 @@ 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()
{
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++)
{
@@ -191,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)
@@ -204,5 +270,10 @@ namespace VocaluxeLib.Songs
line.SetMedley(startBeat, endBeat);
}
#endregion Methods
public int RemoveEmptyLines()
{
return _Lines.RemoveAll(line => line.NoteCount == 0);
}
}
}
+4 -1
View File
@@ -130,6 +130,7 @@
<Reference Include="System.XML" />
</ItemGroup>
<ItemGroup>
<Compile Include="Extensions.cs" />
<Compile Include="GlobalSuppressions.cs" />
<Compile Include="IBasic.cs" />
<Compile Include="Menu\CBackground.cs" />
@@ -174,9 +175,11 @@
<Compile Include="Menu\SongMenu\CSongMenuList.cs" />
<Compile Include="Menu\SongMenu\CSongMenuTileBoard.cs" />
<Compile Include="Menu\SongMenu\ISongMenu.cs" />
<Compile Include="Songs\CSongWriter.cs" />
<Compile Include="Songs\CSungLine.cs" />
<Compile Include="Songs\CSungNote.cs" />
<Compile Include="Songs\CVoice.cs" />
<Compile Include="Songs\CSongLoader.cs" />
<Compile Include="Structs.cs" />
<Compile Include="PartyModes\CPartyMode.cs" />
<Compile Include="PartyModes\CPartyModeNone.cs" />
@@ -195,4 +198,4 @@
<Target Name="AfterBuild">
</Target>
-->
</Project>
</Project>