Skip to content

Commit

Permalink
Fixed midi support
Browse files Browse the repository at this point in the history
MIDI out device shows name in params menu
Updated order of `outputs` param list; This will probably cause issues with any existing PSETs
Cleaned up params menu by grouping some params
Added new "step division" param
  • Loading branch information
Jake Carter authored and JakeCarter committed Nov 22, 2022
1 parent 5ebc193 commit 2cedd76
Showing 1 changed file with 73 additions and 82 deletions.
155 changes: 73 additions & 82 deletions circles.lua
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,34 @@ local libc = include("lib/libCircles")

-- state
local scale_notes
local midi_out_device = midi.connect()
local midi_out_channel
local message = nil
local active_note_age_map = {}
local scale_names = {}

local running = true
local isRunning = true

-- enums
local outputs = { audio = 1, crow = 2, crow_jf = 3, midi = 4 }
local clock_sources = { midi = 1, crow = 2 }
local outputs = { audio = 1, midi = 2, crow = 3, crow_jf = 4 }
local radius_affects = { release = 1, amp = 2 }

function setupParams()
local midi_devices
local midi_out_device
local midi_out_channel
function build_midi_device_list()
midi_devices = {}
for i = 1,#midi.vports do
local long_name = midi.vports[i].name
local short_name = string.len(long_name) > 15 and util.acronym(long_name) or long_name
table.insert(midi_devices,i..": "..short_name)
end
end

function setupParams()
params:add_separator("CIRCLES")

-- output
params:add_option("output", "output", { "audio", "crow", "crow + jf", "midi" }, outputs.audio)
params:set_action("output", function(value)
params:add_group("outs", 3)
params:add_option("circles_output", "out", { "audio", "midi", "crow", "crow + jf" }, outputs.audio)
params:set_action("circles_output", function(value)
if value == outputs.crow then
crow.output[1].action = "pulse(0.1, 5, 1)"
elseif value == outputs.crow_jf then
Expand All @@ -52,66 +63,42 @@ function setupParams()
crow.ii.jf.mode(0)
crow.ii.pullup(false)
end
end)
end)
params:add({type = "option", id = "midi_out_device", name = "midi out device",
options = midi_devices, default = 1,
action = function(value)
midi_out_device = midi.connect(value)
end
})
params:add({type = "number", id = "midi_out_channel", name = "midi out channel",
min = 1, max = 16, default = 1,
action = function(value)
-- JCTODO: Turn all notes off
midi_channel = value
end
})

-- output: audio
-- circles_output: audio
params:add_group("audio", 2)
params:add_option("radius_affects", "radius affects", { "release", "amp" })
params:add_control("cutoff", "cutoff", controlspec.new(50,20000,'exp',0,1000,'hz'))
params:set_action("cutoff", function(x)
engine.cutoff(x)
end)
params:add_separator()

-- JCTODO: Get this working again; I don't think I'm going to need this with the new `clock` api, but I guess we'll see.
-- clock_sources
-- params:add({type = "option", id = "clock_source", name = "clock source", default = clock_sources.midi,
-- options = { "midi", "crow" },
-- action = function(value)
-- if value == clock_sources.midi then
--
-- -- JCTODO: Switch to `clock`
-- clk.on_step = step
-- clk:start()
--
--
-- elseif value == clock_sources.crow then
--
-- -- JCTODO: Switch to `clock`
-- clk:stop()
--
-- crow.input[2].mode("change",1,0.1,"rising")
-- crow.input[2].change = function(s)
-- step()
-- end
-- end
-- end
-- })

-- clock_sources: midi
-- clk:add_clock_params() -- JCTODO: Switch to `clock`

params:add({type = "number", id = "midi_out_device", name = "midi out device",
min = 1, max = 4, default = 1,
action = function(value) midi_out_device = midi.connect(value) end})

params:add{type = "number", id = "midi_out_channel", name = "midi out channel",
min = 1, max = 16, default = 1,
action = function(value)
-- TODO: Turn all notes off
midi_out_channel = value
end}
params:add_separator()


-- scale
params:add_group("scale", 2)
-- scale: root note
params:add{type = "number", id = "root_note", name = "root note",
min = 0, max = 127, default = 60, formatter = function(param) return music_util.note_num_to_name(param:get(), true) end,
action = function() build_scale_notes() end}
params:add({type = "number", id = "root_note", name = "root note",
min = 0, max = 127, default = 60,
formatter = function(param) return music_util.note_num_to_name(param:get(), true) end,
action = function() build_scale_notes() end
})
-- scale: mode
params:add{type = "option", id = "scale_mode", name = "scale mode",
params:add({type = "option", id = "scale_mode", name = "scale mode",
options = scale_names, default = 5,
action = function() build_scale_notes() end}
params:add_separator()
action = function() build_scale_notes() end
})

-- libCircles
params:add_option("keep_on_screen", "keep on screen", { "no", "yes" })
Expand All @@ -124,11 +111,11 @@ params:add_separator()
libc.shouldBurstOnScreenEdge = true
end
end)

params:add_option("burst type", "burst type", { "random", "deterministic"})
params:set_action("burst type", function(value)
params:add_option("burst_type", "burst type", { "random", "deterministic"})
params:set_action("burst_type", function(value)
libc.burst_type = value
end)
params:add({type = "number", id = "step_div", name = "step division", min = 1, max = 16, default = 4})

params:default()
params:bang()
Expand All @@ -143,39 +130,43 @@ function init()


libc.handleCircleBurst = handleCircleBurst
-- midi_out_device.event = clk.process_midi -- JCTODO: Switch to `clock`

build_midi_device_list()

setupParams()

clock.run(step)
end

function kill_old_notes()
for active_note, active_note_age in pairs(active_note_age_map) do
if active_note_age >= 1 then
midi_out_device:send({type='note_off', note=active_note, ch=midi_out_channel})
active_note_age_map[active_note] = nil
else
active_note_age_map[active_note] = active_note_age + 1
end
end
end

function step()
while true do
clock.sync(1/4) -- JCTODO: Provide step divider param? `clock.sync(1/params:get("step_div"))`
if running then
-- kill old notes
for active_note, active_note_age in pairs(active_note_age_map) do
if active_note_age >= 1 then
midi_out_device:send({type='note_off', note=active_note, ch=midi_out_channel})
active_note_age_map[active_note] = nil
else
active_note_age_map[active_note] = active_note_age + 1
end
end

clock.sync(1/params:get("step_div"))
kill_old_notes()

if isRunning then
libc.updateCircles()
redraw()
end
end
end

function clock.transport.start()
running = true
isRunning = true
end

function clock.transport.stop()
running = false
isRunning = false
end

function noteForCircle(circle)
Expand All @@ -187,7 +178,7 @@ end
function handleCircleBurst(circle)
local note = noteForCircle(circle)

if params:get("output") == outputs.audio then
if params:get("circles_output") == outputs.audio then
if params:get("radius_affects") == radius_affects.release then
engine.release(math_helpers.scale(circle.r, 1, 64, 0.03, 1))
engine.amp(0.3)
Expand All @@ -198,15 +189,15 @@ function handleCircleBurst(circle)

engine.pw(math_helpers.scale(circle.y, 0, 64, 0.01, 1))
engine.hz(note)
elseif params:get("output") == outputs.crow then
elseif params:get("circles_output") == outputs.midi then
play_note(note)
elseif params:get("circles_output") == outputs.crow then
crow.output[2].volts = (note - 60) / 12
crow.output[3].volts = math_helpers.scale(circle.y, 1, 64, 0.01, 10)
crow.output[4].volts = math_helpers.scale(circle.r, 0, 64, 0.01, 10)
crow.output[1].execute()
elseif params:get("output") == outputs.crow_jf then
elseif params:get("circles_output") == outputs.crow_jf then
crow.ii.jf.play_note((note - 60) / 12, math_helpers.scale(circle.r, 1, 64, 1, 10))
elseif params:get("output") == outputs.midi then
play_note(note)
end
end

Expand Down

0 comments on commit 2cedd76

Please sign in to comment.