Norns: tutorial

Right, I understand :slight_smile: I even saw them in the docs, but the add and remove names threw me off, I was expecting either on_remove, or removed — And, on_connect, or connected

1 Like

Note - attached used to exist in 1.0

see this PR

I threw up a Github issue about it. Not sure if it got removed as part of the vport updates, or for some other reason.

1 Like

yeah sorry - i dunno, have never touched the midi or vport stuff

i agree, those would be clearer names. i’ve no objection to changing them and i’m not aware of any scripts using this feature at the moment

1 Like

Since I don’t think the on_ naming convention is very common in the monome ecosystem, I would suggest:

Wishlist

  • .attached(), returns true or false if it’s added. This should be is_attached really.
  • .removed(), fires when removed.
  • .added(), fires when added.

Current Issues

  • When disconnecting the grid, the .remove callback is fired twice.
  • .add callback is never fired if the grid is already connected. Is there a way to tell if a grid is connected on the start of an application? .connect() doesn’t throw any error if there’s no grid to connect to.

ah, i remember something like that. thought it was fixed? can’t find the GH issue.

i think it should return nil?

Even tho the grid is not connected, for .connect(), I get:

all	function: 0x31da28
name	monome 128 m1000454
refresh	function: 0x328018
add	function: 0x354590
rotation	function: 0x325eb0
cols	16
rows	8
remove	function: 0x3c2d48
led	function: 0x346688

ok, mea culpa, thanks for investigating.

i shouldn’t make any more statments without being able to verify them - which i can’t do, i’m travelling without a grid for several weeks.

1 Like

That’s alright :slight_smile: Thank you so much for your time and energy to help me. I think I can find something that works somewhat-reliably with what you’ve showed me, and I will update as new tools become available.

1 Like

You can check my_grid.device (after connecting) to see if a grid is still there.

2 Likes

Yes, I ended up finding out.

I have updated the grid example with a check for a connected device.

I have also updated all examples with a pretty splash screen!

I am now working on an example that lets an application switch between available engines as explained here.

10 Likes

tiny heads up that util.clamp(min,max,input) is always available, no need to define it locally

2 Likes

I was actually confused about this. Do you need to require "util" or not to use util.clamp etc.?

If not - then why are some libs like musicutil not available all the time?

util and tabutil are used a lot and so they are added globally by default: https://github.com/monome/norns/blob/master/lua/core/startup.lua#L4

2 Likes

Since the tools seem to be shifting quite fast, I will try to make the tutorials as stand-alone as possible, I was thinking maybe a further tutorial, would be about including libraries.

Added a new tutorial that explore changing engines on the fly. Ultimately, this is something I would like to implement into Orca. Making this tutorial has raised a potential issue where loading a new engine doesn’t change the name of the selected engine.

Engine

In Maiden, look at the tenth example file of this tutorial.

  • Navigate to the tenth example with code > tutorial > A_engine.lua.
  • Run the script by clicking on the play button to the top right of the Maiden window.
  • Press the keys to cycle through all the available engines.
  • Engines with the .noteOn method will display as compatible.

Engines can be swapped within a lua script by using engine.name = name, and engine.load(name, callback). This allows to create projects that can alternate between sound engines. You can test for an available noteOn method with engine['noteOn'] ~= nil, once the engine loading is complete.

function select_engine(name)
  print('Selecting '..name)
  engine.name = name
  engine.load(engine.name, on_engine_load)
end

I made a little script that looks through all available engines and checks for noteOn, noteOff & hz methods. So you can play with any engine using a midi keyboard.

| name:           | noteOn | noteOff | hz | stopAll |
| Glut            | -      | -       | -  | -       | 
| PolySub         | -      | -       | -  | -       | 
| FM7             | -      | -       | -  | -       | 
| KarplusRings    | -      | -       | X  | -       | 
| Ack             | -      | -       | -  | -       | 
| SimplePassThru  | -      | -       | -  | -       | 
| InputTutorial   | -      | -       | -  | -       | 
| MollyThePoly    | X      | X       | -  | -       | 
| R               | -      | -       | -  | -       | 
| PolyPerc        | -      | -       | X  | X       | 
| Passersby       | X      | X       | -  | -       | 
| TestSine        | -      | -       | X  | -       | 
| SimplePassThru2 | -      | -       | -  | -       | 
| OutputTutorial  | X      | X       | X  | X       | 
| Why             | -      | -       | -  | -       | 
| None            | -      | -       | -  | -       | 
| Haven           | -      | -       | -  | -       | 
| Gong            | X      | -       | -  | -       | 
15 Likes

I think these different interfaces are a result of two different ways of managing voices in SuperCollider. e.g. PolyPerc doesn’t have any kind of voice management, it just fires off a one-shot amplitude envelope when you use .hz() I had to watch Eli Fieldsteel’s SuperCollider tutorials on YouTube to get a handle on that.

I wonder if under the current model we might need engine-wrapping “middleware” in lua that checks what an engine responds to and fires off the appropriate method — and your script introspecting all these engines is a good first start.

2 Likes

I could make a small tool that handles that if someone else want to implement engine switching :slight_smile:

I’ve added a series of links to the tutorial:

Useful Links

6 Likes

I have replaced the engine tutorial with a tutorial that explains how to include relative files.

Include

In Maiden, look at the tenth example file of this tutorial.

  • Navigate to the tenth example with code > tutorial > A_include.lua.
  • Run the script by clicking on the play button to the top right of the Maiden window.
  • Rotate the knobs to change the position values of the included file.

Including files with local view1 = include('lib/view1'), will first look in the directory of the current script. This allows using relative paths to use libraries local to the script. The returned value of the included script will be available in your main script file.

-- lib/target.lua
return {
  value = 5
}

-- main script
local target = include('lib/target')
print(target.value)
6 Likes

Added a new tutorial that explains how to add and read parameters.

Parameters

In Maiden, look at the eleventh example file of this tutorial.

  • Navigate to the tenth example with code > tutorial > B_parameters.lua.
  • Run the script by clicking on the play button to the top right of the Maiden window.
  • Press the leftmost key, rotate the leftmost knobs to the right, to see and modify the available parameters.

New parameters can be added with params:add, and read with params:get.

-- Add
params:add{type = "number", id = "number", name = "Number", min = 1, max = 48, default = 4}
params:add{type = "option", id = "option", name = "Option", options = {'yes','no'}, default = 1}

-- Read
print(params:get("number"))
6 Likes

Everyday I learn new things about Lua, and today I learnt how to utilize the :method() syntax and rebuilt the Include example to demonstrate how to use it.

Include

In Maiden, look at the tenth example file of this tutorial.

  • Navigate to the tenth example with code > tutorial > A_include.lua.
  • Run the script by clicking on the play button to the top right of the Maiden window.
  • Rotate the knobs to change the position values of the included file.
  • Press the key to select a different view.

Including files with local view = include('lib/view'), will first look in the directory of the current script. This allows using relative paths to use libraries local to the script. The returned value of the included script will be available in your main script file.

-- lib/target.lua
return {
  value = 5
}

-- main script
local target = include('lib/target')
print(target.value)

In lua, you can create new objects and methods as described the following snippet, notice how the self parameter is omitted when using the colon character before the method name.

obj = { c = 4 }

obj.add = function(self,a,b) 
  return a + b + self.c
end

obj:add(2,3) -- 9
8 Likes