Mod matrix mod for Norns.
Provides a full mod matrix for Norns, as well as the ability to define arbitrary sources of modulation from your scripts or mods. Is the basis of toolkit as well as crowkit now.
I often find that it takes me three times writing a piece of software before it knows its shape well. The first try to allow modulation within norns was a draft PR to core that was interesting but wasn’t quite right — too complex for core, too rich for our blood, something. The second try was
toolkit, which was a focused effort to just get some modulation in there, with vague plans of allowing it to be used as a framework in the future.
This is the framework. This is the third time. I think it’s right. It feels right. It has a goddamn API, that’s even vaguely documented. It has a user interface. There are animated bar graphs. I have never been more excited for bar graphs.
This is a Norns mod providing a mod matrix, and an API to define modulation sources that can participate.
To use, make sure you have turned on the
matrix mod in Norns, and then from your code you can
local matrix = require 'matrix/lib/matrix'.
Using modulation sources
Go to the SYSTEM > MODS > matrix menu. This is the MATRIX menu, and it will look like the parameters menu. When you press K3 with a parameter selected, you will enter the sources menu for that parameter. You can select the source you desire with E2, then use E3 to adjust the depth of modulation. If you would like to zero out the modulation for the selected source, press K3.
Defining modulation sources in your code
matrix:add_binary(id, name) Adds a binary modulation source. Can modulate binary and trigger parameters; triggers will trigger on every modulation that is greater than zero. Can also modulate numeric parameters, like a pulse wave.
matrix:add_unipolar(id, name) Adds a unipolar modulation source. Can modulate numeric parameters.
matrix:add_bipolar(id, name) Adds a bipolar modulation source. Very much like unipolar, but can go negative. The difference is mostly interesting in calculating the total possible range of a parameter to show you.
Providing and reading modulation
matrix:set(id, value) sets the modulation for the given source id. Value should be 0 or 1 for binary parameters, between 0 and 1 for unipolar parameters, and between -1 and 1 for bipolar parameters.
matrix:get(id) gets the modulation value for the given source id.
Managing modulation depth
matrix:get_depth(param_id, source_id) returns the modulation depth of the given param by the given source. Returns nil if not modulated by this source.
matrix:set_depth(param_id, source_id) sets the modulation depth of the given param by the given source. I was thinking you’d use -1 to 1 but I guess you could use bigger numbers if the spirit moved you to do so.
For mod authors: Runninng code after init()
matrix:add_post_init_hook(function() ... end) adds a hook that will be evaluated post init. Use it to add parameters from your mod, or what-have-you.
matrix:defer_bang(param_id, priority) will bang the given param once the current thread yields. If it is called more than once for the same param, it’ll only be banged once. This is useful in post_init functions to only bang the parameters we’ve added since the main init finished, and it’s also useful in internal code to allow a lot of modulation sources to change from a lattice, but only re-evaluate the parameter itself once they’ve all been changed.
- You can use
params:lookup_param(param_id).priority = x to set the priority of any param. The
matrix framework will obey this priority. Useful, for example, to ensure that a sequencer first advances, and then resets, and then triggers a voice, if all three things recieve a trigger at the same time.
what a great mod!
so well thought out and i too am excited by the bars
ummmmmmmm this is amazing, you are a mage, holy hell. the animations!! super attuned workflow, such a joy to mess around with this morning – would still be in it if we didn’t have to build norns so other folks can join in on this type of wildly rad fun. can’t wait to keep digging in during the weekend!!
Tonight’s small update: displays binary and trigger params and binary sources as little heartbeating squares instead of bars. You can see the rhythms better this way.
The one feature that was in the previous way of doing
toolkit and was initially left out of
matrix is that in the original implementation of
toolkit the modulation depths were parameters, and could be themselves modulated or midi-mapped. In matrix, they’re managed by the mod matrix object.
Well, tonight I added a way to modulate or pmap them. If you are in the matrix menu choosing a source for a param, you can hit K1 + K3. This will mark the source with an asterisk, and you will then be able to find a depth parameter for it in the new
mod depths group that’ll appear in your script.
Secretly these are all hidden parameters that the mod installs in its post-init hook, and there are only 16 controls and 16 binary parameters, so you’re limited to 16 modulation depth parameters at a time. (there’s also a separator param, because the menu code gets confused when there’s a group with every param in it hidden). I didn’t want to add every single pair of source,target as a param of its own; that would quickly grow too large to manage.
So the Mod Matrix is causing my scripts to fail on init. It appears to happen regardless of which script I load. Here is some matron output. Any idea what is going on?
SCRIPT ERROR: init
/home/we/norns/lua/core/paramset.lua:187: attempt to perform arithmetic on a string value (field ‘group’)
/home/we/norns/lua/core/norns.lua:146: in metamethod ‘__sub’
/home/we/norns/lua/core/paramset.lua:187: in function ‘core/paramset.add’
/home/we/norns/lua/core/paramset.lua:222: in function ‘core/paramset.add_control’
/home/we/dust/code/matrix/lib/matrix.lua:425: in function ‘matrix/lib/matrix.add_modulation_depth_params’
/home/we/dust/code/matrix/lib/mod.lua:14: in global ‘init’
/home/we/norns/lua/core/script.lua:126: in function ‘core/script.init’
[C]: in function ‘xpcall’
/home/we/norns/lua/core/norns.lua:147: in field ‘try’
/home/we/norns/lua/core/engine.lua:91: in function </home/we/norns/lua/core/engine.lua:89>
EDIT: Nevermind, it appears you have to edit each script to be able to use the mod matrix. Is that correct?
Should not require editing your scripts at all. I’ve seen one other report of this, but haven’t reproduced it. I’ll take a closer look at your stack trace. What’s one script that does this, for reference?
I tried both lissadron and less concepts.
Fix was confirmed in personals, and is live now. It turns out the API for
add_group was (slightly) changed between 2.7.1 and the current main branch, I’ve been running the main branch, and reading the code rather than the docs to use the API. Lesson taken — don’t mix mod/script development and core development
This mod is absolute genius and opens up some crazy new worlds on Norns! Instantly one of my favorite new things!
In addition to the great script, this is a fantastic thread title. A very appropriate tribute, I might say.
Thank you so much @sixolet for this absolutely amazing Mod … I just tried it with a couple of scripts and it works perfectly, wow!
Actually I have to restart Norns every time I select a different script to use, as the Mod page is black the first time I enter it after choosing a new script, but that’s it.
I swear, I swear, this morning I was thinking about how cool
would have been modulating Norns internal reverb aaaand you got me covered!
Thanks again, this is a total game changer for me and for a lot of other user, I bet!
You should be able to freely choose new scripts and it should still work. The behavior you’re seeing is a bug, I’ll try to find and fix it. I definitely can get wonky matrix menus after selecting a new script, though I haven’t seen empty. Hopefully it’s the same issue.
EDIT: I think I fixed it? Probable fix pushed to github.
Uyeah wiggling the MIDI output device and/or MIDI output channel is great fun and makes simple, single-source scripts like Circles produce a whole lot of sound. Digitone and JD-08 duetto plays in the background while I am writing this.
Thanks for matrix (the word derives from latin for motherhood and womb by the way).
I think I’ve got a simple mod working using the Mod Matrix as a library, it’s so rad!
Here’s the code: GitHub - eethann/funkit
It does require one small change to the mod: add the ability to prioritize queued init functions. This is needed in order to ensure the order of execution of lattice ticks - the function operators want to come after any LFOs, etc. from Toolkit. I’ve posted one possible implementation as a PR, if helpful.
Cool idea!! Great source of pumping complexity out of simplicity. Is this like a mod-on-mod, or is the intent/desire to merge it to Matrix mod?
If I understand your tick-priority issues right, you might be able to get around it by setting
priority to 1 on the a and b param objects.
Edit: oh, I see, you are adding your own ticking lattice. If none of your functions are dependent on time, then you can do the calculations in the action of the a and b params, and you’ll get the result in a less computationally intensive way.
EDIT: It worked! Yay deleting lines of code. Closed the PR and I think FunKit is good for at least an alpha release.
@xmacex I’d be totally happy with either merge or separate small mods. I’m also working on an envelope mod source and a midi bridge. The envelope and functions could be candidates for inclusion in toolkit, but the midi bridge is probably best separate.
100% up to @sixolet, of course.
Thinking: the boundaries of norns are porous, and when it’s connected to MIDI devices, their parameters are norns parameters too. Also thinking: MIDI cc output from Matrix mod.
I was looking where would one add MIDI ccs as Matrix mod targets (
util:round(value * 128)) by reading outward from
MatrixMod:bang_all, but failed to find a good spot and confused myself wondering what a good UI for MIDI cc output would be
This mod is like a next version of norns already. Thank you!