Skip to content

Instantly share code, notes, and snippets.

@martinmestres
Created October 13, 2018 15:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save martinmestres/18578ffa675100e4ff93ca33d2688b90 to your computer and use it in GitHub Desktop.
Save martinmestres/18578ffa675100e4ff93ca33d2688b90 to your computer and use it in GitHub Desktop.
-- ANDR
-- Markov chain sequencer
-- -----------------------------
-- GLOBAL:
-- Enc 1: Change Page
-- Key 1 Alternative Controls
--
-- PAGE 1:
-- Key 2: Shift selection's
-- probabilities
-- Key 3: Randomise selected
-- probability
-- Enc 2: ANCHOR position
-- Enc 3: note generation
-- probability
-- Alt+Enc 2: Change note
-- Alt+Enc 3: Change "reset to
-- anchor" counter
-- Alt+Key 2: randomize scale
-- Alt+Key 3: randomize all
-- probabilities
-- 3sec press reset ALL
--
-- PAGE 2:
-- Key 2: PLAY selected LOOP
-- Key 3: Record LOOP *
-- Enc 2: LOOP Shift
-- Enc 3: Select LOOP to PLAY
-- Alt+Enc 1: LOOP length
-- Alt+Key 2: Select REC LOOP 1
-- Alt+Key 3: Select REC LOOP 2
engine.name = "Passersby"
local Passersby = require "mark_eats/passersby"
local MusicUtil = require "mark_eats/musicutil"
local loopLength = {8,8}
local Scale = {0,2,4,5,7,9,11}
local Prob = {{},{},{},{},{},{},{}}
local Loop1 = {{".",".",".",".",".",".",".",".",".",".",".",".",".",".",".","."},
{".",".",".",".",".",".",".",".",".",".",".",".",".",".",".","."}}
local loopPos = {0,0}
local writeLoop = 0
local playLoop = 0
local newNote = 1
local noteSel = 1
local loopSel = 1
local loopRecSel = 1
local pageNumber = 1
local noteOnProb = 100
local alternate = {{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}
local restCounter = 0
local anchor = 1
local blink = 1
local anchorBlink = 1
local rollResult
local loopStateMemory = 0
local altControls = 0
local setRestCounter = 2
local clockState = 1
local tick = 0
local ON = 1
function init()
params:add_number("bpm","bpm",40,200,127)
params:add_number("root_note","root note",30,90,60)
params:add_option("lock_lengths","lock lengths", {"ON","OFF"})
params:add_option("CLOCK","clock", {"INT","EXT"})
params:add_separator()
Passersby.add_params()
math.randomseed( os.time() )
resetProb()
counter = metro.alloc(count, 0.15, -1)
counter:start()
refreshScreen = metro.alloc(refresh, 0.11, -1)
refreshScreen:start()
end
clk_midi = midi.connect()
clk_midi.event = function(data)
if params:get("CLOCK") == 2 then
counter:stop()
clockState = 2
if data[1] == 252 then
ON = 0
tick = 0
elseif data[1] == 250 then ON = 1
end
if data[1] == 248 and ON == 1 then
tick = tick + 1
if tick == 7 then tick = 1 end
if tick == 1 then count() end
end
elseif params:get("CLOCK") == 1 and clockState == 2 then
counter:start()
clockState = 1
end
end
function refresh()
blinker()
anchorBlink = ((anchorBlink + 1)%4)
redraw()
end
-- Counter
function count() --Change Tempo
if playLoop == 0 then --Check LOOPER OFF state
rollResult = noteOnRoll()
if rollResult == 1 then
noteSel = probRoll()
--engine.hz(midi_to_hz(params:get("root note") + noteSel)) --Play note
engine.noteOn(params:get("root_note") + noteSel, MusicUtil.note_num_to_freq(params:get("root_note") + noteSel), 1)
restCounter = 0 --Rester Counter
else actRestCounter()
end
looper()
else --Check LOOPER ON state
rollResult = noteOnRoll()
looper()
if tonumber(Loop1[loopSel][loopPos[loopSel]]) ~= nil then -- Check LOOPER ON state
--engine.hz(midi_to_hz(params:get("root note") + Loop1[loopSel][loopPos[loopSel]]))
engine.noteOn(params:get("root_note") + Loop1[loopSel][loopPos[loopSel]], MusicUtil.note_num_to_freq(params:get("root_note") + Loop1[loopSel][loopPos[loopSel]]), 1)
if loopSel == loopRecSel then
for i=1, 7 do
if Loop1[loopSel][loopPos[loopSel]] == Scale[i] then
newNote = i
break
end
end
else
rollResult = noteOnRoll()
if rollResult == 1 then
noteSel = probRoll()
restCounter = 0
else actRestCounter()
end
end
end
end
redraw()
end
-- LOOPER
function looper()
for i= 1, 2 do
loopPos[i] = util.clamp(((loopPos[i] + 1) % (loopLength[i]+1)), 1, loopLength[i])
if params:get("lock_lengths") == 1 then -- Lock Loops Position in locked lengths MODE
loopPos[2] = loopPos[1]
end
end
if writeLoop == 1 then
if rollResult == 0 then
Loop1[loopRecSel][loopPos[loopRecSel]] = "."
else
Loop1[loopRecSel][loopPos[loopRecSel]] = noteSel
end
end
end
-- Random Scale Generator
function rndScale()
Scale[1] = 0
for i=2, 7 do
Scale[i] = Scale[i-1] + math.random(1,3) -- Creates Scale{}
end
end
-- Random Prob Gen per note
function rndProb(x)
local remain = 100
local deck = shuffleTable{1,2,3,4,5,6,7} -- random distribution order
for i=1, math.random(0, 10) do deck = shuffleTable(deck) end -- even more deck shuffling
for j=1, 7 do
Prob[x][deck[j]] = util.clamp((math.random(0, remain)), 0, 90) -- Mixup tables + prevent 100%
remain = (remain - Prob[x][deck[j]])
end
end
-- Note On roll
function noteOnRoll()
local Test = math.random(100)
if Test >= noteOnProb then return 0
else return 1
end
end
-- Midi to Hz
function midi_to_hz(note)
local hz = (440 / 32) * (2 ^ ((note - 9) / 12))
return hz
end
-- Prob roll
function probRoll()
currentNote = 1
repeat
x = true
if math.random(100) >= Prob[newNote][currentNote] then
currentNote = currentNote+1
if currentNote > 7 then
currentNote = 1
end
x = false
end
until x == true
newNote = currentNote
if restCounter >= setRestCounter then --check Restcounter and reset
newNote = anchor
end --Anchor HERE
return Scale[newNote]
end
-- Table SHUFFLER
function shuffleTable(t)
for i = #t, 2, -1 do
local swap = math.random(1, i)
t[i], t[swap] = t[swap], t[i]
return t
end
end
-- Table SHIFTER
function tableShift(x)
local memory = x[#x]
print(#x)
table.remove(x)
table.insert(x,1,memory)
end
-- Rest Counter
function actRestCounter()
restCounter = restCounter + 1
end
--Blinker
function blinker()
local blinkBrightness = {0, 2, 8, 13}
blink = ((blink + 1) % 4)+1
return blinkBrightness[blink]
end
--Blinker (Anchor)
function blinkerAnchor()
local blinkBrightness = {1,4,8,15}
return blinkBrightness[anchorBlink+1]
end
-- Probs Reset
function resetProb()
for i=1, 7 do
Prob[i] = {100,0,0,0,0,0,0}
end
end
-- KEYS
function key(n, z)
-- KEY1 activate alternative controls
if writeLoop == 0 then --Prevent write/Alt conflicts
if n == 1 then altControls = z end
end
-------------KEYPAGE1----------------------------------------------------
if pageNumber == 1 then
if altControls == 1 then -------------- ALT CONTROLS
-- PAGE 1 Key2 change Scale
if n == 2 and z == 1 then
rndScale()
end
-- PAGE 1 Alt Key3 change ALL Probs, LONG PRESS RESET ALL
if n == 3 and z == 1 then
for i=1, 7 do
rndProb(i)
end
timeCheck = os.clock()
end
if n == 3 and z == 0 then
if (os.clock()-timeCheck)*10 > 3 then
noteOnProb = 100
resetProb()
end
end
else ---------------------------------- BASE CONTROLS
-- PAGE 1 Key 3 chang Anchor probs
if n == 3 and z == 1 then
rndProb(anchor)
end
if n == 2 and z == 1 then
tableShift(Prob[anchor])
end
end
-------------KEYPAGE2----------------------------------------------------
else
if altControls == 1 then -- ALT CONTROLS
-- Loop REC Selector
if n == 2 and z == 1 then loopRecSel = 1
elseif n == 3 and z == 1 then loopRecSel = 2
end
else -- BASE CONTROLS
if n == 2 and z == 1 then -- LOOP SWITCH ON/OF
playLoop = (playLoop + 1) % 2
loopStateMemory = 1
writeLoop = 0
if playLoop == 0 then loopStateMemory = 0 end
end
if n == 3 and z == 1 then
if loopSel == loopRecSel then playLoop = 0 end
writeLoop = 1
else
writeLoop = 0
if loopStateMemory == 1 then playLoop = 1 end
end
end
end
-------------END----------------------------------------------------
redraw()
end
--ENCODERS
function enc(n, d)
--Page change
if altControls == 0 and n == 1 then
pageNumber = util.clamp((pageNumber + d),1,3)
end
-------------ENCPAGE1----------------------------------------------------
if pageNumber == 1 then
if altControls == 1 then -- ALT CONTROLS
-- Note selected note MANUAL change
if n == 2 then
Scale[anchor] = util.clamp(Scale[anchor] + d, 0, 24)
end
if n == 3 then
setRestCounter = util.clamp((setRestCounter + d),1, 8)
redraw()
end
end
if altControls == 0 then -- BASIC CONTROLS
-- Anchor SELECTOR
if n == 2 then
anchor = util.clamp((anchor + d),1,7)
redraw()
end
-- Note On Prob
if n == 3 then
noteOnProb = util.clamp((noteOnProb + d), 1, 100)
redraw()
end
end
end
-------------ENCPAGE2----------------------------------------------------
if pageNumber == 2 then
-- Loop Selection
if n == 3 then
loopSel = util.clamp((loopSel + d), 1, 2)
end
-- Loop Length
if n == 1 and altControls == 1 then
loopLength[loopSel] = util.clamp((loopLength[loopSel] + d), 1, 16)
if params:get("lock_lengths") == 1 then -- LOCK LENTGH PARAM
if loopSel == 1 then
loopLength[2] = loopLength[1]
elseif loopSel == 2 then
loopLength[1] = loopLength[2]
end
end
redraw()
end
--Table SHIFT
if n == 2 then
--RIGHT
if d-1 == 0 then
local memory = Loop1[loopSel][loopLength[loopSel]]
table.remove(Loop1[loopSel], loopLength[loopSel])
table.insert(Loop1[loopSel],1,memory)
--LEFT
else
local memory = Loop1[loopSel][1]
table.remove(Loop1[loopSel], 1)
table.insert(Loop1[loopSel],loopLength[loopSel],memory)
end
end
end
-------------ENCPAGE3----------------------------------------------------
if pageNumber == 3 then
end
end
--VISUAL FEEDBACK
function redraw()
screen.clear()
--------------------------------------------------------PAGE1----------------------------------------------------
if pageNumber == 1 then
screen.aa(0)
screen.font_size(8)
-- Draws Notes
for i=1, 7 do
screen.move((12*i)+4, 6)
if i == anchor then --Anchor blink Feedback
screen.level(blinkerAnchor())
else screen.level(15)
end
screen.font_face(1)
screen.text_center(Scale[i])
-- Draws probability for each note
for j=1, 7 do
screen.move((12*i)+4, 10 + (j*7))
screen.level(1)
screen.text_center(Prob[i][j])
end
end
-- Line
screen.move(12, 9)
screen.line(93, 9)
screen.level(3)
screen.stroke()
screen.move(105, 0)
screen.line(105, 64)
screen.level(15)
screen.stroke()
-- Curent note Feedback
screen.rect((newNote*12-1)+4,8,4,1)
screen.level(5)
screen.stroke()
-- NoteOne Probability
screen.move(118, 55)
screen.level(15)
screen.text_center(noteOnProb .. "%")
-- RestResetSet
screen.move(118, 64)
screen.level(2)
if setRestCounter > 1 then
screen.text_center(setRestCounter .. "rests")
else screen.text_center(setRestCounter .. "rest")
end
--------------------------------------------------------PAGE2----------------------------------------------------
else
screen.clear()
-- Line
screen.level(2 - loopSel) -- Loop 1 selection FeedBack
screen.rect(0, 30, 128, 4)
screen.fill()
screen.level(-1 + loopSel) -- Loop 2 selection FeedBack
screen.rect(0, 45, 128, 4)
screen.fill()
-- Looper Memory feedback
for ii=1, 2 do
for i=1, loopLength[ii] do
screen.move(((127/loopLength[ii])*i)-4, ((15+(ii*15)) - alternate[ii][i]))
-- Jumping numbers feedback
if loopSel == ii then
if playLoop == 1 then
if tonumber(Loop1[ii][i]) ~= nil then
alternate[ii][i] = (alternate[ii][i] + 1) % math.random(2, 4)
end
else alternate[ii] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
end
else alternate[ii] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
end
-- Looper current position feedback
if i == loopPos[ii] then
screen.level(15)
else
screen.level(4)
screen.font_size(8)
screen.font_face(1)
end
screen.text_center(Loop1[ii][i])
end
end
-- Looper On/oFF Feedback rect
screen.move(11, 9)
if playLoop == 1 then --Loop On Blink
screen.rect(115, 58, 6, 6)
screen.level(blinker())
screen.fill()
elseif loopStateMemory == 1 then --Loop On, Write On Steady State
screen.rect(115, 58, 6, 6)
screen.level(15)
screen.fill()
end
if playLoop == 0 and loopStateMemory == 0 then --Loop Off, Write Off
screen.rect(116, 59, 5, 5)
screen.level(1)
screen.stroke()
end
-- Write On/oFF Feedback
if loopRecSel == 1 then
screen.move(80, 64)
else
screen.move(95, 64)
end
if writeLoop == 1 then screen.level(blinker())
else screen.level(1)
end
screen.font_size(8)
screen.font_face(1)
screen.text("REC".. loopRecSel)
end
--------------------------------------------------------PAGE3----------------------------------------------------
if pageNumber == 3 then
screen.clear()
end
-----------------------------------------------------ALL-----------------------------------------
--Curent page
screen.move(112, 1)
if pageNumber == 1 then screen.level(15) else screen.level(2) end
screen.line(116, 1)
screen.stroke()
screen.move(117, 1)
if pageNumber == 2 then screen.level(15) else screen.level(2) end
screen.line(121, 1)
screen.stroke()
screen.move(122, 1)
if pageNumber == 3 then screen.level(15) else screen.level(2) end
screen.line(126, 1)
screen.stroke()
-- screen.font_size(8)
--screen.text_center("P" .. pageNumber)
if altControls == 1 then --ALT Feedback
screen.move(0, 64)
screen.level(blinker())
screen.font_size(7)
screen.text("ALT")
end
screen.update()
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment