From the original SynthMaker forum (2004–2014), with its members’ names and dates.
kessito ·
For all the hardcore DSP guru's out there;
I was wondering (a crazy mind-spin) if it could be possible to use the SSE capability of processing 4 channels at once to make some dmn efficient oversampling algorythm.....
So what I mean is to upsample the signal 4 times and then process every 4th bit of the upsampled signal in a different poly channel.
It could be complete nonsense what I'm talking about here and maybe I should cut down on the smoking 😉 but if this could be possible it would be a very cool way to make some crispy sounding dynamics with low cpu overhead (and throw some dirt in the faces of guys telling you that dedicated DSP is the only way to go)
Kessito
skOre ·
While I guess this won't help for the poly, this might be a good idea for the mono.
Shifrin ·
Yeah, should be able to use SM's SSE nature to speed up oversampling when using mono streams. You can use the pack/unpack with some stream/poly process and a bit of ingenuity to get it to process some data four times for the price of one 😉
Shif.
sambean ·
Good idea Kessito.. No need to give up the smokin' if you come up with gems like this.. 🙂
Managed to get it working from tweaking a few old modules. What its basically doing is:
Interpolate (add zeros to signal -> FIR filter), the signal is now in one mono4 channel..
Function, this can be any sort of waveshaping function. note, you can't use any function with delay or feedback ie filters etc..
Decimate (FIR filter -> skip samples)
The reason you dont see any adding of zero's or skipping of samples is because an FIR is being used for the filtering (you don't need to perform the calculations as 0*n = 0 so it can be ignored). I'm actually quite amazed at how it finally looked.. It looks like a normal FIR, but actually its woven across the 4 channels. It appears alot simpler than a code version I had.
The FIR cutoff should be 0.125, but I find you still get a little aliasing, setting it to 0.1 gives a slightly duller signal but alot less aliasing..
You can use it for RM by just including another interpolator..
The example provided has a tanh waveshaper and you can A/B with a normal signal..
This sounds really cool - going to check it out now, many thanks 🙂
Shif.
kessito ·
Thanks sambeam 😀 that's cool! I'm going to check it out for a dynamics algo when I will finally have some time to fool around again 🙁
Kingston ·
I really like the simplicity of this and it pretty much taught me how sinc up/downsampling works.
How could this be expanded to work with filters?
sambean ·
Hello Kingston, and welcome to the forum!
I was surprised myself at the final simplicity and similarity of both the interpolator and the decimator.
WRT using it with filters, I think it would be quite hard to do with IIR filters as the internal state of the filter depends on samples being fed-back and you need to filter each oversampled value contained in the mono4 connectors. ie
Filter channel 0, then channel 1, then channel 2 and finally channel 3
What you would need to do is first unpack the data, then use the code component to arrange the data into a small array and then in the same code component process the filter in a loop:
// First get the data from the unpacked oversampled data
channel(0) = Channel0input;
channel(1) = Channel1input;
channel(2) = Channel2input;
channel(3) = Channel3input;
This technique may be pretty cpu heavy because of the array accesses, so what may be faster and simpler if you are looking to implement a filter with saturation, is have the filter running at the normal sample rate and only oversample the saturation part.
Sam
Kingston ·
Thank you very much.
I expected it would be something like that. I was thinking of using several stages of the sinc interpolator, and splitting the filter after that. Then got tired of writing the solution. (it would be doable though)
This implementation of yours is much more elegant anyway and it's definitely the filters I want to oversample, not just the waveshapers.
Thanks again. 🙂
Kingston ·
Ok I successfully oversampled the standard moog filter that coes with synthedit.
The code you posted is actually unnecessary, or maybe it's more optimal to my solution?
I simply unpacked the interpolated data, used the filter in all 4 unpacked streams separately, repacked them, and sent it to the decimator. (quite the network of blocks is weaved! it even looks elegant)
Works like a charm, and doesn't seem to eat CPU an awful lot.
It sounds great. A whole lot better than the non-oversampled filter naturally. 🙂
(hmm, correct me if I'm wrong, but this method seems to work)
One thing to mind with the oversampling is that it delays the audio, so one has to make adjustments accordingly.
skOre ·
would be nice if you could share your schematics 🙂
Shifrin ·
skOre wrote: would be nice if you could share your schematics 🙂
Certainly would for lazy people like me 😉
Shif.
Kingston ·
Here's the moogfilter 4x oversampled, based on sambeans original example for waveshapers.
It's in stereo, and I guess I should've used wireless I/O for cutoff and resonance. But then, it looks good to have all those wires weaved. 😍
Let me know if you find something fundamentally wrong with that implementation.
(oh by the way this was made with the synthmaker trial version, I don't know if it affects saving schematics)
Guess I'll look into doing a 2x version now.
When someone implements optimal or hermite interpolation choice to this sinc, I think it would be time to include the decimate/interpolate block with the SM package. This is too useful.
sambean ·
Hey Kingston,
I'm not sure if implementing the interpolator/decimator in this technique is actually properly oversampling.. It may be doing something to improve the sound (averaging 4 signals) but it is not following the upsampling/downsampling path..
The parallel packed data after the interpolator is actually representing a series of samples. If you want to filter this series you need one filter to run for 4 iterations on each of the 4 samples in consecutive order for every sample period of the main application. In your example there are four filters running for 1 iterration on each of the 4 samples in parallel. Maybe both these situations are somehow equivalent, but I don't think they are..
I'm working on an efficient version of the piece of code I posted.. I'll post that up when I'm done..
Sam
sambean ·
When someone implements optimal or hermite interpolation choice to this sinc, I think it would be time to include the decimate/interpolate block with the SM package. This is too useful.
Totally agree here, I'm waiting though till after version 1 is released before I start 'heckling' though.. I'd like to see the situation you have upsample and dowsample modules, all they did was insert/remove zeros and everything inbetween them is calculated the approprite number of times 2x, 4x etc.., it could then be up to the user to chose their interpolation/decimation filters etc..
The version I have here has it's limitations, even thought the SSE makes it quite efficient for certain applications, it is unusable on poly connectors, I think we would need a generic oversampling set of modules, that can be used on all the data types and all types of functions..
Sam
Shifrin ·
Here's the moogfilter 4x oversampled, based on sambeans original example for waveshapers.
Cheers Kingston!
I'd like to see the situation you have upsample and dowsample modules, all they did was insert/remove zeros and everything inbetween them is calculated the approprite number of times 2x, 4x etc.., it could then be up to the user to chose their interpolation/decimation filters etc..
That's a great idea, maybe some time after 1.0 is released we could have this sort of thing added 😉
I think we would need a generic oversampling set of modules, that can be used on all the data types and all types of functions..
Agreed, if at all possible 😀
Shif.
Kingston ·
sambean wrote: I'm not sure if implementing the interpolator/decimator in this technique is actually properly oversampling.. It may be doing something to improve the sound (averaging 4 signals) but it is not following the upsampling/downsampling path..
The parallel packed data after the interpolator is actually representing a series of samples. If you want to filter this series you need one filter to run for 4 iterations on each of the 4 samples in consecutive order for every sample period of the main application. In your example there are four filters running for 1 iterration on each of the 4 samples in parallel. Maybe both these situations are somehow equivalent, but I don't think they are..
But in that example of yours the exact same thing happens. You simply moved the 4x (pseudo) filter code in one code block, I kept it separate. The results are equal, but putting 4 moog filters in one code block is a bit messy.
We're not averaging anything here, that's up to the interpolator/decimator stages.
What happens is,
1. sinc interpolator splits audio to 4 streams and packs them to parallel format,
2. unpack to four discrete audio streams
3. we process the 4 streams independently (any process allowed here, even filters as long as the streams are perfectly synched)
4. pack the 4 streams back to sinc parallel format
5. sinc decimator recombines the parallel format back to one stream (not by averaging!).
there could be something I've ignored, but I think that's the way it's supposed to go.
sambean ·
I've had a look into this and seems the method you are proposing does indeed work, although I still can't see how both are equivalent, I'll come to that in a second. Firstly here's the test I performed:
It consists of two examples you can a/b between the first is a simple 1-pole lowpass filter with saturation, the second is the same filter but put through the oversampling modules in parallel(you don't need to unpack/pack the modules, mono4 connectors calculate 4 mono channels at once using SSE). With the first example there is quite alot of aliasing at the high frequencies(the oscillator is being amplified 2x before the filter), as you can hear with the second the aliasing is significantly reduced..
The reason I'm confused as to why this should work is that with this method we are processing the 4 oversampled samples in parallel in 4 filters at the sample rate, whereas what I imagine we should be doing is processing the 4 samples in series through 1 filter at 4 times the rate. Maybe both these methods are equivalent, but I don't see how.
With IIR filters the state of the filter depends on previous output values, so I can't see how from calculating the result in parallel in independant channels which are 'blind' to each other would be the same as calculating it in series where all the data is processed in one filter..
But it seems to work.. I'll need to have a closer look..
Anyway, thanks for highlighting this
Sam
Kingston ·
sambean wrote: With IIR filters the state of the filter depends on previous output values, so I can't see how from calculating the result in parallel in independant channels which are 'blind' to each other would be the same as calculating it in series where all the data is processed in one filter..
Aha! you found the missing piece. I already bumped in the problem of wanting to have a global samplerate value for the whole project. That should be easy to do, but can I also do this in VST exported plugin? Is the samplerate of the VST host passed to synthmaker plug? and how?
Then it should also be easy to use global samplerate * oversampling for the oversampler processes. every block that has samplerate related coefficients (filters etc) should be treated like this.
I'll test tomorrow when I have the time.
Thanks for the idea.
sambean ·
Had a good ponder and think now both methods are in fact equivalent. By thinking about it in terms of impulse responses and convolutions doing it in parallel is the same as doing it in series, as far as I can tell..
This has opened a few doors.
Thanks for this..
Sam
sambean ·
Is the samplerate of the VST host passed to synthmaker plug? and how?
Yup, there is the timing info module..
Kingston ·
sambean wrote: Had a good ponder and think now both methods are in fact equivalent. By thinking about it in terms of impulse responses and convolutions doing it in parallel is the same as doing it in series, as far as I can tell..
This has opened a few doors.
Thanks for this..
Sam
Yeah, if we do it in series (your implementation), then we need to change the coefficients as we're dealing with a highsamplerate version of the audio. Maybe parallel is more efficient though, especially if we can use the SSE pipeline for this?
sambean ·
Maybe parallel is more efficient though, especially if we can use the SSE pipeline for this?
Yeah, will definately be more efficient, no sorting of data before looping etc. and mono4 connectors can take care of the SSE..
Shifrin ·
This is a really interesting discussion, cheers guys 😉
Shif.
Kingston ·
sambean wrote:
Maybe parallel is more efficient though, especially if we can use the SSE pipeline for this?
Yeah, will definately be more efficient, no sorting of data before looping etc. and mono4 connectors can take care of the SSE..
Great. So I can pass the unpacked data straight into the code block using mono4?
loop4 will take care of SSE parallelisation of the filter calculations?
I think I'd need four sets of independent filter variables for each pipe.
sambean ·
Kingston wrote: Great. So I can pass the unpacked data straight into the code block using mono4?
Yup, in the example that came with the interpolator/decimator that tanh function that was being oversampled was using mono4's basically 4 tanhs in parallel.
You can also feed different variables to the different channels using the pack on any variable inputs, so for example if you have a biquad filter with coefficient inputs, you can feed each biquad different coefficients, effectively having 4 different filters in parallel. Good for cutting cpu on stereo fx.. You can also do series processing by feeding back the mono 4 and offsetting the outputs and inputs with pack/unpack modules. You incur a 1 sample delay between each stage though with this method.
Wrote something here? To have a post removed or your name hidden, write to [email protected].