20 characters of yes :slight_smile:

1 Like

Is this fix applied in the latest update? I’m converting a script I’m working on to use the new clock system, and when I change the source from internal to crow it sounds rather imprecise, like a jittering clock or randomly missed triggers.

yes, the fix is released since norns 200604. could you share the (ideally minimal possible) code that exposes the issue?

The entire script is pretty minimal, bear in mind this is my very first attempt, trying to learn norns, lua, and a new sc engine at the same time, so I would not be surprised if I made an error somewhere.

--  arpcore
--  v0.01 by robotfunk
--  
--  /////////////////////
--  / / / / / / / / / / / / /
--  /  /  /  /  /  /  /  /  /  /  /
--  /   /   /   /   /   /   /   /   /   /   /
--  /    /    /    /    /    /    /    /    /    /    /
engine.name = 'KarplusRings'
musicutil = require 'musicutil'
er = require 'er'
mode = 1
toniclist = musicutil.NOTE_NAMES
tonicnum = 1
mode_name = musicutil.SCALES[mode].name
m = midi.connect()
m:start()

function init()
  generate_notes()
  noteIndex = 1
  length = 3
  stride = 1
  offset = 0
  pulses = 1
  End = 16
  generate_gates()
  --general params
  --params:add{type = "option", id = "use_karplus_rings", name = "Use Karplus Rings", options = {'yes','no'}, default = 1}
  params:add{type = "option", id = "send_midi", name = "Send MIDI Out", options = {'yes','no'}, default = 1}
  params:add{type = "option", id = "send_crow", name = "Send Crow Out", options = {'yes','no'}, default = 2}
  params:add{type = "option", id = "crow_outs", name = "Crow Outputs", options = {'1/2','3/4'}, default = 1}
--  params:add{type = "option", id = "send_osc", name = "Send OSC Out", options = {'yes','no'}, default = 2}
  params:add_separator()
--KarplusRings params
  --cs.AMP = cs.new(0,1,'lin',0,0.5,'')
  params:add_control("amp","amp", controlspec.new(0,1,"lin",0,0.5,""))
  params:set_action("amp", function(x) engine.amp(x) end)
  --cs.BPF_FREQ = cs.new(100,10000,'lin',0,0.5,'')
  params:add_control("bpf_freq","bpf_freq", controlspec.new(100,10000,"lin",0,0.5,""))
  params:set_action("bpf_freq", function(x) engine.bpf_freq(x) end)
  --cs.BPF_RES = cs.new(0,4,'lin',0,0.5,'')
  params:add_control("bpf_res","bpf_res", controlspec.new(1,2,"lin",0,0.5,""))
  params:set_action("bpf_res", function(x) engine.bpf_res(x) end)
  --cs.COEF = cs.new(0,1,'lin',0,0.11,'')
  params:add_control("coef","coef", controlspec.new(0,0.75,"lin",0,0.11,""))
  params:set_action("coef", function(x) engine.coef(x) end)
  -- cs.DECAY = cs.new(0.1,15,'lin',0,3.6,'s')
  params:add_control("decay","decay", controlspec.new(0,15,"lin",0,3.6,"s"))
  params:set_action("decay", function(x) engine.decay(x) end)
  --cs.LPF_FREQ = cs.new(100,10000,'lin',0,4500,'')
  params:add_control("lpf_freq","lpf_freq", controlspec.new(100,10000,"lin",0,4500,""))
  params:set_action("lpf_freq", function(x) engine.lpf_freq(x) end)
  --cs.LPF_GAIN = cs.new(0,3.2,'lin',0,0.5,'')
  params:add_control("lpf_gain","lpf_gain", controlspec.new(0,3.2,"lin",0,0.5,""))
  params:set_action("lpf_gain", function(x) engine.lpf_gain(x) end)
--seq params
--length
  params:add_control("length","length", controlspec.new(1,16,"lin",1,3,""))
  params:set_action("length", function(x) length=x end)--numNotes
--stride
  params:add_control("stride","stride", controlspec.new(-16,16,"lin",1,1,""))
  params:set_action("stride", function(x) stride=x end)
--pulses
  params:add_control("pulses","pulses", controlspec.new(1,16,"lin",1,1,""))
  params:set_action("pulses", function(x) pulses=x end)
  params:bang()
  clk=clock.run(pulse)
end
  
function clock.transport.start()
  clock.cancel(clk)
  clk = clock.run(pulse)
end

function clock.transport.stop()
  clock.cancel(id)
end
  
function pulse()
  while true do
    clock.sync(1/4)
    step()
  end
end

function step()
  if (gates[noteIndex]) then
    engine.hz((440 / 32) * (2 ^ ((notes[noteIndex] - 9) / 12)))
    if (params:get("send_midi")==1) then
      chan=1
      note=notes[noteIndex]
      m:note_on(note, 100, 1)
      noteoff_delayed = metro.init()
      noteoff_delayed.time = .05
      noteoff_delayed.count = 1
      noteoff_delayed.event = 
        function(chan, note) 
          m:note_off(note, 0, 1)
          metro.free(noteoff_delayed.id)
        end--function
      noteoff_delayed:start()
    end
  end
  noteIndex = noteIndex + stride
  while (noteIndex < 1) do
    noteIndex = noteIndex + length
  end
  while (noteIndex > length) do
    noteIndex = noteIndex - length
  end
  redraw()
end
  
function key(n,z)
  if ((n==2) and (z==1)) then 
    mode = mode + 1 
    if (mode>#musicutil.SCALES) then mode = 1 end
    generate_notes()
  end
  if ((n==3) and (z==1)) then shuffle(notes) end
end

function enc(n,d)
  if (n==1) then 
    params:delta("stride", d) 
  end
  if (n==2) then 
    params:delta("length", d) 
    generate_gates()
  end
  if (n==3) then 
    params:delta("pulses", d) 
    generate_gates()
  end
end

function get_value_by_shift(t, index, shift)
  index = (index + shift - 1)%#t + 1
  return t[index]
end

function redraw()
  End = (pulses + length) -1
  if (End>16) then End=End-16 end
  screen.clear()
  screen.level(15)
  screen.line_width(1)
  
  for i=1,16 do
    screen.rect( (i-1)*8+1 , 19, 7, 8)
    if (gates[i]) then screen.fill() else screen.stroke() end
  end
  
  screen.level(6)
  for i=1,16 do
    screen.rect( (i-1)*8+1 , 19, 7, 8)
    if (i==(noteIndex)) then screen.fill() else screen.stroke() end
  end
  --screen.stroke()
  --screen.move((pulses-1)*8+3, 25)
  --screen.text(">")
  --screen.move((End-1)*8+3, 25)
  --screen.text("<")
  screen.move(0,40)
  screen.text(mode .. " " .. musicutil.SCALES[mode].name)
  screen.move(0,8)
  screen.text("stride: "..stride)
  screen.move(0,60)  
  screen.text("length: "..length)
  screen.move(92,60)
  screen.text("pulses: "..pulses)
  screen.update()
end

function cleanup()
  -- deinitialization
end

function generate_notes()
  notes = musicutil.generate_scale_of_length(36+(tonicnum-1),musicutil.SCALES[mode].name,16)
end
  
function generate_gates()
  gates = er.gen(pulses, length)
end

function shuffle(tbl)
  for i = #tbl, 2, -1 do
    local j = math.random(i)
    tbl[i], tbl[j] = tbl[j], tbl[i]
  end
end

--copied from foulplay, not (yet?) used
local function rotate_pattern(t, rot, n, r)
  -- rotate_pattern comes to us via okyeron and stackexchange
  n, r = n or #t, {}
  rot = rot % n
  for i = 1, rot do
    r[i] = t[n - rot + i]
  end
  for i = rot + 1, n do
    r[i] = t[i - rot]
  end
  return r
end

thanks, so clock-wise the code looks mostly fine, except these lines:

function clock.transport.stop()
  clock.cancel(id)
end

i think you could call clock.cancel(clk) here instead.

crow clock takes signal from input 1. does you script sound of of sync with the signal on input 1?

Yeah I should fix that stop code, it has never been called so that can’t be the problem.

And yes, I’m feeding input one of crow with a square LFO, and it sounds jittery.

Last night I noticed that even in clock’s internal mode, very rarely I hear a jump as well, like in once every few minutes. When I select crow as clock source it jitters all the time.

hey, unfortunately i don’t have a crow to reproduce the problem, but just to exclude the problem with crow and/or external LFO could you try doing something like

crow.input[1].change = step
crow.input[1].mode("change", 1.0, 0.1, "rising")

i.e. triggering step on the actual change event from the crow input?

1 Like

That works and is tight as. Obviously I’d like to be able to actually clock it with real triggers. Tried a bunch of sources that I use to clock the rest of the modular, and everything results in the same jitter. Could it be that crow remembers some setting, or is even still running a script that interferes with the ability to clock input 1? I only barely tried crow months ago and I’m just returning to it now, so I’m not ruling out any silly mistakes on my part.

To follow up on my last post : if I do a crow.clear() at the beginning that should fix any weirdness from settings or scripts on crow, right? Because I just tried that and the jitters are back instantly.

That’s strange, does crow clock subdivision affect the jitter in any way?

1 Like

Not really, the jitter is pretty consistent in all subdivisions.
Looks like others are experiencing the same issue with crow clock inputs and MIDI as well : Norns: clock
I’ll try MIDI as source next, but I’m expecting the same jitter.

can you try removing redraw() from your step code as well and share the results?

Sure, just tried it, same problem. By the way I’m using MIDI as clock source now from my MPC1000, same problem as input from crow, with or without redraw() in my step function. Will try Ableton Link next.

Another thing I just noticed : clock.transport.start() doesn’t seem to be called, stop() works fine.
EDIT:
Interesting, the problem does not seem to happen with Ableton Link. And when using Link, clock.transport.start() works fine. Maybe the MPC1000 uses SPP and Continue similar to OP_1 as described in Norns clock and MIDI transport (via OP-1) .

So internal clocking has a very rare hickup but no major issue. Crow and MIDI clock input is wonky. Link is fine and dandy. Is creating a GitHub issue the best way to flag this?

that would be 2 different issues them. one is - it seems the latency of handling the message is a bit too high for the current clock sync tuning, which i think can be fixed by adjusting the tuning. are you comfortable compiling code on norns to test the potential fix?

second - midi continue should be treated as start (i’m sure my op-1 sends start as well though).

I’ve never tried to set up the toolchain to do so, but I think if I throw enough time at it I will manage :slight_smile:
I have to add that I’m on Fates so that might be a slightly different process.
Now that I discovered the issue happens on MIDI clock as well, you could download the script I posted and see for yourself, but looking at the other thread it looks like it happens everywhere the new clocks system is used.

so can you try changing 2000 to 1000 at the following line:

and rebuilding matron?

upd: i’ve been testing midi clock with the op-1 before and wasn’t getting the problems you described with other scripts.

1 Like

OK it is too late now here for thinking, so I’ll have a look at what it involves to compile changes to norns/fates, if it does not look too intimidating I’ll give it a go.

not much of a different process, but remember the github repo fates pulls from is different so you can’t easily switch to a norns dev github branch, etc.

you should be able to just do the following to recompile norns after making changes to the C code

cd ~/norns
./waf configure --enable-ableton-link
./waf

If that throws an error somewhere, DM me and I’ll help you through the process (might need to re-pull some dependencies, etc.)

1 Like