Skip to content

Commit

Permalink
Merge branch 'release/2.7.0.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
paralleltree committed Nov 2, 2020
2 parents a6ffb80 + cd4aeb7 commit 907cfcc
Show file tree
Hide file tree
Showing 29 changed files with 647 additions and 147 deletions.
147 changes: 147 additions & 0 deletions Ched.Core/BarIndexCalculator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Ched.Core.Events;

namespace Ched.Core
{
/// <summary>
/// 拍子変更イベントからTick値に対応する小節位置を求めるクラスです。
/// </summary>
public class BarIndexCalculator
{
private int TicksPerBeat { get; }
private int BarTick => TicksPerBeat * 4;
private IReadOnlyCollection<TimeSignatureItem> ReversedTimeSignatures { get; }

/// <summary>
/// 時間順にソートされた有効な拍子変更イベントのコレクションを取得します。
/// </summary>
public IEnumerable<TimeSignatureItem> TimeSignatures => ReversedTimeSignatures.Reverse();

/// <summary>
/// TicksPerBeatと拍子変更イベントから<see cref="BarIndexCalculator"/>のインスタンスを初期化します。
/// </summary>
/// <param name="ticksPerBeat">譜面のTicksPerBeat</param>
/// <param name="sigs">拍子変更イベントを表す<see cref="TimeSignatureChangeEvent"/>のリスト</param>
public BarIndexCalculator(int ticksPerBeat, IEnumerable<TimeSignatureChangeEvent> sigs)
{
TicksPerBeat = ticksPerBeat;
var ordered = sigs.OrderBy(p => p.Tick).ToList();
var dic = new SortedDictionary<int, TimeSignatureItem>();
int pos = 0;
int barIndex = 0;

for (int i = 0; i < ordered.Count; i++)
{
// 小節先頭に配置されていないイベント
if (pos != ordered[i].Tick) throw new InvalidTimeSignatureException($"TimeSignatureChangeEvent does not align at the head of bars (Tick: {ordered[i].Tick}).", ordered[i].Tick);
var item = new TimeSignatureItem(barIndex, ordered[i]);

// 時間逆順で追加
if (dic.ContainsKey(-pos)) throw new InvalidTimeSignatureException($"TimeSignatureChangeEvents duplicated (Tick: {ordered[i].Tick}).", ordered[i].Tick);
else dic.Add(-pos, item);

if (i < ordered.Count - 1)
{
int barLength = BarTick * ordered[i].Numerator / ordered[i].Denominator;
int duration = ordered[i + 1].Tick - pos;
pos += duration / barLength * barLength;
barIndex += duration / barLength;
}
}

ReversedTimeSignatures = dic.Values.ToList();
}

/// <summary>
/// 指定のTickに対応する小節位置を取得します。
/// </summary>
/// <param name="tick">小節位置を取得するTick</param>
/// <returns>Tickに対応する小節位置を表す<see cref="BarPosition"/></returns>
public BarPosition GetBarPositionFromTick(int tick)
{
foreach (var item in ReversedTimeSignatures)
{
if (tick < item.StartTick) continue;
var sig = item.TimeSignature;
int barLength = BarTick * sig.Numerator / sig.Denominator;
int ticksFromSignature = tick - item.StartTick;
int barsCount = ticksFromSignature / barLength;
int barIndex = item.StartBarIndex + barsCount;
int tickOffset = ticksFromSignature - barsCount * barLength;
return new BarPosition(barIndex, tickOffset);
}

throw new InvalidOperationException();
}

/// <summary>
/// 指定の小節に対応する拍子を取得します。
/// </summary>
/// <param name="barIndex">拍子を求める小節位置。このパラメータは0-basedです。</param>
/// <returns>小節位置に対応する拍子を表す<see cref="TimeSignatureChangeEvent"/></returns>
public TimeSignatureChangeEvent GetTimeSignatureFromBarIndex(int barIndex)
{
foreach (var item in ReversedTimeSignatures)
{
if (barIndex < item.StartBarIndex) continue;
return item.TimeSignature;
}

throw new InvalidOperationException();
}

/// <summary>
/// Tickに対応する小節位置を表します。
/// </summary>
public class BarPosition
{
/// <summary>
/// 小節のインデックスを取得します。このフィールドは0-basedです。
/// </summary>
public int BarIndex { get; }

/// <summary>
/// 小節におけるTickのオフセットを表します。
/// </summary>
public int TickOffset { get; }

public BarPosition(int barIndex, int tickOffset)
{
BarIndex = barIndex;
TickOffset = tickOffset;
}
}

/// <summary>
/// 拍子変更イベントに対応するTick位置と小節位置を表すクラスです。
/// </summary>
public class TimeSignatureItem
{
/// <summary>
/// 拍子変更イベントに対応するTick位置を取得します。
/// </summary>
public int StartTick => TimeSignature.Tick;

/// <summary>
/// 拍子変更イベントに対応する小節位置を取得します。このフィールドは0-basedです。
/// </summary>
public int StartBarIndex { get; }

/// <summary>
/// この<see cref="TimeSignatureItem"/>に関連付けられた拍子変更イベントを取得します。
/// </summary>
public TimeSignatureChangeEvent TimeSignature { get; }

public TimeSignatureItem(int startBarIndex, TimeSignatureChangeEvent timeSignature)
{
StartBarIndex = startBarIndex;
TimeSignature = timeSignature;
}
}
}
}
2 changes: 2 additions & 0 deletions Ched.Core/Ched.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,13 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="BarIndexCalculator.cs" />
<Compile Include="Constants.cs" />
<Compile Include="EventCollection.cs" />
<Compile Include="Events\BPMChangeEvent.cs" />
<Compile Include="Events\EventBase.cs" />
<Compile Include="Events\HighSpeedChangeEvent.cs" />
<Compile Include="Events\InvalidTimeSignatureException.cs" />
<Compile Include="Events\TimeSignatureChangeEvent.cs" />
<Compile Include="NoteCollection.cs" />
<Compile Include="Notes\Air.cs" />
Expand Down
5 changes: 5 additions & 0 deletions Ched.Core/EventCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ public List<HighSpeedChangeEvent> HighSpeedChangeEvents
set { highSpeedChangeEvents = value; }
}

public IEnumerable<EventBase> AllEvents =>
BPMChangeEvents.Cast<EventBase>()
.Concat(TimeSignatureChangeEvents)
.Concat(HighSpeedChangeEvents);

public void UpdateTicksPerBeat(double factor)
{
var events = BPMChangeEvents.Cast<EventBase>()
Expand Down
2 changes: 2 additions & 0 deletions Ched.Core/Events/BPMChangeEvent.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
Expand All @@ -10,6 +11,7 @@ namespace Ched.Core.Events
/// BPMの変更イベントを表すクラスです。
/// </summary>
[Newtonsoft.Json.JsonObject(Newtonsoft.Json.MemberSerialization.OptIn)]
[DebuggerDisplay("Tick = {Tick}, Value = {BPM}")]
public class BPMChangeEvent : EventBase
{
[Newtonsoft.Json.JsonProperty]
Expand Down
6 changes: 6 additions & 0 deletions Ched.Core/Events/EventBase.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
Expand All @@ -10,16 +11,21 @@ namespace Ched.Core.Events
/// 譜面におけるイベントを表すクラスです。
/// </summary>
[Newtonsoft.Json.JsonObject(Newtonsoft.Json.MemberSerialization.OptIn)]
[DebuggerDisplay("Tick = {Tick}")]
public abstract class EventBase
{
[Newtonsoft.Json.JsonProperty]
private int tick;

/// <summary>
/// このイベントの位置を表すTick値を取得、設定します。
/// </summary>
public int Tick
{
get { return tick; }
set
{
if (value < 0) throw new ArgumentOutOfRangeException("value", "Tick must be greater than or equal to 0.");
tick = value;
}
}
Expand Down
2 changes: 2 additions & 0 deletions Ched.Core/Events/HighSpeedChangeEvent.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
Expand All @@ -10,6 +11,7 @@ namespace Ched.Core.Events
/// ハイスピードの変更を表すクラスです。
/// </summary>
[Newtonsoft.Json.JsonObject(Newtonsoft.Json.MemberSerialization.OptIn)]
[DebuggerDisplay("Tick = {Tick}, Value = {SpeedRatio}")]
public class HighSpeedChangeEvent : EventBase
{
[Newtonsoft.Json.JsonProperty]
Expand Down
58 changes: 58 additions & 0 deletions Ched.Core/Events/InvalidTimeSignatureException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;

namespace Ched.Core.Events
{
/// <summary>
/// 拍子定義が無効な場合にスローされる例外です。
/// </summary>
[Serializable]
public class InvalidTimeSignatureException : Exception
{
private static readonly string TickPropertyValue = "tick";

/// <summary>
/// 無効な拍子定義の位置を表すTick値を取得します。
/// </summary>
public int Tick { get; }

public InvalidTimeSignatureException() : base()
{
}

public InvalidTimeSignatureException(string message) : base(message)
{
}

public InvalidTimeSignatureException(string message, Exception inner) : base(message, inner)
{
}

public InvalidTimeSignatureException(string message, int tick) : this(message, tick, null)
{
}

public InvalidTimeSignatureException(string message, int tick, Exception innerException) : base(message, innerException)
{
Tick = tick;
}

protected InvalidTimeSignatureException(SerializationInfo info, StreamingContext context) : base(info, context)
{
if (info == null) return;
Tick = info.GetInt32(TickPropertyValue);
}

public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
if (info == null) return;

info.AddValue(TickPropertyValue, Tick);
}
}
}
2 changes: 2 additions & 0 deletions Ched.Core/Events/TimeSignatureChangeEvent.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
Expand All @@ -10,6 +11,7 @@ namespace Ched.Core.Events
/// 拍子の変更を表します。
/// </summary>
[Newtonsoft.Json.JsonObject(Newtonsoft.Json.MemberSerialization.OptIn)]
[DebuggerDisplay("Tick = {Tick}, Value = {Numerator} / {Denominator}")]
public class TimeSignatureChangeEvent : EventBase
{
[Newtonsoft.Json.JsonProperty]
Expand Down
16 changes: 16 additions & 0 deletions Ched.Core/Notes/AirAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,18 @@ public class AirAction : LongNoteBase
public IAirable ParentNote { get { return parentNote; } }
public override int StartTick { get { return ParentNote.Tick; } }

/// <summary>
/// 親<see cref="IAirable"/>オブジェクトを持たない<see cref="AirAction"/>の新しいインスタンスを初期化します。
/// </summary>
/// <remarks>このコンストラクタはシリアライザ用に存在します。</remarks>
public AirAction()
{
}

/// <summary>
/// 指定の<see cref="IAirable"/>を親とする<see cref="AirAction"/>の新しいインスタンスを初期化します。
/// </summary>
/// <param name="parent">この<see cref="AirAction"/>の親となる<see cref="IAirable"/>オブジェクト</param>
public AirAction(IAirable parent)
{
parentNote = parent;
Expand Down Expand Up @@ -49,6 +61,10 @@ public int Offset
}
}

/// <summary>
/// 指定の<see cref="AirAction"/>を親とする<see cref="ActionNote"/>の新しいインスタンスを初期化します。
/// </summary>
/// <param name="parent">この<see cref="ActionNote"/>の親となる<see cref="AirAction"/>オブジェクト</param>
public ActionNote(AirAction parent)
{
parentNote = parent;
Expand Down
2 changes: 1 addition & 1 deletion Ched.Core/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@
[assembly: ComVisible(false)]
[assembly: Guid("5a5ff947-79dc-4352-94d5-eec14065f93a")]

[assembly: AssemblyVersion("2.6.1.0")]
[assembly: AssemblyVersion("2.7.0.0")]
6 changes: 0 additions & 6 deletions Ched.Core/Score.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,6 @@ public void UpdateTicksPerBeat(int value)
public Score Clone()
{
var score = Newtonsoft.Json.JsonConvert.DeserializeObject<Score>(Newtonsoft.Json.JsonConvert.SerializeObject(this, ScoreBook.SerializerSettings));
foreach (var note in score.Notes.AirActions)
{
var restored = new List<Notes.AirAction.ActionNote>(note.ActionNotes.Select(p => new Notes.AirAction.ActionNote(note) { Offset = p.Offset }));
note.ActionNotes.Clear();
note.ActionNotes.AddRange(restored);
}
return score;
}
}
Expand Down
Loading

0 comments on commit 907cfcc

Please sign in to comment.