Hi Miller,
this sounds great! First-class multi-channel support would be a real game changer.
Actually, after Winfried Ritsch told me about the "pd_snake" project, I came up with a couple of ideas on my own. You can find them here: https://git.iem.at/pd/pdsnake/-/blob/master/docu/discussion.txt. Don't know if this aligns with what you are envisioning, but it might give you some inspiration either way :-)
In particular, I would like to point out https://git.iem.at/pd/pdsnake/-/blob/master/docu/discussion.txt#L33-41. This would allow us to create patches where the channel count can be changed dynamically with a single message!
Also, multi-channel signals would give us a chance to vectorize DSP algorithms that are otherwise hard or impossible to optimize. For example, with modern AVX instructions you can compute 8 oscillators or IIR filters for the price of 1. (With proper manual loop unrolling, just like in the "*_perform8" methods, some compilers are able to vectorize it automatically.)
(one question about this... I _could_ take a sightly bigger risk and put the last 3 fields ahead of s_refcount, etc, which I don't think anyone should be using... this would make things look cleaner).
I think this should be fine.
typedef struct _signal { int s_n; /* *TOTAL* number of points in the array */ t_sample *s_vec; /* the array */ t_float s_sr; /* *TOTAL* samples per second */ int s_refcount; /* number of times used */ int s_isborrowed; /* whether we're going to borrow our array */ struct _signal *s_borrowedfrom; /* signal to borrow it from */ struct _signal *s_nextfree; /* next in freelist */ struct _signal *s_nextused; /* next in used list */ int s_vecsize; /* allocated size of array in points */ /* *** NEW STUFF *** */ t_float s_rate; /* sample rate */ int s_length; /* number of points in each channel */ int s_nchans; /* number of channels */ int s_overlap; /* number of times each sample will appear */ }
Personally, I would keep s_n as the number of samples /per channel/. The total number of samples is simply s_n * s_nchans. Existing externals - that do not know about s_nchans - would effectively operate on the first channel and ignore the rest. Newer multi-channel-aware externals, on the other hand, may use all the channels.
I also think that DSP objects would need a new API method to create multi-channel /outputs/. The general idea is that the /input /channel counts are taken from upstream, but the /output /channel counts are specified by the object and passed downstream. (There might be objects where input and output channel count differs; any kind of merger/splitter/mixer objects comes to my mind.)
I think I have some more ideas/notes in one of my notebooks. I can look them up and see if there's something useful.
Anyway, I am quite excited about this!
Cheers,
Christof
On 01.09.2022 21:58, Miller Puckette via Pd-dev wrote:
Hi Pd dev -
I'm preparing to rework the DSP network to give tilde objects more control over their inputs and outputs, for instance allowing for multi-channel signals and to allow objects to decide for themselves whether to promote float inputs to signals (so that you don't have to say "+~ 0 to get the faster version, and so that I can make the hip/lop/bp/vcf frequency and Q inputs available as signals or as floats).
Of course I mean to make this compatible with existin DSP objects, although for simplicity I'm going to propose one slightly risky move, changing the size of the t_signal structure -- as iohannes mentioned a few years ago, this seems very unlikely to break anyone's tilde objects. The new structure would now look as follows:
typedef struct _signal { int s_n; /* *TOTAL* number of points in the array */ t_sample *s_vec; /* the array */ t_float s_sr; /* *TOTAL* samples per second */ int s_refcount; /* number of times used */ int s_isborrowed; /* whether we're going to borrow our array */ struct _signal *s_borrowedfrom; /* signal to borrow it from */ struct _signal *s_nextfree; /* next in freelist */ struct _signal *s_nextused; /* next in used list */ int s_vecsize; /* allocated size of array in points */ /* *** NEW STUFF *** */ t_float s_rate; /* sample rate */ int s_length; /* number of points in each channel */ int s_nchans; /* number of channels */ int s_overlap; /* number of times each sample will appear */ }
(one question about this... I _could_ take a sightly bigger risk and put the last 3 fields ahead of s_refcount, etc, which I don't think anyone should be using... this would make things look cleaner).
For example, the FFT object's outputs should really have a sample rate of 1/N times the input sample rate, a vector length of 1, and a channel count of N. For compatibility, I'd take the "s_n" field to just be N, although in the future one could optionally use s_length as N and run as many DFTs as there are channels. (This would be incompatible with current practice in wierd situations in which one ran an fft~ objects into another fft~ objects as input - a real bad idea but perhaps the only way in vanilla to time-reverse a signal block by block, so I bet someone is depending on being able to do that :)
Meanwhile, before the DSP routine is called, all signal inputs are populated with vectors by promoting float inputs to signals, all inputs are guaranteed to have the same s_n field, and all outputs are automatically generated to match all the inputs. I want that to be the default option but to allow the object to access non-matching signals, not-filled-in signals (so that it can schedule scalar versions, as in "+~", and to take care of generating its own output signals (which may thus have different sizes from the input signals).
I could then design a "trunk~" object that combines or splits one-channel signals into multichannel ones, and I could extend +~, etc., to know how to add one-channel signals to multichannel ones. Also, clone~ could (optionally) unpack multichannel signals to distribute among copies.
it might also be useful to have the option to ask for the output signals, if auto-generated, never to reuse the same vector as the input; I guess that can be provided if there's a demand for it.
I'm thinking this is a big enough and dangerous enough change that I should do it on a separate branch first. I've got some travel coming up but hope to start coding soonish.
cheers Miller
Pd-dev mailing list Pd-dev@lists.iem.at https://lists.puredata.info/listinfo/pd-dev