{"record":{"id":"26f48d3efb386455","repo":"babalae/better-genshin-impact","slug":"midi-json-rawpart","errorCode":null,"errorMessage":"无法解析 MIDI JSON 变速标记：{rawPart}","messagePattern":"无法解析 MIDI JSON 变速标记：(.+?)","errorType":"exception","errorClass":"FormatException","httpStatus":null,"severity":"error","filePath":"BetterGenshinImpact/GameTask/Music/Service/MusicScoreParser.cs","lineNumber":396,"sourceCode":"        var events = new List<PerformanceEvent>();\n        var cursor = TimeSpan.Zero;\n        var bpm = initialBpm;\n        string previousStatus = string.Empty;\n        string previousKeys = string.Empty;\n        var previousWasEvent = false;\n        var rawParts = sheet.Split(\n            '|',\n            StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);\n\n        for (var index = 0; index < rawParts.Length; index++)\n        {\n            var rawPart = rawParts[index];\n            if (rawPart.StartsWith('*'))\n            {\n                if (!double.TryParse(rawPart[1..], NumberStyles.Float, CultureInfo.InvariantCulture, out bpm)\n                    || bpm <= 0)\n                {\n                    throw new FormatException($\"无法解析 MIDI JSON 变速标记：{rawPart}\");\n                }\n\n                previousWasEvent = false;\n                continue;\n            }\n\n            var match = MidiJsonEventRegex().Match(rawPart);\n            if (!match.Success)\n            {\n                throw new FormatException($\"无法解析 MIDI JSON 事件：{rawPart}\");\n            }\n\n            var status = match.Groups[\"status\"].Value;\n            var keys = match.Groups[\"keys\"].Value;\n            var deltaTicks = long.Parse(match.Groups[\"ticks\"].Value, CultureInfo.InvariantCulture);\n            var delay = TimeSpan.FromMilliseconds(deltaTicks * 60000d / (bpm * ticks));\n            cursor += delay;\n","sourceCodeStart":378,"sourceCodeEnd":414,"githubUrl":"https://github.com/babalae/better-genshin-impact/blob/a7cb36712dcb409be610257d877fcea3597e9d6b/BetterGenshinImpact/GameTask/Music/Service/MusicScoreParser.cs#L378-L414","documentation":"A FormatException from ParseMidiJsonTimeline raised when a pipe-delimited segment of a MIDI-JSON \"notes\" string starts with '*' (a tempo-change marker) but the remainder is not a strictly positive floating-point number. The '*' prefix denotes a BPM change applied from that point; an unparseable or non-positive value aborts the whole timeline build. Parse uses NumberStyles.Float with the invariant culture.","triggerScenarios":"The MIDI-JSON notes string contains a segment like '*X', '**120', '*0', '*-5', or '*120,5' (comma decimal) after splitting on '|'.","commonSituations":"A MIDI-to-JSON exporter emitted a malformed tempo token; manual editing inserted a stray '*'; a locale conversion turned '*120.0' into '*120,0'; a 'multiply' or comment character was mistaken for a tempo marker.","solutions":["Inspect the notes string and fix every '*...' segment to be a positive number with a dot decimal (e.g. '*120').","Regenerate the MIDI-JSON file from the original MIDI with the exporter.","Strip or correct any stray '*' that is not a real tempo change.","Wrap file loading through ParseAsync so a bad tempo yields an InvalidScore with a readable Error instead of crashing the UI."],"exampleFix":"// before\n\"notes\":\"*X|DQ:0|UQ:480\"\n// after\n\"notes\":\"*120|DQ:0|UQ:480\"","handlingStrategy":"try-catch","validationCode":"static bool MidiJsonTemposValid(string notes)\n{\n    foreach (var part in notes.Split('|', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))\n    {\n        if (!part.StartsWith('*')) continue;\n        if (!double.TryParse(part[1..], NumberStyles.Float, CultureInfo.InvariantCulture, out var bpm) || bpm <= 0)\n            return false;\n    }\n    return true;\n}","typeGuard":"static bool IsValidTempoSegment(string s) => s.StartsWith('*') && double.TryParse(s[1..], NumberStyles.Float, CultureInfo.InvariantCulture, out var b) && b > 0;","tryCatchPattern":"var score = await parser.ParseAsync(path, root, ct);\nif (!string.IsNullOrEmpty(score.Error)) { Log.Warning(score.Error); continue; }","preventionTips":["Validate tempo segments before parsing.","Re-export MIDI-JSON rather than hand-editing tempo tokens.","Use ParseAsync to get InvalidScore reporting."],"tags":["music-score","json-parsing","input-validation","midi-json"],"backgroundTag":null,"analyzedSha":"a7cb36712dcb409be610257d877fcea3597e9d6b","analyzedAt":"2026-08-13T16:44:57.548Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}