lfos controlled by Arc? :slight_smile:

1 Like

Ideally I’d want many different shapes :slight_smile:

(Dreaming of Batumi functionality)

Game on

7 Likes

Feeling rather stupid right now as this is so simple in other languages… is there a neat way in Lua to split a string into a table?

My specific issue right now is trying to split a string of numbers for example '123456' into a table of numbers like this: {1, 2, 3, 4, 5, 6}

function split_str(str)
   local tab = {}
   for i=1,string.len(str) do
      tab[i] = string.sub(str, i, i)
   end
   return tab
end

core libraries in lua are pretty minimal. but there is some string manipuation and fairly powerful pattern matching.

http://www.lua.org/manual/5.3/manual.html#6.4

i’d be interested in providing more string and collection utilities out of the box, whether by pulling in some stuff from luarocks or by collecting our own structured utility suite.

the problem with pulling in stuff (imho) is that there a number of ways to shoot yourself in the foot performance-wise by doing this. for example the nice list-comprehension lib in Penlight is unsuitable for soft-realtime use.

1 Like

thanks! i was being lazy thinking that there would be a function for it already!
My vote would be for utilities to be gathered on here - i haven’t hugely explored luarocks but having some recent frustrations with npm makes me err on the side of a more focused utility suite

Playing around right now and I notice Fugu gives me trouble at 19-20fps or faster. Look at the grid_redraw() function there (line 488), it seems to be calling grid:led() many many times.

Am I correct that calls to grid:led() take a roundtrip to libmonome and back?

(total shot in the dark guess) Maybe lots of grid:led() calls together are piling up in libmonome?

Curious if you notice issues at higher BPMs?

unless i’m misunderstanding, no - LED commands go one way, key events come the other way.

not literally. we don’t actually use individual led_set calls. all updates to led’s are with led_level_map. all changes to invididual leds are buffered, and only entire (dirty) quads are transmitted. (on grid:refresh()).

this is generally more efficient, but you can make a script that renders it totally inefficient; for example by doing grid:led() followed by grid:refresh() over and over, instead of using grid:refresh() sparingly once you are done building the LED state you want to see.

we could expose more of the libmonome API to lua if people want to use actual col and led commands. but then they have to know about the bandwidth tradeoffs inherent therein and choose the appropriate method.

(i’d like to move this conversation to GH or at least the scripting thread if we want to continue it.)

2 Likes

given matron (lua) is calling libmonome directly (so there is no OSC overhead) this seems pretty efficient as is. i think adding more libmonome API hooks to lua would be confusing and offer not much benefit.

2 Likes

I’ve been using this little script to give me a grid of program change triggers, which when working with other hardware gives me one-press change of programs/patterns across all the machines. It’s offensively under-utilizing Norns but it’s been surprisingly enjoyable to jam out with in the studio, or quickly get a fresh pattern on all the devices to work something out.

I feel a bit icky about how I’ve mapped 0-127 across the rows and columns of the grid and I feel like there is a more elegant way than how I’m doing it here:

--
-- launchctrl
--
-- provides grid launching
-- of midi program changes

local g = grid.connect();
g:all(0);
g:refresh();

local mo = midi.connect()
local program = 0
local program_change_channel = 7

-- Receive program change event and highlight related grid button
mo.event = function(data)
  d = midi.to_msg(data)
  if d.type == "program_change" then
    local row = math.floor(d.val/16)
    local column = (d.val - row*16)
    row = row + 1
    column = column + 1
    g:all(0)
    g:led(column, row, 8)
    g:refresh()
  end
end

-- Send program change event
g.key = function(x,y,z)
  if z == 0 then
    g:led(x, y, 3)
    local pattern = (y-1) * 16 + (x-1)
    mo:program_change(pattern, program_change_channel)
    g:refresh()
  end
end

any suggestions?

1 Like

You could use util.linlin() as a way of making the mapping between the 1-16 of the grid rows and columns and the 0-127 of midi look a little cleaner. The math that a remapping function is doing is basically the same but I definitely find it easier to read when I come back to it.

1 Like

@markeats had an amazing looking sample player in development (saw a demo on Instagram some time back). Any updates on this?

4 Likes

I put it on hold while the dust settled on norns 2.0 (…sorry)
Picked it up again recently and hope to put out a beta in the coming weeks.

14 Likes

This question might be very general, but is there a way to declare x.y boundaries on the grid for certain keypress events. Example: you can only do a certain commandon the top half of the grid, and a different command on the bottom half.

Set up the ranges you want to use in a table. Then check key press events against those ranges.

@lazzarello are you planning on updating FM7 for v2? no pressure of course, I just miss using it…

edit: seeing this https://github.com/monome/dust/pull/300 - is it just a matter of cleaning up a few things/paths and posting to Library at this point?

1 Like

Looking for just a little bit more help with trying to get rid of the filtering from softcut in MLR as it doesn’t play well with drum sounds and overall just doesn’t match the signal if im doing live looping type stuff. What I’m trying to achieve is just grabbing the dry pass thru signal. @zebra has pointed me to adding these functions

for i=1,4 do 
  softcut.filter_dry(i, 1)
  softcut.filter_lp(i, 0)
  softcut.filter_hp(i, 0)
  softcut.filter_bp(i, 0)
  softcut.filter_br(i, 0)
end

but I get (error:init) when I try to load MLR after adding.

Im a bit confused with where to place them as line on line 358 it reads:

for i=1, TRACKS do

If I change TRACKS to what was suggested, I get the error. If I just add the functions below line 358 sitting with the rest of the softcut functions I also get the error.

In practice I’d like to just understand how to be able to do this to any script that utilizes softcut so I get exactly what I record into the buffers returned out of the outputs. The aliasing is not as much of an issue for me as the difference in sound im currently getting.

Here’s an audio example to show the difference I’m getting in sound: The first half is the pass through signal and the 2nd half is softcut playback

I can’t test at the moment, but I would just put the @zebra code at the bottom of the init function. looks like after line 428.

@Prnts, it would look like this. lines 427 - 436

  gridredrawtimer:start()
  dirtygrid = true
  for i=1,4 do 
    softcut.filter_dry(i, 1)
    softcut.filter_lp(i, 0)
    softcut.filter_hp(i, 0)
    softcut.filter_bp(i, 0)
    softcut.filter_br(i, 0)
  end
end
1 Like

Will give it a try now thanks

to my ears, the sound you’re getting seems consistent with the settings in awake/halfsecond (midrange bandpass with gentle rolloff)

which is why its important to initialize these things in mlr. otherwise state is persisted from the previous script that touches these parameters.

be aware also that it’s possible to (soft-)clip the input to a given softcut voice, which will also subtly affect timbre.

finally, remember that mlr/softcut voices are mono; L/R are summed before sampling. if you want stereo, you have to use two synchronized voices with appropriately configured input routing, buffer assignment, and output pan - mlr doesn’t do this (yet.)

3 Likes