there is a poll for current phase of each softcut voice, arbitrarily quantized

cool, thanks! I’ll have to see if I can poke around and display it.

I had somewhat missed Traffic! It sounds gorgeous! Thanks @carvingcode for directing me to it. @ypxkap Will you update it for norns 2.0? Lovely lovely work.

4 Likes

thanks! it’s up for 2.0 over here—

something is broken in my hacky mod of the traffic speeds, tbd if it should be fixed or removed. really want to redo the sequencer and integrate the arc in…

3 Likes

(I hope this is still the right place for this post… last one was March 19.)

Is this the best way to detect when encoders stop?

I added the visual indicators to make it easier to grok.

3 Likes

got another puzzle if anyone can spare some cycles for me on this fine friday:

-- say we have a metatable Foo...
Foo = {}

function Foo:new()
  local f = setmetatable({}, { __index = Foo })
  f.items = {}
  bar_mixin.init(self)
  return f
end

-- and we have this concept items that mixins register:
function Foo:register_mixin_item(key, value)
  self.items[key] = value
end

-- which we then want to return dynamically with the key
function Foo:get_item(key)
  return self.items[key]
end

-- ...that has the bar "mixin"
bar_mixin = {}

bar_mixin.init = function(self)

  self.register_mixin_item("baz", self:get_baz()) -- (issue is here)

  self.fnord = 1337

  self.get_baz = function(self)
    return self.fnord * 2
  end

end

-- the issue
foo = Foo:new()
foo:get_item("baz") --[[

### SCRIPT ERROR: load fail
/home/we/dust/code/dev/dynamic.lua:25: attempt to call a nil value (method 'get_baz')

]]

(EDIT was missing a return f in the init())

seems like this should work. do i need to use assert() or load() or something with string concatenation? i need to be able to call functions from strings.

i’m not 100% following but i think you’re calling .get_baz before it’s defined, no ?

ohh are you trying to send the function into the Foo object ? in that case flip the order so the function is defined but send in the variable for the function w/o calling it —> self.get_baz

@andrew closer! had to reorder some things but this helped. i can’t get it to call now:

-- metatable
Foo = {}

function Foo:new()
  local f = setmetatable({}, { __index = Foo })
  f.items = {}
  bar_mixin.init(self)
  f.setup_bar(f)
  return f
end

function Foo:register_mixin_item(key, value)
  print(key, value)
  self.items[key] = value
end

function Foo:get_item(key)
  return self.items[key]
end

-- "mixin"
bar_mixin = {}

bar_mixin.init = function(self)
  
  self.setup_bar = function(self)
    self.fnord = 1337
    self:register_mixin_item("baz", self.get_baz)
  end

  self.get_baz = function(self)
    return self.fnord * 2
  end

end

-- implementation
foo = Foo:new()
print(foo.fnord)              -- 1337 OK!
print(foo:get_baz())          -- 2674 OK!
print(foo:get_item("baz"))    -- function: 0x531428 ???
print(foo:get_item("baz")())  -- ERROR: dynamic.lua:33: attempt to index a nil value (local 'self')

print(foo.get_item("baz")(foo)) ?

1 Like

image

tfw u realize foo:baz() == foo.baz(foo)

1 Like

I think it should actually be print(foo:get_item("baz")(foo)) because the above is definitionally equal and will lead to the same error.

@tyleretters I think the problem is in how you defined bar_mixin.init, since you haven’t used get_item yet, the table won’t be have been made yet, so you don’t run into the error until you use foo:get_item("baz") (this is all wrong actually). There should be a way around needing the function wrapper, I just haven’t found it yet.

1 Like

yeah, my great scott was in response to foo:get_item("baz")(foo)! still super fugly but it gets the job done.

@andrew i am embarrased at how long it took me to understand the : vs . mystery.

related i’m doing something similar that i’d love to do better:

I guess I don’t understand why, but self.items[key] is actually a function (bar_mixin.init?), so changing that line to return self.items[key](self) makes foo:get_item("baz") work how I think you want it to.

good catch @Tyler !

like many lua subtleties it confused / weirded me out at first but I’ve grown pretty fond of it compared to javascript which throws in a this implicitly (which doesn’t lend itself to the workaround case that fixed this error)

also I’m being picky here but Foo isn’t strictly speaking a metatable, more like a class or protoype depending on how you think about it. { __index = Foo } is the metatable, which just stores a metamethod to point back to Foo for null members of the object / subtype (metatables are also solidly in the weird/confusing category, but they are sooo powerful)

yea I was thinking something along those lines - the get/register methods feel a little trivial / unnecessary in this example but it depends on if they play a bigger role outside of this example I suppose

4 Likes

ayeeee, thank you for that. i sorta wish the lua community would just put a stake in the ground and tell us what this type of pattern/object/class/construct/prototype/supertype ought to be called.

yeah this is absolutely a contrived and over simplified example. in the context of arcologies there are dozens of these mixins and adding a new one requires updating business logic in several different places. there are only going to be more mixins as time goes on and i want all the concerns to be encapsulated in one file for maintainability and ease of new developers adding their own. right now i’m working on replacing this.

2 Likes

You can make pretty simple table with functions like

funcs = {
   ['example_func'] = function bla bla return smth end, 
   ['another_func'] = function bla bla end
}

and then just call it like that -

funcs['example_func']()
2 Likes

right, that’s where this is headed. had to figure out how to get the mixins to register themselves to funcs! thank you.

and thank you @Tyler and @andrew. you totally made my day. :3

4 Likes

what are some best practices around clock.sync(1)? i’m about to add midi delays into arcologies and changing that integer to a smaller float is terrifying.

delays are going to be the typical tempo divisions or time (ms)… so how low can i go with the float? if i want to go as fast as 32nd notes and .1 second, i dunno what that would make my least common denominator. or should i spin up a separate clock that isn’t coupled to my global playback just for the delay management?

this might get best visibility in Norns: clock, but i’m running 32nd-syncing clocks in cheat codes without any trouble. clock.sync(1) is always a 1/4 note, so clock.sync(1/8) should get you stable 32nd :slight_smile:

i like having separate clocks for different components, especially because it makes management + debugging easy.


where you’d want a lowest common denominator is if you want different steps to have different note length values – this gets into step sequencer territory, which might not be relevant to your goals, but it’s something that tripped me up at first because I thought that changing the clock.sync() argument would magically let me sequence.

programmatically changing the clock.sync() value is not the same as saying “gimme a quarter note and then an eighth note and then a whole note and then a…” – it’s just saying that “when the next clock tick of x value happens, then do y”.
so if you clock.sync(1/4) and then immediately clock.sync(1/2), it won’t be the same as a 16th note followed by an eighth note, because you’ll have to wait a 16th note until the next eighth note clock value occurs. does that explanation make sense? it’s wibbly and just based on hours of being like “the heck is happening?”, so happy to put better words into it.

instead, I’ve had success using a “runner” that has an awareness of a given step’s duration at the lowest common denominator, driven by a clock at the lowest common denominator. so, if i want to have a 1/4 note step followed by an 1/8th note step followed by a 1/32nd note, i’d do something like this:

function advance(target)
  while true do
    clock.sync(1/8)
    if runner == 1 then
      play_note_or_whatever()
    end
    if runner == step_duration[step] then
      step = step + 1
      runner = 0
    end
    if step > seq_end_point then
      step = seq_start_point
    end
    runner = runner + 1
    end
  end
end

where step_duration is a table of durations (defined by how many ticks it takes to count a step value at the desired resolution). so, a 1/4 note step in a sequencer that goes down to 32nd notes would mean 8 ticks, because 8/32 = 1/4.

hope that helps?