Maybe I'm opening a can of worms, but I could imagine tabread etc taking an argument to specify which algorithm to use among a (small) list of those available. The current algorithm would be the default so as not to break existing patches. :)
I'm no expert, but if there are a known set of tried-and-true approaches, maybe they could added through a simple API. This is sort of how I refactored the soundfile type handling in the (as yet unmerged) soundfile object update branch: https://github.com/pure-data/pure-data/blob/0061a083e85ba2548668825b693a72fd... https://github.com/pure-data/pure-data/blob/0061a083e85ba2548668825b693a72fdf98ceef3/src/d_soundfile.h#L47
On May 3, 2021, at 3:37 PM, pd-list-request@lists.iem.at wrote:
Message: 2 Date: Mon, 3 May 2021 15:24:10 +0200 From: Clemens Wegener <clemens@chair.audio mailto:clemens@chair.audio> To: Charles Z Henry <czhenry@gmail.com mailto:czhenry@gmail.com> Cc: Pd-List <pd-list@lists.iem.at mailto:pd-list@lists.iem.at> Subject: Re: [PD] 4-point interpolation changes timbre depending on sample rate Message-ID: <B6FDFF69-EE54-4F59-8234-5A53D086229C@chair.audio mailto:B6FDFF69-EE54-4F59-8234-5A53D086229C@chair.audio> Content-Type: text/plain; charset="utf-8"
I think now would be a good place to pause the implementation and discuss if there is a need for this kind of algorithm in the PD community.
We saw some use cases for the Whittaker-Shannon interpolation where we gain in quality and/or speed. Namely waveguides and pitch shifters.
Is there anything else, where we would like to use this interpolation kernel? Like in general resampling? In the tabread~ for sample playback? Are there really quality or speed issues that we could solve there?
For our use case the code I submitted is good enough. But I would be happy to spend more time optimizing if there is a need / a broad use of that algorithm. In that case we need another restructuring and some help from somebody who is very proficient in writing pd source code. :)
Dan Wilcox @danomatika http://twitter.com/danomatika danomatika.com http://danomatika.com/ robotcowboy.com http://robotcowboy.com/
Em seg., 3 de mai. de 2021 às 12:52, Dan Wilcox danomatika@gmail.com escreveu:
Maybe I'm opening a can of worms, but I could imagine tabread etc taking an argument to specify which algorithm to use among a (small) list of those available. The current algorithm would be the default so as not to break existing patches. :)
I had suggested the same! ;)
I also think it makes sense to specify arguments to tabread~ (or delread~) to specify the interpolation.
Dan you are suggesting an api like this to manage code size and structuring. I see, because when we put everything into the tabread~ (or delread~) class with just flags from arguments/messages, it would get hard to maintain.
E.g. for tabread: So we would create a header „d_array.h" with the function declarations? Should every interpolation type have its own function declaration or do we override for different types? (A bit like an abstract class, but I don’t know how this works in C) The definitions would still go into „d_array.c“?
Refactoring and testing this could take quite some time...
Am 2021-05-04 um 04:35 schrieb Alexandre Torres Porres porres@gmail.com:
Em seg., 3 de mai. de 2021 às 12:52, Dan Wilcox <danomatika@gmail.com mailto:danomatika@gmail.com> escreveu: Maybe I'm opening a can of worms, but I could imagine tabread etc taking an argument to specify which algorithm to use among a (small) list of those available. The current algorithm would be the default so as not to break existing patches. :)
I had suggested the same! ;) _______________________________________________ Pd-list@lists.iem.at mailing list UNSUBSCRIBE and account-management -> https://lists.puredata.info/listinfo/pd-list
(If we go into development details, this discussion could be moved to pd-dev.)
On May 4, 2021, at 10:54 AM, Clemens Wegener clemens@chair.audio wrote:
I also think it makes sense to specify arguments to tabread~ (or delread~) to specify the interpolation.
Dan you are suggesting an api like this to manage code size and structuring. I see, because when we put everything into the tabread~ (or delread~) class with just flags from arguments/messages, it would get hard to maintain.
Exactly. Hacking away and cramming everything in the existing class starts to get unweidly and harder to extend/maintain.
E.g. for tabread: So we would create a header „d_array.h" with the function declarations?
Possibly. I did this approach because I wanted to split out the file type implementations and there were a number of helper functions as well. In this case, if the api is relatively small and self-contained, a header is probably not necessary.
Should every interpolation type have its own function declaration or do we override for different types?
No override.
I'd say there would just need to be a way to pass the array and samples to/from the algorithm, so that would be formalized via function which each implementation can do separately. Then choosing the algorithm just changes which function pointers are called. In the sound file update, the soundfile type is a struct and each implementation sets the function pointers and registers itself on setup (at the bottom of each type implementation file). I used this pattern as it is a common C approach and used through out the Pd sources, as far as I can tell, although I would not say I am a C master having started with C++ & object-orientation.
(A bit like an abstract class, but I don’t know how this works in C) The definitions would still go into „d_array.c“?
That was my approach with the soundfile update. The sound file implementations are in separate files ala d_soundfile_aiff.c.. In this case, I imagine the algorithms are not as complicated, so maybe they can all go in one file such as d_array_inter.c (aka interpolation), etc.
Refactoring and testing this could take quite some time...
Yup. My approach was to start only by refactoring the existing code and separating it without changing anything major. Once that was working, then I started updates and bug fixes. It was maybe 1 month of work with help form others in testing, but I don't think this will take nearly as long.
Am 2021-05-04 um 04:35 schrieb Alexandre Torres Porres <porres@gmail.com mailto:porres@gmail.com>:
Em seg., 3 de mai. de 2021 às 12:52, Dan Wilcox <danomatika@gmail.com mailto:danomatika@gmail.com> escreveu: Maybe I'm opening a can of worms, but I could imagine tabread etc taking an argument to specify which algorithm to use among a (small) list of those available. The current algorithm would be the default so as not to break existing patches. :)
I had suggested the same! ;) _______________________________________________ Pd-list@lists.iem.at mailto:Pd-list@lists.iem.at mailing list UNSUBSCRIBE and account-management -> https://lists.puredata.info/listinfo/pd-list https://lists.puredata.info/listinfo/pd-list
Dan Wilcox @danomatika http://twitter.com/danomatika danomatika.com http://danomatika.com/ robotcowboy.com http://robotcowboy.com/
On Tue, May 4, 2021 at 4:16 AM Dan Wilcox danomatika@gmail.com wrote:
On May 4, 2021, at 10:54 AM, Clemens Wegener clemens@chair.audio wrote:
I also think it makes sense to specify arguments to tabread~ (or delread~) to specify the interpolation.
Dan you are suggesting an api like this to manage code size and structuring. I see, because when we put everything into the tabread~ (or delread~) class with just flags from arguments/messages, it would get hard to maintain.
Exactly. Hacking away and cramming everything in the existing class starts to get unweidly and harder to extend/maintain.
All the changes here are just variations on the same math problem. There's potential to unify the methods. I tend to feel apprehensive about making a swiss-army knife kind of object that tries to solve every problem. It's hard to tell when it actually is the right solution.
Sure but there tends to be a point to where it makes sense. The discussion seems to indicate this. Also, I suppose would have separate objects for sound file types, [wavfile], [aifffile], etc but a unified object clearly made sense in that context. As I mentioned before, it seems like there is a set of "tried & true" algorithms for interpolation...
On May 4, 2021, at 6:23 PM, Charles Z Henry czhenry@gmail.com wrote:
I tend to feel apprehensive about making a swiss-army knife kind of object that tries to solve every problem. It's hard to tell when it actually is the right solution.
Dan Wilcox @danomatika http://twitter.com/danomatika danomatika.com http://danomatika.com/ robotcowboy.com http://robotcowboy.com/
On Tue, May 4, 2021 at 11:25 AM Dan Wilcox danomatika@gmail.com wrote:
Sure but there tends to be a point to where it makes sense. The discussion seems to indicate this. Also, I suppose would have separate objects for sound file types, [wavfile], [aifffile], etc but a unified object clearly made sense in that context. As I mentioned before, it seems like there is a set of "tried & true" algorithms for interpolation...
I'm still looking for a single best review on the subject, but it seems like most methods are either:
You'd think polynomials would be a totally solved implementation---but a few searches turn up relevant, current research [1-3]. Singla and Singh compared window functions with a simple construction that performs better at separating the signals from the noise [1]. I'd really like to read the springer article by Zaitsev and Khzmalyan which has the abstract opening line: "The problem of synthesis of low-order polynomial window functions with an arbitrarily specified decay rate of spectral lobes (optimal with respect to minimization of the maximum side lobe on a specified segment of the frequency axis) is solved." [3] It's from June 2021.
For the window choice, there's Bartlett, Hanning, Hamming, Lanczos, Blackman-Harris, ..., ... If you multiply a C-N function by sinc truncated at an integer, you get a C-(N+1) function. The maximum rate of attenuation increases as you go to higher degrees of smoothness. So, there's a very clear reason why you find variations in performance based on window choice. The windowed sinc function is C0: rectangular window C1: Lanczos, Bartlett C2: Hann C3: Blackman C4: Blackman-Harris (and others.... so many others)
The other parameter you can control is the length of the window. All I can offer is a rule of thumb, use minimum length (2*N+2) for interpolator functions that have N-degrees of smoothness. I'm still working on this area
And check out Sample Rate Converter comparisons at infinitewave [4], to get a look at how much variation there is in performance from one software's implementation to another.
I have yet a third approach that I'm studying: truncated sinc plus polynomial correctors. I think I can make a family of functions that converges to the ideal performance faster than windowed sinc functions as the length and degree become large. There would be a 4-point, 6-point, 8-point, etc...
All of these are functions that are too expensive to calculate at runtime for a fundamental, highly used object like delread~ or tabread4~. So, a table based approach is warranted but has trade-offs. You could choose to calculate the tables at compilation time, start-up, or as needed at runtime.
Max and Clemens have made the first implementation and found a relevant test case. Waveguide synthesis is one where the errors get compounded as the signal gets resampled again and again passing through the delay. I'd bet there are a series of ever more badly behaved waveguide problems that could be tamed by using successively higher-degree-and-length methods
[1] http://code.eng.buffalo.edu/jrnl/Window [2] https://pdfs.semanticscholar.org/2196/5128dd419fd8d0117c97cb23b62724d7c56b.p... [3] https://link.springer.com/article/10.1134/S1064226921050107 [4] https://src.infinitewave.ca/