Resampling/sample degrade

Hi everyone,
I am trying to find the best way to resample or degrade the sound that is output l.
Similar to if you manually resample something in DAW by reducing the sample rate hz, or similar to what the cymatics origins plugin does.I have tried the degrade FX module and also the sampleandhold script node but both introduce a robotic noise as part of the resampling.
I thought I could possibly filter out the additional noise but the results weren't great.
Can this effect be achieved and if so, what is the best way to implement it please?
Thanks in advance

@rzrsharpeprod said in Resampling/sample degrade:
Hi everyone,
I am trying to find the best way to resample or degrade the sound that is output l.
Similar to if you manually resample something in DAW by reducing the sample rate hz, or similar to what the cymatics origins plugin does.I have tried the degrade FX module and also the sampleandhold script node but both introduce a robotic noise as part of the resampling.
I thought I could possibly filter out the additional noise but the results weren't great.
Can this effect be achieved and if so, what is the best way to implement it please?
Thanks in advance
I've had not had a reply on this unfortunately and this forum is excellent at offering help. So does that mean it's not possible or easy to implement?

@rzrsharpeprod
I'm very unfamiliar with scriptnode, but I guess you could achieve your wished result there.
Edit...Sorry, I didn't read well enough to see you already tried that 
@rzrsharpeprod said in Resampling/sample degrade:
Hi everyone,
I am trying to find the best way to resample or degrade the sound that is output l.
Similar to if you manually resample something in DAW by reducing the sample rate hz, or similar to what the cymatics origins plugin does.I have tried the degrade FX module and also the sampleandhold script node but both introduce a robotic noise as part of the resampling.
I thought I could possibly filter out the additional noise but the results weren't great.
Can this effect be achieved and if so, what is the best way to implement it please?
Thanks in advance
are you talking about bit reduction or sample rate reduction?

@Lindon Samplerate ideally. When I used the built in functions though they introduced robotic sounding artifacts which is something I was trying to avoid as it doesn't sound good.
What I'm hoping to achieve is something similar to when you manually reduce the samplerate & resample in DAW. It gives it that underwater effect for want of a better description.
If you are familiar with the free Cymatics Origins plugin, the big dial in the middle of the UI does the same thing as resampling in DAW but in real time. 
Iâ€˜m just talking about the sampleandhold node here  the degrade FX is deprecated and should have been removed ages ago.
The robotic artifacts come from the interpolation algorithm that the sample and hold node is using, which is none  it just picks up a sample and then outputs it as static value until the next interval (exactly what sample and hold means). For a better sounding effect you would have to introduce some interpolation methods (eg. ramping between the samples values or doing cubic splines), but thatâ€˜s currently not built into the node.
Making a sampleandramp node could be a fun excercise for SNEX thoughâ€¦

Yes, I can verify what the last post said, it's a fun exercise:
template <int NV> struct sampleandramp { SNEX_NODE(sampleandramp); // A compile time array of ramper objects // (sfloat is a builtin SNEX type for // ramping between values for parameter // smoothing. span<sfloat, 2> rampers; double sampleRate = 44100.0; double fadeTimeMs = 1.0 / 44100.0 * 1000.0; // Initialise the processing specs here void prepare(PrepareSpecs ps) { sampleRate = ps.sampleRate; // set the fade time again // (for some reason, iterating over the arra // throws a SNEX compilation error, so we have // to spell it out for each element)... rampers[0].prepare(sampleRate, fadeTimeMs); rampers[1].prepare(sampleRate, fadeTimeMs); } // Reset the processing pipeline here void reset() { // This will reset the ramp position, so when you bypass / unbypass the node it will // not pick up from the old value and cause clicks. rampers[0].reset(); rampers[1].reset(); } float calculateSample(float input, sfloat& ramper) { // if the ramper isn't smoothing, it's waiting for // the next input if(!ramper.isActive()) ramper.set(input); // this will calculate the next ramp value and // update the internal state return ramper.advance(); } // Process the signal as frame here template <int C> void processFrame(span<float, C>& data) { data[0] = calculateSample(data[0], rampers[0]); if(C != 1) data[1] = calculateSample(data[1], rampers[1]); } // Process the signal here template <typename ProcessDataType> void process(ProcessDataType& data) { // We'll only use frame processing here... auto pd = data.toFrameData(); while(pd.next()) processFrame(pd.toSpan()); } void handleHiseEvent(HiseEvent& e){} void setExternalData(const ExternalData& d, int index){} template <int P> void setParameter(double v) { if(P == 0) // Factor (1  64 with step size 1) { const auto factor = Math.range(v, 1.0, 64.0); // internally it will calculate the steps in // samples so this is two unnecessary divisions // but we get to use the built in type fadeTimeMs = (factor / sampleRate) * 1000.0; rampers[0].prepare(sampleRate, fadeTimeMs); rampers[1].prepare(sampleRate, fadeTimeMs); } } };
The artifacts are still there (linear interpolation is not a good interpolation algorithm), but it's already way better than sampleandhold.

@ChristophHart said in Resampling/sample degrade:
Yes, I can verify what the last post said, it's a fun exercise:
template <int NV> struct sampleandramp { SNEX_NODE(sampleandramp); // A compile time array of ramper objects // (sfloat is a builtin SNEX type for // ramping between values for parameter // smoothing. span<sfloat, 2> rampers; double sampleRate = 44100.0; double fadeTimeMs = 1.0 / 44100.0 * 1000.0; // Initialise the processing specs here void prepare(PrepareSpecs ps) { sampleRate = ps.sampleRate; // set the fade time again // (for some reason, iterating over the arra // throws a SNEX compilation error, so we have // to spell it out for each element)... rampers[0].prepare(sampleRate, fadeTimeMs); rampers[1].prepare(sampleRate, fadeTimeMs); } // Reset the processing pipeline here void reset() { // This will reset the ramp position, so when you bypass / unbypass the node it will // not pick up from the old value and cause clicks. rampers[0].reset(); rampers[1].reset(); } float calculateSample(float input, sfloat& ramper) { // if the ramper isn't smoothing, it's waiting for // the next input if(!ramper.isActive()) ramper.set(input); // this will calculate the next ramp value and // update the internal state return ramper.advance(); } // Process the signal as frame here template <int C> void processFrame(span<float, C>& data) { data[0] = calculateSample(data[0], rampers[0]); if(C != 1) data[1] = calculateSample(data[1], rampers[1]); } // Process the signal here template <typename ProcessDataType> void process(ProcessDataType& data) { // We'll only use frame processing here... auto pd = data.toFrameData(); while(pd.next()) processFrame(pd.toSpan()); } void handleHiseEvent(HiseEvent& e){} void setExternalData(const ExternalData& d, int index){} template <int P> void setParameter(double v) { if(P == 0) // Factor (1  64 with step size 1) { const auto factor = Math.range(v, 1.0, 64.0); // internally it will calculate the steps in // samples so this is two unnecessary divisions // but we get to use the built in type fadeTimeMs = (factor / sampleRate) * 1000.0; rampers[0].prepare(sampleRate, fadeTimeMs); rampers[1].prepare(sampleRate, fadeTimeMs); } } };
The artifacts are still there (linear interpolation is not a good interpolation algorithm), but it's already way better than sampleandhold.
Thankyou @ChristophHart I will have a go with that code and try to implement it into my project and see how it works out. The assist is very much appreciated

@ChristophHart Is it possible to get at those inbetweens too and not just integers splitting the sample rate in half? For example if I wanted to go from 44100 to 35280?