FA-80881 / Music interval and transposition theory / Open access
Key-aware MIDI note spelling: B sharp keeps the sounding octave number · case 01
In C# major, middle C is spelled B#4, an octave above the sounding pitch, instead of B#3.
ROOT CAUSE
After respelling pitch class 0 as B#, the octave is not moved down to the octave of the letter B.
VERIFIED REPAIR
Restore the B sharp octave carry step so that it reads `name = 'B#' octave -= 1`.
Unsuccessful approach: Adjusting the octave in the wrong direction yields B#5, two octaves from the spelled register.
Case contract
Input [midi, fifths] with midi 0..127 and fifths -7..7. Use flat names when fifths < 0, sharp names otherwise; in keys with at least six sharps pitch class 5 is E#, with seven sharps pitch class 0 is B# of the previous octave, and with at least six flats pitch class 11 is Cb of the next octave. Octave -1 starts at key 0. Invalid input returns None.
Why this case matters
Pitch spelling and interval arithmetic decide the notes that notation, transposition and analysis tools display or play.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
if not isinstance(x, list) or len(x) != 2:
return None
m, f = x
if not isinstance(m, int) or m < 0 or m > 127:
return None
if not isinstance(f, int) or f < -7 or f > 7:
return None
sharps = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']
flats = ['C', 'Db', 'D', 'Eb', 'E', 'F', 'Gb', 'G', 'Ab', 'A', 'Bb', 'B']
table = flats if f < 0 else sharps
pc = m % 12
octave = m // 12 - 1
name = table[pc]
if f >= 6 and pc == 5:
name = 'E#'
if f == 7 and pc == 0:
name = 'B#'
if f <= -6 and pc == 11:
name = 'Cb'
octave += 1
return name + str(octave)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[([60, 0], 'C4'), ([61, 0], 'C#4'), ([61, -1], 'Db4'), ([61, 1], 'C#4'), ([70, -2], 'Bb4'), ([60, 7], 'B#3'), ([72, 7], 'B#4'), ([0, 7], 'B#-2')], [([61, 1], 'C#4'), ([70, -2], 'Bb4'), ([70, 2], 'A#4'), ([65, 6], 'E#4'), ([65, 5], 'F4'), ([60, 7], 'B#3'), ([48, 7], 'B#2'), ([0, 7], 'B#-2')], [([65, 6], 'E#4'), ([65, 5], 'F4'), ([65, 7], 'E#4'), ([65, -6], 'F4'), ([60, 7], 'B#3'), ([48, 7], 'B#2'), ([71, -6], 'Cb5'), ([72, 7], 'B#4')], [([65, -6], 'F4'), ([48, 7], 'B#2'), ([71, -6], 'Cb5'), ([71, -7], 'Cb5'), ([59, -6], 'Cb4'), ([0, 0], 'C-1'), ([72, 7], 'B#4'), ([0, 7], 'B#-2')], [([60, 7], 'B#3'), ([59, -6], 'Cb4'), ([0, 0], 'C-1'), ([1, -3], 'Db-1'), ([11, 0], 'B-1'), ([127, 0], 'G9'), ([72, 7], 'B#4'), ([0, 7], 'B#-2')]]
for i, (args, expected) in enumerate(fixtures[N-1]):
check("oracle %d" % i, solve(args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| oracle 0 | C4 | C4 | Passed |
| oracle 1 | C#4 | C#4 | Passed |
| oracle 2 | Db4 | Db4 | Passed |
| oracle 3 | C#4 | C#4 | Passed |
| oracle 4 | Bb4 | Bb4 | Passed |
| oracle 5 | B#4 | B#3 | Failed |
| oracle 6 | B#5 | B#4 | Failed |
| oracle 7 | B#-1 | B#-2 | Failed |
SHA-256 / 64a550cb2624e264a1295038a9e96b7451699222d75dcc4304348d4b2503eb66
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
if not isinstance(x, list) or len(x) != 2:
return None
m, f = x
if not isinstance(m, int) or m < 0 or m > 127:
return None
if not isinstance(f, int) or f < -7 or f > 7:
return None
sharps = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']
flats = ['C', 'Db', 'D', 'Eb', 'E', 'F', 'Gb', 'G', 'Ab', 'A', 'Bb', 'B']
table = flats if f < 0 else sharps
pc = m % 12
octave = m // 12 - 1
name = table[pc]
if f >= 6 and pc == 5:
name = 'E#'
if f == 7 and pc == 0:
name = 'B#'
octave += 1
if f <= -6 and pc == 11:
name = 'Cb'
octave += 1
return name + str(octave)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[([60, 0], 'C4'), ([61, 0], 'C#4'), ([61, -1], 'Db4'), ([61, 1], 'C#4'), ([70, -2], 'Bb4'), ([60, 7], 'B#3'), ([72, 7], 'B#4'), ([0, 7], 'B#-2')], [([61, 1], 'C#4'), ([70, -2], 'Bb4'), ([70, 2], 'A#4'), ([65, 6], 'E#4'), ([65, 5], 'F4'), ([60, 7], 'B#3'), ([48, 7], 'B#2'), ([0, 7], 'B#-2')], [([65, 6], 'E#4'), ([65, 5], 'F4'), ([65, 7], 'E#4'), ([65, -6], 'F4'), ([60, 7], 'B#3'), ([48, 7], 'B#2'), ([71, -6], 'Cb5'), ([72, 7], 'B#4')], [([65, -6], 'F4'), ([48, 7], 'B#2'), ([71, -6], 'Cb5'), ([71, -7], 'Cb5'), ([59, -6], 'Cb4'), ([0, 0], 'C-1'), ([72, 7], 'B#4'), ([0, 7], 'B#-2')], [([60, 7], 'B#3'), ([59, -6], 'Cb4'), ([0, 0], 'C-1'), ([1, -3], 'Db-1'), ([11, 0], 'B-1'), ([127, 0], 'G9'), ([72, 7], 'B#4'), ([0, 7], 'B#-2')]]
for i, (args, expected) in enumerate(fixtures[N-1]):
check("oracle %d" % i, solve(args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| oracle 0 | C4 | C4 | Passed |
| oracle 1 | C#4 | C#4 | Passed |
| oracle 2 | Db4 | Db4 | Passed |
| oracle 3 | C#4 | C#4 | Passed |
| oracle 4 | Bb4 | Bb4 | Passed |
| oracle 5 | B#5 | B#3 | Failed |
| oracle 6 | B#6 | B#4 | Failed |
| oracle 7 | B#0 | B#-2 | Failed |
SHA-256 / bfc8d7c66f0c0585cbb90dd2f63730a10cc76318b80a3a475a904078680f0a20
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
if not isinstance(x, list) or len(x) != 2:
return None
m, f = x
if not isinstance(m, int) or m < 0 or m > 127:
return None
if not isinstance(f, int) or f < -7 or f > 7:
return None
sharps = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']
flats = ['C', 'Db', 'D', 'Eb', 'E', 'F', 'Gb', 'G', 'Ab', 'A', 'Bb', 'B']
table = flats if f < 0 else sharps
pc = m % 12
octave = m // 12 - 1
name = table[pc]
if f >= 6 and pc == 5:
name = 'E#'
if f == 7 and pc == 0:
name = 'B#'
octave -= 1
if f <= -6 and pc == 11:
name = 'Cb'
octave += 1
return name + str(octave)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[([60, 0], 'C4'), ([61, 0], 'C#4'), ([61, -1], 'Db4'), ([61, 1], 'C#4'), ([70, -2], 'Bb4'), ([60, 7], 'B#3'), ([72, 7], 'B#4'), ([0, 7], 'B#-2')], [([61, 1], 'C#4'), ([70, -2], 'Bb4'), ([70, 2], 'A#4'), ([65, 6], 'E#4'), ([65, 5], 'F4'), ([60, 7], 'B#3'), ([48, 7], 'B#2'), ([0, 7], 'B#-2')], [([65, 6], 'E#4'), ([65, 5], 'F4'), ([65, 7], 'E#4'), ([65, -6], 'F4'), ([60, 7], 'B#3'), ([48, 7], 'B#2'), ([71, -6], 'Cb5'), ([72, 7], 'B#4')], [([65, -6], 'F4'), ([48, 7], 'B#2'), ([71, -6], 'Cb5'), ([71, -7], 'Cb5'), ([59, -6], 'Cb4'), ([0, 0], 'C-1'), ([72, 7], 'B#4'), ([0, 7], 'B#-2')], [([60, 7], 'B#3'), ([59, -6], 'Cb4'), ([0, 0], 'C-1'), ([1, -3], 'Db-1'), ([11, 0], 'B-1'), ([127, 0], 'G9'), ([72, 7], 'B#4'), ([0, 7], 'B#-2')]]
for i, (args, expected) in enumerate(fixtures[N-1]):
check("oracle %d" % i, solve(args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| oracle 0 | C4 | C4 | Passed |
| oracle 1 | C#4 | C#4 | Passed |
| oracle 2 | Db4 | Db4 | Passed |
| oracle 3 | C#4 | C#4 | Passed |
| oracle 4 | Bb4 | Bb4 | Passed |
| oracle 5 | B#3 | B#3 | Passed |
| oracle 6 | B#4 | B#4 | Passed |
| oracle 7 | B#-2 | B#-2 | Passed |
SHA-256 / b041d3978623e67ade7b8c9e69ff8a08fa4ac118460f0f46014e94dbf44cff20
Verification & scope
A deterministic bounded teaching model with a stipulated toy contract; it is not a complete music notation or theory engine. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.
Observations recorded using Python 3.12.14 at 2026-09-29T14:49:57.989223+00:00.
Case digest / 47989c0074e9fc464d3bff1b1ee32e9176d85205050dca12c3dac4d7984c9796