FA-81811 / MIDI event timing / Open access
MIDI Time Code quarter-frame assembly: rate code read from the hours bit · case 01
The frame rate is decoded from the hours bit and one rate bit, so 25 fps material is carried at the wrong rate.
ROOT CAUSE
The rate code is taken from bits 0-1 instead of bits 1-2 of piece 7.
VERIFIED REPAIR
Restore the rate code extraction step so that it reads `(nib[7] >> 1) & 0x3`.
Unsuccessful approach: Shifting by two reads only the upper rate bit and a reserved bit.
Case contract
Input the data bytes of received quarter-frame messages. The last eight must carry piece numbers 0..7 (high nibble) in order; low nibbles give frames (pieces 0,1 as low/high nibble), seconds (2,3), minutes (4,5) and hours (6 low nibble, piece 7 bit 0 as hours bit 4); piece 7 bits 1-2 give the rate code (0: 24, 1: 25, 2: 30 drop, 3: 30 fps). The reported time is the decoded time plus 2 frames, carried into seconds, minutes and hours (mod 24). Return [h, m, s, f, rate_code] or None.
Why this case matters
MIDI sequencers, file readers and synth drivers depend on exact event ordering and tick/time arithmetic.
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) < 8:
return None
last8 = x[-8:]
if [b >> 4 for b in last8] != list(range(8)):
return None
nib = [b & 0x0F for b in last8]
frames = nib[0] | (nib[1] << 4)
secs = nib[2] | (nib[3] << 4)
mins = nib[4] | (nib[5] << 4)
hours = nib[6] | ((nib[7] & 0x1) << 4)
rate = nib[7] & 0x3
fps = [24, 25, 30, 30][rate]
frames += 2
secs += frames // fps
frames %= fps
mins += secs // 60
secs %= 60
hours = (hours + mins // 60) % 24
mins %= 60
return [hours, mins, secs, frames, rate]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[([0, 16, 32, 48, 64, 80, 96, 112], [0, 0, 0, 2, 0]), ([5, 17, 32, 48, 64, 80, 96, 114], [0, 0, 0, 23, 1]), ([10, 16, 47, 48, 78, 81, 97, 117], [17, 30, 15, 12, 2]), ([0, 16, 32, 48, 64, 80, 100, 119], [20, 0, 0, 2, 3]), ([0, 16, 32, 48, 64, 80, 96], None), ([16, 32, 48, 64, 80, 96, 112, 0], None), ([3, 0, 16, 32, 48, 64, 80, 98, 112], [2, 0, 0, 2, 0]), ([9, 16, 41, 48, 73, 80, 105, 112], [9, 9, 9, 11, 0])], [([5, 17, 32, 48, 64, 80, 96, 114], [0, 0, 0, 23, 1]), ([4, 16, 35, 48, 66, 80, 97, 114], [1, 2, 3, 6, 1]), ([10, 16, 47, 48, 78, 81, 97, 117], [17, 30, 15, 12, 2]), ([0, 16, 32, 48, 64, 80, 100, 119], [20, 0, 0, 2, 3]), ([7, 17, 43, 51, 75, 83, 103, 113], [0, 0, 0, 1, 0]), ([12, 17, 43, 51, 75, 83, 103, 119], [0, 0, 0, 0, 3]), ([3, 0, 16, 32, 48, 64, 80, 98, 112], [2, 0, 0, 2, 0]), ([9, 16, 41, 48, 73, 80, 105, 112], [9, 9, 9, 11, 0])], [([4, 16, 35, 48, 66, 80, 97, 114], [1, 2, 3, 6, 1]), ([10, 16, 47, 48, 78, 81, 97, 117], [17, 30, 15, 12, 2]), ([0, 16, 32, 48, 64, 80, 100, 119], [20, 0, 0, 2, 3]), ([7, 17, 43, 51, 75, 83, 103, 113], [0, 0, 0, 1, 0]), ([12, 17, 43, 51, 75, 83, 103, 119], [0, 0, 0, 0, 3]), ([8, 17, 43, 51, 75, 83, 97, 114], [2, 0, 0, 1, 1]), ([7, 16, 38, 48, 69, 80, 96, 115], [16, 5, 6, 9, 1]), ([13, 17, 43, 51, 64, 80, 99, 117], [19, 1, 0, 1, 2])], [([0, 16, 32, 48, 64, 80, 100, 119], [20, 0, 0, 2, 3]), ([7, 17, 43, 51, 75, 83, 103, 113], [0, 0, 0, 1, 0]), ([12, 17, 43, 51, 75, 83, 103, 119], [0, 0, 0, 0, 3]), ([8, 17, 43, 51, 75, 83, 97, 114], [2, 0, 0, 1, 1]), ([7, 16, 38, 48, 69, 80, 96, 115], [16, 5, 6, 9, 1]), ([13, 17, 43, 51, 64, 80, 99, 117], [19, 1, 0, 1, 2]), ([6, 17, 43, 51, 75, 83, 102, 113], [23, 0, 0, 0, 0]), ([4, 17, 40, 51, 66, 82, 108, 118], [12, 34, 56, 22, 3])], [([0, 16, 32, 48, 64, 80, 96, 112], [0, 0, 0, 2, 0]), ([12, 17, 43, 51, 75, 83, 103, 119], [0, 0, 0, 0, 3]), ([8, 17, 43, 51, 75, 83, 97, 114], [2, 0, 0, 1, 1]), ([7, 16, 38, 48, 69, 80, 96, 115], [16, 5, 6, 9, 1]), ([13, 17, 43, 51, 64, 80, 99, 117], [19, 1, 0, 1, 2]), ([6, 17, 43, 51, 75, 83, 102, 113], [23, 0, 0, 0, 0]), ([4, 17, 40, 51, 66, 82, 108, 118], [12, 34, 56, 22, 3]), ([8, 17, 43, 51, 75, 83, 103, 115], [0, 0, 0, 1, 1])]]
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 | [0, 0, 0, 2, 0] | [0, 0, 0, 2, 0] | Passed |
| oracle 1 | [0, 0, 0, 23, 2] | [0, 0, 0, 23, 1] | Failed |
| oracle 2 | [17, 30, 15, 12, 1] | [17, 30, 15, 12, 2] | Failed |
| oracle 3 | [20, 0, 0, 2, 3] | [20, 0, 0, 2, 3] | Passed |
| oracle 4 | None | None | Passed |
| oracle 5 | None | None | Passed |
| oracle 6 | [2, 0, 0, 2, 0] | [2, 0, 0, 2, 0] | Passed |
| oracle 7 | [9, 9, 9, 11, 0] | [9, 9, 9, 11, 0] | Passed |
SHA-256 / 5ab2e4e91f4dfa42e8e1bef820eded0a2f7f30dafc914e862cb2af12a4eb9d17
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) < 8:
return None
last8 = x[-8:]
if [b >> 4 for b in last8] != list(range(8)):
return None
nib = [b & 0x0F for b in last8]
frames = nib[0] | (nib[1] << 4)
secs = nib[2] | (nib[3] << 4)
mins = nib[4] | (nib[5] << 4)
hours = nib[6] | ((nib[7] & 0x1) << 4)
rate = (nib[7] >> 2) & 0x3
fps = [24, 25, 30, 30][rate]
frames += 2
secs += frames // fps
frames %= fps
mins += secs // 60
secs %= 60
hours = (hours + mins // 60) % 24
mins %= 60
return [hours, mins, secs, frames, rate]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[([0, 16, 32, 48, 64, 80, 96, 112], [0, 0, 0, 2, 0]), ([5, 17, 32, 48, 64, 80, 96, 114], [0, 0, 0, 23, 1]), ([10, 16, 47, 48, 78, 81, 97, 117], [17, 30, 15, 12, 2]), ([0, 16, 32, 48, 64, 80, 100, 119], [20, 0, 0, 2, 3]), ([0, 16, 32, 48, 64, 80, 96], None), ([16, 32, 48, 64, 80, 96, 112, 0], None), ([3, 0, 16, 32, 48, 64, 80, 98, 112], [2, 0, 0, 2, 0]), ([9, 16, 41, 48, 73, 80, 105, 112], [9, 9, 9, 11, 0])], [([5, 17, 32, 48, 64, 80, 96, 114], [0, 0, 0, 23, 1]), ([4, 16, 35, 48, 66, 80, 97, 114], [1, 2, 3, 6, 1]), ([10, 16, 47, 48, 78, 81, 97, 117], [17, 30, 15, 12, 2]), ([0, 16, 32, 48, 64, 80, 100, 119], [20, 0, 0, 2, 3]), ([7, 17, 43, 51, 75, 83, 103, 113], [0, 0, 0, 1, 0]), ([12, 17, 43, 51, 75, 83, 103, 119], [0, 0, 0, 0, 3]), ([3, 0, 16, 32, 48, 64, 80, 98, 112], [2, 0, 0, 2, 0]), ([9, 16, 41, 48, 73, 80, 105, 112], [9, 9, 9, 11, 0])], [([4, 16, 35, 48, 66, 80, 97, 114], [1, 2, 3, 6, 1]), ([10, 16, 47, 48, 78, 81, 97, 117], [17, 30, 15, 12, 2]), ([0, 16, 32, 48, 64, 80, 100, 119], [20, 0, 0, 2, 3]), ([7, 17, 43, 51, 75, 83, 103, 113], [0, 0, 0, 1, 0]), ([12, 17, 43, 51, 75, 83, 103, 119], [0, 0, 0, 0, 3]), ([8, 17, 43, 51, 75, 83, 97, 114], [2, 0, 0, 1, 1]), ([7, 16, 38, 48, 69, 80, 96, 115], [16, 5, 6, 9, 1]), ([13, 17, 43, 51, 64, 80, 99, 117], [19, 1, 0, 1, 2])], [([0, 16, 32, 48, 64, 80, 100, 119], [20, 0, 0, 2, 3]), ([7, 17, 43, 51, 75, 83, 103, 113], [0, 0, 0, 1, 0]), ([12, 17, 43, 51, 75, 83, 103, 119], [0, 0, 0, 0, 3]), ([8, 17, 43, 51, 75, 83, 97, 114], [2, 0, 0, 1, 1]), ([7, 16, 38, 48, 69, 80, 96, 115], [16, 5, 6, 9, 1]), ([13, 17, 43, 51, 64, 80, 99, 117], [19, 1, 0, 1, 2]), ([6, 17, 43, 51, 75, 83, 102, 113], [23, 0, 0, 0, 0]), ([4, 17, 40, 51, 66, 82, 108, 118], [12, 34, 56, 22, 3])], [([0, 16, 32, 48, 64, 80, 96, 112], [0, 0, 0, 2, 0]), ([12, 17, 43, 51, 75, 83, 103, 119], [0, 0, 0, 0, 3]), ([8, 17, 43, 51, 75, 83, 97, 114], [2, 0, 0, 1, 1]), ([7, 16, 38, 48, 69, 80, 96, 115], [16, 5, 6, 9, 1]), ([13, 17, 43, 51, 64, 80, 99, 117], [19, 1, 0, 1, 2]), ([6, 17, 43, 51, 75, 83, 102, 113], [23, 0, 0, 0, 0]), ([4, 17, 40, 51, 66, 82, 108, 118], [12, 34, 56, 22, 3]), ([8, 17, 43, 51, 75, 83, 103, 115], [0, 0, 0, 1, 1])]]
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 | [0, 0, 0, 2, 0] | [0, 0, 0, 2, 0] | Passed |
| oracle 1 | [0, 0, 0, 23, 0] | [0, 0, 0, 23, 1] | Failed |
| oracle 2 | [17, 30, 15, 12, 1] | [17, 30, 15, 12, 2] | Failed |
| oracle 3 | [20, 0, 0, 2, 1] | [20, 0, 0, 2, 3] | Failed |
| oracle 4 | None | None | Passed |
| oracle 5 | None | None | Passed |
| oracle 6 | [2, 0, 0, 2, 0] | [2, 0, 0, 2, 0] | Passed |
| oracle 7 | [9, 9, 9, 11, 0] | [9, 9, 9, 11, 0] | Passed |
SHA-256 / 2cfbf858032b548d74bc815ecf43d57e077684216f7c6faec24c2fe1cac581cd
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) < 8:
return None
last8 = x[-8:]
if [b >> 4 for b in last8] != list(range(8)):
return None
nib = [b & 0x0F for b in last8]
frames = nib[0] | (nib[1] << 4)
secs = nib[2] | (nib[3] << 4)
mins = nib[4] | (nib[5] << 4)
hours = nib[6] | ((nib[7] & 0x1) << 4)
rate = (nib[7] >> 1) & 0x3
fps = [24, 25, 30, 30][rate]
frames += 2
secs += frames // fps
frames %= fps
mins += secs // 60
secs %= 60
hours = (hours + mins // 60) % 24
mins %= 60
return [hours, mins, secs, frames, rate]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[([0, 16, 32, 48, 64, 80, 96, 112], [0, 0, 0, 2, 0]), ([5, 17, 32, 48, 64, 80, 96, 114], [0, 0, 0, 23, 1]), ([10, 16, 47, 48, 78, 81, 97, 117], [17, 30, 15, 12, 2]), ([0, 16, 32, 48, 64, 80, 100, 119], [20, 0, 0, 2, 3]), ([0, 16, 32, 48, 64, 80, 96], None), ([16, 32, 48, 64, 80, 96, 112, 0], None), ([3, 0, 16, 32, 48, 64, 80, 98, 112], [2, 0, 0, 2, 0]), ([9, 16, 41, 48, 73, 80, 105, 112], [9, 9, 9, 11, 0])], [([5, 17, 32, 48, 64, 80, 96, 114], [0, 0, 0, 23, 1]), ([4, 16, 35, 48, 66, 80, 97, 114], [1, 2, 3, 6, 1]), ([10, 16, 47, 48, 78, 81, 97, 117], [17, 30, 15, 12, 2]), ([0, 16, 32, 48, 64, 80, 100, 119], [20, 0, 0, 2, 3]), ([7, 17, 43, 51, 75, 83, 103, 113], [0, 0, 0, 1, 0]), ([12, 17, 43, 51, 75, 83, 103, 119], [0, 0, 0, 0, 3]), ([3, 0, 16, 32, 48, 64, 80, 98, 112], [2, 0, 0, 2, 0]), ([9, 16, 41, 48, 73, 80, 105, 112], [9, 9, 9, 11, 0])], [([4, 16, 35, 48, 66, 80, 97, 114], [1, 2, 3, 6, 1]), ([10, 16, 47, 48, 78, 81, 97, 117], [17, 30, 15, 12, 2]), ([0, 16, 32, 48, 64, 80, 100, 119], [20, 0, 0, 2, 3]), ([7, 17, 43, 51, 75, 83, 103, 113], [0, 0, 0, 1, 0]), ([12, 17, 43, 51, 75, 83, 103, 119], [0, 0, 0, 0, 3]), ([8, 17, 43, 51, 75, 83, 97, 114], [2, 0, 0, 1, 1]), ([7, 16, 38, 48, 69, 80, 96, 115], [16, 5, 6, 9, 1]), ([13, 17, 43, 51, 64, 80, 99, 117], [19, 1, 0, 1, 2])], [([0, 16, 32, 48, 64, 80, 100, 119], [20, 0, 0, 2, 3]), ([7, 17, 43, 51, 75, 83, 103, 113], [0, 0, 0, 1, 0]), ([12, 17, 43, 51, 75, 83, 103, 119], [0, 0, 0, 0, 3]), ([8, 17, 43, 51, 75, 83, 97, 114], [2, 0, 0, 1, 1]), ([7, 16, 38, 48, 69, 80, 96, 115], [16, 5, 6, 9, 1]), ([13, 17, 43, 51, 64, 80, 99, 117], [19, 1, 0, 1, 2]), ([6, 17, 43, 51, 75, 83, 102, 113], [23, 0, 0, 0, 0]), ([4, 17, 40, 51, 66, 82, 108, 118], [12, 34, 56, 22, 3])], [([0, 16, 32, 48, 64, 80, 96, 112], [0, 0, 0, 2, 0]), ([12, 17, 43, 51, 75, 83, 103, 119], [0, 0, 0, 0, 3]), ([8, 17, 43, 51, 75, 83, 97, 114], [2, 0, 0, 1, 1]), ([7, 16, 38, 48, 69, 80, 96, 115], [16, 5, 6, 9, 1]), ([13, 17, 43, 51, 64, 80, 99, 117], [19, 1, 0, 1, 2]), ([6, 17, 43, 51, 75, 83, 102, 113], [23, 0, 0, 0, 0]), ([4, 17, 40, 51, 66, 82, 108, 118], [12, 34, 56, 22, 3]), ([8, 17, 43, 51, 75, 83, 103, 115], [0, 0, 0, 1, 1])]]
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 | [0, 0, 0, 2, 0] | [0, 0, 0, 2, 0] | Passed |
| oracle 1 | [0, 0, 0, 23, 1] | [0, 0, 0, 23, 1] | Passed |
| oracle 2 | [17, 30, 15, 12, 2] | [17, 30, 15, 12, 2] | Passed |
| oracle 3 | [20, 0, 0, 2, 3] | [20, 0, 0, 2, 3] | Passed |
| oracle 4 | None | None | Passed |
| oracle 5 | None | None | Passed |
| oracle 6 | [2, 0, 0, 2, 0] | [2, 0, 0, 2, 0] | Passed |
| oracle 7 | [9, 9, 9, 11, 0] | [9, 9, 9, 11, 0] | Passed |
SHA-256 / bef3162f4854ccddc2add5d02490ddd180274438f9826951f32533915703757a
Verification & scope
A deterministic bounded teaching model with a stipulated toy contract; it is not a complete Standard MIDI File or MIDI 1.0/2.0 implementation. 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:50:06.347613+00:00.
Case digest / 8d1d5671e328498d6a378b88e757fd7e1bc1d0ac00bd2c2c80d92c5a80364f6f