Skip to content

Commit 47aaabd

Browse files
authored
Bugfix: Clamp Note.noteNumber to MIDI range (0-127) to prevent crashes (#57)
* Bugfix: Clamp noteNumber to MIDI range (0-127) to prevent crashes - Add early bounds checking for octave values outside -2 to 8 range. Fixes infinite loop for octaves below -2 - Add final bounds checking to ensure note values are within MIDI range. Prevents crash on cast to Int8 for values > 127 - Add test coverage * Fix coding style violations
1 parent 227c89e commit 47aaabd

File tree

2 files changed

+42
-13
lines changed

2 files changed

+42
-13
lines changed

Sources/Tonic/Note.swift

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -82,19 +82,31 @@ public struct Note: Sendable, Equatable, Hashable, Codable {
8282

8383
/// MIDI Note 0-127 starting at C
8484
public var noteNumber: Int8 {
85-
let octaveBounds = ((octave + 2) * 12) ... ((octave + 3) * 12)
86-
var note = Int(noteClass.letter.baseNote) + Int(noteClass.accidental.rawValue)
87-
if noteClass.letter == .B && noteClass.accidental.rawValue > 0 {
88-
note -= 12
89-
}
90-
if noteClass.letter == .C && noteClass.accidental.rawValue < 0 {
91-
note += 12
92-
}
93-
while !octaveBounds.contains(note) {
94-
note += 12
95-
}
96-
return Int8(note)
97-
}
85+
if octave < -2 {
86+
return 0
87+
}
88+
if octave > 8 {
89+
return 127
90+
}
91+
let octaveBounds = ((octave + 2) * 12) ... ((octave + 3) * 12)
92+
var note = Int(noteClass.letter.baseNote) + Int(noteClass.accidental.rawValue)
93+
if noteClass.letter == .B && noteClass.accidental.rawValue > 0 {
94+
note -= 12
95+
}
96+
if noteClass.letter == .C && noteClass.accidental.rawValue < 0 {
97+
note += 12
98+
}
99+
while !octaveBounds.contains(note) {
100+
note += 12
101+
}
102+
if note < 0 {
103+
return 0
104+
}
105+
if note > 127 {
106+
return 127
107+
}
108+
return Int8(note)
109+
}
98110

99111
/// The pitch for the note
100112
public var pitch: Pitch {

Tests/TonicTests/NoteTests.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,4 +162,21 @@ final class NoteTests: XCTestCase {
162162
let empty = NoteSet()
163163
XCTAssertNil(empty.first)
164164
}
165+
166+
func testClampNoteBounds() {
167+
let bMinus3 = Note(.B, octave: -3)
168+
XCTAssertEqual(bMinus3.noteNumber, 0)
169+
170+
let cFlatMinus3 = Note(.C, accidental: .flat, octave: -3)
171+
XCTAssertEqual(cFlatMinus3.noteNumber, 0)
172+
173+
let a8 = Note(.A, octave: 8)
174+
XCTAssertEqual(a8.noteNumber, 127)
175+
176+
let gSharp8 = Note(.G, accidental: .sharp, octave: 8)
177+
XCTAssertEqual(gSharp8.noteNumber, 127)
178+
179+
let c9 = Note(.C, octave: 9)
180+
XCTAssertEqual(c9.noteNumber, 127)
181+
}
165182
}

0 commit comments

Comments
 (0)