Teletype Timing Issues, CV Edition :-)

Running Teletype version “TELETYPE 3.+ BETA E27F640”

I’m having issues with “chirpy” sounding notes and decided to investigate. I found the Teletype’s onboard CV outputs have 1-4ms of latency (i.e. 3ms of jitter)–4ms is more than enough to cause a “chirp” if it’s pitch CV whose change happens after an envelope trigger.

The following metro script demonstrates the problem for me:

TR.P 1 // triggers the scope
A FLIP
CV 1 V A
TO.CV * 2 V A // optional, gives a nice visual reference with a Telex output, which has virtually no jitter

I’m seeing issues with trigger output duration, too–it might be related. I can post scope images if needed, but the above script should do the trick.

Edit: now I’m seeing 2-6ms after a restart to get the firmware version.

The first thing I’d do is move the TR.P to the end…

2 Likes

That TR.P is used to trigger the oscilloscope, as noted in the comment. There is about 200us of latency for the Telex’s CV output to get moving (and a little less than 1ms for it to get to the commanded voltage). The Teletype’s onboard CV output seems to need no less than 1ms (or 2ms after the restart, which is puzzling) to get moving and as much as 6ms to do it.

IOW, adding a TR.P 2 after the CV command still means the CV change is happening after the trigger.

To clarify:

TR.P 1 // triggers the scope
A FLIP
CV 1 V A
TR.P 2 // <-- this happens 1-6ms before the above CV command

CV is updated (with slew) on a poll, which is why there is latency. the exact latency is in the source and tunable, but the value was chosen for CPU optimization. I recall (at some point) a non-poll update the CV NOW possible op?

2 Likes

Could this poll also be responsible for handling TR.P calls? I’m getting massive jitter on the turn-off side (i.e. high to low) depending on the metro timing. TR.TIME 2 1 applied to the above (and see next section about timing increments) results in “on” times from a few hundred microseconds to about 8 milliseconds, which explains some variation I’ve been hearing when hitting LPGs.

TR.TIME seems to only work in roughly 10ms increments: values of ( edit: not 1,) 11, 21, 31, etc. each have turn-on times about 10ms greater (plus the above jitter) than 2-10, 12-20, 22-30, etc. and there is no change for the in-between ranges at all.

Unlike the CV issue above, turn-on latency and jitter is negligible.

I usually add a little bit of delay to the trigger to offset this and it takes care of things for me. Only bothers me when the notes have a sharp attack.

1 Like

Doing:

TR.P 1
DEL 6: TR.P 2

… results in about 6ms of turn on jitter for me: TR 2 is delayed by 2-8ms with respect to TR 1 when the DEL is added.

This is a variable number that changes with different metro/trigger timings.

delays and trigger pulses are handled by a single timer. separate timers could be used, but this could potentially affect the overall system performance, so it’s a tradeoff - better overall stability at the expense of delays and trigger pulses not being super accurate. this timer runs every 10ms, which is why you’re seeing jitter between 1 and 10ms.

CV updates are also handled by a timer which runs every 6ms. i’m not actually sure why in this case it can’t just update the DAC immediately, this 6ms rate is more for handling gliding. looking at the code i wonder if there is a typo here: https://github.com/monome/teletype/blob/master/module/main.c#L897

it’s the function that gets called when CV op is used. the line above sets the ADC timer to 1ms so that it triggers on the next tick. but the ADC timer handles reading the input and the knob value, not sure why it needs to be executed immediately after - i wonder if it’s supposed to be timer_manual(&cvTimer) instead?

4 Likes

Was just wondering this. Here is a build with this patched to see if that improves the chirping: teletype.hex (581.2 KB)

You will see this up to 10ms jitter on falling edges from TR.P because of the 10ms timer mentioned above, but setting the gate high for TR.P, setting TR directly, and TR.TOG are memory-mapped GPIO accesses and are very fast. If you can use an external gate source with the length you want it’s possible you may see tighter timings using $.POL 1 3 to fire on both edges and setting TR 1 STATE 1, because script triggers are driven by GPIO interrupts, STATE 1 is a GPIO read, and TR 1 x is a GPIO write.

5 Likes

great, thanks for posting a test version!

thinking more about this, having 4 dedicated timers for trigger pulses - doesn’t seem like it would affect the overall performance much. for delays this would be 32 extra timers, so a different matter. but maybe a dedicated op for one 1ms precision delay? !DEL similarly to !M op?

3 Likes

Anything that prevents short triggers from disappearing entirely would be wonderful. There is quite an audible difference between 1ms and, say, 8ms pulses when hitting a lowpass gate, so the extra precision and consistency would also be nice. But low latency leading edge signals are good enough for a lot of things, so the TR outputs are quite useful no matter what even as-is.

The CV delay is more troublesome since it falls into a very audible range and can’t be compensated for in any reasonable way.

I will try to test @csboling’s firmware version tomorrow and report back my findings. Thanks to all for their efforts, you’re the best. :slight_smile:

Real Life delayed me a little, but I can report that there is now no jitter and very little latency for CV output calls! This is a pretty big win since it means I can reliably use the onboard CV outputs for pitch. :slight_smile:

I am still seeing the effects of the 10ms loop for TR.P and DEL calls: if the metro is set to a period divisible by 10, everything looks pretty stable–any other value results in tons of jitter.

5 Likes

A little more data, this CV fix really sped stuff up:

I moved stuff to script 1 and ran the tests off an external clock so I could get a handle on total real world latency.

TR.P has maybe 40us (yes, forty microseconds) of off-to-on latency. I’d need to fire up my Rigol scope to see the jitter, it’s too small for the O’Tool+.

CV state changes have c. (edit: double the times here, I thought I was still looking at 100us/div when it was 200us/div! Max observed latency is still a very good 1ms) 200us of latency + c. 500us of jitter after this update, which is very good when we take into account the very fast & clean transitions.

@csboling @scanner_darkly @tehn … nice work, thanks so much for all you do! Teletype keeps getting better and better :slight_smile:

4 Likes

thanks for taking the measurements - it’s nice to see some concrete numbers!

2 Likes

I’ve been tracking down a new issue: Teletype is dropping events randomly. At first I thought it was my imagination, but now I can replicate it pretty easily. The scene only requires this:

TR.P 1

… and a clock fed to the corresponding script. I am giving it a 150bpm train of 1ms pulses and not always getting a pulse on TR 1, as verified by observing scope traces. It gets much worse when I interact with the grid preview mode (without grid plugged in): holding down an arrow key to move around the grid buttons causes maybe 5% of TR.P calls to fall on the floor. Dropped TO.ENV.TRIG calls are where I first noticed the issue, so it’s not limited to just the onboard TR outputs.

This is not related to the above jitter issue: I tried various values for TR.TIME in the 50-100ms range, which is well over the threshold where pulses got jittered out of existence.

Edit: running the above firmware revision and have (2) TXo and (2) TXi connected via backpack.

Is it related to the length of the incoming triggers? I have a vague recollection of another module (or several?) failing to catch short-duration triggers, and 1ms is pretty short.

1 Like

Good call, I just tried it with various input pulse lengths from 5-10ms and did not observe any dropped TR.P calls with lengths of 8ms or more. I’ll keep testing, perhaps it’s related to the 10ms cycle time?

I was using 1ms pulses to get a narrow spike on the scope for an upcoming presentation on generative sequencing techniques–this is the first time I’ve had an issue (that I noticed, anyhow) with short pulses, however.

I did a quick test and noticed dropped TR.Ps from a fast M script (M! < 20ms) when moving the cursor around the Grid pages.

I also noticed that sending a clock of certain speeds to trigger a TR.P from script 1 caused the length of Gates out of 1 to be (cyclically) variable. I wonder whether incoming triggers of certain speeds can cause dropped triggers - eg when they are spaced as a certain division/multiple of TR.TIME.

If that makes little sense, I’ll make a video later to illustrate.

1 Like

This could be a regression in 3.1.0 since the handler now checks the pin state to decide whether or not to fire the script depending on your configured $.POL. If the input is low again by the time that check happens it might not fire the script.

2 Likes