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_instruments/Polyphony.ml.html
Source file Polyphony.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(* 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 Polyphony.mli *) type voice = { release : unit -> unit; fill : Signal.t -> unit; silent : unit -> bool } (* a voice sounding: its key, whether the key is still held *) type sounding = { key : int; voice : voice; mutable held : bool } type t = { limit : int option; mutable sounding : sounding list; (* the oldest first *) mutable scratch : Signal.t; (* a voice's block, before it's added *) } let create ?voices () : t = { limit = voices; sounding = []; scratch = [||] } (* the oldest released, else the oldest *) let steal (t : t) : unit = let victim = match List.find_opt (fun s -> not s.held) t.sounding with Some s -> Some s | None -> List.nth_opt t.sounding 0 in match victim with Some v -> t.sounding <- List.filter (fun s -> s != v) t.sounding | None -> () let release (t : t) (key : int) : unit = List.iter (fun s -> if s.key = key && s.held then begin s.held <- false; s.voice.release () end) t.sounding (* a key already held (a keyboard's glitch, two presses without a * release) is let go first: one voice held per key *) let press (t : t) (key : int) (voice : voice) : unit = release t key; (match t.limit with Some n when n > 0 && List.length t.sounding >= n -> steal t | _ -> ()); t.sounding <- t.sounding @ [ { key; voice; held = true } ] let fill (t : t) (out : Signal.t) : unit = let n = Array.length out in if Array.length t.scratch <> n then t.scratch <- Array.make n 0.; Array.fill out 0 n 0.; List.iter (fun s -> s.voice.fill t.scratch; for i = 0 to n - 1 do out.(i) <- out.(i) +. t.scratch.(i) done) t.sounding; (* the released voices whose sound is over *) t.sounding <- List.filter (fun s -> s.held || not (s.voice.silent ())) t.sounding let voices (t : t) : int = List.length t.sounding let held (t : t) : int list = List.sort compare (List.filter_map (fun s -> if s.held then Some s.key else None) t.sounding) let rate = float_of_int Signal.rate let sine ~(adsr : Envelope.t) (frequency : float) (velocity : float) : voice = let envelope = Envelope.start () and phase = ref 0. and levels = ref [||] in Envelope.gate_on envelope; let fill (out : Signal.t) = let n = Array.length out in if Array.length !levels <> n then levels := Array.make n 0.; Envelope.fill Exponential adsr envelope !levels; for i = 0 to n - 1 do out.(i) <- velocity *. !levels.(i) *. sin (2. *. Float.pi *. !phase); phase := !phase +. (frequency /. rate); if !phase >= 1. then phase := !phase -. 1. done in { release = (fun () -> Envelope.gate_off envelope); fill; silent = (fun () -> Envelope.stage envelope = Idle) }
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>