Skip to content
Permalink
main
Go to file
 
 
Cannot retrieve contributors at this time
executable file 403 lines (368 sloc) 11.8 KB
--- ~ sines 0.8 ~
-- @oootini
-- E1 - norns master volume
-- E2 - select sine 1-16
-- E3 - set sine amplitude
-- K2 + E2 - change note
-- K2 + E3 - detune
-- K2 + K3 - set voice panning
-- K3 + E2 - change envelope
-- K3 + E3 - change FM index
-- K1 + E2 - change sample rate
-- K1 + E3 - change bit depth
local sliders = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
local edit = 1
local accum = 1
-- env_name, env_bias, attack, decay. bias of 1.0 is used to create a static drone
local envs = {{"drone", 1.0, 1.0, 1.0},
{"am1", 0.0, 0.001, 0.01},
{"am2", 0.0, 0.001, 0.02},
{"am3", 0.3, 0.001, 0.05},
{"pulse1", 0.0, 0.001, 0.2},
{"pulse2", 0.0, 0.001, 0.5},
{"pulse3", 0.0, 0.001, 0.8},
{"pulse4", 0.3, 0.001, 1.0},
{"ramp1", 0.0, 1.5, 0.01},
{"ramp2", 0.0, 2.0, 0.01},
{"ramp3", 0.0, 3.0, 0.01},
{"ramp4", 0.3, 4.0, 0.01},
{"evolve1", 0.3, 10.0, 10.0},
{"evolve2", 0.3, 15.0, 11.0},
{"evolve3", 0.3, 20.0, 12.0},
{"evolve4", 0.4, 25.0, 15.0}
}
local env_values = {}
local env_edit = 1
local env_accum = 1
local value = 0
local text = " "
local step = 0
local cents = {}
local notes = {}
local scale_names = {}
local key_1_pressed = 0
local key_2_pressed = 0
local key_3_pressed = 0
local toggle = false
local scale_toggle = false
engine.name = "Sines"
MusicUtil = require "musicutil"
function init()
print("loaded Sines engine")
add_params()
edit = 0
for i = 1,16 do
env_values[i] = params:get("env" .. i)
cents[i] = params:get("cents" .. i)
sliders[i] = (params:get("vol" .. i))*32
end
end
function add_params()
--set the scale note values
for i = 1, #MusicUtil.SCALES do
table.insert(scale_names, string.lower(MusicUtil.SCALES[i].name))
end
params:add{type = "option", id = "scale_mode", name = "scale mode",
options = scale_names, default = 5,
action = function() set_notes() end}
params:add{type = "number", id = "root_note", name = "root note",
min = 0, max = 127, default = 60, formatter = function(param) return MusicUtil.note_num_to_name(param:get(), true) end,
action = function() set_notes() end}
--set voice params
for i = 1,16 do
params:add_group("voice " .. i .. " params", 11)
--set voice vols
params:add_control("vol" .. i, "vol " .. i, controlspec.new(0.0, 1.0, 'lin', 0.01, 0.0))
params:set_action("vol" .. i, function(x) set_vol(i - 1, x) end)
params:add{type = "number", id = "pan" ..i, name = "pan " .. i, min = -1, max = 1, default = 0, formatter = function(param) return pan_formatter(param:get()) end, action = function(x) set_synth_pan(i - 1, x) end}
params:add{type = "number", id = "note" ..i, name = "note " .. i, min = 0, max = 127, default = 60, formatter = function(param) return MusicUtil.note_num_to_name(param:get(), true) end, action = function(x) set_note(i - 1, x) end}
params:add_control("cents" .. i, "cents detune " .. i, controlspec.new(-200, 200, 'lin', 1, 0,'cents'))
params:set_action("cents" .. i, function(x) tune(i - 1, x) end)
params:add_control("fm_index" .. i, "fm index " .. i, controlspec.new(0.0, 200.0, 'lin', 1.0, 3.0))
params:set_action("fm_index" .. i, function(x) set_fm_index(i - 1, x) end)
params:add{type = "number", id = "env" ..i, name = "env " .. i, min = 1, max = 16, default = 1, formatter = function(param) return env_formatter(param:get()) end, action = function(x) set_env(i, x) end}
params:add_control("attack" .. i, "env attack " .. i, controlspec.new(0.01, 15.0, 'lin', 0.01, 1.0,'s'))
params:set_action("attack" .. i, function(x) set_amp_atk(i - 1, x) end)
params:add_control("decay" .. i, "env decay " .. i, controlspec.new(0.01, 15.0, 'lin', 0.01, 1.0,'s'))
params:set_action("decay" .. i, function(x) set_amp_rel(i - 1, x) end)
params:add_control("env_bias" .. i, "env bias " .. i, controlspec.new(0.0, 1.0, 'lin', 0.1, 1.0))
params:set_action("env_bias" .. i, function(x) set_env_bias(i - 1, x) end)
params:add_control("bit_depth" .. i, "bit depth " .. i, controlspec.new(1, 24, 'lin', 1, 24, 'bits'))
params:set_action("bit_depth" .. i, function(x) set_bit_depth(i - 1, x) end)
params:add_control("smpl_rate" .. i, "sample rate " .. i, controlspec.new(480, 48000, 'lin', 100, 48000,'hz'))
params:set_action("smpl_rate" .. i, function(x) set_sample_rate(i - 1, x) end)
end
params:default()
params:bang()
end
function build_scale()
notes = MusicUtil.generate_scale_of_length(params:get("root_note"), params:get("scale_mode"), 16)
local num_to_add = 16 - #notes
for i = 1, num_to_add do
table.insert(notes, notes[16 - num_to_add])
end
end
function set_notes()
build_scale()
scale_toggle = true
for i = 1,16 do
params:set("note" .. i, notes[i])
end
end
function set_note(synth_num, value)
notes[synth_num] = value
--also reset the cents value here too
params:set("cents" .. synth_num + 1, 0)
engine.hz(synth_num, MusicUtil.note_num_to_freq(notes[synth_num]))
engine.hz_lag(synth_num, 0.005)
if scale_toggle then
--do nothing
end
if not scale_toggle then
edit = synth_num
end
redraw()
end
function set_freq(synth_num, value)
engine.hz(synth_num, value)
engine.hz_lag(synth_num, 0.005)
edit = synth_num
redraw()
end
function set_vol(synth_num, value)
engine.vol(synth_num, value)
edit = synth_num
end
function tune(synth_num, value)
--calculate new tuned value from cents value + midi note
--https://music.stackexchange.com/questions/17566/how-to-calculate-the-difference-in-cents-between-a-note-and-an-arbitrary-frequen
local detuned_freq = (math.pow(10, value/3986))*MusicUtil.note_num_to_freq(notes[synth_num])
--round to 2 decimal points
detuned_freq = math.floor((detuned_freq) * 10 / 10)
set_freq(synth_num, detuned_freq)
edit = synth_num
redraw()
end
function set_env(synth_num, value)
--env_name, env_bias, attack, decay
params:set("env_bias" .. synth_num, envs[value][2])
params:set("attack" .. synth_num, envs[value][3])
params:set("decay" .. synth_num, envs[value][4])
end
function env_formatter(value)
local env_name = envs[value][1]
return (env_name)
end
function set_fm_index(synth_num, value)
engine.fm_index(synth_num, value)
edit = synth_num
redraw()
end
function set_amp_atk(synth_num, value)
engine.amp_atk(synth_num, value)
edit = synth_num
redraw()
end
function set_amp_rel(synth_num, value)
engine.amp_rel(synth_num, value)
edit = synth_num
redraw()
end
function set_env_bias(synth_num, value)
engine.env_bias(synth_num, value)
edit = synth_num
redraw()
end
function set_bit_depth(synth_num, value)
engine.bit_depth(synth_num, value)
edit = synth_num
redraw()
end
function set_sample_rate(synth_num, value)
engine.sample_rate(synth_num, value)
edit = synth_num
redraw()
end
function set_synth_pan(synth_num, value)
engine.pan(synth_num, value)
redraw()
end
function pan_formatter(value)
if value == 1 then
text = "right"
elseif value == 0 then
text = "middle"
elseif value == -1 then
text = "left"
end
return (text)
end
function set_pan()
-- pan position on the bus, -1 is left, 1 is right
if key_2_pressed == 1 and key_3_pressed == 1 then
toggle = not toggle
if toggle then
--set hard l/r pan values
for i = 1,16 do
if i % 2 == 0 then
--even, pan right
set_synth_pan(i, 1)
params:set("pan" .. i, 1)
elseif i % 2 == 1 then
--odd, pan left
set_synth_pan(i, -1)
params:set("pan" .. i, -1)
end
end
end
if not toggle then
for i = 1,16 do
set_synth_pan(i, 0)
params:set("pan" .. i, 0)
end
end
end
end
--update when a cc change is detected
m = midi.connect()
m.event = function(data)
local d = midi.to_msg(data)
if d.type == "cc" then
--set all the sliders + fm values
for i = 1,16 do
sliders[i] = (params:get("vol" .. i))*32 - 1
if sliders[i] > 32 then sliders[i] = 32 end
if sliders[i] < 0 then sliders[i] = 0 end
end
end
--allow root note to be set from midi keyboard - doesn't work with multiple midi devices?
if d.type == "note_on" then
params:set("root_note", d.note)
end
redraw()
end
function enc(n, delta)
if n == 1 then
if key_1_pressed == 0 then
params:delta('output_level', delta)
end
elseif n == 2 then
if key_1_pressed == 0 and key_2_pressed == 0 and key_3_pressed == 0 then
--navigate up/down the list of sliders
--accum wraps around 0-15
accum = (accum + delta) % 16
--edit is the slider number
edit = accum
elseif key_1_pressed == 0 and key_2_pressed == 0 and key_3_pressed == 1 then
params:set("env" .. edit+1, params:get("env" .. edit+1) + delta)
--env_accum = (env_accum + delta) % 16
--env_edit is the env_values selector
--env_edit = env_accum
--change the AD env values
--env_values[edit+1] = env_edit+1
--set the env
--set_env(edit+1, env_edit+1)
elseif key_1_pressed == 0 and key_2_pressed == 1 and key_3_pressed == 0 then
-- increment the note value with delta
params:set("note" .. edit+1, params:get("note" .. edit+1) + delta)
elseif key_1_pressed == 1 and key_2_pressed == 0 and key_3_pressed == 0 then
--set sample rate
params:set("smpl_rate" .. edit+1, params:get("smpl_rate" .. edit+1) + (delta) * 1000)
end
elseif n == 3 then
if key_1_pressed == 0 and key_3_pressed == 0 and key_2_pressed == 0 then
--set the slider value
sliders[edit+1] = sliders[edit+1] + delta
amp_value = util.clamp(((sliders[edit+1] + delta) * .026), 0.0, 1.0)
params:set("vol" .. edit+1, amp_value)
if sliders[edit+1] > 32 then sliders[edit+1] = 32 end
if sliders[edit+1] < 0 then sliders[edit+1] = 0 end
elseif key_1_pressed == 0 and key_2_pressed == 1 and key_3_pressed == 0 then
--set the cents value to increment by
params:set("cents" .. edit+1, params:get("cents" .. edit+1) + delta)
elseif key_1_pressed == 0 and key_2_pressed == 0 and key_3_pressed == 1 then
-- set the fm value
params:set("fm_index" .. edit+1, params:get("fm_index" .. edit+1) + delta)
elseif key_1_pressed == 1 and key_2_pressed == 0 and key_3_pressed == 0 then
--set bit depth
params:set("bit_depth" .. edit+1, params:get("bit_depth" .. edit+1) + delta)
end
end
redraw()
end
function key(n, z)
--use these keypress variables to add extra functionality on key hold
if n == 1 and z == 1 then
key_1_pressed = 1
elseif n == 1 and z == 0 then
key_1_pressed = 0
elseif n == 2 and z == 1 then
key_2_pressed = 1
elseif n == 2 and z == 0 then
key_2_pressed = 0
elseif n == 3 and z == 1 then
key_3_pressed = 1
elseif n == 3 and z == 0 then
key_3_pressed = 0
end
set_pan()
redraw()
end
function redraw()
screen.aa(1)
screen.line_width(2.0)
screen.clear()
for i= 0, 15 do
if i == edit then
screen.level(15)
else
screen.level(2)
end
screen.move(32+i*4, 62)
screen.line(32+i*4, 60-sliders[i+1])
screen.stroke()
end
screen.level(10)
screen.line(32+step*4, 68)
screen.stroke()
--display current values
screen.move(0,5)
screen.level(2)
screen.text("note: ")
screen.level(15)
screen.text(MusicUtil.note_num_to_name(params:get("note" .. edit+1),true) .. " ")
screen.level(2)
screen.text("detune: ")
screen.level(15)
screen.text(params:get("cents" .. edit+1) .. " cents")
screen.move(0,12)
screen.level(2)
screen.text("env: ")
screen.level(15)
screen.text(env_formatter(params:get("env" .. edit+1)))
--screen.text(envs[env_values[edit+1]][1])
--screen.text(params:get("attack" .. edit+1) .. "/" .. params:get("decay" .. edit+1) .. " s")
screen.level(2)
screen.text(" fm index: ")
screen.level(15)
screen.text(params:get("fm_index" .. edit+1))
screen.move(0,19)
screen.level(2)
screen.text("smpl rate: ")
screen.level(15)
screen.text(params:get("smpl_rate" .. edit+1)/1000 .. "k")
screen.level(2)
screen.text(" bit dpt: ")
screen.level(15)
screen.text(params:get("bit_depth" .. edit+1))
screen.move(0,26)
screen.level(2)
screen.text("pan: ")
screen.level(15)
screen.text(pan_formatter(params:get("pan" .. edit+1)))
screen.level(2)
screen.text(" master vol: ")
screen.level(15)
screen.text(math.floor((params:get('output_level')) * 10 / 10) .. " dB")
screen.update()
end