Last active
October 26, 2022 19:31
-
-
Save ssj71/0e839f99e5163804ad005773827fc71b to your computer and use it in GitHub Desktop.
moony.lv2 random note drop
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- MIDI Note Event random dropper | |
--- | |
-- This is an example that randomly doesn't let notes through. | |
-- affected MIDI channels | |
-- 0..15 or set to -1 to apply to events on all channels | |
local filter_chan = -1 | |
-- affected note range(s) | |
-- string with comma-separated list of single MIDI note numbers or | |
-- ranges (min-max separated by a dash, whitespace is ignored) | |
-- example: local note_ranges = "0-12, 36,48, 60 - 96" | |
local note_ranges = "0-127" | |
-- whether to pass non-note events or note events outside of affected range(s) | |
local pass_other = true | |
-- the probability (percent) that the note is dropped | |
local drop_prob = 20 | |
-- NO NEED TO CHANGE ANYTHING BELOW | |
-- http://rosettacode.org/wiki/Range_expansion#Lua | |
function range(i, j) | |
local t = {} | |
for n = i, j, i<j and 1 or -1 do | |
t[#t+1] = n | |
end | |
return t | |
end | |
function expand_ranges(rspec) | |
local ptn = "([-+]?%d+)%s?-%s?([-+]?%d+)" | |
local t = {} | |
for v in string.gmatch(rspec, '[^,]+') do | |
local s, e = v:match(ptn) | |
if s == nil then | |
t[tonumber(v)] = true | |
else | |
for _, n in ipairs(range(tonumber(s), tonumber(e))) do | |
t[n] = true | |
end | |
end | |
end | |
return t | |
end | |
local c --random number storage | |
local function send_note() | |
if random.value(c,100) > drop_prob then | |
return true | |
else | |
return false | |
end | |
end | |
-- note responder function factory | |
local function note_responder(cmd) | |
return function(self, frames, forge, chan, note, vel) | |
local pass = pass_other | |
if (filter_chan == -1 or chan == filter_chan) and filter_notes[note] ~= nil then | |
if cmd == MIDI.NoteOn then | |
pass = send_note(vel) | |
else | |
pass = true | |
end | |
end | |
if pass then | |
-- send event | |
forge:time(frames):midi(cmd | chan, note, vel) | |
end | |
end | |
end | |
function once(n, control, notify, seq, forge) | |
filter_notes = expand_ranges(note_ranges) | |
c = random.new() | |
-- define a MIDIResponder object to handle note-on and note-off events | |
midiR = MIDIResponder({ | |
[MIDI.NoteOn] = note_responder(MIDI.NoteOn), | |
[MIDI.NoteOff] = note_responder(MIDI.NoteOff) | |
}, pass_other) | |
end | |
function run(n, control, notify, seq, forge) | |
-- iterate over incoming events | |
for frames, atom in seq:foreach() do | |
-- call responder for event | |
local handled = midiR(frames, forge, atom) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment