public void Run(IScorePluginArgs args) { var form = new ShiftTimeSelectionForm(); if (form.ShowDialog() != DialogResult.OK) { return; } if (form.CountValue == 0) { return; } var score = args.GetCurrentScore(); int origin = args.GetSelectedRange().StartTick; BarIndexCalculator barIndexCalculator; try { barIndexCalculator = new BarIndexCalculator(score.TicksPerBeat, score.Events.TimeSignatureChangeEvents); } catch (InvalidTimeSignatureException ex) { int beatAt = ex.Tick / score.TicksPerBeat + 1; MessageBox.Show(string.Format(ErrorStrings.InvalidTimeSignature, beatAt), DisplayName, MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } var barIndex = barIndexCalculator.GetBarPositionFromTick(origin).BarIndex; var sig = barIndexCalculator.GetTimeSignatureFromBarIndex(barIndex); int offset = 4 * score.TicksPerBeat * form.CountValue * (form.DurationType == DurationType.Bar ? sig.Numerator : 1) / sig.Denominator; var(heading, targets) = Partition(score.Events.AllEvents.Where(p => p.Tick > 0), p => p.Tick <= origin); if (targets.Count == 0) { return; } int firstEventTick = targets.Min(p => p.Tick); int lowerLimitTick = heading.Count == 0 ? 0 : heading.Max(p => p.Tick); if (offset < 0 && firstEventTick + offset <= lowerLimitTick) { MessageBox.Show("移動対象のイベントが先行するイベントを追い越すため、移動は実行されません。", DisplayName, MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } foreach (var item in targets) { item.Tick += offset; } args.UpdateScore(score); }
public void Export(string path, ScoreBook book) { // TODO: コンストラクタに移してreadonlyにする ScoreBook = book; BarIndexCalculator = new BarIndexCalculator(book.Score.TicksPerBeat, book.Score.Events.TimeSignatureChangeEvents); SusArgs args = CustomArgs; BarIndexOffset = args.HasPaddingBar ? 1 : 0; var notes = book.Score.Notes; using (var writer = new StreamWriter(path)) { writer.WriteLine("This file was generated by Ched {0}.", System.Reflection.Assembly.GetEntryAssembly().GetName().Version.ToString()); writer.WriteLine("#TITLE \"{0}\"", book.Title); writer.WriteLine("#ARTIST \"{0}\"", book.ArtistName); writer.WriteLine("#DESIGNER \"{0}\"", book.NotesDesignerName); writer.WriteLine("#DIFFICULTY {0}", (int)args.PlayDifficulty + (string.IsNullOrEmpty(args.ExtendedDifficulty) ? "" : ":" + args.ExtendedDifficulty)); writer.WriteLine("#PLAYLEVEL {0}", args.PlayLevel); writer.WriteLine("#SONGID \"{0}\"", args.SongId); writer.WriteLine("#WAVE \"{0}\"", args.SoundFileName); writer.WriteLine("#WAVEOFFSET {0}", args.SoundOffset); writer.WriteLine("#JACKET \"{0}\"", args.JacketFilePath); writer.WriteLine(); if (!string.IsNullOrEmpty(args.AdditionalData)) { writer.WriteLine(args.AdditionalData); writer.WriteLine(); } writer.WriteLine("#REQUEST \"ticks_per_beat {0}\"", book.Score.TicksPerBeat); writer.WriteLine(); var timeSignatures = BarIndexCalculator.TimeSignatures.Select(p => new SusDataLine(p.StartBarIndex, barIndex => string.Format("#{0:000}02: {1}", barIndex, 4f * p.TimeSignature.Numerator / p.TimeSignature.Denominator), p.StartBarIndex == 0)); WriteLinesWithOffset(writer, timeSignatures); writer.WriteLine(); var bpmlist = book.Score.Events.BPMChangeEvents .GroupBy(p => p.BPM) .SelectMany((p, i) => p.Select(q => new { Index = i, Value = q, BarPosition = BarIndexCalculator.GetBarPositionFromTick(q.Tick) })) .ToList(); if (bpmlist.Count >= 36 * 36) { throw new ArgumentException("BPM定義数が上限を超えました。"); } var bpmIdentifiers = EnumerateIdentifiers(2).Skip(1).Take(bpmlist.Count).ToList(); foreach (var item in bpmlist.GroupBy(p => p.Index).Select(p => p.First())) { writer.WriteLine("#BPM{0}: {1}", bpmIdentifiers[item.Index], item.Value.BPM); } // 小節オフセット追加用に初期BPM定義だけ1行に分離 var bpmChanges = bpmlist.GroupBy(p => p.Value.Tick == 0).SelectMany(p => p.GroupBy(q => q.BarPosition.BarIndex).Select(eventInBar => { var sig = BarIndexCalculator.GetTimeSignatureFromBarIndex(eventInBar.Key); int barLength = StandardBarTick * sig.Numerator / sig.Denominator; var items = eventInBar.Select(q => (q.BarPosition.TickOffset, bpmIdentifiers[q.Index])); return(new SusDataLine(eventInBar.Key, barIndex => string.Format("#{0:000}08: {1}", barIndex, GenerateLineData(barLength, items)), p.Key)); })); WriteLinesWithOffset(writer, bpmChanges); writer.WriteLine(); var speeds = book.Score.Events.HighSpeedChangeEvents.Select(p => { var barPos = BarIndexCalculator.GetBarPositionFromTick(p.Tick); return(string.Format("{0}'{1}:{2}", barPos.BarIndex + (p.Tick == 0 ? 0 : BarIndexOffset), barPos.TickOffset, p.SpeedRatio)); }); writer.WriteLine("#TIL00: \"{0}\"", string.Join(", ", speeds)); writer.WriteLine("#HISPEED 00"); writer.WriteLine("#MEASUREHS 00"); writer.WriteLine(); var shortNotes = notes.Taps.Cast <TappableBase>().Select(p => new { Type = '1', Note = p }) .Concat(notes.ExTaps.Cast <TappableBase>().Select(p => new { Type = '2', Note = p })) .Concat(notes.Flicks.Cast <TappableBase>().Select(p => new { Type = '3', Note = p })) .Concat(notes.Damages.Cast <TappableBase>().Select(p => new { Type = '4', Note = p })) .Select(p => (p.Note.Tick, p.Note.LaneIndex, p.Type + ToLaneWidthString(p.Note.Width))); WriteLinesWithOffset(writer, GetShortNoteLines("1", shortNotes)); writer.WriteLine(); var airs = notes.Airs.Select(p => { string type = ""; switch (p.HorizontalDirection) { case HorizontalAirDirection.Center: type = p.VerticalDirection == VerticalAirDirection.Up ? "1" : "2"; break; case HorizontalAirDirection.Left: type = p.VerticalDirection == VerticalAirDirection.Up ? "3" : "5"; break; case HorizontalAirDirection.Right: type = p.VerticalDirection == VerticalAirDirection.Up ? "4" : "6"; break; } return(p.Tick, p.LaneIndex, type + ToLaneWidthString(p.Width)); }); WriteLinesWithOffset(writer, GetShortNoteLines("5", airs)); writer.WriteLine(); var identifier = new IdentifierAllocationManager(); var holds = book.Score.Notes.Holds .OrderBy(p => p.StartTick) .Select(p => new { Identifier = identifier.Allocate(p.StartTick, p.Duration), StartTick = p.StartTick, EndTick = p.StartTick + p.Duration, Width = p.Width, LaneIndex = p.LaneIndex }) .SelectMany(hold => { var items = new[] { (hold.StartTick, hold.LaneIndex, "1" + ToLaneWidthString(hold.Width)), (hold.EndTick, hold.LaneIndex, "2" + ToLaneWidthString(hold.Width)) }; return(GetLongNoteLines("2", hold.Identifier.ToString(), items)); });
public void Export(IScoreBookExportPluginArgs args) { var book = args.GetScoreBook(); var buffer = new List <Tuple <int, string> >(); var bars = new BarIndexCalculator(book.Score.TicksPerBeat, book.Score.Events.TimeSignatureChangeEvents); var bpms = book.Score.Events.BPMChangeEvents.OrderBy(p => p.Tick).ToList(); if (bpms[0].Tick == 0) { buffer.Add(Tuple.Create(0, $"*0/0/4,bpm,{bpms[0].BPM}")); } foreach (var bpm in bpms.Skip(1)) { buffer.Add(Tuple.Create(bpm.Tick, $"{GetTime(bpm.Tick)},bpm,{bpm.BPM}")); } var sigs = book.Score.Events.TimeSignatureChangeEvents.OrderBy(p => p.Tick).ToList(); if (sigs[0].Tick == 0) { buffer.Add(Tuple.Create(0, $"*0/0/4,beat,{sigs[0].Numerator}/{sigs[0].Denominator}")); } foreach (var sig in sigs.Skip(1)) { buffer.Add(Tuple.Create(sig.Tick, $"{GetTime(sig.Tick)},beat,{sig.Numerator}/{sig.Denominator}")); } var highSpeeds = book.Score.Events.HighSpeedChangeEvents.OrderBy(p => p.Tick).ToList(); foreach (var highSpeed in highSpeeds) { buffer.Add(Tuple.Create(highSpeed.Tick, $"{GetTime(highSpeed.Tick)},hispeed,{highSpeed.SpeedRatio}")); } // フィールド var sides = new[] { book.Score.Field.Left, book.Score.Field.Right }; var stepTicks = sides.SelectMany(p => p.FieldWall.Points.Select(q => q.Tick)); int li = 0; int ri = 0; var leftSteps = book.Score.Field.Left.FieldWall.Points.OrderBy(p => p.Tick).ToList(); var rightSteps = book.Score.Field.Right.FieldWall.Points.OrderBy(p => p.Tick).ToList(); foreach (var tick in stepTicks.Distinct().OrderBy(p => p)) { if (li < leftSteps.Count - 1) { if (tick >= leftSteps[li + 1].Tick) { li++; } } if (ri < rightSteps.Count - 1) { if (tick >= rightSteps[ri + 1].Tick) { ri++; } } int left = li < leftSteps.Count - 1 ? GetInterpolated(leftSteps[li], leftSteps[li + 1], tick) : GetOffset(leftSteps[li].LaneOffset); int right = ri < rightSteps.Count - 1 ? GetInterpolated(rightSteps[ri], rightSteps[ri + 1], tick) : GetOffset(rightSteps[ri].LaneOffset); buffer.Add(Tuple.Create(tick, $"{GetTime(tick)},fieldset,{left},{right},,")); } WriteField(book.Score.Field.Left, "L"); WriteField(book.Score.Field.Right, "R"); var surfaceLanes = book.Score.SurfaceLanes.Select(p => new { MinTick = p.Points.Min(q => q.Tick), MaxTick = p.Points.Max(q => q.Tick), Value = p }).OrderBy(p => p.MinTick).ToList(); var allocator = new IdentifierAllocator(Enumerable.Range(0, 30).Select(p => p.ToString())); foreach (var lane in surfaceLanes) { var laneNumber = allocator.Allocate(lane.MinTick, lane.MaxTick - lane.MinTick); var points = lane.Value.Points.OrderBy(p => p.Tick).ToList(); int color = lane.Value.LaneColor == SurfaceLaneColor.Red ? 0 : lane.Value.LaneColor == SurfaceLaneColor.Green ? 1 : 2; buffer.Add(Tuple.Create(points[0].Tick, $"{GetTime(points[0].Tick)},laneset,{laneNumber},{color},{GetOffset(points[0].LaneOffset)}")); foreach (var point in points.Skip(1).Take(points.Count - 2)) { buffer.Add(Tuple.Create(point.Tick, $"{GetTime(point.Tick)},lanepos,{laneNumber},{GetOffset(point.LaneOffset)},")); } foreach (var note in lane.Value.Notes.OrderBy(p => p.TickRange.StartTick)) { if (note.TickRange.Duration == 0) { // TAP buffer.Add(Tuple.Create(note.TickRange.StartTick, $"{GetTime(note.TickRange.StartTick)},{(note.IsCritical ? "extap" : "tap")},{laneNumber}")); } else { // HOLD buffer.Add(Tuple.Create(note.TickRange.StartTick, $"{GetTime(note.TickRange.StartTick)},{(note.IsCritical ? "exholdset" : "holdset")},{laneNumber}")); buffer.Add(Tuple.Create(note.TickRange.EndTick, $"{GetTime(note.TickRange.EndTick)},holdend,{laneNumber}")); } } buffer.Add(Tuple.Create(points[points.Count - 1].Tick, $"{GetTime(points[points.Count - 1].Tick)},laneend,{laneNumber},{GetOffset(points[points.Count - 1].LaneOffset)},")); } foreach (var flick in book.Score.Flicks) { string direction = flick.Direction == HorizontalDirection.Left ? "left" : "right"; buffer.Add(Tuple.Create(flick.Position.Tick, $"{GetTime(flick.Position.Tick)},{(flick.IsCritical ? "exflick" : "flick")},{direction},{GetOffset(flick.Position.LaneOffset)}")); } foreach (var bell in book.Score.Bells) { buffer.Add(Tuple.Create(bell.Position.Tick, $"{GetTime(bell.Position.Tick)},heal,{GetOffset(bell.Position.LaneOffset)}")); } foreach (var bullet in book.Score.Bullets) { buffer.Add(Tuple.Create(bullet.Position.Tick, $"{GetTime(bullet.Position.Tick)},shot,{GetOffset(bullet.Position.LaneOffset)},1,1")); } using (var writer = new StreamWriter(args.OutputPath, false, Encoding.GetEncoding("shift-jis"))) { writer.WriteLine($"#title {book.Title}"); writer.WriteLine($"#artist {book.ArtistName}"); writer.WriteLine($"#notes {book.NotesDesignerName}"); writer.WriteLine("#datend"); foreach (var line in buffer.OrderBy(p => p.Item1)) { writer.WriteLine(line.Item2); } writer.WriteLine("+0/4,fin"); } void WriteField(FieldSide fs, string side) { foreach (var guarded in fs.FieldWall.GuardedSections) { buffer.Add(Tuple.Create(guarded.StartTick, $"{GetTime(guarded.StartTick)},setwall,{side}")); buffer.Add(Tuple.Create(guarded.EndTick, $"{GetTime(guarded.EndTick)},delwall,{side}")); } foreach (var guide in fs.SideLanes.Select(p => p.ValidRange)) { buffer.Add(Tuple.Create(guide.StartTick, $"{GetTime(guide.StartTick)},setnoti,{side}")); buffer.Add(Tuple.Create(guide.EndTick, $"{GetTime(guide.EndTick)},delnoti,{side}")); } foreach (var note in fs.SideLanes.SelectMany(p => p.Notes)) { if (note.TickRange.Duration == 0) { buffer.Add(Tuple.Create(note.TickRange.StartTick, $"{GetTime(note.TickRange.StartTick)},{(note.IsCritical ? "extap" : "tap")},{side}")); } else { buffer.Add(Tuple.Create(note.TickRange.StartTick, $"{GetTime(note.TickRange.StartTick)},{(note.IsCritical ? "exholdset" : "holdset")},{side}")); buffer.Add(Tuple.Create(note.TickRange.EndTick, $"{GetTime(note.TickRange.EndTick)},holdend,{side}")); } } } int GetOffset(int offset) { return(480 * offset / book.Score.HalfHorizontalResolution); } int GetInterpolated(FieldPoint first, FieldPoint second, int posTick) { float rate = (float)(posTick - first.Tick) / (second.Tick - first.Tick); return((int)(GetOffset(first.LaneOffset) + GetOffset(second.LaneOffset - first.LaneOffset) * rate)); } string GetTime(int tick) { var pos = bars.GetBarPositionFromTick(tick); int gcd = GetGcd(bars.BarTick, pos.TickOffset); return($"*{pos.BarIndex + 1}/{pos.TickOffset / gcd}/{bars.BarTick / gcd}"); } }
public void Export(string path, ScoreBook book) { SusArgs args = CustomArgs; var notes = book.Score.Notes; using (var writer = new StreamWriter(path)) { writer.WriteLine("This file was generated by Ched {0}.", System.Reflection.Assembly.GetEntryAssembly().GetName().Version.ToString()); writer.WriteLine("#TITLE \"{0}\"", book.Title); writer.WriteLine("#ARTIST \"{0}\"", book.ArtistName); writer.WriteLine("#DESIGNER \"{0}\"", book.NotesDesignerName); writer.WriteLine("#DIFFICULTY {0}", (int)args.PlayDifficulty + (string.IsNullOrEmpty(args.ExtendedDifficulty) ? "" : ":" + args.ExtendedDifficulty)); writer.WriteLine("#PLAYLEVEL {0}", args.PlayLevel); writer.WriteLine("#SONGID \"{0}\"", args.SongId); writer.WriteLine("#WAVE \"{0}\"", args.SoundFileName); writer.WriteLine("#WAVEOFFSET {0}", args.SoundOffset); writer.WriteLine("#JACKET \"{0}\"", args.JacketFilePath); writer.WriteLine(); writer.WriteLine("#REQUEST \"ticks_per_beat {0}\"", book.Score.TicksPerBeat); writer.WriteLine(); int barTick = book.Score.TicksPerBeat * 4; var barIndexCalculator = new BarIndexCalculator(barTick, book.Score.Events.TimeSignatureChangeEvents, args.HasPaddingBar); foreach (var item in barIndexCalculator.TimeSignatures) { writer.WriteLine("#{0:000}02: {1}", item.StartBarIndex + (args.HasPaddingBar && item.StartBarIndex == 1 ? -1 : 0), 4f * item.TimeSignature.Numerator / item.TimeSignature.Denominator); } writer.WriteLine(); var bpmlist = book.Score.Events.BPMChangeEvents .GroupBy(p => p.BPM) .SelectMany((p, i) => p.Select(q => new { Index = i, Value = q, BarPosition = barIndexCalculator.GetBarPositionFromTick(q.Tick) })) .ToList(); if (bpmlist.Count >= 36 * 36) { throw new ArgumentException("BPM定義数が上限を超えました。"); } var bpmIdentifiers = EnumerateIdentifiers(2).Skip(1).Take(bpmlist.Count).ToList(); foreach (var item in bpmlist) { writer.WriteLine("#BPM{0}: {1}", bpmIdentifiers[item.Index], item.Value.BPM); } if (args.HasPaddingBar) { writer.WriteLine("#{0:000}08: {1:x2}", 0, bpmIdentifiers[bpmlist.OrderBy(p => p.Value.Tick).First().Index]); } foreach (var eventInBar in bpmlist.GroupBy(p => p.BarPosition.BarIndex)) { var sig = barIndexCalculator.GetTimeSignatureFromBarIndex(eventInBar.Key); int barLength = barTick * sig.Numerator / sig.Denominator; var dic = eventInBar.ToDictionary(p => p.BarPosition.TickOffset, p => p); int gcd = eventInBar.Select(p => p.BarPosition.TickOffset).Aggregate(barLength, (p, q) => GetGcd(p, q)); writer.Write("#{0:000}08: ", eventInBar.Key); for (int i = 0; i *gcd < barLength; i++) { int tickOffset = i * gcd; writer.Write(dic.ContainsKey(tickOffset) ? bpmIdentifiers[dic[tickOffset].Index] : "00"); } writer.WriteLine(); } writer.WriteLine(); var speeds = book.Score.Events.HighSpeedChangeEvents.Select(p => { var barPos = barIndexCalculator.GetBarPositionFromTick(p.Tick); return(string.Format("{0}'{1}:{2}", args.HasPaddingBar && barPos.BarIndex == 1 && barPos.TickOffset == 0 ? 0 : barPos.BarIndex, barPos.TickOffset, p.SpeedRatio)); }); writer.WriteLine("#TIL00: \"{0}\"", string.Join(", ", speeds)); writer.WriteLine("#HISPEED 00"); writer.WriteLine(); var shortNotes = notes.Taps.Cast <TappableBase>().Select(p => new { Type = '1', Note = p }) .Concat(notes.ExTaps.Cast <TappableBase>().Select(p => new { Type = '2', Note = p })) .Concat(notes.Flicks.Cast <TappableBase>().Select(p => new { Type = '3', Note = p })) .Concat(notes.Damages.Cast <TappableBase>().Select(p => new { Type = '4', Note = p })) .Select(p => new { BarPosition = barIndexCalculator.GetBarPositionFromTick(p.Note.Tick), LaneIndex = p.Note.LaneIndex, Width = p.Note.Width, Type = p.Type }); foreach (var notesInBar in shortNotes.GroupBy(p => p.BarPosition.BarIndex)) { foreach (var notesInLane in notesInBar.GroupBy(p => p.LaneIndex)) { var sig = barIndexCalculator.GetTimeSignatureFromBarIndex(notesInBar.Key); int barLength = barTick * sig.Numerator / sig.Denominator; var offsetList = notesInLane.GroupBy(p => p.BarPosition.TickOffset).Select(p => p.ToList()); var separatedNotes = Enumerable.Range(0, offsetList.Max(p => p.Count)).Select(p => offsetList.Where(q => q.Count >= p + 1).Select(q => q[p])); foreach (var dic in separatedNotes.Select(p => p.ToDictionary(q => q.BarPosition.TickOffset, q => q))) { int gcd = dic.Values.Select(p => p.BarPosition.TickOffset).Aggregate(barLength, (p, q) => GetGcd(p, q)); writer.Write("#{0:000}1{1}:", notesInBar.Key, notesInLane.Key.ToString("x")); for (int i = 0; i *gcd < barLength; i++) { int tickOffset = i * gcd; writer.Write(dic.ContainsKey(tickOffset) ? dic[tickOffset].Type + ToLaneWidthString(dic[tickOffset].Width) : "00"); } writer.WriteLine(); } } } var airs = notes.Airs.Select(p => { string type = ""; switch (p.HorizontalDirection) { case HorizontalAirDirection.Center: type = p.VerticalDirection == VerticalAirDirection.Up ? "1" : "2"; break; case HorizontalAirDirection.Left: type = p.VerticalDirection == VerticalAirDirection.Up ? "3" : "5"; break; case HorizontalAirDirection.Right: type = p.VerticalDirection == VerticalAirDirection.Up ? "4" : "6"; break; } return(new { BarPosition = barIndexCalculator.GetBarPositionFromTick(p.Tick), LaneIndex = p.LaneIndex, Type = type, Width = p.Width }); }); foreach (var airsInBar in airs.GroupBy(p => p.BarPosition.BarIndex)) { foreach (var airsInLane in airsInBar.GroupBy(p => p.LaneIndex)) { var sig = barIndexCalculator.GetTimeSignatureFromBarIndex(airsInBar.Key); int barLength = barTick * sig.Numerator / sig.Denominator; var offsetList = airsInLane.GroupBy(p => p.BarPosition.TickOffset).Select(p => p.ToList()); var separatedNotes = Enumerable.Range(0, offsetList.Max(p => p.Count)).Select(p => offsetList.Where(q => q.Count >= p + 1).Select(q => q[p])); foreach (var dic in separatedNotes.Select(p => p.ToDictionary(q => q.BarPosition.TickOffset, q => q))) { int gcd = dic.Values.Select(p => p.BarPosition.TickOffset).Aggregate(barLength, (p, q) => GetGcd(p, q)); writer.Write("#{0:000}5{1}:", airsInBar.Key, airsInLane.Key.ToString("x")); for (int i = 0; i *gcd < barLength; i++) { int tickOffset = i * gcd; writer.Write(dic.ContainsKey(tickOffset) ? dic[tickOffset].Type + ToLaneWidthString(dic[tickOffset].Width) : "00"); } writer.WriteLine(); } } } var identifier = new IdentifierAllocationManager(); var holds = book.Score.Notes.Holds .OrderBy(p => p.StartTick) .Select(p => new { Identifier = identifier.Allocate(p.StartTick, p.Duration), StartTick = p.StartTick, EndTick = p.StartTick + p.Duration, Width = p.Width, LaneIndex = p.LaneIndex }); foreach (var hold in holds) { var startBarPosition = barIndexCalculator.GetBarPositionFromTick(hold.StartTick); var endBarPosition = barIndexCalculator.GetBarPositionFromTick(hold.EndTick); if (startBarPosition.BarIndex == endBarPosition.BarIndex) { var sig = barIndexCalculator.GetTimeSignatureFromBarIndex(startBarPosition.BarIndex); int barLength = barTick * sig.Numerator / sig.Denominator; writer.Write("#{0:000}2{1}{2}:", startBarPosition.BarIndex, hold.LaneIndex.ToString("x"), hold.Identifier); int gcd = GetGcd(GetGcd(startBarPosition.TickOffset, endBarPosition.TickOffset), barLength); for (int i = 0; i *gcd < barLength; i++) { int tickOffset = i * gcd; if (startBarPosition.TickOffset == tickOffset) { writer.Write("1" + ToLaneWidthString(hold.Width)); } else if (endBarPosition.TickOffset == tickOffset) { writer.Write("2" + ToLaneWidthString(hold.Width)); } else { writer.Write("00"); } } writer.WriteLine(); } else { var startSig = barIndexCalculator.GetTimeSignatureFromBarIndex(startBarPosition.BarIndex); int startBarLength = barTick * startSig.Numerator / startSig.Denominator; writer.Write("#{0:000}2{1}{2}:", startBarPosition.BarIndex, hold.LaneIndex.ToString("x"), hold.Identifier); int gcd = GetGcd(startBarPosition.TickOffset, startBarLength); for (int i = 0; i *gcd < startBarLength; i++) { int tickOffset = i * gcd; if (startBarPosition.TickOffset == tickOffset) { writer.Write("1" + ToLaneWidthString(hold.Width)); } else { writer.Write("00"); } } writer.WriteLine(); var endSig = barIndexCalculator.GetTimeSignatureFromBarIndex(endBarPosition.BarIndex); int endBarLength = barTick * endSig.Numerator / endSig.Denominator; writer.Write("#{0:000}2{1}{2}:", endBarPosition.BarIndex, hold.LaneIndex.ToString("x"), hold.Identifier); gcd = GetGcd(endBarPosition.TickOffset, endBarLength); for (int i = 0; i *gcd < endBarLength; i++) { int tickOffset = i * gcd; if (endBarPosition.TickOffset == tickOffset) { writer.Write("2" + ToLaneWidthString(hold.Width)); } else { writer.Write("00"); } } writer.WriteLine(); } } identifier.Clear(); var slides = notes.Slides .OrderBy(p => p.StartTick) .Select(p => new { Identifier = identifier.Allocate(p.StartTick, p.GetDuration()), Note = p }); foreach (var slide in slides) { var start = new[] { new { TickOffset = 0, BarPosition = barIndexCalculator.GetBarPositionFromTick(slide.Note.StartTick), LaneIndex = slide.Note.StartLaneIndex, Width = slide.Note.StartWidth, Type = "1" } }; var steps = slide.Note.StepNotes.OrderBy(p => p.TickOffset).Select(p => new { TickOffset = p.TickOffset, BarPosition = barIndexCalculator.GetBarPositionFromTick(p.Tick), LaneIndex = p.LaneIndex, Width = p.Width, Type = p.IsVisible ? "3" : "5" }).Take(slide.Note.StepNotes.Count - 1); var endNote = slide.Note.StepNotes.OrderBy(p => p.TickOffset).Last(); var end = new[] { new { TickOffset = endNote.TickOffset, BarPosition = barIndexCalculator.GetBarPositionFromTick(endNote.Tick), LaneIndex = endNote.LaneIndex, Width = endNote.Width, Type = "2" } }; var slideNotes = start.Concat(steps).Concat(end); foreach (var notesInBar in slideNotes.GroupBy(p => p.BarPosition.BarIndex)) { foreach (var notesInLane in notesInBar.GroupBy(p => p.LaneIndex)) { var sig = barIndexCalculator.GetTimeSignatureFromBarIndex(notesInBar.Key); int barLength = barTick * sig.Numerator / sig.Denominator; int gcd = notesInLane.Select(p => p.BarPosition.TickOffset).Aggregate(barLength, (p, q) => GetGcd(p, q)); var dic = notesInLane.ToDictionary(p => p.BarPosition.TickOffset, p => p); writer.Write("#{0:000}3{1}{2}:", notesInBar.Key, notesInLane.Key.ToString("x"), slide.Identifier); for (int i = 0; i *gcd < barLength; i++) { int tickOffset = i * gcd; writer.Write(dic.ContainsKey(tickOffset) ? dic[tickOffset].Type + ToLaneWidthString(dic[tickOffset].Width) : "00"); } writer.WriteLine(); } } } identifier.Clear(); var airActions = notes.AirActions .OrderBy(p => p.StartTick) .Select(p => new { Identifier = identifier.Allocate(p.StartTick, p.GetDuration()), Note = p }); foreach (var airAction in airActions) { var start = new[] { new { TickOffset = 0, BarPosition = barIndexCalculator.GetBarPositionFromTick(airAction.Note.StartTick), Type = "1" } }; var actions = airAction.Note.ActionNotes.OrderBy(p => p.Offset).Select(p => new { TickOffset = p.Offset, BarPosition = barIndexCalculator.GetBarPositionFromTick(p.ParentNote.StartTick + p.Offset), Type = "3" }).Take(airAction.Note.ActionNotes.Count - 1); var endNote = airAction.Note.ActionNotes.OrderBy(p => p.Offset).Last(); var end = new[] { new { TickOffset = endNote.Offset, BarPosition = barIndexCalculator.GetBarPositionFromTick(airAction.Note.StartTick + endNote.Offset), Type = "2" } }; var actionNotes = start.Concat(actions).Concat(end); foreach (var airActionsInBar in actionNotes.GroupBy(p => p.BarPosition.BarIndex)) { var sig = barIndexCalculator.GetTimeSignatureFromBarIndex(airActionsInBar.Key); int barLength = barTick * sig.Numerator / sig.Denominator; writer.Write("#{0:000}4{1}{2}:", airActionsInBar.Key, airAction.Note.ParentNote.LaneIndex.ToString("x"), airAction.Identifier); int gcd = airActionsInBar.Select(p => p.BarPosition.TickOffset).Aggregate(barLength, (p, q) => GetGcd(p, q)); var dic = airActionsInBar.ToDictionary(p => p.BarPosition.TickOffset, p => p); for (int i = 0; i *gcd < barLength; i++) { int tickOffset = i * gcd; if (dic.ContainsKey(tickOffset)) { writer.Write("{0}{1}", dic[tickOffset].Type, ToLaneWidthString(airAction.Note.ParentNote.Width)); } else { writer.Write("00"); } } writer.WriteLine(); } } } }
public void Export(string path, ScoreBook book) { SusArgs args = CustomArgs; var notes = book.Score.Notes; notes.Taps = notes.Taps.Distinct().ToList(); notes.DTaps = notes.DTaps.Distinct().ToList(); notes.HTaps = notes.HTaps.Distinct().ToList(); notes.LTaps = notes.LTaps.Distinct().ToList(); notes.Traces = notes.Traces.Distinct().ToList(); notes.DTraces = notes.DTraces.Distinct().ToList(); notes.HTraces = notes.HTraces.Distinct().ToList(); notes.LTraces = notes.LTraces.Distinct().ToList(); notes.Holds = notes.Holds.Distinct().ToList(); notes.DHolds = notes.DHolds.Distinct().ToList(); notes.HHolds = notes.HHolds.Distinct().ToList(); notes.LHolds = notes.LHolds.Distinct().ToList(); using (var writer = new StreamWriter(path)) { writer.WriteLine("#TITLE \"{0}\"", book.Title); writer.WriteLine("#ARTIST \"{0}\"", book.ArtistName); writer.WriteLine("#DESIGNER \"{0}\"", book.NotesDesignerName); writer.WriteLine("#DIFFICULTY {0}", (int)args.PlayDifficulty + (string.IsNullOrEmpty(args.ExtendedDifficulty) ? "" : ":" + args.ExtendedDifficulty)); writer.WriteLine("#PLAYLEVEL {0}", args.PlayLevel); writer.WriteLine("#WAVE \"{0}\"", args.SoundFileName); writer.WriteLine("#WAVEOFFSET {0}", args.SoundOffset); writer.WriteLine(); int barTick = book.Score.TicksPerBeat * 4; var barIndexCalculator = new BarIndexCalculator(barTick, book.Score.Events.TimeSignatureChangeEvents, args.HasPaddingBar); foreach (var item in barIndexCalculator.TimeSignatures) { writer.WriteLine("#{0:000}02: {1}", item.StartBarIndex + (args.HasPaddingBar && item.StartBarIndex == 1 ? -1 : 0), 4f * item.TimeSignature.Numerator / item.TimeSignature.Denominator); } writer.WriteLine(); var bpmlist = book.Score.Events.BPMChangeEvents .GroupBy(p => p.BPM) .SelectMany((p, i) => p.Select(q => new { Index = i, Value = q, BarPosition = barIndexCalculator.GetBarPositionFromTick(q.Tick) })) .ToList(); if (bpmlist.Count >= 36 * 36) { throw new ArgumentException("BPM定義数が上限を超えました。"); } var bpmIdentifiers = EnumerateIdentifiers(2).Skip(1).Take(bpmlist.Count).ToList(); foreach (var item in bpmlist.GroupBy(p => p.Index).Select(p => p.First())) { writer.WriteLine("#BPM{0}: {1}", bpmIdentifiers[item.Index], item.Value.BPM); } if (args.HasPaddingBar) { writer.WriteLine("#{0:000}08: {1:x2}", 0, bpmIdentifiers[bpmlist.OrderBy(p => p.Value.Tick).First().Index]); } foreach (var eventInBar in bpmlist.GroupBy(p => p.BarPosition.BarIndex)) { var sig = barIndexCalculator.GetTimeSignatureFromBarIndex(eventInBar.Key); int barLength = barTick * sig.Numerator / sig.Denominator; var dic = eventInBar.ToDictionary(p => p.BarPosition.TickOffset, p => p); int gcd = eventInBar.Select(p => p.BarPosition.TickOffset).Aggregate(barLength, (p, q) => GetGcd(p, q)); writer.Write("#{0:000}08: ", eventInBar.Key); for (int i = 0; i *gcd < barLength; i++) { int tickOffset = i * gcd; writer.Write(dic.ContainsKey(tickOffset) ? bpmIdentifiers[dic[tickOffset].Index] : "00"); } writer.WriteLine(); } writer.WriteLine(); foreach (var EventVar in book.Score.Events.HighSpeedChangeEvents) { var barPos = barIndexCalculator.GetBarPositionFromTick(EventVar.Tick); writer.Write("#{0:000}07: ", barPos.BarIndex); writer.WriteLine(EventVar.SpeedRatio); } foreach (var EventVar in book.Score.Events.SplitLaneEvents) { var barPos = barIndexCalculator.GetBarPositionFromTick(EventVar.Tick); writer.Write("#{0:000}05: ", barPos.BarIndex); writer.WriteLine(EventVar.ToString().ToCharArray()); } writer.WriteLine(); var shortNotes = notes.Taps.Cast <TappableBase>().Select(p => new { Type = '1', Note = p }) .Concat(notes.DTaps.Cast <TappableBase>().Select(p => new { Type = '2', Note = p })) .Concat(notes.HTaps.Cast <TappableBase>().Select(p => new { Type = '3', Note = p })) .Concat(notes.LTaps.Cast <TappableBase>().Select(p => new { Type = '4', Note = p })) .Concat(notes.Traces.Cast <TappableBase>().Select(p => new { Type = '5', Note = p })) .Concat(notes.DTraces.Cast <TappableBase>().Select(p => new { Type = '6', Note = p })) .Concat(notes.HTraces.Cast <TappableBase>().Select(p => new { Type = '7', Note = p })) .Concat(notes.LTraces.Cast <TappableBase>().Select(p => new { Type = '8', Note = p })) .Concat(notes.Flicks.Cast <TappableBase>().Select(p => new { Type = '9', Note = p })) .Concat(notes.Damages.Cast <TappableBase>().Select(p => new { Type = 'A', Note = p })) .Select(p => new { BarPosition = barIndexCalculator.GetBarPositionFromTick(p.Note.Tick), LaneIndex = p.Note.LaneIndex, Width = p.Note.Width, Type = p.Type }); foreach (var notesInBar in shortNotes.GroupBy(p => p.BarPosition.BarIndex)) { foreach (var notesInLane in notesInBar.GroupBy(p => p.LaneIndex)) { var sig = barIndexCalculator.GetTimeSignatureFromBarIndex(notesInBar.Key); int barLength = barTick * sig.Numerator / sig.Denominator; var offsetList = notesInLane.GroupBy(p => p.BarPosition.TickOffset).Select(p => p.ToList()); var separatedNotes = Enumerable.Range(0, offsetList.Max(p => p.Count)).Select(p => offsetList.Where(q => q.Count >= p + 1).Select(q => q[p])); foreach (var dic in separatedNotes.Select(p => p.ToDictionary(q => q.BarPosition.TickOffset, q => q))) { int gcd = dic.Values.Select(p => p.BarPosition.TickOffset).Aggregate(barLength, (p, q) => GetGcd(p, q)); writer.Write("#{0:000}1{1}:", notesInBar.Key, notesInLane.Key.ToString("x")); for (int i = 0; i *gcd < barLength; i++) { int tickOffset = i * gcd; writer.Write(dic.ContainsKey(tickOffset) ? dic[tickOffset].Type + ToLaneWidthString(dic[tickOffset].Width) : "00"); } writer.WriteLine(); } } } var identifier = new IdentifierAllocationManager(); var holds = book.Score.Notes.Holds .OrderBy(p => p.StartTick) .Select(p => new { Identifier = identifier.Allocate(p.StartTick, p.Duration), StartTick = p.StartTick, EndTick = p.StartTick + p.Duration, Width = p.Width, LaneIndex = p.LaneIndex }); foreach (var hold in holds) { var startBarPosition = barIndexCalculator.GetBarPositionFromTick(hold.StartTick); var endBarPosition = barIndexCalculator.GetBarPositionFromTick(hold.EndTick); if (startBarPosition.BarIndex == endBarPosition.BarIndex) { var sig = barIndexCalculator.GetTimeSignatureFromBarIndex(startBarPosition.BarIndex); int barLength = barTick * sig.Numerator / sig.Denominator; writer.Write("#{0:000}2{1}{2}:", startBarPosition.BarIndex, hold.LaneIndex.ToString("x"), hold.Identifier); int gcd = GetGcd(GetGcd(startBarPosition.TickOffset, endBarPosition.TickOffset), barLength); for (int i = 0; i *gcd < barLength; i++) { int tickOffset = i * gcd; if (startBarPosition.TickOffset == tickOffset) { writer.Write("1" + ToLaneWidthString(hold.Width)); } else if (endBarPosition.TickOffset == tickOffset) { writer.Write("2" + ToLaneWidthString(hold.Width)); } else { writer.Write("00"); } } writer.WriteLine(); } else { var startSig = barIndexCalculator.GetTimeSignatureFromBarIndex(startBarPosition.BarIndex); int startBarLength = barTick * startSig.Numerator / startSig.Denominator; writer.Write("#{0:000}2{1}{2}:", startBarPosition.BarIndex, hold.LaneIndex.ToString("x"), hold.Identifier); int gcd = GetGcd(startBarPosition.TickOffset, startBarLength); for (int i = 0; i *gcd < startBarLength; i++) { int tickOffset = i * gcd; if (startBarPosition.TickOffset == tickOffset) { writer.Write("1" + ToLaneWidthString(hold.Width)); } else { writer.Write("00"); } } writer.WriteLine(); var endSig = barIndexCalculator.GetTimeSignatureFromBarIndex(endBarPosition.BarIndex); int endBarLength = barTick * endSig.Numerator / endSig.Denominator; writer.Write("#{0:000}2{1}{2}:", endBarPosition.BarIndex, hold.LaneIndex.ToString("x"), hold.Identifier); gcd = GetGcd(endBarPosition.TickOffset, endBarLength); for (int i = 0; i *gcd < endBarLength; i++) { int tickOffset = i * gcd; if (endBarPosition.TickOffset == tickOffset) { writer.Write("2" + ToLaneWidthString(hold.Width)); } else { writer.Write("00"); } } writer.WriteLine(); } } var identifier2 = new IdentifierAllocationManager(); var dholds = book.Score.Notes.DHolds .OrderBy(p => p.StartTick) .Select(p => new { Identifier2 = identifier2.Allocate(p.StartTick, p.Duration), StartTick = p.StartTick, EndTick = p.StartTick + p.Duration, Width = p.Width, LaneIndex = p.LaneIndex }); foreach (var dhold in dholds) { var startBarPosition = barIndexCalculator.GetBarPositionFromTick(dhold.StartTick); var endBarPosition = barIndexCalculator.GetBarPositionFromTick(dhold.EndTick); if (startBarPosition.BarIndex == endBarPosition.BarIndex) { var sig = barIndexCalculator.GetTimeSignatureFromBarIndex(startBarPosition.BarIndex); int barLength = barTick * sig.Numerator / sig.Denominator; writer.Write("#{0:000}3{1}{2}:", startBarPosition.BarIndex, dhold.LaneIndex.ToString("x"), dhold.Identifier2); int gcd = GetGcd(GetGcd(startBarPosition.TickOffset, endBarPosition.TickOffset), barLength); for (int i = 0; i *gcd < barLength; i++) { int tickOffset = i * gcd; if (startBarPosition.TickOffset == tickOffset) { writer.Write("1" + ToLaneWidthString(dhold.Width)); } else if (endBarPosition.TickOffset == tickOffset) { writer.Write("2" + ToLaneWidthString(dhold.Width)); } else { writer.Write("00"); } } writer.WriteLine(); } else { var startSig = barIndexCalculator.GetTimeSignatureFromBarIndex(startBarPosition.BarIndex); int startBarLength = barTick * startSig.Numerator / startSig.Denominator; writer.Write("#{0:000}3{1}{2}:", startBarPosition.BarIndex, dhold.LaneIndex.ToString("x"), dhold.Identifier2); int gcd = GetGcd(startBarPosition.TickOffset, startBarLength); for (int i = 0; i *gcd < startBarLength; i++) { int tickOffset = i * gcd; if (startBarPosition.TickOffset == tickOffset) { writer.Write("1" + ToLaneWidthString(dhold.Width)); } else { writer.Write("00"); } } writer.WriteLine(); var endSig = barIndexCalculator.GetTimeSignatureFromBarIndex(endBarPosition.BarIndex); int endBarLength = barTick * endSig.Numerator / endSig.Denominator; writer.Write("#{0:000}3{1}{2}:", endBarPosition.BarIndex, dhold.LaneIndex.ToString("x"), dhold.Identifier2); gcd = GetGcd(endBarPosition.TickOffset, endBarLength); for (int i = 0; i *gcd < endBarLength; i++) { int tickOffset = i * gcd; if (endBarPosition.TickOffset == tickOffset) { writer.Write("2" + ToLaneWidthString(dhold.Width)); } else { writer.Write("00"); } } writer.WriteLine(); } } var identifier3 = new IdentifierAllocationManager(); var hholds = book.Score.Notes.DHolds .OrderBy(p => p.StartTick) .Select(p => new { Identifier3 = identifier3.Allocate(p.StartTick, p.Duration), StartTick = p.StartTick, EndTick = p.StartTick + p.Duration, Width = p.Width, LaneIndex = p.LaneIndex }); foreach (var hhold in hholds) { var startBarPosition = barIndexCalculator.GetBarPositionFromTick(hhold.StartTick); var endBarPosition = barIndexCalculator.GetBarPositionFromTick(hhold.EndTick); if (startBarPosition.BarIndex == endBarPosition.BarIndex) { var sig = barIndexCalculator.GetTimeSignatureFromBarIndex(startBarPosition.BarIndex); int barLength = barTick * sig.Numerator / sig.Denominator; writer.Write("#{0:000}4{1}{2}:", startBarPosition.BarIndex, hhold.LaneIndex.ToString("x"), hhold.Identifier3); int gcd = GetGcd(GetGcd(startBarPosition.TickOffset, endBarPosition.TickOffset), barLength); for (int i = 0; i *gcd < barLength; i++) { int tickOffset = i * gcd; if (startBarPosition.TickOffset == tickOffset) { writer.Write("1" + ToLaneWidthString(hhold.Width)); } else if (endBarPosition.TickOffset == tickOffset) { writer.Write("2" + ToLaneWidthString(hhold.Width)); } else { writer.Write("00"); } } writer.WriteLine(); } else { var startSig = barIndexCalculator.GetTimeSignatureFromBarIndex(startBarPosition.BarIndex); int startBarLength = barTick * startSig.Numerator / startSig.Denominator; writer.Write("#{0:000}4{1}{2}:", startBarPosition.BarIndex, hhold.LaneIndex.ToString("x"), hhold.Identifier3); int gcd = GetGcd(startBarPosition.TickOffset, startBarLength); for (int i = 0; i *gcd < startBarLength; i++) { int tickOffset = i * gcd; if (startBarPosition.TickOffset == tickOffset) { writer.Write("1" + ToLaneWidthString(hhold.Width)); } else { writer.Write("00"); } } writer.WriteLine(); var endSig = barIndexCalculator.GetTimeSignatureFromBarIndex(endBarPosition.BarIndex); int endBarLength = barTick * endSig.Numerator / endSig.Denominator; writer.Write("#{0:000}4{1}{2}:", endBarPosition.BarIndex, hhold.LaneIndex.ToString("x"), hhold.Identifier3); gcd = GetGcd(endBarPosition.TickOffset, endBarLength); for (int i = 0; i *gcd < endBarLength; i++) { int tickOffset = i * gcd; if (endBarPosition.TickOffset == tickOffset) { writer.Write("2" + ToLaneWidthString(hhold.Width)); } else { writer.Write("00"); } } writer.WriteLine(); } identifier.Clear(); } var identifier4 = new IdentifierAllocationManager(); var lholds = book.Score.Notes.DHolds .OrderBy(p => p.StartTick) .Select(p => new { Identifier4 = identifier4.Allocate(p.StartTick, p.Duration), StartTick = p.StartTick, EndTick = p.StartTick + p.Duration, Width = p.Width, LaneIndex = p.LaneIndex }); foreach (var lhold in lholds) { var startBarPosition = barIndexCalculator.GetBarPositionFromTick(lhold.StartTick); var endBarPosition = barIndexCalculator.GetBarPositionFromTick(lhold.EndTick); if (startBarPosition.BarIndex == endBarPosition.BarIndex) { var sig = barIndexCalculator.GetTimeSignatureFromBarIndex(startBarPosition.BarIndex); int barLength = barTick * sig.Numerator / sig.Denominator; writer.Write("#{0:000}5{1}{2}:", startBarPosition.BarIndex, lhold.LaneIndex.ToString("x"), lhold.Identifier4); int gcd = GetGcd(GetGcd(startBarPosition.TickOffset, endBarPosition.TickOffset), barLength); for (int i = 0; i *gcd < barLength; i++) { int tickOffset = i * gcd; if (startBarPosition.TickOffset == tickOffset) { writer.Write("1" + ToLaneWidthString(lhold.Width)); } else if (endBarPosition.TickOffset == tickOffset) { writer.Write("2" + ToLaneWidthString(lhold.Width)); } else { writer.Write("00"); } } writer.WriteLine(); } else { var startSig = barIndexCalculator.GetTimeSignatureFromBarIndex(startBarPosition.BarIndex); int startBarLength = barTick * startSig.Numerator / startSig.Denominator; writer.Write("#{0:000}5{1}{2}:", startBarPosition.BarIndex, lhold.LaneIndex.ToString("x"), lhold.Identifier4); int gcd = GetGcd(startBarPosition.TickOffset, startBarLength); for (int i = 0; i *gcd < startBarLength; i++) { int tickOffset = i * gcd; if (startBarPosition.TickOffset == tickOffset) { writer.Write("1" + ToLaneWidthString(lhold.Width)); } else { writer.Write("00"); } } writer.WriteLine(); var endSig = barIndexCalculator.GetTimeSignatureFromBarIndex(endBarPosition.BarIndex); int endBarLength = barTick * endSig.Numerator / endSig.Denominator; writer.Write("#{0:000}5{1}{2}:", endBarPosition.BarIndex, lhold.LaneIndex.ToString("x"), lhold.Identifier4); gcd = GetGcd(endBarPosition.TickOffset, endBarLength); for (int i = 0; i *gcd < endBarLength; i++) { int tickOffset = i * gcd; if (endBarPosition.TickOffset == tickOffset) { writer.Write("2" + ToLaneWidthString(lhold.Width)); } else { writer.Write("00"); } } writer.WriteLine(); } } identifier.Clear(); identifier2.Clear(); identifier3.Clear(); identifier4.Clear(); } }