Max: Procesing buffer~ with MSP objects (hacking)

And if there’s anything you think the community could contribute to helping it get nearer release readiness…

Well, that would be wonderful :slight_smile:

I’ll try to see whether I can tidy up things slightly and put them on github.

If you want to help that would be amazing, there’s one thing which I would love to have but which I am unable to do: a general off-line processer of buffers via “lambda” paths. I have been doing HOA lately and it’s painful to wait for a playback and record, whereas I could just take a buffer, pass it through ICST/SPAT/HOA tools and have it bounced. Think of a [ears.process~] that takes a buffer and uses a sort of bach-like lambda inlet/outelt behavior for signals. You would be able to process offline any treatment (say, your own beloved timestretcher, and not the ones from rubberband and paulstretch provided). That would be a game changer in the library, I think.

After discussing with Andrea it looked like this was not possible and seemed more likely that the behavior should have been poly~-like instead (which I hate, but the best is enemy of the good).

1 Like

What was the obstacle? If these are signal processing objects you’d be wanting to use, but as if they were offline processors I guess you can’t just make an instance in C and set to work because one doesn’t know its processing mehtod, as this gets registered via the dsp64(). But, maybe (mwahaha) one could send a fake t_object *dsp64 pointer to that function such that the external ‘resgisters’ its processing method with ears.process~ instead.

@a.harker you have much more experience of making the Max API go to strange places. Is that a totally ludicrous idea?

1 Like

Well, the obstacle was exactly that: my complete ignorance of this kind of hackiness stuff. :slight_smile:

Again, if you or @a.harker would like to contribute I would be thrilled. I’ll put it on github ASAP, and send you the link. Differently from bach, the library should be rather transparent (well, still C and not C++, but still better than bach, I hope).

I also read @rodrigo.constanzo’s post, if you think you may benefit from it I could weigh in in one of your meeting and present the library at some point. It’s really a stupid do-nothing library (no intelligence, no real research, just development), but it helps me all the time.

1 Like

I would be down for that, particularly since it may solve a couple different problems and/or patch holes in the current FluCoMa workflow.

I would also be interested. I’m using the 2017 version and would love to see where it stands now.

Is the question “can I load a max patch into another max object and run it’s DSP processing from/to buffers”?

If so then the answer is, I think, yes, with a few possible gotchas if certain things are in the patch, but it’s little involved and you’d probably need custom “in~” and “out~” objects to feed the buffer I/O to /from the patch. I can’t commit at the moment to contributing a full solution, but I can definitely commit to appropriate teleconferencing calls that might be able to outline what that solution would look like and some technical details of how to do it - any good?

I’m not entirely following the argument above, so it may be that you are already aware that this approach (which would be poly~-like) is possible. I imagine you might be able to make it more lambda-like, although I suspect you’d still have to host the lambda in a custom object - I don’t think something like a sub patcher lambda is viable, either because it’s not possible, or because it is, but might be too unreliable - I’m not 100% on that and could brainstorm - I think if you want it to be able to hook up to anything it’s a no - if you’re comfortable with limiting it to subpatchers/abstractions, you might be able to detect the patch, copy it internally and run it that way, which would be relatively safe. I’d need to check the API for the copying stage.

1 Like

Hi Alex, precisely. Yes, I suspected that one could do something poly~ like (I think you already did it for your tools ;-).
Programming with polys is less immediate though (1 more file, handling ins, outs, etc.), though, so I was hoping for something more direct in lambda style, say
[ears.process~] X [supervp.play~]
but I would be amazed even for something like
[ears.process~] X [p supervpplay_inside]

As far as programming is concerned, this is not my cup of tea, though, and it would take me too much time to get into it right now, so that’s not going to happen soon on my side. Again, if anyone else wanted to contribute, I may share the github repo once I’ve made it. I joked about “commissioning” the tool to Andrea at some point :wink: but it doesn’t looks likely in the short period.

In any case I’d be glad to share and discuss the library with you at some point after the plenary. Maybe there are other ways.

1 Like

If you want to chat sometime that’d be cool - most likely it’s easiest to understand what you would want with some discussion, but the bottom line is that if you’re in a generic patch you can’t control the DSP (max will do it’s own thing) so anything you’d do here relies on you being able to internally host a copy of a patch of some kind. There might be some ways to make it look like you are just programming in a lama, but then there will be edge cases where the artifice becomes apparent or has weird edge effects - again probably best illustrated with examples discussed on screen, with some mock-ups.

Ooops. I’d forgotten this thread. I had in mind something much more lofi than dynamically loading patchers, which was simply talking to compiled signal processing externals in such a way as to get the address of their perform funciton, and passing a buffer’s data through this. Something like (e.g.) [ears.process biquad~ @inputs 0.9 0.2 0.3 0.9 -0.99]

I’m sure there’s a very good reason people haven’t already done this though :grimacing:

Well - that should be possible but hacky - it sounds like the sort of thing only someone unwise like me would do - I’d advise copying the object internally, making a suitable call to the dsp routine and then several to the perform routine - these are fairly easily retrievable. You’d run into issues if you are reliant on the state of the actual object in the patch (and I wouldn’t advise sending these calls to that because Max will have it’s own ideas about that and definitely that conflict will become a problem), so for instance if you send a message that changes the internal state of the object you won’t see that when you make a copy and then it might get too weird to be useful.

Sorry - replied concerning a lamda config - the sort of thing @weefuzzy has posted seems possible.

I like to feel I’ve grown during our time together.

I’d advise copying the object internally, making a suitable call to the dsp routine and then several to the perform routine - these are fairly easily retrievable.

I was thinking of making a whole new instance, given the class name

make instance 
call DSP with fake DSP64 object
hit perform multiple times with sigvs-ish sized portions of buffer

Much of the discussion here has gone right over my head, but I wanted to restate how useful something like this would be (or another way to get at the same kind of thing (though a generalizable thing would most certainly be useful elsewhere)), particularly with the partial hint by @weefuzzy and discovery that native fluid.buf...~ objects are happy to take signal-rate input.

So if that follows through to its logical conclusion, being able to have a fully fluid.buf...~-based “signal” path for things would be very desirable (to the point that I’d have to A/B test a bunch to see if leaving the signal domain to do something like weighted averages would offset the loss in latency).

It also occurred to me this morning that another interesting use case for this would be as a ‘brute force’ way of biasing a query, if applied to a buffer~ that will be used used as part of another query.

So you have a a bunch of descriptors (and/or reduced macro descriptors) that have been fluid.bufcompose~'d into a buffer~ for a knearest-type query, but you care more about some than others. Just apply a multiplication factor to each, so that given a normalized or otherwise scaled space, that would make those less significant in the overall distance matching (ala what @danieleghisi was suggesting they do for their weighting in queries).

Basically yes, but you’d need to figure out what messages the dsp64 object needs to respond to - in theory people might call it from either the dsp or the perform routine, although I’m not sure what the latter would be used for.

It needs to be safe to pass it to whatever dsp routines expect one, as well as anticipating any other messages that might be in someone’s code, for which I expect most signatures will be A_CANT, which makes them hard to figure out. “dsp_add64” is the obvious one here, but I’d probably iterate over a real dsp64 object’s message list to find out all the things it can respond to - this is probably the most brittle part of the system, although if you get it wrong, the most likely issue is just a “can’t respond to” kind of error.

Ok, I’ve had a quick bash and got it to a sometimes-working state. With basic binary operators *~ et al, it works as hoped. slide~ and thresh aren’t happy though (I never get a callback from dsp64 that gives me the perform function pointer). Possibly:

  1. Like @a.harker suggests, my fake dsp64 class might need to offer more than just dsp_add64 for a wider range of objects to be happy
  2. I’ve misunderstood how to spell the connection information that I pass to dsp64
  3. These (quite old) objects still don’t actually support dsp64 and I’d have to fall back to The Old Ways (is this even a thing still?)
  4. The flags argument to dsp64 actually means something, and I should find out what
1 Like

Yeah, this isn’t going to work in all cases. Some built-in objects don’t seem to do the SDK reccomended thing of calling object_method to add themselves to a DSP chain, but call the dsp_add64 function directly :cry:

We talked about that yesterday and I suggested to @danieleghisi to take @a.harker’s hint and check at his dsp-chain construction in the dynamicdsp~ code - that could work I think and would be sexy!

Official max objects don’t need to call object_method - that’s just something that aids backwards compatibility, which official objects don’t need to care about. I actually expected dsp_add64() call dsp_add64 on the chain, but it looks like it is actually the method that does stuff.

In this case you might need a real DSP chain, but to be honest it’s getting very brittle and hacky, because you’d need to inspect the contents of the chain, and I wouldn’t be confident that I could get it right. It’s more complex than what I do in dynamic DSP and so there I have a real patch and can let the dsp stuff work normally - at this point that seems like a more viable approach than hosting individual objects, given what @weefuzzy has found.

1 Like

Ok, having a look now at whether I can build a patcher on the fly and use dspchain_compile to get a proper DSP chain that I can push vectors through.

1 Like