Files
Vocaluxe/VocaluxeLib/Menu/SingNotes/CNotes.cs
T
2013-03-22 23:17:33 +01:00

758 lines
19 KiB
C#

using System;
using System.Collections.Generic;
namespace VocaluxeLib.Menu.SingNotes
{
public class CNotes
{
private readonly List<CLines> _Lines = new List<CLines>();
public CLines[] Lines
{
get { return _Lines.ToArray(); }
}
public CNotes() {}
public int LinesCount
{
get { return _Lines.Count; }
}
public CNotes(CNotes notes)
{
foreach (CLines lines in notes._Lines)
_Lines.Add(new CLines(lines));
}
public CLines GetLines(int Index)
{
while (Index >= _Lines.Count)
_Lines.Add(new CLines());
return _Lines[Index];
}
public int GetPoints(int Index)
{
if (Index >= _Lines.Count)
return 0;
return _Lines[Index].Points;
}
public int GetNumLinesWithPoints(int Index)
{
if (Index >= _Lines.Count)
return 0;
return _Lines[Index].NumLinesWithPoints;
}
public void AddLines(CLines Lines)
{
_Lines.Add(Lines);
}
public bool ReplaceLinesAt(int Index, CLines Lines)
{
if (Index >= _Lines.Count)
return false;
_Lines[Index] = Lines;
return true;
}
public void Reset()
{
_Lines.Clear();
}
public void SetMedley(int StartBeat, int EndBeat)
{
foreach (CLines lines in _Lines)
lines.SetMedley(StartBeat, EndBeat);
}
}
public class CNote
{
private int _StartBeat;
private int _Duration;
private int _Tone;
private ENoteType _NoteType;
private string _Text;
private bool _Hit; // for drawing player notes
private bool _Perfect; // for drawing perfect note effect
#region Contructors
public CNote()
{
StartBeat = 0;
Duration = 1;
Tone = 0;
NoteType = ENoteType.Normal;
Text = String.Empty;
_Hit = false;
_Perfect = false;
}
public CNote(CNote note)
{
_StartBeat = note._StartBeat;
_Duration = note._Duration;
_Tone = note._Tone;
_NoteType = note._NoteType;
_Text = note._Text;
_Hit = note._Hit;
_Perfect = note._Perfect;
}
public CNote(int StartBeat, int Duration, int Tone, string Text)
: this()
{
this.StartBeat = StartBeat;
this.Duration = Duration;
this.Tone = Tone;
NoteType = ENoteType.Normal;
this.Text = Text;
}
public CNote(int StartBeat, int Duration, int Tone, string Text, bool Hit)
: this()
{
this.StartBeat = StartBeat;
this.Duration = Duration;
this.Tone = Tone;
NoteType = ENoteType.Normal;
this.Text = Text;
this.Hit = Hit;
}
public CNote(int StartBeat, int Duration, int Tone, string Text, ENoteType NoteType)
: this(StartBeat, Duration, Tone, Text)
{
this.NoteType = NoteType;
}
public CNote(int StartBeat, int Duration, int Tone, string Text, bool Hit, ENoteType NoteType)
: this(StartBeat, Duration, Tone, Text)
{
this.NoteType = NoteType;
this.Hit = Hit;
}
#endregion Constructors
#region Properties
public bool Hit
{
get { return _Hit; }
set { _Hit = value; }
}
public bool Perfect
{
get { return _Perfect; }
set { _Perfect = value; }
}
public int StartBeat
{
get { return _StartBeat; }
set { _StartBeat = value; }
}
public int EndBeat
{
get { return _StartBeat + _Duration - 1; }
}
public int Duration
{
get { return _Duration; }
set
{
if (value > 0)
_Duration = value;
}
}
public int Tone
{
get { return _Tone; }
set
{
if ((value >= CBase.Settings.GetToneMin()) && (value <= CBase.Settings.GetToneMax()))
_Tone = value;
}
}
public ENoteType NoteType
{
get { return _NoteType; }
set { _NoteType = value; }
}
public string Text
{
get { return _Text; }
set { _Text = value; }
}
public int Points
{
get
{
switch (_NoteType)
{
case ENoteType.Normal:
return _Duration;
case ENoteType.Golden:
return _Duration * 2;
case ENoteType.Freestyle:
return 0;
default:
return 0;
}
}
}
public int PointsForBeat
{
get
{
switch (_NoteType)
{
case ENoteType.Normal:
return 1;
case ENoteType.Golden:
return 2;
case ENoteType.Freestyle:
return 0;
default:
return 0;
}
}
}
#endregion Properties
#region Methods
public bool IsPerfect(CNote CompareNote)
{
bool Result = true;
if (!Hit)
Result = false;
if (StartBeat != CompareNote.StartBeat)
Result = false;
if (EndBeat != CompareNote.EndBeat)
Result = false;
if (Tone != CompareNote.Tone)
Result = false;
Perfect = Result;
return Result;
}
#endregion Methods
}
public class CLine
{
private int _StartBeat = int.MinValue;
private int _EndBeat = int.MinValue;
private bool _PerfectLine; // for drawing perfect line effect
private bool _VisibleInTimeLine = true;
private int _MinBeat = int.MaxValue;
private int _MaxBeat = int.MinValue;
private readonly List<CNote> _Notes = new List<CNote>();
#region Constructors
public CLine() {}
public CLine(CLine line)
{
foreach (CNote note in line._Notes)
_Notes.Add(new CNote(note));
_StartBeat = line._StartBeat;
_EndBeat = line._EndBeat;
_PerfectLine = line._PerfectLine;
_MinBeat = line._MinBeat;
_MaxBeat = line._MaxBeat;
_VisibleInTimeLine = line._VisibleInTimeLine;
}
#endregion Constructors
#region Properties
public bool VisibleInTimeLine
{
get { return _VisibleInTimeLine; }
set { _VisibleInTimeLine = value; }
}
public int StartBeat
{
get { return _StartBeat; }
set
{
if (value <= _MinBeat)
_StartBeat = value;
}
}
public int EndBeat
{
get { return _EndBeat; }
set
{
if (value >= _MaxBeat)
_EndBeat = value;
}
}
public bool PerfectLine
{
get { return _PerfectLine; }
set { _PerfectLine = value; }
}
public int FirstNoteBeat
{
get { return _MinBeat; }
}
public int LastNoteBeat
{
get { return _MaxBeat; }
}
public string Lyrics
{
get
{
string lyrics = String.Empty;
foreach (CNote note in _Notes)
lyrics += note.Text;
return lyrics;
}
}
public int NoteCount
{
get { return _Notes.Count; }
}
public int Points
{
get
{
int points = 0;
foreach (CNote note in _Notes)
points += note.Points;
return points;
}
}
public CNote[] Notes
{
get { return _Notes.ToArray(); }
}
public CNote FirstNote
{
get
{
if (_Notes.Count > 0)
return _Notes[0];
else
return null;
}
}
public CNote LastNote
{
get
{
if (_Notes.Count > 0)
return _Notes[_Notes.Count - 1];
else
return null;
}
}
public int BaseLine
{
get
{
int Min = int.MaxValue;
int Max = int.MinValue;
foreach (CNote note in _Notes)
{
if (note.Tone < Min)
Min = note.Tone;
if (note.Tone > Max)
Max = note.Tone;
}
return Min - (Max - Min) / 4;
}
}
#endregion Properties
#region Methods
public bool IsPerfect(CLine CompareLine)
{
if (_Notes.Count == 0)
return false;
if (_Notes.Count != CompareLine.NoteCount)
return false;
if (CompareLine.Points == 0)
return false;
_PerfectLine = Points == CompareLine.Points;
return _PerfectLine;
}
public int FindPreviousNote(int Beat)
{
//If no notes -> No previous note
if (_Notes.Count == 0)
return -1;
int start = 0;
int end = _Notes.Count - 1;
//Ensure that start.StartBeat<=Beat && end.StartBeat>Beat
if (_Notes[0].StartBeat > Beat)
return -1;
if (_Notes[end].StartBeat <= Beat)
return end;
//Binary search
while (end - start > 1)
{
int mid = (start + end) / 2;
if (_Notes[mid].StartBeat <= Beat)
start = mid;
else
end = mid;
}
return start;
}
public bool AddNote(CNote Note)
{
if (_Notes.Count == 0)
_Notes.Add(Note);
else
{
int insPos = FindPreviousNote(Note.StartBeat);
//Flamefire: May be a performance hit as notes are also used for sung notes
//But for song loading this might be very helpful!
//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;
* */
_Notes.Insert(insPos + 1, Note);
}
updateMinMaxBeat(Note);
return true;
}
public bool DeleteNote(int Index)
{
if (_Notes.Count > Index)
{
_Notes.RemoveAt(Index);
if (Index == 0 || Index == _Notes.Count - 1)
updateMinMaxBeat();
return true;
}
return false;
}
public bool ReplaceNote(int Index, CNote Note)
{
if (_Notes.Count > Index)
{
_Notes.RemoveAt(Index);
return AddNote(Note);
}
return false;
}
public bool IncLastNoteLength()
{
if (_Notes.Count > 0)
{
_Notes[_Notes.Count - 1].Duration++;
_MaxBeat++;
}
return false;
}
public void DeleteAllNotes()
{
_Notes.Clear();
updateMinMaxBeat();
}
public void SetMedley(int StartBeat, int EndBeat)
{
foreach (CNote note in _Notes)
{
if (note.StartBeat < StartBeat || note.EndBeat > EndBeat)
note.NoteType = ENoteType.Freestyle;
}
_VisibleInTimeLine = _MinBeat >= StartBeat && _MaxBeat <= EndBeat;
}
private void updateMinMaxBeat(CNote Note)
{
if (Note.StartBeat < _MinBeat)
_MinBeat = Note.StartBeat;
if (Note.EndBeat > _MaxBeat)
_MaxBeat = Note.EndBeat;
}
private void updateMinMaxBeat()
{
if (_Notes.Count > 0)
{
_MinBeat = _Notes[0].StartBeat;
_MaxBeat = _Notes[_Notes.Count - 1].EndBeat;
}
else
{
_MinBeat = int.MaxValue;
_MaxBeat = int.MinValue;
}
}
#endregion Methods
}
public class CLines
{
private readonly List<CLine> _Lines = new List<CLine>();
public CLines() {}
public CLines(CLines lines)
{
foreach (CLine line in lines._Lines)
_Lines.Add(new CLine(line));
}
public CLine[] Line
{
get { return _Lines.ToArray(); }
}
public int LineCount
{
get { return _Lines.Count; }
}
/// <summary>
/// Total song length in beats
/// </summary>
public int Length
{
get
{
if (_Lines.Count == 0)
return 0;
int startbeat = int.MaxValue;
for (int i = 0; i < _Lines.Count; i++)
{
if (_Lines[i].FirstNoteBeat < startbeat)
startbeat = _Lines[i].FirstNoteBeat;
}
int endbeat = int.MinValue;
for (int i = 0; i < _Lines.Count; i++)
{
if (_Lines[i].LastNoteBeat < endbeat)
endbeat = _Lines[i].LastNoteBeat;
}
int result = endbeat - startbeat;
if (result > 0)
return result;
return 0;
}
}
public int Points
{
get
{
int points = 0;
foreach (CLine line in _Lines)
points += line.Points;
return points;
}
}
public int NumLinesWithPoints
{
get
{
int num = 0;
foreach (CLine line in _Lines)
{
if (line.Points > 0f)
num++;
}
return num;
}
}
#region Methods
//Find last line with StartBeat<=Beat
public int FindPreviousLine(int Beat)
{
//If no line -> No previous line
if (_Lines.Count == 0)
return -1;
int start = 0;
int end = _Lines.Count - 1;
//Ensure that start.StartBeat<=Beat && end.StartBeat>Beat
if (_Lines[0].StartBeat > Beat)
return -1;
if (_Lines[end].StartBeat <= Beat)
return end;
//Binary search
while (end - start > 1)
{
int mid = (start + end) / 2;
if (_Lines[mid].StartBeat <= Beat)
start = mid;
else
end = mid;
}
return start;
}
public void AddLine(CLine Line)
{
if (_Lines.Count == 0)
_Lines.Add(Line);
else
{
int insPos = FindPreviousLine(Line.StartBeat);
_Lines.Insert(insPos + 1, Line);
}
}
public bool DeleteLine(int Index)
{
if (_Lines.Count > Index)
{
_Lines.RemoveAt(Index);
UpdateTimings();
return true;
}
return false;
}
public void DeleteAllLines()
{
_Lines.Clear();
}
public bool AddNote(CNote Note, bool updateTimings = false)
{
int LineIndex = FindPreviousLine(Note.StartBeat);
if (LineIndex < 0)
{
//Note is before ALL lines
CLine Line = new CLine();
Line.StartBeat = Note.StartBeat;
Line.AddNote(Note);
_Lines.Insert(0, Line);
}
else
{
if (!_Lines[LineIndex].AddNote(Note))
return false;
}
if (updateTimings)
UpdateTimings();
return true;
}
public bool InsertNote(CNote Note, int LineIndex, bool updateTimings = false)
{
if (_Lines.Count > LineIndex && _Lines[LineIndex].StartBeat <= Note.StartBeat)
{
_Lines[LineIndex].AddNote(Note);
if (updateTimings)
UpdateTimings();
return true;
}
return false;
}
public void UpdateTimings()
{
CNote LastNote, FirstNote;
int min, max, s;
if (_Lines.Count > 0)
_Lines[0].StartBeat = -10000;
for (int i = 1; i < _Lines.Count; i++)
{
LastNote = _Lines[i - 1].LastNote;
FirstNote = _Lines[i].FirstNote;
if ((LastNote != null) && (FirstNote != null))
{
min = LastNote.EndBeat;
max = FirstNote.StartBeat;
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;
}
}
LastNote = _Lines[_Lines.Count - 1].LastNote;
if (LastNote != null)
_Lines[_Lines.Count - 1].EndBeat = LastNote.EndBeat;
}
public void SetMedley(int StartBeat, int EndBeat)
{
foreach (CLine line in _Lines)
line.SetMedley(StartBeat, EndBeat);
}
#endregion Methods
}
}