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/Sequencer.ml.html
Source file Sequencer.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 121 122 123 124 125 126 127 128 129 130 131 132 133 134(* 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 Sequencer.mli *) type step = { note : int option; accent : bool; slide : bool; locks : (string * float) list } let rest = { note = None; accent = false; slide = false; locks = [] } let note ?(accent = false) ?(slide = false) (n : int) : step = { note = Some n; accent; slide; locks = [] } let lock (s : step) (name : string) (v : float) : step = { s with locks = (name, v) :: List.remove_assoc name s.locks } let unlock (s : step) : step = { s with locks = [] } type event = Note_on of { note : int; accent : bool; glide : bool } | Note_off let rate = float_of_int Signal.rate let samples_per_step (bpm : float) : float = 60. /. (bpm *. 4.) *. rate type t = { mutable pattern : step array; mutable bpm : float; mutable running : bool; mutable clock : int; (* samples since the start *) mutable next : int; (* the step to begin next *) mutable next_at : float; (* its exact time, in samples *) mutable off_at : float option; (* the gate's closing, exact *) mutable sliding : bool; (* the step sounding slides into the next *) mutable sounding : int; (* the step sounding, for a panel *) mutable block_start : int; (* the block last advanced, for [position] *) } let create ?(bpm = 120.) (pattern : step array) : t = { pattern; bpm; running = false; clock = 0; next = 0; next_at = 0.; off_at = None; sliding = false; sounding = 0; block_start = 0 } let set_pattern (t : t) (p : step array) : unit = t.pattern <- p let set_bpm (t : t) (bpm : float) : unit = t.bpm <- Float.max 20. bpm let running (t : t) : bool = t.running let step (t : t) : int = t.sounding let start (t : t) : unit = t.running <- true; t.next <- 0; t.next_at <- float_of_int t.clock; t.off_at <- None; t.sliding <- false let stop (t : t) : unit = t.running <- false (* the first sample at or after an exact time *) let sample_of (time : float) : int = int_of_float (Float.ceil (time -. 1e-9)) let advance (t : t) (n : int) (f : int -> event -> unit) : unit = t.block_start <- t.clock; let block_end = t.clock + n in let continue = ref t.running in while !continue do let len = Array.length t.pattern in let start = sample_of t.next_at in let off = Option.map sample_of t.off_at in match off with (* the gate closes before the next step begins *) | Some o when o <= start && o < block_end -> f (o - t.clock) Note_off; t.off_at <- None | _ when start < block_end && len > 0 -> let k = t.next mod len in let s = t.pattern.(k) in let sps = samples_per_step t.bpm in (match s.note with | Some n -> f (start - t.clock) (Note_on { note = n; accent = s.accent; glide = t.sliding }); let next_is_note = t.pattern.((k + 1) mod len).note <> None in t.sliding <- s.slide && next_is_note; t.off_at <- (if t.sliding then None else Some (t.next_at +. (sps /. 2.))) | None -> (* a rest after a slide: the held gate closes now *) if t.sliding then f (start - t.clock) Note_off; t.sliding <- false); t.sounding <- k; t.next <- t.next + 1; t.next_at <- t.next_at +. sps | _ -> continue := false done; t.clock <- block_end (*****************************************************************************) (* Parameter locks *) (*****************************************************************************) type locks = Per_step | Points of float let lock_value (locks : locks) (pattern : step array) (name : string) (position : float) : float option = let len = Array.length pattern in if len = 0 then None else let p = Float.rem position (float_of_int len) in let p = if p < 0. then p +. float_of_int len else p in match locks with | Per_step -> List.assoc_opt name pattern.(min (len - 1) (Float.to_int p)).locks | Points smoothing -> ( (* the lock points, in order: (step, value) *) let points = List.concat (List.init len (fun i -> match List.assoc_opt name pattern.(i).locks with Some v -> [ (float_of_int i, v) ] | None -> [])) in match points with | [] -> None | [ (_, v) ] -> Some v | first :: _ -> let last = List.nth points (List.length points - 1) in (* a, the point at or before p, b the one after: round the * pattern's end if need be *) let a = List.fold_left (fun a (i, v) -> if i <= p then (i, v) else a) (fst last -. float_of_int len, snd last) points in let b = match List.find_opt (fun (i, _) -> i > p) points with Some b -> b | None -> (fst first +. float_of_int len, snd first) in let (ai, av), (bi, bv) = (a, b) in (* held, then a line over the last [smoothing] of the way *) let start = bi -. (smoothing *. (bi -. ai)) in if p < start then Some av else Some (av +. ((bv -. av) *. (p -. start) /. (bi -. start)))) let position (t : t) (offset : int) : float option = if (not t.running) || t.next = 0 then None else (* the last step begun, [next - 1], began at [next_at - sps]; the * steps in a block all [sps] apart *) let sps = samples_per_step t.bpm in let s = float_of_int (t.block_start + offset) in let p = float_of_int (t.next - 1) -. ((t.next_at -. sps -. s) /. sps) in if p < 0. then None else Some p let locked (t : t) (locks : locks) (name : string) (offset : int) : float option = Option.bind (position t offset) (lock_value locks t.pattern name)
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>