Last active
October 5, 2018 03:15
-
-
Save ypxk/4292b70f7ce7346b732b89e28431bfff to your computer and use it in GitHub Desktop.
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
-- EXPENSIVE ARPEGGIATOR | |
-- based on norns study 4 | |
-- grid controls arpeggio | |
-- midi selects notes | |
-- button 2 = start/stop | |
-- button 3 = random arp | |
-- encoder 1 = bpm | |
-- encoder 2 = skim arp/note length | |
-- encoder 3 = probability of note on (thanks @burn) | |
-- TODO: | |
-- find missing nil value on start | |
-- change vars to local | |
-- better solution for octaves | |
-- visual feedback for encoder 3 | |
-- notes of different length | |
-- support for other engines | |
-- velocity? don't have anything that sends vel | |
-- mlr style range loop | |
-- record chord changes and iterate through | |
engine.name = 'Passersby' | |
music = require 'mark_eats/musicutil' | |
beatclock = require 'beatclock' | |
passersby = require "mark_eats/passersby" | |
steps = {} | |
position = 1 | |
going = true | |
skimming = false | |
prob_data = {} | |
reveal_level = 90 | |
skim = 0 | |
chordsList = {31,43,51,55,58,62,63,67} | |
scale = chordsList | |
timeLast = 0 | |
capturedNotes = music.note_nums_to_names(scale) | |
clk = beatclock.new() | |
clk_midi = midi.connect() | |
clk_midi.event = clk.process_midi | |
function init() | |
for i=1,16 do | |
table.insert(steps,math.random(8)) | |
end | |
grid_redraw() | |
clk.on_step = count | |
clk.on_select_internal = function() clk:start() end | |
clk.on_select_external = function() print("external") end | |
clk:add_clock_params() | |
params:add_separator() | |
passersby.add_params() | |
create_sequence_data() | |
clk:start() | |
end | |
function create_sequence_data() --encoder 3 controlls likelyhood of note trigger | |
for i=1, 16 do | |
prob_data[i] = math.random (1, 100); | |
end | |
end | |
function enc(n,d) | |
if n == 1 then --bpm | |
params:delta("bpm",d) | |
elseif n == 2 then --skimming through arp | |
skimming = true | |
params:delta("LPG Decay", d) --clockwise = legado notes, counterclockwise = shorter notes | |
skim = (skim + d) % 16 | |
if skim ~= 0 then | |
engine.noteOn(1,music.note_num_to_freq(scale[steps[(skim % 16)]]+12),1) | |
grid_redraw() | |
end | |
skimTime = util.time() -- for grid redraw | |
elseif n == 3 then -- controlls prob of note hit | |
reveal_level = reveal_level + d | |
if reveal_level > 100 then reveal_level = 100 end | |
if reveal_level < 0 then reveal_level = 0 end | |
end | |
redraw() | |
end | |
function key(n,z) | |
if n == 2 then -- start/stop arp | |
if z == 1 then | |
if going == false then | |
clk:start() | |
going = true | |
elseif going == true then | |
clk:stop() | |
going = false | |
end | |
end | |
elseif n == 3 then -- new sarp | |
if z == 1 then | |
steps = {} | |
for i=1,16 do | |
table.insert(steps,math.random(8)) end | |
create_sequence_data() | |
grid_redraw() | |
end | |
end | |
redraw() | |
end | |
function redraw() | |
screen.clear() | |
screen.level(15) | |
screen.move(0,30) | |
screen.text("bpm: "..params:get("bpm")) | |
screen.move(0,40) | |
screen.text(table.concat(capturedNotes, ' ')) | |
screen.update() | |
end | |
g = grid.connect() | |
g.event = function(x,y,z) -- manual set pitch, toggle notes | |
if z == 1 then | |
if steps[x] == y then | |
steps[x] = 0 | |
else | |
steps[x] = y | |
end | |
grid_redraw() | |
end | |
end | |
function grid_redraw() | |
g.all(0) | |
if skimming == false then --skimming mode overrides display | |
for i=1,16 do | |
g.led(i,steps[i],i==position and 15 or 4) | |
end | |
else | |
for i=1,16 do | |
g.led(i,steps[i],i==skim and 15 or 4) | |
end | |
if util.time() - skimTime < .5 then | |
skimming = false | |
end | |
end | |
g.refresh() | |
end | |
function count() | |
position = (position % 16) + 1 --determines if note plays | |
if prob_data[position] <= reveal_level then | |
engine.noteOn(1,music.note_num_to_freq(scale[steps[position]]),1) | |
end | |
grid_redraw() | |
end | |
function noteBuffer(aNote) | |
for k, v in pairs (chordsList) do --this is supposed to block duplicates but it doesn't work correctly | |
if aNote == v then | |
return | |
end | |
end | |
if util.time() - timeLast < .6 then --window you have to input chords before reset | |
table.insert(chordsList, aNote) --script needs 8 notes for grid, adds octaves | |
if #chordsList < 8 then table.insert(chordsList, (aNote + 12)) end | |
if #chordsList < 8 then table.insert(chordsList, (aNote + 24)) end | |
else --starts new chords | |
print('Initializing buffer') | |
for k, v in pairs(chordsList) do chordsList[k]=nil end | |
table.insert(chordsList, aNote) | |
if #chordsList < 8 then table.insert(chordsList, (aNote + 12)) end --sometimes you need more octaves | |
while #chordsList < 8 do | |
for i=1,8 do | |
x = aNote + (i*12) | |
if x > 90 then if (x - 84) > 0 then x = x - 84 end end --adjusts range of octaves | |
table.insert(chordsList, x) | |
end | |
end | |
end | |
table.sort(chordsList) --sorts high to low | |
--duplicate detection that actually works but i don't understand why | |
local notehash = {} | |
local prunednotes = {} | |
for i,v in pairs(chordsList) do | |
if (not notehash[v]) then | |
prunednotes[#prunednotes+1] = v | |
notehash[v] = true | |
end | |
end | |
chordsList = prunednotes | |
capturedNotes = music.note_nums_to_names(chordsList) --for display | |
print(table.concat(capturedNotes, ' ')) | |
for k, v in pairs(chordsList) do | |
print(k, v) | |
end | |
scale = chordsList | |
redraw() | |
timeLast = util.time() | |
end | |
m = midi.connect() | |
m.event = function(data) | |
local d = midi.to_msg(data) | |
if d.type == "note_on" then | |
noteBuffer(d.note) | |
end | |
end | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment