minor correction: only in a 4/4 (or, more generally, n/4) song :slight_smile:

1 Like

ahh yes, this is clicking. the way i have it setup is i have this concept of ā€œregistering notesā€:

which handles duplicate notes etc. then i subtract from the note’s duration each beat:

sorta like each note has hit points and each tick is an attack against those hit points.

so the notion of a runner is exactly where i think i need to go. thanks for such a detailed explanation!!

2 Likes

here is my solution! Athenaeum


(EDIT can’t create new posts consecutively)

i’m working on a livecoding feature for the REPL. is there a way to call functions without typing the () afterwards? since this is live coding, terseness is highly valued! i don’t want to type ().

example = {}

function example.init()
  example.foo1 = function() example:bar() end
  example.foo2 = function() return example:bar() end
  example.foo3 = load("example:bar()")
  example.foo4 = example.bar
end

function example:bar() 
  print("hi")
end

example.init()

with all the above examples i have to append the parenthesis.

# script run
>> reading PMAP /home/we/dust/data/dev/untitled/untitled.pmap
m.read: /home/we/dust/data/dev/untitled/untitled.pmap not read.
<ok>
Engine.register_commands; count: 0
___ engine commands ___
___ polls ___
amp_in_l
amp_in_r
amp_out_l
amp_out_r
cpu_avg
cpu_peak
pitch_in_l
pitch_in_r
# script init
example.foo1
function: 0x3a08a8
example.foo2
function: 0x415de8
example.foo3
function: 0x415140
example.foo4
function: 0x3c4700
example.foo1()
hi
<ok>
example.foo2()
hi
<ok>
example.foo3()
hi
<ok>
example.foo4()
hi
<ok>

You should be able to do this with a metatable with an __index function — something like

example = {}
example.methods = {
  foo1 = function() ... end
  foo2 = function() ... end
}
setmetatable(example, {
  __index = function(self, index)
    if self.methods[index] ~= nil then
      return self.methods[index]()
    end
  end
})

Accessing the ā€œpseudo-fieldā€ example.foo1 would call example.methods.foo1() behind the scenes.

Not omitting the parentheses would then be like calling example.methods.foo1()(): calling the function, then calling its return value. I doubt the __index function has any way of knowing whether the field being indexed is going to be called as a function or not, so I don’t think you can make parens and no-parens behave identically (all the methods could return themselves at the end, but then calling them with parens would still mean calling them twice).

edit: if you don’t need them to return anything else, they could all return a dummy function that did nothing (or __index could return it, that would save you a few repeated lines of code). Then example.foo1() would work like example.methods.foo1(); noop()

5 Likes

ahhh thank you. this is super close now. any idea why the REPL spits out a nil afterwards? i tried messing around with __newindex too but it wasn’t doing anything:

example = {}
example.methods = {
  foo1 = function() print("this is magic!") end,
  foo2 = function() print("this is sparta!") end
}
e = setmetatable(example, {
  __index = function(self, index)
    if self.methods[index] ~= nil then
      return self.methods[index]()
    end
  end,
  __newindex = function(self, k, v)
    -- doesn't do anything?
    print(k, v)
  end
})

in the REPL, e.foo1 outputs:

this is magic!
nil

2 Likes

nil is probably the return value of the call to foo1().

__newindex should run if you call e.foo1 = 'bar' – if it doesn’t, that’s strange…

2 Likes

ahh, brilliant. this does the trick!! foo1 = function() print("this is magic!") return "" end

yes, e.foo1 = 'bar' behaves as expected. i misunderstood how that metamethod works.

THANK YOU!!

3 Likes

Hi. I’ve stumbled my way through a POC of my first norns script here. I’d appreciate being skewered with best practices.

3 Likes

Looks pretty good to me! You’ve managed to do a lot with a pretty terse script.

From a readability place it’s pretty good. The naming could be improved a little (some functions are unclear what they do). Perhaps write a single paragraph comment at the top describing the general structure as I skimmed the code but didn’t grok how it works just that it looks like i could understand it with some fine attention.

Personally when writing lua ternary statements (the and or idiom), I wrap the conditional in parens. It’s not necessary, and most Lua-familiar programmers identify it straight away, but it helps newer scripters to understand what’s going on IMO.

Looks nice though, and seems a fun game / music making crossover.

2 Likes

Thank you! These are great notes for my next pass.