What's wrong
Pitch computes the MIDI number as ((octave + 1) * 12) + letter + accidental in unchecked int, so a huge octave wraps around into the 0..127 range and passes the range check.
Where
Semantics.Music/Pitch.cs:81-86 (TryParse):
if (!int.TryParse(name[index..], NumberStyles.Integer, CultureInfo.InvariantCulture, out int octave)) return false;
int midi = ((octave + 1) * 12) + (int)letter + accidental;
if (midi is < 0 or > 127) return false;
Semantics.Music/Pitch.cs:46 (Create(NoteLetter, Accidental, int octave)) uses the same expression.
Failure scenarios
These were reproduced against the built library on .NET 10:
(1073741823 + 1) * 12 wraps to 0, so:
Pitch.TryParse("C1073741823", out var p) returns true with p = C-1 (MIDI 0).
Pitch.Create(NoteLetter.C, Accidental.Natural, 1073741823) returns C-1 instead of throwing the documented ArgumentOutOfRangeException.
NumberStyles.Integer allows leading and trailing whitespace and a leading sign, so "C 4 " and "C+4" parse. They then print back as "C4", which breaks the round-trip/canonical-text contract that the type-safe-factories work set up for Music types.
Suggested fix
- Range-check the octave before the multiplication. Any octave outside roughly −2..10 cannot produce MIDI 0..127 even with accidentals, so reject it early. Alternatively, compute in
long.
- Parse the octave with
NumberStyles.AllowLeadingSign only.
- Add tests for
"C1073741823", "C 4", "C+4", and the typed factory with int.MaxValue.
What's wrong
Pitchcomputes the MIDI number as((octave + 1) * 12) + letter + accidentalin uncheckedint, so a huge octave wraps around into the 0..127 range and passes the range check.Where
Semantics.Music/Pitch.cs:81-86(TryParse):Semantics.Music/Pitch.cs:46(Create(NoteLetter, Accidental, int octave)) uses the same expression.Failure scenarios
These were reproduced against the built library on .NET 10:
(1073741823 + 1) * 12wraps to 0, so:Pitch.TryParse("C1073741823", out var p)returnstruewithp= C-1 (MIDI 0).Pitch.Create(NoteLetter.C, Accidental.Natural, 1073741823)returns C-1 instead of throwing the documentedArgumentOutOfRangeException.NumberStyles.Integerallows leading and trailing whitespace and a leading sign, so"C 4 "and"C+4"parse. They then print back as"C4", which breaks the round-trip/canonical-text contract that the type-safe-factories work set up for Music types.Suggested fix
long.NumberStyles.AllowLeadingSignonly."C1073741823","C 4","C+4", and the typed factory withint.MaxValue.