Norns: tutorial

Tutorial

This is a crash-course tutorial to get started with the sound computer Norns, follow the discussion here. If you would like to contribute, visit the repository.

Connection

You need to get the device online and accessible to your WIFI network.

  • Add your router details to the device, in WIFI > ADD.
  • Once the device is online, the IP field will display the Norns’s IP, example 192.168.128.109.

  • To edit code(IDE), open Maiden by going http://norns.local/ in your browser.
  • To transfer files(SFTP), open your SFTP client and connect to the Norns’s IP, with username we and password sleep.
  • To install new projects(SSH), open a new terminal window, type ssh we@norns.local, with password sleep.

You are now connected to the device, via the IDE, FTP and SSH.

Setup

In your terminal window, while being connected via SSH.

  • Move into the code folder where scripts are typically held with cd dust/code.
  • Install this tutorial on your device with git clone https://github.com/neauoire/tutorial.git
  • Move into the tutorial folder with cd tutorial.

You are now ready to use this tutorial.

Basics

In Maiden, reload the window to make sure the tutorial files are visible, and look at the first example file of this tutorial.

  • Navigate to the first example with code > tutorial > 1_blank.lua.
  • Run the script by clicking on the play button to the top right of the Maiden window.
  • Look at the blank screen, there is nothing to see.
  • Look at the logs, at the bottom of the browser window in the console, it should display init and redraw.
  • This example is not interactive.

You have run your first Norns script, from Github, via Maiden. The basic functions are as follow

function init() # On launch
function key(id,state) # On key press
function enc(id,delta) # On knob turn
function cleanup() # On Quit

Interface

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

  • Navigate to the second example with code > tutorial > 2_interface.lua.
  • Run the script by clicking on the play button to the top right of the Maiden window.
  • Look at the screen, notice the basic lines being drawn.
  • This example is not interactive.

The interface is draw by a combination of methods from the screen object. The screen always must be cleared, and updated between changes.

screen.clear()
screen.move(10,10)
screen.line(30,30)
screen.update()

Interaction

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

  • Navigate to the third example with code > tutorial > 3_interface.lua.
  • Run the script by clicking on the play button to the top right of the Maiden window.
  • Move the crosshair by turning the two knobs to the right of the device.
  • Look at the screen, notice the crosshair moving.

The interactions are triggering the key(id,state) and enc(id,delta) functions, remember to redraw the interface after an interaction. The key state is a value of either 1(key_down), or 0(key_up). The knobs delta is a value of either -1(counter_clockwise), or 1(clockwise).

function key(id,state)
  print('key',id,state)
end

function enc(id,delta)
  print('enc',id,delta)
end

Animation

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

  • Navigate to the fourth example with code > tutorial > 4_animation.lua.
  • Run the script by clicking on the play button to the top right of the Maiden window.
  • Change the animation modulation by turning the two knobs to the right of the device.
  • Look at the screen, notice the screen being updated automatically.

The animation loop uses the metro object, the 1.0/15 time parameter sets the reload to the rate of 15fps.

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

Output

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

  • Navigate to the fifth example with code > tutorial > 5_output.lua.
  • Run the script by clicking on the play button to the top right of the Maiden window.
  • Change the rate of notes by turning the left knob, to the right of the device.
  • Change the frequency of these notes by turning the right knob, to the right of the device.
  • Listen to the generated sound by connecting a pair of headphones to the leftmost 1/4" input.

To send messages from Norns to Supercollider, use the addCommand method in your supercollider synth.

this.addCommand("amp", "f", { arg msg;
  amp = msg[1];
});

Input

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

  • Navigate to the sixth example with code > tutorial > 6_input.lua.
  • Run the script by clicking on the play button to the top right of the Maiden window.
  • Send audio to the device by connecting a sound source in the fourth 1/4" input.
  • Change the amplitude of the outgoing sound by turning the one of the two knobs, to the right of the device.
  • Listen to the modified sound by connecting a pair of headphones to the leftmost 1/4" input.

To receive the audio signal, you need to start polling with poll.set("amp_in_l"), and bind a callback function to p_amp_in.callback.

p_amp_in = poll.set("amp_in_l")
p_amp_in.time = refresh_rate
p_amp_in.callback = function(val) 
  print(val)
end
p_amp_in:start()

Available polls

  • amp_in_l / amp_in_r
  • amp_out_l / amp_out_r
  • cpu_avg / cpu_peak
  • pitch_in_l / pitch_in_r
  • tape_play_pos / tape_rec_dur

Midi

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

  • Navigate to the seventh example with code > tutorial > 7_midi.lua.
  • Run the script by clicking on the play button to the top right of the Maiden window.
  • Send midi to the device by connecting a midi instrument via usb and pressing a key.
  • Listen to the resulting note by connecting a pair of headphones to the leftmost 1/4" input.

You can control which device is sending midi, by selecting it in SYSTEM > MIDI, this example will receive midi from the first device, and send midi to the second device.

To receive the midi signal, you need to connect to the midi interface with midi_signal = midi.connect(), and give it a method to get the event, like midi_signal.event = on_midi_event.

midi_signal = midi.connect()
midi_signal.event = on_midi_event

function on_midi_event(data)
  msg = midi.to_msg(data)
  tab.print(msg)
end

OSC

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

  • Navigate to the eight example with code > tutorial > 8_osc.lua.
  • Run the script by clicking on the play button to the top right of the Maiden window.
  • Send osc to the device by addressing norns.local at port 10111.
  • Look at the incoming data.

To receive the osc signal, you need to connect to the osc interface with osc.event = on_osc_event, and give it a method to get the event, like on_osc_event(path, args, from), the from parameter is an array including the ip and port.

Grid

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

  • Connect a Monome grid device, and select it in SYSTEM > GRID.
  • Navigate to the ninth example with code > tutorial > 9_grid.lua.
  • Run the script by clicking on the play button to the top right of the Maiden window.
  • Touch a button on the grid and watch it light up on the Norns.
  • Move the light across the monome by turning the knobs on the Norns.
  • Change the brightness by pressing the buttons on the Norns.

To communicate with the grid, you need to connect to the grid with g = grid.connect(), and give it a method to get the key event, like g.key = on_grid_key. The led(x,y,brightness) method allows you to toggle LEDs.

g:all(0)
g:led(1,2,15)
g:refresh()

Include

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

  • Navigate to the example and run the script.
  • 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 shown in 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

Parameters

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

  • Navigate to the example and run the script.
  • 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"))

Softcut

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

  • Navigate to the example and run the script.
  • Connect a sound source, into the 4th input(Input 1) from the left of the Norns.
  • Fade between the sound source and the recorded loop, by rotating the leftmost knob.
  • Change the offset of the recorded loop, by rotating the second knob.
  • Change the length of recorded loop, by rotating the rightmost knob.
  • Change the playback speed, by pressing the rightmost buttons.

Softcut is a recording tool built into the Norns, it makes it easy to record and play samples at various speed. Generally, the softcut methods will operate on a voice, softcut has a maximum of 6 voices, so the functions are always in the format of .method(voice_id,value). For example, here a few methods used in this example file:

  • softcut.fade_time(1,0.25), set the fade in/out length of the sample, of voice1, to 0.25.
  • softcut.rate(1,2), set the rate(speed), of the sample to play, at speed 200%, also -2 would play in reverse.
  • softcut.loop_start(2,0.5), set the offset of the sample, of voice2, to 0.5.
  • softcut.loop_end(3,4), set the limit of the sample, of voice3, to 4.

You can also explore each one with the Softcut Studies.

Useful Commands

  • If you need to restart the device, type ;restart in Maiden.
  • If you want to take a screenshot, type s_export_png("/home/we/screenshot.png") in Maiden.

Useful Links

I hope you enjoyed these simple examples, good luck!

67 Likes

awesome, thanks for doing this!

small feedback on the last example: in general stuff in _norns is by convention intended for system use and not use in user scripts.

in particular the vu polls use a weird scaling for coarse visualization (currently linear post-2.0, and will return to ‘reverse audio taper’ in a near update)

(i think lua API docs are stale on this point, apologies, we need to clean it up)

you can use the polls named amp_in_l, amp_in_r, amp_out_l, amp_out_r which will give exact running-averaged amplitude.

there are also input pitch polls - they need to be explicitly enabled (off to save CPU by default), mentioned in study 5

LMK if you notice issues with this stuff. it’s in a slightly funky state after the 2.0 migration - should really be handled in crone process but these polls are the one thing area still handled by supercollider AudioContext class (which is otherwise obsolete)

3 Likes

I will update! I’ve picked bits of pieces off Norns’s core/menu that’s probably why. I will also display and draw the incoming pitch. Is it possible to get an array of FFT values so could draw the incoming waveform?

1 Like

nah, sorry. we’re using Pitch.kr supercollider UGen, which does autocorrelation stuff internally, and at no point exposes the magnitude spectrum directly. (i think if you peek at the FFT buffer used by Pitch.kr it will be the weighted autocorrelation of the power-spectral-density… or something.) some day i would like to add a phase-vocoder component to crone that is analogous to the softcut component.

(i won’t have time to do anything like that until after this summer at least.)

for purposes of spectrum visualization on a small screen it would likely make more sense to use a coarser FFT anyway (compared to what is needed for good pitch tracking,) or (even better) a polyphase analysis filterbank.

a 128-point FFT with mag spectrum could be added to SC pretty easily… but our poll system is farily primitive so getting those values into lua is also not trivial.[*]

another (medium-term) task on my plate is replacing crone <-> matron IPC with shared memory or something else, more efficient than UDP for things like this, or for buffer waveform thumbnails… with supercollider we are limited to OSC transport and working with arrays there is a bit cumbersome and not super efficient.

[*] that said, there is actually a “waveform” poll type which currently isn’t being used for anything. it’s designed for 128-byte arrays since that matches the norns screen size… so, go for it if you want!

4 Likes

For what it is worth there is a strong desire to expose basic project management within maiden such that folks don’t immediately have to jump into the deep end with ssh and git directly…

As others have already hinted in various threads work is underway to bring functionality much like @its_your_bedtime library script directly into maiden.

UPDATE: It is also worth noting that if one can successfully use http://norns.local to connect to maiden then ssh we@norns.local should also work (instead of typing in the IP address directly)

5 Likes

@zebra no problem, I’ll be keeping an eye out for progress on this, maybe even contribute to this feature. Updated the polling scheme, it’s a lot faster now too.

@ngwese Oh! thanks I will update with norns.local, lots cleaner. If maiden gets the ability to push/pull repositories I will update that step.

Added a tutorial for midi receive and send, although I never managed to get midi send to work properly, could anyone give me a hand with the example 7, the m.note_on method does not seem to be working for me?

Sending needs a colon instead of a dot

midi_signal:note_on(60,127)

1 Like

this is amazing

i’ll give some feedback when i have time to review and test properly

1 Like

for ppl really familiar with object oriented programming the colons thing in lua makes tons of sense, but I could see how that could get totally confusing for beginners. I wonder if this could/should be more standardised for the core library stuff. maybe that’s been brought up already.

Great stuff taking this on!

Just one small thing I noticed – I believe norns redraw is limited to 15fps (it definitely was in v1, haven’t tested in v2).

you sir, legend!!!

A post was merged into an existing topic: Norns: scripting

this is beautiful, thank you!

Oh, I’ll fix that. But I would love to see a definite answer, perhaps in a documentation somewhere?

Updated with an example of OSC receiver.

OSC

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

  • Navigate to the eight example with code > tutorial > 8_osc.lua.
  • Send it to the device by clicking on the play button to the top right of the Maiden window.
  • Send osc to the device by addressing norns.local at port 10111.
  • Look at the incoming data.

To recieve the osc signal, you need to connect to the osc interface with osc.event = on_osc_event, and give it a method to get the event, like on_osc_event(path, args, from), the from parameter is an array including the ip and port.

5 Likes

could you do one on midi clock in/out? :wink: and thank you so much for taking the time to do this! :raised_hands:

I’ve just looked at the implementation, I think I can make a simple implementation for the tutorial, but it’s a bit more involved than the rest of the midi example. I’m thinking about making it its own example file. Stay tuned :slight_smile:

edit: @sno Nevermind, that was easier than I thought, I will add it to the current tutorial shortly!

5 Likes

Added a grid example(video)!

Grid

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

  • Connect a Monome grid device, and select it in SYSTEM > GRID.
  • Navigate to the ninth example with code > tutorial > 9_grid.lua.
  • Send it to the device by clicking on the play button to the top right of the Maiden window.
  • Touch a button on the grid and watch it light up on the Norns.
  • Move the light across the monome by turning the knobs on the Norns.
  • Change the brightness by pressing the buttons on the Norns.

To communicate with the grid, you need to connect to the grid with g = grid.connect(), and give it a method to get the key event, like g.key = on_grid_key. The led(x,y,brightness) method allows you to toggle LEDs.

g:all(0)
g:led(1,2,15)
g:refresh()
9 Likes

thanks for this, i’m very interested in Norns, this will get me thinking more clearly.
Cheers

amazing work, thank you so much! :pray: