String handling inside of framelib networks

Right now the only way to change a buffer dynamically is to use fl.frommax~ params and to send the buffer $buffername into the FrameLib network. It would be good if there were more Frame-thonic ways of dealing with string conctatenation/formation so that buffers could be dynamically accessed without relying on sloppy outside Max timing.

2 Likes

Agreed - there probably needs to be a string table object or similar - haven’t figured out how that should work. A request on GitHub would be good for this.

1 Like

BTW - it’s been an idea for a while, just haven’t really needed it yet.

I’m a new excited user of FrameLib, and I don’t remember how I used Max before the FrameLib. It’s like BC/AC - BFl/AFl, but I think generating strings inside the ecosystem would be an awesome and obvious addition. For instance, triggering samples randomly from a polybuffer~ And not having to concatenate them into a single long buffer every time.

2 Likes

Hi @koka - nice to see you here! Thanks for your nice words.

Feel free to propose ideas here about how you’d see that working. I know framelib needs some more things in this direction, but I don’t have a clear idea of the nicest way to do these things right now - and I’ve not yet needed it for my purposes. Any ideas for specific objects/how you would see them working are welcome.

Hi Alex! Thank you for your reply. In this context I would imagine an object like fl.sprintf, behaving exactly like sprintf in Max, but with that accurate timing. So that you could let’s say drive it with fl.random, feed it to fl.read and so pick your random sample to be played from your polybuffer in the beginning of every frame.

OK - that’s useful - so string concatenation with numbers would be the top priority? It might also be possible to concatenate strings, but they can only be passed as parameters.

I will be honest - I find that sprintf syntax is unpleasant to work with (and not as excusable as say regex, where the horror is at least matched by power) and it’s probably got various features that wouldn’t make sense here, so my preference would be to design a simpler or at least more pleasant syntax (a bit more like [combine]). However, I can imagine some number formatting options might be handy - so the question is what you think you might need:

  • padding with zeros
  • specified number of decimal places
  • specified number of significant figures
  • hex formating
  • scientific notation
  • anything I’ve forgotten

There’s also an option to have things like the above set by parameters, rather than a single formatting/content string.

Thoughts?

1 Like

Ok, this is a very interesting discussion. The syntax question is crucial, I think the simpler-the better.

Maybe it’s time to ditch sprintf and regex philosophies and go for something more transparent and clear, but powerful.

I love the simplicity of changeable arguments ($1, $2) in Max messages. On the other hand, I find it would be extremely powerful if you could do some calculations inside the sprintf, just like with expr. There needs to be a golden balance somewhere.

This requires a very thoughtful design, so I’ll sit and prototype for some hours and summarize my humble suggestions by tomorrow morning.

(post deleted by author)

So, just met Balint at Notam and after his input, I think cloning a combine in the fl land would solve enormous number of problems. So, I think, time-accurate combine is the way to go.

1 Like

And if I can pitch in it would also be great to clone the @padding feature of [combine] as well!
GO FRAMELIB! :)))

1 Like

Thanks both. This is all very useful. I didn’t know about the padding attribute on combine, but that seems totally relevant and numbers just as ints + padding seems like it should cover most uses for this type of object I can think of right now.

A few things:

  • strings can’t be passed without tags (meaning you can’t pass strings to inputs in quite the same way as with the combine object), so:
    1. There will need to be an output tag set
    1. Each element will be settable by tag (so you can dynamically change strings) via the parameter input
  • There will need to be a special syntax to refer to inputs (fl.expr~ uses in1, in2 etc. so there’s an argument for that, but I need to think how likely it is you’d ever want to use that as a string)
  • I’ll account for padding - possibly in the same way as the combine object, or possibly with a parameter per input for padding amount (I’ll have a think)

In general it is worth mentioning that as FrameLib itself is not reliant on max so I might avoid max naming or conventions at times to make sure that FrameLib makes sense in other contexts also.

I’ll try to find some time to code this up in the not too distant future.

2 Likes

How about [fl.string~]?

something like:

fl.string~ buf.in1 /output_tag buffer

and slightly more advanced

fl.string~ in1.in2 /output_tag buffer (to change the polybuffer for example)

I guess the tricky part to conceptualise (maybe its not possible) is how you can store and concatenate different strings inside the network. Kinda like how one might do this:

patch ```

----------begin_max5_patcher----------
533.3ocsU00bbBBE8Y8WACOacDV+X29WISlLnKcCYTvAwjMIS9uW3hl1zpst
qtufCWAN2Cmyk66gA3R0YdGF8czcnff2CCBfPt.ACyCvMryU0rNXY3FdWG6D
GG4+mge1.w+gREmWLFV12n5M0bCrIxPTeHyqsbOhXL59geINBmhp7ouQH+1o
HjiGBcHXKyT8nPd5AMux3OGZZQbRDZGIy8IKwMRowIn6ca4ivP2PzBIXYuwn
jWHQJYxSSRlC3OSaMqga35G3RVYMrwjo3I4evyh8NpsOGHX533UQSI+Ea98W
xnlIOpZPjjjK7FPHMSdATboh4tbPLI4fXVraUh4Lr7s5Xo4QDYZNRm0tFMik
M+hcrdkjlBjzaeuAjrqxZ5blsMhmYWKOIfLleXc7ruojquBtLa0Y5VVcNXbG
35JeEZFMsR0TJjbjPRPw1Q5VorzIU1c+eks.bvjj82HKrleBY6sfJY5shqjq
zEO9jzztX.IbsUc9i9ovA5h+U92o50Ui45fDfR9LeOx6LBIyHr8h90Zb8FQj
YthWJPEK.nT+ZVENGV.NeMWT5i1BbWs2MGXxz.SVEv4K.X5Fbyls.bx2.bHK
.mrM.mzEduQmnhi019LW2MrX.B6aKOo.4beDLUH8SgZbrl+rXb8vi7Xl1Vxa
r068ZHsvmy8sFvMJqwP1KF7FVxYgDd2RZaXz0x77.ddK7iveBjP5XFJ
-----------end_max5_patcher-----------
```
2 Likes

Thank you Alex! This is amazing.

I think James’ examples are very good.
So if the input was two ints coming out of two fl.random objects, like 1 and 2, for instance:

fl.string~ samplesin1.in2 /output_tag buffer /padding 2 3 /triggers 0 1

It would print:

samples001.0002

And only the second inlet would trigger the output. If /triggers were set to 1 1, then both would trigger.

1 Like

This mostly seems all fine. Here’s where I’m at so far regarding any notes or differences to the above:

  1. The issue of storing strings is separate - you can do it with fl.register~, but only one at a time. That needs a different object which I will work on later, with a key decision relating to that being that I’m currently unclear on whether to keep the restriction of single strings per tag that exists at the moment. It’s possible that I could add lists of strings to the parameter system which would also call into question the way that some parameters in other objects are currently dealt with (possibly for the better, but I need to figure out the scope of this, as well as the memory allocation issues it creates). It’s also possible that could be done without changing the way the arguments are dealt with in Max, so it may have minimal effect on pre-existing patches, but I need to think about this more and whether it is a pain worth going through. Probably something to decide either way on before an initial non-beta release.

  2. I think the object we are discussing right now will be called fl.concat~, because fl.string~ is not very descriptive.

  3. @koka I can indeed add a parameter to control what inputs trigger, but it’ll be called /trigger_ins, for consistency with other framelib objects, rather than /triggers.The usage would follow that of fl.expr~.

  4. I think the interface would be one argument per item, so it would be something more like (still thinking about whether to use verbose output_tag, or just tag):

fl.concat~ samples in1 . in2 /tag buffer /padding 2 3 /trigger_ins 0 1
  1. @james.bradbury - you won’t be able to input strings direct to inputs as in your example because it feels weird to have to tag the string (which you would) and then use an input where the tag is meaningless, so changing string parts of the input would be done via the parameter system/input.
2 Likes

Having thought a bit more from a max usage point of view it’s also probable/possible that I’d choose to have the first argument set the tag.

Consider all the following as just a user feedback:

  • I like the /tag buffer syntax. I think making the first argument be the tag would make the role of arguments ambiguous. From fl.concat~ buffer samples in1 . in2 /padding 2 3 /trigger_ins 1 versus fl.concat~ samples in1 . in2 /tag buffer /padding 2 3 /trigger ins 1 I would always choose the latter since there is no question about what an argument does.

  • To me the name fl.concat~ suggests the funcionality of concatenating frames rather than creating strings. Like if I would like to concatenate fl.random~ /length 10 and fl.random~ /length 5 I would probably look for something like fl.concat~. As a Max user, if I would have to create strings I would naturally look for the word “string”, or max-clone names, like “sptrinf” or “combine” (maybe “symbol” but with a bad feeling in my soul). (Update: or something with “tag” since that’s what we are making in the end.)

Just a placeholder for the example

I agree with this. Not sure what the alternative is though without putting string/tag somewhere in the name. Maybe fl.jointag/string~

I will think about naming. You aren’t actually joining/concatenating/combining the tag part though - it’s the string, so probably something like fl.concatenatestring~ or fl.joinstring~ is preferable. The latter might be best for consistency with fl.join~ and isn’t too long. I think the danger of confusion will remain whatever the verb used - it’s specifying the string bit that is important. Right now this is the only object operating on strings, but if there are more later then fl.string~ is probably too vague.

In the meantime…

2 Likes