@weights attribute for fluid.bufstats~ doesn't work with @startframe/@numframes

I’ve revamping my 40melband analysis thing to incorporate the loudness weighting afforded by fluid.scale~, and when adding the @weights attribute to fluid.bufstats~ it complains about:

fluid.bufstats~: Weights buffer invalid size

If I remove my @startframe/@numframes attributes it works fine, so I’m guessing this is because it’s trying to apply the weights form the entire @weights buffer to a subset of that.

I would expect the behavior here to be that @startframe/@numframes applies to both the @weights and @source buffers as I can’t imagine a situation in which you’d have a set of weights from a differently-lenghted buffer you’d want to apply to another one.

Or more specifically, the size-check here should be for comparing the overall @weights and @source sizes, not the potentially cropped @source frames after going through the selectiveness of the @startframe/@numframes logic.

Obviously I can fluid.bufcompose~ a step before this, but that’s more faff, friction, buffers, and points of failure/confusion, particularly if you want to test different @startframe/@numframes and are already operating in @blocking 2, so the amount of samples/channels everywhere matters.

I also realized that in my patch I use a different amount of @startframe/@numframes depending on the descriptor I’m using (e.g. all frames for loudness, “middle frames” for spectral descriptors, etc…), so if I have to use fluid.bufcompose~, I’d have to use a separate one per thing I want to scale (with corresponding buffers, faff, friction, etc…).

The weights buffer needs to match the size of the thing you’re gathering stats from (i.e. numFrames, if it isn’t -1; otherwise the length of the buffer - startFrame)

I am not into that idea at all :laughing: And I can quite readily imagine situations where the buffer sizes of the weights and overall inputs aren’t the same, especially if the weights aren’t coming from descriptors (say you have a multislider that you’re drawing weights into).

I do have some very-future thoughts brewing about cutting down on buffer related filddliness, but have yet to sell them to my lovely colleagues.

1 Like

This seems more like a peculiar edge-case behavior in which you’d then be expected to manage your own sizes.

The creation of the @weights attribute was specifically for descriptor-based weighting of summary statistics (it even says so in the helpfile for fluid.bufstats~), so it’s not some generic Ears-like buffer transformation function/utility.

The current behavior would also make sense if fluid.weights~ was an object that was independent of fluid.bufstats~, but at the moment @weights is an attribute of fluid.bufstats~. It just ignores some of the other attributes of fluid.bufstats~ in a way that, as a user, makes no sense. Unless I’m overlooking something, I don’t know of any of the other fluid.objects~ in which object-specific attributes apply to only parts of the functionality, or end up behaving in an either/or manner (as in, you can use @weights or @numframes, not both).

Perhaps something like @weightsstartframe/@weightsnumframes (ala fluid.bufcompose~'s separate attributes for source/destination buffers) would help here in terms of legibility and consistency in interface.

I like the sound of that!

I know I’ve beleaguered the point but revisiting and modifying patches in the last couple of days, every time I need to create a new buffer, rename an existing buffer (along with every instance of it across all the related objects, which then resizes all the objects) I’m instantly in a non-creative/administrative mindset, rather than a tweaky/coding/creative one.

Rereading this now, it comes off more sassy that I intentioned!

I feel strongly about it, but my comments should be taken with a couple units less sass then presented.

2 Likes

My feeling is exactly the inverse :man_shrugging: If the help file says exactly that, then it oughtn’t (even if that was the impetus for adding it), and won’t for much longer. The object definitely aims to be more general purpose (which isn’t saying that we shouldn’t / won’t try and find a way to make your use case less painful).

Conversely, there isn’t any other object where startframes etc refer to anything other than the input buffer selection.

You can use both, it’s just your responsibility to make sure that the weights buffer matches in size with the segment you are analysing.

My preferred direction of travel for tidying some of this stuff up is towards fewer attributes rather than more (especially on buf* objects), such as making a new object that represents a selection from an extant buffer and can be passed into our stuff interchangeably with raw buffers. Don’t know yet how feasible this is in general, and it’s definitely the far side of a bunch of more urgent work Gerard and I need to do on the codebase in the coming months, but I think – coupled with making objects able to autocreate their output storage when needed – this would cut down a lot of faff, and clean up the interfaces somewhat.

In your use cases though, I think really what you want is a list-based (or, knowing you mc.-based) interface all round, given the restrictions on resizing buffers in the scheduler thread. Again, not yet do-able, but certainly on my aspiration-horizon.

:joy: :wink:

Based on the plenary/forum discussions, my impression was that this was specifically for descriptor-weighting and not a generic buffer transformation utility (hence there being no atodb-ing or other functions included).

From the helpfile:

Sometimes it can be useful to express greater or lesser importance to an observation when calculating summary statistics. For example, we might use a loudness estimate to assign variable weight to statistics on some other feature, which can yield improved (or less surpising) perforamance for things like distance-based matching

I guess in the case of this object, it’s another function added in to an existing object. Not that I’m about having “more objects” but the general loudness-being-a-separate-descriptor/object paradigm has made it so there was no way to do this at source (e.g. fluid.spectralshape~ having a @loudnessweight 1 flag). So the current implementation also means you can’t single out stats for weighting either.

This presumes that upstream of this is two unrelated processes as (presumably) most use cases would analyze the same amount of frames for each descriptor (loudness/spectralshape/etc…) so by the time it comes to this step in the process, you have equal numbers of frames everywhere.

The default behavior here is that, somewhere upstream, I’ve now taken less analysis frames from the source I want to use as @weights than what would actually be in the input buffer of fluid.bufstats~. Other than arbitrarily generating your own @weights (via multislider or whatever), I don’t know where this would happen in a (normal) descriptor-based workflow. Or at least it would be rare enough that it wouldn’t be the default behavior.

I can definitely see how that may be useful/interesting, but short of this being the start of an ears-like library or set of additions, it seems to me that loudness/confidence-weighting would be the main use case for this attribute.

Lists would have been my poison from the start, as they are legible, easily transformable, etc… and when you have to change domains, don’t require loads of boiler plate (peek~-ing things in/out of buffers/datasets). But that ship sailed long ago…

“For example” is quite important there, for me. I don’t follow how adding transformations like a2dB would make it more generic either: seems like it would make it more targeted at the assumption that the inputs are always descriptors.

We are definitely aiming at something more generic than ‘normal’ descriptor based workflows, not least because part of our motivation (and funding) is to create enabling conditions for different workflows to be discovered and developed, and that means that some objects are going to be lower level than one would wish for a particular way of working. Some of these we’ll provide more scaffolding for in the later stages of the project, but in the meantime, I suggest that this is a prime opportunity to wrap your desired behaviour up in to an abstraction that takes care of the faff in one place (presumably wrapping a bufcompose + bufstats, and capturing startframe etc so that the weights buffer can be passed as you expect).

We started with buffers because they’re cross-host, two-dimensional, and don’t have the limitation in size that lists do, and that means we now have a framework in place for doing analysis and decomposition quite generically on stuff that looks like 1D or 2D blocks of data. What remains, in order to make lists a possibility, is to wrap lists up so that the framework can ‘see’ them as 1D blocks like other things (simpler to say than to do at this point). So, not only has the ship not sailed, it’s not even out of the dry dock.

1 Like

I guess I meant in terms of having some more generic, it would be great to see other transformations available in fluid.scale~ (e.g. atodb/ftom/etc…).

This probably is “the thing” about the whole project in terms of what to make available and what not to make available as everything can’t be everything.

The limitation(s) of fluid.scale~, along with other conversations led me to the impression that this aspect of things was not going to be that generic.

There may be a programmatic way around this (for me), but given that everything needs to be quite calculated (for @blocking 2), I find myself with very little reusable/abstracted code. Plus always trying to optimize for the minimum amount of steps in the way by leveraging @numframes/@numchans to handle removing frames/channels I don’t want passed along instead of manually doing so with fluid.bufcompose~ steps. So, as much as possible, I try to avoid having to add extra fluid.bufcompose~(s) in a patch.

This obviously is a quite different discussion that we’ve had in many places/states, but if that’d be possible that’d be great. Or if “staying in one domain” (e.g. chained fluid.objects~ the whole way) could be possible also awesome.

I’m curious to see/hear what feedback is from users out in the wild as even in using these tools for years, making patches is a headspace that is very different from exploring/creativity. It just so happens that as of late I’m having to actually code more of these chunks of code (hence the threads/posts on the friction again), whereas I’ve been happily using chunks I built last year for the most part prior to this.

PA has been working on some embellishments to bufscale (which was originally hacked out just to solve a single problem), and then there will follow the ritual dance where we negotiate about much can be pushed into an single object vs split out into other things. I’m still resistant to the idea that we divert much energy into providing a whole suite of every imaginable operation for buffers because, in Max at least, there are other ways of doing it (ears, gen-without-a-tilde, and then js or jitter for the non-scheduler-bound). But, again, I don’t feel that dB or MIDI pitch conversion are especially general, rather that they’re quite specific to working with particular descriptors in particular ways.

All I’m saying any differently to other times we’ve had this conversation is that I can see how list versions might be achieved. Everything else – including acknowledging the desirability of working with lists when it makes sense – is the same.

1 Like

Great! He plays his cards close to his chest in those weekly geekout sessions!

Well, by generalizable I meant more than the point-to-point linear scaling that it does now. With dB/Hz being quite relevant muso units (which are related, but different from “units of sass”).

That is super interesting/exciting/promising indeed. Particularly since I had resigned myself to writing buffer~ rodrigo 85 256 on my FluCoMa-themed tombstone.

underpromise, overdeliver, my new motto.