aboutsummaryrefslogtreecommitdiffstats
path: root/lib/time.moon
blob: 5823d80ca5ea22e5b7640cfe5a5e7463d57ba4d3 (plain)
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
import Registry, Value, IO, Op, ValueInput, IOInput, match
  from require 'core.base'
import monotime from require 'system'

class Clock extends IO
  new: (@frametime) =>
    @last = monotime!
    @dt = 0
    @is_dirty = false

  tick: =>
    time = monotime!
    @dt = time - @last
    @is_dirty = if @dt >= @frametime
      @last = time
      true
    else
      false

  dirty: => @is_dirty

class clock extends Op
  @doc: "(clock [fps]) - a clock source

IO that triggers other operators at a fixed frame rate.
fps defaults to 60 and has to be an eval-time constant"

  new: => super 'clock'

  setup: (inputs) =>
    { fps } = match 'num?', inputs
    super fps: ValueInput fps or Value.num 60

  tick: =>
    if @inputs.fps\dirty!
      @out\set Clock 1 / @inputs.fps!

class lfo extends Op
  @doc: "(lfo [clock] freq [wave]) - low-frequency oscillator

oscillates between 0 and 1 at the frequency freq.
wave selects the wave shape from the following:
- sin (default)
- saw
- tri"

  new: =>
    super 'num'
    @phase = 0

  default_wave = Value.str 'sin'
  setup: (inputs, scope) =>
    { clock, freq, wave } = match 'clock? num any?', inputs
    super
      clock: IOInput clock or scope\get '*clock*'
      freq: ValueInput freq
      wave: ValueInput wave or default_wave

  tau = math.pi * 2
  tick: =>
    if @inputs.clock\dirty!
      { :clock, :freq, :wave } = @unwrap_all!

      @phase += clock.dt * freq
      @out\set switch wave
        when 'sin' then .5 + .5 * math.cos @phase * tau
        when 'saw' then @phase % 1
        when 'tri' then math.abs (2*@phase % 2) - 1
        else error "unknown wave type"

class ramp extends Op
  @doc: "(ramp [clock] period [max]) - sawtooth lfo

ramps from 0 to max (default same as ramp) once every period seconds."

  new: =>
    super 'num'
    @phase = 0

  setup: (inputs, scope) =>
    { clock, period, max } = match 'clock? num num?', inputs
    super
      clock: IOInput clock or scope\get '*clock*'
      period: ValueInput period
      max: max and ValueInput max

  tick: =>
    clock_dirty = @inputs.clock\dirty!
    if clock_dirty
      { :clock, :period, :max } = @unwrap_all!
      max or= period
      @phase += clock.dt / period

      while @phase >= 1
        @phase -= 1

    if clock_dirty or (@inputs.max and @inputs.max\dirty!)
      @out\set @phase * max

class tick extends Op
  @doc: "(tick [clock] period) - count ticks

counts upwards by one every period seconds and returns the number of completed ticks."
  new: =>
    @phase, @count = 0, 0
    super 'num', @count

  setup: (inputs, scope) =>
    { clock, period } = match 'clock? num', inputs
    super
      clock: IOInput clock or scope\get '*clock*'
      period: ValueInput period

  tick: =>
    if @inputs.clock\dirty!
      { :clock, :period, :max } = @unwrap_all!
      @phase += clock.dt / period

      while @phase >= 1
        @phase -= 1
        @count += 1
        @out\set @count

class every extends Op
  @doc: "(every [clock] period) - trigger every period seconds

returns true once every period seconds."
  new: =>
    super 'bang'
    @phase = 0

  setup: (inputs, scope) =>
    { clock, period } = match 'clock? num', inputs
    super
      clock: IOInput clock or scope\get '*clock*'
      period: ValueInput period

  tick: =>
    if @inputs.clock\dirty!
      { :clock, :period, :max } = @unwrap_all!
      @phase += clock.dt / period

      while @phase >= 1
        @phase -= 1
        @out\set true

{
  :clock
  :lfo
  :ramp
  :tick
  :every
}