Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also .

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also .
base repository: aidanreilly/sines
base: main
head repository: lazzarello/sines
compare: arc-enable
Checking mergeability… Don’t worry, you can still create the pull request.
  • 16 commits
  • 2 files changed
  • 0 comments
  • 1 contributor
Showing with 125 additions and 18 deletions.
  1. +1 −0 README.md
  2. +124 −18 sines.lua
@@ -32,3 +32,4 @@ Saving a pset saves the note selection and midi mapping. The last saved pset is

Control individual sine amplitude, envelope type, and FM index with norns or a midi controller. Controls are mapped from the norns parameters page.

Attach an Arc and watch the envelopes spin! Knobs control speed and direction, LED indicates oscillations. 16 voices are addressable by scrolling enc2 on norns in groups of four.
142 sines.lua
@@ -7,8 +7,27 @@
-- K3 + E2 - change envelope
-- K3 + E3 - change FM index
-- K2 + K3 - set voice panning

-- arc lfo control vars
a = arc.connect()
-- aspirational
-- c = clock.set_source("midi")
local framerate = 40
local arcDirty = true
local startTime
local tau = math.pi * 2
local newSpeed = false
local options = {}
-- sort of overloading #lfo as a synonym for voices. Great start?
local lfo = {}
for i=1,16 do
lfo[i] = {init=1, freq=1, counter=1, interpolator=1}
end
local voice_quad = 1

-- engine control vars
local sliders = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
local env_types = {"drone", "am1", "am2", "am3", "pulse1", "pulse2", "pulse3", "pulse4", "ramp1", "ramp2", "ramp3", "ramp4", "evolve1", "evolve2", "evolve3", "evolve4"}
local env_types = {"drone", "am1", "am2", "am3", "pulse1", "pulse2", "pulse3", "pulse4", "ramp1", "ramp2", "ramp3", "ramp4", "evolve1", "evolve2", "evolve3", "evolve4", "arc"}
-- env_num, env_bias, attack, decay. bias of 1.0 is used to create a static drone
local envs = {{1, 1.0, 1.0, 1.0},--drone
{2, 0.0, 0.001, 0.01},--am1
@@ -25,7 +44,8 @@ local envs = {{1, 1.0, 1.0, 1.0},--drone
{13, 0.3, 10.0, 10.0},--evolve1
{14, 0.3, 15.0, 11.0},--evolve2
{15, 0.3, 20.0, 12.0},--evolve3
{16, 0.3, 25.0, 15.0}--evolve4
{16, 0.3, 25.0, 15.0},--evolve4
{17, 0.0, 1.0, 1.0} -- arc
}
local env_values = {}
local fm_index_values = {}
@@ -43,11 +63,31 @@ local key_2_pressed = 0
local key_3_pressed = 0
local toggle = false
local pan_display = "m"
local interp_divisor = 100


engine.name = "Sines"
MusicUtil = require "musicutil"

function init()
startTime = util.time()
lfo_metro = metro.init()
lfo_metro.time = 0.01
lfo_metro.count = -10
lfo_metro.event = function()
currentTime = util.time()
for i = 1,#lfo do
lfo[i].counter = ((lfo[i].counter + (1*lfo[i].freq)))%100
lfo[i].ar = lfo[i].counter*0.64
end
end
lfo_metro:start()
local arc_redraw_metro = metro.init()
arc_redraw_metro.event = function()
arc_redraw()
redraw()
end
arc_redraw_metro:start(1 / framerate)
print("loaded Sines engine")
add_params()
set_voices()
@@ -64,29 +104,29 @@ function add_params()
min = 0, max = 127, default = 60, formatter = function(param) return MusicUtil.note_num_to_name(param:get(), true) end,
action = function() build_scale() end}
--set voice vol, fm, env controls
for i = 1,16 do
for i = 1,#lfo do
params:add_control("vol" .. i, "voice " .. i .. " volume", controlspec.new(0.0, 1.0, 'lin', 0.01, 0.0))
params:set_action("vol" .. i, function(x) set_voice(i - 1, x) end)
end
for i = 1,16 do
for i = 1,#lfo do
params:add_control("fm_index" .. i, "fm_index " .. i, controlspec.new(0.1, 100.0, 'lin', 0.1, 3.0))
params:set_action("fm_index" .. i, function(x) engine.fm_index(i - 1, x) end)
end
for i = 1,16 do
params:add_number("env" .. i, "env " .. i, 1, 16, 1)
for i = 1,#lfo do
params:add_number("env" .. i, "env " .. i, 1, #lfo, 1)
params:set_action("env" .. i, function(x) set_env(i, x) end)
end
params:default()
edit = 0
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
notes = MusicUtil.generate_scale_of_length(params:get("root_note"), params:get("scale_mode"), #lfo)
local num_to_add = #lfo - #notes
for i = 1, num_to_add do
table.insert(notes, notes[16 - num_to_add])
table.insert(notes, notes[#lfo - num_to_add])
end
for i = 1,16 do
for i = 1,#lfo do
--also set notes
set_freq(i, MusicUtil.note_num_to_freq(notes[i]))
end
@@ -99,7 +139,7 @@ function set_voice(voice_num, value)
end

function set_voices()
for i = 1,16 do
for i = 1,#lfo do
cents_values[i] = 0
env_values[i] = "drone"
fm_index_values[i] = 3.0
@@ -111,8 +151,8 @@ function set_voices()
end

function set_env(synth_num, env_num)
--goofy way to loop through the envs list, but whatever
for i = 1,16 do
-- goofy way to loop through the envs list, but whatever
for i = 1,#env_types do
if envs[i][1] == env_num then
engine.env_bias(synth_num - 1, envs[i][2])
engine.amp_atk(synth_num - 1, envs[i][3])
@@ -139,7 +179,7 @@ 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
for i = 1,#lfo do
sliders[i] = (params:get("vol" .. i))*32-1
fm_index_values[i] = params:get("fm_index" .. i)
if sliders[i] > 32 then sliders[i] = 32 end
@@ -156,7 +196,7 @@ function set_pan()
if toggle then
pan_display = "l/r"
--set hard l/r pan values
for i = 1,16 do
for i = 1,#lfo do
if i % 2 == 0 then
--even, pan right
set_synth_pan(i,1)
@@ -168,24 +208,90 @@ function set_pan()
end
if not toggle then
pan_display = "m"
for i = 1,16 do
for i = 1,#lfo do
set_synth_pan(i,0)
end
end
end
end

-- hardware functions

function a.delta(n,delta)
-- gross, refactor plz, I'm tired of typing the numbers.
-- this seems like a maths thing. something about a vector of 16
-- into a 4x4 matrix? Computer, do what I say in English, not Lua
local voice = 1
if voice_quad == 1 then
voice = n
elseif voice_quad == 2 then
voice = n + 4
elseif voice_quad == 3 then
voice = n + 8
elseif voice_quad == 4 then
voice = n + 12
end
if lfo[voice].interpolater == 1 then
lfo[voice].freq = lfo[voice].freq + delta/interp_divisor
newSpeed = true
-- we need polarity of the LED ring
if lfo[voice].freq > 0 then
-- seventeen is a special arc envelope, sorry I know magic numbers...
envs[17][3] = 0.001
-- we need seconds per cycle for the envelope
envs[17][4] = 1 / lfo[voice].freq
else
envs[17][4] = 0.001
envs[17][3] = math.abs(1 / lfo[voice].freq)
end
set_env(voice, 17)
end
lfo[voice].interpolater = 1
lastTouched = n
arcDirty = true
end

function arc_redraw()
local brightness = 12
a:all(0)
-- there are 4 encoders and 16 lfos. Using the same voice_quad logic
-- as the encoder delta, determine what quadrant we are in before setting
-- the value of seg
for n = 1,4 do
if voice_quad == 1 then
seg = lfo[n].ar/64
elseif voice_quad == 2 then
seg = lfo[n + 4].ar/64
elseif voice_quad == 3 then
seg = lfo[n + 8].ar/64
elseif voice_quad == 4 then
seg = lfo[n + 12].ar/64
end
a:segment(n, seg*tau, tau*seg+0.2, brightness)
end
a:refresh()
end

function enc(n, delta)
if n == 1 then
params:delta('output_level', delta)

elseif n == 2 then
if 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
-- this would be better with maths
if edit < 4 then
voice_quad = 1
elseif edit > 3 and edit < 8 then
voice_quad = 2
elseif edit > 7 and edit < 12 then
voice_quad = 3
elseif edit > 11 then
voice_quad = 4
end
elseif key_2_pressed == 0 and key_3_pressed == 1 then
env_accum = (env_accum + delta) % 16
--env_edit is the env_values selector
@@ -290,4 +396,4 @@ function redraw()
screen.level(15)
screen.text(math.floor((params:get('output_level')) * 10 / 10) .. " dB")
screen.update()
end
end

No commit comments for this range