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.

what i mean is: you use Z as your current position. in your scene, the only valid positions are 0…127. if you use addition or subtraction to determine a new position, you have to make sure not to go out of bounds.

there are 2 options: you either wrap to the other side when it reaches any of the edges, or you treat it as a collision. if you wrap (which iirc what my scene does), then you can use WRAP op. let’s say you want to move down one row, and Z is 120. to move down one row you add 16. 120+16=136. this is out of the valid range. if we wrap it, we will get 136%128=8 - which means moving down from the bottom row it will appear at the top row [essentially, your grid becomes a toroid - left column is connected to the right column, bottom row is connected to the top row].

if you don’t need to wrap, then you just have to check that your new position will not be out of bounds, which you already do.

this is also the case for checking pressed buttons - if you in the leftmost column, then there is no button to the left of it - unless you want it to wrap to the other edge. to get the button to the left from the current one you have to subtract 1. so if Z is 0 then the you’ll be checking G.BTN.V -1. since there is no button with that index, the op will simply return 0. in your case it doesn’t matter, since you have separate checks for your edge collision detection anyway.

1 Like

Got it. I appreciate your help.

Since getting the Grid to go with my Teletype I have been experimenting with writing little sequencers that captures some aspect of how I work with other sequencers. This one is a simple two track polymeter that makes it easy to switch key and mode. Check out the video for a quick demo.

I:
G.FDR 0 0 7 12 1 2 2 2
G.FDR 1 0 6 9 1 2 2 2
O.INC 1; O.MAX 11; O.WRAP 1
G.FDR 2 0 0 13 1 2 2 2
G.FDR 3 0 3 13 1 2 2 2
SCRIPT 7

M:
X WRAP + X 1 0 - G.FDR.n 2 1
Y WRAP Y 1 0 - G.FDR.N 3 1
SCRIPT 1
SCRIPT 3
SCRIPT 4

1:
G.CLR
P.N 0; J - G.FDR.N 2 1
L 0 J: G.LED I 0 * P I 4
P.N 2; J - G.FDR.N 3 1
L 0 J: G.LED I 3 * P I 4
G.LED X 0 6; G.LED y 3 6

2:
P.N 0; P.L G.FDR.N 2; J 16
L 0 P.L: P I G.BTN.V + J / I 6
P.N 1; P.L 0; P.END 7
L 0 6: P.PUSH G.BTN.V I
L 0 6: P I ? EQ P I 0 -1 P I
SCRIPT 5

3:
IF ! PN 0 X: BREAK
A G.FDR.N 0; B G.FDR.N 1
PN.NEXT 1
W EQ PN.HERE 1 -1: PN.NEXT 1
C N.S A B PN.HERE 1
CV 1 C; TR.PULSE 1

4:
IF ! PN 2 Y: BREAK
A G.FDR.N 0; B G.FDR.N 1
PN.NEXT 3
W EQ PN.HERE 3 -1: PN.NEXT 3
C N.S A B PN.HERE 3
CV 3 C; TR.PULSE 3

5:
PN.2; P.L G.FDR.N 3; J 24
L 0 P.L: P I G.BTN.V + J / I 6
P.N 3; P.L 0; P.END 7
L 0 6: P.PUSH G.BTN.V + I 7
L 0 6: P I ? EQ P I 0 -1 P I
SCRIPT 6

6:
P.N 1
L 0 6: P I ? EQ P I -1 -1 I
P.N 3
L 0 6: P I ? EQ P I -1 -1 I

7:
G.BTX 0 7 1 1 1 1 2 2 7 1
G.BTX 7 7 4 1 1 1 2 2 7 1
G.BTX 16 0 1 1 1 1 2 2 6 1
G.BTX 24 0 4 1 1 1 2 2 6 1

Still feeling new to the Teletype language, though it does bring fond memories of Lisp, Forth and OPL programming, so… don’t hesitate to suggest different ways of doing things!

Thanks to @scanner_darkly for all these wonderfully well thought out Grid ops!

/K

8 Likes

great work! and also a good example of how even with limitations of a single scene you can still build a fully featured sequencer. also nice to see newer ops like N.S being used, especially important in grid teletype scenes, since it allows you to dedicate more script space to the UI / sequencer logic.

added your scene to the codex page!

1 Like

I made a grid-enabled teletype scene today that I call chordboard. It uses the N.C operator in teletype to create a performance-friendly interface for playing with chord progressions. It supports four different voices.

The top row buttons correspond to 16 memory locations. Each memory location stores a chord-type (one of the 12 that N.C offers), a root note and a root note octave.

Each TR and CV pair correspond to a voice.

The first voice always outputs the root note of the chord at the octave set by the left column of buttons along the bottom of the grid.

The second voice outputs a random note from the current chord when a trigger is received on input 5. This voice has its own octave buttons next to the root note octave buttons. The param knob also changes the range of the random notes from 1 octave at full ccw to 4 octaves at full cw.

The third voice will output a note from the current chord based on the external CV input when a trigger is received at input 6. In this track, I’m using a looping smooth random channel from Pamela’s New Workout that is 6 beats long so it phases a bit with the bars of the chord changes. Each octave of the chord spans 1v of CV, but it’s not quantizing the voltage itself, just using its value for lookups.

The fourth voice is played manually on a 4x4 grid of buttons on the bottom left corner of the grid. Each row corresponds to the notes of the chord at successive octaves.

I slapped on a really basic script for the 8th input to let me go through the first four memory slots, but I’m hoping to come up with something more flexible for looping sequences of the memory slots.

p.s. can someone remind me how you do the cool monospace indented formatting for teletype code?

Chordboard code (how do I format this the cool way?)

CHORDBOARD

#1
G.BTN.SW G.BTNI; A G.BTNX
G.BTN.SW + PN 0 A 16
G.BTN.SW + PN 1 A 32
G.BTN.SW + - 3 PN 2 A 64
C PN 1 A; B PN 0 A; D PN 2 A
CV 1 + V D N.C C B 0; TR.P 1

#2
G.BTN.SW G.BTNI; X G.GBTN.X1 1
PN 0 X G.BTNX; B G.BTNX

#3
G.BTN.SW G.BTNI; X G.GBTN.X1 1
PN 1 X G.BTNX; C G.BTNX
CV 1 + V D N PN 1 X; TR.P 1

#4
Z && EZ G.BTNV EZ G.GBTN.C 4
IF NZ Z: TR 4 0
IF EZ G.BTNV: BREAK
TR 4 1; X V - 7 G.BTNY
CV 4 + N.C C B G.BTNX X

#5
PARAM.SCALE 0 3; Z PARAM
X - 7 G.GBTN.Y2 7; Y RRAND 1 4
X + RRAND 0 Z X
CV 2 + V X N.C C B Y; TR.P 2

#6
IN.SCALE 0 40; X IN
Y V / X 4; Z % X 4
CV 3 + N.C C B Z Y; TR.P 3

#7
X ? == G.BTNX 5 2 3
Y - 7 G.BTNY; G.BTN.SW G.BTNI
PN X A Y
IF == X 2: D Y; TR.P 1
CV 1 + V D N.C C B 0

#8
X % + G.GBTN.I 1 0 1 4
G.BTN.PR X 1

#M
M.ACT 0

#I
G.GBX 1 0 0 0 1 1 1 3 1 16 1
G.GBX 2 16 0 1 1 1 1 3 2 12 1
G.GBX 3 32 0 2 1 1 1 3 3 12 1
G.GBX 4 48 0 4 1 1 0 3 4 4 4
G.GBX 5 64 5 4 1 1 1 3 7 1 4
G.GBX 7 80 7 4 1 1 1 3 7 1 4

#P
0 0 0 0
1 1 1 1
0 0 0 0
63 63 63 63

2 1 0 2
1 5 0 1
0 4 0 3
2 8 0 1
2 1 1 0
1 5 1 0
0 4 1 0
2 8 1 1
8 6 0 0
9 2 0 0
10 11 0 0
11 4 0 0
7 8 0 0
8 9 0 0
10 11 0 0
10 11 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0

#G
0100000000000000
0100000000000000
0000010000000000
0000000000000000
0001100000000000
0010000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

tt03s.txt (1.9 KB)

2 Likes

love the track!

this is a perfect example of why i think grid integration can be so usable - teletype has some really powerful ops that really open up when you interact with them directly via a grid interface.

for code formatting use backticks: `
for inline use, surround the content with single backticks, for multiple lines place content between lines with 3 backticks each. alternatively, select a block and press ctrl-shift-c.

1 Like

Hi! Here’s a video using rhythmicon code. I’ve changed faders behaviour: instead of controlling decay time, they are controlling note’s pitch (based on scales stored on pattern one). Great, great, code, @scanner_darkly!

1 Like