OSC API in Lua Terminal & Editor Not Firing Notes

Hi,

Can anybody tell me why this code snippet is not firing notes in Renoise when run in the scripting terminal/editor:

local server, client

-------------------------------------------------------------------------------
---- Osc server (receive Osc from one or more clients)

-- open a socket connection to the server
if not server then 
  local socket_error
  server, socket_error = renoise.Socket.create_server(
    "localhost", 8008, renoise.Socket.PROTOCOL_UDP)
 
  if (socket_error) then 
    renoise.app():show_warning(("Failed to start the " .. 
      "OSC server. Error: '%s'"):format(socket_error))
    return
  end

  print('hit')
end

server:run {
  socket_message = function(socket, data)
    -- decode the data to Osc
    local message, osc_error = renoise.Osc.from_binary_data(data)

    -- show what we've got
    if (message) then
      if (type(message) == "Message") then
        print(message.pattern)
        rprint(message.arguments)
      end
    else
      print(("Got invalid OSC data, or data which is not " .. 
    "OSC data at all. Error: '%s'"):format(osc_error))
    end
  
  end    
}


-------------------------------------------------------------------------------
-- Osc client & message construction (send Osc to a server)

-- open a socket connection to the server
if not client then
  local socket_error 
    client, socket_error = renoise.Socket.create_client(
    "localhost", 8008, renoise.Socket.PROTOCOL_UDP)
 
  if (socket_error) then 
    renoise.app():show_warning(("Failed to start the " .. 
      "OSC client. Error: '%s'"):format(socket_error))
    return
  end
  print('hit2')
end

-- construct and send messages
client:send(
  renoise.Osc.Message("/renoise/trigger/note_on", {
    { tag="i", value=1 },
    { tag="i", value=1 },
    { tag="i", value=60 },
    { tag="i", value=64 }
  })
)

It’s mainly copied from the OSC snippet in the xrnx dev package. I’ve looked at Duplex and xLib and all that, and see similar constructions. This fires the print statements, but will not play any notes.

Halp.

I have not reviewed the code, but make sure the port is the same. Renoise, by default, uses port 8000. In your copied code you are using port 8008.
The port is configured in Renoise Preferences / OSC. If you do not use the same port, it will obviously not send messages to be interpreted.

On the other hand, in programming it is a bad practice to “copy code”. It is a priority to know what each thing does. And if so, then copy and then modify to adjust it.

1 Like

I already tried that, options is set with server running and 8008 as port. Still nothing. I don’t see why it would be necessary to have an OSC server running in the user-level options as well as creating one in Lua API. Bit redundant no?

Also, documentation for OSC in Renoise is shit, hence copying code to experiment with.

1 Like

True, it’s a bit messy. But once you have the code that works, everything is ready.

Months ago I created a tool called KangarooX120. There is code that uses OSC in it. Maybe if you take a look you understand better how it works. It would only be necessary to copy the piece of OSC code and then make sure to locate how to shoot each note.

Try stripping the server part in the code and restart Renoise. That works for me.

My guess is that your server init will override the ‘native’ server (i never used servers in the API myself). And sometimes when you trial and error you need to restart Renoise since things can get stuck in the ‘testpad’ environment. Explained a bit simplistic, probably.

Thanks you two.

I’m confused by this. As you suggested Joule, removing the server code and just using the user-options OSC server allows me to play a note from a script. But the opposite does not work. I cannot play a note purely from the scripted API OSC server with the options server shut down.

I was using as much info as i could find from the forums and documentation. I even copied this class you wrote for Raul: [Solved] Help, code for play/stop specific note of a instrument, is po which uses the same syntactic setup as my code above, but has a tool() call which doesn’t work in the scripting editor of course.

The OSC docs are using functional notation to explain the trigger messages eg. “/renoise/trigger/note_on(number, number, number, number)” which is not valid if taken literally, as Renoise is looking for a table of tables for the arguments and they have to be passed outside of the selector string, more like a list. It’s a mess tbh. Danoise’s tools and libraries are mental, and way too complicated to be useful as a ‘meta-API’ in my opinion if that’s what they’re for, and he’s doing exactly the same, with the table construct, just in a more extreme way.

Extracted from my KangarooX120 tool, and adapted for direct use:

-------------------------------------------------------------------------------------------------
--osc server (localhost)
KNG_OSC_IP_1 = 127  -- always 127
KNG_OSC_IP_2 = 0    -- 0 to 255
KNG_OSC_IP_3 = 0    -- 0 to 255
KNG_OSC_IP_4 = 1    -- 1 to 255
KNG_OSC_P0RT = 8000 -- 1 to 9999
KNG_OSC_PROT = renoise.Socket.PROTOCOL_UDP



class "KNG_OscClient"
function KNG_OscClient:__init( osc_host, osc_port, protocol )
  self._connection = nil
  local client, socket_error = renoise.Socket.create_client( osc_host, osc_port, protocol )
  if ( socket_error ) then 
    rna:show_warning( "Warning: Failed to start the internal OSC client" )
    self._connection = nil
  else
    self._connection = client
  end
end
---
-- Trigger instrument-note
  --- note_on (bool), true when note-on and false when note-off
  --- instr    (int), the Renoise instrument index 1-254
  --- track    (int), the Renoise track index 
  --- note     (int), the desired pitch, 0-119
  --- velocity (int), the desired velocity, 0-127
function KNG_OscClient:trigger_instrument( note_on, instr, track, note, velocity )
  if not self._connection then
    return false
  end
  local osc_vars = { }
        osc_vars[1] = { tag = "i", value = instr }
        osc_vars[2] = { tag = "i", value = track }
        osc_vars[3] = { tag = "i", value = note  }
        
  local header = nil
  if ( note_on ) then
    header = "/renoise/trigger/note_on"
      osc_vars[4] = { tag = "i", value = velocity }    
  else
    header = "/renoise/trigger/note_off"
  end
  self._connection:send( renoise.Osc.Message( header, osc_vars ) )
  return true
end
---
local KNG_OSC_CLIENT = nil
function kng_osc_client_launch() 
  KNG_OSC_CLIENT = KNG_OscClient( ("%s.%s.%s.%s"):format( KNG_OSC_IP_1, KNG_OSC_IP_2, KNG_OSC_IP_3, KNG_OSC_IP_4 ), KNG_OSC_P0RT, KNG_OSC_PROT ) --127.0.0.1, 8000, 2
end
kng_osc_client_launch()


-------------------------------------------------------------------------------------------------
--trigger_note_on function
local function trigger_note_on(nte)  --nte=0 to 119
  local song=renoise.song()
  local ins=song.selected_instrument_index
  local trk=song.selected_track_index
  local vel=127 -- 0 to 127
  KNG_OSC_CLIENT:trigger_instrument( true, ins, trk, nte, vel )
end

--trigger_note_off function
local function trigger_note_off(nte)
  local song=renoise.song()
  local ins=song.selected_instrument_index
  local trk=song.selected_track_index
  KNG_OSC_CLIENT:trigger_instrument( false, ins, trk, nte )
end

--bang! (this should sound the C-4 note with a volume of 127, with the instrument and track selected.)
trigger_note_on(48) --nte=48 --> C-4
--trigger_note_off(48)

You can copy it to your main.lua (and then adapt it for your control)…

You can create a single function for trigger_note_on and trigger_note_off, using a local state (a table) and making sure you always use the same instrument index and track index, to stop the note triggered.

The OSC code has specific specifications. It is not the responsibility of the programmer of Renoise. Its use may not be so intuitive, but once you build your OSC code, you simply call a function with several variables to play notes. And that’s it all.

If you need to understand the OSC code, you should read the related documentation: http://opensoundcontrol.org/spec-1_0

Hi Raul,

I’m already sending OSC messages in exactly the same format as your bit of code there. I’m using a client to send a selector string and table of tables containing OSC integers to trigger a note_on in Renoise.

I get a note playing from Renoise when i set the user-options server running, but the same message will not fire a note from Renoise when i create a server with the xrnx OSC API.

I am able to get the server to print the message on receipt, but no notes play. And i am also getting a funky return from the client:send() method, which is supposed to return [bool, string or nil] but is instead giving me [selector string just sent, bool] so wtf is going on there i don’t know.

I’m about ready to give up on this shit and go elsewhere with my work as i cannot be wasting time dealing with inaccurate documentation and misinformation. Why does the server not get any response from the program when i instantiate it from a script? The whole point of the script is that it should be usable and do what it purports to offer. Where i am making the error if i am indeed making one is beyond me to discern, and the fact i can get an undocumented return value is worrying and makes for impossible debugging situations when the sender is opaque like in this instance.

Why Renoise doesn’t give a passthrough option for MIDI information captured by a script is baffling and results in hacky workarounds like this which seemingly are unstable at best or just do not work at all. Is the server only there to provide the option to receive OSC messages within a script to be captured by the tool? Does it not pass the selectors on to the Renoise internal OSC protocol? Hence requiring stupid things like having to request users activate an option in the settings menu? If this is the case it’s pointless. Having all these nice options and being curtailed in usage of them by weird dead-ends and incomplete documentation is frustrating, especially when you’re doing it for the love of the thing, no money to be gotten, a bit of fun and challenge. kick in the balls

I understand your frustration. Activating the OSC Server in the Renoise Preferences is necessary. The user must know that he has that “open channel” to receive messages. From the API there is no way to change this state from off to on, therefore, it is the user who must do it and the tool to report it.

Otherwise, I have no problems at all with OSC. It works perfectly on the tools I do. I use Windows 10.

You only have to do 2 things:

  1. Use the correct code.
  2. Activate the OSC Server (Reonise / Preferences / OSC) and ensure that it is well bridged (the correct port, through the UDP protocol).

And of course, knowing where to invoke functions.

OSC and dealing with sysex for MIDI input are the two most complicated things you’ll find to make LUA tools.

Have you tried the code I’ve written to you before?

Renoise says:

By enabling the OSC server in Renoise, you will allow remote control of Renoise and thus also your computer over the network. Please do this with care.

Ok, I think i got this.

The server part of the API is just there if you want the script to catch everything. You can connect to it just like you would connect to the Renoise native server (the one being set up in preferences). You can send via OSC directly to the Renoise native server, if you want, or to your custom server in the script. And you can do both at the same time, by using unique ports.

So, why not just connect to a server that you start from within your script, and then forward everything to the native server that you don’t need the script server to catch? Use a blacklist approach that filters out only the stuff you need to catch, and forwards the rest. (or the opposite) This is done with quite simple code in your script.

It seems flexible and simple, but perhaps the docs haven’t clarified it that well.

PS. You don’t even need the native OSC server enabled for setting up servers inside your script. Also, you can actually use it to transmit data between different tools in realtime.

Thanks Joule, that’s pretty much what i figured was happening. The fact that i have to fuck about with OSC to be able to play notes in Renoise via a script is what is pissing me off actually. I just need to be able to audition notes played from the Push device i am scripting for, so that adding notes with it makes sense, but that it requires asking people to set up stuff in the preferences is not very portable in my opinion. I can understand users won’t want open sockets being added by scripts which could go wrong and fuck the program about, but god damn, make it possible to activate notes by a script API method then!!! It can’t be that difficult.

I want to make a control surface that is designed as an instrument into a playable controller for Renoise. That, as far as i see it currently, is impossible with the API as it stands today. OSC is an unnecessary layer of complication to add to something which frankly already works, ie. being able to capture MIDI input inside a script. Why is there no way to pass on note information??? I need to be able to manipulate the note input so that it doesn’t just send notes chromatically from the pads, which would make no sense. It needs to be worked with and mapped in the right way depending on the mode. This is what scripting is for. But i’ll be buggered if i can be arsed to do all the OSC forwarding hoopla when i don’t even NEED it in the tool, as i have no OSC coming in from anywhere else.

Anyway, thanks for your help boys. I’ll just leave it at this for now. You both understand what i’m trying to do and i think the only answer is that it’s not worth the fuss.

Well, having to enable the native server is a well known ‘obstacle’ indeed.

But wouldn’t this work quite well?

  1. set up your own server in the script
  2. use this server to recieve data
  3. modify any values as you see fit. change pitches under certain conditions, for example.
  4. forward the result to the native renoise server. Let all ‘irrelevant’ stuff just pass thru to it.

It makes sense to me that you can start a ‘silent’ server in a script, as an intermediary before you send it to the Renoise engine.

Frankly, I think that the OSC specific part of it all is a rather small thing. The main work would be to design and make proper classes for handling different modes, et c.

To be frank, activating the OSC server in Renoise is a step that the user would only do once. It is a checkbox that will remain activated in the next Renoise session. Only, you must make your CSO code in your script is ready. The user does not have to configure anything, just “activate one thing” once.

Particularly, I don’t like to “disturb” the user with this step. I have had problems with some users myself when using my tools with OSC. They had the damn checkbox disabled. But OSC is a messaging method. It is not intended only to “play notes”. The available API has no way of controlling the sound, either with the notes or with the phrases. You can only do it through OSC.

For me, the only problem is not even the badly explained documentation. There is no “sample tool” prepared for a particular case that comes in the download package. That is, it provides users with a single-button window tool that is capable of using OSC to play (and edit) a note (or several). If there are several ways to do it, provide more separate examples.

Particularly, OSC works frankly well. But I recognize that when I approached this subject, I lost a lot of time researching, because it is not clear at all. Thanks to the forums I solved it. With the documentation it was impossible.

Docs are very concise and technical, as they should be. That’s what tutorials are for. Too bad noone makes them :slight_smile:

Because they cost a lot of work, and only 4 crazy people would see them.

1 Like

Yeah, don’t worry about copying code. Experienced coders do it all the time. But do make try to understand it ; ) Understanding may only (or best) come from running and hacking and seeing what happens when you change things.

Anyways, I have a number of tools, many (most?) using OSC.

Go copy some code : )

Hack around to get an understanding of what’s going on.

BTW, a value i having user-level or tool-level OSC is that you can have multiply servers running on different ports for different things.

At least one of my tools will listen for OSC on one port, and it it doesn’t know what to do with it then it forwards the message to the default Renoise OSC server. So you can one OSC server that accepts all the standard Renoise stuff (stop, start, etc.) as well as stuff you added for a particular tool or whatever (and without having to edit you GlobalOscActions.lua file)

Thanks for all the answers guys. But it seems i need to explain a little further: i have no need of OSC for anything beyond realising it is the only way to get Renoise to play notes programmatically via a script. I do not need to send OSC from anywhere else, nor for there to be any method of control which OSC would be in charge of receiving. I am sending normal MIDI data from a physical controller into my Renoise script. This works just fine and i have it all going lovely. The problem arises in needing to forward that data on to Renoise to trigger note events. I cannot just have the surface appear as a normal MIDI device in the Renoise prefs because it sends the wrong notes, or at least, it is not useful for what i am doing, so i must manipulate that data in a script. I find it a horrendous hack to have to instantiate a whole OSC level within my code just to be able to trick Renoise into playing a note as if it’s coming from outside the program’s environment instead of just being able to straightforwardly pass on the information after mutating it in the way i want.

So, long story short, it’s possible to do, but requires a lot of fuss to get set up, bloating my code, and making it impossible to create a ‘plug and play’ device for other people to use, people who know nothing of OSC, nor would ever need to. Not what i’m after. I am fully aware of what i am doing, and i can see what the code is doing, and i don’t like it.

I do agree it’s a bit bloated - perhaps because it’s powerful. The user actually has to make sure that two OSC connections are properly configured to make stuff like this work. One would have been bad enough. I guess the presumption is that a user of something as fancy as OSC is also a bit technically minded. And I’m not sure of how else it would work, retaining some kind of respect for how the protocol layers are designed.