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 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 { if (!_Lines[lineIndex].AddNote(note)) { return(false); } } if (updateTimings) { UpdateTimings(); } return(true); }
private bool _AddNote(int player, CSongNote note) { CVoice voice = _Song.Notes.GetVoice(player, true); return(voice.AddNote(note, false)); }
/// <summary> /// Read notes. First try to read notes normally (assume standard)<br /> /// If there are more than _MaxZeroNoteCt with length < 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 < 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)) { CLog.CSongLog.Error("[{SongFileName}] The file songfile does not exist", CLog.Params(_Song.FileName)); 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)) { CLog.CSongLog.Error("[{SongFileName}] Wrong or missing number after \"P\" (in line {LineNr})", CLog.Params(_Song.FileName, _LineNr)); return(false); } currentBeat = 0; lastNote = null; break; case ':': case '*': case 'F': case 'R': // Read R (RAP) and G (Golden RAP) notes as Freestyle notes currently. Final implementation needed! case 'G': // Read R (RAP) and G (Golden RAP) notes as Freestyle notes currently. Final implementation needed! string[] noteData = line.Split(splitChars, 4); if (noteData.Length < 4) { if (noteData.Length == 3) { CLog.CSongLog.Warning("[{SongFileName}] Ignored note without text (in line {LineNr})", CLog.Params(_Song.FileName, _LineNr)); changesMade.NoTextNoteCt++; continue; } CLog.CSongLog.Error("[{SongFileName}] Invalid note found (in line {LineNr}): {noteData}", CLog.Params(_Song.FileName, _LineNr, noteData)); 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)) { CLog.CSongLog.Error("[{SongFileName}] Invalid note found (non-numeric values) (in line {LineNr}): {noteData}", CLog.Params(_Song.FileName, _LineNr, noteData)); sr.Dispose(); return(false); } string text = noteData[3].TrimMultipleWs(); if (text == "") { CLog.CSongLog.Warning("[{SongFileName}] Ignored note without text (in line {LineNr})", CLog.Params(_Song.FileName, _LineNr)); changesMade.NoTextNoteCt++; continue; } if (_CurrentReadMode == ENoteReadMode.ZeroBased) { length++; } if (length < 1) { changesMade.ZeroLengthNoteCt++; if (_CurrentReadMode == ENoteReadMode.Normal && changesMade.ZeroLengthNoteCt > _MaxZeroNoteCt && changesMade.OverlapNoteCt <= _MaxOverlapNoteCt) { CLog.CSongLog.Warning("[{SongFileName}] Found more than {MaxZeroNoteCt} notes with length < 1. Trying alternative read mode.", CLog.Params(_Song.FileName, _MaxZeroNoteCt)); _CurrentReadMode = ENoteReadMode.ZeroBased; sr.Dispose(); return(ReadNotes(true)); } CLog.CSongLog.Warning("[{SongFileName}] Ignored note with length < 1 (in line {LineNr})", CLog.Params(_Song.FileName, _LineNr)); } else { ENoteType noteType; if (tag.Equals('*')) { noteType = ENoteType.Golden; } else if (tag.Equals('F') || tag.Equals('R') || tag.Equals('G')) // Read R (RAP) and G (Golden RAP) notes as Freestyle notes currently. Final implementation needed! { 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) { CLog.CSongLog.Warning("[{SongFileName}] Found more than {MaxOverlapNoteCt} overlapping notes. Using standard mode.", CLog.Params(_Song.FileName, _MaxOverlapNoteCt)); _CurrentReadMode = ENoteReadMode.OneBased; sr.Dispose(); return(ReadNotes(true)); } } CLog.CSongLog.Warning("[{SongFileName}] Ignored note for player {CurrentPlayerNumber} because it overlaps with other note (in line {LineNr})", CLog.Params(_Song.FileName, (curPlayer + 1), _LineNr)); } } } break; case '-': string[] lineBreakData = line.Split(splitChars); if (lineBreakData.Length < 1) { CLog.CSongLog.Error("[{SongFileName}] Invalid line break found (No beat) (in line {LineNr}): {LineBreakData}", CLog.Params(_Song.FileName, _LineNr, lineBreakData)); sr.Dispose(); return(false); } if (!int.TryParse(lineBreakData[0], out beat)) { CLog.CSongLog.Error("[{SongFileName}] Invalid line break found (Non-numeric value) (in line {LineNr}): {LineBreakData}", CLog.Params(_Song.FileName, _LineNr, lineBreakData)); sr.Dispose(); return(false); } if (_Song.Relative) { beat += currentBeat; if (lineBreakData.Length < 2 || !int.TryParse(lineBreakData[1], out length)) { CLog.CSongLog.Warning("[{SongFileName}] Missing line break length (in line {LineNr}):{LineBreakData}", CLog.Params(_Song.FileName, _LineNr, lineBreakData)); changesMade.NoLengthBreakCt++; currentBeat = beat; } else { currentBeat += length; } } if (lastNote != null && beat <= lastNote.EndBeat) { CLog.CSongLog.Warning("[{SongFileName}] Line break is before previous note end. Adjusted. (in line {LineNr})", CLog.Params(_Song.FileName, _LineNr)); changesMade.AjustedBreakCt++; if (_Song.Relative) { currentBeat += lastNote.EndBeat - beat + 1; } beat = lastNote.EndBeat + 1; } if (beat < 1) { CLog.CSongLog.Warning("[{SongFileName}] Ignored line break because position is < 1 (in line {LineNr})", CLog.Params(_Song.FileName, _LineNr)); changesMade.InvalidPosBreakCt++; } else { foreach (int curPlayer in player.GetSetBits()) { if (!_NewSentence(curPlayer, beat)) { CLog.CSongLog.Warning("[{SongFileName}] Ignored line break for player {CurPlayerNr} (Overlapping or duplicate) (in line {LineNr})", CLog.Params(_Song.FileName, (curPlayer + 1), _LineNr)); } } } break; default: CLog.CSongLog.Error("[{SongFileName}] Unexpected or missing character ({Tag})", CLog.Params(_Song.FileName, 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) { CLog.CSongLog.Warning("[{SongFileName}] Removed {NumEmptyLines} empty lines from P .This often indicates a problem with the line breaks in the file", CLog.Params(_Song.FileName, emptyLines)); } voice.UpdateTimings(); } } catch (Exception e) { CLog.CSongLog.Error(e, "[{SongFileName}] An unhandled exception occured: {ExceptionMessage}", CLog.Params(_Song.FileName, e.Message)); if (sr != null) { sr.Dispose(); } return(false); } sr.Dispose(); try { _Song._CalcMedley(); _Song._CheckPreview(); _Song._FindShortEnd(); _Song.NotesLoaded = true; if (_Song.IsDuet) { _Song._CheckDuet(); } } catch (Exception e) { CLog.CSongLog.Error(e, "[{SongFileName}] An unhandled exception occured: {ExceptionMessage}", CLog.Params(_Song.FileName, e.Message)); return(false); } if (changesMade.IsModified) { CLog.Warning("Automatic changes have been made to {FilePath} Please check result!\r\n{ChangesMade}", CLog.Params(filePath, changesMade)); if (CBase.Config.GetSaveModifiedSongs() == EOffOn.TR_CONFIG_ON) { string name = Path.GetFileNameWithoutExtension(_Song.FileName); _Song.Save(Path.Combine(_Song.Folder, name + ".fix")); } } return(true); }
/*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) * { * if (!_Lines[lineIndex].AddNote(note)) * return false; * if (updateTimings) * UpdateTimings(); * return true; * } * return false; * }*/ public void UpdateTimings() { if (_Lines.Count == 0) { return; } _Lines[0].StartBeat = -10000; for (int i = 1; i < _Lines.Count; i++) { CSongNote lastNote = _Lines[i - 1].LastNote; CSongNote firstNote = _Lines[i].FirstNote; if ((lastNote != null) && (firstNote != null)) { int min = lastNote.EndBeat; int max = firstNote.StartBeat; int s; switch (max - min) { case 0: s = max; break; case 1: s = max; break; case 2: s = max - 1; break; case 3: s = max - 2; break; default: s = min + 2; break; } _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 } } _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 }