This sorta makes sense, but like most things in this forum, it’ll take me a bit to grok.

Lucky for me, I’ve got the TXi just waiting for some good use. I don’t have a lot of stack cables so it might wait a little for that. But dang, this is cool.

shoot i guess i never saw this, or assumed someone else would pipe up. i know it is months later… but needs must.

it is a terminal command.

you open terminal
use cd to navigate to the location of the file to convert
type the command, replacing windows.txt with the file you want to conver,t and unix.txt with the desired ouput filename.

1 Like

I’m trying to understand this amazing scene. Could I ask you some questions?

  1. I can’t find the op BREAK on the manual.
    In your code that means that every time P Z is not 0, script 8 won’t execute the following lines of code?
  2. I don’t have a TEXEXo yet and I don’t understand this line of code:
    TO.OSC.N Z + 40 PN 1 Z
    Why do you add 40?

it’s under the control flow section - all ops are also listed in alphabetical order below if you need to find a specific one. BREAK halts execution of the current script, so yeah, in my script if P Z is not zero it will skip the rest of the script.

just to transpose it up by 40 semitones - the number doesn’t really have any significance, probably sounded good!

1 Like

Thanks for the info! This afternoon I’m going to study the whole scene a little bit more.

1 Like

I would like to ask you a few questions more (to you or to anybody who knows the answer):

M SCALE 0 15 30 200 G.FDR.N 0

  1. I don’t understand SCALE op. This means that M is a value between 170 (200-30) and 15 (15-0) based on top fader, right?

I + 35 * 30 G.FDR.N Z

  1. Are 35 and 30 arbitrary values like the 40 semitones you mentioned before? I guess they have no significance beyond that they are good oscillator decay values, but I’m not sure.

  2. This a more generic question regarding loops behaviour. Is script 8 called 16 times at each metronome beat? When is executed a line like TO.OSC.N Z + 40 PN 1 Z? Sequentially? I mean, we don’t know oscillator decay value yet. Decay value is two lines down. So I guess that all the lines from script 8 are executed in parallel 16 times per metronome beat. Does make any sense to you what I’m trying to explain?

SCALE a b c d x op will scale value x from range a..b to range c..d. it’s useful for something like scaling the full travel of the param knob to a desired range. in this case i’m using a simple fader which provides values in the range 0..15 which will translate into metro rate between 30 and 200ms.

this value is used to set decay length. since this fader will produce values in the range 0..7, this expression will result in envelopes with decay between 35ms and 30*7+35=245ms. so yes, arbitrarily chosen for what sounded good.

correct. script 8 will be executed 16 times, with each iteration executed sequentially, not in parallel. it’ll basically do:

script 8 line 1
script 8 line 2
script 8 line 3
script 8 line 4
script 8 line 5
script 8 line 6
script 8 line 1
script 8 line 2
etc etc
1 Like

Thanks for the info! Step by step I’m learning some things. Now I fully understand what SCALE does. However, I still have a little confusion around loops. If all is executed sequentially, in a trigger sequencer like the one in the Grid integration studies (https://github.com/scanner-darkly/teletype/wiki/TRIGGER-SEQUENCER), when are triggered two notes in the same step but diferent rows? They aren’t triggered at the same time? Is there a micro delay?

yes, there would be a small delay but it’s negligible, very likely under 1ms. i don’t think anybody measured it but i would be surprised if it’s more than that. it’s possible to create a more noticeable delay, like if you have a loop with 10000 iterations but it’s not something you’d likely want to do in a teletype script anyway.

1 Like

Hi!
I would like to share this scene with the community. I have a Teletype since twenty days ago, so it’s the first scene I code. It’s based on pong scene by @scanner_darkly but it has a more “complex” collision logic. It’s incomplete because I need more available lines of code. Or maybe someone could tell me how to code the same functionality with less lines of code.

I:

P.N 0
Z RAND 127
D + 1 RAND 3
G.BTX 0 0 0 1 1 1 0 0 16 8

Z is the current position. Since Grid has 128 buttons, Z is a number between 0 and 127. At the begining it’s a random number between those boundaries.

D is the direction of the movement. It’s represented by a number between 1 and 4:

M:

M SCALE 0 V 10 V 1 50 PRM
P 4 Z; X % Z 16; Y / Z 16
G.CLR; G.LED X Y 15
P 0 G.BTN.V - Z 17
P 1 G.BTN.V - Z 16
SCRIPT 8

Index 4 of pattern 0 stores current position. Indexes 0, 1, 2, 3, 5, 6, 7, 8 are destined to store potential button presses:

X and Y are the coordinates of Z. Since Z = X + 16Y then X = Z mod 16 and Y = Z div 16.

This picture may help to understand what’s going on M, script 8 and script 7:

SCRIPT 8:

P 2 G.BTN.V - Z 15
P 3 G.BTN.V - Z 1
P 5 G.BTN.V + Z 1
P 6 G.BTN.V + Z 15
P 7 G.BTN.V + Z 16
SCRIPT 7

SCRIPT 7:

P 8 G.BTN.V + Z 17
IF EQ D 1: SCRIPT 1
IF EQ D 2: SCRIPT 2
IF EQ D 3: SCRIPT 3
IF EQ D 4: SCRIPT 4
SCRIPT 6

Scripts 1, 2, 3 and 4 are the ones in charge of collision logic. Since we can have 4 directions of movement, we have 4 collision logic scripts.

SCRIPT 1:

IF && P 7 P 5: D 4; SCRIPT 5
IF P 8: D 4; SCRIPT 5
IF && P 7 ! P 5: D 3; SCRIPT 5
IF && P 5 ! P 7: D 2; SCRIPT 5
IF EQ Z 127: D 4; SCRIPT 5
ELIF EQ Y 7: D 3; SCRIPT 5

Here we check if there is a collindant pressed button. We may also update direction. We also check if current position is on the Grid corners. But one case left:

I need one extra line of code:

ELIF EQ X 15: D 2; SCRIPT 5

This pictures may help to understand collision logic:

Scripts 2, 3 and 4 are similar to script 1.

SCRIPT 2:

IF && P 7 P 3: D 3; SCRIPT 5
IF P 6: D 3; SCRIPT 5
IF && P 7 ! P 3: D 4; SCRIPT 5
IF && P 3 ! P 7: D 1; SCRIPT 5
IF EQ Z 112: D 3; SCRIPT 5
ELIF EQ Y 7: D 4; SCRIPT 5

In this case I need this extra line of code:

ELIF EQ X 0: D 1; SCRIPT 5

SCRIPT 3:

IF && P 1 P 5: D 2; SCRIPT 5
IF P 2: D 2; SCRIPT 5
IF && P 1 ! P 5: D 1; SCRIPT 5
IF && P 5 ! P 1: D 4; SCRIPT 5
IF EQ Z 15: D 2; SCRIPT 5
ELIF EQ Y 0: D 1; SCRIPT 5

In this case I need this extra line of code:

ELIF EQ X 15: D 4; SCRIPT 5

SCRIPT 4:

IF && P 1 P 3: D 1; SCRIPT 5
IF P 0: D 1; SCRIPT 5
IF && P 1 ! P 3: D 2; SCRIPT 5
IF && P 3 ! P 1: D 3; SCRIPT 5
IF EQ Z 0: D 1; SCRIPT 5
ELIF EQ Y 0: D 2; SCRIPT 5

In this case I need this extra line of code:

ELIF EQ X 0: D 3; SCRIPT 5

Script 5 is a simple one. If script 5 is triggered it means there is a collision.

SCRIPT 5:

TR.P 1 1

Script 6 is where Z (current position) is updated:

IF EQ D 1: Z + Z 17
IF EQ D 2: Z + Z 15
IF EQ D 3: Z - Z 15
IF EQ D 4: Z - Z 17

(I don’t have a USB drive, so I’ve retyped code manually. May be errors…)

1 Like

Here’s a little demo:

1 Like

Is there a way to get your extra lines of code onto the top of script 5? Extra handling might be necessary, but you have a couple extra lines to work with that might make it possible.

1 Like

Maybe I’m wrong but I don’t think so because script 5 is only called if there is a collision. And the code that still remains unwrited will be in charge of collision detection (on Grid corners).

TR.P 1 1 has one too many arguments. Do you mean to set trigger output 1 high, or ping it? Regardless you don’t need script 5; just replicate script 5’s code in all those spots you would call it. For example: if pinging just replace all of your instances of SCRIPT 5 with TR.P 1.

1 Like

Yes, but:

  1. Script 5 will have more lines of code in the future. Right now just pulse trigger output 1, but my intention is to send a CV computed with x, y coordinates.
  2. Where I need more available lines of code is in scripts 1, 2, 3 and 4. Even if I eliminate script 5 that necessity wouldn’t dissapear, because those scripts are full :frowning:

there are several ways to optimize but the simplest one would be changing the first 2 lines of scripts 1-4. using script 1 as an example, change

IF && P 7 P 5: D 4; SCRIPT 5
IF P 8: D 4; SCRIPT 5

to

IF || P 8 && P 7 P 5: D 4; $ 5

this will free one line. you will have to use the $ alias for SCRIPT to make the line shorter.

i don’t see you limiting Z - you have to make sure it doesn’t go out of bounds. you should add something like this to script 6: Z % + Z 128 128 - this will shift it to positive range and then wrap it to 0…127 range. or you could use the WRAP op: Z WRAP Z 0 127. same for when you check pressed buttons, instead of P 0 G.BTN.V - Z 17 you should do P 0 G.BTN.V WRAP - Z 17 0 127.

2 Likes

Thanks! I didn’t know those aliases ($ for SCRIPT and || for OR I guess)!
And yes, I should limit Z…
Thanks for the advices, guys!

actually, thinking about it more, you don’t want to limit Z for checking pressed buttons, since it will wrap to buttons on the other side which is not what you want. in this case, it’s fine to go out of bounds - G.BTN.V will return zero if the button index is negative. same for inactive buttons.

I’m not understanding this. Could you develop it a little bit more, please?

Anyway, if someone wants to try it, here’s the scene:
tt09s.txt (2.1 KB)
Right now, CV 1 is updated depending on X value and P.N 4.
P.N 4 stores semitone values of the double harmonic scale, but is easily changeable.