Pondering a DIG input digit qty OP for getting these embedded numbers. OTOH I like my hacky modulo + division approach just fine and there’s not a great way to make this OP both set and get.

/ X 1000 (8 chars) vs. DIG X 3 2 (9 chars)
% X 10 (6 chars) vs. DIG X 0 1 (9 chars)
% / X 10 100 (12 chars) vs. DIG X 1 2 (9 chars)
So there is a use for it when you’re pulling out from the middle of a number!

Totally agree. Seeing as there was no objections to the current DEL.G behaviour, I’ll try to tidy it up and start a PR in the next days.

On a wider topic - is there anything that speaks against bumping up the delay buffer size again in the next release? The only discussion I found before seemed to indicate that this would not have a major impact on memory usage. I’ve been running with length 64 on my personal builds for a while, and haven’t seen any instability. Anyone have an opinion?

1 Like

I just watched a bunch of videos about how to set up a pull request so I’ll be able to put one together this week (probably Friday after I have time to test).

I thought of a semi-wild set of OPs: TR.D and TR.PD which would be like normal trigger OPs but with an added delay argument! I mostly use delays for firing triggers so this would make those simple delays play nice with other PREs without needing a second script.

The only issue is that I don’t know how to code this one :laughing: All the other delay OPs take the scene state, exec state, and the post command and pass them straight to the common delay handler. In this case I guess I would need to build the “post command” equivalent based on the trigger OP inputs…

1 Like

Does your build include everything that’s in the most recent beta listed at the top of this thread?

I think so, but it definitely will because I’m going to fork a fresh copy right before I commit my changes.

1 Like

Good news / bad news: These OPs are all working great but I completely forgot what a setter is for on Teletype. For example: you’re not allowed to use a setter to return a value to be used within another OP. It’s a bit confusing to me but I bet the other devs have a better sense of what I’m talking about.

The BIT and BITS will need to be split into “getters that return a value” and “getters that return a modified value” (I thought these were “setters”). This makes the BIT OPs redundant with BSET, BGET, and BCLR, so I’m probably killing the BIT OP.
There might be some utility in an OP that combines BSET and BCLR using a 1/0 argument for whether to set or clear the bit, but I’d rather not duplicate OPs just because they could be different.

BITS is really fun with QT.B and DEL.B, and it’s a great reference tool. I’ll have to pick another name though. Maybe BGETS and BSETS would be appropriate but I would rather keep it to 4 chars.

Here’s that experimental firmware if you want to play with it. I’ve removed my earlier ones and I’ll remove this one too once it’s merged into the main branch. I need to fix BITS and then do some documentation before making a pull request.
[REMOVED - Merged with official beta!]

3 Likes

a couple of suggestions:

  • what about renaming it to BIT4? i would typically say that we should just have a general op for changing any arbitrary number of bits, but i get that it’s fun/convenient/musical to work with 4 bits, but perhaps the name should reflect that, and we could also add BIT8.

  • i don’t like the fact that it’s the only op that would interpret a decimal parameter as a string representation of binary - what about my suggestion to just add support for binary and hex numbers by using B and X prefixes? this way you can use binary/hex parameters with other ops.

5 Likes

I settled on NGET and NSET for now since it’s specifically working with nibbles :rabbit: (half a byte)

Dunno how we’d do that right now since a 16-bit integer can only represent 6 bits of binary. “Ideal” OPs would certainly just accept a number, an offset, and a length for the “getter”; then a number, an offset, and arbitrary string of bits for the “setter”. The limitation of working with 1 of 4 nibbles makes the current OPs shorter, though.

Love it. My hack-y solution would deffo be less needed, but honestly not obsoleted. Unique uses of nibble OPs would be:

  • Change a specific subset of a binary mask (rhythmically and melodically useful; 4, 8, 12, and 16 are magic numbers in western music)
  • Simply display a binary nibble in live mode (the reference potential of this when live coding is very handy, though a way to display the full 16 bits would be better)

Do you have a beta to share? I don’t mind holding my changes back until I find the right context alongside your B and X numbers. I really designed these nibble OPs to be companions to QT.B and DEL.B if you want to try out my beta and comment!

(:point_up: well, ackshually) the BGET and BSET OPs already use decimal 1s and 0s to represent binary. I know “1” is more truthy than “1011” but my sweet snare pattern doesn’t care.

Seems like the conversation of representing different number systems perches us right on the edge of talking about representation in PATTERN mode again :open_mouth:

2 Likes

how so? they still take parameters that are decimal values - like all the other ops.

you would either provide a decimal value or use the B notation if you want more readability. the limitation you mention only applies when you interpret a regular decimal number as if it were a string representation of a binary number.

maybe i’m not explaining it well. here is your example of using BITS op:

Set 4 lowest bits in X to 1010:
BITS X 1 1010

here you have a decimal number (one thousand and ten) represent a binary number 1010. if instead of a value you stored in a variable:

Z 1010; BITS X 1 Z

and then used this variable with any other op it would get interpreted as one thousand and ten. it would only get interpreted as a binary number (0b1010 using C notation) by the BITS op - which creates a special case and possible confusion. what i suggest is keeping the benefit of binary representation but not creating any special cases, by allowing to specify values for any ops using binary and hex in addition to decimal. with this approach your example would look like this:

BITS X 1 B1010

B prefix tells teletype: interpret this as a binary number. X would represent hex. you could rewrite the above line like this:

BITS X 1 10 (since binary 1010 is 10 in decimal system)

or like this:

BITS X 1 XA (hex A is 10 in decimal system)

another example, all these numbers are equal: XFF, 255, B11111111

i think BITS is more readable. in terms of getters / setters, you could just do:

BITS value index returns the current 4 bits at the index
BITS value index bits sets the bits at the index

for the index, that’s the index of the group of 4 bits, correct? any particular reason for this index to be 1-based instead of 0-based? we have a mix of both approaches in teletype but for the bit ops 0-based feels more natural.

for representation, we could update the variable view in live screen, one question would be, do we display all numbers as binary? selected? this can get complicated. changing pattern view would be a more significant change.

1 Like

No you did a good job! I think it’s just the context limits of text-based communications. I understood your B and X representations from the outset, but your expanded explanation is great and will certainly help others come up to speed.

I have the getters and setters for BITS arranged exactly as you suggest with one more argument making it choose the setter. I’m having a usability issue though as I mentioned a few posts up. For example, try using the current setter like this, “DEL.B 100 BITS X 2 A: TR.P 1” and TT will tell you “too many arguments”.

Agreed on 0 index for the nibbles.

If we can get the getters/setters in one OP then maybe NIB is a good name. But I suspect that my issue was shared by whoever made the BGET and BSET OPs, and that’s why those are separate OPs.

Sorry for formatting; I’m on phone :metal:
Thanks for jamming with me on this!

2 Likes

yeah, setters can’t return values. one potential solution is to treat BITS as a function, so BITS X Y Z would not update the value of X, it would just return it, and if you want to update X you’d just do X BITS X Y Z.

BITS X Y Z never did set X, it’s just a getter that “returns” X modified by Y and Z, like you’re suggesting but it only works on the live mode screen. I was using the concept of a setter wrong. There seems to be no way to just return the Yth nibble of X using this 3-arg setup though because there’s no value of Z that won’t change the returned value. I still don’t see how we can pull it off with one OP.

Maybe along these lines it would help to focus on my original question: why are BGET and BSET two separate OPs?

it should definitely be possible. your op should be defined like this:

const tele_op_t op_BITS = MAKE_GET_OP(BITS, op_BITS_get, 3, true);

and it should contain 3 cs_pop calls and one cs_push. if it only works in live screen, there might be something wrong with the setup, try building and running tests - it’s a good way to check if your ops are properly set up.

because these 2 ops are not actually a getter/setter pair - BSET doesn’t take a parameter, since that parameter is baked into its name (somewhat confusingly, with the corresponding BCLR op).

the BIT op you proposed would work as the proper setter sibling to BGET:

BGET value bit_to_get
BIT value bit_to_set bit

Are there any setters that take a variable and store the result back in that variable that I could use as a reference? All the setters I’m seeing are for system variables like TR.TIME or R.MAX.

not sure what you mean?

I was trying to get a sense of how setters are used. I think these bitwise/nibblewise OPs are all math operations, and notice how there aren’t any math setters besides the ones that change environmental variables, e.g. R.MAX. My conclusion for now is that it is impossible to make TT pick between two getters based on number of args, so these OPs must be separated into two distinct getters ala BSET and BGET.

Your example

is not going to work because the concept of “setting” seems to imply that the assignment is happening within the OP. I assume we should not stray from using X value as the only method for assigning values to X. So that leaves nothing to “set” using a setter, just a second math operation.

Here’s what they look like currently:

nibble code
static void op_NGET_get(const void *NOTUSED(data), scene_state_t *NOTUSED(ss),
                        exec_state_t *NOTUSED(es), command_state_t *cs) {
    int16_t v = cs_pop(cs);
    int16_t seg = cs_pop(cs) - 1;
	if (seg < 0) seg = 0;
	if (seg > 3) seg = 3;
    cs_push(cs, ((v >> (seg * 4)) & 1) + 10 * ((v >> (seg * 4 + 1)) & 1) + 100 * ((v >> (seg * 4 + 2)) & 1) + 1000 * ((v >> (seg * 4 + 3)) & 1));
}

static void op_NSET_get(const void *NOTUSED(data), scene_state_t *NOTUSED(ss),
                        exec_state_t *NOTUSED(es), command_state_t *cs) {
    int16_t v = cs_pop(cs);
    int16_t seg = cs_pop(cs) - 1;
	if (seg < 0) seg = 0;
	if (seg > 3) seg = 3;
	int16_t decimal = cs_pop(cs);
	if (decimal < 0) decimal = -decimal;
	
	bool z[4];
	
	if (!(decimal % 10)) z[0] = 0;
	else z[0] = 1;
	
	if (!((decimal / 10) % 10)) z[1] = 0;
	else z[1] = 1;

	if (!((decimal / 100) % 10)) z[2] = 0;
	else z[2] = 1;

	if (!((decimal / 1000) % 10)) z[3] = 0;
	else z[3] = 1;
	
	for (uint8_t i = 0; i <= 3; i++) {	
		if (!z[i]) v = v & ~(1 << (seg * 4 + i));
		else v = v | (1 << (seg * 4 + i));
	}
	cs_push(cs, v);
}

I feel like I’m hung up on the “how” of the nibble OPs when the “why” isn’t quite so solid. For now I’m going to exclude them from my pull request. They might need a few more minutes in the oven and I would love to reevaluate when we have your B and X numbers.

I would love to hear more dev’s voices weigh in on the setter vs. getter conundrum!

ah, you’re right, BIT is not a setter. it’s a function that takes parameters and returns a value, just like BGET. same for BCLR and BSET. we do have functions that have a side effect of modifying some variables - like O and P.NEXT - but the variables they modify are implied by the op, not passed as a parameter.

to avoid confusion i think we should stop using the getter/setter terminology as it doesn’t really apply in this case.

so the proper definitions would be:

BGET x y
return bit y in value x

BSET x y
take value x, set bit y to 1 and return the result

BCLR x y
take value x, set bit y to 0 and return the result

BIT x y z
take value x, set bit y to z and return the result

1 Like

I have this recurring dream of being able to trigger TT scripts from Crow, which would require TT to be able to act as an i2c follower. Norns + Crow + TT could then control four voices of CV+gate+other (velocity, expression, whatever), with TT as a kind of auxiliary live-coded manipulator of events generated by Norns (or by a standalone Crow script).

I swear this has come up before and wasn’t immediately declared impossible, but I’m having trouble finding that conversation. Is there i2c follower code in Multipass that could potentially be rolled into TT by a mortal like myself?

1 Like

I think Trent mentioned some crow features which would let it respond to I2C queries. IMHO that is a much better choice than getting into the multi-leader quagmire. You could hypothetically use a fast metro to query crow and run scripts as crow dictates.

you don’t need multipass for this - it’s fairly simple to configure teletype to work as a follower (and shouldn’t cause any issues if you’re just planning to only use it as a follower).

replace this line: https://github.com/monome/teletype/blob/main/module/main.c#L987

with init_i2c_follower(0x31); process_ii = &tt_process_ii;

(0x31 is er-301, you can use er-301 crow functions, or change address to something else and modify crow code)

and add this:

static void tt_process_ii(uint8_t *data, uint8_t l) {
    if (l != 3) return;
    if (data[0] || !data[2]) return;
    if (data[1] > 9) return;
    run_script(&scene_state, data[1]);
}

data[0] should be 0 (gate message for er-301)
data[1] is the gate index, 0-based
data[2] is the gate state (1 == on, 0 == off)

didn’t test it so bugs/compilation errors are possible

1 Like