Real-time TimeStretching

Hey there

I’m finding the process of getting real-time time stretching in TE kind of puzzling.

In the Pitch And Time Demo the pitch shifting is near instantanous, but any time stretching requires re-calculation.

Oddly enough inserting
clip->state.setProperty("elastiqueMode", 0, nullptr);
in to the demo code lets me prevent the recalculation and will play back the audio with the new tempo almost immediately, albeit with a changed pitch. If the pitch shift plugin (with SoundTouch) is also on I can correct it back. That’s really responsive, but I’m doubtful that this is the right way to approach this.

What’s the right way to get an changed tempo respected quickly with pitch maintained in TE? Is trying to use clip->setSpeedRatio (ratio); and trying to use TimeStretcher::Mode SoundTouch, RubberBand etc. barking up the wrong tree? That’s what I’ve been trying but I’ve yet to get it to work.

btw, I’m not yet on TE2.0. I’m using
TE 6ad0fcadc5 from 18 May 2022
and
JUCE6 b0a8bc0ce from 26 Apr 2022 at 08:38

Many thanks for any suggestions or thoughts on this,

Jeff

I think your

is not actually a valid time-stretch mode so it’s just falling back to “no time-stretching” and just resampling instead (hence the pitch shift).

You need to set a time-stretch mode using the TimeStretcher::Mode enum and then set setUsesProxy (false);.

However, you must be using the latest develop branch to get this option otherwise it won’t work as expected.

1 Like

Thanks so much for this info @dave96

And yes that’s correct, I am currently resampling and correcting the pitch back with the plug-in. I’ll try your suggestion soon, but am aware that it might not work for me yet as I’m still on TE1/Juce6

Kind regards :slight_smile:

I’ve recently started having a play with Juce and Tracktion, particularly with regard to pitch and tempo shifting. I am a bad guitarist and so this is a really useful capability to help me learn songs. Way back when, there was a free plugin for Winamp. When Winamp died I bought Riffstation - but that got bought by Fender who subsequently dropped it and prevented it from working in its last licensed state. As it happens, I have now bought zPlane but the combination of Juce, Tracktion and Rubberband or SoundTouch make it possible to create a tailored open source option.

I have essentially recreated the PitchAndTimeDemo in a clean Projucer project (VS2022) but outside the DemoRunner framework in order to learn how it all fits together. I have removed the Key and Tempo controls and added a Speed slider alongside the Real-time Pitch slider with the intention of being able to alter the speed and the pitch in real-time. The algorithm (TimeStretcher::Mode) is set from the menu and I have invoked setUsesProxy (false); on the clip. This all seems to work really well but there is one (small) fly in the ointment: For the thumbnail to work properly (i.e. at the right speed), I have to reload the adjusted clip file into the thumbnail with setFile() and this forces the audio to restart. However, if I don’t do this last step then the thumbnail merrily carries on playing at normal speed regardless of how fast or slow the audio is actually playing. Is it possible to keep the thumbnail and the audio in sync when making on the fly changes to the speed?

Many thanks,

Oliver

If you’re doing it in real-time there won’t be a proxy file created so you don’t need to draw a different file.
If you’re only playing the file through once then as you change the tempo of the file, you’ll need to change the clip length so it only corresponds to a single file length. Then your playhead will move between the start and this new length so should correctly represent the playing position.

Does that make sense?
It’s a bit difficult to say more without knowing exactly how you’re setting things up and drawing them. The number of combinations explodes pretty quickly.

Many thanks, Dave - will have a play with the code based on your suggestion. The setup is very close to the “PitchAndTime” example code. In the setFile method, I have added the line:

    clip->setUsesProxy(false);

For setting the speed in real time after dragging the slider I have:

if (auto clip = getClip()) {
    auto f = getSourceFile();
    const auto audioFileInfo = tracktion::AudioFile(engine, f).getInfo();
    const double ratio = speedSlider.getValue() / 100.0;

    clip->setSpeedRatio(ratio);
    clip->setLength(tracktion::TimeDuration::fromSeconds(audioFileInfo.getLengthInSeconds()) / clip->getSpeedRatio(), true);
    clip->setPitchChange(float(pitchShiftSlider.getValue()));
    thumbnail.setFile(loopAroundClip(*clip)->getPlaybackFile());
}

The loopAroundClip structure currently has loop set to true but I will change that

As a Tracktion (and Juce) newbie I’ve still got a fair bit of learning to do - both Juce and Tracktion are fantastic pieces of software but it looks like Tracktion doesn’t yet have the level of documentation as Juce, so working through examples feels like a good way to go.

Thanks again,

Oliver

Yeah, it’s a bit chicken and egg as the more paying licensees we have, the more resources we’ll have to spend time on documentation, tutorials etc.

I don’t think you need to do the setFile call every time you change the slider though, that’s just updating some properties of the clip, not what file is being played.

Hi Dave,

Many thanks - completely understand re the licensing (although I’m very much only a hobbyist I’m afraid!).

Yes, removing the call to setFile on the Thumbnail works for the audio but I was then finding that the Thumbnail no longer tracked properly. I think the mists are parting, though - the Thumbnail struct (juce::Component) is copied from the one in examples/common/Utilities.h. This clearly wraps the SmartThumbnail and adds cursors etc., using the 25Hz LambdaTimer. I believe I just need to provide methods to allow the cursor updating to be changed to account for the new speed (I now see that they are just thin Rectangles drawn on top of the SmartThumbnail so entirely under my control).

Thanks again,

Oliver