exploring scripting for the first time - my goal is to replace one of the K functions with a HID footswitch.

Trying to understand the basics still. If I get this data from my device:

HID selected USB2.0 Hub
hid.event	type: 1	code: 2	value: 1	keycode: KEY_1
hid.event	type: 1	code: 2	value: 0	keycode: KEY_1

and I have this function, as an example:

function key(n,z)
  if z == 1 then
  print("pressed")
  else
   print("off")
  end
end

Is replacing the function call method “key” with HID done by just changing “key” to something else? or does it involve more things that are less intuitive?

HID events are going to have 3 args, type, code, and value. so you’d need something like…

function HID.event(type, code, value)
  if value == 1 then
    print(type .. " : " .. code)
  else print("nah") end
end
1 Like

how do i refer to the declaration after the function? the difference between

function key() 

and

function HID.event()

is that a method? or an argument? feels like im missing a doc somewhere. I get that its the name of a function but it also contains data? like globally assigned variables?

(thank you this is very helpful)

1 Like

So, when you connect to a HID with
local HID = hid.connect()
one of the things that happens (tbh, I only really understand enough to make use of things :sweat_smile:) is that you get a callback, norns is all about the callbacks. In this case it’s called HID.event and you can re-define it in your script.

-- event callback function 
-- prints event data to Maiden REPL
function HID.event(typ, code, val)
    print("HID.event : ", typ, code, val)
end

It might be helpful to further dissect what’s happening in the HID demo script. (I should probably add some comments in there to describe what is what)

Look at the chunk which prints the assorted device info. That’s the event callback which happens when an event comes in. That’s where your “key pressed” stuff would happen. (Like if val == 1 it’s pressed)

I can do an example later when I’m not on my phone. :slight_smile:

2 Likes
function HID.event()

The HID.event() function is an event handler callback.

In developer terms, HID.connect() returns a HID device.
When norns receives a HID event it checks to see if there is a callback function defined for the device the HID event originated from and calls it.

If you just create a key() function it is not “attached” to the device callback. A valid, but slightly verbose, approach to this is:

function key(typ, code, val)
  print("HID.event : ", typ, code, val)
end
HID.event = key -- Attach the key function to the HID.event callback

This could be useful if you wanted to have a single function attached to multiple devices (if you don’t care what device the event comes from…)

probable fix https://github.com/monome/norns/pull/1011

I’m playing with devices - so if I change devices and then change which device is in a specific vport, then my things table (devices names in this case) is no longer accurate when I select something with that param action.

Maybe I need to refresh the vports in that action?

That sounds like the right direction…

Is it possible to params:set() the options in an option type?

If the device list changes after the script has run, my parameter option list for device selection becomes incorrect and I’d like to update it. Just not sure how.

FWIW - I hacked this by using params.params[1].options = devicelist but this is less than ideal.

if you’re changing the options it’s likely a fragile usecase and possibly not advised. consider that psets store the number position of the option selected. if the number of options changes, then psets will be an invalid state based on dynamic options changing at runtime.

re: device list, are you trying to replicate some vport stuff inside of the param menu?

I am attempting to provide a param to switch between attached devices for the current script. (see Grid Test) Currently if the devices change after the script loads (new device added, device changes vport position), the param options list will be incorrect. Thus I was attempting to bullet-proof the param listing.

In this particular case, the options list size wont change since it’s mirroring the vports, but I see your point about psets (although I’m not saving state in this script).

Does the Lua environment have a TCP/IP client library? There are a couple of servers I would like to run on my Norns in order to bridge to certain USB devices – e.g. http://openpixelcontrol.org protocol talking to the Fadecandy Server to send visualization data to Neopixel LEDs.

1 Like

not installed by default but you can get it with luarocks install luasocket

https://luarocks.org/modules/luasocket/luasocket

2 Likes

Have you tried Fennel in the meantime? I’m interested too!

I’ve had a bit of a look around here and the docs and can’t seem to see if there is a command to list all the parameters of a script? I’m having a bit of a toy around with an op-z style external ui over OSC and was going to start with mapping some params.

Edit: I did some source digging and found params.params :slight_smile:

I’ve written a little toy to line up hex values common in trackers (like my beloved Adlib Tracker II) with rhythmic counting devices like

  • ta ka di mi
    or
  • 1 e & a 2 e & a 3 e … …

I’m just not sure it’s worthy of a library post?

the code is a little rough, but I'm proud of it and think it might be useful to some!
-- tracker tool
-- ttool
--
-- easy to use rulers for
-- all sorts of tracking
--      scenarios
--
-- TODO: add params
--       for which counts
--       are displayed
--      rather than all at once
--
-- TODO: add custom rulers
--       maybe chord progs
--       ill hardcode some first
--
-- TODO: split counts off into 
--       a little lib
--       so i can store 
--       a default instance 
--       in a var
-- 
-- ---idea:
-- local n = {},{},{}
-- counts = require lib/counts
-- 
-- function init()
--   for i=1,3 do
--     n[i] = counts.newBlank()
--   end
-- 
-- ---lib/counts--------
-- function newBlank()
--   local t = {}
--   local mt = { 
--     __index = function (t, k) 
--       return "---" 
--     end }
--   t = setmetatable(
--     counts.blank, mt)
--   return t
-- end

engine.name = "TestSine"

counts = {
  blank = {},
  hex = {}, -- see hex_mt
  four_four={ 
    
    quarter={
      [1]="one",[5]="two",[9]="three",[13]="four"
    },
  
    eigth={
      [1]="one",[5]="two",[9]="three",[13]="four", 
      [3]="and",[7]="and",[11]="and",[15]="and"
    },
  
    sixteenth={
      [1]="1",[5]="2",[9]="3",[13]="4", 
      [2]="e",[6]="e",[10]="e",[14]="e",
      [3]="&",[7]="&",[11]="&",[15]="&",
      [4]="a",[8]="a",[12]="a",[16]="a"
    },
  
    takadimi={
      "ta", "ka", "di", "mi",
      "ta", "ka", "di", "mi",
      "ta", "ka", "di", "mi",
      "ta", "ka", "di", "mi"
    }
  }
}

local counts_mt = { __index = function (t, k) return "---" end }

local hex_mt = {
  __index = function (t,k) 
    return string.format('%02x', k-1)
  end
}

function counts_metatables()
  -- TODO: apply metatable to each counts.x.y table for x,y
  counts.blank = setmetatable(
    counts.blank, counts_mt)
  counts.four_four.quarter = setmetatable(
    counts.four_four.quarter, counts_mt)
  counts.four_four.eigth = setmetatable(
    counts.four_four.eigth, counts_mt)
  counts.four_four.sixteenth = setmetatable(
    counts.four_four.sixteenth, counts_mt)
  counts.hex = setmetatable(
    counts.hex, hex_mt)
end

function init()
  counts_metatables() 
  
  re=metro.init()
  re=metro.init()
	re.time = 1.0/15
	re.event = function()
	  redraw()
	end
	
	screen_init()
	re:start()
end

function screen_init()
  screen.aa(1)
end

local scroll = 0
local column_positions = {2, 16, 30, 46, 58, 72, 88, 98, 112}
local c_p_hl= { 2, 3, 5, 6, 8, 9 }
local row_offset = 6
local hx,hy = 1,1

function redraw()
  screen.clear()
  
  screen.level(10)
  screen.font_face(1)
  screen.font_size(8)
  
  for i=1,16 do
    
    -- COL 1 -- HEX
    fx_move(
      column_positions[1],
      row_offset*(i-scroll)) -- start @ 2
    screen.text(counts.hex[i])
    
    -- COL 2 -- NOTE 1
    fx_move(
      column_positions[2],
      row_offset*(i-scroll)) -- n1 = start + 14
    screen.text(counts.blank[i])
    
    -- COL 3 -- FX 1
    fx_move(
      column_positions[3],
      row_offset*(i-scroll)) -- f1 = n1 + 14
    screen.text(counts.blank[i])
    
    -- COL 4 -- RULER 1
    fx_move(
      column_positions[4],
      row_offset*(i-scroll)) -- r1 = f1 + 16
    screen.text(
      counts.four_four.takadimi[i])
    
    -- COL 5 -- NOTE 2
    fx_move(
      column_positions[5],
      row_offset*(i-scroll)) -- n2 = r1 + 12
    screen.text(counts.blank[i])
    
    -- COL 6 -- FX 2
    fx_move(
      column_positions[6],
      row_offset*(i-scroll)) -- f2 = n2 + 14
    screen.text(counts.blank[i])
    
    -- COL 7 -- RULER 2
    fx_move(
      column_positions[7],
      row_offset*(i-scroll)) -- r2 = f2 + 16
    screen.text(
      counts.four_four.sixteenth[i])
    
    -- COL 8 -- NOTE 3
    fx_move(
      column_positions[8],
      row_offset*(i-scroll)) -- n3 = r2 + 10
    screen.text(counts.blank[i])
    
    -- COL 9 -- FX 3
    fx_move(
      column_positions[9],
      row_offset*(i-scroll)) -- f3 = n3 + 14
    screen.text(counts.blank[i])
  end 
  
  -- OPERATIONS THAT USE THIS RECTANGLE
  -- AS THEIR TARGERT
  -- NEED TO OPEPRATE ON Y+SCROLL
  screen.level(6)
  fx_rect(
    column_positions[c_p_hl[math.floor(hx)]], 
    row_offset*(math.floor(hy)-1),
    12, row_offset)
  screen.stroke()
  
  screen.update()
end


function fx_move(x,y,fx)
  local xscale,yscale = 1,1
  -- TODO: what if i made everything wiggle by doing something fun in here
  screen.move(x*xscale,y*yscale) 
end

function fx_rect(x,y,w,h)
  local xscale,yscale = 1,1
  -- TODO: what if i made rectangles move around by doing something fun in here
  screen.rect(x*xscale,y*yscale,w,h) 
end

function enc(n,d)
  if n == 1 then
    scroll = util.clamp((scroll + d), 0, 6)
  elseif n == 2 then
    hx = util.clamp((hx + d*0.2), 1,6)
  elseif n == 3 then
    hy = util.clamp((hy + d*0.4), 1,16)
  end
end

Recommendations?

Update: i’ve got the bug. This turned into a tracker using softcut as it’s sampler and it will be finished and released as soon as I can manage it. If anyone wants to help build or test I’m slightly in over my head but having a great time!

4 Likes

pulling a bit of stupid tonight and can’t quite figure this out…

I have a redraw function for the OLED, with a gridredraw inside, and when I do a K1 to go to the menu or params screens, the grid leds stop updating

redraw() is getting called from a beatclock step event

any help?

function gridredraw()
  grid_device:all(0)
  -- function to set grid leds here
  grid_device:refresh()
end 

function redraw()
  gridredraw()
  -- oled update stuff here
end

menu steals the redraw function

call gridredraw separately

1 Like

… or can I just rename redraw()?