123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566(* 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 Phaser.mli *)typesettings={low:float;high:float;rate:float;feedback:float;mix:float}letinitial={low=200.;high=3000.;rate=0.3;feedback=0.3;mix=1.}letstages=4letrate=float_of_intSignal.rate(* a first-order all-pass's memory: its last input and output *)typestage={mutablex1:float;mutabley1:float}(* one side: its four stages, and the last output, fed back *)typeside={stages:stagearray;mutablelast:float}typet={left:side;right:side;mutablephase:float;(* the LFO's, in turns *)(* the last block's, ramped from (nan: none yet) *)mutablelast_mix:float;}letside()={stages=Array.initstages(fun_->{x1=0.;y1=0.});last=0.}letcreate():t={left=side();right=side();phase=0.;last_mix=Float.nan}letcoefficient(fc:float):float=letk=tan(Float.pi*.Float.minfc(0.49*.rate)/.rate)in(k-.1.)/.(k+.1.)letthrough(s:side)(a:float)(feedback:float)(x:float):float=letv=ref(x+.(feedback*.s.last))inArray.iter(funst->lety=(a*.!v)+.st.x1-.(a*.st.y1)inst.x1<-!v;st.y1<-y;v:=y)s.stages;s.last<-!v;!vletprocess(t:t)(s:settings)(out:Signal.stereo):unit=letn=Array.lengthout.leftandfeedback=Float.min0.9(Float.max(-0.9)s.feedback)inletfrom_mix=ifFloat.is_nant.last_mixthens.mixelset.last_mixin(* the break frequency at an LFO phase: evenly in octaves *)letfcphase=s.low*.Float.pow(s.high/.s.low)(0.5+.(0.5*.sin(2.*.Float.pi*.phase)))infori=0ton-1doletmix=Effect.rampfrom_mixs.mixininletwet_left=throught.left(coefficient(fct.phase))feedbackout.left.(i)andwet_right=throught.right(coefficient(fc(t.phase+.0.25)))feedbackout.right.(i)inout.left.(i)<-out.left.(i)+.(mix*.wet_left);out.right.(i)<-out.right.(i)+.(mix*.wet_right);t.phase<-t.phase+.(s.rate/.rate);ift.phase>=1.thent.phase<-t.phase-.1.done;t.last_mix<-s.mix