aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authors-ol <s-ol@users.noreply.github.com>2020-03-02 14:40:00 +0000
committers-ol <s-ol@users.noreply.github.com>2020-03-02 16:48:09 +0000
commit4ebb6f4408600f3c0f2a745b7ed0ba951bf521b3 (patch)
treecdf0953fdee07a6c83c510470a7b8ab8d832d25d
parentupdate lib.math and lib.logic to new op interface (diff)
downloadalive-4ebb6f4408600f3c0f2a745b7ed0ba951bf521b3.tar.gz
alive-4ebb6f4408600f3c0f2a745b7ed0ba951bf521b3.zip
lift remaining libs to new op interface
-rw-r--r--core/base.moon9
-rw-r--r--core/init.moon5
-rw-r--r--lib/envelope.moon26
-rw-r--r--lib/logic.moon4
-rw-r--r--lib/midi/core.moon1
-rw-r--r--lib/midi/init.moon32
-rw-r--r--lib/midi/launchctl.moon1
-rw-r--r--lib/osc.moon76
-rw-r--r--lib/pilot.moon56
-rw-r--r--lib/random.moon77
-rw-r--r--lib/sc.moon42
-rw-r--r--lib/string.moon12
-rw-r--r--lib/util.moon75
13 files changed, 279 insertions, 137 deletions
diff --git a/core/base.moon b/core/base.moon
index c6ea694..41ddd4c 100644
--- a/core/base.moon
+++ b/core/base.moon
@@ -27,6 +27,12 @@ class Input
cls.__base.__call = @__call
cls.__base.__tostring = @__tostring
+-- ColdInput scheduling policy
+--
+-- never marked dirty
+class ColdInput extends Input
+ dirty: => false
+
-- ValueInput scheduling policy
--
-- during setup, only marked dirty if old and new stream differ in value
@@ -210,8 +216,7 @@ class FnDef
"(fn (#{table.concat [p\stringify! for p in *@params], ' '}) ...)"
{
- :ValueInput, :EventInput, :IOInput
- :Dispatcher
+ :ValueInput, :EventInput, :IOInput, :ColdInput
:IO
:Op
:Action
diff --git a/core/init.moon b/core/init.moon
index 6b5bdd0..6a8c5d4 100644
--- a/core/init.moon
+++ b/core/init.moon
@@ -1,6 +1,6 @@
L or= setmetatable {}, __index: => ->
-import Op, IO, Action, FnDef, EventInput, ValueInput, IOInput
+import Op, IO, Action, FnDef, EventInput, ValueInput, IOInput, ColdInput
from require 'core.base'
import match from require 'core.pattern'
@@ -21,7 +21,8 @@ globals = Scope.from_table require 'core.builtin'
:Cell, :RootCell
:Op, :IO, :Action, :FnDef
- :EventInput, :ValueInput, :IOInput, :match
+ :EventInput, :ValueInput, :IOInput, :ColdInput,
+ :match
:Scope
:Registry, :Tag
diff --git a/lib/envelope.moon b/lib/envelope.moon
deleted file mode 100644
index eb79a64..0000000
--- a/lib/envelope.moon
+++ /dev/null
@@ -1,26 +0,0 @@
-import Stream, Op, Scope, eval from require 'core'
-
-class ar_core extends Op
- @doc: "(ar-core attack release gate)
-((ar attack release) gate) - AR envelope
-
-goes from 0 to 1 in attack seconds, holds while gate is on, then goes back to 0 in release seconds."
-
- new: (...) =>
- super ...
- @out = Stream 'num', 0
-
- setup: (@a, @r, @gate) =>
- assert @a, "ar requires an attack value"
- assert @r, "ar requires a release value"
- assert @gate, "ar requires a gate value"
- @out
-
- update: (dt) =>
- slope = if (@gate\unwrap 'bool') then (@a\unwrap! or 0.1) else -(@r\unwrap! or 0.5)
-
- @out\set math.min 1, math.max 0, @out\unwrap! + dt / slope
-
-scope = Scope.from_table { 'ar-core': ar_core }
-scope\set 'ar', eval '(fn (a r) (fn (gate) (ar-core a r gate)))', scope
-scope
diff --git a/lib/logic.moon b/lib/logic.moon
index d2144f6..b4c6cf7 100644
--- a/lib/logic.moon
+++ b/lib/logic.moon
@@ -5,7 +5,7 @@ all_same = (first, list) ->
if v != first
return false
- true
+ first
tobool = (val) ->
switch val
@@ -83,9 +83,7 @@ class not_eq extends Op
diff = true
for a=1, #@inputs-1
for b=a+1, #@inputs
- print @inputs[a], @inputs[b]
if @inputs[a].stream == @inputs[b].stream
- print "found a match #{a} == #{b}"
diff = false
break
diff --git a/lib/midi/core.moon b/lib/midi/core.moon
index aeef861..d91fdcb 100644
--- a/lib/midi/core.moon
+++ b/lib/midi/core.moon
@@ -105,6 +105,7 @@ apply_range = (range, val) ->
when 'uni' then val / 128
when 'bip' then val / 64 - 1
when 'rad' then val / 64 * math.pi
+ when 'deg' then val / 128 * 360
else
error "unknown range #{@range}"
elseif range.type == 'num'
diff --git a/lib/midi/init.moon b/lib/midi/init.moon
index 3bfba48..daf0088 100644
--- a/lib/midi/init.moon
+++ b/lib/midi/init.moon
@@ -8,14 +8,14 @@ class gate extends Op
super 'bool', false
setup: (inputs) =>
- { :port, :note, :chan } = match 'midi/port num num?', inputs
+ { port, note, chan } = match 'midi/port num num?', inputs
super
port: IOInput port
note: ValueInput note
chan: ValueInput chan or Value.num -1
tick: =>
- { port, note, chan } = @inputs
+ { :port, :note, :chan } = @inputs
if note\dirty! or chan\dirty!
@out\set false
@@ -28,6 +28,32 @@ class gate extends Op
elseif msg.status == 'note-off'
@out\set false
+class trig extends Op
+ @doc: "(midi/trig port note [chan]) - gate from note-on and note-off messages"
+
+ new: =>
+ super 'bang', false
+
+ setup: (inputs) =>
+ { port, note, chan } = match 'midi/port num num?', inputs
+ super
+ port: IOInput port
+ note: ValueInput note
+ chan: ValueInput chan or Value.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
+
+
class cc extends Op
@doc: "(midi/cc port cc [chan [range]]) - MIDI CC to number
@@ -36,6 +62,7 @@ range can be one of:
- 'uni' [ 0 - 1[ (default)
- 'bip' [-1 - 1[
- 'rad' [ 0 - tau[
+- 'deg' [ 0 - 360[
- (num) [ 0 - num["
new: =>
@@ -66,5 +93,6 @@ range can be one of:
:output
:inout
:gate
+ :trig
:cc
}
diff --git a/lib/midi/launchctl.moon b/lib/midi/launchctl.moon
index b3bf5da..1acafdc 100644
--- a/lib/midi/launchctl.moon
+++ b/lib/midi/launchctl.moon
@@ -16,6 +16,7 @@ range can be one of:
- 'uni' [ 0 - 1[ (default)
- 'bip' [-1 - 1[
- 'rad' [ 0 - tau[
+- 'deg' [ 0 - 360[
- (num) [ 0 - num["
new: =>
diff --git a/lib/osc.moon b/lib/osc.moon
index bced72f..e73a982 100644
--- a/lib/osc.moon
+++ b/lib/osc.moon
@@ -1,41 +1,67 @@
-import Op from require 'core'
-import pack, unpack from require 'osc'
+import Op, ValueInput, EventInput, ColdInput, match from require 'core'
+import pack from require 'osc'
import dns, udp from require 'socket'
-class addr extends Op
- @doc: "(remote host port) - UDP remote definition"
+unpack or= table.unpack
- new: =>
- super 'udp/remote'
- @@udp or= udp!
+class connect extends Op
+ @doc: "(osc/connect host port) - UDP remote definition"
- setup: (params) =>
- super params
- @assert_types 'str', 'num'
+ new: => super 'udp/socket'
+
+ setup: (inputs) =>
+ { host, port } = match 'str num', inputs
+ super
+ host: ValueInput host
+ port: ValueInput port
tick: =>
- host, port = @unwrap_inputs!
+ { :host, :port } = @unwrap_all!
ip = dns.toip host
- @out\set { :ip, :port }
-class out extends Op
- @doc: "(out remote path val) - send a value via OSC"
+ @out\set with sock = udp!
+ \setpeername ip, port
+
+class send extends Op
+ @doc: "(osc/send socket path val) - send a value via OSC
+
+sends a message only when val is dirty."
+
+ setup: (inputs) =>
+ { socket, path, value } = match 'udp/socket str any', inputs
+ super
+ socket: ColdInput socket
+ path: ColdInput path
+ value: EventInput 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
+
+class send_state extends Op
+ @doc: "(osc/send! socket path val) - synchronize a value via OSC
- new: (...) =>
- @@udp or= udp!
+sends a whenever any parameter changes."
- setup: (params) =>
- super params
- assert @inputs[3], "need a value"
- @assert_types 'udp/remote', 'str', @inputs[3].type
+ setup: (inputs) =>
+ { socket, path, value } = match 'udp/socket str any', inputs
+ super
+ socket: ValueInput socket
+ path: ValueInput path
+ value: ValueInput value
tick: =>
- remote, path, value = @unwrap_inputs!
- { :ip, :port } = remote
+ { :socket, :path, :value } = @unwrap_all!
msg = pack path, value
- @@udp\sendto msg, ip, port
+ socket\send msg
{
- :addr
- :out
+ :connect
+ :send
+ 'send!': send_state
}
diff --git a/lib/pilot.moon b/lib/pilot.moon
index 2a7d21a..a442b77 100644
--- a/lib/pilot.moon
+++ b/lib/pilot.moon
@@ -1,4 +1,4 @@
-import Op from require 'core'
+import Op, EventInput, ValueInput, ColdInput, match from require 'core'
import udp from require 'socket'
conn = udp!
@@ -11,32 +11,62 @@ encode = (arg) ->
when 'string' then arg
else error "invalid type: #{type arg}"
-send = (tbl) ->
- str = table.concat [encode v for v in *tbl]
+send = (...) ->
+ str = ''
+ for i = 1, select '#', ...
+ tbl = select i, ...
+ str ..= table.concat [encode v for v in *tbl]
conn\sendto str, '127.0.0.1', 49161
class play extends Op
@doc: "(play trig ch oct note [vel [len]]) - play a note when trig is live"
- setup: (@trig, ...) =>
- @vals = { ... }
+ setup: (inputs) =>
+ { trig, args } = match 'bang *any', inputs
+ assert #args < 6, "too many arguments!"
+ super
+ trig: EventInput trig
+ args: [ColdInput a for a in *args]
- update: (dt) =>
- if @trig\unwrap 'bool'
- send [c\unwrap! for c in *@vals]
+ tick: =>
+ { :trig, :args } = @inputs
+ if trig\dirty! and trig!
+ send [a! for a in *@inputs.args]
+
+class play_ extends Op
+ @doc: "(play! ch oct note [vel [len]]) - play a note when note is live"
+
+ setup: (inputs) =>
+ { chan, octv, note, args } = match 'any any any *any', inputs
+ assert #args < 3, "too many arguments!"
+ super
+ chan: ColdInput chan
+ octv: ColdInput octv
+ note: EventInput note
+ args: [ColdInput a for a in *args]
+
+ tick: =>
+ if @inputs.note\dirty!
+ { :chan, :oct, :note, :args } = @unwrap_all!
+ send { chan, oct, note }, args
class effect extends Op
@doc: "(effect which a b) - set an effect
which is one of 'DIS', 'CHO', 'REV' or 'FEE'"
- setup: (@which, @a, @b) =>
+ setup: (inputs) =>
+ { which, a, b } = match 'str num num', inputs
+ super {
+ ColdInput which
+ ValueInput a
+ ValueInput b
+ }
- update: (dt) =>
- which, a, b = (@which\unwrap 'str'), @a\unwrap!, @b\unwrap!
- if which and a and b
- send { which, a, b }
+ tick: =>
+ send @unwrap_all!
{
:play
+ 'play!': play_
:effect
}
diff --git a/lib/random.moon b/lib/random.moon
new file mode 100644
index 0000000..9e5e62b
--- /dev/null
+++ b/lib/random.moon
@@ -0,0 +1,77 @@
+import Op, Value, ValueInput, EventInput, match from require 'core'
+
+apply_range = (range, val) ->
+ if range\type! == 'str'
+ switch range\unwrap!
+ when 'uni' then val
+ when 'bip' then val * 2 - 1
+ when 'rad' then val * 2 * math.pi
+ when 'deg' then val * 360
+ else
+ error "unknown range #{@range}"
+ elseif range.type == 'num'
+ val * range\unwrap!
+ else
+ error "range has to be a string or number"
+
+range_doc = "
+range can be one of:
+- 'uni' [ 0 - 1[ (default)
+- 'bip' [-1 - 1[
+- 'rad' [ 0 - tau[
+- 'deg' [ 0 - 360[
+- (num) [ 0 - num["
+
+class num extends Op
+ @doc: "(random/num [trigger] [range]) - create a random number
+
+generates a random value in range on create and trigger.
+#{range_doc}"
+ new: =>
+ super 'num'
+ @gen!
+
+ gen: => @val = math.random!
+
+ setup: (inputs) =>
+ { trig, range } = match 'bang? any?', inputs
+ super
+ trig: trig and EventInput trig
+ range: ValueInput range or Value.str 'uni'
+
+ tick: =>
+ @gen! if @inputs.trig and @inputs.trig\dirty!
+ @out\set apply_range @inputs.range, @val
+
+vec_ = (n) ->
+ class vec extends Op
+ @doc: "(random/vec#{n} [trigger] [range]) - create a random number
+
+generates a random vec#{n} on create and trigger.
+each component is in range.
+#{range_doc}"
+ new: =>
+ super "vec#{n}"
+ @gen!
+
+ gen: => @val = for i=1,n do math.random!
+
+ setup: (inputs) =>
+ { trig, range } = match 'bang? any?', inputs
+ super
+ trig: trig and EventInput trig
+ range: ValueInput range or Value.str 'uni'
+
+ tick: =>
+ @gen! if @inputs.trig and @inputs.trig\dirty!
+ @out\set [apply_range @inputs.range, v for v in *@val]
+
+ vec.__name = "vec#{n}"
+ vec
+
+{
+ :num
+ vec2: vec_ 2
+ vec3: vec_ 3
+ vec4: vec_ 4
+}
diff --git a/lib/sc.moon b/lib/sc.moon
index 7ffd16c..b30e30d 100644
--- a/lib/sc.moon
+++ b/lib/sc.moon
@@ -1,36 +1,30 @@
-import Op, Value, Scope from require 'core'
+import Op, EventInput, ColdInput, match from require 'core'
import pack from require 'osc'
import dns, udp from require 'socket'
class play extends Op
- @doc: "(sc/play remote synth trigger [name-str val]...) - play a SC SynthDef"
+ @doc: "(sc/play socket synth trigger [name-str val]...) - play a SC SynthDef"
- new: =>
- super!
- @@udp or= udp!
+ setup: (inputs) =>
+ { socket, synth, trig, ctrls } = match 'udp/socket str bang *any?', inputs
- setup: (params) =>
- super params
- @assert_first_types 'udp/remote', 'str', 'bool'
+ assert #ctrls % 2 == 0, "parameters need to be specified as pairs"
+ for key in *ctrls[1,,2]
+ assert key\type! == 'str', "ony strings are supported as control names"
+ for val in *ctrls[2,,2]
+ assert val\type! == 'num', "only numbers are supported as control values"
- assert #@inputs % 2 == 1, "parameters need to be specified as pairs"
- for key in *@inputs[4,,2]
- assert key.type == 'str', "ony strings are supported as control names"
- for val in *@inputs[5,,2]
- assert val.type == 'num', "only numbers are supported as control values"
+ super
+ socket: ColdInput socket
+ synth: ColdInput synth
+ trig: EventInput trig
+ ctrls: [ColdInput v for v in *ctrls]
tick: =>
- { remote, synth, trig, p } = @inputs
-
- if trig\dirty! and trig!
- controls = {}
- for i = 4, #@inputs, 2
- table.insert controls, @inputs[i]!
- table.insert controls, @inputs[i+1]!
- msg = pack '/s_new', synth!, -1, 0, 1, unpack controls
-
- { :ip, :port } = remote!
- @@udp\sendto msg, ip, port
+ if @inputs.trig\dirty! and @inputs.trig!
+ { :socket, :synth, :ctrls } = @unwrap_all!
+ msg = pack '/s_new', synth, -1, 0, 1, unpack ctrls
+ socket\send msg
{
:play
diff --git a/lib/string.moon b/lib/string.moon
index 6a9b7d7..8c10c5d 100644
--- a/lib/string.moon
+++ b/lib/string.moon
@@ -1,16 +1,12 @@
-import Stream, Op from require 'core'
+import Op, ValueInput from require 'core'
class str extends Op
@doc: "(str v1 [v2]...)
(.. v1 [v2]...) - concatenate/stringify values"
+ new: => super 'str'
- setup: (...) =>
- @children = { ... }
- @out = Stream 'str'
- @out
-
- update: (dt) =>
- @out\set table.concat [tostring child\unwrap! for child in *@children]
+ setup: (inputs) => super [ValueInput v for v in *inputs]
+ tick: => @out\set table.concat [tostring v! for v in *@inputs]
{
:str, '..': str
diff --git a/lib/util.moon b/lib/util.moon
index 747cb0d..6ada851 100644
--- a/lib/util.moon
+++ b/lib/util.moon
@@ -1,4 +1,11 @@
-import Value, Op from require 'core'
+import Op, Value, ValueInput, EventInput, ColdInput, match from require 'core'
+
+all_same = (list) ->
+ for v in *list[2,]
+ if v != list[1]
+ return false
+
+ list[1]
class switch_ extends Op
@doc: "(switch i v0 [v1 v2...]) - switch between multiple inputs
@@ -7,27 +14,29 @@ when i is true, the first value is reproduced.
when i is false, the second value is reproduced.
when i is a num, it is (floor)ed and the matching argument (starting from 0) is reproduced."
- setup: (params) =>
- super params
+ setup: (inputs) =>
+ { i, values } = match 'any *any', inputs
- { i, first } = @inputs
- assert i.type == 'bool' or i.type == 'num', "#{@}: i has to be bool or num"
+ i_type = i\type!
+ assert i_type == 'bool' or i_type == 'num', "#{@}: i has to be bool or num"
+ typ = all_same [v\type! for v in *values]
+ @out = Value typ
- for inp in *@inputs[3,]
- assert inp.type == first.type, "not all values have the same type: #{first.type} != #{inp.type}"
- @out = Value first.type, first!
+ super
+ i: ValueInput i
+ values: [ValueInput v for v in *values]
tick: =>
- i = @inputs[1]\unwrap!
- active = switch i
+ { :i, :values } = @inputs
+ active = switch i!
when true
- @inputs[2]
+ values[1]
when false
- @inputs[3]
+ values[2]
else
- i = 2 + (math.floor i) % (#@inputs - 1)
- @inputs[i]
- @out\set active and active\unwrap!
+ i = 1 + (math.floor i!) % #values
+ values[i]
+ @out\set active and active!
--class switch_pause extends Op
-- @doc: "(switch- i v0 [v1 v2...]) - switch and pause multiple inputs
@@ -60,38 +69,40 @@ when i is a num, it is (floor)ed and the matching argument (starting from 0) is
class edge extends Op
@doc: "(edge bool) - convert rising edges to bangs"
+ new: => super 'bang'
- new: =>
- super 'bang'
-
- setup: (params) =>
- super params
- @assert_types 'bool'
+ setup: (inputs) =>
+ { value } = match 'bool', inputs
+ super value: EventInput
tick: =>
- now = @params[1]\unwrap!
+ now = @inputs.value!
if now and not @last
@out\set true
@last = now
-class keep extends Op
- @doc: "(keep value [init]) - keep the last non-nil value
+class default extends Op
+ @doc: "(default stream default) - provide a default value for an event stream
-always reproduces the last non-nil value the input produced or default.
+starts out as default but forwards events from stream.
default defaults to zero."
setup: (params) =>
- super params
- { i, init } = @inputs
- @out = Value i.type, default and init\unwrap!
+ { value, init } = match 'any any', inputs
+ super
+ value: EventInput value
+ init: ColdInput init
+
+ @out = Value value\type!
+ @out\set @inputs.init\unwrap!
tick: =>
- if next = @params[1]\unwrap!
- @out\set next
+ { :value } = @inputs
+ if value\dirty!
+ @out\set value!
{
'switch': switch_
--- 'switch-': switch_pause
:edge
- :keep
+ :default
}