edit: I restarted crow and the script is working now. I’ll post my code when it’s done or ask if I get stuck on the next steps, thanks everyone!

4 Likes

Has anyone succeeded using infinite loops? I’m trying to get more precise timing than metros or input streams allow for. I crashed my crow yesterday and had to put it in bootloader mode to recover it, though, so I’m a little skittish about this.

EDIT: I should probably be a little more specific. I don’t want to lock up whatever thread is processing druid input. What I’m looking for is something like setTimeout(thisFunction, 0) in JavaScript where I can defer processing to all that stuff, and then come back and do some more work once it’s done.

This makes me think of a coroutine – would that fit the bill?

1 Like

Wow, yeah, that sounds right about on the nose - thanks! Am I understanding that using these lets the main crow internals manage the coroutine’s suspending, resuming, etc.? That’d be awesome because I don’t want to have to worry about that stuff.

I’m afraid this is probably not possible. Both Metros & the input streams run on hardware timers on the chip. They have sub-microsecond accuracy.

Perhaps you could explain where you’re experiencing the timing inaccuracy and we can help with figuring out how to improve that particular issue.

No this is not the case. Coroutines in Lua are not multi-threaded (nor is the microcontroller we use), they are instead ‘cooperative multitasking’ where they share processing with the main thread. Coroutines are great for conceptually separating tasks from a scripting perspective, but not from a processing one.

1 Like

Honestly I might be jumping the gun a bit here. I’m trying to make basically a polyrhythmic metronome. I had a metro per output, and they ran roughly at the set BPM, but they’d jitter a little bit, which did add a little flavor but wasn’t necessarily what I’m going for with this. If the BPM was changed (which changed the timers’ intervals) the phases would go way out of whack, which was also kind of neat but not necessarily desired.

Also I ran a quick test using time() on input.stream and I found that it was always 2-3ms later than expected.

I plan to refactor my script to use phase & threshold with just one metro instead of a bunch of independent, loosey goosey metros, so it will probably end up being more resilient to this stuff. This will probably greatly mitigate my concerns, but I’d definitely be better off knowing what my options are.

Ah, yeah – I don’t think coroutines will help with that. A coroutine is useful if you have an expensive process that you’d like to run on every metro tick but might sometimes take longer than the metro interval: you can break that process out into a coroutine and, inside that coroutine, periodically check that there’s still time left before the next tick, and yield if there isn’t enough, to resume on the next tick.

But it sounds like running a single metro at high resolution (the greatest common factor of your different metro times) would help with your phase problems. Multiple metros going out of phase when their times change is probably not due to inaccuracy in the metro timing, it’s probably due to the way metros work: if you change their timing between ticks, I don’t think the change takes effect until the next tick.

2 Likes

The single high-resolution metro (per @synthetivv 's suggestion) will certainly work. This should help solve the phase problems, and avoids the issues caused by changing metro times (I’m not entirely sure how / when those changes are applied). Having a faster metro will mean the weirdness that occurs when changing tempo will be reduced due to the smaller time window.

The other thing I’d ask about is what your outputs are? Are you just sending CV pulses, or is some other more complex event happening each beat? Could there might be another source of latency / jitter in the system?

1 Like

Single clock is what I arrived at too, thanks for confirming. I think I’m going to avoid GCF because I have a sneaking suspicion this will still be quite jittery and I’ll instead continually reconcile with the always-out-of-phase clock. I’m thinking of the old Air Force navigators’ motto my Dad told me: “Always off-course, always correcting” :slight_smile:

Good question, but I was just throwing simple pulses at the input, setting the pulses razor-thin in init() and then just invoking their outputs on the metro handler. I do a bit of math to figure out the new BPM but I wouldn’t think this would slow anything down:

function v_to_bpm(v)
    v = math.min(math.max(v, v_min), v_max)
    local ratio = (v - v_min) / (v_max - v_min)
    local new_bpm = bpm_min + (ratio * (bpm_max - bpm_min))
    local frac = new_bpm % 1
    new_bpm = new_bpm - frac
    return new_bpm + (frac < 0.5 and 0 or 1)
end

And then there’s calculating the new interval for the metro, even simpler:

function div_to_seconds(divisor)
    return 1 / (bpm / 60 * divisor)
end

The phase-reconciliation required a bit of rework but it actually feels cleaner. What I came up with is in here:

1 Like

My first script for Crow. A companion script for 0-ctrl. Outputs 1/2 are playing a melodic sequence with 50% chance of jumping to fifths. 3/4 are used for a slower moving bassline. Here to learn, input, ideas, challenges welcome!

function init()
  input[1].mode( 'scale', {0,3,5,7,10}, 12, 1.0 )
  input[2]{ mode = 'change', direction ='rising'}
end

count = 0

input[2].change = function()
  v = input[1].volts
  if math.random(100) > 50 then
    output[1].volts = ((v * 12) + 5) / 12
  else
    output[1].volts = v
  end
  output[2](pulse())
  count = count + 1
  if count >= 5 then
    count = 0
    output[3].volts = v
    output[4](pulse())
  end
end

Edit: not sure if this actually works is it should. I got wobbly off tune pitches when returning to the script. If someone wants to try and try it out, find what’s wrong I’d gladly get some input.

8 Likes

That’s cool. What do you patch from your 0-ctrl into crow generally?

1 Like

In this case pitch and the ‘clock’ gates. I will probably move this to Teletype when I get my hands on that.

1 Like

Looking pretty good already.
What i would do is change the random to 2 instead of 100. just a little bit shorter.
Place count in the init and make the “if count” a modulus calculations this way you don’t have to reset it all the time.

1 Like

Random 100 is for being able to choose freely on a percentage based possibility. I’ll definitely take on the modulus approach. Thanks.

Sorry,

Can someone please remind me what I’m doing wrong? Getting a syntax error when uploading scripts from druid?

Screen Shot 2020-10-29 at 7.54.40 AM

Be sure to launch druid from the same folder where the script files are. Or, in the current version of druid, you should get some filename autocompletion to help find where the script files are relative to druid’s working directory.

1 Like

I don’t get it. I set my bowery folder as the directory but the autocompletion in druid lists my home folder contents and not the bowery folder?

It should be relative to whichever directory druid was launched from. If your terminal is inside the Bowery folder when you run druid then that should be the working directory druid sees. So a typical session when you launch Terminal might go like this, using cd to change directories and assuming your Bowery folder is /path/to/my/bowery

$ cd /path/to/my/bowery
$ druid
r boids.lua

(The $ is not something you would type, I’m just using that to indicate the typical prompt when you’re in the Terminal shell itself, not yet inside druid)

1 Like

Thank you. I was forgetting the cd part when setting the path. All is working as it should.

1 Like