cl-patterns icon indicating copy to clipboard operation
cl-patterns copied to clipboard

Math operators on prest not working as expected

Open kflak opened this issue 10 months ago • 5 comments

Hi there,

I bumped into this situation:

     :db (ptrace (pseq (p- (list -9 (prest) -12 (prest) -15 (prest) (prest)) 10)))

Gives me this output:

-19
-9
-22
-9
-25
-9
-9
-19
-19

In other words, the p- works correctly on the list positions that have a value, but where there is a prest it gives me a -9 (which is weird, I would assume that it would at least give me a -10...?) . p+ exhibits the same behavior, giving me 11 db for each rest.

It seems to make no difference if I place the p- inside or outside the pseq. In both cases the end result is that I don't get a rest at all in these situations, but an extra note that I don't really want...

kflak avatar Feb 21 '25 13:02 kflak

Each prest has its own numeric value, set by its optional argument, which defaults to 1. So the -9 occurs because the actual expression at that point is (p- (prest 1) 10)--in other words, (- 1 10). 1 is the default of prest because it's a safer value than 0 since 0 is potentially problematic in expressions such as division (i.e. (p/ 1 (prest 0))) and similar. And prest has to have its own numeric value so that it can be placed in pattern expressions that are expecting a number, such as your example here.

The hope is that since the event containing the prest is going to be a rest, the results of expressions it's in won't matter that much since they won't be heard, but it can of course still cause issues since other patterns can depend on the results of those expressions (i.e. phistory, pdelta, etc).

Thinking about this, though, this might be another situation where the user should be able to customize this default value - probably globally, but maybe it would also be good for there to be some way to set it on a per-pattern basis (i.e. being able to specify things like "from this point onward in the pattern, prest should be treated as 0"). I'll have to think about the best way for that to work and will probably implement something like that in the near future.

Feel free to let me know if you have any thoughts/suggestions for what might be a good API/default behavior for any of this or anything :)

defaultxr avatar Feb 21 '25 20:02 defaultxr

Just off the top of my head I'm imagining an API that could look something along these lines:

(setf *prest-value* 2) ; set prest's global default

(pbind :foo (p+ (prest) 1) ; result will be 3 here
       :prest-value 0 ; set prest's default for the remainder of this pattern
       :bar (p+ (prest) 1)) ; result will be 1 here

(pbind :baz (p+ (prest) 1)) ; result will be 3 again

defaultxr avatar Feb 21 '25 20:02 defaultxr

Ah, I see... Hmm... Tricky one. In my mind I always expect a prest to cause a silence, but in my example above it actually triggers a synth with db value -9, which is kind of the opposite of what I am after. And I would expect the numeric value to determine the length of the rest. I seem to remember that when an sclang Rest is used in either the dur or db slots, it determines the length of the rest, and in any other slot the numerical value is ignored. I can't find the documentation for this right now, though. I'm not sure that it's necessary to make the prest default value setable, though, as it's trivially easy to just set it per instance.

kflak avatar Feb 21 '25 21:02 kflak

Huh, okay, yeah, if a prest is provided then it should definitely be turning that event into a rest, so that's a bug for sure. To be honest that one sounds like it might take a bit of work to fix but I'll definitely take a look into it when I'm able. I'm guessing the problem is that the p- is basically eating the prest, causing it to disappear and thus not take effect.

(I'm hoping there is some simple solution to make prest consistently work how it should without having to add special cases for it all over the codebase, but it's possible there's no way around that. I'll have to see what can be done...)

As for the prest's value determining the dur of the event, I'll think about that. Following sclang's behavior is good, but I definitely always prefer to prioritize cl-patterns being self-consistent and behaving in a way that is most intuitive/logical for the maximal amount of users, even if they're unfamiliar with how sclang does things--i.e. "principle of least surprise". To me it feels like it would be more expected for prest's argument to specify its numeric value and for the meaning of that value to come from the event/pbind key that the prest occurs within since that's how numbers in patterns/events usually get their meaning.


Semi-related, I've also been thinking of adding the concept of "quantities" into cl-patterns, so that users could do stuff like this:

(pbind :dur (beats 3)
       :pitch (midinote 60)
       :vol (amp 0.5))

Expressions like (beats 3), (midinote 60), and (amp 0.5) would return objects representing those quantities. Having the type of quantity specified explicitly and associated with the value rather than implicitly deduced from its context would (I imagine) be cleaner and allow for meaningful values to be converted and passed around more easily. And I think that it might also make it easier to have the type of behavior you're suggesting for prest--and potentially to also have similar in other parts of the library too.

defaultxr avatar Feb 22 '25 01:02 defaultxr

As for the prest's value determining the dur of the event, I'll think about that. Following sclang's behavior is good, but I definitely always prefer to prioritize cl-patterns being self-consistent and behaving in a way that is most intuitive/logical for the maximal amount of users, even if they're unfamiliar with how sclang does things--i.e. "principle of least surprise". To me it feels like it would be more expected for prest's argument to specify its numeric value and for the meaning of that value to come from the event/pbind key that the prest occurs within since that's how numbers in patterns/events usually get their meaning.

Very good point. Totally agree!

Semi-related, I've also been thinking of adding the concept of "quantities" into cl-patterns, so that users could do stuff like this:

(pbind :dur (beats 3) :pitch (midinote 60) :vol (amp 0.5))

Expressions like (beats 3), (midinote 60), and (amp 0.5) would return objects representing those quantities. Having the type of quantity specified explicitly and associated with the value rather than implicitly deduced from its context would (I imagine) be cleaner and allow for meaningful values to be converted and passed around more easily. And I think that it might also make it easier to have the type of behavior you're suggesting for prest--and potentially to also have similar in other parts of the library too.

Sounds interesting! There's indeed something about the pattern library in general... Lots of dark magic under the hood that can be hard to crack. I think one of the strengths of cl-patterns is how it makes the underlying assumptions much more coherent than the thicket of stuff that has grown into sclang over the years, so anything that would further that process is interesting. As long as it doesn't break existing code ;-)

kflak avatar Feb 22 '25 07:02 kflak