diff options
| author | s-ol <s+removethis@s-ol.nu> | 2025-03-19 17:48:09 +0000 |
|---|---|---|
| committer | s-ol <s+removethis@s-ol.nu> | 2025-03-20 13:35:18 +0000 |
| commit | 5e0c6b9b935b7b30e36579f812105472cfd6924e (patch) | |
| tree | 53ac9d324261ee7510b5025dc29cebb188b83f25 | |
| parent | lib: add osc/dump (diff) | |
| download | alive-5e0c6b9b935b7b30e36579f812105472cfd6924e.tar.gz alive-5e0c6b9b935b7b30e36579f812105472cfd6924e.zip | |
lib: add ad, decay to link-time
| -rw-r--r-- | alv-lib/link-time.moon | 179 |
1 files changed, 139 insertions, 40 deletions
diff --git a/alv-lib/link-time.moon b/alv-lib/link-time.moon index 34e2220..251ee6c 100644 --- a/alv-lib/link-time.moon +++ b/alv-lib/link-time.moon @@ -97,6 +97,8 @@ every = Constant.meta if post < pre @out\set evt + vis: => type: 'event' + lfo = Constant.meta meta: name: 'lfo' @@ -139,39 +141,6 @@ lfo = Constant.meta vis: => type: 'bar', bar: @.out! -ramp = Constant.meta - meta: - name: 'ramp' - summary: "Sawtooth LFO." - examples: { '(ramp [clock] period [phase] [max])' } - description: "Ramps from `0` to `max` once every `period` beats. - -- `clock` should be a `time/clock!` stream. This argument can be omitted - and the stream be passed as a dynamic definition in `*clock*` instead. -- `period` should be a num~ stream. -- `phase` should be a num~ stream and defaults to 0. -- `max` should be a num~ stream and defaults to `period` if omitted." - value: class extends Op - pattern = -sig['link/clock'] + sig.num + -sig.num + -sig.num - setup: (inputs, scope) => - { clock, period, phase, max } = pattern\match inputs - super - clock: Input.hot clock or scope\get '*clock*' - period: Input.cold period - phase: Input.cold phase or Constant.num 0 - max: max and Input.cold max - - @setup_out '~', T.num, 0 - - tick: => - { :clock, :period, :phase, :max } = @unwrap_all! - t = clock.state\phase_at_time clock.time, period - t = (t - phase) / period % 1 - max or= period - @out\set t * max - - vis: => type: 'bar', bar: @state - tick = Constant.meta meta: name: 'tick' @@ -214,20 +183,20 @@ phase = Constant.meta meta: name: 'phase' summary: "Get phase." - examples: { '(tick [clock] period)' } - description: "Counts upwards by one every `period` beats. + examples: { '(tick [clock] period [phase])' } + description: "Ramps from `0` to `period` once every `period` beats. - `clock` should be a `link/clock~` stream. This argument can be omitted and the stream be passed as a dynamic definition in `*clock*` instead. -- `period` should be a num~ stream and defaults to 1. -- returns a `num~` stream that ticks from 0 to `period`." +- `period` should be a num~ stream. +- `phase` should be a num~ stream and defaults to 0." value: class extends Op - pattern = -sig['link/clock'] + -sig.num + -sig.num + pattern = -sig['link/clock'] + sig.num + -sig.num setup: (inputs, scope) => { clock, period, phase } = pattern\match inputs super clock: Input.hot clock or scope\get '*clock*' - period: Input.cold period or Constant.num 0 + period: Input.cold period phase: Input.cold phase or Constant.num 0 @setup_out '~', T.num @@ -241,8 +210,136 @@ phase = Constant.meta @out\set t - vis: => type: 'event' + vis: => type: 'bar', bar: @.out! / @inputs.period! + +ramp = Constant.meta + meta: + name: 'ramp' + summary: "Get normalized phase." + examples: { '(ramp [clock] period [phase])' } + description: "Ramps from `0` to `1` once every `period` beats. + +- `clock` should be a `time/clock!` stream. This argument can be omitted + and the stream be passed as a dynamic definition in `*clock*` instead. +- `period` should be a num~ stream. +- `phase` should be a num~ stream and defaults to 0." + value: class extends Op + pattern = -sig['link/clock'] + sig.num + -sig.num + setup: (inputs, scope) => + { clock, period, phase } = pattern\match inputs + super + clock: Input.hot clock or scope\get '*clock*' + period: Input.cold period + phase: Input.cold phase or Constant.num 0 + + @setup_out '~', T.num + + tick: => + { :clock, :period, :phase, :evt } = @unwrap_all! + return if period == 0 + + t = clock.state\phase_at_time clock.time, period + t = (t + period - phase) % period + + @out\set t / period + + vis: => type: 'bar', bar: @.out! + +decay = Constant.meta + meta: + name: 'decay' + summary: "Decay envelope from bang!." + examples: { '(decay [clock] dur trigger)' } + description: "Ramp from `1` to `0` in `dur` beats on `trigger`. + +- `clock` should be a `link/clock~` stream. This argument can be omitted + and the stream be passed as a dynamic definition in `*clock*` instead. +- `dur` should be a num~ stream. +- `trigger` should be a bang! stream." + value: class extends Op + pattern = -sig['link/clock'] + sig.num + evt.bang + setup: (inputs, scope) => + { clock, dur, trigger } = pattern\match inputs + super + clock: Input.hot clock or scope\get '*clock*' + dur: Input.cold dur + trigger: Input.hot trigger + + @setup_out '~', T.num, 0 + @state = math.min @inputs.dur!, (@state or 0) + + tick: => + { :clock, :dur, :trigger } = @unwrap_all! + if trigger + @state = dur + elseif clock + return if @state <= 0 + pre = clock.state\beat_at_time clock.time - clock.dt, 1 + post = clock.state\beat_at_time clock.time, 1 + delta = post - pre + @state = math.max 0, @state - delta + + @out\set @state / dur + + vis: => type: 'bar', bar: @.out! + +ad = Constant.meta + meta: + name: 'ad' + summary: "Attack-Decay enveloper from bang!." + examples: { '(ad [clock] attack decay trigger)' } + description: "Ramp from `0` to `1` and back on `trigger`. + +- `clock` should be a `link/clock~` stream. This argument can be omitted + and the stream be passed as a dynamic definition in `*clock*` instead. +- `attack` and `decay` should be num~ streams. +- `trigger` should be a bang! stream." + value: class extends Op + pattern = -sig['link/clock'] + sig.num + sig.num + evt.bang + setup: (inputs, scope) => + { clock, attack, decay, trigger } = pattern\match inputs + super + clock: Input.hot clock or scope\get '*clock*' + attack: Input.cold attack + decay: Input.cold decay + trigger: Input.hot trigger + + @setup_out '~', T.num, 0 + @state = math.min @inputs.decay!, math.max -@inputs.attack!, (@state or 0) + + tick: => + { :clock, :attack, :decay, :trigger } = @unwrap_all! + + delta = 0 + + if trigger + @state = -attack + elseif clock + return if @state == 0 + + pre = clock.state\beat_at_time clock.time - clock.dt, 1 + post = clock.state\beat_at_time clock.time, 1 + delta = post - pre + + if @state < 0 + -- attack phase + @state = @state + delta + + if @state >= 0 + -- fall through to decay phase with remaining delta + delta = @state + @state = decay + else + @out\set 1 + @state / attack + return + + if @state > 0 + -- decay phase + @state = math.max 0, @state - delta + @out\set @state / decay + + vis: => type: 'bar', bar: @.out! Constant.meta meta: @@ -256,3 +353,5 @@ Constant.meta :ramp :tick :phase + :decay + :ad |
