{"record":{"id":"b7d0ec8633a763a6","repo":"babalae/better-genshin-impact","slug":"bpm-0","errorCode":null,"errorMessage":"BPM 和音符时值必须大于 0","messagePattern":"BPM 和音符时值必须大于 0","errorType":"exception","errorClass":"FormatException","httpStatus":null,"severity":"error","filePath":"BetterGenshinImpact/GameTask/Music/Service/MusicScoreParser.cs","lineNumber":655,"sourceCode":"                previousDown[item.Key] = item.Time;\n            }\n            else\n            {\n                previousUpIndex[item.Key] = i;\n            }\n        }\n\n        return events\n            .OrderBy(x => x.Time)\n            .ThenBy(x => x.Type == PerformanceEventType.KeyUp ? 0 : 1)\n            .ToList();\n    }\n\n    private static TimeSpan GetNoteDuration(double bpm, int beatDenominator, double noteDenominator)\n    {\n        if (bpm <= 0 || noteDenominator <= 0)\n        {\n            throw new FormatException(\"BPM 和音符时值必须大于 0\");\n        }\n\n        return TimeSpan.FromMilliseconds(60000d / bpm * beatDenominator / noteDenominator);\n    }\n\n    private static int ParseBeatDenominator(string timeSignature)\n    {\n        var parts = timeSignature.Split('/');\n        return parts.Length == 2 && int.TryParse(parts[1], out var denominator) && denominator > 0\n            ? denominator\n            : 4;\n    }\n\n    private static TimeSpan ToTimeSpan(MetricTimeSpan time)\n    {\n        return TimeSpan.FromMicroseconds(time.TotalMicroseconds);\n    }\n","sourceCodeStart":637,"sourceCodeEnd":673,"githubUrl":"https://github.com/babalae/better-genshin-impact/blob/a7cb36712dcb409be610257d877fcea3597e9d6b/BetterGenshinImpact/GameTask/Music/Service/MusicScoreParser.cs#L637-L673","documentation":"A FormatException from GetNoteDuration guarding against non-positive BPM or note denominator. The duration formula 60000/bpm*beatDenominator/noteDenominator requires both inputs strictly positive to avoid division by zero or negative durations. This is a downstream precondition failure, usually caused by bad upstream values rather than user input directly.","triggerScenarios":"GetNoteDuration is called with bpm <= 0 or noteDenominator <= 0. This happens when a score's bpm field is missing/zero/non-positive, or a note's denominator parsed as <= 0.","commonSituations":"A yuanqin score with bpm set to 0 or a negative value; a note with a fractional type that rounds to <= 0; upstream GetDouble returning its fallback only when the value is non-positive, but a direct caller bypassing that guard.","solutions":["Ensure the score's \"bpm\" field is a positive number (e.g. 120).","Ensure every note denominator is positive.","Validate bpm and denominators at the ParseJsonAsync entry before dispatching to timeline builders.","Route through ParseAsync so the file is reported as InvalidScore.Error."],"exampleFix":"// before\n{\"bpm\":0,\"notes\":[...]}\n// after\n{\"bpm\":120,\"notes\":[...]}","handlingStrategy":"validation","validationCode":"static bool PositiveBpmAndNotes(double bpm, IEnumerable<double> denominators)\n    => bpm > 0 && denominators.All(d => d > 0);","typeGuard":"static bool IsValidBpm(double bpm) => bpm > 0;","tryCatchPattern":"var score = await parser.ParseAsync(path, root, ct);\nif (!string.IsNullOrEmpty(score.Error)) { Log.Warning(score.Error); continue; }","preventionTips":["Ensure the score's bpm and every note denominator are positive.","Validate at ParseJsonAsync entry before dispatching to timeline builders.","Use ParseAsync for graceful InvalidScore handling."],"tags":["music-score","input-validation","math-guard","precondition"],"backgroundTag":null,"analyzedSha":"a7cb36712dcb409be610257d877fcea3597e9d6b","analyzedAt":"2026-08-13T16:44:57.548Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}