^^ crow: druid scripts

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.

9 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

Hello! Think this is the right thread for this.
I’m working on my first original Crow script, and not being a coder I’m probably gonna need a little help!

I know the functions I want (described below), but I wanna bring in some patch-programmability, and have the outputs function a lil differently depending on what I/O I have going.

Summary

Sequential Shift & Hold / Random Source / CV+Gate

Config 1:
In 1: Clock
In 2: Voltage
Out 1-4: S&H / Stepped Random
USB: None
Every Clock pulse, step the S&H incoming voltage to the next plugged output, and the previous output(s) are set to a random selection of Unison, 4ths, 5ths, and 8va above/below the last voltage. Acts as a S&H when only out 1 is used

Config 2:
In 1: Clock
In 2: None
Out 1: Random Voltage (ignores clock)
Out 2: S&H
Out 3: Quantized Random
Out 4: Quantized S&H
USB: None

Config 3:
In 1: Clock
In 2: Voltage
Out 1: Random Voltage (ignores clock)
Out 2: S&H
Out 3: CV from Norns
Out 4: Gate from Norns
USB: Norns

I’m planning on firing up Druid this week and getting some of the basic functions down, since the Crow studies iirc help one set up clocks and random outputs, but if others have insights as to how I could make this script easy on myself, please lemme know!

2 Likes

Here’s an approach that takes a clock signal & selects a mode based off the voltage window. If you attenuate the clock signal, you’ll still get tempo information but also be able to select different behaviors (these are filler)

-- switch between behaviors based on high voltage of clock
-- in1: attenuated clock
-- out1: voltages from mode a
-- out2: voltages from mode b
-- out3: voltages from mode c
-- out4: voltages from mode d
-- TODO implement clocking 


function init()
    input[1].mode( 'window', {0,1,2,3,4,5}, 0.1 )
end

input[1].window = function(state)
    -- skip the low window (clock is down)
    if state ~= 1 then  
        -- switch behavior based off state
        if state == 2 then
            mode_a(state)
        end
        if state == 3 then
            mode_b(state)
        end
        if state == 4 then
            mode_c(state)
        end
        if state == 5 then
            mode_d(state)
       end 
    end
end

function mode_a(state)
    output[1].volts = state
    output[2].volts = 0
    output[3].volts = 0
    output[4].volts = 0
    
end

function mode_b(state)
    output[2].volts = state
    output[1].volts = 0
    output[3].volts = 0
    output[4].volts = 0
end

function mode_c(state)
    output[3].volts = state
    output[2].volts = 0
    output[1].volts = 0
    output[4].volts = 0
end

function mode_d(state)
    output[4].volts = state
    output[2].volts = 0
    output[3].volts = 0
    output[1].volts = 0
end
4 Likes

Single signal can encode up to four values — offset, frequency, amplitude and phase. In theory it is possible to extract all four of them. Since crow has only two inputs, these techniques can be helpful to pass more than two values around.

2 Likes

Here’s a trigger sequencer script I’ve been using to jam out rhythms quickly…

11 Likes