PitchShiftPlugin

Hi all,

I do some steps using tracktion engine but I’m stuck on this:

Using the PitchShiftPlugin after a SamplerPlugin, is this possible?

This is the code I’m trying to use:

void App::loadSounds()
{
    // Creating eight tracks
    edit.ensureNumberOfAudioTracks(8);
    auto& transport = edit.getTransport();

    // Setting BPM to the track
    //edit.tempoSequence.getTempos()[0]->setBpm(90);
    edit.tempoSequence.getTempos()[0]->setBpm(90);

    edit.setClickTrackVolume(1.0f);
    edit.clickTrackEnabled = false;
    edit.clickTrackEmphasiseBars = false;

    // Crea un intervallo di tempo che copre esattamente 1 barra
    const tracktion::TimeRange editTimeRange(0s, edit.tempoSequence.toTime({ 4, tracktion::BeatDuration() }));

    juce::AudioFormatManager formatManager;
    formatManager.registerBasicFormats();

    // Just add 8 random sound to a fixed array.
    juce::String basePath("C:\\FAKEPATH\\Projects\\1\\Sounds\\");

    for (int i = 0; i < numFiles; ++i)
    {
        juce::String fileName = "sample" + juce::String(i + 1) + ".wav";
        audioFiles[i].file = juce::File(basePath + fileName);
        std::unique_ptr<juce::AudioFormatReader> reader(formatManager.createReaderFor(audioFiles[i].file));

        if (reader)
        {
            audioFiles[i].numSamples = reader->lengthInSamples;
            int numSamples = reader->lengthInSamples;
            double sampleRate = reader->sampleRate;
            double durationInSeconds = numSamples / sampleRate;
            DBG("sample" + juce::String(i + 1) + ".wav");
            DBG("Lunghezza in sample: " + juce::String(numSamples));
            DBG("Frequenza di campionamento: " + juce::String(sampleRate));
            DBG("Durata in secondi: " + juce::String(durationInSeconds));
        }
        else
        {
            audioFiles[i].numSamples = 0;
            DBG("Errore: impossibile leggere il file audio.");
        }
    }

    auto audioTracks = tracktion::getAudioTracks(edit);

    for (size_t i = 0; i < 8; ++i)
    {
        if (audioTracks.size() <= i)
        {
            DBG("Errore: meno tracce del previsto!");
            continue;
        }

        auto track = audioTracks[i];
        if (!track)
        {
            DBG("Errore: traccia " + juce::String(i + 1) + " non valida!");
            continue;
        }

        track->setName("Track " + juce::String(i + 1));
        track->setColour(juce::Colour::fromRGB(230, 244, 241));

        auto clip = track->insertNewClip(tracktion::TrackItem::Type::midi, "Clip 1", editTimeRange, nullptr);
        if (!clip)
        {
            DBG("Errore: impossibile inserire il MIDI Clip sulla traccia " + juce::String(i + 1));
            continue;
        }

        DBG("MIDI Clip creata sulla traccia: " + juce::String(i + 1));
        DBG("Midi clip lunghezza: " + juce::String(clip->getLengthInBeats().inBeats()));

        auto midiClip = dynamic_cast<tracktion::MidiClip*>(clip);
        if (!midiClip)
        {
            DBG("Errore: MidiClip non valido sulla traccia " + juce::String(i + 1));
            continue;
        }

        juce::Random random;
        midiClip->setColour(juce::Colour::fromRGB(random.nextInt(256), random.nextInt(256), random.nextInt(256)));

        juce::String samplerName = "SamplerPlugin_" + juce::String(i);**
        auto sampler = dynamic_cast<tracktion::SamplerPlugin*>(
            edit.getPluginCache().createNewPlugin(tracktion::SamplerPlugin::xmlTypeName, {}).get()
            );

        if (!sampler)
        {
            DBG("Errore: impossibile creare il SamplerPlugin sulla traccia " + juce::String(i + 1));
            continue;
        }

        auto pitchShift = dynamic_cast<tracktion::PitchShiftPlugin*>(
            edit.getPluginCache().createNewPlugin(tracktion::PitchShiftPlugin::xmlTypeName, {}).get()
            );

        pitchShift->semitonesValue = 10.0f;
        pitchShift->setEnabled(true);
        
        track->pluginList.insertPlugin(*sampler, 0, nullptr); 
        track->pluginList.insertPlugin(*pitchShift, -1, nullptr);
  
        int midiNote = 36;

        if (i == 1) {
            DBG("Carico il file: " + audioFiles[i].file.getFullPathName());
            const auto error = sampler->addSound(audioFiles[1].file.getFullPathName(), "Slice 1", 0, audioFiles[1].numSamples, 1.0f);
            if (!error.isEmpty())
            {
                DBG("Errore nel caricamento del suono sulla traccia " + juce::String(i) + ": " + error);
                continue;
            }

            sampler->setSoundParams(sampler->getNumSounds() - 1, midiNote, midiNote, midiNote);
            //sampler->setSoundOpenEnded(sampler->getNumSounds() - 1, true);
            DBG("Settaggio parametri sampler per slice: 1");
            midiNote = 36 + 1;

            DBG("Carico il file: " + audioFiles[i].file.getFullPathName());
            const auto error2 = sampler->addSound(audioFiles[2].file.getFullPathName(), "Slice 2", 0, audioFiles[2].numSamples, 1.0f);
            if (!error2.isEmpty())
            {
                DBG("Errore nel caricamento del suono sulla traccia " + juce::String(i) + ": " + error);
                continue;
            }

            sampler->setSoundParams(sampler->getNumSounds() - 1, midiNote, midiNote, midiNote);
            //sampler->setSoundOpenEnded(sampler->getNumSounds() - 1, true);
            DBG("Settaggio parametri sampler per slice: 2");
        }
        else {
            for (size_t y = 0; y < 16; y++)
            {
                DBG("Carico il file: " + audioFiles[i].file.getFullPathName());
                const auto error = sampler->addSound(audioFiles[i].file.getFullPathName(), "Slice " + juce::String(y + 1), 0, audioFiles[i].numSamples, 1.0f);
                if (!error.isEmpty())
                {
                    DBG("Errore nel caricamento del suono sulla traccia " + juce::String(i) + ": " + error);
                    continue;
                }

                sampler->setSoundParams(sampler->getNumSounds() - 1, midiNote, midiNote, midiNote);
                //sampler->setSoundOpenEnded(sampler->getNumSounds() - 1, true);
                DBG("Settaggio parametri sampler per slice: " + juce::String(y));
                midiNote = 36 + y;
            }
        }

        if (i == 0) {
            //midiClip->getSequence().addNote(36, tracktion::BeatPosition::fromBeats(0), tracktion::BeatDuration::fromBeats(4), 127, 0, nullptr);
        }
        else if (i == 1) {
            midiClip->getSequence().addNote(36, tracktion::BeatPosition::fromBeats(1), tracktion::BeatDuration::fromBeats(0.5), 127, 0, nullptr);
            midiClip->getSequence().addNote(36, tracktion::BeatPosition::fromBeats(3), tracktion::BeatDuration::fromBeats(0.5), 127, 0, nullptr);

            midiClip->getSequence().addNote(37, tracktion::BeatPosition::fromBeats(0), tracktion::BeatDuration::fromBeats(0.5), 127, 0, nullptr);
            midiClip->getSequence().addNote(37, tracktion::BeatPosition::fromBeats(0.5), tracktion::BeatDuration::fromBeats(0.5), 127, 0, nullptr);
            midiClip->getSequence().addNote(37, tracktion::BeatPosition::fromBeats(1), tracktion::BeatDuration::fromBeats(0.5), 127, 0, nullptr);
            midiClip->getSequence().addNote(37, tracktion::BeatPosition::fromBeats(1.5), tracktion::BeatDuration::fromBeats(0.5), 127, 0, nullptr);
            midiClip->getSequence().addNote(37, tracktion::BeatPosition::fromBeats(2), tracktion::BeatDuration::fromBeats(0.5), 127, 0, nullptr);
            midiClip->getSequence().addNote(37, tracktion::BeatPosition::fromBeats(2.5), tracktion::BeatDuration::fromBeats(0.5), 127.0, 0, nullptr);
            midiClip->getSequence().addNote(37, tracktion::BeatPosition::fromBeats(3), tracktion::BeatDuration::fromBeats(0.5), 127, 0, nullptr);
            midiClip->getSequence().addNote(37, tracktion::BeatPosition::fromBeats(3.5), tracktion::BeatDuration::fromBeats(0.5), 127, 0, nullptr);
        }
        else if (i == 2) {
            /*midiClip->getSequence().addNote(36, tracktion::BeatPosition::fromBeats(0), tracktion::BeatDuration::fromBeats(0.5), 127, 0, nullptr);
            midiClip->getSequence().addNote(36, tracktion::BeatPosition::fromBeats(0.5), tracktion::BeatDuration::fromBeats(0.5), 127, 0, nullptr);
            midiClip->getSequence().addNote(36, tracktion::BeatPosition::fromBeats(1), tracktion::BeatDuration::fromBeats(0.5), 127, 0, nullptr);
            midiClip->getSequence().addNote(36, tracktion::BeatPosition::fromBeats(1.5), tracktion::BeatDuration::fromBeats(0.5), 127, 0, nullptr);
            midiClip->getSequence().addNote(36, tracktion::BeatPosition::fromBeats(2), tracktion::BeatDuration::fromBeats(0.5), 127, 0, nullptr);
            midiClip->getSequence().addNote(36, tracktion::BeatPosition::fromBeats(2.5), tracktion::BeatDuration::fromBeats(0.5), 127, 0, nullptr);
            midiClip->getSequence().addNote(36, tracktion::BeatPosition::fromBeats(3), tracktion::BeatDuration::fromBeats(0.5), 127, 0, nullptr);
            midiClip->getSequence().addNote(36, tracktion::BeatPosition::fromBeats(3.5), tracktion::BeatDuration::fromBeats(0.5), 127, 0, nullptr);*/
        }
        else if (i == 3) {
            midiClip->getSequence().addNote(36, tracktion::BeatPosition::fromBeats(0), tracktion::BeatDuration::fromBeats(0.5), 80, 0, nullptr);
            midiClip->getSequence().addNote(36, tracktion::BeatPosition::fromBeats(1.75), tracktion::BeatDuration::fromBeats(0.25), 60, 0, nullptr);
            midiClip->getSequence().addNote(36, tracktion::BeatPosition::fromBeats(2), tracktion::BeatDuration::fromBeats(0.5), 80, 0, nullptr);
            midiClip->getSequence().addNote(36, tracktion::BeatPosition::fromBeats(2.50), tracktion::BeatDuration::fromBeats(0.5), 80, 0, nullptr);
        }
        else {
            //6midiClip->getSequence().addNote(42, tracktion::BeatPosition::fromBeats(0), tracktion::BeatDuration::fromBeats(0.5), 127, 0, nullptr);
        }
    }

    transport.setLoopRange(editTimeRange);
    //transport.setPosition(0s);
    transport.looping = false;
    //transport.play(false);
}

Basically the program compile correctly but crash on:

        if (! timestretcher->isInitialised())
        {
            timestretcher->initialise (sr, samplesPerBlock, 2, mode, elastiqueOptions, true);
            jassert (timestretcher->isInitialised());
        }

The part of the function that should do what I expect is this:

 juce::String samplerName = "SamplerPlugin_" + juce::String(i);**
        auto sampler = dynamic_cast<tracktion::SamplerPlugin*>(
            edit.getPluginCache().createNewPlugin(tracktion::SamplerPlugin::xmlTypeName, {}).get()
            );

        if (!sampler)
        {
            DBG("Errore: impossibile creare il SamplerPlugin sulla traccia " + juce::String(i + 1));
            continue;
        }

        auto pitchShift = dynamic_cast<tracktion::PitchShiftPlugin*>(
            edit.getPluginCache().createNewPlugin(tracktion::PitchShiftPlugin::xmlTypeName, {}).get()
            );

        pitchShift->semitonesValue = 10.0f;
        pitchShift->setEnabled(true);
        
        track->pluginList.insertPlugin(*sampler, 0, nullptr); 
        track->pluginList.insertPlugin(*pitchShift, -1, nullptr);

Any one has some suggestions?

Thanks!

Have you enabled one of the time-stretch engines?
I’m guessing you’ll probably want TRACKTION_ENABLE_TIMESTRETCH_SOUNDTOUCH=1 as that is the built-in one.

I was trying: “TRACKTION_ENABLE_SOUNDTOUCH” instead of “TRACKTION_ENABLE_TIMESTRETCH_SOUNDTOUCH”

Now it works or at least it doesn’t crash. I need to check more in deep if is applied.

        track->pluginList.insertPlugin(*sampler, 0, nullptr); 
        track->pluginList.insertPlugin(*pitchShift,1, nullptr);

Is this correct? Sampler and then pitchShift?

Thank you!

Yes, that looks correct.

Is correct position 1 for PitchShift or I need to set -1 because the track is midi type?

There’s no audio/midi track types in TE. Any AudioTrack can be output to audio or MIDI devices.

-1 means “at the end” so will probably be inserted after a vol/pan and level meter plugin.

Nothing to do.

With TRACKTION_ENABLE_TIMESTRETCH_SOUNDTOUCH I don’t have any more crashes but if the SamplerPlugin works correctly the PitchShift no.

Have you some suggestion?

Here the code used in loadSound function (just dummy for the moment)

void App::loadSounds()
{
    // Creating eight tracks
    edit.ensureNumberOfAudioTracks(8);
    auto& transport = edit.getTransport();

    // Setting BPM to the track
    //edit.tempoSequence.getTempos()[0]->setBpm(90);
    edit.tempoSequence.getTempos()[0]->setBpm(90);

    edit.setClickTrackVolume(1.0f);
    edit.clickTrackEnabled = false;
    edit.clickTrackEmphasiseBars = false;

    // Crea un intervallo di tempo che copre esattamente 1 barra
    const tracktion::TimeRange editTimeRange(0s, edit.tempoSequence.toTime({ 4, tracktion::BeatDuration() }));

    juce::AudioFormatManager formatManager;
    formatManager.registerBasicFormats();

    // Just add 8 random sound to a fixed array.
    juce::String basePath("C:\\FAKE_PATH\\Projects\\1\\Sounds\\");

    for (int i = 0; i < numFiles; ++i)
    {
        juce::String fileName = "sample" + juce::String(i + 1) + ".wav";
        audioFiles[i].file = juce::File(basePath + fileName);
        std::unique_ptr<juce::AudioFormatReader> reader(formatManager.createReaderFor(audioFiles[i].file));

        if (reader)
        {
            audioFiles[i].numSamples = reader->lengthInSamples;
            int numSamples = reader->lengthInSamples;
            double sampleRate = reader->sampleRate;
            double durationInSeconds = numSamples / sampleRate;
            DBG("sample" + juce::String(i + 1) + ".wav");
            DBG("Lunghezza in sample: " + juce::String(numSamples));
            DBG("Frequenza di campionamento: " + juce::String(sampleRate));
            DBG("Durata in secondi: " + juce::String(durationInSeconds));
        }
        else
        {
            audioFiles[i].numSamples = 0;
            DBG("Errore: impossibile leggere il file audio.");
        }
    }

    auto audioTracks = tracktion::getAudioTracks(edit);

    for (size_t i = 0; i < 8; ++i)
    {
        if (audioTracks.size() <= i)
        {
            DBG("Errore: meno tracce del previsto!");
            continue;
        }

        auto track = audioTracks[i];
        if (!track)
        {
            DBG("Errore: traccia " + juce::String(i + 1) + " non valida!");
            continue;
        }

        track->setName("Track " + juce::String(i + 1));
        track->setColour(juce::Colour::fromRGB(230, 244, 241));

        auto clip = track->insertNewClip(tracktion::TrackItem::Type::midi, "Clip 1", editTimeRange, nullptr);
        if (!clip)
        {
            DBG("Errore: impossibile inserire il MIDI Clip sulla traccia " + juce::String(i + 1));
            continue;
        }

        DBG("MIDI Clip creata sulla traccia: " + juce::String(i + 1));
        DBG("Midi clip lunghezza: " + juce::String(clip->getLengthInBeats().inBeats()));

        auto midiClip = dynamic_cast<tracktion::MidiClip*>(clip);
        if (!midiClip)
        {
            DBG("Errore: MidiClip non valido sulla traccia " + juce::String(i + 1));
            continue;
        }

        juce::Random random;
        midiClip->setColour(juce::Colour::fromRGB(random.nextInt(256), random.nextInt(256), random.nextInt(256)));

        auto sampler = dynamic_cast<tracktion::SamplerPlugin*>(
            edit.getPluginCache().createNewPlugin(tracktion::SamplerPlugin::xmlTypeName, {}).get()
            );

        if (!sampler)
        {
            DBG("Errore: impossibile creare il SamplerPlugin sulla traccia " + juce::String(i + 1));
            continue;
        }

        auto pitchShift = dynamic_cast<tracktion::PitchShiftPlugin*>(
            edit.getPluginCache().createNewPlugin(tracktion::PitchShiftPlugin::xmlTypeName, {}).get()
            );

        if (!pitchShift)
        {
            DBG("Errore: impossibile creare il PitchShiftPlugin sulla traccia " + juce::String(i + 1));
            continue;
        }

        pitchShift->semitonesValue = -12.0f;
        pitchShift->setEnabled(true);

        track->pluginList.insertPlugin(*sampler, 0, nullptr); 
        track->pluginList.insertPlugin(*pitchShift, 1, nullptr);

        DBG("Pitch plugin enabled: " + juce::String(pitchShift->isEnabled() ? "true" : "false"));
        DBG("Semitones for track " + juce::String(i + 1) + ": " + juce::String(pitchShift->semitonesValue));
  
        int midiNote = 36;

        if (i == 1) {
            DBG("Carico il file: " + audioFiles[i].file.getFullPathName());
            const auto error = sampler->addSound(audioFiles[1].file.getFullPathName(), "Slice 1", 0, audioFiles[1].numSamples, 1.0f);
            if (!error.isEmpty())
            {
                DBG("Errore nel caricamento del suono sulla traccia " + juce::String(i) + ": " + error);
                continue;
            }

            sampler->setSoundParams(sampler->getNumSounds() - 1, midiNote, midiNote, midiNote);
            //sampler->setSoundOpenEnded(sampler->getNumSounds() - 1, true);
            DBG("Settaggio parametri sampler per slice: 1");
            midiNote = 36 + 1;

            DBG("Carico il file: " + audioFiles[i].file.getFullPathName());
            const auto error2 = sampler->addSound(audioFiles[2].file.getFullPathName(), "Slice 2", 0, audioFiles[2].numSamples, 1.0f);
            if (!error2.isEmpty())
            {
                DBG("Errore nel caricamento del suono sulla traccia " + juce::String(i) + ": " + error);
                continue;
            }

            sampler->setSoundParams(sampler->getNumSounds() - 1, midiNote, midiNote, midiNote);
            //sampler->setSoundOpenEnded(sampler->getNumSounds() - 1, true);
            DBG("Settaggio parametri sampler per slice: 2");
        }
        else {
            for (size_t y = 0; y < 16; y++)
            {
                DBG("Carico il file: " + audioFiles[i].file.getFullPathName());
                const auto error = sampler->addSound(audioFiles[i].file.getFullPathName(), "Slice " + juce::String(y + 1), 0, audioFiles[i].numSamples, 1.0f);
                if (!error.isEmpty())
                {
                    DBG("Errore nel caricamento del suono sulla traccia " + juce::String(i) + ": " + error);
                    continue;
                }

                sampler->setSoundParams(sampler->getNumSounds() - 1, midiNote, midiNote, midiNote);
                //sampler->setSoundOpenEnded(sampler->getNumSounds() - 1, true);
                DBG("Settaggio parametri sampler per slice: " + juce::String(y));
                midiNote = 36 + y;
            }
        }

        if (i == 0) {
            midiClip->getSequence().addNote(36, tracktion::BeatPosition::fromBeats(0), tracktion::BeatDuration::fromBeats(4), 80, 0, nullptr);
        }
        else if (i == 1) {
            midiClip->getSequence().addNote(36, tracktion::BeatPosition::fromBeats(1), tracktion::BeatDuration::fromBeats(0.5), 127, 0, nullptr);
            midiClip->getSequence().addNote(36, tracktion::BeatPosition::fromBeats(3), tracktion::BeatDuration::fromBeats(0.5), 127, 0, nullptr);

            midiClip->getSequence().addNote(37, tracktion::BeatPosition::fromBeats(0), tracktion::BeatDuration::fromBeats(0.5), 127, 0, nullptr);
            midiClip->getSequence().addNote(37, tracktion::BeatPosition::fromBeats(0.5), tracktion::BeatDuration::fromBeats(0.5), 127, 0, nullptr);
            midiClip->getSequence().addNote(37, tracktion::BeatPosition::fromBeats(1), tracktion::BeatDuration::fromBeats(0.5), 127, 0, nullptr);
            midiClip->getSequence().addNote(37, tracktion::BeatPosition::fromBeats(1.5), tracktion::BeatDuration::fromBeats(0.5), 127, 0, nullptr);
            midiClip->getSequence().addNote(37, tracktion::BeatPosition::fromBeats(2), tracktion::BeatDuration::fromBeats(0.5), 127, 0, nullptr);
            midiClip->getSequence().addNote(37, tracktion::BeatPosition::fromBeats(2.5), tracktion::BeatDuration::fromBeats(0.5), 127.0, 0, nullptr);
            midiClip->getSequence().addNote(37, tracktion::BeatPosition::fromBeats(3), tracktion::BeatDuration::fromBeats(0.5), 127, 0, nullptr);
            midiClip->getSequence().addNote(37, tracktion::BeatPosition::fromBeats(3.5), tracktion::BeatDuration::fromBeats(0.5), 127, 0, nullptr);
        }
        else if (i == 2) {
            /*midiClip->getSequence().addNote(36, tracktion::BeatPosition::fromBeats(0), tracktion::BeatDuration::fromBeats(0.5), 127, 0, nullptr);
            midiClip->getSequence().addNote(36, tracktion::BeatPosition::fromBeats(0.5), tracktion::BeatDuration::fromBeats(0.5), 127, 0, nullptr);
            midiClip->getSequence().addNote(36, tracktion::BeatPosition::fromBeats(1), tracktion::BeatDuration::fromBeats(0.5), 127, 0, nullptr);
            midiClip->getSequence().addNote(36, tracktion::BeatPosition::fromBeats(1.5), tracktion::BeatDuration::fromBeats(0.5), 127, 0, nullptr);
            midiClip->getSequence().addNote(36, tracktion::BeatPosition::fromBeats(2), tracktion::BeatDuration::fromBeats(0.5), 127, 0, nullptr);
            midiClip->getSequence().addNote(36, tracktion::BeatPosition::fromBeats(2.5), tracktion::BeatDuration::fromBeats(0.5), 127, 0, nullptr);
            midiClip->getSequence().addNote(36, tracktion::BeatPosition::fromBeats(3), tracktion::BeatDuration::fromBeats(0.5), 127, 0, nullptr);
            midiClip->getSequence().addNote(36, tracktion::BeatPosition::fromBeats(3.5), tracktion::BeatDuration::fromBeats(0.5), 127, 0, nullptr);*/
        }
        else if (i == 3) {
            midiClip->getSequence().addNote(36, tracktion::BeatPosition::fromBeats(0), tracktion::BeatDuration::fromBeats(0.5), 80, 0, nullptr);
            midiClip->getSequence().addNote(36, tracktion::BeatPosition::fromBeats(1.75), tracktion::BeatDuration::fromBeats(0.25), 60, 0, nullptr);
            midiClip->getSequence().addNote(36, tracktion::BeatPosition::fromBeats(2), tracktion::BeatDuration::fromBeats(0.5), 80, 0, nullptr);
            midiClip->getSequence().addNote(36, tracktion::BeatPosition::fromBeats(2.50), tracktion::BeatDuration::fromBeats(0.5), 80, 0, nullptr);
        }
        else {
            //6midiClip->getSequence().addNote(42, tracktion::BeatPosition::fromBeats(0), tracktion::BeatDuration::fromBeats(0.5), 127, 0, nullptr);
        }
    }

    transport.setLoopRange(editTimeRange);
    //transport.setPosition(0s);
    transport.looping = false;
    //transport.play(false);
}

Thanks!

Additionally:

        pitchShift->semitonesValue = -12.0f;
        pitchShift->setEnabled(true);

        track->pluginList.insertPlugin(*sampler, 0, nullptr);
        track->pluginList.insertPlugin(*pitchShift, -1, nullptr);

        DBG("Pitch plugin enabled: " + juce::String(pitchShift->isEnabled() ? "true" : "false"));
        DBG("PitchShiftPlugin mode int: " + juce::String(static_cast<int>(pitchShift->mode)));
        DBG("Semitones for track " + juce::String(i + 1) + ": " + juce::String(pitchShift->semitonesValue));

give me:

Pitch plugin enabled: true
PitchShiftPlugin mode int: 4
Semitones for track 1: -12

So it seams that is correctly activated but the sound doesn’t change.
Basically I’m trying to play a slice of a wav sample and pitch it.

Thanks

If I were debugging this I’d do two things:

  1. Check that the DemoRunner PitchAndTimeDemo is working for you. That should prove the Pitch Shift plugin is working
  2. Step through the audio processing

For 2, you want to end up in PitchShiftPlugin::Pimpl::applyToBuffer so add a breakpoint there and see if it’s hit (at the top of tracktion_PitchShift.cpp).

Step through the processing to see if it goes in to the TimeStretcher.

Thanks Dave!

For the second point I try to put a breakpoint here (path: tracktion_engine\modules\tracktion_engine\plugins\effects\tracktion_PitchShift.cpp)

void applyToBuffer (const PluginRenderContext& fc, float semis)
{
    SCOPED_REALTIME_CHECK

    if (timestretcher->isInitialised())
    {
        auto newMode = (TimeStretcher::Mode) owner.mode.get();
        auto newOptions = owner.elastiqueOptions.get();

        if (newMode != mode || newOptions != elastiqueOptions)
            initialise (owner.sampleRate, semis, newMode, newOptions);

        inputFifo.write (*fc.destBuffer, fc.bufferStartSample, fc.bufferNumSamples);

        if (! timestretcher->setSpeedAndPitch (1.0f, semis))
            jassertfalse;

        // Loop until output FIFO has enough samples to proceed
        {
            int needed = timestretcher->getFramesNeeded();

            for (;;)
            {
                if (inputFifo.getNumReady() < needed)
                    break;

                if (outputFifo.getFreeSpace() < samplesPerBlock)
                    break;

                timestretcher->processData (inputFifo, needed, outputFifo);
                needed = timestretcher->getFramesNeeded();

                if (outputFifo.getNumReady() >= fc.bufferNumSamples)
                    break;
            }
        }

        if (outputFifo.getNumReady() < fc.bufferNumSamples)
        {
            jassertfalse;
            fc.destBuffer->clear (fc.bufferStartSample, fc.bufferNumSamples);
        }
        else
        {
            outputFifo.read (*fc.destBuffer, fc.bufferStartSample, fc.bufferNumSamples);
        }

        if (fc.bufferForMidiMessages != nullptr)
            fc.bufferForMidiMessages->addToNoteNumbers (juce::roundToInt (semis));
    }
}

And it arrive correctly! But the sound doesn’t change.

I’m trying to build the DemoRunner, I have to change $DIR/build to “$DIR/build” in generate_examples script because my directory has spaces :smiley:

Just to be sure, mi clip is a midi clip should be a problem or as in Waveform is possible to change pitch of the sound out of the sampler?

I can open the DemoRunner, Pitch and Time.

It works but the quality is very poor. It works quite well if I put just, for example, one kick or one snare but if I put a longer file no.

Is this how expect to work?
Anyway in my project it doesn’t take any changes.

Thanks!

What do you mean by “quality”?
It’s using SoundTouch so isn’t great at large pitch changes.

We also support RubberBand and Elastique.
They are much higher quality but have their own licensing requirements so aren’t bundled with the Engine.

It croaks at a pitch of -1 or +1 :frowning:

After a reload of the system it works correctly but still not working on my project.
Have you an idea of why?

Can you try inserting it at index 0 but before the sampler plugin (that should push the pitch shifter up when the sampler is inserted).

Hi Dave,

you mean like this:

        track->pluginList.insertPlugin(*pitchShift, 0, nullptr);
        track->pluginList.insertPlugin(*sampler, 1, nullptr);

?

I tried but doesn’t work :frowning:

For testing I try the DemoRunner with my .wav file and they worked.

I tried also to create only one track with directly a wav file in this way:

auto audioTracks = tracktion::getAudioTracks(edit);

auto& tempoSequence = edit.tempoSequence;
auto startBeat = tracktion::BeatPosition::fromBeats(0.0);
auto endBeat = tracktion::BeatPosition::fromBeats(16.0);
auto beatRange = tracktion::BeatRange(startBeat, endBeat);
auto offset = tracktion::BeatDuration::fromBeats(0.0);
auto clipPos = tracktion::createClipPosition(tempoSequence, beatRange, offset);

auto track = audioTracks[0];
auto clip = track->insertWaveClip(audioFiles[0].file.getFileNameWithoutExtension(), audioFiles[0].file.getFullPathName(), clipPos, true);
auto pitchShift = dynamic_cast<tracktion::PitchShiftPlugin*>(
    edit.getPluginCache().createNewPlugin(tracktion::PitchShiftPlugin::xmlTypeName, {}).get()
    );

if (clip == nullptr) {
    DBG("Failed to insert audio clip!");
}
else {
    clip->setAutoPitch(0);
    clip->setAutoTempo(0);
}

if (!pitchShift)
{
    DBG("Errore: impossibile creare il PitchShiftPlugin sulla traccia audio");
}

pitchShift->semitonesValue = 12.0f;
pitchShift->setEnabled(true);

track->pluginList.insertPlugin(*pitchShift, 0, nullptr);

But also here nothing to do. the file play correctly but without the pitch

No, I meant like this:

        track->pluginList.insertPlugin(*pitchShift, 0, nullptr);
        track->pluginList.insertPlugin(*sampler, 0, nullptr);

But looking at the code, what you had originally should work. -1 means “at the end” (but that would got after the vol/pan and level meter plugins).

If you don’t change the number of semitones does it work?

If not, can you provide a project I can just build and run?
I.e. a cmake based project I can just cmake ... then cmake --build ... on?
That’s probably the quickest way to a resolution.

Hi Dave,

I will try the double 0 index,

In fact setting semitones or not doesn’t change, the sound always pass the plugin but without affecting it.

I can send you the project but I used Projucer and I have a Visual Srudio Buiod and an ARM64, can I send you the project via private message?

Thanks

Also with this configuration nothing to do. I ear the sound but without the pitch.