Split Notes, Lines and Voices into separate files/classes
This commit is contained in:
1 parent
84fc0fcb91
commit
44c78fad19
32 files changed
+889
-926
No files matched your search
@@ -0,0 +1,63 @@
|
||||
namespace VocaluxeLib.Songs
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for all note types
|
||||
/// </summary>
|
||||
public abstract class CBaseNote
|
||||
{
|
||||
private int _Duration = 1;
|
||||
private int _Tone;
|
||||
|
||||
#region Contructors
|
||||
protected CBaseNote(CBaseNote note)
|
||||
{
|
||||
StartBeat = note.StartBeat;
|
||||
_Duration = note._Duration;
|
||||
_Tone = note._Tone;
|
||||
}
|
||||
|
||||
protected CBaseNote(int startBeat, int duration, int tone)
|
||||
{
|
||||
StartBeat = startBeat;
|
||||
Duration = duration;
|
||||
Tone = tone;
|
||||
}
|
||||
#endregion Constructors
|
||||
|
||||
#region Properties
|
||||
public int StartBeat { get; set; }
|
||||
|
||||
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 int Points
|
||||
{
|
||||
get { return PointsForBeat * Duration; }
|
||||
}
|
||||
|
||||
public abstract int PointsForBeat { get; }
|
||||
#endregion Properties
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace VocaluxeLib.Songs
|
||||
{
|
||||
public class CLineBase<T> where T : CBaseNote
|
||||
{
|
||||
protected readonly List<T> _Notes = new List<T>();
|
||||
|
||||
#region Properties
|
||||
public int FirstNoteBeat
|
||||
{
|
||||
get { return (_Notes.Count == 0) ? int.MaxValue : _Notes[0].StartBeat; }
|
||||
}
|
||||
|
||||
public int LastNoteBeat
|
||||
{
|
||||
get { return (_Notes.Count == 0) ? int.MinValue : _Notes[_Notes.Count - 1].EndBeat; }
|
||||
}
|
||||
|
||||
public int NoteCount
|
||||
{
|
||||
get { return _Notes.Count; }
|
||||
}
|
||||
|
||||
public T[] Notes
|
||||
{
|
||||
get { return _Notes.ToArray(); }
|
||||
}
|
||||
|
||||
public T FirstNote
|
||||
{
|
||||
get { return _Notes.Count > 0 ? _Notes[0] : null; }
|
||||
}
|
||||
|
||||
public T LastNote
|
||||
{
|
||||
get { return _Notes.Count > 0 ? _Notes[_Notes.Count - 1] : null; }
|
||||
}
|
||||
|
||||
public int Points
|
||||
{
|
||||
get { return _Notes.Sum(note => note.Points); }
|
||||
}
|
||||
public int BaseLine
|
||||
{
|
||||
get
|
||||
{
|
||||
int min = int.MaxValue;
|
||||
int max = int.MinValue;
|
||||
foreach (T 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
|
||||
//Find last note with StartBeat<=Beat
|
||||
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 virtual bool AddNote(T note)
|
||||
{
|
||||
if (_Notes.Count == 0)
|
||||
_Notes.Add(note);
|
||||
else
|
||||
{
|
||||
int insPos = FindPreviousNote(note.StartBeat);
|
||||
_Notes.Insert(insPos + 1, note);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool DeleteNote(int index)
|
||||
{
|
||||
if (_Notes.Count > index)
|
||||
{
|
||||
_Notes.RemoveAt(index);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool ReplaceNote(int index, T 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++;
|
||||
return false;
|
||||
}
|
||||
|
||||
public void DeleteAllNotes()
|
||||
{
|
||||
_Notes.Clear();
|
||||
}
|
||||
#endregion Methods
|
||||
}
|
||||
}
|
||||
@@ -17,9 +17,7 @@
|
||||
// */
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace VocaluxeLib.Songs
|
||||
{
|
||||
@@ -94,629 +92,4 @@ namespace VocaluxeLib.Songs
|
||||
voice.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()
|
||||
{
|
||||
StartBeat = startBeat;
|
||||
Duration = duration;
|
||||
Tone = tone;
|
||||
NoteType = ENoteType.Normal;
|
||||
Text = text;
|
||||
}
|
||||
|
||||
public CNote(int startBeat, int duration, int tone, string text, bool hit)
|
||||
: this()
|
||||
{
|
||||
StartBeat = startBeat;
|
||||
Duration = duration;
|
||||
Tone = tone;
|
||||
NoteType = ENoteType.Normal;
|
||||
Text = text;
|
||||
Hit = hit;
|
||||
}
|
||||
|
||||
public CNote(int startBeat, int duration, int tone, string text, ENoteType noteType)
|
||||
: this(startBeat, duration, tone, text)
|
||||
{
|
||||
NoteType = noteType;
|
||||
}
|
||||
|
||||
public CNote(int startBeat, int duration, int tone, string text, bool hit, ENoteType noteType)
|
||||
: this(startBeat, duration, tone, text)
|
||||
{
|
||||
NoteType = noteType;
|
||||
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 = Hit;
|
||||
|
||||
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 { return _Notes.Aggregate(String.Empty, (current, note) => current + note.Text); }
|
||||
}
|
||||
|
||||
public int NoteCount
|
||||
{
|
||||
get { return _Notes.Count; }
|
||||
}
|
||||
|
||||
public int Points
|
||||
{
|
||||
get { return _Notes.Sum(note => note.Points); }
|
||||
}
|
||||
|
||||
public CNote[] Notes
|
||||
{
|
||||
get { return _Notes.ToArray(); }
|
||||
}
|
||||
|
||||
public CNote FirstNote
|
||||
{
|
||||
get { return _Notes.Count > 0 ? _Notes[0] : null; }
|
||||
}
|
||||
|
||||
public CNote LastNote
|
||||
{
|
||||
get { return _Notes.Count > 0 ? _Notes[_Notes.Count - 1] : 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;
|
||||
}
|
||||
|
||||
//Find last note with StartBeat<=Beat
|
||||
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 CVoice
|
||||
{
|
||||
private readonly List<CLine> _Lines = new List<CLine>();
|
||||
|
||||
public CVoice() {}
|
||||
|
||||
public CVoice(CVoice voice)
|
||||
{
|
||||
foreach (CLine line in voice._Lines)
|
||||
_Lines.Add(new CLine(line));
|
||||
}
|
||||
|
||||
public CLine[] Lines
|
||||
{
|
||||
get { return _Lines.ToArray(); }
|
||||
}
|
||||
public int NumLines
|
||||
{
|
||||
get { return _Lines.Count; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Total song length in beats
|
||||
/// </summary>
|
||||
public int Length
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_Lines.Count == 0)
|
||||
return 0;
|
||||
|
||||
int startbeat = _Lines.Max(line => line.FirstNoteBeat);
|
||||
int endbeat = _Lines.Min(line => line.LastNoteBeat);
|
||||
|
||||
int result = endbeat - startbeat;
|
||||
return result > 0 ? result : 0;
|
||||
}
|
||||
}
|
||||
|
||||
public int Points
|
||||
{
|
||||
get { return _Lines.Sum(line => line.Points); }
|
||||
}
|
||||
|
||||
public int NumLinesWithPoints
|
||||
{
|
||||
get { return _Lines.Count(line => line.Points > 0f); }
|
||||
}
|
||||
|
||||
#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 {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;
|
||||
|
||||
if (_Lines.Count > 0)
|
||||
_Lines[0].StartBeat = -10000;
|
||||
|
||||
for (int i = 1; i < _Lines.Count; i++)
|
||||
{
|
||||
lastNote = _Lines[i - 1].LastNote;
|
||||
CNote 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;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -725,7 +725,7 @@ namespace VocaluxeLib.Songs
|
||||
|
||||
private bool _ParseNote(int player, ENoteType noteType, int start, int length, int tone, string text)
|
||||
{
|
||||
CNote note = new CNote(start, length, tone, text, noteType);
|
||||
CSongNote note = new CSongNote(start, length, tone, text, noteType);
|
||||
CVoice voice = Notes.GetVoice(player);
|
||||
return voice.AddNote(note);
|
||||
}
|
||||
@@ -733,7 +733,7 @@ namespace VocaluxeLib.Songs
|
||||
private void _NewSentence(int player, int start)
|
||||
{
|
||||
CVoice voice = Notes.GetVoice(player);
|
||||
CLine line = new CLine {StartBeat = start};
|
||||
CSongLine line = new CSongLine {StartBeat = start};
|
||||
voice.AddLine(line);
|
||||
}
|
||||
|
||||
@@ -933,7 +933,7 @@ namespace VocaluxeLib.Songs
|
||||
}
|
||||
|
||||
//Check if stop is in line
|
||||
foreach (CLine line in voice.Lines)
|
||||
foreach (CSongLine line in voice.Lines)
|
||||
{
|
||||
if (line.FirstNoteBeat < stop && line.LastNoteBeat > stop)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace VocaluxeLib.Songs
|
||||
{
|
||||
public class CSongLine : CLineBase<CSongNote>
|
||||
{
|
||||
private int _StartBeat = int.MinValue;
|
||||
private int _EndBeat = int.MinValue;
|
||||
|
||||
#region Constructors
|
||||
public CSongLine()
|
||||
{
|
||||
VisibleInTimeLine = true;
|
||||
}
|
||||
|
||||
public CSongLine(CSongLine line)
|
||||
{
|
||||
foreach (CSongNote note in line._Notes)
|
||||
_Notes.Add(new CSongNote(note));
|
||||
_StartBeat = line._StartBeat;
|
||||
_EndBeat = line._EndBeat;
|
||||
VisibleInTimeLine = line.VisibleInTimeLine;
|
||||
}
|
||||
#endregion Constructors
|
||||
|
||||
#region Properties
|
||||
public bool VisibleInTimeLine { get; set; }
|
||||
|
||||
public int StartBeat
|
||||
{
|
||||
get { return _StartBeat; }
|
||||
set
|
||||
{
|
||||
if (value <= FirstNoteBeat)
|
||||
_StartBeat = value;
|
||||
}
|
||||
}
|
||||
|
||||
public int EndBeat
|
||||
{
|
||||
get { return _EndBeat; }
|
||||
set
|
||||
{
|
||||
if (value >= LastNoteBeat)
|
||||
_EndBeat = value;
|
||||
}
|
||||
}
|
||||
|
||||
public string Lyrics
|
||||
{
|
||||
get { return _Notes.Aggregate(String.Empty, (current, note) => current + note.Text); }
|
||||
}
|
||||
#endregion Properties
|
||||
|
||||
#region Methods
|
||||
public override bool AddNote(CSongNote note)
|
||||
{
|
||||
if (_Notes.Count == 0)
|
||||
_Notes.Add(note);
|
||||
else
|
||||
{
|
||||
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;
|
||||
_Notes.Insert(insPos + 1, note);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void SetMedley(int startBeat, int endBeat)
|
||||
{
|
||||
foreach (CSongNote note in _Notes)
|
||||
{
|
||||
if (note.StartBeat < startBeat || note.EndBeat > endBeat)
|
||||
note.NoteType = ENoteType.Freestyle;
|
||||
}
|
||||
|
||||
VisibleInTimeLine = FirstNoteBeat >= startBeat && LastNoteBeat <= endBeat;
|
||||
}
|
||||
#endregion Methods
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
namespace VocaluxeLib.Songs
|
||||
{
|
||||
/// <summary>
|
||||
/// Class to represent a note in a song
|
||||
/// </summary>
|
||||
public class CSongNote : CBaseNote
|
||||
{
|
||||
#region Contructors
|
||||
public CSongNote(CSongNote note) : base(note)
|
||||
{
|
||||
NoteType = note.NoteType;
|
||||
Text = note.Text;
|
||||
}
|
||||
|
||||
public CSongNote(int startBeat, int duration, int tone, string text) : base(startBeat, duration, tone)
|
||||
{
|
||||
NoteType = ENoteType.Normal;
|
||||
Text = text;
|
||||
}
|
||||
|
||||
public CSongNote(int startBeat, int duration, int tone, string text, ENoteType noteType)
|
||||
: this(startBeat, duration, tone, text)
|
||||
{
|
||||
NoteType = noteType;
|
||||
}
|
||||
#endregion Constructors
|
||||
|
||||
#region Properties
|
||||
public ENoteType NoteType { get; set; }
|
||||
|
||||
public string Text { get; set; }
|
||||
|
||||
public override 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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using System.Linq;
|
||||
|
||||
namespace VocaluxeLib.Songs
|
||||
{
|
||||
public class CSungLine : CLineBase<CSungNote>
|
||||
{
|
||||
// for drawing perfect line effect
|
||||
public bool PerfectLine { get; set; }
|
||||
|
||||
public bool IsPerfect(CSongLine compareLine)
|
||||
{
|
||||
PerfectLine = _Notes.Count > 0;
|
||||
PerfectLine &= _Notes.Count == compareLine.NoteCount;
|
||||
PerfectLine &= _Notes.All(note => note.Perfect);
|
||||
return PerfectLine;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
namespace VocaluxeLib.Songs
|
||||
{
|
||||
/// <summary>
|
||||
/// Note that was sung by a player
|
||||
/// </summary>
|
||||
public class CSungNote : CBaseNote
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor for a sung note that was not hit
|
||||
/// </summary>
|
||||
/// <param name="startBeat"></param>
|
||||
/// <param name="duration"></param>
|
||||
/// <param name="tone"></param>
|
||||
public CSungNote(int startBeat, int duration, int tone) : base(startBeat, duration, tone)
|
||||
{
|
||||
Perfect = false;
|
||||
HitNote = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for a sung note that hit the given note
|
||||
/// </summary>
|
||||
/// <param name="startBeat"></param>
|
||||
/// <param name="duration"></param>
|
||||
/// <param name="tone"></param>
|
||||
/// <param name="hitNote"></param>
|
||||
public CSungNote(int startBeat, int duration, int tone, CSongNote hitNote) : this(startBeat, duration, tone)
|
||||
{
|
||||
HitNote = hitNote;
|
||||
}
|
||||
|
||||
#region Properties
|
||||
// for drawing player notes
|
||||
public bool Hit
|
||||
{
|
||||
get { return HitNote != null; }
|
||||
}
|
||||
public CSongNote HitNote { get; set; }
|
||||
// for drawing perfect note effect
|
||||
public bool Perfect { get; set; }
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
public void CheckPerfect()
|
||||
{
|
||||
bool result = HitNote != null;
|
||||
if (result)
|
||||
{
|
||||
result = (StartBeat == HitNote.StartBeat);
|
||||
result &= (EndBeat == HitNote.EndBeat);
|
||||
result &= (Tone == HitNote.Tone);
|
||||
}
|
||||
|
||||
Perfect = result;
|
||||
}
|
||||
#endregion Methods
|
||||
|
||||
public override int PointsForBeat
|
||||
{
|
||||
get { return (HitNote == null) ? 0 : HitNote.PointsForBeat; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace VocaluxeLib.Songs
|
||||
{
|
||||
public class CVoice
|
||||
{
|
||||
private readonly List<CSongLine> _Lines = new List<CSongLine>();
|
||||
|
||||
public CVoice() {}
|
||||
|
||||
public CVoice(CVoice voice)
|
||||
{
|
||||
foreach (CSongLine line in voice._Lines)
|
||||
_Lines.Add(new CSongLine(line));
|
||||
}
|
||||
|
||||
public CSongLine[] Lines
|
||||
{
|
||||
get { return _Lines.ToArray(); }
|
||||
}
|
||||
public int NumLines
|
||||
{
|
||||
get { return _Lines.Count; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Total song length in beats
|
||||
/// </summary>
|
||||
public int Length
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_Lines.Count == 0)
|
||||
return 0;
|
||||
|
||||
int startbeat = _Lines.Max(line => line.FirstNoteBeat);
|
||||
int endbeat = _Lines.Min(line => line.LastNoteBeat);
|
||||
|
||||
int result = endbeat - startbeat;
|
||||
return result > 0 ? result : 0;
|
||||
}
|
||||
}
|
||||
|
||||
public int Points
|
||||
{
|
||||
get { return _Lines.Sum(line => line.Points); }
|
||||
}
|
||||
|
||||
public int NumLinesWithPoints
|
||||
{
|
||||
get { return _Lines.Count(line => line.Points > 0f); }
|
||||
}
|
||||
|
||||
#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(CSongLine 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(CSongNote note, bool updateTimings = false)
|
||||
{
|
||||
int lineIndex = FindPreviousLine(note.StartBeat);
|
||||
if (lineIndex < 0)
|
||||
{
|
||||
//Note is before ALL lines
|
||||
CSongLine line = new CSongLine {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(CSongNote 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()
|
||||
{
|
||||
CSongNote lastNote;
|
||||
|
||||
if (_Lines.Count > 0)
|
||||
_Lines[0].StartBeat = -10000;
|
||||
|
||||
for (int i = 1; i < _Lines.Count; i++)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
lastNote = _Lines[_Lines.Count - 1].LastNote;
|
||||
if (lastNote != null)
|
||||
_Lines[_Lines.Count - 1].EndBeat = lastNote.EndBeat;
|
||||
}
|
||||
|
||||
public void SetMedley(int startBeat, int endBeat)
|
||||
{
|
||||
foreach (CSongLine line in _Lines)
|
||||
line.SetMedley(startBeat, endBeat);
|
||||
}
|
||||
#endregion Methods
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user