12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485(* 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 Oscillator.mli *)typewaveform=Sine|Square|Triangle|Sawtoothletwaveforms=[Sine;Square;Triangle;Sawtooth]letname=functionSine->"sine"|Square->"square"|Triangle->"triangle"|Sawtooth->"sawtooth"letwave(w:waveform)(phase:float):float=matchwwith|Sine->sin(2.*.Float.pi*.phase)|Square->ifphase<0.5then1.else-1.(* 0 up to 1 at a quarter, down to -1 at three quarters, back to 0:
* in step with the sine *)|Triangle->ifphase<0.25then4.*.phaseelseifphase<0.75then2.-.(4.*.phase)else(4.*.phase)-.4.|Sawtooth->(2.*.phase)-.1.letpolyblep~(dt:float)(t:float):float=ift<dtthenletu=t/.dtin(2.*.u)-.(u*.u)-.1.elseift>1.-.dtthenletu=(t-.1.)/.dtin(u*.u)+.(2.*.u)+.1.else0.(* the integral of polyblep's residual for a unit step, (u+1)^3 / 6
* before, (1-u)^3 / 6 after: 1/6 at the corner *)letpolyblamp~(dt:float)(t:float):float=ift<dtthenletu=1.-.(t/.dt)inu*.u*.u/.6.elseift>1.-.dtthenletu=((t-.1.)/.dt)+.1.inu*.u*.u/.6.else0.letwrap(phase:float):float=phase-.Float.floorphaseletwave_band_limited(w:waveform)~(dt:float)(phase:float):float=matchwwith|Sine->wavewphase(* corners at 0.25 (the slope from +4 to -4 a period: -8 dt a sample)
* and 0.75 (back up, +8 dt) *)|Triangle->wavewphase-.(8.*.dt*.polyblamp~dt(wrap(phase-.0.25)))+.(8.*.dt*.polyblamp~dt(wrap(phase-.0.75)))(* up at 0, down at 0.5 *)|Square->wavewphase+.polyblep~dtphase-.polyblep~dt(wrap(phase+.0.5))(* down at 0 *)|Sawtooth->wavewphase-.polyblep~dtphaseletpulse~(width:float)(phase:float):float=(ifphase<widththen1.else-1.)-.((2.*.width)-.1.)(* up at 0, down at [width] *)letpulse_band_limited~(width:float)~(dt:float)(phase:float):float=pulse~widthphase+.polyblep~dtphase-.polyblep~dt(wrap(phase-.width))typet={waveform:waveform;frequency:float;phase:float}letmake(waveform:waveform)(frequency:float):t={waveform;frequency;phase=0.}letnext?(band_limited=false)(o:t):float*t=letdt=o.frequency/.float_of_intSignal.rateinletphase=o.phase+.dtinletx=ifband_limitedthenwave_band_limitedo.waveform~dto.phaseelsewaveo.waveformo.phasein(x,{owithphase=phase-.Float.floorphase})letrender?band_limited(w:waveform)~(frequency:float)(seconds:float):Signal.t=leto=ref(makewfrequency)inArray.init(Signal.samplesseconds)(fun_->let(x,o')=next?band_limited!oino:=o';x)