Norns — receive OSC notes (from Ableton)

I’d love to wirelessly send notes to norns from Ableton. My impression is that wireless MIDI is not an option, so I figured I’d try to do it through OSC.

After some digging I found a device in Connection Kit called OSC MIDI Send, which simply converts MIDI notes to OSC messages like so —

image

I figured this would work out of the box, but it doesn’t seem like norns is configured to recognize these messages as notes — if it isn’t already clear, I’m not too familiar with the technical details of MIDI or OSC.

So now I’m wondering — if I want this functionality, would it be as simple as writing a script to translate the OSC messages back into MIDI?

If so, what would be the most elegant way to integrate this functionality into pre-existing scripts? For instance, if I wanted to trigger molly_the_poly, would I simply insert my translation script at the top of its main file or something?

And is there a reason this functionality isn’t already supported? Lag perhaps? It seems like a desirable feature to me.

hey! great q’s.

OSC is supported, it’s just up to the script to state how it’d like to handle OSC messages (since each script defines its interactions with controllers – grids, MIDI, OSC, arc, HID, etc).

this raises a different question, though – why don’t more scripts support OSC control out the box? from my understanding, OSC messages aren’t always standardized the way MIDI messages are, since they can be customized and totally arbitrary. it seems that Connection Kit has decided on a particular style, but perhaps the wide variance of approaches contributes to a lack of built-in support in scripts? there’s probably some work to do on the norns documentation, as well – clearer API would help make OSC easier to add.

for now, the OSC-centric norns study is a good place to start unpacking how to work with OSC + norns: https://monome.org/docs/norns/study-5/#numbers-through-air

pretty much, yeah.

using molly as an example, MIDI input is defined starting on line 153.

molly's MIDI input code
-- MIDI input
local function midi_event(data)
  
  local msg = midi.to_msg(data)
  local channel_param = params:get("midi_channel")
  
  if channel_param == 1 or (channel_param > 1 and msg.ch == channel_param - 1) then
    
    -- Note off
    if msg.type == "note_off" then
      note_off(msg.note)
    
    -- Note on
    elseif msg.type == "note_on" then
      note_on(msg.note, msg.vel / 127)
      
    -- Key pressure
    elseif msg.type == "key_pressure" then
      set_pressure(msg.note, msg.val / 127)
      
    -- Channel pressure
    elseif msg.type == "channel_pressure" then
      set_pressure_all(msg.val / 127)
      
    -- Pitch bend
    elseif msg.type == "pitchbend" then
      local bend_st = (util.round(msg.val / 2)) / 8192 * 2 -1 -- Convert to -1 to 1
      local bend_range = params:get("bend_range")
      set_pitch_bend_all(bend_st * bend_range)
      
    -- CC
    elseif msg.type == "cc" then
      -- Mod wheel
      if msg.cc == 1 then
        set_timbre_all(msg.val / 127)
      end
      
    end
  
  end
  
end

using the OSC study + your sample OSC messages as an example, you could add a parallel osc_event function under that midi_event function which connects received OSC messages with the script’s defined note_on and note_off functions.

example code (not tested or totally comprehensive, but should get you started):

function osc_in(path, args, from)

     if path == "/Note1" then
          osc_note = args[1]
     elseif path == "/Velocity1" then
          osc_vel = args[1]
     end

     if osc_vel > 0 then
          note_on(osc_note, osc_vel)
     else
          note_off(osc_note)
     end

end

osc.event = osc_in

hopefully this helps get you going? lmk!

Ooh yes, very cool! Thanks for the info.

I think you’re right — the OSC specification doesn’t at all define a “note” in the way the MIDI spec does.

That’s exactly what I’m thinking… I’d love to see a norns “OSC note” spec that individual scripts could accept (or flip on its head) — but perhaps I should get this working in molly first :slight_smile:


Speaking of which, thanks for the code! I’ll certainly dig into this later and report back.

One thing I’ll say though — perhaps for someone who is reading this later — is that Connection Kit appears to encode polyphony in its OSC Container (as I believe it is called). So Note1 / Velocity1 are the messages for the 1st key held down, Note2 / Velocity2 the messages for the 2nd key held down, and so on.

Other than, that the code looks great :slight_smile: I’m looking forward to getting this working.

Aaand I’m done lol.

This goes anywhere:

-- OSC input
osc_note = {} -- holds { note number, velocity }
function osc_in(path, args, from)
  
  if string.find(path, 'Velocity') ~= nil then
    osc_note[2] = args[1]    -- Ableton Connection Kit sends velocity before note, so we
    osc_note[1] = nil        -- 1. Erase previous note upon receiving new velocity.
  
  elseif string.find(path, 'Note') ~= nil then
    osc_note[1] = args[1]    -- 2. Update note value upon receiving it.
  
  end

  if osc_note[1] ~= nil then -- 3. Trigger a complete note.
    
    -- Note on
    if osc_note[2] > 0 then
      note_on(osc_note[1], osc_note[2] / 127)
    
    -- Note off
    else                    
      note_off(osc_note[1]) -- Note off

    end

  end

end

And

  osc.event = osc_in

goes in the init function. And it works!!

Quite magical I must say… I can now effortlessly play or sequence norns from Ableton (or, anything else for that matter — thinking specifically of desktop Orca).

Amazing stuff! Can you imagine desktop Orca and norns Orca sequencing one another? Lol

3 Likes