Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fast output (re)sets trip up asl #209

Closed
pq opened this issue Oct 8, 2019 · 9 comments
Closed

fast output (re)sets trip up asl #209

pq opened this issue Oct 8, 2019 · 9 comments
Labels
bug
Milestone

Comments

@pq
Copy link
Contributor

@pq pq commented Oct 8, 2019

hacking up a monosynth patch and hit this while playing around w/ note retriggers.

here's a distilled test case that repros for me on norns w/ crow v1.0.0:

-- crow stress test

function init()
end

local function sleep(n)
  os.execute("sleep " .. tonumber(n))
end

function redraw()
  print('stressing...')
  sleep(2)
  crow.send('^^v')
  for i=1,100 do
    print('>>')
    crow.output[2].volts = 0
    crow.output[2].action = 'ar(1,5)'
    crow.output[2].execute()
    sleep(0.01)
  end
  print('done')
end

salient repl output:

...
crow:	[string "local Asl = {}..."]:115: attempt to get length of a nil value (field 'exe')
crow:	no asl active

/cc @trentgill @tehn

@trentgill
Copy link
Collaborator

@trentgill trentgill commented Oct 8, 2019

@trentgill trentgill added this to the 1.0.1 milestone Oct 8, 2019
@pq
Copy link
Contributor Author

@pq pq commented Oct 9, 2019

@trentgill
Copy link
Collaborator

@trentgill trentgill commented Oct 9, 2019

from user Dewb on lines. seems similar:
Sure, happy to help! Here's a distilled example. I'm using crow 1.0.0 but I saw similar effects prior to the update. I'm using the USB cable that came with crow in the leftmost norns USB port.

[details="test script"]

-- crow: example of setting action on clock

local BeatClock = require 'beatclock'
local clk = BeatClock.new()

local function step()
  local a = 4
  local b = 2
  local c = 5
  crow.output[1].volts = a
  crow.output[2].action = "pulse(0.1, 8, 0)"
  crow.output[2].execute()
  crow.output[3].volts = b
  crow.output[4].volts = c
end

function init()
  
  clk.on_step = step
  clk.on_select_internal = function() clk:start() end
  clk:add_clock_params()

  params:set("bpm", 91)
  params:default()

  crow.reset()
  
  clk:start()
  
end

function cleanup ()
  clk:stop()
end

[/details]

This works initially -- but the pulse sounds too fast, almost double-time. And eventually, sometime between a couple triggers and a couple minutes later, it starts failing with this error:

crow receive: [string "local Asl = {}..."]:115: attempt to get length of a nil value (field 'exe')
crow receive: [string "local Asl = {}..."]:115: attempt to get length of a nil value (field 'exe')
crow receive: [string "local Asl = {}..."]:115: attempt to get length of a nil value (field 'exe')
crow receive: [string "local Asl = {}..."]:115: attempt to get length of a nil value (field 'exe')
crow receive: [string "local Asl = {}..."]:115: attempt to get length of a nil value (field 'exe')
crow receive: [string "local Asl = {}..."]:115: attempt to get length of a nil value (field 'exe')

Sometimes, if the script is able to run for a while, periodic instances of this error also appear in the console. Sometimes the error is accompanied by a single missed pulse, but not always.

crow receive: [string "eval"]:1: attempt to index a nil value (field 'vooutput')

If I move the crow.output[2].action line into init() after crow.reset(), then I never see the local Asl errors, and the timing on the pulse is correct. I do occasionally still see the attempt to index a nil value message, but more rarely, sometimes with a dropped pulse also.

(In both cases, I also have about a 10% failure rate with hitting Play in maiden and having the script work, hitting Play again usually fixes it, maybe I need a delay after calling crow.reset()?)

@trentgill
Copy link
Collaborator

@trentgill trentgill commented Oct 9, 2019

for exe to be a nil value it likely means .action is not receiving the arguments correctly. i'm guessing this is because of strings getting corrupted as they're sent norns -> crow.

the fact that it seems fine for some seconds to minutes before starting to fail suggests a memory issue. should try calling print(collectgarbage('count')) before the .action to see if failure is correlated to a higher RAM usage reading.

@Dewb
Copy link
Contributor

@Dewb Dewb commented Oct 11, 2019

After some reflection I think the nil value (field 'vooutput') error is a bigger issue than the repeated .action assignment issue, as it results in dropped triggers with no workaround. Does it seem like the two symptoms are related, or should I file a separate issue?

@Dewb
Copy link
Contributor

@Dewb Dewb commented Oct 11, 2019

The plot thickens. I did some more testing, and it looks like moving the crow.output[2].execute() to the end of step(), after the other outputs' .volts are set is successful in preventing the nil value (field 'vooutput') error. But I still got dropped triggers.

So I took a look at output 2 on an oscilloscope to see if the pulses were not being omitted, or if they were and Plaits was missing them. Surprisingly, the output was not a 0-8V pulse train, but a -5/+5 triangle LFO! (I'm guessing that's the default linear ramp LFO action behavior.) Changing the .action set in init() seemed to have no effect -- pulse, ar, whatever, I always got a triangle wave. (I think this explains the double-time triggers observed in my lines post.)

I moved the .action back into step(), and that made the oscilloscope output look as expected. I figured the .action assignment immediately after reset() is getting lost somehow, and indeed adding a metro delay of about 680ms in between calling crow.reset() and crow.output[2].action = ... seems to make the action take effect.

So it looks like there are three interesting things going on here:

  • crow.reset() prevents scripts from communicating with crow for a period of time while it's removed/re-added.
  • Calling crow.output[n].volts = x immediately after a crow.output[m].execute() seems to interfere with the execution occasionally. (Filed this as #214)
  • Calling crow.output[n].action = f too often results in errors (the original issue.)
@trentgill trentgill added the bug label Oct 14, 2019
@trentgill
Copy link
Collaborator

@trentgill trentgill commented Oct 14, 2019

This seems like a race-condition between the slopes library & the usb-input, as it's norns specific. The same script running natively on crow doesn't have the problem.

Probably because either the .volts or .execute is creating a callback from C into Lua, right after the next command has been parsed & executed over USB.

I'm not entirely sure the best way to go about solving this. It seems the only reason to call .volts and .action in series is because you want to force the output to a predefined level before starting an envelope / lfo etc. Is the purpose pretty universally to set it to 0V before starting an envelope? If that's the desired behaviour, we can at least provide an additional argument to ar(attack-time, release-time, reset-to-zero) to make the issue less present until we can solve it.

@pq
Copy link
Contributor Author

@pq pq commented Oct 14, 2019

my use case was env re-trigger so something like reset-to-zero would do it!

@trentgill
Copy link
Collaborator

@trentgill trentgill commented Oct 23, 2019

Fixed by #238

@trentgill trentgill closed this Oct 23, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
3 participants