-
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
Open
Applesaber
wants to merge
18
commits into
MuNET-OSS:master
Choose a base branch
from
Applesaber:master
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 3 commits
Commits
Show all changes
18 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 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
Some comments aren't visible on the classic Files Changed page.
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 |
|---|---|---|
|
|
@@ -8,4 +8,8 @@ riderModule.iml | |
| .cursor | ||
| /.tmp* | ||
| *scratch* | ||
| *.lscache | ||
| *.lscache | ||
|
|
||
| # 测试 dump 输出 | ||
| *_output.* | ||
| placeholder.txt | ||
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 ? 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 ? 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(Warning, string.Format(Locale.ChuGeneratorUnsupported, "→ C2S"))); | ||
| return new C2sChart(); | ||
| } | ||
|
|
||
| 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)); | ||
|
sourcery-ai[bot] marked this conversation as resolved.
|
||
|
|
||
| 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.Cell}\t{n.Width}\t{n.StartHeight}\t{n.SlideDuration}\t{n.EndCell}\t{n.EndWidth}\t{n.TargetHeight}\t{n.NoteColor}", | ||
|
cubic-dev-ai[bot] marked this conversation as resolved.
Outdated
|
||
| "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.
|
||
| }; | ||
|
Starrah marked this conversation as resolved.
|
||
|
|
||
| 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,114 @@ | ||
| 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; | ||
| private const int C2sRsl = 384; | ||
|
|
||
| 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; | ||
| var result = new SusChart { Bpm = bpm, TicksPerBeat = SusTpb, Title = title, Artist = artist }; | ||
| foreach (var n in c2s.Notes) result.Notes.Add(ScaleUp(n)); | ||
| 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(MapLaneOnly(n)); | ||
|
cubic-dev-ai[bot] marked this conversation as resolved.
Outdated
|
||
| return result; | ||
| } | ||
|
|
||
| alerts.Add(new Alert(Warning, string.Format(Locale.ChuGeneratorUnsupported, "→ SUS"))); | ||
| return new SusChart(); | ||
| } | ||
|
|
||
| private static ChuNote ScaleUp(ChuNote n) | ||
| { | ||
| int s(int v) => (int)((long)v * SusTpb / (C2sRsl / 4)); | ||
|
cubic-dev-ai[bot] 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 ChuNote MapLaneOnly(ChuNote n) => new() | ||
| { | ||
| Type = n.Type, Measure = n.Measure, Offset = n.Offset, | ||
| Cell = n.Cell * 2, Width = n.Width * 2, | ||
| HoldDuration = n.HoldDuration, SlideDuration = n.SlideDuration, | ||
| EndCell = n.EndCell * 2, EndWidth = n.EndWidth * 2, | ||
| Extra = n.Extra, TargetNote = n.TargetNote, AirHoldDuration = 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.
|
||
|
|
||
| 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.