feat(tosc): clip quantize and queued blink
CI build oscope-of / build-check (push) Has been cancelled
CI build oscope-of / build-check (push) Has been cancelled
Add 4 /launch/quant buttons (1/2, 1, 1BAR, 2BAR) with constf args 0.5/1/4/8 to LIVE page top bar. Extend pad.lua: onReceiveOSC reads state int 0-3 (off/playing/queued-launch/queued-stop); onFrame pulses amber when blinking=true. Test: test_live_page_quant_buttons asserts CONSTANT/FLOAT args. 22 tests pass; dist rebuilt (8482 bytes).
This commit is contained in:
Vendored
BIN
Binary file not shown.
@@ -138,6 +138,14 @@ def live_page():
|
||||
messages=[s.osc("/sync/beat", [s.val()], send=0, receive=1, feedback=1)],
|
||||
))
|
||||
|
||||
# Quantize selector row (send-only momentary): 1/2 beat / 1 beat / 1 bar / 2 bars
|
||||
quant_defs = [("1/2", 0.5), ("1", 1), ("1 BAR", 4), ("2 BAR", 8)]
|
||||
qx_start, qy, qw, qh, qgap = 625, 8, 130, 50, 8
|
||||
for i, (lbl, beats) in enumerate(quant_defs):
|
||||
qx = qx_start + i * (qw + qgap)
|
||||
ch.append(button(lbl, "/launch/quant", qx, qy, qw, qh, args=[s.constf(beats)]))
|
||||
ch.append(label("QUANT", qx_start, 62, 120, 20))
|
||||
|
||||
# 4x4 pad grid -----------------------------------------------------------
|
||||
gx, gy, pw, ph, gap = 10, 96, 283, 178, 10
|
||||
for i, name in enumerate(PATTERNS):
|
||||
|
||||
@@ -1,16 +1,33 @@
|
||||
-- Pad GROUP: reflect armed state from SC engine onto the arm button color.
|
||||
-- API verified on GrosMac at fidelity gate.
|
||||
-- self.children[1] is the arm BUTTON; color driven by /armed/<name>.
|
||||
-- State int: 0=off, 1=playing (solid green), 2=queued-launch (blink), 3=queued-stop (blink).
|
||||
local blinking = false
|
||||
|
||||
function onReceiveOSC(message, connections)
|
||||
local path = message[1]
|
||||
local name = self.name:gsub("pad_", "")
|
||||
if path == "/armed/" .. name then
|
||||
local args = message[2]
|
||||
local on = (args and args[1] and tonumber(args[1].value)) or 0
|
||||
local state = math.floor((args and args[1] and tonumber(args[1].value)) or 0)
|
||||
if self.children and self.children[1] then
|
||||
self.children[1].color = on > 0.5
|
||||
and Color(0.27, 0.80, 0.40, 1)
|
||||
or Color(0.11, 0.11, 0.11, 1)
|
||||
if state == 0 then
|
||||
blinking = false
|
||||
self.children[1].color = Color(0.11, 0.11, 0.11, 1)
|
||||
elseif state == 1 then
|
||||
blinking = false
|
||||
self.children[1].color = Color(0.27, 0.80, 0.40, 1)
|
||||
else
|
||||
-- state 2 (queued-launch) or 3 (queued-stop): blink amber
|
||||
blinking = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function onFrame()
|
||||
if blinking and self.children and self.children[1] then
|
||||
local phase = (math.sin(getTime() * math.pi * 2.5) + 1) * 0.5
|
||||
self.children[1].color = Color(0.80 * phase, 0.60 * phase, 0.10, 1)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -249,6 +249,31 @@ def test_seq_page_no_sync_amp():
|
||||
assert "/sync/amp" not in addrs, "/sync/amp found — per-voice VU was supposed to be omitted"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Page 1 — LIVE: quantize selector
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def test_live_page_quant_buttons():
|
||||
"""LIVE page must have 4 /launch/quant buttons with CONSTANT/FLOAT args {0.5,1,4,8}."""
|
||||
page = layout.live_page()
|
||||
quant_args = []
|
||||
for osc_el in page.findall(".//messages/osc"):
|
||||
path_val = osc_el.find("path/partial/value")
|
||||
if path_val is not None and path_val.text == "/launch/quant":
|
||||
for part in osc_el.findall("arguments/partial"):
|
||||
typ = part.find("type")
|
||||
conv = part.find("conversion")
|
||||
val = part.find("value")
|
||||
if typ is not None and conv is not None and val is not None:
|
||||
quant_args.append((typ.text, conv.text, val.text))
|
||||
assert len(quant_args) == 4, f"expected 4 /launch/quant arg partials, got {len(quant_args)}"
|
||||
for typ, conv, _ in quant_args:
|
||||
assert typ == "CONSTANT", f"quant arg type must be CONSTANT, got {typ}"
|
||||
assert conv == "FLOAT", f"quant arg conversion must be FLOAT, got {conv}"
|
||||
vals = {v for _, _, v in quant_args}
|
||||
assert vals == {"0.5", "1", "4", "8"}, f"expected quant values {{0.5,1,4,8}}, got {vals}"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Pad armed receive on GROUP — FIX 1 regression guard
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user