-
Notifications
You must be signed in to change notification settings - Fork 2
feat: 新增 C2S/UGC/SUS 谱面格式支持 (chu) #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 5 commits
Commits
Show all changes
45 commits
Select commit
Hold shift + click to select a range
54e5c27
feat: 新增 C2S/UGC/SUS 谱面格式支持
Applesaber e2488df
fix: 修复数字解析区域依赖和 tick 缩放溢出问题
Applesaber e4619d1
fix: 修复 review bot 发现的 9 个问题
Applesaber cd61fd7
fix: 修复 cubic 第2轮审查的 5 个问题
Applesaber 91a0455
[F&R] 修复若干问题
Starrah 828dbe6
fix: UgcGenerator.UCode 补充 HXD/SXD/SXC/SLC 映射
Applesaber 5abf8ae
[F&O] 修复一些小问题,补充测试等
Starrah ec9d78d
[F] 修复UgcParser,未能正确实现对AIR的解析,在多字符 TargetNote的AIR时会解析错误的问题
Starrah 41bc684
[F] 优化HexToInt
Starrah 7f19976
Merge remote-tracking branch 'origin/master'
Starrah f1ec9ee
fix: C2sParser.ParseNote ALD/ASD Cell/Width 错误赋值
Applesaber 8265abc
[+&O] CLI支持新增的中二转谱;同时优化提示文本,避免太罗嗦
Starrah 4041dc3
[+] CLI for 中二
Starrah 1cde69b
fix: UgcParser 兼容大写类型前缀和 >c 跟随行
Applesaber ca6db1e
fix: UgcParser 兼容独立跟随行和 @USETIL 指令
Applesaber b483eb0
[F] 为 ParseHoldNote 实现与 ParseSlideNote 相同的多行跟随消费逻辑(循环读取合法 #…>s/#…>c,跳…
Starrah 05b8945
[R&doc]优化CLI和README
Starrah d404a68
[R] 优化中文等语言下的报错行号提示文本(删掉一个空格)
Starrah 1cc5037
[R] ChuNote,重命名Extra为Tag
Starrah 37c6b6d
Merge branch 'master' into dev
Starrah 5f8bddb
[R] ChuNote的重新设计
Starrah 0b2e921
[R] 适应 5f8bddb 中所做的修改,使能过编译
Starrah a0ff8b0
[R] Chart相关重构(初步)
Starrah 84a39e5
[R] Chart相关重构(第二步)——使用BaseChart中提供的List对象
Starrah ba1bbcc
[F&R] 1. 现在的ugcgenerator缺少SflEvent的生成/写出。
Starrah 09c5ead
[R] Chart相关重构(第三步)——移除各个谱面中的Resolution,而是parser解析存成分数、generator使用写死的固定值
Starrah d92a9e3
[R] Chart相关重构(最后一步)——三种Chart合并为统一的ChuChart!
Starrah 54bc9f4
[F] 不要屏蔽本应出现的警告
Starrah fa9dbcb
[F] UGCParser对Slide的解析
Starrah 9c13621
[+] 在ChuNote中新增Previous字段,新增BaseChuParser和FillAllPrevious通用工具方法。以实现sl…
Starrah 52af0a3
[+] UGC 对Air Slide和Air Hold的解析
Starrah b54b7e9
[+] UgcGenerator Slide、Air Hold、Air Slide的正确实现
Starrah d580e85
[F] 修一些小问题
Starrah 00178c1
[R] 把UGC常用的一些工具方法,提取到ChuUtils中
Starrah a994505
[+&R&F] ChuNote新增Height和CrushInterval;UgcParser和UgcGenerator,重构height…
Starrah 028305c
[Test&F] 新增几个测试谱面,并修bug
Starrah 317ea02
[F] 修复一些问题:
Starrah bcfbe78
[Test] 重构测试代码比较音符相等的整体逻辑;同时修复若干问题。
Starrah afd485b
[F] lineNum等一点小问题
Starrah a9a83a1
[+] UGC AirCrush
Starrah c9fe48d
[F] RSL不再是static的,解决多实例冲突问题
Starrah 477a767
[F] decimal.Parse InvarientCulture
Starrah d0de6ad
[F] 生成谱面时,字符串插值确保使用InvarientCultrue
Starrah db62da3
[F] C2S的高度只准保留一位小数后,舍入误差引起测试爆炸
Starrah 25f364b
[doc] Update README.md
Starrah File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| using MuConvert.chart; | ||
|
|
||
| namespace MuConvert.chu; | ||
|
|
||
| /** | ||
| * C2S 格式谱面 IR(官方格式,RESOLUTION=384 tick/小节)。 | ||
| */ | ||
| public class C2sChart : BaseChart<ChuNote>, IChuChart | ||
| { | ||
| public string Version { get; set; } = "1.08.00\t1.08.00"; | ||
| public int MusicId { get; set; } | ||
| public int DifficultId { get; set; } | ||
| public string Creator { get; set; } = ""; | ||
| public int Resolution { get; set; } = 384; | ||
| public double DefBpm { get; set; } = 120.0; | ||
| public List<(int Measure, int Offset, double Bpm)> BpmEvents = []; | ||
| public List<(int Measure, int Offset, int Denom, int Num)> MetEvents = []; | ||
| public List<(int Measure, int Offset, int Duration, double Multiplier)> SflEvents = []; | ||
|
|
||
| public override decimal StartBpm => (decimal)(BpmEvents.Count > 0 ? BpmEvents[0].Bpm : DefBpm); | ||
| public override decimal StartTime => Notes.Count > 0 ? Notes.Min(n => n.Measure * Resolution + n.Offset) / (decimal)Resolution * 240m / StartBpm : 0; | ||
| public override decimal EndTime => Notes.Count > 0 ? Notes.Max(n => n.Measure * Resolution + n.Offset + Math.Max(n.HoldDuration, Math.Max(n.SlideDuration, n.AirHoldDuration))) / (decimal)Resolution * 240m / StartBpm : 0; | ||
| public override int TotalNotes => Notes.Count; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| namespace MuConvert.chu; | ||
|
|
||
| /** | ||
| * CHUNITHM 通用音符,C2S / UGC / SUS 共用此结构。 | ||
| */ | ||
| public class ChuNote | ||
| { | ||
| /** 音符类型 (TAP, CHR, HLD, SLD, AIR, AHD 等) */ | ||
| public string Type { get; set; } = "TAP"; | ||
| /** 小节号 */ | ||
| public int Measure { get; set; } | ||
| /** 小节内偏移 (C2S: 0–383, UGC/SUS: 0–1919) */ | ||
| public int Offset { get; set; } | ||
| /** 起始列 (0–15) */ | ||
| public int Cell { get; set; } | ||
| /** 宽度 (1–16) */ | ||
| public int Width { get; set; } = 1; | ||
| /** HLD 持续时长 */ | ||
| public int HoldDuration { get; set; } | ||
| /** SLD 持续时长 */ | ||
| public int SlideDuration { get; set; } | ||
| /** SLD 终点列 */ | ||
| public int EndCell { get; set; } | ||
| /** SLD 终点宽度 */ | ||
| public int EndWidth { get; set; } = 1; | ||
| /** CHR/FLK 附加数据(方向等) */ | ||
| public string Extra { get; set; } = ""; | ||
| /** AIR/AHD 关联的目标音符类型 */ | ||
| public string TargetNote { get; set; } = ""; | ||
| /** AHD 持续时长 */ | ||
| public int AirHoldDuration { get; set; } | ||
| /** Air Crush 起始高度 */ | ||
| public int StartHeight { get; set; } | ||
| /** Air Crush 目标高度 */ | ||
| public int TargetHeight { get; set; } | ||
| /** Air Crush 颜色 */ | ||
| public string NoteColor { get; set; } = ""; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| using MuConvert.chart; | ||
|
|
||
| namespace MuConvert.chu; | ||
|
|
||
| /** | ||
| * CHUNITHM 所有谱面格式的统一接口,作为 Generator 的输入类型。 | ||
| */ | ||
| public interface IChuChart : IBaseChart; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| using MuConvert.chart; | ||
|
|
||
| namespace MuConvert.chu; | ||
|
|
||
| /** | ||
| * SUS 格式谱面 IR(REQUEST=480 tick/拍,lane 0–31)。 | ||
| */ | ||
| public class SusChart : BaseChart<ChuNote>, IChuChart | ||
| { | ||
| public string Title { get; set; } = ""; | ||
| public string Artist { get; set; } = ""; | ||
| public string Designer { get; set; } = ""; | ||
| public int TicksPerBeat { get; set; } = 480; | ||
| public double Bpm { get; set; } = 120.0; | ||
|
|
||
| public override decimal StartBpm => (decimal)Bpm; | ||
| public override decimal StartTime => Notes.Count > 0 ? Notes.Min(n => n.Measure * TicksPerBeat * 4 + n.Offset) / (decimal)(TicksPerBeat * 4) * 240m / StartBpm : 0; | ||
| public override decimal EndTime => Notes.Count > 0 && StartBpm > 0 ? Notes.Max(n => n.Measure * TicksPerBeat * 4 + n.Offset + Math.Max(n.HoldDuration, Math.Max(n.SlideDuration, n.AirHoldDuration))) / (decimal)(TicksPerBeat * 4) * 240m / StartBpm : 0; | ||
| public override int TotalNotes => Notes.Count; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| using MuConvert.chart; | ||
|
|
||
| namespace MuConvert.chu; | ||
|
|
||
| /** | ||
| * UGC 格式谱面 IR(UMIGURI 格式,@TICKS=480 tick/拍)。 | ||
| */ | ||
| public class UgcChart : BaseChart<ChuNote>, IChuChart | ||
| { | ||
| public string Version { get; set; } = "6"; | ||
| public string Title { get; set; } = ""; | ||
| public string Artist { get; set; } = ""; | ||
| public string Designer { get; set; } = ""; | ||
| public string Difficulty { get; set; } = ""; | ||
| public int Level { get; set; } | ||
| public double Constant { get; set; } | ||
| public string SongId { get; set; } = ""; | ||
| public int TicksPerBeat { get; set; } = 480; | ||
| public List<(int Measure, int Num, int Den)> BeatEvents = []; | ||
| public List<(int Measure, int Offset, double Bpm)> BpmEvents = []; | ||
| public List<(int Measure, int Offset, double Multiplier)> SpeedEvents = []; | ||
|
|
||
| public override decimal StartBpm => (decimal)(BpmEvents.Count > 0 ? BpmEvents[0].Bpm : 120.0); | ||
| public override decimal StartTime => Notes.Count > 0 ? Notes.Min(n => n.Measure * TicksPerBeat * 4 + n.Offset) / (decimal)(TicksPerBeat * 4) * 240m / StartBpm : 0; | ||
| public override decimal EndTime => Notes.Count > 0 && StartBpm > 0 ? Notes.Max(n => n.Measure * TicksPerBeat * 4 + n.Offset + Math.Max(n.HoldDuration, Math.Max(n.SlideDuration, n.AirHoldDuration))) / (decimal)(TicksPerBeat * 4) * 240m / StartBpm : 0; | ||
| public override int TotalNotes => Notes.Count; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,125 @@ | ||
| using System.Globalization; | ||
| using System.Text; | ||
| using MuConvert.chart; | ||
| using MuConvert.generator; | ||
| using MuConvert.utils; | ||
| using static MuConvert.utils.Alert.LEVEL; | ||
|
|
||
| namespace MuConvert.chu; | ||
|
|
||
| /** | ||
| * C2S 格式生成器。 | ||
| * 输入 IChuChart,内部自动转换后输出 C2S 文本。 | ||
| */ | ||
| public class C2sGenerator : IGenerator<IChuChart> | ||
| { | ||
| private const int C2sResolution = 384; | ||
|
|
||
| public (string, List<Alert>) Generate(IChuChart chart) | ||
| { | ||
| var alerts = new List<Alert>(); | ||
| var c2s = ConvertToC2s(chart, alerts); | ||
| var text = Serialize(c2s); | ||
| return (text, alerts); | ||
| } | ||
|
|
||
| private static C2sChart ConvertToC2s(IChuChart chart, List<Alert> alerts) | ||
| { | ||
| if (chart is C2sChart c2s) return c2s; | ||
|
|
||
| if (chart is UgcChart ugc) | ||
| { | ||
| var result = new C2sChart | ||
| { | ||
| Version = "1.08.00\t1.08.00", | ||
| Creator = ugc.Designer, | ||
| DefBpm = ugc.BpmEvents.Count > 0 ? ugc.BpmEvents[0].Bpm : 120.0, | ||
| }; | ||
| foreach (var b in ugc.BpmEvents) | ||
| result.BpmEvents.Add((b.Measure, ScaleDown(b.Offset, ugc.TicksPerBeat), b.Bpm)); | ||
| foreach (var b in ugc.BeatEvents) | ||
| result.MetEvents.Add((b.Measure, 0, b.Den, b.Num)); | ||
| foreach (var n in ugc.Notes) | ||
| result.Notes.Add(ScaleNote(n, ugc.TicksPerBeat)); | ||
| return result; | ||
| } | ||
|
|
||
| if (chart is SusChart sus) | ||
| { | ||
| var result = new C2sChart { DefBpm = sus.Bpm }; | ||
| result.BpmEvents.Add((0, 0, sus.Bpm)); | ||
| foreach (var n in sus.Notes) | ||
| result.Notes.Add(ScaleNote(n, sus.TicksPerBeat)); | ||
| return result; | ||
| } | ||
|
|
||
| alerts.Add(new Alert(Error, string.Format(Locale.ChuGeneratorUnsupported, "→ C2S"))); | ||
| throw new ConversionException(alerts); | ||
| } | ||
|
|
||
| private static ChuNote ScaleNote(ChuNote n, int tpb) | ||
| { | ||
| int scaleDown(int v) => (int)((long)v * (C2sResolution / 4) / tpb); | ||
| return new ChuNote | ||
| { | ||
| Type = n.Type, Measure = n.Measure, Offset = scaleDown(n.Offset), | ||
| Cell = n.Cell, Width = n.Width, | ||
| HoldDuration = scaleDown(n.HoldDuration), SlideDuration = scaleDown(n.SlideDuration), | ||
| EndCell = n.EndCell, EndWidth = n.EndWidth, | ||
| Extra = n.Extra, TargetNote = n.TargetNote, AirHoldDuration = scaleDown(n.AirHoldDuration), | ||
| StartHeight = n.StartHeight, TargetHeight = n.TargetHeight, NoteColor = n.NoteColor, | ||
| }; | ||
| } | ||
|
|
||
| private static int ScaleDown(int ticks, int tpb) => (int)((long)ticks * (C2sResolution / 4) / tpb); | ||
|
|
||
| private static string Serialize(C2sChart chart) | ||
| { | ||
| var sb = new StringBuilder(); | ||
| sb.AppendLine($"VERSION\t{chart.Version}"); | ||
| sb.AppendLine($"MUSIC\t{chart.MusicId}"); | ||
| sb.AppendLine("SEQUENCEID\t0"); | ||
| sb.AppendLine($"DIFFICULT\t{chart.DifficultId:D2}"); | ||
| sb.AppendLine("LEVEL\t0.0"); | ||
| sb.AppendLine($"CREATOR\t{chart.Creator}"); | ||
| sb.AppendLine($"BPM_DEF\t{Fmt(chart.DefBpm)}\t{Fmt(chart.DefBpm)}\t{Fmt(chart.DefBpm)}\t{Fmt(chart.DefBpm)}"); | ||
| sb.AppendLine("MET_DEF\t4\t4"); | ||
| sb.AppendLine($"RESOLUTION\t{chart.Resolution}"); | ||
| sb.AppendLine($"CLK_DEF\t{chart.Resolution}"); | ||
| sb.AppendLine("PROGJUDGE_BPM\t240.000"); | ||
| sb.AppendLine("PROGJUDGE_AER\t0.999"); | ||
| sb.AppendLine("TUTORIAL\t0"); | ||
| sb.AppendLine(); | ||
|
|
||
| foreach (var b in chart.BpmEvents) | ||
| sb.AppendLine($"BPM\t{b.Measure}\t{b.Offset}\t{Fmt(b.Bpm)}"); | ||
| foreach (var m in chart.MetEvents) | ||
| sb.AppendLine($"MET\t{m.Measure}\t{m.Offset}\t{m.Denom}\t{m.Num}"); | ||
| foreach (var s in chart.SflEvents) | ||
| sb.AppendLine($"SFL\t{s.Measure}\t{s.Offset}\t{s.Duration}\t{Mlt(s.Multiplier)}"); | ||
| sb.AppendLine(); | ||
|
|
||
| foreach (var n in chart.Notes.OrderBy(n => n.Measure * C2sResolution + n.Offset)) | ||
| sb.AppendLine(FormatNote(n)); | ||
|
|
||
| sb.AppendLine(); | ||
| return sb.ToString(); | ||
| } | ||
|
|
||
| private static string FormatNote(ChuNote n) => n.Type switch | ||
| { | ||
| "TAP" => $"TAP\t{n.Measure}\t{n.Offset}\t{n.Cell}\t{n.Width}", | ||
| "CHR" => $"CHR\t{n.Measure}\t{n.Offset}\t{n.Cell}\t{n.Width}\t{n.Extra}", | ||
| "HLD" or "HXD" => $"{n.Type}\t{n.Measure}\t{n.Offset}\t{n.Cell}\t{n.Width}\t{n.HoldDuration}", | ||
| "SLD" or "SLC" or "SXD" or "SXC" => $"{n.Type}\t{n.Measure}\t{n.Offset}\t{n.Cell}\t{n.Width}\t{n.SlideDuration}\t{n.EndCell}\t{n.EndWidth}", | ||
| "FLK" => $"FLK\t{n.Measure}\t{n.Offset}\t{n.Cell}\t{n.Width}\t{n.Extra}", | ||
| "AIR" or "AUR" or "AUL" or "ADW" or "ADR" or "ADL" => $"{n.Type}\t{n.Measure}\t{n.Offset}\t{n.Cell}\t{n.Width}\t{n.TargetNote}", | ||
| "AHD" => $"AHD\t{n.Measure}\t{n.Offset}\t{n.Cell}\t{n.Width}\t{n.TargetNote}\t{n.AirHoldDuration}", | ||
| "ALD" or "ASD" => $"{n.Type}\t{n.Measure}\t{n.Offset}\t{n.StartHeight}\t{n.SlideDuration}\t{n.EndCell}\t{n.EndWidth}\t{n.TargetHeight}\t{n.NoteColor}", | ||
| "MNE" => $"MNE\t{n.Measure}\t{n.Offset}\t{n.Cell}\t{n.Width}", | ||
| _ => $"TAP\t{n.Measure}\t{n.Offset}\t{n.Cell}\t{n.Width}" | ||
|
cubic-dev-ai[bot] marked this conversation as resolved.
Outdated
|
||
| }; | ||
|
|
||
| private static string Fmt(double v) => v.ToString("0.000", CultureInfo.InvariantCulture); | ||
| private static string Mlt(double v) => v.ToString("0.000000", CultureInfo.InvariantCulture); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,105 @@ | ||
| using System.Text; | ||
| using MuConvert.chart; | ||
| using MuConvert.generator; | ||
| using MuConvert.utils; | ||
| using static MuConvert.utils.Alert.LEVEL; | ||
|
|
||
| namespace MuConvert.chu; | ||
|
|
||
| /** | ||
| * SUS 格式生成器。 | ||
| * 输入 IChuChart,内部自动转换后输出 SUS 文本。 | ||
| */ | ||
| public class SusGenerator : IGenerator<IChuChart> | ||
| { | ||
| private const int SusTpb = 480; | ||
|
|
||
| public (string, List<Alert>) Generate(IChuChart chart) | ||
| { | ||
| var alerts = new List<Alert>(); | ||
| var sus = ConvertToSus(chart, alerts); | ||
| var text = Serialize(sus); | ||
| return (text, alerts); | ||
| } | ||
|
|
||
| private static SusChart ConvertToSus(IChuChart chart, List<Alert> alerts) | ||
| { | ||
| if (chart is SusChart sus) return sus; | ||
|
|
||
| double bpm = 120.0; | ||
| string title = "", artist = ""; | ||
|
|
||
| if (chart is C2sChart c2s) | ||
| { | ||
| bpm = c2s.BpmEvents.Count > 0 ? c2s.BpmEvents[0].Bpm : c2s.DefBpm; | ||
| int c2sTpb = c2s.Resolution / 4; | ||
| var result = new SusChart { Bpm = bpm, TicksPerBeat = SusTpb, Title = title, Artist = artist }; | ||
| foreach (var n in c2s.Notes) result.Notes.Add(ScaleUp(n, c2sTpb)); | ||
| return result; | ||
| } | ||
|
|
||
| if (chart is UgcChart ugc) | ||
| { | ||
| bpm = ugc.BpmEvents.Count > 0 ? ugc.BpmEvents[0].Bpm : 120.0; | ||
| var result = new SusChart { Bpm = bpm, TicksPerBeat = SusTpb, Title = ugc.Title, Artist = ugc.Artist }; | ||
| foreach (var n in ugc.Notes) result.Notes.Add(ScaleUp(n, ugc.TicksPerBeat)); | ||
| return result; | ||
| } | ||
|
|
||
| alerts.Add(new Alert(Error, string.Format(Locale.ChuGeneratorUnsupported, "→ SUS"))); | ||
| throw new ConversionException(alerts); | ||
| } | ||
|
|
||
| private static ChuNote ScaleUp(ChuNote n, int sourceTicksPerBeat) | ||
| { | ||
| int s(int v) => (int)((long)v * SusTpb / sourceTicksPerBeat); | ||
|
Starrah marked this conversation as resolved.
Outdated
|
||
| return new ChuNote | ||
| { | ||
| Type = n.Type, Measure = n.Measure, Offset = s(n.Offset), | ||
| Cell = n.Cell * 2, Width = n.Width * 2, | ||
| HoldDuration = s(n.HoldDuration), SlideDuration = s(n.SlideDuration), | ||
| EndCell = n.EndCell * 2, EndWidth = n.EndWidth * 2, | ||
| Extra = n.Extra, TargetNote = n.TargetNote, AirHoldDuration = s(n.AirHoldDuration), | ||
| }; | ||
| } | ||
|
|
||
| private static string Serialize(SusChart sus) | ||
| { | ||
| var sb = new StringBuilder(); | ||
| if (!string.IsNullOrEmpty(sus.Title)) sb.AppendLine($"#TITLE \"{sus.Title}\""); | ||
| if (!string.IsNullOrEmpty(sus.Artist)) sb.AppendLine($"#ARTIST \"{sus.Artist}\""); | ||
| if (!string.IsNullOrEmpty(sus.Designer)) sb.AppendLine($"#DESIGNER \"{sus.Designer}\""); | ||
| sb.AppendLine($"#BPM_DEF {sus.Bpm:F2}"); | ||
| sb.AppendLine($"#REQUEST \"{sus.TicksPerBeat}\""); | ||
| sb.AppendLine(); | ||
|
|
||
| foreach (var n in sus.Notes.OrderBy(n => n.Measure).ThenBy(n => n.Offset)) | ||
| sb.AppendLine($"#{n.Measure:X2}{n.Offset:X3}:{FormatData(n)}"); | ||
|
sourcery-ai[bot] marked this conversation as resolved.
Outdated
|
||
|
|
||
| return sb.ToString(); | ||
| } | ||
|
|
||
| private static string FormatData(ChuNote n) | ||
| { | ||
| string lw = $"{n.Cell:X2}{n.Width:X2}"; | ||
| string tc = TypeCode(n.Type); | ||
| string dur = $"{(n.HoldDuration > 0 ? n.HoldDuration : n.SlideDuration > 0 ? n.SlideDuration : n.AirHoldDuration):X4}"; | ||
| return tc switch | ||
| { | ||
| "01" or "02" or "03" or "10" => $"{tc}{lw}", | ||
| "05" or "08" => $"{tc}{lw}{dur}", | ||
| "06" => $"{tc}{lw}{dur}{n.EndCell:X2}{n.EndWidth:X2}", | ||
| "07" or "09" => $"{tc}{lw}{n.TargetNote}", | ||
| _ => $"01{lw}" | ||
| }; | ||
| } | ||
|
|
||
| private static string TypeCode(string t) => t switch | ||
| { | ||
| "TAP" => "01", "CHR" => "02", "FLK" => "03", | ||
| "HLD" => "05", "SLD" => "06", "SLC" => "06", | ||
| "AIR" => "07", "AUR" => "07", "AUL" => "07", | ||
| "AHD" => "08", "ADW" => "09", "ADR" => "09", "ADL" => "09", | ||
| "MNE" => "10", _ => "01" | ||
| }; | ||
|
Starrah marked this conversation as resolved.
|
||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.