Help to start

Hi all,

I create a new JUCE GUI project with ProJuce linking tracktion_engine (cloned with git) and it seams integrated correctly.

I have my MainComponent.h like this:

private:
    std::unique_ptr<tracktion::engine::Engine> engine;
    std::unique_ptr<ProjectSelectionComponent> projectSelection;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainComponent)
};

Then my MainComponent.cpp:

#include "tracktion_engine/tracktion_engine.h" corretto

MainComponent::MainComponent()
{

    engine = std::make_unique<tracktion::engine::Engine>(ProjectInfo::projectName);

    projectSelection = std::make_unique<ProjectSelectionComponent>(*engine);
    addAndMakeVisible(*projectSelection);
    projectSelection->setListener(this);
  
    setSize(800, 480);
}

Then in ProjectSelectionComponent.h I have:

private:
    te::Engine& engine;
    te::Edit edit { engine, te::Edit::EditRole::forEditing };
    te::TransportControl& transport { edit.getTransport() };

and in ProjectSelectionComponent.cpp I have this:

ProjectSelectionComponent::ProjectSelectionComponent(te::Engine& eng)
    : engine(eng)
{
    auto& deviceManager = engine.getDeviceManager().deviceManager;
    deviceManager.getCurrentDeviceTypeObject()->scanForDevices();
    auto result = deviceManager.initialiseWithDefaultDevices(0, 2);
    if (result != "") {
        DBG("Attempt to initialise default devices failed!");
    }
    edit.ensureNumberOfAudioTracks(1);
    auto track = te::getAudioTracks(edit)[0];
    DBG("Track name: " + juce::String(track->getName()));
    juce::File audioFile("correct_path/sample2.wav");
    if (!audioFile.existsAsFile())
    {
        DBG("Audio file not found!");
    }
    else
    {
                const tracktion::TimeRange fourBarTimeRange(
            0s,
            edit.tempoSequence.toTime({ 4, tracktion::BeatDuration() })
        );

        auto clip = track->insertNewClip(te::TrackItem::Type::wave, "Audio Clip", fourBarTimeRange, nullptr);
        if (clip == nullptr)
            DBG("Failed to insert audio clip!");

        auto& transport = edit.getTransport();
        transport.setPosition(0s);
        transport.setLoopRange(clip->getEditTimeRange());
        transport.looping = true;
        transport.play(false);
    }

I see Track Name: Track 1 so it seams that the track is correctly inizialized but I don’t ear anything. Is that wrong?

Any suggestion? Sorry I’m a little bit noob and I don’t find a good starting guide.

Thanks

Hi there, can you double check your code MD formatting as some of the above is a bit hard to read.

I’ll take a look once the formatting is a bit clearer :+1:

1 Like

Thank you dave! I tried to format better the code :slight_smile:

I can’t actually see where you’re creating a clip with the audio file as a source?

Maybe you want:

insertWaveClip (ClipOwner&, const juce::String& name, const juce::File& sourceFile,
                ClipPosition, DeleteExistingClips);

And if you want to specify the ClipPosition in beats you can use:

createClipPosition (const TempoSequence&, BeatRange, BeatDuration offset = {});

Finally, I don’t think your fourBarTimeRange is correct.
You probably want something like TempoSequnce::toBeats (tempo::BarsAndBeats).

Does that help?

I will try later and I let you know, thanks Dave, appreciated!

Hi Dave, nice it works! I can manage to ear the file.

The code is this one, maybe can help some one in the future:

edit.ensureNumberOfAudioTracks(1);
auto track = te::getAudioTracks(edit)[0];
DBG("Track name: " + juce::String(track->getName()));

juce::File audioFile("YOUR_PATH/sample.wav");
if (!audioFile.existsAsFile())
{
    DBG("Audio file not found!");
}
else
{
    edit.tempoSequence.getTempos()[0]->setBpm(90);

    auto& tempoSequence = edit.tempoSequence;
    DBG("BPM: " & juce::String(tempoSequence))
    auto startBeat =te::BeatPosition::fromBeats(0.0);
    auto endBeat = te::BeatPosition::fromBeats(16.0);
    auto beatRange = te::BeatRange(startBeat, endBeat);
    auto offset = te::BeatDuration::fromBeats(0.0);
    auto clipPos = te::createClipPosition(tempoSequence, beatRange, offset);

    auto clip = track->insertWaveClip(audioFile.getFileNameWithoutExtension(), audioFile, clipPos, false);
    if (clip == nullptr) {
        DBG("Failed to insert audio clip!");
    }
    else {
        clip->setAutoPitch(0);
        clip->setAutoTempo(0);
        DBG("Clip name: " + clip->getName());
        auto& transport = edit.getTransport();
        transport.setPosition(0s);
        transport.setLoopRange(clip->getEditTimeRange());
        transport.looping = true;
        transport.play(false);

        
    }
}

If I can bother you again I would like to build a 8 tracks (fixed) step sequencer.
So if I understand well I can create the 8 tracks and give this tracks a new clip like this:

auto audioTracks = te::getAudioTracks(edit);
auto& transport = edit.getTransport();
if (audioTracks.size() < 8)
{
    DBG("Non ci sono 8 tracce audio disponibili!");
}
else
{
    for (size_t i = 0; i < 8; ++i)
    {
        auto track = audioTracks[i];
        const te::TimeRange editTimeRange(0s, edit.tempoSequence.toTime({ 1, te::BeatDuration() }));

        auto clip = track->insertNewClip(te::TrackItem::Type::step, "Step Clip " + juce::String(i + 1), editTimeRange, nullptr);
        if (clip == nullptr) {
            DBG("Failed to insert audio clip for track: " + juce::String(i + 1));
        }
        else {
            DBG("Step Clip inserted inside track: " + juce::String(i + 1));
        }
    }
}

In this way I have 8 tracks and every track has a step clip, now I can associate to the track (or the clip) a sampler in order to manage 16 notes per sampler that will be a wav file sliced in maximum 16 slices.

And the inside the step clip I can create a pattern.

Maybe I misunderstand something.

Super thanks!

It will be fantastic to have more documentation except the demo in order to understand better this super engine!

Hi Dave, thank you again for the help, I made some step ahead but still need a little bit of help on understanding some points.

I downloaded Waveform 13 and this help a lot understanding the concept of tracks, clips, midiClips etc.

I try also to use sampler plugin in so I tried to implement this on code.

Not sure if this can work because the sound played on note 37 should not be pitched (don’t know if is possible).

I see on waveform 13 I see on sampler you have the possibility to say “fixed pitch” and change Envelope Mode from ADRS to OneShot, is this possible from tracktion_engine? Maybe also the pitch :grinning_face:

Have you any suggestion? All seams to be correct (or at least I don’t have any crash or error message) but I ear just a strange sound (from the file but just one second) - I expect to ear the “full slice”

         // Creating eight tracks
 edit.ensureNumberOfAudioTracks(8);
 auto& transport = edit.getTransport();

 constexpr int numFiles = 8;
 std::array<juce::File, numFiles> files;

 // Just add 8 random sound to a fixed array. FAKEPATH is just for the forum
 juce::String basePath("FAKEPATH\\Sounds\\");

 for (int i = 0; i < numFiles; ++i)
 {
     juce::String fileName = "sample" + juce::String(i + 1) + ".wav";
     files[i] = juce::File(basePath + fileName);
 }

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

 // Retrieving all the tracks created
 auto audioTracks = te::getAudioTracks(edit);
 // Checking if I have 8 tracks
 if (audioTracks.size() < 8)
 {
     DBG("There aren't 8 tracks available!");
 }
 else
 {
     // Looping through tracks
     for (size_t i = 0; i < 8; ++i)
     {
         // Retrieve track with index = i
         auto track = audioTracks[i];
         // Create a timeRange to create the clip 0 to 4 bars
         const te::TimeRange editTimeRange(0s, edit.tempoSequence.toTime({ 4, te::BeatDuration() }));
         // Creating the clip with timeRange and name = Midi Clip i
         auto clip = track->insertNewClip(te::TrackItem::Type::midi, "Midi Clip " + juce::String(i + 1), editTimeRange, nullptr);
         // Check if the clip was created successfully
         if (clip == nullptr) {
             DBG("Failed to insert midi clip for track: " + juce::String(i + 1));
         }
         else {
             // If clip is created successfully...
             DBG("Midi Clip inserted inside track: " + juce::String(i + 1));
             // Create a Midi Clip inside clip with index = 0
             auto midiClip = dynamic_cast<te::MidiClip*> (track->getClips()[0]);
             // Check if midiClip is valid
             if (midiClip == nullptr)
             {
                 DBG("midiClip is not valid!");
             }
             else
             {
                 // If midiClip is created correctly. create a SamplerPlugin
                 if (auto sampler = dynamic_cast<te::SamplerPlugin*> (edit.getPluginCache().createNewPlugin(te::SamplerPlugin::xmlTypeName, {}).get()))
                 {
                     // Add the sampler plugin to the track (can be also track->pluginList.insertPlugin(*sampler, 0, nullptr); ?
                     midiClip->getTrack()->pluginList.insertPlugin(*sampler, 0, nullptr);
                     // Retrieve the sequence inside midiClip
                     auto& seq = midiClip->getSequence();
                     // Just add a note #36 in position 0 and lenght 0.5
                     seq.addNote(36, te::BeatPosition::fromBeats(0), te::BeatDuration::fromBeats(1), 127.0, 0, nullptr);

                     // Adding the sound 16 times (in the future with different time frame to simulate slice?
                     // Adding the sound to the midiNote starting from 36 an increase
                     for (size_t y = 0; y < 16; y++) {
                         int midiNote = 36 + y;
                         // This is just a fake implementation "Slice i" starting at 10 to 20 seconds
                         DBG("File " + files[1].getFileNameWithoutExtension() + " loaded on slice : " + juce::String(y + 1));
                         const auto error = sampler->addSound(files[i].getFullPathName(), "Slice " + juce::String(y), 1264283, 1487128, 1.0f);
                         // Set the sound with the midiNote fixed to midiNote value
                         sampler->setSoundParams(sampler->getNumSounds() - 1, midiNote, midiNote, midiNote);
                         jassert(error.isEmpty());
                     }
                 }
                 else
                 {
                     DBG("Sampler plugin creation failed!");
                 }
             }

         }
     }
 }

 // Checking if note is added correctly added to the midiClip
 auto track = audioTracks[0];

 if (track == nullptr)
 {
     DBG("Track null");
     return;
 }
 else 
 {
     DBG("Track exists");
 }

 auto clips = track->getClips();
 if (clips.isEmpty())
 {
     DBG("Clips empty");
     return;
 }
 else
 {
     DBG("Clips exists");
 }

 auto midiClip = dynamic_cast<te::MidiClip*>(clips[0]);
 if (midiClip == nullptr)
 {
     DBG("midiClip null");
     return;
 }
 else
 {
     DBG("midiClip exists");
 }

 auto& seq = midiClip->getSequence();
 DBG("Check notes");

 for (auto note : seq.getNotes())
 {
     // Here I see correctly Note: 36, Velocity: 1
     DBG("Note: " + juce::String(note->getNoteNumber()) +
         ", Velocity: " + juce::String(note->getVelocity()));
         
 }

 const te::TimeRange editTimeRange(0s, edit.tempoSequence.toTime({ 16, te::BeatDuration() }));

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

Thanks again.
I would like also, if possible, to contact you privately I would like to send a gift for your time!

I have do some more tests and:

This works:

auto audioTrack = tracktion::getAudioTracks(edit)[0];
        DBG("Track name: " + juce::String(audioTrack->getName()));

        juce::File audioFile1("FAKEPATH\\sample2.wav");
        juce::File audioFile2("FAKEPATH\\sample8.wav");
        if (!audioFile1.existsAsFile() or !audioFile2.existsAsFile())
        {
            DBG("Audio file not found!");
        }
        else
        {
            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 audioTracks = tracktion::getAudioTracks(edit);
            auto& transport = edit.getTransport();
            if (audioTracks.size() < 2)
            {
                DBG("Non ci sono 2 tracce audio disponibili!");
            }
            else
            {
                for (size_t i = 0; i < 2; ++i)
                {
                    auto audioTrack = audioTracks[i];

                    const auto& currentAudioFile = (i == 1) ? audioFile1 : audioFile2;

                    auto clip = audioTrack->insertWaveClip(
                        currentAudioFile.getFileNameWithoutExtension(),
                        currentAudioFile,
                        clipPos,
                        true
                    );

                    if (clip == nullptr)
                    {
                        DBG("Inserimento wave clip fallito sulla traccia " << i);
                    }
                    else
                    {
                        clip->setAutoPitch(0);
                        clip->setAutoTempo(0);

                        DBG("Inserimento wave clip riuscito sulla traccia " << i);
                    }
                }
            }
        }

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

But this with midi no.

constexpr int numFiles = 8;
std::array<juce::File, numFiles> files;

// Just add 8 random sound to a fixed array.
juce::String basePath("C:\\Users\\luca.piona\\Desktop\\Backup PC\\VBNET2\\JuicyPadsSMPLR\\Builds\\VisualStudio2022\\x64\\Debug\\App\\Projects\\1\\Sounds\\");

for (int i = 0; i < numFiles; ++i)
{
    juce::String fileName = "sample2.wav";
    files[i] = juce::File(basePath + fileName);
}

auto audioTracks = tracktion::getAudioTracks(edit);

for (size_t i = 0; i < 1; ++i)
{
    // Retrieve track with index = i
    auto track = audioTracks[i];
    // Create a timeRange to create the clip 0 to 4 bars
    // Creating the clip with timeRange and name = Midi Clip i
    auto clip = track->insertNewClip(tracktion::TrackItem::Type::midi, "Midi Clip " + juce::String(i + 1), editTimeRange, nullptr);
    // Check if the clip was created successfully
    if (clip == nullptr) {
        DBG("Failed to insert midi clip for track: " + juce::String(i + 1));
    }
    else {
        // If clip is created successfully...
        DBG("Midi Clip inserted inside track: " + juce::String(i + 1));
        // Create a Midi Clip inside clip with index = 0
        auto midiClip = dynamic_cast<tracktion::MidiClip*> (track->getClips()[0]);
        // Check if midiClip is valid
        if (midiClip == nullptr)
        {
            DBG("midiClip is not valid!");
        }
        else
        {
            // If midiClip is created correctly. create a SamplerPlugin
            if (auto sampler = dynamic_cast<tracktion::SamplerPlugin*> (edit.getPluginCache().createNewPlugin(tracktion::SamplerPlugin::xmlTypeName, {}).get()))
            {
                // Add the sampler plugin to the track (can be also track->pluginList.insertPlugin(*sampler, 0, nullptr); ?
                midiClip->getTrack()->pluginList.insertPlugin(*sampler, 0, nullptr);
                // Retrieve the sequence inside midiClip
                auto& seq = midiClip->getSequence();
               
                // Adding the sound 16 times (in the future with different time frame to simulate slice?
                // Adding the sound to the midiNote starting from 36 an increase
                for (size_t y = 0; y < 1; y++) {
                    int midiNote = 36 + y;
                    // This is just a fake implementation "Slice i" starting at 10 to 20 seconds
                    DBG("File " + files[1].getFullPathName() + " loaded on slice : " + juce::String(y + 1));
                    const auto error = sampler->addSound(files[1].getFullPathName(), "Slice " + juce::String(y + 1), 100, 10000, 1.0f);
                    // Set the sound with the midiNote fixed to midiNote value
                    DBG("Cambio la nota (" + juce::String(midiNote) + ") per lo slice : " + juce::String(sampler->getNumSounds() - 1));
                    sampler->setSoundParams(sampler->getNumSounds() - 1, midiNote, midiNote, midiNote);
                    jassert(error.isEmpty());
                }

                // Just add a note #36 in position 0 and lenght 0.5
                seq.addNote(36, tracktion::BeatPosition::fromBeats(0), tracktion::BeatDuration::fromBeats(4), 127.0, 0, nullptr);
            }
            else
            {
                DBG("Sampler plugin creation failed!");
            }
        }

    }
}

// Checking if note is added correctly added to the midiClip
auto track = audioTracks[0];

if (track == nullptr)
{
    DBG("Track null");
    return;
}
else
{
    DBG("Track exists");
}

auto clips = track->getClips();
if (clips.isEmpty())
{
    DBG("Clips empty");
    return;
}
else
{
    DBG("Clips exists");
}

auto midiClip = dynamic_cast<tracktion::MidiClip*>(clips[0]);
if (midiClip == nullptr)
{
    DBG("midiClip null");
    return;
}
else
{
    DBG("midiClip exists");
}

auto& seq = midiClip->getSequence();
DBG("Check notes");

for (auto note : seq.getNotes())
{
    // Here I see correctly Note: 36, Velocity: 1
    DBG("Note: " + juce::String(note->getNoteNumber()) +
        ", Velocity: " + juce::String(note->getVelocity()));

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

In the first case I ear two sound + metronome in the second case only the metronome :frowning:

Any idea?

Thanks!

I found that using a small file (like a snare) it works.
The file should be 44100 instread of 48000

The problem is that using a longer file I ear nothing.

I’m trying to do that:

sampler->addSound(files[1].getFullPathName(), "Slice " + juce::String(y + 1), 2800512, 1000511, 1.0f);

And checking files lenght I have this:

sample2.wav
Lenght in sample: 4410471
Sampling rate: 44100
Lenght in seconds: 100.011

There are some limits about lenght of the file?

Understand something more.

If I switch sound 1 and 2 I ear the one in the first track.
The second track is not sounding correctly :frowning:

Hi there, unfortunately the SamplerPlugin that TE comes with is different to the newer Micro/Multi Sampler that Waveform comes with. It’s under the “Legacy” menu in Waveform which you have to enabled from the “Plugins” settings page.

Have you tried looking at the StepSequenceDemo which is part of the DemoRunner example? That shows how to set up a Sampler plugin and a StepClip to drive it.

Most of the code in that example is for the UI, the setup of the Sampler and StepClip is relatively small.


If you are only playing short notes, maybe you need to set the setSoundOpenEnded property to not stop the sound on note-off events?

Thank you, I made some steps ahead :slight_smile:
Learning juce and tracking_engine.

Any change in the future to have micro sampler in tracking_engine?