From 1f2e491188da0752a88bb055d11bebe43e550f3f Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Tue, 8 Jan 2019 16:56:44 +1100 Subject: [PATCH] started windows vst support --- effects/internal/vsthostwin.cpp | 185 ++++++++++++++++++++++++++++++++ effects/internal/vsthostwin.h | 36 +++++++ olive.pro | 6 +- project/effect.cpp | 7 ++ project/effect.h | 2 +- project/effectrow.cpp | 10 +- project/effectrow.h | 2 + 7 files changed, 244 insertions(+), 4 deletions(-) create mode 100644 effects/internal/vsthostwin.cpp create mode 100644 effects/internal/vsthostwin.h diff --git a/effects/internal/vsthostwin.cpp b/effects/internal/vsthostwin.cpp new file mode 100644 index 000000000..afa320352 --- /dev/null +++ b/effects/internal/vsthostwin.cpp @@ -0,0 +1,185 @@ +#include "vsthostwin.h" + +// adapted from http://teragonaudio.com/article/How-to-make-your-own-VST-host.html + +#include + +#include + +#include "playback/audio.h" +#include "mainwindow.h" +#include "debug.h" + +#define BLOCK_SIZE 512 +#define CHANNEL_COUNT 2 + +// C callbacks +extern "C" { + // Main host callback + VstIntPtr VSTCALLBACK hostCallback(AEffect *effect, int opcode, int index, long long value, void *ptr, float opt) { + switch(opcode) { + case audioMasterVersion: + return 2400; + case audioMasterIdle: + effect->dispatcher(effect, effEditIdle, 0, 0, 0, 0); + break; + case audioMasterGetCurrentProcessLevel: + return 0; + // Handle other opcodes here... there will be lots of them + default: + dout << "[INFO] Plugin requested unhandled opcode" << opcode; + break; + } + } +} + +// Plugin's entry point +typedef AEffect *(*vstPluginFuncPtr)(audioMasterCallback host); +// Plugin's getParameter() method +typedef float (*getParameterFuncPtr)(AEffect *effect, VstInt32 index); +// Plugin's setParameter() method +typedef void (*setParameterFuncPtr)(AEffect *effect, VstInt32 index, float value); +// Plugin's processEvents() method +typedef VstInt32 (*processEventsFuncPtr)(VstEvents *events); +// Plugin's process() method +typedef void (*processFuncPtr)(AEffect *effect, float **inputs, float **outputs, VstInt32 sampleFrames); + +void VSTHostWin::loadPlugin() { + plugin = NULL; + + const char* vst_path = "C:\\Users\\Matt\\Downloads\\ToneGenerator_Win\\ToneGenerator_64b.dll"; + wchar_t win_char[200]; + mbstowcs(win_char, vst_path, 200); + + HMODULE modulePtr = LoadLibrary(win_char); + if(modulePtr == NULL) { + DWORD dll_err = GetLastError(); + dout << "[ERROR] Failed to load VST" << vst_path << "-" << dll_err; + if (dll_err == 193) dout << " Mixing 32-bit and 64-bit?"; + return; + } + + vstPluginFuncPtr mainEntryPoint = + (vstPluginFuncPtr)GetProcAddress(modulePtr, "VSTPluginMain"); + // Instantiate the plugin + plugin = mainEntryPoint(hostCallback); +} + +int VSTHostWin::configurePluginCallbacks() { + // Check plugin's magic number + // If incorrect, then the file either was not loaded properly, is not a + // real VST plugin, or is otherwise corrupt. + if(plugin->magic != kEffectMagic) { + printf("Plugin's magic number is bad\n"); + return -1; + } + + // Create dispatcher handle + dispatcher = (dispatcherFuncPtr)(plugin->dispatcher); + + // Set up plugin callback functions + plugin->getParameter = (getParameterFuncPtr)plugin->getParameter; + plugin->processReplacing = (processFuncPtr)plugin->processReplacing; + plugin->setParameter = (setParameterFuncPtr)plugin->setParameter; + + //return plugin; + return 0; +} + +void VSTHostWin::startPlugin() { + dispatcher(plugin, effOpen, 0, 0, NULL, 0.0f); + + // Set some default properties + dispatcher(plugin, effSetSampleRate, 0, 0, NULL, current_audio_freq()); + dispatcher(plugin, effSetBlockSize, 0, BLOCK_SIZE, NULL, 0.0f); + + resumePlugin(); +} + +void VSTHostWin::resumePlugin() { + dispatcher(plugin, effMainsChanged, 0, 1, NULL, 0.0f); +} + +void VSTHostWin::suspendPlugin() { + dispatcher(plugin, effMainsChanged, 0, 0, NULL, 0.0f); +} + +bool VSTHostWin::canPluginDo(char *canDoString) { + return (dispatcher(plugin, effCanDo, 0, 0, (void*)canDoString, 0.0f) > 0); +} + +void VSTHostWin::initializeIO() { + // inputs and outputs are assumed to be float** and are declared elsewhere, + // most likely the are fields owned by this class. numChannels and blocksize + // are also fields, both should be size_t (or unsigned int, if you prefer). + inputs = new float* [CHANNEL_COUNT]; + outputs = new float* [CHANNEL_COUNT]; + for(int channel = 0; channel < CHANNEL_COUNT; channel++) { + inputs[channel] = new float[BLOCK_SIZE]; + outputs[channel] = new float[BLOCK_SIZE]; + } +} + +void VSTHostWin::processAudio(long numFrames) { + // Always reset the output array before processing. + silenceChannel(outputs, CHANNEL_COUNT, numFrames); + + plugin->processReplacing(plugin, inputs, outputs, numFrames); +} + +void VSTHostWin::silenceChannel(float **channelData, int numChannels, long numFrames) { + for(int channel = 0; channel < numChannels; ++channel) { + for(long frame = 0; frame < numFrames; ++frame) { + channelData[channel][frame] = 0.0f; + } + } +} + +VSTHostWin::VSTHostWin(Clip* c, const EffectMeta *em) : Effect(c, em) { + EffectRow* interface_row = add_row("Open Interface"); + QPushButton* show_interface_btn = new QPushButton(); + interface_row->add_widget(show_interface_btn); + loadPlugin(); + if (plugin != NULL) { + initializeIO(); + configurePluginCallbacks(); + startPlugin(); + } +} + +void VSTHostWin::process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int) { + if (plugin != NULL) { + // convert to floats + int nb_frames = qMin(nb_bytes >> 2, BLOCK_SIZE); + int lim = qMin(nb_bytes, BLOCK_SIZE << 2); + for (int i=0;i> 2; + inputs[0][index] = float(left_sample) / float(INT16_MAX); + inputs[1][index] = float(right_sample) / float(INT16_MAX); + } + + // send to VST + processAudio(nb_frames); + + // convert back to int16 + for (int i=0;i> 8); + samples[index+2] = (quint8) right_sample; + samples[index+1] = (quint8) (left_sample >> 8); + samples[index] = (quint8) left_sample; + } + } +} + +void VSTHostWin::show_interface() { +// dispatcher(plugin, effEditOpen, 0, 1, NULL, 0.0f); +// dispatcher(plugin, effEditOpen, 0, 0, mainWindow->winId()); +} diff --git a/effects/internal/vsthostwin.h b/effects/internal/vsthostwin.h new file mode 100644 index 000000000..762d1c2a0 --- /dev/null +++ b/effects/internal/vsthostwin.h @@ -0,0 +1,36 @@ +#ifndef VSTHOSTWIN_H +#define VSTHOSTWIN_H + +#include "project/effect.h" + +#include + +// Plugin's dispatcher function +typedef VstIntPtr (*dispatcherFuncPtr)(AEffect *effect, VstInt32 opCode, VstInt32 index, VstInt32 value, void *ptr, float opt); + +struct AEffect; + +class VSTHostWin : public Effect { + Q_OBJECT +public: + VSTHostWin(Clip* c, const EffectMeta* em); + void process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int channel_count); +private slots: + void show_interface(); +private: + void loadPlugin(); + dispatcherFuncPtr dispatcher; + AEffect* plugin; + int configurePluginCallbacks(); + void startPlugin(); + void resumePlugin(); + void suspendPlugin(); + bool canPluginDo(char *canDoString); + void initializeIO(); + void processAudio(long numFrames); + void silenceChannel(float **channelData, int numChannels, long numFrames); + float** inputs; + float** outputs; +}; + +#endif // VSTHOSTWIN_H diff --git a/olive.pro b/olive.pro index 28e268fbe..b7f0d3108 100644 --- a/olive.pro +++ b/olive.pro @@ -117,7 +117,8 @@ SOURCES += \ ui/clickablelabel.cpp \ project/keyframe.cpp \ ui/rectangleselect.cpp \ - dialogs/actionsearch.cpp + dialogs/actionsearch.cpp \ + effects/internal/vsthostwin.cpp HEADERS += \ mainwindow.h \ @@ -204,7 +205,8 @@ HEADERS += \ ui/clickablelabel.h \ project/keyframe.h \ ui/rectangleselect.h \ - dialogs/actionsearch.h + dialogs/actionsearch.h \ + effects/internal/vsthostwin.h FORMS += diff --git a/project/effect.cpp b/project/effect.cpp index 3b5baef6a..d043433ac 100644 --- a/project/effect.cpp +++ b/project/effect.cpp @@ -28,6 +28,7 @@ #include "effects/internal/paneffect.h" #include "effects/internal/shakeeffect.h" #include "effects/internal/cornerpineffect.h" +#include "effects/internal/vsthostwin.h" #include #include @@ -60,6 +61,7 @@ Effect* create_effect(Clip* c, const EffectMeta* em) { case EFFECT_INTERNAL_TONE: return new ToneEffect(c, em); case EFFECT_INTERNAL_SHAKE: return new ShakeEffect(c, em); case EFFECT_INTERNAL_CORNERPIN: return new CornerPinEffect(c, em); + case EFFECT_INTERNAL_VST: return new VSTHostWin(c, em); } } else { dout << "[ERROR] Invalid effect data"; @@ -92,6 +94,10 @@ void load_internal_effects() { em.internal = EFFECT_INTERNAL_PAN; effects.append(em); + em.name = "VST Plugin 2.x"; + em.internal = EFFECT_INTERNAL_VST; + effects.append(em); + em.name = "Tone"; em.internal = EFFECT_INTERNAL_TONE; effects.append(em); @@ -466,6 +472,7 @@ void Effect::copy_field_keyframes(Effect* e) { EffectField* field = row->field(j); EffectField* copy_field = copy_row->field(j); copy_field->keyframes = field->keyframes; + copy_field->set_current_data(field->get_current_data()); } } } diff --git a/project/effect.h b/project/effect.h index 290fa6cc7..96c6a10e2 100644 --- a/project/effect.h +++ b/project/effect.h @@ -63,7 +63,7 @@ extern QMutex effects_loaded; #define EFFECT_INTERNAL_SHAKE 7 #define EFFECT_INTERNAL_TIMECODE 8 #define EFFECT_INTERNAL_MASK 9 - +#define EFFECT_INTERNAL_VST 10 #define EFFECT_INTERNAL_CORNERPIN 12 #define EFFECT_INTERNAL_COUNT 13 diff --git a/project/effectrow.cpp b/project/effectrow.cpp index 08a5608f5..cf2e452b4 100644 --- a/project/effectrow.cpp +++ b/project/effectrow.cpp @@ -29,6 +29,8 @@ EffectRow::EffectRow(Effect *parent, bool save, QGridLayout *uilayout, const QSt ui->addWidget(label, row, 0); + column_count = 1; + if (parent_effect->meta->type != EFFECT_TYPE_TRANSITION) { connect(label, SIGNAL(clicked()), this, SLOT(focus_row())); @@ -145,11 +147,17 @@ EffectField* EffectRow::add_field(int type, const QString& id, int colspan) { if (parent_effect->meta->type != EFFECT_TYPE_TRANSITION) connect(field, SIGNAL(clicked()), this, SLOT(focus_row())); fields.append(field); QWidget* element = field->get_ui_element(); - ui->addWidget(element, ui_row, fields.size(), 1, colspan); + ui->addWidget(element, ui_row, column_count, 1, colspan); + column_count++; connect(field, SIGNAL(changed()), parent_effect, SLOT(field_changed())); return field; } +void EffectRow::add_widget(QWidget* w) { + ui->addWidget(w, ui_row, column_count); + column_count++; +} + EffectRow::~EffectRow() { for (int i=0;i unsafe_old_data; QVector key_is_new; + int column_count; }; #endif // EFFECTROW_H