-- Stub just enough of the reaper API to exercise the pure logic in the lib. local ext = { root = "0", scale = "1" } reaper = { GetExtState = function(_, k) return ext[k] or "" end, SetExtState = function(_, k, v) ext[k] = v end, APIExists = function() return false end, ShowConsoleMsg = function() end, SNM_GetIntConfigVar = function(_, d) return 1 end, GetCursorPosition = function() return 0 end, TimeMap_GetTimeSigAtTime = function() return 4, 4 end, } local G = dofile("D:\\code\\midigrid\\midigrid_lib.lua") local fails = 0 local function eq(got, want, label) if got ~= want then print(("FAIL %s: got %s want %s"):format(label, tostring(got), tostring(want))) fails = fails + 1 end end -- Note naming: with midioctoffs=1, note 60 reads as C5 (Reaper's default). eq(G.noteName(60), "C5", "noteName 60") eq(G.noteName(61), "C#5", "noteName 61") eq(G.noteName(72), "C6", "noteName 72") eq(G.noteName(48), "C4", "noteName 48") -- C major: stepping up from C5 walks the scale, not semitones. ext.root, ext.scale = "0", "1" local p, seq = 60, {} for _ = 1, 7 do p = G.scaleStep(p, 1); seq[#seq + 1] = p end eq(table.concat(seq, ","), "62,64,65,67,69,71,72", "C major ascending") p, seq = 60, {} for _ = 1, 3 do p = G.scaleStep(p, -1); seq[#seq + 1] = p end eq(table.concat(seq, ","), "59,57,55", "C major descending") -- An off-scale note must not trap the cursor: C#5 in C major steps to D5. eq(G.scaleStep(61, 1), 62, "off-scale up") eq(G.scaleStep(61, -1), 60, "off-scale down") -- Pentatonic skips more than a tone. ext.scale = "10" -- major pentatonic eq(G.scaleStep(60, 1), 62, "pentatonic C->D") eq(G.scaleStep(64, 1), 67, "pentatonic E->G") -- Chromatic behaves like plain semitones. ext.scale = "14" eq(G.scaleStep(60, 1), 61, "chromatic up") -- Root transposition: A natural minor. ext.root, ext.scale = "9", "2" eq(G.inScale(69), true, "A in A minor") eq(G.inScale(70), false, "A# not in A minor") eq(G.scaleName(), "A natural minor", "scale name") -- Range clamping at the extremes of the MIDI range. ext.root, ext.scale = "0", "1" eq(G.scaleStep(127, 1), nil, "top of range") eq(G.scaleStep(0, -1), nil, "bottom of range") -- Grid labels in 4/4: one bar is 4 QN. eq(G.gridLabel(4), "1 bar", "grid 1 bar") eq(G.gridLabel(8), "2 bars", "grid 2 bars") eq(G.gridLabel(1), "1/4", "grid quarter") eq(G.gridLabel(0.25), "1/16", "grid sixteenth") eq(G.gridLabel(0.5), "1/8", "grid eighth") print(fails == 0 and "ALL PASS" or (fails .. " FAILURES"))