package tiny_libs
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>
From-scratch libraries for teaching: graphics, audio, compression, crypto, networking and more
Install
dune-project
Dependency
Authors
Maintainers
Sources
0.3.6.tar.gz
md5=7c636383d146d30ac6f2fa234a6253c8
sha512=c79f3823c5f8f57e5038eb640d487c61168b84aa07c61999d6622ef9fd0c890e2b03b4c6a7cdbbe9352a49e25dda00ac7bb14693cee8e3d7beeed251351a2af0
doc/src/tiny_libs.audio_effects/Leslie.ml.html
Source file Leslie.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120(* 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 Leslie.mli *) let rate = float_of_int Signal.rate let horn_slow = 0.8 let horn_fast = 6.8 let drum_slow = 0.7 let drum_fast = 5.6 let horn_seconds = 0.5 let drum_seconds = 1.2 let crossover = 800. let sound_speed = 343. (* m/s *) (* ours: the horn's mouth's circle, the drum opening's, their levels * facing and turned away *) let horn_radius = 0.15 let drum_radius = 0.1 (* the microphones, a quarter turn left and right of the listener *) let microphones = [| 0.25; -0.25 |] (* a delay line: what a rotor emitted, [at] the next to write *) type line = { samples : float array; mutable at : int } type rotor = { line : line; radius : float; mutable angle : float; (* turns *) mutable speed : float; (* turns a second *) facing : float; (* its level turned towards a microphone, 1 - facing away (0.6 + 0.4 cos) *) way : float; (* 1 or -1: the drum turns the other way *) } type t = { horn_rotor : rotor; drum_rotor : rotor; low : Filter.biquad; high : Filter.biquad; low_memory : Filter.memory; high_memory : Filter.memory; } let rotor radius facing way speed = { line = { samples = Array.make 64 0.; at = 0 }; radius; angle = 0.; speed; facing; way } let create () : t = { horn_rotor = rotor horn_radius 0.4 1. horn_slow; drum_rotor = rotor drum_radius 0.2 (-1.) drum_slow; low = Filter.biquad Low_pass ~cutoff:crossover ~q:0.707; high = Filter.biquad High_pass ~cutoff:crossover ~q:0.707; low_memory = Filter.silence (); high_memory = Filter.silence (); } let horn (t : t) : float = t.horn_rotor.speed let drum (t : t) : float = t.drum_rotor.speed (* what microphone [m] hears of rotor [r] now: the sound emitted a * distance's time ago -- the mouth nearer, less long ago -- at the * mouth's level towards it *) let heard (r : rotor) (m : float) : float = let a = 2. *. Float.pi *. (r.angle -. m) in let delay = ((r.radius *. (1. -. cos a)) /. sound_speed *. rate) +. 1. in let n = Array.length r.line.samples in let whole = Float.to_int delay in let frac = delay -. float_of_int whole in let at k = r.line.samples.((r.line.at - k + (2 * n)) mod n) in let x = ((1. -. frac) *. at whole) +. (frac *. at (whole + 1)) in (1. -. r.facing +. (r.facing *. cos a)) *. x (* one sample: the rotor's speed nearer its target, its angle on *) let turn (r : rotor) (target : float) (seconds : float) : unit = r.speed <- r.speed +. ((target -. r.speed) *. (1. -. exp (-1. /. (seconds *. rate)))); r.angle <- r.angle +. (r.way *. r.speed /. rate); r.angle <- r.angle -. Float.of_int (Float.to_int r.angle) let write (l : line) (x : float) : unit = l.samples.(l.at) <- x; l.at <- (l.at + 1) mod Array.length l.samples let process_mix (t : t) ~(fast : bool) ~(from_mix : float) ~(mix : float) (s : Signal.stereo) : unit = let n = Array.length s.left in let h = t.horn_rotor and d = t.drum_rotor in for i = 0 to n - 1 do let x = (s.left.(i) +. s.right.(i)) /. 2. in write h.line (Filter.step t.high t.high_memory x); write d.line (Filter.step t.low t.low_memory x); turn h (if fast then horn_fast else horn_slow) horn_seconds; turn d (if fast then drum_fast else drum_slow) drum_seconds; let mix = Effect.ramp from_mix mix i n in let mic m = heard h m +. heard d m in s.left.(i) <- ((1. -. mix) *. x) +. (mix *. mic microphones.(0)); s.right.(i) <- ((1. -. mix) *. x) +. (mix *. mic microphones.(1)) done let process (t : t) ~(fast : bool) (s : Signal.stereo) : unit = process_mix t ~fast ~from_mix:1. ~mix:1. s let knobs : Effect.knob list = [ { name = "fast"; control = Switch; initial = 0. }; { name = "mix"; control = Knob (0., 1.); initial = 1. } ] let fx () : Effect.t = let t = create () and fast = ref false and mix = ref 1. and last_mix = ref 1. in let set (knob : string) (x : float) = match knob with "fast" -> fast := Control.on x | "mix" -> mix := x | _ -> () in let process s = process_mix t ~fast:!fast ~from_mix:!last_mix ~mix:!mix s; last_mix := !mix in { name = "leslie"; knobs; set; process; meters = (fun () -> [ ("horn", horn t); ("drum", drum t) ]) }
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>