Crash in call to vstEffect->processReplacing with Synth1 plugin

I have a midi input selected on a track with the Synth1 plugin.
The physical midi device is the MPK mini piano which I use for testing.
The pluging shows up as it should with this code:

    //	Xml :
    //
    //	<PLUGIN name="Synth1 VST64" 
    //			descriptiveName="Synth1 VSTi" 
    //			format="VST"
    //	        category="Synth" 
    //			manufacturer="Daichi" 
    //			version="1.0.0.0" 
    //			file="C:\Synth1\Synth1 VST64.dll"
    //	        uniqueId="53315673" 
    //			isInstrument="1" 
    //			fileTime="1892628bcdc" 
    //			infoUpdateTime="18926312fb4"
    //	        numInputs="0" 
    //			numOutputs="2" 
    //			isShell="0" 
    //			hasARAExtension="0"
    //	        uid="53315673"/>

    XmlDocument pdXml(Xml);
    PluginDescription pd;
    pd.loadFromXml(*pdXml.getDocumentElement().get());
    auto plugin = edit.createNewPlugin(xmlType, pd);
    track->pluginList.insertPlugin(plugin, index, nullptr);
    plugin->showWindowExplicitly();

The input is selected using this code:

    te::InputDeviceInstance* targetInputDevice = getInputDeviceByName("MPK mimi 3");
    targetInputDevice->setTargetTrack(*track, 0, true);
    targetInputDevice->getInputDevice().flipEndToEnd();

When I press down on the piano, I can hear the notes fine.
However, if I switch notes quickly or play a chord, the plugin crashes.
The crash occurs here:

I am using the VST2 SDK in my project. I know that this is no longer supported. Maybe this is the problem? Another thing I should mention is that the plugin works fine in Waveform. So the problem is on my side. Other VSTs seem to work fine. This is my 2nd day of intense debugging. I can post more information if someone has an idea. Here is the call stack:

        Synth1 VST64.dll!00007ff99e421fa6()	Unknown
        Synth1 VST64.dll!00007ff99e40c5a3()	Unknown
        Synth1 VST64.dll!00007ff99e40bf0a()	Unknown
        Synth1 VST64.dll!00007ff99e40df6d()	Unknown
        Synth1 VST64.dll!00007ff99e3dcd1f()	Unknown
        Synth1 VST64.dll!00007ff99e3dcbdc()	Unknown
        Synth1 VST64.dll!00007ff99e3dcacc()	Unknown
        Synth1 VST64.dll!00007ff99e3dcacc()	Unknown
        Synth1 VST64.dll!00007ff99e3e5758()	Unknown
        Synth1 VST64.dll!00007ff99e3dc367()	Unknown
        Synth1 VST64.dll!00007ff99e3de6e2()	Unknown
        Synth1 VST64.dll!00007ff99e3de6e2()	Unknown
        Synth1 VST64.dll!00007ff99e3ebaf6()	Unknown
        Synth1 VST64.dll!00007ff99e3e6134()	Unknown
        Synth1 VST64.dll!00007ff99e3d685b()	Unknown
        Synth1 VST64.dll!00007ff99e3d36f7()	Unknown
        Synth1 VST64.dll!00007ff99e3f86c8()	Unknown
        Synth1 VST64.dll!00007ff99e3e9e1d()	Unknown
        Synth1 VST64.dll!00007ff99e3ea251()	Unknown
        MyProject.dll!juce::VSTPluginInstance::invokeProcessFunction(juce::AudioBuffer<float> &buffer, int sampleFrames) Line 2487	C++
        MyProject.dll!juce::VSTPluginInstance::processAudio<float>(juce::AudioBuffer<float> &buffer, juce::MidiBuffer & midiMessages, juce::AudioBuffer<float> &tmpBuffer, juce::HeapBlock<float*, 0> &channelBuffer, bool processBlockBypassedCalled) Line 2461	C++
        MyProject.dll!juce::VSTPluginInstance::processBlock(juce::AudioBuffer<float> &buffer, juce::MidiBuffer & midiMessages) Line 1449	C++
        MyProject.dll!tracktion::engine::ExternalPlugin::processPluginBlock(const tracktion::engine::PluginRenderContext & fc, bool processedBypass) Line 1426	C++
        MyProject.dll!tracktion::engine::ExternalPlugin::applyToBuffer(const tracktion::engine::PluginRenderContext & fc) Line 1333	C++
        MyProject.dll!tracktion::engine::Plugin::applyToBufferWithAutomation(const tracktion::engine::PluginRenderContext & pc) Line 685	C++
        MyProject.dll!tracktion::engine::PluginNode::process(tracktion::graph::Node::ProcessContext & pc) Line 198	C++
        MyProject.dll!tracktion::graph::Node::process(unsigned int numSamples, juce::Range<__int64> referenceSampleRange) Line 480	C++
        MyProject.dll!tracktion::graph::LockFreeMultiThreadedNodePlayer::processNode(tracktion::graph::LockFreeMultiThreadedNodePlayer::PreparedNode & preparedNode, tracktion::graph::Node & node) Line 413	C++
        MyProject.dll!tracktion::graph::LockFreeMultiThreadedNodePlayer::processNextFreeNode(tracktion::graph::LockFreeMultiThreadedNodePlayer::PreparedNode & preparedNode) Line 395	C++
        MyProject.dll!tracktion::graph::LockFreeMultiThreadedNodePlayer::ThreadPool::process() Line 174	C++
        MyProject.dll!tracktion::graph::ThreadPoolSemHybrid<tracktion::graph::LightweightSemaphore>::runThread() Line 625	C++
        MyProject.dll!tracktion::graph::ThreadPoolSemHybrid<tracktion::graph::LightweightSemaphore>::createThreads::__l6::<lambda>() Line 530	C++

Regards

@dave96

I have cloned the latest version of tracktion and created a basic test application.
In the test application I’ve enabled VST via JUCE_PLUGINHOST_VST

I’ve included in my project the VST2 SDK

As well as the tracktion modules

image

Here is the VST2 SDK (I am not able to upload a zip containing the SDK).

I’ve also installed the Synth1 plugin which can be downloaded here:

Even with the latest version of tracktion, the test app crashes.

I downloaded that latest version of Waveform and the plugin work properly.
Maybe Waveform uses an older version of tracktion? Maybe it loads the VST a different way?

The test code to load the plugin and midi input into the test app is as follows:

MainComponent::MainComponent()
{
    setSize (600, 400);

    // Creat the tracktion engine and edit.
    engine = std::make_unique<te::Engine>(ProjectInfo::projectName, nullptr, nullptr);
    edit = std::make_unique<te::Edit>(*engine, te::createEmptyEdit(*engine), te::Edit::forEditing, nullptr, 0);
    edit->getTransport().ensureContextAllocated(true);

    auto& dm = edit->engine.getDeviceManager();
    for (int i = 0; i < dm.getNumMidiInDevices(); i++)
    {
        if (auto mip = dm.getMidiInDevice(i))
        {
            jassert(nullptr != mip);
            if (nullptr != mip)
                mip->setEnabled(true);
        }
    }

    te::InputDeviceInstance* targetInputDevice = nullptr;
    for (auto instance : edit->getAllInputDevices())
    {
        DBG(instance->getInputDevice().getName());

        if (instance->getInputDevice().getDeviceType() == te::InputDevice::waveDevice ||
            instance->getInputDevice().getDeviceType() == te::InputDevice::physicalMidiDevice ||
            instance->getInputDevice().getDeviceType() == te::InputDevice::virtualMidiDevice)
        {
            // Use the name of the midi device you are using.
            // I'm using the MPK mini 3
            if (instance->getInputDevice().getName().compareIgnoreCase("MPK mini 3") == 0)
            {
                targetInputDevice = instance;
                break;
            }
        }
    }

    jassert(nullptr != targetInputDevice);
    if (nullptr != targetInputDevice)
    {
        // Add a test track.
        int index = 0;
        edit->ensureNumberOfAudioTracks(index + 1);
        auto t = te::getAudioTracks(*edit)[index];

        jassert(nullptr != t);

        // Hook the midi input to the tack.
        targetInputDevice->setTargetTrack(*t, 0, true, nullptr);

        // Midi plugin details
        juce::String test1 = "<PLUGIN name = \"Synth1 VST64\" \
                descriptiveName = \"Synth1 VSTi\" \
                format = \"VST\" \
                category = \"Synth\" \
                manufacturer = \"Daichi\" \
                version = \"1.0.0.0\" \
                file = \"C:\\Users\\Work\\source\\repos\\Plugins\\Synth1\\Synth1 VST64.dll\" \
                uniqueId = \"53315673\" \
                isInstrument = \"1\" \
                fileTime = \"1892628bcdc\" \
                infoUpdateTime = \"18926312fb4\" \
                numInputs = \"0\" \
                numOutputs = \"2\" \
                isShell = \"0\" \
                hasARAExtension = \"0\" \
                uid = \"53315673\"/>";
        juce::String test2 = "vst";

        // Tracktion need a list of 'Known plugins' or else the plusing instance won't be created.
        juce::String test3;
        test3 += "<KNOWNPLUGINS>";
        test3 += test1;
        test3 += "</KNOWNPLUGINS>";
        juce::XmlDocument plugList(test3);
        engine->getPluginManager().knownPluginList.recreateFromXml(*plugList.getDocumentElement());

        // Create the plugin
        juce::PluginDescription pd;
        juce::XmlDocument pdXml(test1);
        pd.loadFromXml(*pdXml.getDocumentElement().get());
        auto plugin = edit->getPluginCache().createNewPlugin(test2, pd);

        jassert(nullptr != plugin.get());

        // Append the plugin to the test track.
        t->pluginList.insertPlugin(*plugin, 0, nullptr);
    }
}

We use a fairly recent version of TE in Waveform (only a couple of weeks behind if at all).

Can I double check this is only an issue in TE? I.e. have you tried it in the JUCE audio plugin host?
Looking at the stack trace, it’s fairly deep in the Synth1 code…

Hi Dave

I’ve just tried the AudioPluginHost example. I had to enable the VST just like I posted above (with the link to the VST2 SDK) and I get the exact same crash. I didn’t modify any of the code in the AudioPluginHost. I just rescanned to get access at Synth1.

It would be cool to know exactly how it’s done in Waveform. Why is it working there?

Thanks for looking into this.

What exact version of Waveform are you using?
I don’t think we do anything different in Waveform…
We do compile with these linker flags though: /NXCOMPAT:NO /DYNAMICBASE:NO

Maybe try that?

There is progress.

The above test app uses a GUI and with the linker flags it now works. More specifically when adding DYNAMICBASE:NO (NXCOMPAT:NO has no effect).

However, we still have the crash in our code. The only difference that I can see with test app is that we are using a DLL not GUI. I’m going to try to create a test DLL and see.

Let me know if you have anymore ideas.

Thanks again.

I’ve put the test code in a DLL and calling it from the test app with the following:

    typedef void(__cdecl* MYPROC)(void);
...
    HINSTANCE hinstLib = ::LoadLibrary("Test.dll");
    if (hinstLib != NULL)
    {
        MYPROC procAdd = (MYPROC)GetProcAddress(hinstLib, "TestSynth1");
        if (procAdd != NULL)
        {
            procAdd();
        }
    }

The /DYNAMICBASE:NO has no effect on the crash if I include it in the DLL compile only.
Only when the flag is included in the test app exe compile does the crash disappear.

At this point, I’m just posting stuff in case someone has the same problem.
I tried the following in Windows Security but no dice.

With these things its usually best to reach out to the plugin developer as they’re really the only ones who can tell you what the problem might be.

If you do something like load a different preset before processing does it work?

I reached out to Ichiro Toda a few days ago when I saw this forum post, asking him if he could update this plugin, because Synth1 is my favourite plugin and I want it to live on as long as possible. However I don’t think anyone ever managed to reach him. So I would personally find it cool if this was resolved on the framework’s side if possible

Presets don’t change anything unfortunately.
After slaloming around some kanji characters on the site where the plugin lives, I was able to find a weird looking email address. I reached out but no replies yet.
All we have now is hope.

idk if it helps, but synth1 also has a bit of a bug, that only appeared to me in bitwig so far, in which touching a plugin parameter does not notify the host about the change. sometimes when i tried automating parameters it also refused to act smooth. maybe this misbehaviour is a hint on some bigger underlying problem and certain edge case handling would prevent a daw from crashing on load

It’s too bad the plugin is not opensource.