More Lua but with Norns
A while ago I wrote a small Norns app which just fulfilled a particular requirement for me and was very simple. I can’t even remember what it was now! But having studied Lua for the last week I am hoping to relieve the world of Norns development and see if it makes more sense (whereas before I was basically copying scripts and hacking things around.
I have Visual studio code setup to remotely access my Norns, although in the past I’ve used terminal Emacs to edit via ssh. I think I probably prefer the second method but I’m trying to use VSC more so that’s what I’m doing. Every time I modify the script I need to reload it on the Norns which only takes a second but it would be nice to find a way to do this automatically in the future.
The most basic Norns script?
-- zobbo/test just testing
-- 240407
function init()
print('Hello World')
end
That’s it. All it does, on loading the script, is print “Hello World” to the console. The comments at the top are what are displayed when you select the scriptm within the Norns. So let’s make a noise. I’ll omit the comments from now on.
engine.name = 'PolyPerc'
function init()
engine.hz(330)
end
When you load this script it makes one sound. Very much like a ping on a submarine, especially as I have it going through reverb on the Norns and I need to remember how I did that.
engine.name is the Super Collider engine I’m using. engine.hz plays a note through the engine at 330 hz, which is an E4, the note E on the fourth octave. it’s easy to play an E on the fifth octave by just doubling the hertz or on the 3rd octave by halving it. And of course we can just do engine.hz(330*2)
or engine.hz(330/2)
to achieve that.
We can play all those three notes together by just putting them one after the other:
engine.name = 'PolyPerc'
function init()
engine.hz(330)
engine.hz(330*2)
engine.hz(330/2)
end
And from that we could deduce we could play a chord like E minor. If we talk to our friend at ChatGPT they will tell us that the actual Hz values to make an E minor chord are specifically as follows:
engine.hz(329.63)
engine.hz(392)
engine.hz(493.88)
And putting those into our init function does indeed play that chord on loading the script. This specifying of hertz values is quite interesting but a little, er, technical. Norns has a library you can use called musicutil and we can use this instead.
Now when we move to music util this is giving us midi note numbers instead of hertz values. The note E4 is midi note 64 and therefore the three midi notes to generate the E minor chord are midi note numbers 64, 67 and 71.
local MusicUtil = require "musicutil"
engine.name = 'PolyPerc'
function init()
local e4 = 64 -- Midi note number for e4
chord = MusicUtil.generate_chord(e4, 'minor')
-- THIS DOES NOT WORK!!
engine.hz(chord)
end
The above doesn’t work. We’re throwing a table of three midi note numbers to the hz method of engine which expects a floating point value in hz. But musicutil has note_nums_to_freqs
so we can do:
function init()
local e4 = 64 -- Midi note number for e4
chord_midi_notes = MusicUtil.generate_chord(e4, 'minor')
chord_frequences = MusicUtil.note_nums_to_freqs(chord_midi_notes)
for key,frequency in pairs(chord_frequences) do
print(key, frequency)
engine.hz(frequency)
end
end
That plays us an E minor chord when the script starts. You can see I am printing the value of key and frequency there, the output is
1 329.62755691287
2 391.99543598175
3 493.88330125612
I will assume that is very accurate 🙂
We can go a bit further. This is not right but it does a job. To be honest I’m putting it here so I can revisit it in the future. We need to have a better way of creating chords and of making them play against a tempo. But I am happy with this for the moment.
-- zobbo/test just testing
-- 0.0 @zobbo
local MusicUtil = require "musicutil"
engine.name = 'PolyPerc'
function play_chord(midi_note_table)
chord_frequences = MusicUtil.note_nums_to_freqs(midi_note_table)
for key,frequency in pairs(chord_frequences) do
print(key, frequency)
engine.hz(frequency)
end
end
function init()
local e4 = 64 -- Midi note number for e4
chord_midi_notes = MusicUtil.generate_chord(e4, 'min')
play_chord(chord_midi_notes)
os.execute("sleep 0.25")
local g4 = 64+3
chord_midi_notes = MusicUtil.generate_chord(g4, 'min')
play_chord(chord_midi_notes)
os.execute("sleep 0.25")
local something = 64+5
chord_midi_notes = MusicUtil.generate_chord(something, 'min')
play_chord(chord_midi_notes)
os.execute("sleep 1")
local something = 64-2
chord_midi_notes = MusicUtil.generate_chord(something, 'maj')
play_chord(chord_midi_notes)
os.execute("sleep 0.25")
local something = 64-2
chord_midi_notes = MusicUtil.generate_chord(something, 'maj')
play_chord(chord_midi_notes)
print(os.date())
end