0
0

Why it is not possible to send/receive VST events via FMOD?

  • You must to post comments
0
0

I have created a custom FMOD::DSP as dynamic library VST.
But some VST plugins crash my application in AEffect::processReplacing. What am I missing?
(This is not FMOD problem)

[code:8oevy98h]

include "vstplugin.h"

include <QDebug>

include <QWidget>

include <QTimer>

include "vst2.x/audioeffect.h"

include <math.h>

include "fmod/fmod.hpp"

include "fmod/fmod_errors.h"

include "SoundSystem.h"

include <QFile>

include <QTextStream>

include "MidiNotes.h"

include <QMutexLocker>

include "Windows.h"

include <QThread>

static int samplePos=0;

inline void ERRCHECK(FMOD_RESULT result)
{
if (result != FMOD_OK)
{
qDebug()<<"FMOD error! "<<result<<" "<<FMOD_ErrorString(result);
exit(-1);
}
}

static VstTimeInfo time;

static VstIntPtr VSTCALLBACK hostCallback (AEffect* effect,
VstInt32 opcode, VstInt32 index, VstIntPtr value, void *ptr, float opt){

//qDebug()&lt;&lt;&quot;hostCallback &quot;&lt;&lt;opcode;
VstIntPtr ret = 0;
switch (opcode) {

    // VST 1.0 opcodes...
case audioMasterVersion:
    //qDebug()&lt;&lt;&quot;audioMasterVersion&quot;;
    ret = 2400L;
    break;

case audioMasterAutomate:
    //qDebug()&lt;&lt;&quot;audioMasterAutomate&quot;;
    /*//    effect-&gt;setParameter(effect, index, opt);
            pVstPlugin = qtractorVstPlugin::findPlugin(effect);
            if (pVstPlugin) {
                    qtractorPluginForm *pForm = pVstPlugin-&gt;form();
                    if (pForm)
                            pForm-&gt;updateParamValue(index, opt);
            //  QApplication::processEvents();
            }*/
    break;

case audioMasterCurrentId:
    qDebug()&lt;&lt;&quot;audioMasterCurrentId&quot;;
    break;

case audioMasterIdle:
    qDebug()&lt;&lt;&quot;audioMasterIdle&quot;;
    /*qtractorVstPlugin::idleEditorAll();
            QApplication::processEvents();*/
    break;

case audioMasterGetTime:
    //qDebug()&lt;&lt;&quot;audioMasterGetTime&quot;;
    time.samplePos=samplePos;
    time.sampleRate=48000;
    return (long) &amp;time;
    /*if (pSession) {
                    ::memset(&amp;s_vstTimeInfo, 0, sizeof(s_vstTimeInfo));
                    s_vstTimeInfo.samplePos  = pSession-&gt;playHead();
                    s_vstTimeInfo.sampleRate = pSession-&gt;sampleRate();
                    ret = (long) &amp;s_vstTimeInfo;
            }*/
    break;

case audioMasterProcessEvents:
    qDebug()&lt;&lt;&quot;audioMasterProcessEvents&quot;;
    break;

case audioMasterIOChanged:
    qDebug()&lt;&lt;&quot;audioMasterIOChanged&quot;;
    break;

case audioMasterSizeWindow:
    qDebug()&lt;&lt;&quot;audioMasterSizeWindow&quot;;
    break;

case audioMasterGetSampleRate:
    qDebug()&lt;&lt;&quot;audioMasterGetSampleRate&quot;;
    /*pVstPlugin = qtractorVstPlugin::findPlugin(effect);
            if (pVstPlugin) {
                    effect-&gt;dispatcher(effect,
                            effSetSampleRate, 0, 0, NULL, float(pVstPlugin-&gt;sampleRate()));
            }*/
    break;

case audioMasterGetBlockSize:
    qDebug()&lt;&lt;&quot;audioMasterGetBlockSize&quot;;
    /*pVstPlugin = qtractorVstPlugin::findPlugin(effect);
            if (pVstPlugin) {
                    effect-&gt;dispatcher(effect,
                            effSetBlockSize, 0, pVstPlugin-&gt;bufferSize(), NULL, 0.0f);
            }*/
    break;

case audioMasterGetInputLatency:
    qDebug()&lt;&lt;&quot;audioMasterGetInputLatency&quot;;
    break;

case audioMasterGetOutputLatency:
    qDebug()&lt;&lt;&quot;audioMasterGetOutputLatency&quot;;
    ret=10;
    break;

case audioMasterGetCurrentProcessLevel:
    //qDebug()&lt;&lt;&quot;audioMasterGetCurrentProcessLevel&quot;;
    return kVstProcessLevelRealtime;
    break;

case audioMasterGetAutomationState:
    qDebug()&lt;&lt;&quot;audioMasterGetAutomationState&quot;;
    ret = 1; // off
    break;

if !defined(VST_2_3_EXTENSIONS)

case audioMasterGetSpeakerArrangement:
    qDebug()&lt;&lt;&quot;audioMasterGetSpeakerArrangement&quot;;
    break;

endif

case audioMasterGetVendorString:
    qDebug()&lt;&lt;&quot;audioMasterGetVendorString&quot;;
    ::strcpy((char *) ptr, &quot;Semenenko&quot;);
    break;

case audioMasterGetProductString:
    qDebug()&lt;&lt;&quot;audioMasterGetProductString&quot;;
    ::strcpy((char *) ptr, &quot;QVSTHOST&quot;);
    break;

case audioMasterGetVendorVersion:
    qDebug()&lt;&lt;&quot;audioMasterGetVendorVersion&quot;;
    break;

case audioMasterVendorSpecific:
    qDebug()&lt;&lt;&quot;audioMasterVendorSpecific&quot;;
    break;

case audioMasterCanDo:
    if (::strcmp(&quot;receiveVstMidiEvent&quot;, (char *) ptr) == 0 ||
            ::strcmp(&quot;sendVstMidiEvent&quot;,    (char *) ptr) == 0 ||
            ::strcmp(&quot;sendVstEvents&quot;,    (char *) ptr) == 0 ||
            ::strcmp(&quot;supplyIdle&quot;,    (char *) ptr) == 0 ||
            ::strcmp(&quot;asyncProcessing&quot;,    (char *) ptr) == 0 ||
        //  ::strcmp(&quot;midiProgramNames&quot;,    (char *) ptr) == 0 ||
        ::strcmp(&quot;openFileSelector&quot;,    (char *) ptr) == 0 ||
        ::strcmp(&quot;closeFileSelector&quot;,   (char *) ptr) == 0) {
        ret = 1;
    }else
        ret=0;
    qDebug()&lt;&lt;QThread::currentThreadId()&lt;&lt;&quot;audioMasterCanDo &quot;&lt;&lt;(char *) ptr&lt;&lt;&quot; &quot;&lt;&lt;ret;
    break;

case audioMasterGetLanguage:
    qDebug()&lt;&lt;&quot;audioMasterGetLanguage&quot;;
    //ret = kVstLangEnglish;
    break;

if 0 // !VST_FORCE_DEPRECATED

case audioMasterPinConnected:
    qDebug()&lt;&lt;&quot;audioMasterPinConnected&quot;;
    break;

    // VST 2.0 opcodes...
case audioMasterWantMidi:
    qDebug()&lt;&lt;&quot;audioMasterWantMidi&quot;;
    break;

case audioMasterSetTime:
    qDebug()&lt;&lt;&quot;audioMasterSetTime&quot;;
    break;

case audioMasterTempoAt:
    qDebug()&lt;&lt;&quot;audioMasterTempoAt&quot;;
    if (pSession)
        ret = (long) (pSession-&gt;tempo() * 10000.0f);
    break;

case audioMasterGetNumAutomatableParameters:
    qDebug()&lt;&lt;&quot;audioMasterGetNumAutomatableParameters&quot;;
    break;

case audioMasterGetParameterQuantization:
    qDebug()&lt;&lt;&quot;audioMasterGetParameterQuantization&quot;;
    ret = 1; // full single float precision
    break;

case audioMasterNeedIdle:
    qDebug()&lt;&lt;&quot;audioMasterNeedIdle&quot;;
    /*pVstPlugin = qtractorVstPlugin::findPlugin(effect);
            if (pVstPlugin &amp;&amp; !pVstPlugin-&gt;isIdleTimer()) {
                    effect-&gt;dispatcher(effect, effIdle, 0, 0, NULL, 0.0f);
                    pVstPlugin-&gt;setIdleTimer(true);
            }*/
    break;

case audioMasterGetPreviousPlug:
    qDebug()&lt;&lt;&quot;audioMasterGetPreviousPlug&quot;;
    break;

case audioMasterGetNextPlug:
    qDebug()&lt;&lt;&quot;audioMasterGetNextPlug&quot;;
    break;

case audioMasterWillReplaceOrAccumulate:
    qDebug()&lt;&lt;&quot;audioMasterWillReplaceOrAccumulate&quot;;
    ret = 1;
    break;

case audioMasterSetOutputSampleRate:
    qDebug()&lt;&lt;&quot;audioMasterSetOutputSampleRate&quot;;
    break;

case audioMasterSetIcon:
    qDebug()&lt;&lt;&quot;audioMasterSetIcon&quot;;
    break;

case audioMasterOpenWindow:
    qDebug()&lt;&lt;&quot;audioMasterOpenWindow&quot;;
    break;

case audioMasterCloseWindow:
    qDebug()&lt;&lt;&quot;audioMasterCloseWindow&quot;;
    break;

endif

case audioMasterGetDirectory:
    qDebug()&lt;&lt;&quot;audioMasterGetDirectory&quot;;
    break;

case audioMasterUpdateDisplay:
    //qDebug()&lt;&lt;&quot;audioMasterUpdateDisplay&quot;;
    /*pVstPlugin = qtractorVstPlugin::findPlugin(effect);
            if (pVstPlugin) {
                    qtractorPluginForm *pForm = pVstPlugin-&gt;form();
                    if (pForm)
                            pForm-&gt;refresh();
            //  QApplication::processEvents();
            }*/
    break;

case audioMasterBeginEdit:
    qDebug()&lt;&lt;&quot;audioMasterBeginEdit&quot;;
    break;

case audioMasterEndEdit:
    qDebug()&lt;&lt;&quot;audioMasterEndEdit&quot;;
    /*pVstPlugin = qtractorVstPlugin::findPlugin(effect);
            if (pVstPlugin) {
                    qtractorPluginForm *pForm = pVstPlugin-&gt;form();
                    if (pForm) {
                            pForm-&gt;updateParamValue(index,
                                    effect-&gt;getParameter(effect, index));
                    }
            //  QApplication::processEvents();
            }*/
    break;

case audioMasterOpenFileSelector:
    qDebug()&lt;&lt;&quot;audioMasterOpenFileSelector&quot;;
    //ret = qtractorVstPlugin_openFileSelector(effect, (VstFileSelect *) ptr);
    break;

case audioMasterCloseFileSelector:
    qDebug()&lt;&lt;&quot;audioMasterCloseFileSelector&quot;;
    //ret = qtractorVstPlugin_closeFileSelector(effect, (VstFileSelect *) ptr);
    break;

default:
    qDebug()&lt;&lt;&quot;audioMasterUnknown &quot;&lt;&lt;opcode;
    break;
}
return ret;

}

VstPlugin::VstPlugin(const QString &fileName,SoundSystem *asystem): QObject(asystem),system(asystem),library(fileName)
{
playedNote=-1;
dsp=0;
effect=0;
midi=0;
widget=0;
timer=new QTimer(this);
connect(timer,SIGNAL(timeout()),SLOT(idle()));
timer->start(100);
if(!library.load()) return;
instance=(VST_GetPluginInstance)library.resolve("VSTPluginMain");
if(!instance)
instance=(VST_GetPluginInstance)library.resolve("main");
if(!instance) return;

effect= (*instance)(hostCallback);

if(effect){
    AudioEffect *o=(AudioEffect *)effect-&gt;object;
    if(o){
        //o-&gt;setParameter(37,1);
        o-&gt;setSampleRate(48000);
        o-&gt;setNumInputs(2);
        o-&gt;setNumOutputs(2);
        o-&gt;canDoubleReplacing(false);
        o-&gt;canProcessReplacing(true);
        o-&gt;resume();            
        qDebug()&lt;&lt;&quot;blockSize=&quot;&lt;&lt;o-&gt;getBlockSize();

// o->setBlockSize(16384);
{
int channels=20;
int samples=1024;
float **input=new float[channels];
float **output=new float
[channels];
for(int c=0;c<channels;c++){
input[c]=new float[samples];
output[c]=new float[samples];
}
qDebug()<<"process";
o->processReplacing(input,output,samples);
qDebug()<<"process~";
}
}
createDsp();
}else{
qDebug()<<"No effect";
}
qDebug()<<"create";
}

VstPlugin::~VstPlugin(){
QMutexLocker locker(&mutex);
qDebug()<<"a~VstPlugin()";
if(dsp){
dsp->remove();
dsp->release();
dsp=0;
}
if(effect){
//store();
if(widget){
vst_dispatch(effEditClose, 0, 0, 0, 0.0f);
delete widget;
}
vst_dispatch(effClose, 0, 0, 0, 0.0f);
instance=0;
library.unload();
effect=0;
}
qDebug()<<"~VstPlugin()";
}

bool VstPlugin::isValid() const{
return library.isLoaded() && instance && effect;
}

QString VstPlugin::getName() const{
if(!effect) return QString::null;
char szName[256]; ::memset(szName, 0, sizeof(szName));
if (vst_dispatch(effGetEffectName, 0, 0, (void *) szName, 0.0f)){
qDebug()<<"name="<<szName;
}
return QString(szName);
}

int VstPlugin::vst_dispatch(long opcode, long index, long value, void *ptr, float opt) const{
if(!effect) return 0;
return effect->dispatcher(effect,opcode,index,value,ptr,opt);
}

void VstPlugin::dumpInfo(){
if(!effect){
qDebug()<<"Invalid";
return;
}
qDebug()<<"UID="<<effect->uniqueID;
qDebug()<<"Name="<<getName();
qDebug()<<"NumParams="<<effect->numParams;
qDebug()<<"inputs="<<effect->numInputs;
qDebug()<<"outputs="<<effect->numOutputs;
qDebug()<<"hasEditor="<<(effect->flags & effFlagsHasEditor);
}

void VstPlugin::closeEditor(){
vst_dispatch(effEditClose, 0, 0, NULL, 0.0f);
vst_dispatch(effEditIdle, 0, 0, NULL, 0.0f);
delete widget; widget=0;
}

void VstPlugin::openEditor(QWidget *parent){
if(!effect) return;
if(widget){
return;
}

widget=new QWidget(parent);

long  value = 0;
void *ptr = (void *) widget-&gt;winId();

vst_dispatch(effEditOpen, 0, value, ptr, 0.0f);

struct ERect {
    short top;
    short left;
    short bottom;
    short right;
} *pRect;

if (vst_dispatch(effEditGetRect, 0, 0, &amp;pRect, 0.0f)) {
    int width=pRect-&gt;right - pRect-&gt;left;
    int height=pRect-&gt;bottom - pRect-&gt;top;
    if(width &amp;&amp; height){
        widget-&gt;setFixedSize(width,height);
    }
    qDebug()&lt;&lt;&quot;size: &quot;&lt;&lt;width&lt;&lt;&quot;x&quot;&lt;&lt;height;
}
qDebug()&lt;&lt;&quot;show&quot;;

widget-&gt;show();
vst_dispatch(effEditIdle, 0, 0, NULL, 0.0f);
Sleep(100);
vst_dispatch(effEditIdle, 0, 0, NULL, 0.0f);

}

void VstPlugin::idle(){
QMutexLocker locker(&mutex);
if(widget){
//vst_dispatch(effEditIdle, 0, 0, NULL, 0.0f);
}
}

void VstPlugin::process(float *pInBuf, float *pOutBuf,unsigned int nSamples){
bool instrument=false;
//QMutexLocker locker(&mutex);
int note=-1;
if(midi){
if(instrument)
note=midi->getCurrentRow();
else
note=midi->getCurrentNote();
}
VstEvents events;
events.reserved=0;
events.numEvents=0;
if(playedNote!=note){
if(playedNote!=-1){
// qDebug()<<"noteOff "<<playedNote;

        VstMidiEvent *event=new VstMidiEvent();
        event-&gt;flags=kVstMidiEventIsRealtime;

        ::memset(event, 0, sizeof(VstMidiEvent));
        event-&gt;type = kVstMidiType;
        event-&gt;byteSize = sizeof(VstMidiEvent);
        if(instrument){
            event-&gt;midiData[0]=0xB0;
            event-&gt;midiData[1]=0x7b;
        }else{
            event-&gt;midiData[0]=0x80;
            event-&gt;midiData[1]=playedNote;
        //event-&gt;midiData[1]=midi-&gt;noteAtRow(playedNote);
        }
        event-&gt;midiData[2]=0x0;
        event-&gt;midiData[3]=0;
        events.events[events.numEvents]=(VstEvent*)event;
        events.numEvents++;
    }
    if(note!=-1){
        //qDebug()&lt;&lt;&quot;noteOn &quot;&lt;&lt;note;
        VstMidiEvent *event=new VstMidiEvent();
        event-&gt;flags=kVstMidiEventIsRealtime;

        ::memset(event, 0, sizeof(VstMidiEvent));
        event-&gt;type = kVstMidiType;
        event-&gt;byteSize = sizeof(VstMidiEvent);
        if(instrument){
            event-&gt;midiData[0]=0x90;
            event-&gt;midiData[1]=midi-&gt;noteAtRow(note);
        }else{
            event-&gt;midiData[0]=0x90;
            event-&gt;midiData[1]=note;
        }
        event-&gt;midiData[2]=0x7F;
        event-&gt;midiData[3]=0;
        events.events[events.numEvents]=(VstEvent*)event;
        events.numEvents++;
    }
    playedNote=note;
    if(events.numEvents&gt;0){
        vst_dispatch(effProcessEvents,0,0,&amp;events,0.0f);
    }
    //vst_dispatch(effEditIdle, 0, 0, NULL, 0.0f);
}

int channels=2;
float **input=new float*[channels];
float **output=new float*[channels];
for(int c=0;c&lt;channels;c++){
    input[c]=new float[nSamples];
    output[c]=new float[nSamples];
    float *pi=input[c],*po=output[c];
    float *p=pInBuf+c;
    for(int i=0;i&lt;nSamples;i++){
        (*po)=(*pi)=*p;
        po++, pi++, p+=channels;
    }
}
if(effect){
   (*effect-&gt;processReplacing)(effect,input,output,nSamples);
}

for(int c=0;c&lt;channels;c++){
    float *po=output[c];
    float *p=pOutBuf+c;
    for(int i=0;i&lt;nSamples;i++){
        *p=*po;
        po++, p+=channels;
    }
}
for(int c=1;c&lt;channels;c++){
    float *po=output[c];
    float *p=pOutBuf+c;
    for(int i=0;i&lt;nSamples;i++){
        *p=0;
        po++, p+=channels;
    }
}
samplePos+=nSamples;
for(int c=0;c&lt;channels;c++){
    delete[] output[c];
    delete[] input[c];
}
delete[] output;
delete[] input;
for(int i=0;i&lt;events.numEvents;i++){
     delete events.events[i];
}
if(widget){
    vst_dispatch(effEditIdle, 0, 0, NULL, 0.0f);
}

}

void VstPlugin::add(FMOD::Channel *channel){
QMutexLocker locker(&mutex);
if(!dsp){
qDebug()<<"No dsp";
return;
}
FMOD_RESULT result;
result=channel->addDSP(dsp, 0);
ERRCHECK(result);
}

void VstPlugin::remove(){
QMutexLocker locker(&mutex);
if(dsp){
dsp->remove();
}
}

void VstPlugin::createDsp(){
QMutexLocker locker(&mutex);
if(dsp){
qDebug()<<"dsp already created";
return;
}
FMOD::System *system=this->system->fmod();
FMOD_RESULT result;
FMOD_DSP_DESCRIPTION dspdesc;
memset(&dspdesc, 0, sizeof(FMOD_DSP_DESCRIPTION));
strcpy(dspdesc.name, "DSP unit");
dspdesc.channels = 0;
dspdesc.userdata=this;
dspdesc.read = DSPGetResultCallback;
result=system->createDSP(&dspdesc, &dsp);
ERRCHECK(result);

dsp-&gt;setUserData(this);

}

FMOD_RESULT F_CALLBACK VstPlugin::DSPGetResultCallback(
FMOD_DSP_STATE *pDspState,
float *pInBuf,
float *pOutBuf,
unsigned int nSamples,
int nInChannels,
int nOutChannels){

// qDebug()<<"dsp "<<nSamples;
FMOD::DSP *dsp=(FMOD::DSP *)(pDspState->instance);
VstPlugin *plugin=0;
dsp->getUserData((void**)&plugin);
QMutexLocker locker(&plugin->mutex);
if(plugin && plugin->isValid()){
plugin->process(pInBuf,pOutBuf,nSamples);
}

return FMOD_OK;

}

void VstPlugin::store(){
//return;
if(effect){
QFile f("vst.par");
if(!f.open(QFile::WriteOnly)){
qDebug()<<"error in store";
return;
}
QTextStream o(&f);
AudioEffect *a=(AudioEffect *)effect->object;
if(a){
for(int i=0;i<effect->numParams;i++){
float value=a->getParameter(i);
char name[256];
a->getParameterName(i,name);
o<<value<<" "<<i<<" "<<name<<"\n";
//qDebug()<<value;
}
}
}
}

void VstPlugin::restore(){
if(!effect) return;
AudioEffect *a=(AudioEffect *)effect->object;
if(a){
QFile f("vst.par");
if(!f.open(QFile::ReadOnly)){
qDebug()<<"error in store";
return;
}
QTextStream o(&f);
int i=0;
while(o.atEnd()){
QString line=o.readLine();
float value=line.toFloat();
a->setParameter(i,value);
i++;
}
}
}

void VstPlugin::setMidi(MidiNotes *midi){
this->midi=midi;
}
[/code:8oevy98h]

  • You must to post comments
0
0

The problem is solved.
Incorrection in this code:
1. You can NOT setNumInputs&output for plugin from host NEVER!
2. Before processReplacing all buffers must be allocated accrogin with plugin numInputs and numOutput values.
3. It is good to dispatch effOpen, effMainsChanged, effStartProcess from host.
Corrected code:
[code:224mxp4s]
// in constructor
VstPlugin:: VstPlugin{
...
vst_dispatch(effOpen,0, 0, NULL, 0.0f);
int nSamples=1024; // FMOD DSP always 1024
vst_dispatch(effSetSampleRate, 0, 0, NULL, 48000);
vst_dispatch(effSetBlockSize, 0, nSamples, NULL, 0.0f);
vst_dispatch(effMainsChanged, 0, 1, NULL, 0.0f);
vst_dispatch(effStartProcess, 0, 0, NULL, 0.0f);
{// allocate process buffers
if(effect->numInputs>0)
input=new float[effect->numInputs];
if(effect->numOutputs>0)
output=new float
[effect->numOutputs];
for(int c=0;c<effect->numInputs;c++){
input[c]=new float[nSamples];
}
for(int c=0;c<effect->numOutputs;c++){
output[c]=new float[nSamples];
}
}

[/code:224mxp4s]

  • You must to post comments
Showing 2 results
Your Answer

Please first to submit.