void every_set_mod(every_count_t *e, int16_t mod) {
    if (mod < 0)
        mod = -mod;
    else if (mod == 0)
        mod = 1;        // lazy initialization
    e->mod = mod;
    e->count %= e->mod;
}

(Edit: why did I post this code? I need to slow down. :upside_down_face:)

You can check out the implementation details here: https://github.com/burnsauce/teletype/commit/8e9c3b78fec17a6bff051e76fffc727377273a58

The resultant behaviour right now is that the new mod is applied to the old value immediately before the increment. The count could be zeroed to “reset it” or set to mod - 1 to force it to fire immediately when the mod changes, or it could only do that if the mod shrunk beyond the current count.

Edit2: EVERY RRAND 1 10: should probably be equivalent to IF EZ % O RRAND 1 10:

3 Likes

i was leaning towards @tehn’s reasoning that an existing IF % already covers this and provides more flexibility. here is why: in addition to reset i’d want to add an offset, which would be an equivalent of doing IF EQ 2 % T 5, basically comparing the result of MOD with an arbitrary value. and i’m starting to like an idea of having a dedicated op for comparing the result of MOD op, like %= X Y Z or %EQ X Y Z being a shortcut for EQ Z MOD X Y.

having said that it does seem to fit well within the ecosystem, it’s concise, logical and non coder friendly, especially if supplemented with additional sync and skip, and i would most definitely use it if it became available!

this also makes me think again i’d love to see more variables. especially with a couple of new variations: variables that are local to a script (like I will be), this would work really well as a temp variable where you don’t have to worry about it being overwritten by another script, and a variable that acts like O except that it increments on each script execution, not each time it’s called (but otherwise would behave like O).

i would say that when your complete cycle is 38630800 long you won’t notice when some of the counters reset too soon :wink:

2 Likes

Just to clarify, what do you mean by ‘un-subbed’ SYNC?

OTHER.SYNC with no arguments, or just plain SYNC?

I had a dream that I connected my modular’s output to a ham transmitter’s input with an encoded message.

BE SURE TO DRINK YOUR OVALTINE.

5 Likes

one more reason to have offsets! :slight_smile:

I think that this advanced use case is covered by your already-concise use of IF, and would complicate EVERY et. al.

1 Like

apologies, bad wording.

just SYNC which applies to EVERY, SKIP, and OTHER instead of EVERY.SYNC etc. there should only be one sync for the whole system. also sync should take (require) an argument for the set sync count. sync should queue the next action of each individual every/skip/etc. it doesn’t make sense for sync to trigger all the every’s everywhere, or some such logic.

1 Like

SYNC won’t trigger the EVERY commands, just set the count.

I think that maybe SYNC -1 should set all EVERY counts to their MOD minus 1. I can see value in resetting the whole scene to that state.

2 Likes

Here is a build with the changes discussed up to this point.

teletype-2.1.0-rc1-every.zip (489.5 KB) (edit: oops! this one was built with -O2)

teletype-2.1.0-rc1-every.zip (484.8 KB)

The only outstanding question is: what does OTHER at the top of a script do? Currently, it never runs the command.

i feel that each EVERY and SKIP command should set a global flag to true/false depending on their execution. OTHER should read this global flag.

so OTHER at the top of a script is basically just acting on the global flag, which would mean whatever EVERY or SKIP was executed from previous scripts— which i think is interesting musically

1 Like

Yeah, in the light of the full feature set I think that makes a lot of sense. It will only affect the behaviour of the single case that is OTHER-at-the-top.

I’ll make this change and then call the feature complete. Whatever release you want to include it with is fine with me. The branch will sit in my repo until then.

This line is the piece that worries me about EVERY. There’s a precedent for commands that track internal state, DRUNK is one, O is another - but creating an OP (PRE?) where there’s actually multiple internal states introduces a new concept into the language that initially feels out of place.

What if EVERY was just syntactic sugar around % for users that don’t understand modulo?

M:
X + X 1 # increments X each tick, might be an more idiomatic way to do this, but I'm new.

1:
EVERY 2 X: TR.P 1
EVERY 4 X: TR.P 2

It’s more explicit where the counter for EVERY is coming from. Resetting becomes X 0.

The problem that EVERY solves is that there are few general-purpose variables with which to create control flow divisions. Complex polyrhythms become an excercise in seeking out lowest common multiples with which to wrap your counters, and you’re left with fewer variables to do other stuff with.

Just my 2 cents.

1 Like

i mean, it’s done. if the docs match, then we should put it in 2.1. unless there are objections?

this was my initial comment, but EVERY/etc is a useful easy-entry per-line polyrhythm thing. you can still do complex if-else conditionals with manual counting. these new ops represent a special case which is musically interesting enough to make an exception.

3 Likes

Alright, here’s the final build of this feature set:

teletype-2.1.0-rc1-every.zip (484.8 KB)

I’ll drop this as rc2 (a complete violation of the release candidate stage, but whatever) later this week after I’ve soaked the feature some more.

3 Likes

Don’t want to throw off momentum, but I’m not sure why we can’t do complex polyrhythms with modulo or a variation on it, but can do them with a per-line context operator?

1:
EVERY 2: TR.P 1 
EVERY 4: TR.P 2
EVERY 6: TR.P 3

Seems functionally equivalent to:

1:
X + X 1 # moving this from M in my other example so that the count is local to the script.
EVERY 2 X: TR.P 1
...

Just with one extra line (incrementing X). If you wanted to create polyrhythms, you would add a 2 script that was similar, but called on a different rhythm. If the issue is running out of variables (likely) does it make more sense to increase the number of variables rather than introduce a new language paradigm?

What about LOCAL X; X + X 1 that allows a script scoped local variable? That could have utility beyond this example.

I’m not necessarily advocating one over the other, just interested in what I’m missing.

You guys (girls?) continue to hit it out of the park. Well done.

I’m back from a month of travel now, so will try and kick the tyres of rc1 this week, in between bouts of jet lag.

I’d also love to help with the docs (I think in markdown these days), though hours might be hard to come by this week. I’ll put my hand up properly soon if I can wing it.

2 Likes

That would be much appreciated! I’m half-regretful that that I don’t put the time into more full-featured documentation (including the studies), but I realize that it’s not really my strength.

I’m withdrawing my comments above in favor of the proposed. I’m interested in thinking about what EVERY could lead to. It’s essentially a closure PRE? Does that sound right? Anyway - I’m generally in favor of combining general purpose language features into more complex things, but given the space constraints and lack of function creation (although I end up using SCRIPT as a stand in for functions in the couple of hours I’ve spent with TT so far) it makes sense to have some heavy duty constructs like EVERY.

In short, the language enthusiast in me is nervous, but the TT user is intrigued. Regardless, I like the enthusiasm @sliderule, you made my afternoon super interesting. I like thinking about this stuff.

2 Likes

It is one of my strengths, though time management isn’t!

Step 1 for me, read the current docs / threads and try and get a handle on what’s documented and what’s not … while playing with rc1/2.

2 Likes