1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768(* Claude Code
*
* Copyright (C) 2026 Yoann Padioleau
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* (LGPL) as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*)(* See Eq.mli *)(* three bands a side *)typet={left:Filter.memoryarray;right:Filter.memoryarray;mutablelast:floatarray;(* the last block's gains, [||]: none yet *)}letcreate():t=letbands()=Array.init3(fun_->Filter.silence())in{left=bands();right=bands();last=[||]}letbands(g:floatarray):Filter.biquadarray=[|Filter.low_shelf~frequency:200.~gain:g.(0);Filter.peaking~frequency:1000.~q:0.7~gain:g.(1);Filter.high_shelf~frequency:4000.~gain:g.(2);|](* a gain turned is ramped too, but its coefficients can't be
* multiplied in: they are recomputed every [step] samples, the gains
* part of the way there each time (the common compromise: 32 samples,
* 0.7 ms, too short a step to hear) *)letstep=32letprocess(t:t)~(bass:float)~(middle:float)~(treble:float)(s:Signal.stereo):unit=letnow=[|bass;middle;treble|]andn=Array.lengths.leftinletlast=ift.last=[||]thennowelset.lastinleti=ref0inwhile!i<ndoletlen=minstep(n-!i)inletf=bands(Array.mapi(funkg->Effect.ramplast.(k)g(!i+len-1)n)now)inforj=!ito!i+len-1dofork=0to2dos.left.(j)<-Filter.stepf.(k)t.left.(k)s.left.(j);s.right.(j)<-Filter.stepf.(k)t.right.(k)s.right.(j)donedone;i:=!i+lendone;t.last<-nowletknobs:Effect.knoblist=List.map(funname:Effect.knob->{name;control=Knob(-12.,12.);initial=0.})["bass";"middle";"treble"]letfx():Effect.t=lett=create()andgains=[|0.;0.;0.|]inletset(knob:string)(x:float)=matchknobwith"bass"->gains.(0)<-x|"middle"->gains.(1)<-x|"treble"->gains.(2)<-x|_->()in{name="eq";knobs;set;process=(funs->processt~bass:gains.(0)~middle:gains.(1)~treble:gains.(2)s);meters=(fun()->[]);}