Crow as precision gate delay?

Please, I need some help figuring out the following: I need to delay gate signals by a minimum of 140µs, plus a yet unknown amount of time I will find out later (please background information below), with the lowest amount of jitter possible.

In total I need to simultaneously delay four different gate signals by the same (ideally identical) delay time. I would like to use two crows to do this, so that each crow handles two gate delays. This means I can’t set the delay time via CV, but need to enter it into the script, which is fine.

I have no coding experience with crow, so I don’t know
a) how to code this gate delay to minimize the delay time jitter,
b) if crow’s delay time resolution can be set in µs, and
c) how large the potential jitter of this delay time would be.

Thanks much in advance for any help!

Background

I use the ER-101 sequencer, which I really like. It sends one gate signal and two control voltages per step. But it sends the gate signal of a step before sending the two control voltages of this step.

This can cause glitches when e.g. using a step’s control voltage A to select a sample or change the synthesis model in a module, as the module starts playing a step with the current sample/synthesis model selected, then switches to the new sample/synthesis model.

To avoid these glitches I’d like to delay the gate signal, so that the module has time to load the new sample/synthesis model, before starting to actually play it.

I measured the time that elapses between the start of the gate signal and the start of the control voltages. This time seems to vary between 44µs and 144µs (please see pics below). So I figure I should be fine if I delay the gate by a minimum of 144µs, plus a yet unknown amount required by the module to load the sample/synthese model.

Although I’m sure this can be done with Crow, using two « Crow » for this sole purpose seems a bit expensive. Are you sure there ain’t no other way to achieve this « gate delay » ? If the « receiver » was an ER-301, I would suggest to add a « micro delay » unit before each Gate input. For another module, Math + a mult would also delay a gate and send it simultaneously to the four sample players.

Or if you want to use Crow for that, you could use only one Crow (up to four outputs I believe ?) or pair it with a TXo or any multiple output, it would easier to sync.

Edit: sorry I misread the 140µs required, but its a minimum, right ? :wink: (to me the 1.5ms announced below would be imperceptible).

(mod note: i moved your thread to Questions category as it seemed a better fit)

Crow only handles events (ie. calls into Lua) every 750uS, so it’s likely not going to do the super tight timing you’re expecting. Couple that to the potential jitter in the input handling and you’re looking at 1.5ms. This kind of micro-timing is something we sacrifice to have access to a full Lua environment.

This is to say, Crow will likely be underwhelming in your specific use case.

edit:
on further thought, this isn’t quite accurate. The inputs are only analyzed once per processing block. These blocks happen every 750uS. Once the input event occurs, Lua will run the input’s ‘change’ handler, which would then tell the output state to change. This output will be updated at the next processing block.

That means you’ll have 750uS latency (the time between sequential blocks) plus 750uS jitter.

3 Likes

are you using LuaJIT ? just curious !

MI Plaits and Braids both allow switching their synthesis modes via CV. And as both can be triggered independently, doing the delay via Math + multiple doesn’t do the trick – so each of the sequencer tracks needs its own delay.

Thanks. To paraphrase: Without any additional delay processing in code, a plain “gate pass-thru function” would produce delays with a minimum time of 750µs and a maximum time of 1.5ms, correct?

When I want to program such a pass-thru function, I would try to set up one function that looks for a “rising change at the input”, and another one that looks for a “falling change at the input”, and in case such a change is detected, it would call up functions that set the output to high or low respectively, correct?

Correct.

The whole script would be this, where inputs 1 & 2 are mapped to outputs 1 & 2:

function init()
  input[1].mode( 'change' )
  input[2].mode( 'change' )
end

input[1].change = function(dir)
  output[1].volts = dir and 5 or 0
end

input[2].change = function(dir)
  output[2].volts = dir and 5 or 0
end
4 Likes

I love crow and have a murder of them, but you might also want to look at a uO_C. It will cost you about the same as a single crow, and the Hemisphere Suite code is fairly easy to modify. Custom modules are not especially complicated. You could even build a UI for the delay amounts.

crow doesn’t use luajit.

Thanks, but as I am really a beginner in coding and do not have any coding experience yet in crow, please allow me some questions on your code:

  • Whenever a change is detected on input 1, be it a rising or a falling change, function(dir) is executed, correct?
  • function(dir) sets output 1 to either 5 volts if dir is true, or to 0 volts if dir is false. Correct?
  • But what is dir referencing to in the line output[1].volts = dir and 5 or 0? Is it the current value read at input[1]? If yes, how does the script “know” that dir “belongs” to input 1, as there is no reference to input 1? I looked for documentation about dir here and here, but also, no success.

Sorry for asking such simple things, but I fail to figure these out on my own.

good for humanising things :]

1 Like

The function being executed is* input[1].change, and dir is just that functions argument. It might make more sense to see the alternate style:

-- NOTE: this doesn't actually work
function input[1].change(dir)
  -- dir is the direction of the change
end

If you still want more info, you can read the crow Scripting Tutorial, which contains a walkthrough of crow’s basic syntax & functionality.

* Technically
  • Functions in Lua don’t have names. Instead they are just values that can be assigned to symbols. In this case we are assigning our function to the symbol input[1].change.

Thanks, but I read this, and did not find any information about dir, that’s why I asked. :slight_smile: Still not really clear about what dir really is, hope it will get clearer once I get my hands/head dirty with crow itself instead of just reading about it.

dir is just the argument to the function. It could be called anything: foo, val, direction… It is either true or false, and indicates if the function was called because of a rising voltage (true) or a falling voltage (false).

1 Like

But the value of dir is being used to decide whether to turn the output to 5 volt or 0 volt, right?

As far as I understand output[1].volts = dir and 5 or 0, it will output 5 volt if dir is true, and 0 volt if dir is false. Does this mean that dir is true if a rising change is being detected, and false if a falling change is detected?

1 Like

Yup! Exactly. I think I just edited my answer to clarify when you posted this haha, sorry for not being clear the first time!

1 Like

I re-read this passage in the Scripting Tutorial, and I think I found out why I had difficulties understanding the script @Galapagoose kindly provided above:

While the scripting tutorial explains that input[1].change creates a change event if the input changes, and that this change event calls a function, it does not mention that the state of the change is also transmitted.

As far as I believe to understand it now, if input[X].mode is set to either falling or rising, the state of input[1].change will always be true. But in case of mode both, the input[1].change will be true for the rising edge, and false for the falling edge.

Of course I am biased by my own experience, but I think it would be helpful for beginners like me to mention this state thingy in the tutorial. I believe this could be achieved by two edits to this part:

EDIT 1 Print the state instead of “Bang!” (it is already handed over to the function):

input[1].change = function(state)
    print(state)
end

EDIT 2 Replace this sentence

Those BANG! s tell us the input is correctly setup and detecting the clock signal.

with something like this:
"These true messages tell us the input is correctly setup and detecting the clock signal.

But where does this true come from? Each time change event is triggered, it not only calls the function, but also hands over the state of the input change to the function. This is called the argument of a function, and as we gave it a name (state), the print command inside the function can print this argument. We could have given it any name we like, but state seemed like the right one.

Please note that for the input modes rising and falling the state is always true, whereas for the input mode boththe state is true for the rising edge and false for the falling edge."

I hope my proposal does not come across as preposterous or impertinent, if it does, please accept my apologies. I just feel this could really help absolute beginners like me. Thanks for listening!

2 Likes

I think the difficulty in writing these documents is some people already know how to write code (or even Lua), and just want an Introduction to writing for crow. Meanwhile others want an introduction to scripting in general. I think we skew toward the former as there are ample tutorials (better than I could write) for writing Lua.

Furthermore, these things are much easier when you have the device in front of you, rather than just reading along. Being able to try things as you go often shakes out the details.

//

So. Absolutely I hear you. We should improve the scripting tutorial (I’ve never been happy with it myself). But perhaps we need to reconsider who is the audience for the tutorial. Are we

  • teaching to code for crow; or,
  • teaching to code for crow

But now we entirely off topic. There’s a whole thread about this exact issue.

3 Likes

I am not sure I agree that you “skew toward…people already know how to write code”, as looking at the tutorial you explain basic concepts like strings and variables.

Very true – however, the concept of an argument in a function is not explained in the tutorial, nor is the argument state used anywhere in the tutorial. In fact, as far as I can tell from trying it out, the examples shown in the tutorial would work if just function() had been used instead of function(state).
So any sort of pointer towards what this state is and what it could be used for would be a nice breadcrumb when going thru the tutorial (be it offline by just reading, or online with crow & druid at hand).

On the contrary: I personally found the tutorial to be quite helpful for somebody interested in, but new to coding, and wanting to know what crow can do and how it does it – except for the missing argument thingy. It’s a really useful introduction!

That’s a great goal, and I have read the thread you recommended, but to me this reconsideration sounds like it might involve a rewrite of the existing tutorial. Perhaps I am too pragmatic, or seeing the whole thing too much from my viewpoint only, but I think improving the existing tutorial with iterative small steps is more manageable.

Please pardon me, if I come across as arrogant, or trying to sound smart, or as trying to push my own agenda by desperately pushing my small change proposal into the tutorial. That’s not at all my intention. As I am not a native English speaker, I often struggle to bring my point across without sounding pushy. I guess all that I am really trying to do is to help to make access to this wonderful module a little easier for those beginners like me. Again, thanks for listening.

1 Like