From 16f68efe011814b3cc0112f903a9ea64e24a6a46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=27=C3=A9lectron=20rare?= <108685187+electron-rare@users.noreply.github.com> Date: Sun, 28 Jun 2026 14:52:12 +0200 Subject: [PATCH] feat(tosc): clip quantize and queued blink 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). --- touchosc/dist/av-live-control.tosc | Bin 7930 -> 8482 bytes touchosc/gen/layout.py | 8 ++++++++ touchosc/gen/lua/pad.lua | 25 +++++++++++++++++++++---- touchosc/tests/test_layout.py | 25 +++++++++++++++++++++++++ 4 files changed, 54 insertions(+), 4 deletions(-) diff --git a/touchosc/dist/av-live-control.tosc b/touchosc/dist/av-live-control.tosc index 9827522ca68143a045d39f0772911347c6c36cd6..ae9fec41652bec1b58d3dbf782aa53d770a89cec 100644 GIT binary patch literal 8482 zcmZ{mbzBr&zwMa1*A(5=>|a>X}CV` zdGEdF+}{B|1MtDzhsVj@$C}5|)y2!h)rr^3)zcE=cTj*Q zX@AqXEavYQwSR>c;EtkWR5(-;fo%1sVdn`^1Bk&)%Zgy+3H$123D4k~^_Z zn;!zbwx#K_lIPcqlj-oHh}+ngB^OA{CJ_w)ml%M#EQWFlYLhc}Bc}F=i+5Uc<)zzR zbZ>Z3$RK-BQdFgA?wMO))16Q7fat?5&hj(r7Sh04<0iV>)$5w$*EgW+4k@A@H-0Mo z8$uu3Hu`;DOh4PIiNGqMR*9K#k>r8!MUiB>cAk*a11WkHFZf!C)k^Fe1T;C{;skETaS0e7N3-6XL>(!CQKpW+b+aNLTdW{yReJz0*7IJd&t z)Dq!Mbdh4oltlEztSrnvKSlP%(X<~C=V?;<Hv*6f!;TBhH~j(8-P&Y*v?CA&NsD#+n4%qx8B8K5QGa$%Yo@6X>(znQHV6bM``WNaa$;%7#j@dff-! z4qQF!i#GOpN3)J>66ir05=tyE8Jo%^EJf;1@fix>@vGEDQEwk!(T~&@CcNUS*GL#^ z8vHJoyp%4s`8!Z;KSP`Dx8%cl_rcXHR-0}AVPUfSFSh>8$7A#7rztt2$#nD*A?^(q zw;c5Potcc=aVHb;rM>K9A=w0NKZM!mU6woOBGwn<36ZoW^hM{Gk^Q4h+X1F*XBMtY zqhOO^3*RSPBCoEl!B}a>o6!5Zi?i*!i{XQtN~{3+!@Pw&U-q&kDQ39=a!;e&5trcf ztJB*FURbBZ(PGc3a(=QN^f_rb8a$Fuk1u)%vWsNOBjt^8#~lr6z4(1UHMVhEdS@&3 zNM}9JyMk=gpy)LYd^L#7<2BeSyY9_7NrX2^U<$P1+uP7ND+}?`@n$LdM~@^PR7fO) zTR;2$_$?XK3eOR>%n|};s6G!Yi^2}_DjlS@7}P6b%2+TWHTC&b77u@HHojtEwSu3< zBKL;GU3h*dT2kkm|Ao0Yz#s!3cX=s6HPcKyJ5-2e@0qKbp1 z6;o8n=RMNS@Go$LsA<5f+=g==_n*y&hXk>;1$d?DUYW)X4K~(Zy*#=hC#8+nJoNHd z#lcu4yesy-dwgJ4d%F{?zqMT6Nki|u?8WXP(XHuwx&Pu~XzZ!)No+t`EN(xn?OSag z?fU+a%KK6LwY8&{!3PyMr8ESsB%NMfKEKq}m0ISMI|5(b??z9!-3~{7FsY&ZxIE+; zvdh-u$!^VKXvISBEt>bEJHFp{Tm5RO4(77F+j`IMsp$Jta(_$yHt*i2>FMJkzgBHc8tyG`#b1MO3YV+XD zG{N$=DJa=mdZI-A?jI{rzo0dDFW*j2+iPS~b6cRM$FumHnV4Sm^>*w*O-bEEmY%0p(9XLM=-G}*qU(A}ifA&;xDtLSIgfisbS}yMj zoF~&M!}wBzu-TOPv1{0hA@gHBH1GwE4ubm&QKVW`Hhx9iyt41yTX91H|GCx8k<~ke z=!=V4m!tBzdk|@R>gl&66UxI=2I4ejMEIz*fHffhJ7%`PG*?B~lM;f%n!zih4Bljp_ub_6-B_5m(`S4a zSGjwX#1Ju!6cb-DI@!V77DSRS*$Vp=k+{&Dx>BF!c?*Q#6uNKL=YbyQv0Ubr>&wtqpkAIqg_PI#7lkQL2`Av5C z<&zH%En&2+B9oLiEyW*R2~rWXa^@b#V(UEfi;5U7(gV1>TCaa?J9tH3U)&}-XyiD4 z)x3D%<`M}#*WKRRd` z37QVQ_g4>Vj)mp2cV8cTIK05DIUx)@?)4LtFx_AJ^tji(VtLSxux;bovi;N_W~3)_ z7(Fq#$n92M7xb`s+4#9l7iPa_*4)@A8U4KiL}uV57xUi5*4F31+HGI5c44h~E__64 ztJ_B_M3i>%4v)+^K-{8mDso#?^00tC;K`>sX#Jkl{=f<0Vw;fJt($+l@+H+=@tIlk ztzz@`_Gs~!dag+$eXCZJp_CKOfpV@weC?LNSN1cD9bMkqBq#L+k?T!|V--`MWKJ$r z-34EDz*-Tg?GUL1qpce$nvH_WQ_3;N(WZVE6WG>4x@BKS_hOc)*sqM^w$Ecfb{evp zei!0;7h7shY0UV>q72|Zr}Y~?2#bTesWjH2EJX~(GEKd)3q@Tw+l3kdfj+2WxH7>~V3m_t1Y~I##EBT;qv0Fy!Ic!K zj3WvljK7@QjWHH!OO5{pL7N^3t<8O-BAw>7G7u^BS7qF{l8dsI4Da9m5FijWY`hCa zhyqaVkMKZ1ULJE{%n)Afrb7u9{!mh+jwqfx4!I5hQBsS%P zv}kO?wqw%J)yFfQQK@f@{vKw|5!tmB3YcX>i4fzVzhJ6*qhbcbh~_HKq?uv44QO6S zaxo@MW9sNV72b+C+q+1N-`X>V3F5WfT)aBNh9vFCGHO@~A>O;x5!n3yfHF!Jt{gD_ zMGMYL3|g-OPAjk||8z8~XT^5n-TyqEhMWQe70k^VfHiPFG|`W z-91=Wf}H`P7f4MpUWR`>D87|@~27i349}=`HtzQn= zdN3HH%F3-73RoCS5|AwL+69kaEb3AEp#V5_hj1x|bU&;moko9>(y6OG1-7~nphr0euX4TzK|~p}nnzj0sO%YuTx0d5cH)}EY5)KlY;c0h->fHvI@+2h z-3u6Zq-I-^vZKpKNOlCXK?vNsR~BYi!Q3ZbN_P(`sD~~DyUKi#hNMTav=+q)k#H9(H_G+5DF{-jE~hg;H(zR@CkdN8Dsms2AnG70u8Lzumh-< z^|^);4?U>#rVNn71S!S`Y`?+mQxFuuS$+Rzh8rEWFU*ewwLwg>FqQ^}QQ0ik-7;=7 zVF=~!m<^OB6S-=7iKXsOh>lR8BPJ-DeOigVTfmHhK9iyy0qoO|wND>!rG~lvf;ZvX zA^&&Z{4?v7x2#|z3)B=`lw62qoz?0kc6UkgidgBKu*D>)p)DjdlX4u5ioY9jbvHsK zPM;!8DcZguh~9TAvr20p@OabVQ+}Tzv@-BBT9u2~!RPJzY zw7WEioO8x^&=WTHE@k;tU9y<6@@AP-B#SUQjGx?}2qPUhkxzKbMD3$_RKFMj?D*Jr zh^~JsW(|X40y7kv&kU6vG&XwySxVc0n(Gw53kT&pDa|ALV3=PS3~cLqEauc#V#8+o z+$+5%tEut$sS0)WNYP~W9Ej%tYMgNmR1{E%Y$b@}UoFUisLd=P0;a$VEJ<9tl}EeS zq+=#c&^6lB8r=;dWXMtt)c5j3E?#^$9nK6>`>3#RjbYU35~?5B5kKfD?NE&IH^jvl zdC@?Vtjk4NG6a_raU{gqfI^Cx%wP~UfW*`PmYe>Tn@(e5=Y5+gS{2r=O_Z(1(7={s zAqQkCWfY)FJN}*uAnG@$kw<`l)I{Sb6ed`R0Sy4?mxD}dMVrZR_?2Wc@xswVlUqje zN8hv=G~qVd4YK-hf-3Xne-gs^w@HrnB0)0Ku|LvLlP5`u8v|czBUYI7m2y`yPQIrC z9TZ-Iz)$#jR({GJvB?0rMV-^Q`I>dX#Hm2OM35RDD?wUPuAU-ZmHq7u9*zMMKPNBB z-bf2%0akv*!=h8w&Z@OzghurThnUouBR~*pj&VdNCXSE>Iuxjn0#fpf@<1Rl)-bCS z#HWopo;nhhX-tKsxG?x?Y!whk=i?zh*$)m4bQCza1C2px#1W0B@kQr4i>kJQ+o5mV zfwW}!gD;N;I_^wZ19^%F-6QSJ!^ROU)!i@&qhP~o5#(@7V8R|LA^CFS1aU+GbrY9c zXGvB@#hAIr4U7s5_`2EFLamy+AFugg*Zu4X3t=jpaismf7V*jK&|SL9xMu(0BpZ)o zp%O6mC1=65qLUE8&XwB>$Bj}jvUTpJ{?ic(%xi2xLZG*=qyaS@c(EEpQC3%;is6(b zjG{fPzJh%#Z{PD8Jin&qs0|&$HOs?Z8aRiPt4wdk%+Tb%NO_8zM1@<`LivobUPs^X zWo^Z=F(JnhARD3mh<^t>T8jDFX=ze6<2<(Bu^ZGc4uLE`;07-|4aCfupg*Li`|iWW z(P*Kj*82kTiuaE>3-6CK&7Zb7Smo>g;_{gsRMZJmtbTI%Lqy5GRc-{+C=T= zyrq4!S9r~wm#=EL285w-dGiS_ZKdY=SIBg$5dO=ZDq+p0=*?8J37K-cpb- z=#rv7lcA0`&aHw-+(>&F!{j`8RkWXZg9Vb7i|1RPsRLeXWh zUAD0Z*r#K6Qq-wWm=MmoB-W@kToO`GjLm{E^#zArcddkg>|%^P`N8n@gZKKw*xG}B z$I;u54_Yy0!g2doZViBl7&2NVph*r#N4x^dyLHgr#r;&}`DIaT-$T(h@GkZ)_^&uo zcCi;Hqt!F`b&+!IAaPg93s8Z`vuSr!r{u!&&9*Pr!(Ds8NxKCLB9AsOllGFhRp0$` z8yR$Yg!Lu#!~2?4UYF0%mB!h6a+LxfY!ks3cqa^^HQWSPQuMm%SiQLtPK6RF=Q+^~ z8Omf5T)6ke6-XkGT|%5-L5bR1%m@>PKjGWzfAoWm?f6Ou5(52UVtY8c4zyZ0&*sPN zx{bR-6_j5y6B~SsqNAgw_0~w1+ZN4D!L^<+dvhOIC+bt0wz~5Bz)m!OLBSf}PpVIDhSWq)Zi-z*35Gye~)4l$`TM6oup3Q=KJTC6Iu z9)|t;*Y*!r6J{??$G?~}e`QSGm|?|*y^;QAmqb&IY3|ALJB-8h4x8ObF)l;gj!xp) zF9pe|u1hAdAE|6OMI1Q2{O%#h@B%)Dw}rBY85li@d_%^f{9=BXI^raWU#JG(L4l^9 zbOT8v@F^o2JCap|jDglrSqu9=Nfv`knmDLY97WpGzCDMqF1=9e?aNo=bUU zkQ<5v@yi1e@#M@7Kepb}c@!LQv{-XAHP!KwYcPyhmEwsah0?>KBSi$^ds3dVZ6&T1 zc%~Oof4Bd;E?mCYQGq0%k8;M9ls6BJg?eg5(`Q$Z`=tUKL0)mG&(9LeRe$eX{@%E> zn$c)El=x#S8LD0@mEhX8!Iaecsg~zjM*U`9Er9

G)!C4!f=XLvy*RCtoM$(oe6O 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 GsQ&@-UszrM diff --git a/touchosc/gen/layout.py b/touchosc/gen/layout.py index 3a749ab..a07a7f2 100644 --- a/touchosc/gen/layout.py +++ b/touchosc/gen/layout.py @@ -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): diff --git a/touchosc/gen/lua/pad.lua b/touchosc/gen/lua/pad.lua index 661f7f6..1d162dd 100644 --- a/touchosc/gen/lua/pad.lua +++ b/touchosc/gen/lua/pad.lua @@ -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/. +-- 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 diff --git a/touchosc/tests/test_layout.py b/touchosc/tests/test_layout.py index dff8728..40f3f3d 100644 --- a/touchosc/tests/test_layout.py +++ b/touchosc/tests/test_layout.py @@ -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 # ---------------------------------------------------------------------------