Skip to content

Instantly share code, notes, and snippets.

@martinmestres
Last active August 7, 2018 14:17
Show Gist options
  • Save martinmestres/9db4270028a6d5e1d0b1b42a23c2537e to your computer and use it in GitHub Desktop.
Save martinmestres/9db4270028a6d5e1d0b1b42a23c2537e to your computer and use it in GitHub Desktop.
-- Markov's Playground
-- --------------------------------
-- Page 1:
-- Key 1 change PAGE
-- Key 2 randomize SCALE
-- Key 3 randomize PROBABILITIES
--
-- Page 2:
-- Key 1 change PAGE
-- Key 2 PLAY recorded LOOP
-- Key 3 record LOOP
-- Enc 1 LOOP LENGTH
-- Enc 2 NOTE ON probability
engine.name = "PolyPerc"
local loopLength = 8 -- Loops Length {8,8} ???
local Scale = {0,2,4,5,7,9,11}
local Prob = {{},{},{},{},{},{},{}}
local Loop1 = {{".",".",".",".",".",".",".",".",".",".",".",".",".",".",".","."},
{".",".",".",".",".",".",".",".",".",".",".",".",".",".",".","."}}
local loopPos = 0
local writeLoop = 0
local playLoop = 0
local newNote = 1
local noteSel = 1
local loopSel = 1
local loopRecSel = 1
local pageNumber = 1
local noteOnProb = 90
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 rollResult
local loopStateMemory = 0
local altControls = 0
params:add_number("bpm",40,200,100)
params:add_number("root note",30,90,60)
params:add_number("rest counter limit",1,16,2)
function init()
math.randomseed( os.time() )
engine.cutoff(4000)
params:add_control("cutoff",controlspec.new(60,10000,'exp',2,4000,'hz'))
params:set_action("cutoff", function(x) engine.cutoff(x) end)
engine.amp(1)
engine.pw(0.5)
rndProb()
counter = metro.alloc(count, 0.15, -1)
counter:start()
end
-- Counter
function count()
blinker()
counter.time = (60 / (params:get("bpm"))) / 4 --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
restCounter = 0 --Rester Counter
else actRestCounter()
end
looper() --If
else
rollResult = noteOnRoll()
looper()
if tonumber(Loop1[loopSel][loopPos]) ~= nil then -- Check LOOPER ON state
engine.hz(midi_to_hz(params:get("root note") + Loop1[loopSel][loopPos]))
if loopSel == loopRecSel then
for i=1, 7 do -- LOOP path update
if Loop1[loopSel][loopPos] == Scale[i] then
newNote = i
break
end
end
else noteSel = probRoll() -- Keep on markoving if LOOP ~ REC
end
end
end
redraw()
end
-- LOOPER
function looper()
loopPos = util.clamp(((loopPos + 1) % (loopLength+1)), 1, loopLength)
if writeLoop == 1 then
if rollResult == 0 then
Loop1[loopRecSel][loopPos] = "."
else
Loop1[loopRecSel][loopPos] = noteSel
end
end
end
-- Random Scale Generator
function rndScale()
for i=2, 7 do
Scale[i] = Scale[i-1] + math.random(1,3) -- Creates Scale{}
end
end
-- Random Prob Gen per note --REVOIR SYSTEME D'ALEATOIRE
function rndProb() --FAIRE DE LA FONCTION LE MELANGE DUNE TABLE
for i=1, 7 do --METTRE CA EN CAS DE LONG PRESS DANS KEY3
remain = 100
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 shuffle
for j=1, 7 do
Prob[i][deck[j]] = util.clamp((math.random(0, remain)), 0, 90) -- Mixup tables + prevent 100%
remain = (remain - Prob[i][deck[j]])
end
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
local Test = math.random(100)
if Test >= Prob[newNote][currentNote] then
currentNote = currentNote+1
if currentNote > 7 then currentNote = 1 end
x = false
else x = true end
until x == true
newNote = currentNote
if restCounter >= params:get("rest counter limit") then --check Restcounter and reset
newNote = anchor end --Anchor HERE
return Scale[newNote]
end
-- shuffle table content
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
-- Rest Counter
function actRestCounter()
restCounter = restCounter + 1
end
--Blinker
function blinker()
blink = (blink + 7) % 14
end
-- FPS
function fps()
end
-- BUTTONS
function key(n, z)
-- KEY1 activate alternative controls
if n == 1 and z == 1 then altControls = 1
elseif n == 1 and z == 0 then altControls = 0
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 Key 3 chang Probs
if n == 3 and z == 1 then
rndProb()
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
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,2)
end
-------------ENCPAGE1----------------------------------------------------
if pageNumber == 1 then
if altControls == 1 then -- ALT CONTROLS
-- Anchor SELECTOR
if n == 3 then
anchor = util.clamp((anchor + d),1,7)
redraw()
end
-- Note selected note MANUAL change
if n == 2 then
Scale[anchor] = util.clamp(Scale[anchor] + d, 0, 24)
end
end
if altControls == 0 then -- BASIC CONTROLS
-- 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 = util.clamp((loopLength + d), 1, 16)
redraw()
end
--Table SHIFT
if n == 2 then
--RIGHT
if d-1 == 0 then
local memory = Loop1[loopSel][loopLength]
table.remove(Loop1[loopSel], loopLength)
table.insert(Loop1[loopSel],1,memory)
--LEFT
else
local memory = Loop1[loopSel][1]
table.remove(Loop1[loopSel], 1)
table.insert(Loop1[loopSel],loopLength,memory)
end
end
end
end
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((15*i)+1, 6)
if i == anchor then --Anchor blink Feedback
screen.level(blink)
else screen.level(10)
end
screen.font_face(1)
screen.text_center(Scale[i])
-- Draws probability for each note
for j=1, 7 do
screen.move((15*i)+1, 10 + (j*7))
screen.level(1)
screen.text_center(Prob[i][j])
end
end
-- Line
screen.move(11, 9)
screen.line(112, 9)
screen.stroke()
-- Curent note Feedback
screen.rect((newNote*15-1)+1,8,4,1)
screen.level(5)
screen.stroke()
-- NoteOne Probability
screen.move(120, 64)
screen.level(5)
screen.text_center(noteOnProb .. "%")
--------------------------------------------------------PAGE2----------------------------------------------------
else
-- 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 do
screen.move(((127/loopLength)*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 -- Marche mais entraine ligne du dessous
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 then screen.level(15)
else screen.level(4)
end
screen.font_face(1)
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(blink)
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
screen.move(80, 64)
if writeLoop == 1 then screen.level(blink)
else screen.level(1)
end
screen.text("REC".. loopRecSel)
end
-----------------------------------------------------ALL-----------------------------------------
--Curent page
screen.move(0, 64)
screen.level(4)
screen.text("P" .. pageNumber)
screen.update()
end
@martinmestres
Copy link
Author

Thank you, your advices will certainly help me a lot. I will try to apply them one by one without breaking everything.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment