1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162(* 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 Instrument.mli *)typet={note_on:int->float->unit;note_off:int->unit;set:string->float->unit;fill:Signal.stereo->unit;}letgate_samples=0.005*.float_of_intSignal.rate(* the sine's state: the key sounding, its pitch and velocity; the
* oscillator's phase (0 to 1); the gate's level, ramping to the
* velocity or to 0; the volume knob, and its value at the last block,
* ramped from *)typesine={mutablekey:intoption;mutablefrequency:float;mutablevelocity:float;mutablephase:float;mutablegate:float;mutablevolume:float;mutablelast_volume:float;}letsine():t=lets={key=None;frequency=440.;velocity=1.;phase=0.;gate=0.;volume=0.5;last_volume=0.5}inletnote_onkeyvelocity=s.key<-Somekey;s.frequency<-Music.midi_frequencykey;s.velocity<-velocityinletnote_offkey=ifs.key=Somekeythens.key<-Noneinletsetnamevalue=ifname="volume"thens.volume<-Float.min1.(Float.max0.value)inletfill(out:Signal.stereo)=letn=Array.lengthout.leftinlettarget=ifs.key=Nonethen0.elses.velocityinletstep=1./.gate_samplesinletdphase=s.frequency/.float_of_intSignal.rateinfori=0ton-1do(* the gate: towards its target, a 5 ms ramp's step at a time *)s.gate<-(ifs.gate<targetthenFloat.mintarget(s.gate+.step)elseFloat.maxtarget(s.gate-.step));(* the knob: from the last block's value to this one's *)letvolume=s.last_volume+.((s.volume-.s.last_volume)*.float_of_int(i+1)/.float_of_intn)inletx=volume*.s.gate*.sin(2.*.Float.pi*.s.phase)inout.left.(i)<-x;out.right.(i)<-x;s.phase<-Float.rem(s.phase+.dphase)1.done;s.last_volume<-s.volumein{note_on;note_off;set;fill}