Using SignalsmithStretch inside Juce

Hi all, hope you are doing well :slight_smile:
I’m starting using JUCE and I achieve to do some functionalities but now I would like to pitch a sound in real time so i found “SignalsmithStretch” library.

I achieve to include the library with ProJucer and the project compile the program but when I reproduce the sound I ear nothing (without signal smith it works).

Snippet of the code is here:

void Track::prepareToPlay(int samplesPerBlockExpected, double sampleRate)
{
//stretcher.reset();
stretcher.presetDefault(2, sampleRate);
stretcher.setTransposeSemitones(0);
sampler.prepareToPlay(samplesPerBlockExpected, sampleRate);
}

void Track::processBlock(juce::AudioBuffer& buffer, juce::MidiBuffer& midiMessages)
{
sampler.processBlock(buffer, midiMessages);

int numSamples = buffer.getNumSamples();
int numChannels = buffer.getNumChannels();

std::vector<const float*> inputBuffers(numChannels);
std::vector<float*> outputBuffers(numChannels);

for (int ch = 0; ch < numChannels; ++ch)
{
    inputBuffers[ch] = buffer.getReadPointer(ch);
    outputBuffers[ch] = buffer.getWritePointer(ch);
}

stretcher.process(inputBuffers.data(), numSamples, outputBuffers.data(), numSamples);

}

Any one can help me understanding the problem?

Thanks!

1 Like

Usually prepareToPlay’s args are opposite to that

Rail

Sorry, I forgot to say that is a standalone app

I’ve made some progress:

This in Track.h

#include “signalsmith-stretch.h”

private:
signalsmith::stretch::SignalsmithStretch<float, std::default_random_engine> stretch;

and in Track.cpp

void Track::prepareToPlay(int samplesPerBlockExpected, double sampleRate)
{
stretch.reset();
stretch = signalsmith::stretch::SignalsmithStretch<float, std::default_random_engine>(sampleRate);
//stretch.presetDefault(2, sampleRate);
//stretch.presetCheaper(2, sampleRate);
sampler.prepareToPlay(samplesPerBlockExpected, sampleRate);
}

void Track::processBlock(juce::AudioBuffer& buffer, juce::MidiBuffer& midiMessages)
{
sampler.processBlock(buffer, midiMessages);

int numSamples = buffer.getNumSamples();
int numChannels = buffer.getNumChannels();

// Prepara i vettori dei puntatori per il processing
std::vector<const float*> inputBuffers(numChannels);
std::vector<float*> outputBuffers(numChannels);
for (int ch = 0; ch < numChannels; ++ch)
{
    inputBuffers[ch] = buffer.getReadPointer(ch);
    outputBuffers[ch] = buffer.getWritePointer(ch);
}

stretch.setTransposeSemitones(12);

stretch.process(inputBuffers.data(), numSamples, outputBuffers.data(), numSamples);

If I comment this:

//stretch.presetDefault(2, sampleRate);
//stretch.presetCheaper(2, sampleRate);

at least I can lessen to the sound but it seams not affected by the pitch :frowning:

Some one have any idea?

Thanks

I worked on a juce wrapper around this a while ago. I can’t share the full code, but here’s some observations:

  • I would call reset() on the stretcher after you call presetDefault() or presetCheaper() rather than before.
  • The juce AudioBuffer class has a function called getArrayOfWritePointers() which I used to send the data to the stretcher. You don’t need to create the vectors as an in-between step.
  • You don’t need this line:
    stretch = signalsmith::stretch::SignalsmithStretch<float, std::default_random_engine>(sampleRate)
    in prepareToPlay(). I’m not sure if it’s doing any harm, but it’s not necessary.

But I think the main issue is that you need separate buffers for the input and output samples. getReadPointer() and getWritePointer() both point to the same buffer in your code. So you need to create a separate juce AudioBuffer for the output, being sure to set its size in prepareToPlay(). Then the process step should look more like this:
stretch.process(buffer.getArrayOfWritePointers(), numSamples, outputBuffer.getArrayOfWritePointers(), numSamples);
Where outputBuffer is the juce AudioBuffer that you defined beforehand. You can then copy the data from the output buffer to the input buffer afterwards if that’s what you want to do.
Don’t use read pointers for any of this, they just add const to the data to make it “read only”, which might get in the way of what the stretcher is trying to do.

I hope this helps.