aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authors-ol <s-ol@users.noreply.github.com>2020-03-25 10:43:29 +0000
committers-ol <s-ol@users.noreply.github.com>2020-03-25 11:25:05 +0000
commit7cb862f6f6079509dafd466fff83c719cb2fd89e (patch)
tree843f92c4d4b2507e88a525b7c56c29d343958e5b /lib
parentValue -> Value/Event/IO-Stream (diff)
downloadalive-7cb862f6f6079509dafd466fff83c719cb2fd89e.tar.gz
alive-7cb862f6f6079509dafd466fff83c719cb2fd89e.zip
new core.base.match, update lib
Diffstat (limited to 'lib')
-rw-r--r--lib/logic.moon49
-rw-r--r--lib/math.moon73
-rw-r--r--lib/midi.moon90
-rw-r--r--lib/midi/core.moon70
-rw-r--r--lib/midi/launchctl.moon133
-rw-r--r--lib/osc.moon52
-rw-r--r--lib/pilot.moon39
-rw-r--r--lib/random.moon59
-rw-r--r--lib/sc.moon20
-rw-r--r--lib/string.moon12
-rw-r--r--lib/time.moon151
-rw-r--r--lib/util.moon110
12 files changed, 459 insertions, 399 deletions
diff --git a/lib/logic.moon b/lib/logic.moon
index 157d190..62f1963 100644
--- a/lib/logic.moon
+++ b/lib/logic.moon
@@ -1,4 +1,4 @@
-import Op, Value, Input, Error, match from require 'core.base'
+import Op, ValueStream, Input, Error, val from require 'core.base'
all_same = (first, list) ->
for v in *list
@@ -15,13 +15,13 @@ tobool = (val) ->
true
class ReduceOp extends Op
- new: => super 'bool'
-
+ pattern = val! + val! * 0
setup: (inputs) =>
- { first, rest } = match "any *any", inputs
+ @out or= ValueStream 'bool'
+ { first, rest } = pattern\match inputs
super
- first: Input.value first
- rest: [Input.value v for v in *rest]
+ first: Input.hot first
+ rest: [Input.hot v for v in *rest]
tick: =>
{ :first, :rest } = @unwrap_all!
@@ -31,7 +31,7 @@ class ReduceOp extends Op
@out\set accum
-eq = Value.meta
+eq = ValueStream.meta
meta:
name: 'eq'
summary: "Check for equality."
@@ -39,16 +39,16 @@ eq = Value.meta
description: "`true` if the types and values of all arguments are equal."
value: class extends Op
- new: => super 'bool', false
-
+ pattern = val! + val! * 0
setup: (inputs) =>
- { first, rest } = match "any *any", inputs
+ @out or= ValueStream 'bool', false
+ { first, rest } = pattern\match inputs
same = all_same first\type!, [i\type! for i in *rest]
super if same
{
- first: Input.value first
- rest: [Input.value v for v in *rest]
+ first: Input.hot first
+ rest: [Input.hot v for v in *rest]
}
else
{}
@@ -68,8 +68,7 @@ eq = Value.meta
@out\set equal
-
-not_eq = Value.meta
+not_eq = ValueStream.meta
meta:
name: 'not-eq'
summary: "Check for inequality."
@@ -77,11 +76,10 @@ not_eq = Value.meta
description: "`true` if types or values of any two arguments are different."
value: class extends Op
- new: => super 'bool'
-
setup: (inputs) =>
+ @out or= ValueStream 'bool', false
assert #inputs > 1, Error 'argument', "need at least two values"
- super [Input.value v for v in *inputs]
+ super [Input.hot v for v in *inputs]
tick: =>
if not @inputs[1]
@@ -99,7 +97,7 @@ not_eq = Value.meta
@out\set diff
-and_ = Value.meta
+and_ = ValueStream.meta
meta:
name: 'and'
summary: "Logical AND."
@@ -107,7 +105,7 @@ and_ = Value.meta
value: class extends ReduceOp
fn: (a, b) -> a and b
-or_ = Value.meta
+or_ = ValueStream.meta
meta:
name: 'or'
summary: "Logical OR."
@@ -115,7 +113,7 @@ or_ = Value.meta
value: class extends ReduceOp
fn: (a, b) -> a or b
-not_ = Value.meta
+not_ = ValueStream.meta
meta:
name: 'not'
summary: "Logical NOT."
@@ -126,11 +124,11 @@ not_ = Value.meta
setup: (inputs) =>
{ value } = match 'any', inputs
- super value: Input.value value
+ super value: Input.hot value
tick: => @out\set not tobool @inputs.value!
-bool = Value.meta
+bool = ValueStream.meta
meta:
name: 'bool'
summary: "Cast value to bool."
@@ -138,11 +136,10 @@ bool = Value.meta
description: "`false` if a is `false`, `nil` or `0`, `true` otherwise."
value: class extends Op
- new: => super 'bool'
-
setup: (inputs) =>
- { value } = match 'any', inputs
- super value: Input.value value
+ @out or= ValueStream 'bool'
+ { value } = val!\match inputs
+ super value: Input.hot value
tick: => @out\set tobool @inputs\value!
diff --git a/lib/math.moon b/lib/math.moon
index 69d83bb..b2f4ce0 100644
--- a/lib/math.moon
+++ b/lib/math.moon
@@ -1,14 +1,14 @@
-import Op, Value, Error, Input, match from require 'core.base'
+import Op, ValueStream, Error, Input, val from require 'core.base'
unpack or= table.unpack
class ReduceOp extends Op
- new: => super 'num'
-
+ pattern = val.num + val.num*0
setup: (inputs) =>
- { first, rest } = match 'num *num', inputs
+ @out or= ValueStream 'num'
+ { first, rest } = pattern\match inputs
super
- first: Input.value first
- rest: [Input.value v for v in *rest]
+ first: Input.hot first
+ rest: [Input.hot v for v in *rest]
tick: =>
{ :first, :rest } = @unwrap_all!
@@ -17,44 +17,39 @@ class ReduceOp extends Op
accum = @.fn accum, val
@out\set accum
-func_op = (arity, func) ->
+func_op = (func, pattern) ->
class extends Op
- new: => super 'num'
setup: (inputs) =>
- { params } = match '*num', inputs
- if arity != '*'
- err = Error 'argument', "need exactly #{arity} arguments"
- assert #params == arity, err
- super [Input.value p for p in *params]
+ @out or= ValueStream 'num'
+ params = pattern\match inputs
+ super [Input.hot p for p in *params]
tick: => @out\set func unpack @unwrap_all!
-func_def = (name, args, func, summary) ->
- _, arity = args\gsub ' ', ' '
-
- Value.meta
+func_def = (name, args, func, summary, pattern) ->
+ ValueStream.meta
meta:
:name
:summary
examples: { "(#{name} #{args})" }
- value: func_op arity+1, func
+ value: func_op func, pattern or val.num\rep 1, 1
evenodd_op = (remainder) ->
class extends Op
- new: => super 'bool'
-
+ pattern = val.num + -val.num
setup: (inputs) =>
- { val, div } = match 'num num?', inputs
+ @out or= ValueStream 'bool'
+ { val, div } = pattern\match inputs
super
- val: Input.value val
- div: Input.value div or Value.num 2
+ val: Input.hot val
+ div: Input.hot div or ValueStream.num 2
tick: =>
{ :val, :div } = @unwrap_all!
@out\set (val % div) == remainder
-add = Value.meta
+add = ValueStream.meta
meta:
name: 'add'
summary: "Add values."
@@ -63,7 +58,7 @@ add = Value.meta
value: class extends ReduceOp
fn: (a, b) -> a + b
-sub = Value.meta
+sub = ValueStream.meta
meta:
name: 'sub'
summary: "Subtract values."
@@ -72,7 +67,7 @@ sub = Value.meta
value: class extends ReduceOp
fn: (a, b) -> a - b
-mul = Value.meta
+mul = ValueStream.meta
meta:
name: 'mul'
summary: "Multiply values."
@@ -80,7 +75,7 @@ mul = Value.meta
value: class extends ReduceOp
fn: (a, b) -> a * b
-div = Value.meta
+div = ValueStream.meta
meta:
name: 'div'
summary: "Divide values."
@@ -89,7 +84,7 @@ div = Value.meta
value: class extends ReduceOp
fn: (a, b) -> a / b
-pow = Value.meta
+pow = ValueStream.meta
meta:
name: 'pow'
summary: "Raise to a power."
@@ -98,7 +93,7 @@ pow = Value.meta
value: class extends ReduceOp
fn: (a, b) -> a ^ b
-mod = Value.meta
+mod = ValueStream.meta
meta:
name: 'mod'
summary: 'Modulo operator.'
@@ -106,7 +101,7 @@ mod = Value.meta
description: "Calculate remainder of division by `div`."
value: func_op 2, (a, b) -> a % b
-even = Value.meta
+even = ValueStream.meta
meta:
name: 'even'
summary: 'Check whether val is even.'
@@ -115,7 +110,7 @@ even = Value.meta
`div` defaults to 2."
value: evenodd_op 0
-odd = Value.meta
+odd = ValueStream.meta
meta:
name: 'odd'
summary: 'Check whether val is odd.'
@@ -124,7 +119,7 @@ odd = Value.meta
`div` defaults to 2."
value: evenodd_op 1
-mix = Value.meta
+mix = ValueStream.meta
meta:
name: 'mix'
summary: 'Linearly interpolate.'
@@ -132,7 +127,7 @@ mix = Value.meta
description: "Interpolate between `a` and `b` using `i` in range 0-1."
value: func_op 3, (a, b, i) -> i*b + (1-i)*a
-min = Value.meta
+min = ValueStream.meta
meta:
name: 'min'
summary: "Find the minimum."
@@ -140,7 +135,7 @@ min = Value.meta
description: "Return the lowest of arguments."
value: func_op '*', math.min
-max = Value.meta
+max = ValueStream.meta
meta:
name: 'max'
summary: "Find the maximum."
@@ -154,7 +149,7 @@ tan = func_def 'tan', 'alpha', math.tan, "Tangent function (radians)."
acos = func_def 'acos', 'cos', math.acos, "Inverse cosine function (radians)."
asin = func_def 'asin', 'sin', math.asin, "Inverse sine function (radians)."
atan = func_def 'atan', 'tan', math.atan, "Inverse tangent function (radians)."
-atan2 = func_def 'atan2', 'y x', math.atan2, "Inverse tangent function (two argument version)."
+atan2 = func_def 'atan2', 'y x', math.atan2, "Inverse tangent function (two argument version).", val.num\rep(2, 2)
cosh = func_def 'cosh', 'alpha', math.cosh, "Hyperbolic cosine function (radians)."
sinh = func_def 'sinh', 'alpha', math.sinh, "Hyperbolic sine function (radians)."
tanh = func_def 'tanh', 'alpha', math.tanh, "Hyperbolic tangent function (radians)."
@@ -164,7 +159,7 @@ ceil = func_def 'ceil', 'val', math.ceil, "Round towards positive infinity."
abs = func_def 'abs', 'val', math.abs, "Get the absolute value."
exp = func_def 'exp', 'exp', math.floor, "*e* number raised to a power."
-log = func_def 'log', 'val base', math.log, "Logarithm with given base."
+log = func_def 'log', 'val [base]', math.log, "Logarithm with given base.", val.num*2
log10 = func_def 'log10', 'val', math.log10, "Logarithm with base 10."
sqrt = func_def 'sqrt', 'val', math.sqrt, "Square root function."
@@ -181,11 +176,11 @@ sqrt = func_def 'sqrt', 'val', math.sqrt, "Square root function."
:mix
:min, :max
- pi: with Value.wrap math.pi
+ pi: with ValueStream.wrap math.pi
.meta = summary: 'The pi constant.'
- tau: with Value.wrap math.pi*2
+ tau: with ValueStream.wrap math.pi*2
.meta = summary: 'The tau constant.'
- huge: with Value.wrap math.huge
+ huge: with ValueStream.wrap math.huge
.meta = summary: 'Positive infinity constant.'
:sin, :cos, :tan
diff --git a/lib/midi.moon b/lib/midi.moon
index e978702..2f3a109 100644
--- a/lib/midi.moon
+++ b/lib/midi.moon
@@ -1,22 +1,21 @@
-import Value, Op, Input, match from require 'core.base'
+import ValueStream, EventStream, Op, Input, val, evt from require 'core.base'
import input, output, inout, apply_range from require 'lib.midi.core'
-gate = Value.meta
+gate = ValueStream.meta
meta:
name: 'gate'
summary: "gate from note-on and note-off messages."
- examples: { '(midi/gate port note [chan])' }
+ examples: { '(midi/gate [port] note [chan])' }
value: class extends Op
- new: =>
- super 'bool', false
-
- setup: (inputs) =>
- { port, note, chan } = match 'midi/port num num?', inputs
+ pattern = -evt['midi/port'] + val.num -val.num
+ setup: (inputs, scope) =>
+ @out or= ValueStream 'bool'
+ { port, note, chan } = pattern\match inputs
super
- port: Input.io port
- note: Input.value note
- chan: Input.value chan or Value.num -1
+ port: Input.hot port or scope\get '*midi*'
+ note: Input.hot note
+ chan: Input.hot chan or ValueStream.num -1
tick: =>
{ :port, :note, :chan } = @inputs
@@ -25,47 +24,42 @@ gate = Value.meta
@out\set false
if port\dirty!
- for msg in port!\receive!
+ for msg in *port!
if msg.a == note! and (chan! == -1 or msg.chan == chan!)
if msg.status == 'note-on'
@out\set true
elseif msg.status == 'note-off'
@out\set false
-trig = Value.meta
+trig = ValueStream.meta
meta:
name: 'trig'
summary: "`bang`s from note-on messages."
- examples: { '(midi/trig port note [chan])' }
+ examples: { '(midi/trig [port] note [chan])' }
value: class extends Op
- new: =>
- super 'bang', false
-
- setup: (inputs) =>
- { port, note, chan } = match 'midi/port num num?', inputs
+ pattern = -evt['midi/port'] + val.num -val.num
+ setup: (inputs, scope) =>
+ @out or= EventStream 'bang'
+ { port, note, chan } = pattern\match inputs
super
- port: Input.io port
- note: Input.value note
- chan: Input.value chan or Value.num -1
+ port: Input.hot port or scope\get '*midi*'
+ note: Input.cold note
+ chan: Input.cold chan or ValueStream.num -1
tick: =>
{ :port, :note, :chan } = @inputs
- if note\dirty! or chan\dirty!
- @out\set false
-
- if port\dirty!
- for msg in port!\receive!
- if msg.a == note! and (chan! == -1 or msg.chan == chan!)
- if msg.status == 'note-on'
- @out\set true
+ for msg in *port!
+ if msg.a == note! and (chan! == -1 or msg.chan == chan!)
+ if msg.status == 'note-on'
+ @out\add true
-trig = Value.meta
+cc = ValueStream.meta
meta:
- name: 'trig'
+ name: 'cc'
summary: "`num` from cc-change messages."
- examples: { '(midi/cc port cc [chan [range]])' }
+ examples: { '(midi/cc [port] cc [chan [range]])' }
description: "
`range` can be one of:
- 'raw' [ 0 - 128[
@@ -76,28 +70,24 @@ trig = Value.meta
- (num) [ 0 - num["
value: class extends Op
- new: =>
- super 'num'
-
- setup: (inputs) =>
- { port, cc, chan, range } = match 'midi/port num num? any?', inputs
+ pattern = -evt['midi/port'] + val.num + -val.num + -val.num
+ setup: (inputs, scope) =>
+ { port, cc, chan, range } = pattern\match inputs
super
- port: Input.io port
- cc: Input.value cc
- chan: Input.value chan or Value.num -1
- range: Input.value range or Value.str 'uni'
+ port: Input.hot port or scope\get '*midi*'
+ cc: Input.cold cc
+ chan: Input.cold chan or ValueStream.num -1
+ range: Input.cold range or ValueStream.str 'uni'
- if not @out\unwrap!
- @out\set apply_range @inputs.range, 0
+ @out or= ValueStream 'num', apply_range @inputs.range, 0
tick: =>
{ :port, :cc, :chan, :range } = @inputs
- if port\dirty!
- for msg in port!\receive!
- if msg.status == 'control-change' and
- (chan! == -1 or msg.chan == chan!) and
- msg.a == cc!
- @out\set apply_range range, msg.b
+ for msg in *port!
+ if msg.status == 'control-change' and
+ (chan! == -1 or msg.chan == chan!) and
+ msg.a == cc!
+ @out\set apply_range range, msg.b
{
:input
diff --git a/lib/midi/core.moon b/lib/midi/core.moon
index d1930eb..e520bbf 100644
--- a/lib/midi/core.moon
+++ b/lib/midi/core.moon
@@ -1,4 +1,4 @@
-import Value, IO, Op, Registry, Input, Error, match from require 'core.base'
+import ValueStream, IOStream, Op, Input, Error, val from require 'core.base'
import RtMidiIn, RtMidiOut, RtMidi from require 'luartmidi'
bit = do
@@ -30,27 +30,23 @@ find_port = (Klass, name) ->
\openport id
-class MidiPort extends IO
- new: (@inp, @out) =>
- @messages = {}
-
- tick: =>
- if @inp
- @messages = while true
- delta, bytes = @inp\getmessage!
- break unless delta
+class MidiPort extends IOStream
+ new: => super 'midi/port'
- { status, a, b } = bytes
- chan = band status, 0xf
- status = MIDI[rshift status, 4]
- { :status, :chan, :a, :b, port: @ }
+ setup: (inp, out) =>
+ @inp = inp and find_port RtMidiIn, inp
+ @out = out and find_port RtMidiOut, out
- dirty: => #@messages > 0
+ tick: =>
+ return unless @inp
+ while true
+ delta, bytes = @inp\getmessage!
+ break unless delta
- receive: =>
- coroutine.wrap ->
- for msg in *@messages
- coroutine.yield msg
+ { status, a, b } = bytes
+ chan = band status, 0xf
+ status = MIDI[rshift status, 4]
+ @add { :status, :chan, :a, :b }
send: (status, chan, a, b) =>
assert @out, Error 'type', "#{@} is not an output or bidirectional port"
@@ -59,15 +55,15 @@ class MidiPort extends IO
@out\sendmessage status, a, b
class PortOp extends Op
- new: => super 'midi/port'
+ new: (...) =>
+ super ...
+ @out or= MidiPort!
tick: (inp, out) =>
- if (inp and inp\dirty!) or (out and out\dirty!)
- inp = inp and find_port RtMidiIn, inp!
- out = out and find_port RtMidiOut, out!
- @out\set MidiPort inp, out
+ { :inp, :out } = @unwrap_all!
+ @out\setup inp, out
-input = Value.meta
+input = ValueStream.meta
meta:
name: 'input'
summary: "Create a MIDI input port."
@@ -75,12 +71,10 @@ input = Value.meta
value: class extends PortOp
setup: (inputs) =>
- { name } = match 'str', inputs
- super name: Input.value name
-
- tick: => super @inputs.name
+ name = val.str\match inputs
+ super inp: Input.hot name
-output = Value.meta
+output = ValueStream.meta
meta:
name: 'output'
summary: "Create a MIDI output port."
@@ -88,12 +82,10 @@ output = Value.meta
value: class extends PortOp
setup: (inputs) =>
- { name } = match 'str', inputs
- super name: Input.value name
+ name = val.str\match inputs
+ super out: Input.hot name
- tick: => super nil, @inputs.name
-
-inout = Value.meta
+inout = ValueStream.meta
meta:
name: 'inout'
summary: "Create a bidirectional MIDI port."
@@ -101,12 +93,10 @@ inout = Value.meta
value: class extends PortOp
setup: (inputs) =>
- { inp, out } = match 'str str', inputs
+ { inp, out } = (val.str + val.str)\match inputs
super
- inp: Input.value inp
- out: Input.value out
-
- tick: => super @inputs.inp, @inputs.out
+ inp: Input.hot inp
+ out: Input.hot out
apply_range = (range, val) ->
if range\type! == 'str'
diff --git a/lib/midi/launchctl.moon b/lib/midi/launchctl.moon
index 37ccb82..1e33cc4 100644
--- a/lib/midi/launchctl.moon
+++ b/lib/midi/launchctl.moon
@@ -1,15 +1,15 @@
-import Value, Op, Input, match from require 'core.base'
+import ValueStream, EventStream, Op, Input, val, evt from require 'core.base'
import apply_range, bit from require 'lib.midi.core'
import bor, lshift from bit
unpack or= table.unpack
color = (r, g) -> bit.bor 12, r, (bit.lshift g, 4)
-cc_seq = Value.meta
+cc_seq = ValueStream.meta
meta:
name: 'cc-seq'
summary: "MIDI CC-Sequencer."
- examples: { '(launchctl/cc-seq port i start chan [steps [range]])' }
+ examples: { '(launchctl/cc-seq [port] i start chan [steps [range]])' }
description: "
returns the value for the i-th step steps (buttons starting from start).
steps defaults to 8.
@@ -23,22 +23,21 @@ range can be one of:
- (num) [ 0 - num["
value: class extends Op
- new: => super 'num'
-
- setup: (inputs) =>
- { port, i, start,
- chan, steps, range } = match 'midi/port num num num num? any?', inputs
+ num = val.num
+ pattern = -evt['midi/port'] + num + num + num + -num + -(val.str + num)
+ setup: (inputs, scope) =>
+ { port, i, start, chan, steps, range } = pattern\match inputs
super
- port: Input.io port
- i: Input.value i
- start: Input.value start
- chan: Input.value chan
- steps: Input.value steps or Value.num 8
- range: Input.value range or Value.str 'uni'
+ port: Input.hot port or scope\get '*ctrl*'
+ i: Input.hot i
+ start: Input.hot start
+ chan: Input.hot chan
+ steps: Input.hot steps or ValueStream.num 8
+ range: Input.hot range or ValueStream.str 'uni'
- if not @out\unwrap!
- @out\set apply_range @inputs.range, 0
+ @state or= {}
+ @out or= ValueStream 'num', apply_range @inputs.range, 0
tick: =>
{ :port, :i, :start, :chan, :steps, :range } = @inputs
@@ -52,7 +51,7 @@ range can be one of:
curr_i = i! % #@state
if port\dirty!
changed = false
- for msg in port!\receive!
+ for msg in *port!
if msg.status == 'control-change' and msg.chan == chan!
rel_i = msg.a - start!
if rel_i >= 0 and rel_i < #@state
@@ -62,29 +61,28 @@ range can be one of:
else
@out\set apply_range range, @state[curr_i+1]
-gate_seq = Value.meta
+gate_seq = ValueStream.meta
meta:
name: 'gate-seq'
summary: "MIDI Gate-Sequencer."
- examples: { '(launchctl/gate-seq port i start chan [steps])' }
+ examples: { '(launchctl/gate-seq [port] i start chan [steps])' }
description: "
-returns `true` or `false` for the `i`-th note-button (MIDI-notes starting from
+Send `true` or `false` for the `i`-th note-button (MIDI-notes starting from
`start`). `steps` defaults to 8."
value: class extends Op
- new: =>
- super 'bool', false
- @state = {}
-
- setup: (inputs) =>
- { port, i, start, chan, steps } = match 'midi/port num num num num?', inputs
+ pattern = -evt['midi/port'] + val.num + val.num + val.num + -val.num
+ setup: (inputs, scope) =>
+ @out or= ValueStream 'bool'
+ @state or= {}
+ { port, i, start, chan, steps } = pattern\match inputs
super
- port: Input.io port
- i: Input.value i
- start: Input.value start
- chan: Input.value chan
- steps: Input.value steps or Value.num 8
+ port: Input.hot port or scope\get '*ctrl*'
+ i: Input.hot i
+ start: Input.hot start
+ chan: Input.hot chan
+ steps: Input.hot steps or ValueStream.num 8
light = (set, active) ->
set = if set then 'S' else ' '
@@ -96,8 +94,8 @@ returns `true` or `false` for the `i`-th note-button (MIDI-notes starting from
when 'SA' then 3, 1
display: (i, active) =>
- { :port, :start, :chan } = @unwrap_all!
- port\send 'note-on', chan, (start + i), light @state[i+1], active
+ start, chan = @inputs.start!, @inputs.chan!
+ @inputs.port.stream\send 'note-on', chan, (start + i), light @state[i+1], active
tick: =>
{ :port, :i, :start, :chan, :steps } = @inputs
@@ -111,7 +109,7 @@ returns `true` or `false` for the `i`-th note-button (MIDI-notes starting from
curr_i = i! % #@state
if port\dirty!
- for msg in port!\receive!
+ for msg in *port!
if msg.status == 'note-on' and msg.chan == chan!
rel_i = msg.a - start!
if rel_i >= 0 and rel_i < #@state
@@ -126,7 +124,72 @@ returns `true` or `false` for the `i`-th note-button (MIDI-notes starting from
@out\set @state[curr_i+1]
+trig_seq = ValueStream.meta
+ meta:
+ name: 'trig-seq'
+ summary: "MIDI Trigger-Sequencer."
+ examples: { '(launchctl/trig-seq [port] i start chan [steps])' }
+ description: "
+Send bangs for the `i`-th note-button (MIDI-notes starting from `start`).
+`steps` defaults to 8."
+
+ value: class extends Op
+ pattern = -evt['midi/port'] + val.num + val.num + val.num + -val.num
+ setup: (inputs, scope) =>
+ @out or= EventStream 'bang'
+ @state or= {}
+ { port, i, start, chan, steps } = pattern\match inputs
+
+ super
+ port: Input.hot port or scope\get '*ctrl*'
+ i: Input.hot i
+ start: Input.hot start
+ chan: Input.hot chan
+ steps: Input.hot steps or ValueStream.num 8
+
+ light = (set, active) ->
+ set = if set then 'S' else ' '
+ active = if active then 'A' else ' '
+ color switch set .. active
+ when ' ' then 0, 0
+ when ' A' then 1, 1
+ when 'S ' then 1, 0
+ when 'SA' then 3, 1
+
+ display: (i, active) =>
+ start, chan = @inputs.start!, @inputs.chan!
+ @inputs.port.stream\send 'note-on', chan, (start + i), light @state[i+1], active
+
+ tick: =>
+ { :port, :i, :start, :chan, :steps } = @inputs
+
+ if steps\dirty!
+ while steps! > #@state
+ table.insert @state, false
+ while steps! < #@state
+ table.remove @state
+
+ curr_i = i! % #@state
+
+ if port\dirty!
+ for msg in *port!
+ if msg.status == 'note-on' and msg.chan == chan!
+ rel_i = msg.a - start!
+ if rel_i >= 0 and rel_i < #@state
+ @state[rel_i+1] = not @state[rel_i+1]
+ @display rel_i, rel_i == curr_i
+
+ if i\dirty!
+ prev_i = (curr_i - 1) % #@state
+
+ @display curr_i, true
+ @display prev_i, false
+
+ if @state[curr_i+1]
+ @out\add true
+
{
- 'gate-seq': gate_seq
'cc-seq': cc_seq
+ 'gate-seq': gate_seq
+ 'trig-seq': trig_seq
}
diff --git a/lib/osc.moon b/lib/osc.moon
index fe4eae4..2453940 100644
--- a/lib/osc.moon
+++ b/lib/osc.moon
@@ -1,23 +1,23 @@
-import Op, Value, Input, match from require 'core.base'
+import Op, ValueStream, Input, val from require 'core.base'
import pack from require 'osc'
import dns, udp from require 'socket'
unpack or= table.unpack
-connect = Value.meta
+connect = ValueStream.meta
meta:
name: 'connect'
summary: "Create a UDP remote."
examples: { '(osc/connect host port)' }
value: class extends Op
- new: => super 'udp/socket'
-
+ pattern = val.str + val.num
setup: (inputs) =>
- { host, port } = match 'str num', inputs
+ @out or= ValueStream 'udp/socket'
+ { host, port } = pattern\match inputs
super
- host: Input.value host
- port: Input.value port
+ host: Input.hot host
+ port: Input.hot port
tick: =>
{ :host, :port } = @unwrap_all!
@@ -26,44 +26,42 @@ connect = Value.meta
@out\set with sock = udp!
\setpeername ip, port
-send = Value.meta
+send = ValueStream.meta
meta:
name: 'send'
summary: "Send a value via OSC."
- examples: { '(osc/send socket path val)' }
+ examples: { '(osc/send [socket] path val)' }
description: "sends a message only when `val` is dirty."
value: class extends Op
- setup: (inputs) =>
- { socket, path, value } = match 'udp/socket str any', inputs
+ pattern = -val['udp/socket'] + val.str + val!
+ setup: (inputs, scope) =>
+ { socket, path, value } = pattern\match inputs
super
- socket: Input.cold socket
+ socket: Input.cold socket or scope\get '*sock*'
path: Input.cold path
- value: Input.value value
+ value: Input.hot value
tick: =>
- if @inputs.value\dirty!
- { :socket, :path, :value } = @unwrap_all!
- msg = if 'table' == type value
- pack path, unpack value
- else
- pack path, value
- socket\send msg
+ { :socket, :path, :value } = @unwrap_all!
+ msg = pack path, if 'table' == type value then unpack value else value
+ socket\send msg
-send_state = Value.meta
+send_state = ValueStream.meta
meta:
name: 'send'
summary: "Synchronize a value via OSC."
- examples: { '(osc/send! socket path val)' }
+ examples: { '(osc/send! [socket] path val)' }
description: "sends a message whenever any parameter is dirty."
value: class extends Op
- setup: (inputs) =>
- { socket, path, value } = match 'udp/socket str any', inputs
+ pattern = -val['udp/socket'] + val.str + val!
+ setup: (inputs, scope) =>
+ { socket, path, value } = pattern\match inputs
super
- socket: Input.value socket
- path: Input.value path
- value: Input.value value
+ socket: Input.hot socket or scope\get '*sock*'
+ path: Input.hot path
+ value: Input.hot value
tick: =>
{ :socket, :path, :value } = @unwrap_all!
diff --git a/lib/pilot.moon b/lib/pilot.moon
index b851440..1d6b554 100644
--- a/lib/pilot.moon
+++ b/lib/pilot.moon
@@ -1,4 +1,4 @@
-import Op, Value, Input, Error, match from require 'core.base'
+import Op, ValueStream, Input, val, evt from require 'core.base'
import udp from require 'socket'
local conn
@@ -20,47 +20,49 @@ send = (...) ->
conn or= udp!
conn\sendto str, '127.0.0.1', 49161
-play = Value.meta
+arg = val.num / val.str
+
+play = ValueStream.meta
meta:
name: 'play'
summary: "Play a note when a bang arrives."
examples: { '(pilot/play trig ch oct note [vel [len]])' }
value: class extends Op
+ pattern = evt.bang + arg^5
setup: (inputs) =>
- { trig, args } = match 'bang *any', inputs
- assert #args < 6, Error 'argument', "too many arguments!"
+ { trig, args } = pattern\match inputs
super
- trig: Input.event trig
+ trig: Input.hot trig
args: [Input.cold a for a in *args]
tick: =>
{ :trig, :args } = @inputs
- if trig\dirty! and trig!
+ for _ in *trig!
send [a! for a in *@inputs.args]
-play_ = Value.meta
+play_ = ValueStream.meta
meta:
name: 'play!'
summary: "Play a note when a note arrives."
examples: { '(pilot/play! ch oct note [vel [len]])' }
value: class extends Op
+ pattern = arg + arg + (evt.num / evt.str) + arg^2
setup: (inputs) =>
- { chan, octv, note, args } = match 'any any any *any', inputs
- assert #args < 3, Error 'argument', "too many arguments!"
+ { chan, octv, note, args } = pattern\match inputs
super
chan: Input.cold chan
octv: Input.cold octv
- note: Input.event note
+ note: Input.hot note
args: [Input.cold a for a in *args]
tick: =>
- if @inputs.note\dirty!
- { :chan, :oct, :note, :args } = @unwrap_all!
- send { chan, oct, note }, args
+ { :chan, :octv, :note, :args } = @inputs
+ for note in *note!
+ send { chan!, octv!, note! }, args
-effect = Value.meta
+effect = ValueStream.meta
meta:
name: 'effect'
summary: "Set effect parameters."
@@ -68,12 +70,13 @@ effect = Value.meta
description: "`effect` should be one of 'DIS', 'CHO', 'REV' or 'FEE'"
value: class extends Op
+ pattern = val.str + arg + arg
setup: (inputs) =>
- { which, a, b } = match 'str num num', inputs
+ { which, a, b } = pattern\match inputs
super {
- Input.cold which
- Input.value a
- Input.value b
+ Input.hot which
+ Input.hot a
+ Input.hot b
}
tick: =>
diff --git a/lib/random.moon b/lib/random.moon
index 9a98702..49da2b9 100644
--- a/lib/random.moon
+++ b/lib/random.moon
@@ -1,16 +1,16 @@
-import Value, Error, Op, Input, match from require 'core.base'
+import ValueStream, Error, Op, Input, val, evt from require 'core.base'
apply_range = (range, val) ->
if range\type! == 'str'
switch range!
- when 'uni' then val / 128
- when 'bip' then val / 64 - 1
- when 'rad' then val / 64 * math.pi
- when 'deg' then val / 128 * 360
+ when 'uni' then val
+ when 'bip' then val*2 - 1
+ when 'rad' then val*2 * math.pi
+ when 'deg' then val * 360
else
error Error 'argument', "unknown range '#{range!}'"
- elseif range.type == 'num'
- val / 128 * range!
+ elseif range\type! == 'num'
+ val * range!
else
error Error 'argument', "range has to be a string or number"
@@ -22,8 +22,9 @@ range can be one of:
- 'deg' [ 0 - 360[
- (num) [ 0 - num["
-class num extends Op
-num = Value.meta
+pattern = -evt.bang + -(val.num / val.str)
+
+num = ValueStream.meta
meta:
name: 'num'
summary: 'Generate a random number.'
@@ -32,24 +33,25 @@ num = Value.meta
#{range_doc}"
value: class extends Op
- new: =>
- super 'num'
- @gen!
+ new: (...) =>
+ super ...
+ @out or= ValueStream 'num'
+ @state or @gen!
- gen: => @state = { math.random! }
+ gen: => @state = math.random!
setup: (inputs) =>
- { trig, range } = match 'bang? any?', inputs
+ { trig, range } = pattern\match inputs
super
- trig: trig and Input.event trig
- range: Input.value range or Value.str 'uni'
+ trig: trig and Input.hot trig
+ range: Input.hot range or ValueStream.str 'uni'
tick: =>
@gen! if @inputs.trig and @inputs.trig\dirty!
- @out\set apply_range @inputs.range, @state[1]
+ @out\set apply_range @inputs.range, @state
-vec_ = (n) ->
- Value.meta
+vec = (n) ->
+ ValueStream.meta
meta:
name: "vec#{n}"
summary: 'Generate a random vector.'
@@ -58,17 +60,18 @@ vec_ = (n) ->
#{range_doc}"
value: class extends Op
- new: =>
- super "vec#{n}"
- @gen!
+ new: (...) =>
+ super ...
+ @out or= ValueStream "vec#{n}"
+ @state or @gen!
gen: => @state = for i=1,n do math.random!
setup: (inputs) =>
- { trig, range } = match 'bang? any?', inputs
+ { trig, range } = pattern\match inputs
super
- trig: trig and Input.event trig
- range: Input.value range or Value.str 'uni'
+ trig: trig and Input.hot trig
+ range: Input.hot range or ValueStream.str 'uni'
tick: =>
@gen! if @inputs.trig and @inputs.trig\dirty!
@@ -76,7 +79,7 @@ vec_ = (n) ->
{
:num
- vec2: vec_ 2
- vec3: vec_ 3
- vec4: vec_ 4
+ vec2: vec 2
+ vec3: vec 3
+ vec4: vec 4
}
diff --git a/lib/sc.moon b/lib/sc.moon
index dd9e826..5d68eee 100644
--- a/lib/sc.moon
+++ b/lib/sc.moon
@@ -1,8 +1,8 @@
-import Op, Value, Input, Error, match from require 'core.base'
+import Op, ValueStream, Input, val, evt from require 'core.base'
import pack from require 'osc'
import dns, udp from require 'socket'
-play = Value.meta
+play = ValueStream.meta
meta:
name: 'play'
summary: 'Play a SuperCollider SynthDef.'
@@ -12,20 +12,20 @@ Plays the synth `synth` on the `udp/socket` `socket` whenever `trig` is live.
Any number of parameter-value pairs can be specified and are captured and sent
together with the note when triggered."
value: class extends Op
+ pattern = val['udp/socket'] + val.str + evt.bang + (val.str + val.num)\rep 0
setup: (inputs) =>
- { socket, synth, trig, ctrls } = match 'udp/socket str bang *any?', inputs
+ { socket, synth, trig, ctrls } = pattern\match inputs
- assert #ctrls % 2 == 0, Error 'argument', "parameters need to be specified as pairs"
- for key in *ctrls[1,,2]
- assert key\type! == 'str', Error 'argument', "ony strings are supported as control names"
- for val in *ctrls[2,,2]
- assert val\type! == 'num', Error 'argument', "only numbers are supported as control values"
+ flat_ctrls = {}
+ for { key, value } in *ctrls
+ table.insert flat_ctrls, key
+ table.insert flat_ctrls, value
super
- trig: Input.event trig
+ trig: Input.hot trig
socket: Input.cold socket
synth: Input.cold synth
- ctrls: [Input.cold v for v in *ctrls]
+ ctrls: [Input.cold v for v in *flat_ctrls]
tick: =>
if @inputs.trig\dirty! and @inputs.trig!
diff --git a/lib/string.moon b/lib/string.moon
index 8b48977..3f9b28b 100644
--- a/lib/string.moon
+++ b/lib/string.moon
@@ -1,15 +1,17 @@
-import Op, Value, Input from require 'core.base'
+import Op, ValueStream, Input from require 'core.base'
-str = Value.meta
+str = ValueStream.meta
meta:
name: 'str'
summary: "Concatenate/stringify values."
examples: { '(.. v1 [v2…])', '(str v1 [v2…])' }
value: class extends Op
- new: => super 'str'
+ setup: (inputs) =>
+ @out or= ValueStream 'string'
+ super [Input.hot v for v in *inputs]
- setup: (inputs) => super [Input.value v for v in *inputs]
- tick: => @out\set table.concat [tostring v! for v in *@inputs]
+ tick: =>
+ @out\set table.concat [tostring v! for v in *@inputs]
{
:str, '..': str
diff --git a/lib/time.moon b/lib/time.moon
index ea0e80a..28a0824 100644
--- a/lib/time.moon
+++ b/lib/time.moon
@@ -1,8 +1,13 @@
-import Value, Error, IO, Op, Input, match from require 'core.base'
+import
+ ValueStream, EventStream, IOStream,
+ Error, Op, Input, val, evt
+from require 'core.base'
import monotime from require 'system'
-class Clock extends IO
+class Clock extends IOStream
new: (@frametime) =>
+ super 'clock'
+
return unless monotime
@last = monotime!
@dt = 0
@@ -17,9 +22,14 @@ class Clock extends IO
else
false
+ unwrap: =>
+ dt = @dt
+ time = @last
+ { :dt, :time }
+
dirty: => @is_dirty
-clock = Value.meta
+clock = ValueStream.meta
meta:
name: 'clock'
summary: "Create a clock source."
@@ -28,21 +38,23 @@ clock = Value.meta
IO that triggers other operators at a fixed frame rate.
`fps` defaults to 60 and has to be an eval-time constant"
value: class extends Op
- new: => super 'clock'
+ new: (...) =>
+ super ...
+ @out or= Clock!
setup: (inputs) =>
- { fps } = match 'num?', inputs
- super fps: Input.value fps or Value.num 60
+ fps = (-val.num)\match inputs
+ super fps: Input.hot fps or ValueStream.num 60
+ @out.frametime = 1 / @inputs.fps!
tick: =>
- if @inputs.fps\dirty!
- @out\set Clock 1 / @inputs.fps!
+ @out.frametime = 1 / @inputs.fps!
-lfo = Value.meta
+lfo = ValueStream.meta
meta:
name: 'lfo'
summary: "Low-frequency oscillator."
- examples: { '(lfo [clock] freq wave)' }
+ examples: { '(lfo [clock] freq [wave])' }
description: "
oscillates between 0 and 1 at the frequency freq.
wave selects the wave shape from the following:
@@ -50,31 +62,32 @@ wave selects the wave shape from the following:
- `'saw'`
- `'tri'`"
value: class extends Op
- new: =>
- super 'num'
- @state.phase or= 0
+ new: (...) =>
+ super ...
+ @state or= 0
+ @out or= ValueStream 'num'
- default_wave = Value.str 'sin'
+ default_wave = ValueStream.str 'sin'
+ pattern = -evt.clock + val.num + -val.str
setup: (inputs, scope) =>
- { clock, freq, wave } = match 'clock? num any?', inputs
+ { clock, freq, wave } = pattern\match inputs
super
- clock: Input.io clock or scope\get '*clock*'
- freq: Input.value freq
- wave: Input.value wave or default_wave
+ clock: Input.hot clock or scope\get '*clock*'
+ freq: Input.cold freq
+ wave: Input.hot wave or default_wave
tau = math.pi * 2
tick: =>
if @inputs.clock\dirty!
- { :clock, :freq, :wave } = @unwrap_all!
+ @state += @inputs.clock!.dt * @iputs.freq!
- @state.phase += clock.dt * freq
- @out\set switch wave
- when 'sin' then .5 + .5 * math.cos @state.phase * tau
- when 'saw' then @state.phase % 1
- when 'tri' then math.abs (2*@state.phase % 2) - 1
- else error Error 'argument', "unknown wave type '#{wave}'"
+ @out\set switch @inputs.wave!
+ when 'sin' then .5 + .5 * math.cos @state * tau
+ when 'saw' then @state % 1
+ when 'tri' then math.abs (2*@state % 2) - 1
+ else error Error 'argument', "unknown wave type '#{wave}'"
-ramp = Value.meta
+ramp = ValueStream.meta
meta:
name: 'ramp'
summary: "Sawtooth LFO."
@@ -82,31 +95,32 @@ ramp = Value.meta
description: "
ramps from 0 to max (default same as ramp) once every period seconds."
value: class extends Op
- new: =>
- super 'num'
- @state.phase or= 0
+ new: (...) =>
+ super ...
+ @state or= 0
+ @out or= ValueStream 'num'
+ pattern = -evt.clock + val.num + -val.num
setup: (inputs, scope) =>
- { clock, period, max } = match 'clock? num num?', inputs
+ { clock, period, max } = pattern\match inputs
super
- clock: Input.io clock or scope\get '*clock*'
- period: Input.value period
- max: max and Input.value max
+ clock: Input.hot clock or scope\get '*clock*'
+ period: Input.cold period
+ max: max and Input.cold max
tick: =>
clock_dirty = @inputs.clock\dirty!
if clock_dirty
- { :clock, :period, :max } = @unwrap_all!
- max or= period
- @state.phase += clock.dt / period
+ period = @inputs.period!
+ max = (@inputs.max or @inputs.period)!
+ @phase += @inputs.clock!.dt / period
- while @state.phase >= 1
- @state.phase -= 1
+ while @phase >= 1
+ @phase -= 1
- if clock_dirty or (@inputs.max and @inputs.max\dirty!)
- @out\set @state.phase * max
+ @out\set @phase * max
-tick = Value.meta
+tick = ValueStream.meta
meta:
name: 'tick'
summary: "Count ticks."
@@ -115,52 +129,51 @@ tick = Value.meta
counts upwards by one every period seconds and returns the number of completed
ticks."
value: class extends Op
- new: =>
- super 'num', 0
- @state.phase or= 0
- @state.count or= 0
+ new: (...) =>
+ super ...
+ @state or= { phase: 0, count: 0 }
+ @out or= ValueStream 'num', @state.count
+ pattern = -evt.clock + val.num
setup: (inputs, scope) =>
- { clock, period } = match 'clock? num', inputs
+ { clock, period } = pattern\match inputs
super
- clock: Input.io clock or scope\get '*clock*'
- period: Input.value period
+ clock: Input.hot clock or scope\get '*clock*'
+ period: Input.cold period
tick: =>
- if @inputs.clock\dirty!
- { :clock, :period, :max } = @unwrap_all!
- @state.phase += clock.dt / period
+ @state.phase += @inputs.clock!.dt / @inputs.period!
- while @state.phase >= 1
- @state.phase -= 1
- @state.count += 1
- @out\set @state.count
+ while @state.phase >= 1
+ @state.phase -= 1
+ @state.count += 1
+ @out\set @state.count
-every = Value.meta
+every = ValueStream.meta
meta:
name: 'every'
summary: "Emit bangs."
examples: { '(every [clock] period)' }
description: "returns true once every period seconds."
value: class extends Op
- new: =>
- super 'bang'
- @state.phase or= 0
+ new: (...) =>
+ super ...
+ @state or= 0
+ @out or= EventStream 'bang'
+ pattern = -evt.clock + val.num
setup: (inputs, scope) =>
- { clock, period } = match 'clock? num', inputs
+ { clock, period } = pattern\match inputs
super
- clock: Input.io clock or scope\get '*clock*'
- period: Input.value period
+ clock: Input.hot clock or scope\get '*clock*'
+ period: Input.cold period
tick: =>
- if @inputs.clock\dirty!
- { :clock, :period, :max } = @unwrap_all!
- @state.phase += clock.dt / period
+ @state += @inputs.clock!.dt / @inputs.period!
- while @state.phase >= 1
- @state.phase -= 1
- @out\set true
+ while @state >= 1
+ @state -= 1
+ @out\add true
{
:clock
@@ -168,7 +181,7 @@ every = Value.meta
:ramp
:tick
:every
- '*clock*': with Value 'clock', Clock 1/60
+ '*clock*': with Clock 1/60
.meta =
name: '*clock*'
summary: 'Default clock source (60fps).'
diff --git a/lib/util.moon b/lib/util.moon
index a2c65ba..d760a64 100644
--- a/lib/util.moon
+++ b/lib/util.moon
@@ -1,4 +1,4 @@
-import Op, Value, Input, Error, match from require 'core.base'
+import Op, ValueStream, EventStream, Input, val, evt from require 'core.base'
all_same = (list) ->
for v in *list[2,]
@@ -7,7 +7,7 @@ all_same = (list) ->
list[1]
-switch_ = Value.meta
+switch_ = ValueStream.meta
meta:
name: 'switch'
summary: "Switch between multiple inputs."
@@ -19,17 +19,21 @@ switch_ = Value.meta
(indexed starting from 0) is reproduced."
value: class extends Op
+ val_or_evt = (val! / evt!)!
+ pattern = (val.num / val.bool) + val_or_evt*0
setup: (inputs) =>
- { i, values } = match 'any *any', inputs
+ { i, values } = pattern\match inputs
- i_type = i\type!
- assert i_type == 'bool' or i_type == 'num', Error 'argument', "i has to be bool or num"
- typ = all_same [v\type! for v in *values]
- @out = Value typ if not @out or typ != @out.type
+ @state = values[1].value.__class == ValueStream
+
+ @out = if @state
+ ValueStream values[1]\type!
+ else
+ EventStream values[1]\type!
super
- i: Input.value i
- values: [Input.value v for v in *values]
+ i: Input.hot i
+ values: [Input.hot v for v in *values]
tick: =>
{ :i, :values } = @inputs
@@ -41,66 +45,68 @@ switch_ = Value.meta
else
i = 1 + (math.floor i!) % #values
values[i]
- @out\set active and active!
-
-route = Value.meta
+ if @state
+ @out\set active and active!
+ else
+ if active and active\dirty!
+ for event in *active!
+ @out\add event
+
+edge = ValueStream.meta
meta:
- name: 'route'
- summary: "Route between multiple inputs."
- examples: { '(route i v0 [e1 e2…])' }
- description: "
-- when `i` is `true`, the first event stream is reproduced.
-- when `i` is `false`, the second event stream is reproduced.
-- when `i` is a `num`, it is [math/floor][]ed and the matching argument
- (indexed starting from 0) is reproduced."
+ name: 'edge'
+ summary: "Convert rising edges to bangs."
+ examples: { '(edge bool)' }
value: class extends Op
setup: (inputs) =>
- { i, values } = match 'any *any', inputs
+ @out or= EventStream 'bang'
+ value = val.bool\match inputs
+ super value: Input.hot value
+
+ tick: =>
+ now = @inputs.value!
+ if now and not @state.last
+ @out\set true
+ @state.last = now
- i_type = i\type!
- assert i_type == 'bool' or i_type == 'num', Error 'argument', "i has to be bool or num"
- typ = all_same [v\type! for v in *values]
- @out = Value typ if not @out or typ != @out.type
+change = ValueStream.meta
+ meta:
+ name: 'change'
+ summary: "Convert value changes to events."
+ examples: { '(change val)' }
- super
- i: Input.value i
- values: [Input.value v for v in *values]
+ value: class extends Op
+ setup: (inputs) =>
+ value = val!\match inputs
+ @out or= EventStream value\type!
+ super value: Input.hot value
tick: =>
- { :i, :values } = @inputs
- active = switch i!
- when true
- values[1]
- when false
- values[2]
- else
- i = 1 + (math.floor i!) % #values
- values[i]
- if active and active\dirty!
- @out\set active!
+ now = @inputs.value!
+ if now != @state
+ @out\add @inputs.value!
+ @state = now
-route = Value.meta
+hold = ValueStream.meta
meta:
- name: 'edge'
- summary: "Convert rising edges to bangs."
- examples: { '(edge bool)' }
+ name: 'hold'
+ summary: "Convert events to value changes."
+ examples: { '(hold evt)' }
value: class extends Op
- new: => super 'bang'
-
setup: (inputs) =>
- { value } = match 'bool', inputs
- super value: Input.value value
+ event = evt!\match inputs
+ @out or= ValueStream event\type!
+ super event: Input.hot event
tick: =>
- now = @inputs.value!
- if now and not @state.last
- @out\set true
- @state.last = now
+ for val in *@inputs.event!
+ @out\set val
{
'switch': switch_
- :route
:edge
+ :change
+ :hold
}