Gen~ simple data management with buffer

oshii's icon

Happy new year everyone!

Hopefully you've had nice new year's celebration. Mine mostly went in banging my head with Gen - my favourite head banger these days! :D

Honestly, I'm not quite happy with the amount of information provided in the help files/ Gen references, which (not for a first time) couldn't help me finding solution for this simple problem which would take just few minutes to solve in Max: write simple data in buffer.

I've attached a simple patcher, would be really happy if someone can have a look and let me know what I'm doing wrong.

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

mizu's icon

Hello OSHII thanks and Happy new year so much !

sharing the same perplexity about poke in gen. (nb: function isn't best idea to begin, index is an integer first... To interpolate is for me now a mystery ) in progress ( my way to explore ):

without gen~and poke :

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

with gen~and poke ( some mystery allways )

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

oshii's icon

Thanks for your response.

Your patcher doesn't work as expected, because in your main patch you have toggle object connected to inlet 2 of the gen~, which keeps sending a constant signal with value 0 or 1 to it, which keeps trying to poke to the buffer whatever you are sending to inlet 1 (0 or 1), so no matter what you do with the multi slider, it's always overwritten by that.

The overdub function is also a not fully understandable to me yet and as I said, the help file is not of much help.

I think the key to the solution (or bigger part of it) of both your problem (multi slider) and mine (function) is to manage to feed poke's both inlets with information only for a short duration. That's what I'm trying to achieve with the help of [change] and [latch].

Graham Wakefield's icon

It's not entirely clear to me yet what you are trying to achieve. You have a function UI object, which can store a series of values at arbitrary positions between 0.0 and 2.0 (because of "domain 2"). Sending "dump" to the Function will just output this sparse set of points (3 points in your patcher example).

You seem to want to copy this shape into a buffer~ FuncPt, where the buffer~ is defined as being 5ms long (which is 221 samples at 44.1khz). A buffer~ is a series of sample values. If you have 221 samples, then you need to define a distinct value for each one of them.

It's not clear to me why you want to use gen~ here if you are just filling a buffer~ from a function object. You can just fill the buffer~ directly in Max. The important thing to remember with gen~ is that everything is an audio signal, and the patch is always running at audio rate all the time, and that's what it is best at doing. What you are doing here is inherently a process using messages and lists (neither of which gen~ supports) and I presume isn't something you want to be happening continuously all the time. Of course you can still use gen~ to read from this buffer~, and write and do other operations on the buffer~.

OK to fill 221 samples of buffer~ FuncPt with interpolated values from your function, logically the first thing to do is to sweep over the domain of the Function in such a way that you produce 221 values, and poke them into the buffer~ one by one. Here's one way to do it:

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

oshii's icon

Thanks for your time Graham!

Are you saying that I always need to fill the whole buffer capacity?

To answer your question: I guess it is not pretty clear what I am doing, because in general I'm not doing something much, just trying to wrap my head around gen~. To do that I'm just exploring how different Max functionalities can be translated into gen~. In a previous forum post someone asked how lists can be managed in gen~ where coll object is not available, and the response was that buffer is to be used instead. In my particular case, I am using Function as an amplitude envelope, and was working on patcher which would make its points float randomly around set original points.

So, my idea was to store the functions points' coordinates in buffer, from where I could change them as i like (whatever deviation percentage I want).

Graham Wakefield's icon

Do you know the difference between bitmap and vector graphics? https://filecamp.com/blog/vector-vs-bitmap-images-explained/

It's similar here. Buffer is like bitmap, Function is like vector graphics. With a buffer you have to specify a value for every point in the buffer (like every pixel in an image). With a Function you just specify the coordinates of specific special points, and everything in between is calculated only when needed via math. Converting from vector to bitmap means deciding a resolution (the number of samples in the buffer) and performing the math (which Function does) to generate each element (each sample in the buffer). [Side note: converting from bitmap to vector is a lot harder].

Or in math terms, you can think of these as implicit (Function) vs explicit (buffer) representations.

But here's a thought: instead of rasterizing into a buffer, you could just store the time & value coordinates into a buffer, and have gen~ do the math on the fly to turn them into samples as needed. They are linear segments, which means the math is just linear interpolation.

Here's how to copy the coordinates of a Function into a buffer~. The buffer~ here has two channels, the first channel is the coordinate times, the second channel is the coordinate values. So each 2-channel frame of the buffer is one coordinate of the Function. Note that this can handle up to 16 points, because the buffer~ has 16 samples.

And here's how to play that back in a gen~ patcher:

First we have to define a speed to move through the Function's time base. Here I used an accum, which is just a sample-rate integrator (a counter).

So on each passing sample of real time, it moves a little bit through the time base of the function. Then it checks via the > whether this time has gone past the end of the current segment, and if so, it increases our segment step (the second accum counter).

(Both accum ops get reset to zero if the gen~ input in 1 is triggered, which will restart the function playback at the beginning.)

The 2nd accum, the current segment, is what we use to index into the buffer and find out what our nearest two coordinates are -- the previous and next points -- which we read via peek operators. This gives us both the times and values of these coordinates.

Finally we use the scale operator to do the actual interpolation math. The scale op just linearly maps an input from one range to another range. Here we map the current time from the range of times to the range of values, which gives the interpolated value output.

A versatile patch :-)

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