sampler structure/efficiency question

woyteg's icon

hey!
I'm building/rebuidling kind of a sampler. i need about 8 buffers, but only one [play], [xplay].. so it won't be polyphonic.
My question now is, is it more efficient to have the playing object insie a poly and mute all instances but one at a time or to just send a set buffer1, set buffer2 etc message to the playing object?
I tend to believe the second solution is better, what do you think?
Thanks a lot!

woyteg's icon

oh and another question. I am using polybuffer~ the first time. I can't seem to find a message like (replace the file in buffer 3 with ... .aif), but only append or read folder messages. Isn't there anything like that? That would be a real pity.

pdelges's icon

You can use the send message:send 3 replace ... .aif

p

woyteg's icon

Ah! Thanks a lot!

Dave Mollen's icon

With poly~ you can make a nice crossfade. Otherwise I don't really see why you'd need 8 buffers. Because if you don't care about interrupting the dsp chain then you might aswell just use one buffer and change the sample with the replace message.

woyteg's icon

I don't think i interrupt the dsp chain when i change the buffer a groov object should read from, do i? seems to work really fast.
i'm planning to change the referenced buffer at quite high rates and i'm quite sure reading a new file in is the slowest option, isn't it?
I mean my whole question is about efficience in the sense that i want it to react at hight rates and be as less CPU consuming as possible.

Peter McCulloch's icon

I'd recommend polybuffer~. It's fast, since you'll have already loaded the files, and you're just changing the memory pointer for the current buffer. It uses more memory, but that's not a big deal at all. It also means that if you do want something to overlap, you're not going to create a click or gap as you change sound files.

I'd say this quote from Donald Knuth is really good to bear in mind regarding optimization:
We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil.
(and I'd highly recommend the Wikipedia page on program optimization as a primer)

As long as you're not at the wall, you're going to be a lot happier with a solution that works well musically than one that runs 5% faster. Easy to control code makes for better music, and saves you time now in terms of complexity, as well as in the future, in terms of maintenance and flexibility.

I'd also highly recommend using poly~. I think a lot of people avoid it unnecessarily because it adds a slight amount of complexity, but the rewards are substantial in that it actually gets rid of some of the complexity that people create trying to avoid using poly~. (voice-handling/allocation, overlapping, etc.) Once you learn the adsr~ + thispoly~ combo, you're pretty much set. You can also very easily adapt your abstractions to use poly~.

The only way to know for sure if process A is more efficient than process B is to profile your code. Poly~ is a nice way of doing this since you can load up n copies of whatever with ease. I don't see anything in what you're describing that suggests a huge bottleneck with using poly~, so I'd say don't worry too much about any costs from using poly~ here.

In general, I think that most signal processing patches should be implemented as a poly~ for all but the most trivial cases or unless there's a highly compelling need otherwise. Here's what you get:
- Overlapping and polyphony. It's very rare that things don't overlap, and when you need it, it's already there.
- Not having to write your own extra handling code which will be buggy. (try it; it's a PITA)
- Muting
- Down-sampling/up-sampling/different vector sizes
- Almost as efficient for almost everything.
- Way more flexible, since you can also dynamically swap out poly~ patches.
- Parallel processing algorithms such as vocoders, spectral delay, etc. become trivial to implement and substantially more flexible. "16-band vocoder? Oh, you wanted 8 bands? Okay, let me change the argument to poly~..."

I'm a little bit zealous about the use of poly~, but it's always served me well. It also helps to promote good software development practices like decoupling controls from DSP which makes things easier down the road and that is a good thing IMHO.

Dave Mollen's icon

Ah, I understand. You're right, changing the reference for the play object is way faster.
But you'll still want to use poly~, at least two instances of them. Because when changing the reference you'll get some discontinuity. With just two poly~ instances you'll be able to make a crossfade. But it might of course be that you don't care about little clicks.

woyteg's icon

Fisrt off, thanks a lot, i really appreciate you took the time t think about this!
About optimization, these are interesting thoughts, but i really want to do some optimization, this actually is a patcher that is.. hm i guess more than 4 years old and that i am continuously working on. now i am thinking about finally changing the structure of it since i need some CPU overhead for other features i want to implement.

Yes, i also tend to using poly, and it actually was my first thought too.
Anyways, maybe i even follow Peters suggestion and put the whole thiong inside a poly but still do the "sample change" vie reference to another buffer inside polybuffer~.
So the question that finally stays is, does it have a reason i am kind of skeptical about changing the buffer of a groove~play~ whatever object at very high rates?
(i will deal with clicking with windowing)

Roman Thilenius's icon

8 buffers and one play~ would be my choice, too.

of course changing the buffer to read from does not happen sample exact this way, but otherwise it is the best of all thinkable solutions.

-110

woyteg's icon

".. is the best of all thinkable solutions."

sorry to be kinky about this, but since you say it that way..
What about having 8 [play~] instances inside poly~, use 8 buffers~(rather one polybuffer~ actually) and muting the voices not needed. one could stay sample accurate if one could unmute in advance. Maybe leading two having always two voices active. which would still be a vast improvement compared to the patcher as it is right now. And if i wanted to have crossfading at some point, it could be implemented easily. I really don't care about crossfading in this situation, but this seems like a very common thing and having a good recipe for it is useful for many people i hope.
any thoughts?
Thanks again!

Roman Thilenius's icon

it would be worth a test, but i believe that putting 1-2 simple audio externals into a poly~
will double the CPU that takes when all of them are on.

okay, in your situation you might run only a max of two instances, but the poly~ itself
might still be an issue. plus it is making the patching lots more complicated.

8 play inside poly? i am not sure why. one (or two for the crossfade option) should be
just fine.

and multiple buffer objects ... do not really eat up cycles when nothing reads from them.

i cannot really comment on polybuffer, i have never used it and i dont think i would when
the aim is to just load multiple files.

-110

Peter McCulloch's icon

I guess it really depends on a number of things:
What is the process triggering this? (i.e. what is the sample accurate process (oscillator/sah/?) that is playing the soundfiles/loops, or do you just want it to pretty accurate and not click?) Groove~ doesn't trigger sample accurately, so I'm guessing it might be the latter; this is good because it's easier.

If sample accuracy is absolutely important:
1. Are you always playing from the same predefined locations in the soundfiles? If yes, then use sfplay~/sflist~ with the preload message. You can trigger cues sample accurately. IIRC you can have around 255 cues or so. (though maybe more)
If no, then you could copy your files into one buffer. It would be really awesome if there were a convenient way of doing this, but you'd probably have to roll your own for the time being.

2. As for as optimizations go, it's kind of hard to say without seeing your code (post it?) but, generally speaking, the core sound making parts are not always great candidates for optimization. For example, in a synth you don't want to skimp on processing power for your oscillators or your filter(s). If anything, you're much better off looking at doing things like downsampling adsr~ and LFOs (wrap them inside a poly~ patch!). That saves a ton of CPU, since for both of those things you can usually downsample by a factor of at least 16.

Poly~ really doesn't introduce a ton of overhead. I write synths that are pretty heavy (6-7% CPU per vox), but when there are no voices playing, the CPU usage is very, very low. One thing that may be skewing the CPU usage that you're seeing is that all voices are on by default in poly~. You can loadmess "mute 1" into all of your thispoly~ objects to get a more accurate sense of runtime overhead vs peak overhead. For whatever reason, adsr~ is inconsistent in that at load time it is at 0 but there's no mute message from its 3rd outlet (whereas for the remainder of its life, this is NOT the case). I consider this a bug myself...

It's good for things to be more efficient, but--as someone who as gone down that rabbit hole--you want to make sure you're spending your time efficiently.

woyteg's icon

@roman, puh some misunderstanding..
I made a typo, i meant 1 play inside poly not 8, which is really a stupid point for a typo in this thread :),
what i meant are 8 poly instances, each with one play oject, of course. And, maybe having always 2 instances unmuted.
The whole playing engine doesn't only consist of a play object and i did some tests, poly does cost something, but it isn't a lot.
tests:
[phasor]
[* 5]
[play~]
[out~]

200 times in a patcher without any poly stuff i had ~36% average audio cpu
200 instances of the little patcher loaded inside of one poly i had 37 %
200 polys with one instance loaded i had 43%

patches are attached, a bit confusing naming though.. sorry

Max Patch
Copy patch and select New From Clipboard in Max.

no poly

Max Patch
Copy patch and select New From Clipboard in Max.

200 [poly~ 1]

Max Patch
Copy patch and select New From Clipboard in Max.

poly toplevel patcher

Max Patch
Copy patch and select New From Clipboard in Max.

poly guts

cheers!

woyteg's icon

Thanks again peter!
Great stuff. Mainly about the adsr downsampling, what a hint, i would have never thought about that. So as you can see i am not too deep in the optimization trap :)
Its just, that i know a have been using this patch a long time and i will be using it for a long time onwards, and i know for sure it is quite inefficient right now. It needs a structural change and those are timeconsuming, so i try to use my time efficiently and talk to you guys on the forum before going about things.
And yes i am on this forum for too long time to not know that it's really not great talking about patches without posting them, but this patch is really a whole lot bigger and messier than it sounds, so i will maybe post something if i made structural changes and cleaned it all up a bit.
Thanks a lot again, i'm really greatful and enjoy this alot,
have nice day all

Peter McCulloch's icon

I'd recommend polybuffer~ because it takes care of the numbering and the loading of a bunch of different soundfiles. Sure, you can use 8 buffer~ objects, but it's always nice to avoid hardcoding.

Here's something that I cooked up for one of my classes. It's using polybuffer~, and it's randomly picking a segment of a specified duration from one of however many files. I've got one of the early generations of MacBook Pro and it only takes 10-14% with 32 voices and this is without optimizing envelopes, etc. and with the generating metro set to 0 ms. It doesn't do looping (I was using play~ for the class), but you can probably add that later.

Max Patch
Copy patch and select New From Clipboard in Max.

Since your process might be dealing with note-ons, here's an abstraction that I use all the time when dealing with poly~. My approach is to feed poly~ giant lists of parameters with the assumption that usually the only thing I care about on the note-off is that it's a noteoff (hence the effect of this which is a lot like stripnote, but for additional parameters)

4191.MySamplerPlayer1.maxpat
Max Patch
Roman Thilenius's icon

no, i got you , i got you, of course one per instance.
but why anyway? when you dont want to run them all at the same time. the play could be outside the poly just fine.
and then i also dont see the point of having the 8 buffers in a poly, as turning them off when not needed wont save much.

woyteg's icon

i would put them inside polys so i can mute them, i mean it does make some dififference if i have 8 [play~] s and really a good load of other calculation running, or if i have them inside poly, or even polys(strange idea, i'm getting tired, its 2 am here) and mute 7 or six of them, no? So actually just because i now i don't want them all at the same time, and at the moment the CPU is doing the work as if i needed them to.

and again, i was/am a bit sceptical about just switching the referred buffer, also because i can't do it at signal rate.

yeah, putting 8 buffers inside one poly, using one buffer per poly instance and all that jazz is.. i don't know, i would just keep the buffers out of the poly buisiness, doesn't seem to make sense, maybe i said something misleading somewhere up there. Polybuffer is an option, maybe i wrote poly buffer instead of [polybuffer~] ?

edit: i wanted to add that my little play~ patch is actually consisting of a regular xplay~ object and additionally there already is a poly with two instances inside there which does some granular stuff (two instances! thats why i want to optimize), so you can at least assume 8 plays~ really mean 8*3 play~s and more..

Roman Thilenius's icon

you cannot switch the buffer at samplingrate using poly either, because turning off and on the poly happens only at the beginning of a vector, just as it would with groove~, for example.

woyteg's icon

yes i just thought if i knew what poly instance would be active next, i can unmute it before and have everyhing still running at samplerate.
So just to be clear again, this would be the [poly mygrrovepatch 8] sollution with the buffers outside of course.