12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273(* 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 Modulated_delay.mli *)typesettings={center:float;depth:float;rate:float;feedback:float;mix:float}letchorus={center=0.015;depth=0.003;rate=0.5;feedback=0.;mix=0.5}letflanger={center=0.0025;depth=0.002;rate=0.2;feedback=0.5;mix=0.7}letlongest=0.05letrate=float_of_intSignal.rate(* the last [size] samples written, [at] the next to write *)typeline={samples:floatarray;mutableat:int;mutablelast:float(* the last copy, fed back *)}letsize=Signal.sampleslongest+4letline()={samples=Array.makesize0.;at=0;last=0.}(* [d] samples ago (d >= 1), between two samples: linear *)letread(l:line)(d:float):float=letback=Float.to_intdinletfrac=d-.float_of_intbackinletatk=l.samples.((l.at-k+(2*size))modsize)in((1.-.frac)*.atback)+.(frac*.at(back+1))letwrite(l:line)(x:float):unit=l.samples.(l.at)<-x;l.at<-(l.at+1)modsizetypet={left:line;right:line;mutablephase:float;(* the LFO's, in turns *)(* the last block's, ramped from (nan: none yet) *)mutablelast_mix:float;mutablelast_feedback:float;}letcreate():t={left=line();right=line();phase=0.;last_mix=Float.nan;last_feedback=Float.nan}letprocess(t:t)(s:settings)(out:Signal.stereo):unit=letn=Array.lengthout.leftinletfeedback=Float.min0.95(Float.max(-0.95)s.feedback)inletfrom_mix=ifFloat.is_nant.last_mixthens.mixelset.last_mixandfrom_feedback=ifFloat.is_nant.last_feedbackthenfeedbackelset.last_feedbackin(* the delay in samples at an LFO phase, kept within the line *)letdelayphase=letd=(s.center+.(s.depth*.sin(2.*.Float.pi*.phase)))*.rateinFloat.max1.(Float.min(float_of_int(size-3))d)inletside(l:line)(x:float)(phase:float)(mix:float)(feedback:float):float=letcopy=readl(delayphase)inwritel(x+.(feedback*.l.last));l.last<-copy;x+.(mix*.copy)infori=0ton-1doletmix=Effect.rampfrom_mixs.mixinandfeedback=Effect.rampfrom_feedbackfeedbackininout.left.(i)<-sidet.leftout.left.(i)t.phasemixfeedback;(* the right side's LFO a quarter turn ahead *)out.right.(i)<-sidet.rightout.right.(i)(t.phase+.0.25)mixfeedback;t.phase<-t.phase+.(s.rate/.rate);ift.phase>=1.thent.phase<-t.phase-.1.done;t.last_mix<-s.mix;t.last_feedback<-feedback