aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authors-ol <s-ol@users.noreply.github.com>2021-01-08 13:43:24 +0000
committers-ol <s+removethis@s-ol.nu>2025-03-02 14:24:49 +0000
commit020429ac00fb4827fc528313ad85f6ca87733ebe (patch)
tree720d6d9fab2bbaccb8f72602cc5ddb851aeba6a0
parentcopilot/cli: colorize output linewise (diff)
downloadalive-020429ac00fb4827fc528313ad85f6ca87733ebe.tar.gz
alive-020429ac00fb4827fc528313ad85f6ca87733ebe.zip
lib: various fixes
-rw-r--r--alv-lib/midi.moon85
-rw-r--r--alv-lib/midi/core.moon2
-rw-r--r--alv-lib/rhythm.moon4
-rw-r--r--alv-lib/time.moon13
4 files changed, 86 insertions, 18 deletions
diff --git a/alv-lib/midi.moon b/alv-lib/midi.moon
index a42867e..83fe32f 100644
--- a/alv-lib/midi.moon
+++ b/alv-lib/midi.moon
@@ -1,5 +1,6 @@
-import Constant, Op, Input, T, sig, evt from require 'alv.base'
+import Constant, Op, Input, T, Struct, sig, evt from require 'alv.base'
import input, output, port, apply_range from require 'alv-lib.midi.core'
+import monotime from require 'system'
gate = Constant.meta
meta:
@@ -81,7 +82,7 @@ cc = Constant.meta
meta:
name: 'cc'
summary: "`num` from cc-change messages."
- examples: { '(midi/cc [port] cc [chan [range]])' }
+ examples: { '(midi/cc [port] cc [range] [chan])' }
description: "
`range` can be one of:
@@ -93,18 +94,19 @@ cc = Constant.meta
- (num) [ 0 - num["
value: class extends Op
- pattern = -sig['midi/in'] + sig.num + -sig.num + -sig.num
+ pattern = -sig['midi/in'] + sig.num + -(sig.num / sig.str) + -sig.num
setup: (inputs, scope) =>
- { port, cc, chan, range } = pattern\match inputs
+ { port, cc, range, chan } = pattern\match inputs
super
port: Input.cold port or scope\get '*midi*'
cc: Input.cold cc
- chan: Input.cold chan or Constant.num -1
range: Input.cold range or Constant.str 'uni'
+ chan: Input.cold chan or Constant.num -1
- internal: Input.hot T.num\mk_sig 0
+ internal: Input.hot T.bang\mk_evt!
- @out or= T.num\mk_sig!
+ @state or= 0
+ @out or= T.num\mk_sig apply_range @inputs.range, @state
poll: =>
{ :port, :cc, :chan, :internal } = @inputs
@@ -114,7 +116,8 @@ cc = Constant.meta
msg = msgs[i]
if msg.a == cc! and (chan! == -1 or msg.chan == chan!)
if msg.status == 'control-change'
- internal.result\set msg.b
+ @state = msg.b
+ internal.result\set true
return true
false
@@ -122,16 +125,72 @@ cc = Constant.meta
tick: =>
{ :range, :internal } = @inputs
- value = internal!
- @state = value / 128
- @out\set apply_range range, value
+ if internal!
+ @out\set apply_range range, @state
vis: =>
{
type: 'bar'
- bar: @state
+ bar: @state / 128
}
+send_notes = Constant.meta
+ meta:
+ name: 'send-notes'
+ summary: "`send MIDI note events."
+ examples: { '(midi/send-notes [port] [chan] note-events)' }
+ description: "
+`chan` can be
+`note-events` is a !-stream of structs with the following keys:
+
+- `pitch`: MIDI pitch (num)
+- `dur`: note duration in seconds (num)
+- `vel`: MIDI velocity (num, optional)"
+
+ value: class extends Op
+ thin = Struct pitch: T.num, dur: T.num
+ thiq = Struct pitch: T.num, dur: T.num, vel: T.num
+ pattern = -sig['midi/out'] + -sig.num + (evt(thin) / evt(thiq))
+ setup: (inputs, scope) =>
+ { port, chan, notes } = pattern\match inputs
+ @state = {}
+ super
+ port: Input.cold port or scope\get '*midi*'
+ notes: Input.hot notes
+ chan: Input.cold chan or Constant.num 0
+
+ note_off: Input.hot T.num\mk_evt!
+
+ poll: =>
+ time = monotime!
+
+ for pitch, endt in pairs @state
+ if endt <= time
+ @inputs.note_off.result\set pitch
+ return true
+
+ false
+
+ tick: =>
+ { :port, :chan, :notes, :note_off } = @unwrap_all!
+
+ if notes
+ { :pitch, :dur, :vel } = notes
+ pitch = math.floor pitch
+ vel = if vel then math.floor vel else 127
+ @state[pitch] = monotime! + dur
+ port\send 'note-on', chan, pitch, vel
+
+ if pitch = note_off
+ @state[pitch] = nil
+ port\send 'note-off', chan, pitch, 0
+
+ destroy: =>
+ { :port, :chan } = @unwrap_all!
+
+ for pitch, endt in pairs @state
+ port\send 'note-off', chan, pitch, 0
+
Constant.meta
meta:
name: 'midi'
@@ -141,6 +200,8 @@ Constant.meta
:input
:output
:port
+
:gate
:trig
:cc
+ 'send-notes': send_notes
diff --git a/alv-lib/midi/core.moon b/alv-lib/midi/core.moon
index 03cfcb2..c456a01 100644
--- a/alv-lib/midi/core.moon
+++ b/alv-lib/midi/core.moon
@@ -134,7 +134,7 @@ apply_range = (range, val) ->
when 'deg' then val / 128 * 360
else
error Error 'argument', "unknown range '#{range!}'"
- elseif range.type == T.num
+ elseif range\type! == T.num
val / 128 * range!
else
error Error 'argument', "range has to be a string or number"
diff --git a/alv-lib/rhythm.moon b/alv-lib/rhythm.moon
index 57be9a7..582483f 100644
--- a/alv-lib/rhythm.moon
+++ b/alv-lib/rhythm.moon
@@ -24,7 +24,7 @@ bjorklund2 = (n, k) ->
b, bs = '0', n - k
while true
- if bs == 1 or bs == 0
+ if as < 2 or bs < 2
break
elseif as < bs
a, b = a .. b, b
@@ -64,6 +64,8 @@ When fed a num~ or num! stream, outputs a bang if the corresponding step is on."
tick: =>
{ :trig, :n, :k } = @unwrap_all!
+ n = math.floor n
+ k = math.floor k
if @inputs.trig\type! == T.bang
@state += 1
diff --git a/alv-lib/time.moon b/alv-lib/time.moon
index f091792..8b3f47d 100644
--- a/alv-lib/time.moon
+++ b/alv-lib/time.moon
@@ -149,6 +149,7 @@ ramp = Constant.meta
tick: =>
{ :clock, :period, :max } = @unwrap_all!
max or= period
+ return if period == 0
@state += clock.dt / period
while @state >= 1
@@ -187,7 +188,9 @@ tick = Constant.meta
period: Input.cold period
tick: =>
- @state.phase += @inputs.clock!.dt / @inputs.period!
+ { :clock, :period } = @unwrap_all!
+ return if period == 0
+ @state.phase += clock.dt / period
if @state.phase >= 1
@state.phase -= 1
@@ -223,11 +226,13 @@ every = Constant.meta
@out = @inputs.evt\type!\mk_evt!
tick: =>
- @state += @inputs.clock!.dt / @inputs.period!
+ { :clock, :period, :evt } = @unwrap_all!
+ return if period == 0
+ @state += clock.dt / period
if @state >= 1
- @state -= 1
- @out\set @inputs.evt!
+ @state = @state % 1
+ @out\set evt
val_seq = Constant.meta
meta: