diff --git a/drift.lua b/drift.lua index 646f12d..887d634 100644 --- a/drift.lua +++ b/drift.lua @@ -1,6 +1,6 @@ -- drift - dust motes in flight -- --- v0.4 @echophon +-- v0.5 @echophon -- -- KEY1 shift -- KEY2 cycles param focus @@ -14,6 +14,23 @@ engine.name = 'PolyPerc' +MusicUtil = require "musicutil" + +local scale_names = {} +notes = {} +num_to_add = 0 + +function build_scale() + notes = {} + notes = MusicUtil.generate_scale(params:get("root_note"), params:get("scale_mode"), params:get("octaves")) + -- notes = MusicUtil.generate_scale_of_length(params:get("root_note"), params:get("scale_mode"), length) + -- num_to_add = 128 - #notes + num_to_add = #notes + for i = 1, num_to_add do + table.insert(notes, notes[i]) + end +end + local viewport = { width = 128, height = 64 } local frame = 0 @@ -46,32 +63,57 @@ local useMidi = 0 local channel = 1 local m = midi.connect() -params:add_number("useMidi","useMidi",0,1,0) -params:set_action("useMidi", function(x) useMidi = x end) -params:add_number("channel","channel",1,16,1) -params:set_action("channel", function(x) channel = x end) -params:add_number("drift_max","drift_max",1,40,12) -params:set_action("drift_max", function(x) drift_max = x end) +function init() + for i = 1, #MusicUtil.SCALES do + table.insert(scale_names, string.lower(MusicUtil.SCALES[i].name)) + end -params:add_number("connect_distance","connect_distance",4,48,24) -params:set_action("connect_distance", function(x) connect_distance = x end) + params:add_number("useMidi","useMidi",0,1,0) + params:set_action("useMidi", function(x) useMidi = x end) -params:add_option("boundary_ops","boundary_ops",{"wrap", "wrap random", "bounce", "bounce random"},1) -params:set_action("boundary_ops", function(x) boundary_ops = x end) + params:add_number("channel","channel",1,16,1) + params:set_action("channel", function(x) channel = x end) -params:add_option("random_ops","random_ops",{"default", "xpos", "ypos", "xypos", "xmove", "ymove", "xymove", "xboundary", "yboundary", "xyboundary"},1) -params:set_action("random_ops", function(x) random_ops = x end) + params:add_separator() -params:add_trigger("randomize", "randomize") -params:set_action("randomize", function(x) randomize_dots() end) + params:add{type = "number", id = "step_div", name = "step division", min = 1, max = 16, default = 4} -params:add_trigger("reset", "reset") -params:set_action("reset", function(x) reset() end) + params:add{type = "number", id = "octaves", name = "octaves", min = 1, max = 10, default = 5} -function init() + params:add{type = "option", id = "scale_mode", name = "scale mode", + options = scale_names, default = 5, + action = function() build_scale() end} + + params:add{type = "number", id = "root_note", name = "root note", + min = 0, max = 127, default = 24, formatter = function(param) return MusicUtil.note_num_to_name(param:get(), true) end, + action = function() build_scale() end} + + params:add_separator() + + params:add_number("drift_max","drift_max",1,40,12) + params:set_action("drift_max", function(x) drift_max = x end) + + params:add_number("connect_distance","connect_distance",4,48,24) + params:set_action("connect_distance", function(x) connect_distance = x end) + + params:add_option("boundary_ops","boundary_ops",{"wrap", "wrap random", "bounce", "bounce random"},1) + params:set_action("boundary_ops", function(x) boundary_ops = x end) + + params:add_option("random_ops","random_ops",{"default", "xpos", "ypos", "xypos", "xmove", "ymove", "xymove", "xboundary", "yboundary", "xyboundary"},1) + params:set_action("random_ops", function(x) random_ops = x end) + + params:add_trigger("randomize", "randomize") + params:set_action("randomize", function(x) randomize_dots() end) + + params:add_trigger("reset", "reset") + params:set_action("reset", function(x) reset() end) + + build_scale() randomize_dots() + + clock.run(step) end function key(id,state) @@ -132,6 +174,10 @@ function reset() dots[i].move_y = 0 dots[i].y = viewport.height/2 dots[i].x = ((viewport.width/8)*i)-8 + dots[i].x1_bound = 1 + dots[i].x2_bound = viewport.width + dots[i].y1_bound = 1 + dots[i].y2_bound = viewport.height end end @@ -273,7 +319,7 @@ end function move_dots_random() for i=1, dot_count do if dots[i].x < dots[i].x1_bound or dots[i].x > dots[i].x2_bound then - dots[i].x = math.random(dots[i].x1_bound,dots[i].x1_bound) + dots[i].x = math.random(dots[i].x1_bound,dots[i].x2_bound) end if dots[i].y < dots[i].y1_bound or dots[i].y > dots[i].y2_bound then dots[i].y = math.random(dots[i].y1_bound,dots[i].y2_bound) @@ -303,11 +349,24 @@ function midi_to_hz(note) return hz end +function note_debug() + for i = 1, #notes do + print(i .. ": " .. notes[i]) + end +end + function play() for i=1, dot_count do for j=1, dot_count do if distance(dots[i].x, dots[i].y, dots[j].x, dots[j].y) < connect_distance and dots[j].dirty[i] == 0 then - engine.hz(midi_to_hz( util.clamp(dots[j].x,10,viewport.width))) + + local note_num = notes[(math.floor( dots[j].x * (num_to_add / viewport.width) ) % num_to_add) + 1] + local freq = MusicUtil.note_num_to_freq(note_num) + + -- print("note_num:" .. note_num) + + engine.hz(freq) + -- engine.hz(midi_to_hz( util.clamp(dots[j].x,10,viewport.width))) engine.cutoff( util.clamp(dots[j].y,2,viewport.height)*20) dots[j].dirty[i]=1 end @@ -322,7 +381,8 @@ function play_midi() for i=1, dot_count do for j=1, dot_count do if distance(dots[i].x, dots[i].y, dots[j].x, dots[j].y) < connect_distance and dots[j].dirty[i] == 0 then - dots[j].note = math.floor(dots[j].x) + + dots[j].note = notes[(math.floor( dots[j].x * (num_to_add / viewport.width) ) % num_to_add) + 1] m:note_on(dots[j].note,math.floor(dots[j].y),channel) dots[j].dirty[i]=1 end @@ -405,27 +465,52 @@ function redraw() screen.update() end -re = metro.init() -re.time = 0.1 -re.event = function() - frame = frame + 1 - move_dots() - if boundary_ops == 1 then - move_dots_wrap() - elseif boundary_ops == 2 then - move_dots_random() - elseif boundary_ops == 3 then - move_dots_bounce() - elseif boundary_ops == 4 then - move_dots_random_bounce() - end - - if useMidi == 1 then - play_midi() - else - play() +function step() + while true do + clock.sync(1/params:get("step_div")) + frame = frame + 1 + move_dots() + if boundary_ops == 1 then + move_dots_wrap() + elseif boundary_ops == 2 then + move_dots_random() + elseif boundary_ops == 3 then + move_dots_bounce() + elseif boundary_ops == 4 then + move_dots_random_bounce() + end + + if useMidi == 1 then + play_midi() + else + play() + end + + redraw() end - - redraw() end -re:start() + +-- re = metro.init() +-- re.time = 0.1 +-- re.event = function() +-- frame = frame + 1 +-- move_dots() +-- if boundary_ops == 1 then +-- move_dots_wrap() +-- elseif boundary_ops == 2 then +-- move_dots_random() +-- elseif boundary_ops == 3 then +-- move_dots_bounce() +-- elseif boundary_ops == 4 then +-- move_dots_random_bounce() +-- end + +-- if useMidi == 1 then +-- play_midi() +-- else +-- play() +-- end + +-- redraw() +-- end +-- re:start()