From db2815ce095d43a37f6b6a4a7a58c5503bf32b78 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Wed, 9 Jan 2019 00:04:20 +1100 Subject: [PATCH] completed winvst frontend --- effects/internal/vsthostwin.cpp | 104 ++++++++++++++++++++------------ effects/internal/vsthostwin.h | 8 ++- olive.pro | 6 +- project/effect.cpp | 25 ++++++-- project/effect.h | 2 +- project/effectfield.cpp | 37 ++++++++++-- project/effectfield.h | 12 ++-- project/effectrow.cpp | 4 +- project/effectrow.h | 2 +- ui/embeddedfilechooser.cpp | 61 +++++++++++++++++++ ui/embeddedfilechooser.h | 27 +++++++++ 11 files changed, 228 insertions(+), 60 deletions(-) create mode 100644 ui/embeddedfilechooser.cpp create mode 100644 ui/embeddedfilechooser.h diff --git a/effects/internal/vsthostwin.cpp b/effects/internal/vsthostwin.cpp index 593d63ab0..59ddcbf4e 100644 --- a/effects/internal/vsthostwin.cpp +++ b/effects/internal/vsthostwin.cpp @@ -6,6 +6,7 @@ #include #include +#include #include "playback/audio.h" #include "mainwindow.h" @@ -48,17 +49,22 @@ typedef VstInt32 (*processEventsFuncPtr)(VstEvents *events); typedef void (*processFuncPtr)(AEffect *effect, float **inputs, float **outputs, VstInt32 sampleFrames); void VSTHostWin::loadPlugin() { - plugin = NULL; + QString dll_fn = file_field->get_filename(0, true); + LPCWSTR dll_fn_w = reinterpret_cast(dll_fn.utf16()); - const char* vst_path = "C:\\Program Files\\VSTPlugins\\ReaPlugs\\reaeq-standalone.dll"; - wchar_t win_char[200]; - mbstowcs(win_char, vst_path, 200); - - HMODULE modulePtr = LoadLibrary(win_char); + modulePtr = LoadLibrary(dll_fn_w); 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?"; + dout << "[ERROR] Failed to load VST" << dll_fn_w << "-" << dll_err; + QString msg_err = "Failed to load VST plugin \"" + dll_fn + "\": " + QString::number(dll_err); + if (dll_err == 193) { +#ifdef _WIN64 + msg_err += "\n\nNOTE: You can't load 32-bit VST plugins into a 64-bit build of Olive. Please find a 64-bit version of this plugin or switch to a 32-bit build of Olive."; +#elif _WIN32 + msg_err += "\n\nNOTE: You can't load 64-bit VST plugins into a 32-bit build of Olive. Please find a 32-bit version of this plugin or switch to a 64-bit build of Olive."; +#endif + } + QMessageBox::critical(mainWindow, "Error loading VST plugin", msg_err); return; } @@ -68,13 +74,14 @@ void VSTHostWin::loadPlugin() { plugin = mainEntryPoint(hostCallback); } -int VSTHostWin::configurePluginCallbacks() { +bool 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; + dout << "[ERROR] Plugin's magic number is bad"; + QMessageBox::critical(mainWindow, "VST Error", "Plugin's magic number is invalid"); + return false; } // Create dispatcher handle @@ -85,8 +92,7 @@ int VSTHostWin::configurePluginCallbacks() { plugin->processReplacing = (processFuncPtr)plugin->processReplacing; plugin->setParameter = (setParameterFuncPtr)plugin->setParameter; - //return plugin; - return 0; + return true; } void VSTHostWin::startPlugin() { @@ -99,6 +105,12 @@ void VSTHostWin::startPlugin() { resumePlugin(); } +void VSTHostWin::stopPlugin() { + suspendPlugin(); + + dispatcher(plugin, effClose, 0, 0, NULL, 0); +} + void VSTHostWin::resumePlugin() { dispatcher(plugin, effMainsChanged, 0, 1, NULL, 0.0f); } @@ -125,42 +137,33 @@ void VSTHostWin::initializeIO() { void VSTHostWin::processAudio(long numFrames) { // Always reset the output array before processing. - silenceChannel(outputs, CHANNEL_COUNT, numFrames); + for (int i=0;iprocessReplacing(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("Interface"); + plugin = NULL; + + initializeIO(); + + file_field = add_row("Plugin", true, false)->add_field(EFFECT_FIELD_FILE, "filename"); + connect(file_field, SIGNAL(changed()), this, SLOT(change_plugin())); + + EffectRow* interface_row = add_row("Interface", false, false); show_interface_btn = new QPushButton("Show"); show_interface_btn->setCheckable(true); + show_interface_btn->setEnabled(false); connect(show_interface_btn, SIGNAL(toggled(bool)), this, SLOT(show_interface(bool))); interface_row->add_widget(show_interface_btn); - loadPlugin(); - if (plugin != NULL) { - initializeIO(); - configurePluginCallbacks(); - startPlugin(); - dialog = new QDialog(mainWindow); - dialog->setWindowTitle("VST Plugin"); - dialog->setAttribute(Qt::WA_NativeWindow, true); - dialog->setWindowFlags(dialog->windowFlags() | Qt::MSWindowsFixedSizeDialogHint); - connect(dialog, SIGNAL(finished(int)), this, SLOT(uncheck_show_button())); - dispatcher(plugin, effEditOpen, 0, 0, reinterpret_cast(dialog->winId()), 0); - ERect* eRect = NULL; - plugin->dispatcher(plugin, effEditGetRect, 0, 0, &eRect, 0); - dialog->setFixedWidth(eRect->right); - dialog->setFixedHeight(eRect->bottom); - } + dialog = new QDialog(mainWindow); + dialog->setWindowTitle("VST Plugin"); + dialog->setAttribute(Qt::WA_NativeWindow, true); + dialog->setWindowFlags(dialog->windowFlags() | Qt::MSWindowsFixedSizeDialogHint); + connect(dialog, SIGNAL(finished(int)), this, SLOT(uncheck_show_button())); } void VSTHostWin::process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int) { @@ -215,3 +218,26 @@ void VSTHostWin::show_interface(bool show) { void VSTHostWin::uncheck_show_button() { show_interface_btn->setChecked(false); } + +void VSTHostWin::change_plugin() { + if (plugin != NULL) { + stopPlugin(); + FreeLibrary(modulePtr); + plugin = NULL; + } + loadPlugin(); + if (plugin != NULL) { + if (configurePluginCallbacks()) { + startPlugin(); + dispatcher(plugin, effEditOpen, 0, 0, reinterpret_cast(dialog->winId()), 0); + ERect* eRect = NULL; + plugin->dispatcher(plugin, effEditGetRect, 0, 0, &eRect, 0); + dialog->setFixedWidth(eRect->right); + dialog->setFixedHeight(eRect->bottom); + } else { + FreeLibrary(modulePtr); + plugin = NULL; + } + } + show_interface_btn->setEnabled(plugin != NULL); +} diff --git a/effects/internal/vsthostwin.h b/effects/internal/vsthostwin.h index c5dc606a1..eb7ab8273 100644 --- a/effects/internal/vsthostwin.h +++ b/effects/internal/vsthostwin.h @@ -19,22 +19,26 @@ public: private slots: void show_interface(bool show); void uncheck_show_button(); + void change_plugin(); private: + EffectField* file_field; + void loadPlugin(); dispatcherFuncPtr dispatcher; AEffect* plugin; - int configurePluginCallbacks(); + bool configurePluginCallbacks(); void startPlugin(); + void stopPlugin(); 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; QDialog* dialog; QPushButton* show_interface_btn; + HMODULE modulePtr; }; #endif // VSTHOSTWIN_H diff --git a/olive.pro b/olive.pro index 03aea3757..89d27d198 100644 --- a/olive.pro +++ b/olive.pro @@ -118,7 +118,8 @@ SOURCES += \ project/keyframe.cpp \ ui/rectangleselect.cpp \ dialogs/actionsearch.cpp \ - effects/internal/vsthostwin.cpp + effects/internal/vsthostwin.cpp \ + ui/embeddedfilechooser.cpp HEADERS += \ mainwindow.h \ @@ -206,7 +207,8 @@ HEADERS += \ project/keyframe.h \ ui/rectangleselect.h \ dialogs/actionsearch.h \ - effects/internal/vsthostwin.h + effects/internal/vsthostwin.h \ + ui/embeddedfilechooser.h FORMS += diff --git a/project/effect.cpp b/project/effect.cpp index d043433ac..828997f09 100644 --- a/project/effect.cpp +++ b/project/effect.cpp @@ -313,6 +313,8 @@ Effect::Effect(Clip* c, const EffectMeta *em) : type = EFFECT_FIELD_FONT; } else if (comp == "STRING") { type = EFFECT_FIELD_STRING; + } else if (comp == "FILE") { + type = EFFECT_FIELD_FILE; } } else if (attr.name() == "id") { id = attr.value().toString(); @@ -405,6 +407,14 @@ Effect::Effect(Clip* c, const EffectMeta *em) : } } break; + case EFFECT_FIELD_FILE: + for (int i=0;iset_filename(attr.value().toString()); + } + } + break; } } } @@ -477,7 +487,7 @@ void Effect::copy_field_keyframes(Effect* e) { } } -EffectRow* Effect::add_row(const QString& name, bool savable) { +EffectRow* Effect::add_row(const QString& name, bool savable, bool keyframable) { EffectRow* row = new EffectRow(this, savable, ui_layout, name, rows.size()); rows.append(row); return row; @@ -586,10 +596,12 @@ QVariant load_data_from_string(int type, const QString& string) { switch (type) { case EFFECT_FIELD_DOUBLE: return string.toDouble(); case EFFECT_FIELD_COLOR: return QColor(string); - case EFFECT_FIELD_STRING: return string; case EFFECT_FIELD_BOOL: return (string == "1"); case EFFECT_FIELD_COMBO: return string.toInt(); - case EFFECT_FIELD_FONT: return string; + case EFFECT_FIELD_STRING: + case EFFECT_FIELD_FONT: + case EFFECT_FIELD_FILE: + return string; } return QVariant(); } @@ -598,10 +610,12 @@ QString save_data_to_string(int type, const QVariant& data) { switch (type) { case EFFECT_FIELD_DOUBLE: return QString::number(data.toDouble()); case EFFECT_FIELD_COLOR: return data.value().name(); - case EFFECT_FIELD_STRING: return data.toString(); case EFFECT_FIELD_BOOL: return QString::number(data.toBool()); case EFFECT_FIELD_COMBO: return QString::number(data.toInt()); - case EFFECT_FIELD_FONT: return data.toString(); + case EFFECT_FIELD_STRING: + case EFFECT_FIELD_FONT: + case EFFECT_FIELD_FILE: + return data.toString(); } return QString(); } @@ -872,6 +886,7 @@ void Effect::process_shader(double timecode, GLTextureCoords&) { glslProgram->setUniformValue(field->id.toUtf8().constData(), field->get_combo_index(timecode)); break; case EFFECT_FIELD_FONT: break; // can you even send a string to a uniform value? + case EFFECT_FIELD_FILE: break; // can you even send a string to a uniform value? } } } diff --git a/project/effect.h b/project/effect.h index 96c6a10e2..965e91368 100644 --- a/project/effect.h +++ b/project/effect.h @@ -119,7 +119,7 @@ public: QString name; CollapsibleWidget* container; - EffectRow* add_row(const QString &name, bool savable = true); + EffectRow* add_row(const QString &name, bool savable = true, bool keyframable = true); EffectRow* row(int i); int row_count(); diff --git a/project/effectfield.cpp b/project/effectfield.cpp index 6eda75505..f6626dc91 100644 --- a/project/effectfield.cpp +++ b/project/effectfield.cpp @@ -6,6 +6,7 @@ #include "ui/checkboxex.h" #include "ui/comboboxex.h" #include "ui/fontcombobox.h" +#include "ui/embeddedfilechooser.h" #include "effectrow.h" #include "effect.h" @@ -70,6 +71,13 @@ EffectField::EffectField(EffectRow *parent, int t, const QString &i) : connect(fcb, SIGNAL(activated(int)), this, SLOT(ui_element_change())); } break; + case EFFECT_FIELD_FILE: + { + EmbeddedFileChooser* efc = new EmbeddedFileChooser(); + ui_element = efc; + connect(efc, SIGNAL(changed()), this, SLOT(ui_element_change())); + } + break; } } @@ -81,6 +89,7 @@ QVariant EffectField::get_previous_data() { case EFFECT_FIELD_BOOL: return !static_cast(ui_element)->isChecked(); case EFFECT_FIELD_COMBO: return static_cast(ui_element)->getPreviousIndex(); case EFFECT_FIELD_FONT: return static_cast(ui_element)->getPreviousValue(); + case EFFECT_FIELD_FILE: return static_cast(ui_element)->getPreviousValue(); } return QVariant(); } @@ -93,6 +102,7 @@ QVariant EffectField::get_current_data() { case EFFECT_FIELD_BOOL: return static_cast(ui_element)->isChecked(); case EFFECT_FIELD_COMBO: return static_cast(ui_element)->currentIndex(); case EFFECT_FIELD_FONT: return static_cast(ui_element)->currentText(); + case EFFECT_FIELD_FILE: return static_cast(ui_element)->getFilename(); } return QVariant(); } @@ -113,6 +123,7 @@ void EffectField::set_current_data(const QVariant& data) { case EFFECT_FIELD_BOOL: return static_cast(ui_element)->setChecked(data.toBool()); case EFFECT_FIELD_COMBO: return static_cast(ui_element)->setCurrentIndexEx(data.toInt()); case EFFECT_FIELD_FONT: return static_cast(ui_element)->setCurrentTextEx(data.toString()); + case EFFECT_FIELD_FILE: return static_cast(ui_element)->setFilename(data.toString()); } } @@ -246,6 +257,12 @@ QVariant EffectField::validate_keyframe_data(double timecode, bool async) { } static_cast(ui_element)->setCurrentTextEx(before_data.toString()); break; + case EFFECT_FIELD_FILE: + if (async) { + return before_data; + } + static_cast(ui_element)->setFilename(before_data.toString()); + break; } } return QVariant(); @@ -313,12 +330,12 @@ int EffectField::get_combo_index(double timecode, bool async) { return static_cast(ui_element)->currentIndex(); } -const QVariant EffectField::get_combo_data(double timecode) { +QVariant EffectField::get_combo_data(double timecode) { validate_keyframe_data(timecode); return static_cast(ui_element)->currentData(); } -const QString EffectField::get_combo_string(double timecode) { +QString EffectField::get_combo_string(double timecode) { validate_keyframe_data(timecode); return static_cast(ui_element)->currentText(); } @@ -343,7 +360,7 @@ void EffectField::set_bool_value(bool b) { return static_cast(ui_element)->setChecked(b); } -const QString EffectField::get_string_value(double timecode, bool async) { +QString EffectField::get_string_value(double timecode, bool async) { if (async && hasKeyframes()) { return validate_keyframe_data(timecode, true).toString(); } @@ -355,7 +372,7 @@ void EffectField::set_string_value(const QString& s) { static_cast(ui_element)->setPlainTextEx(s); } -const QString EffectField::get_font_name(double timecode, bool async) { +QString EffectField::get_font_name(double timecode, bool async) { if (async && hasKeyframes()) { return validate_keyframe_data(timecode, true).toString(); } @@ -378,3 +395,15 @@ QColor EffectField::get_color_value(double timecode, bool async) { void EffectField::set_color_value(QColor color) { static_cast(ui_element)->set_color(color); } + +QString EffectField::get_filename(double timecode, bool async) { + if (async && hasKeyframes()) { + return validate_keyframe_data(timecode, true).toString(); + } + validate_keyframe_data(timecode); + return static_cast(ui_element)->getFilename(); +} + +void EffectField::set_filename(const QString &s) { + static_cast(ui_element)->setFilename(s); +} diff --git a/project/effectfield.h b/project/effectfield.h index c946c475b..0e836feaf 100644 --- a/project/effectfield.h +++ b/project/effectfield.h @@ -7,6 +7,7 @@ #define EFFECT_FIELD_BOOL 3 #define EFFECT_FIELD_COMBO 4 #define EFFECT_FIELD_FONT 5 +#define EFFECT_FIELD_FILE 6 #include #include @@ -39,25 +40,28 @@ public: void set_double_minimum_value(double v); void set_double_maximum_value(double v); - const QString get_string_value(double timecode, bool async = false); + QString get_string_value(double timecode, bool async = false); void set_string_value(const QString &s); void add_combo_item(const QString& name, const QVariant &data); int get_combo_index(double timecode, bool async = false); - const QVariant get_combo_data(double timecode); - const QString get_combo_string(double timecode); + QVariant get_combo_data(double timecode); + QString get_combo_string(double timecode); void set_combo_index(int index); void set_combo_string(const QString& s); bool get_bool_value(double timecode, bool async = false); void set_bool_value(bool b); - const QString get_font_name(double timecode, bool async = false); + QString get_font_name(double timecode, bool async = false); void set_font_name(const QString& s); QColor get_color_value(double timecode, bool async = false); void set_color_value(QColor color); + QString get_filename(double timecode, bool async = false); + void set_filename(const QString& s); + QWidget* get_ui_element(); void set_enabled(bool e); QVector keyframes; diff --git a/project/effectrow.cpp b/project/effectrow.cpp index cf2e452b4..7f574d379 100644 --- a/project/effectrow.cpp +++ b/project/effectrow.cpp @@ -16,7 +16,7 @@ #include "ui/keyframenavigator.h" #include "ui/clickablelabel.h" -EffectRow::EffectRow(Effect *parent, bool save, QGridLayout *uilayout, const QString &n, int row) : +EffectRow::EffectRow(Effect *parent, bool save, QGridLayout *uilayout, const QString &n, int row, bool keyframable) : parent_effect(parent), savable(save), keyframing(false), @@ -31,7 +31,7 @@ EffectRow::EffectRow(Effect *parent, bool save, QGridLayout *uilayout, const QSt column_count = 1; - if (parent_effect->meta->type != EFFECT_TYPE_TRANSITION) { + if (parent_effect->meta->type != EFFECT_TYPE_TRANSITION && keyframable) { connect(label, SIGNAL(clicked()), this, SLOT(focus_row())); keyframe_nav = new KeyframeNavigator(); diff --git a/project/effectrow.h b/project/effectrow.h index 08137d734..8ad91be23 100644 --- a/project/effectrow.h +++ b/project/effectrow.h @@ -17,7 +17,7 @@ class ClickableLabel; class EffectRow : public QObject { Q_OBJECT public: - EffectRow(Effect* parent, bool save, QGridLayout* uilayout, const QString& n, int row); + EffectRow(Effect* parent, bool save, QGridLayout* uilayout, const QString& n, int row, bool keyframable = true); ~EffectRow(); EffectField* add_field(int type, const QString &id, int colspan = 1); void add_widget(QWidget *w); diff --git a/ui/embeddedfilechooser.cpp b/ui/embeddedfilechooser.cpp new file mode 100644 index 000000000..adb1e8484 --- /dev/null +++ b/ui/embeddedfilechooser.cpp @@ -0,0 +1,61 @@ +#include "embeddedfilechooser.h" + +#include +#include +#include +#include +#include + +EmbeddedFileChooser::EmbeddedFileChooser(QWidget* parent) : QWidget(parent) { + QHBoxLayout* layout = new QHBoxLayout(); + layout->setMargin(0); + setLayout(layout); + file_label = new QLabel(); + update_label(); + layout->addWidget(file_label); + QPushButton* browse_button = new QPushButton("..."); + browse_button->setFixedWidth(25); + layout->addWidget(browse_button); + connect(browse_button, SIGNAL(clicked(bool)), this, SLOT(browse())); +} + +const QString &EmbeddedFileChooser::getFilename() { + return filename; +} + +const QString &EmbeddedFileChooser::getPreviousValue() { + return old_filename; +} + +void EmbeddedFileChooser::setFilename(const QString &s) { + old_filename = filename; + filename = s; + update_label(); + emit changed(); +} + +void EmbeddedFileChooser::update_label() { + QString l = "File: "; + if (filename.isEmpty()) { + l += "(none)"; + } else { + bool file_exists = QFileInfo::exists(filename); + if (!file_exists) l += ""; + QString short_fn = filename.mid(filename.lastIndexOf('/')+1); + if (short_fn.size() > 20) { + l += "..." + short_fn.right(20); + } else { + l += short_fn; + } + if (!file_exists) l += ""; + } + l += ""; + file_label->setText(l); +} + +void EmbeddedFileChooser::browse() { + QString fn = QFileDialog::getOpenFileName(this); + if (!fn.isEmpty()) { + setFilename(fn); + } +} diff --git a/ui/embeddedfilechooser.h b/ui/embeddedfilechooser.h new file mode 100644 index 000000000..9a01fa31d --- /dev/null +++ b/ui/embeddedfilechooser.h @@ -0,0 +1,27 @@ +#ifndef EMBEDDEDFILECHOOSER_H +#define EMBEDDEDFILECHOOSER_H + +#include + +class QLabel; + +class EmbeddedFileChooser : public QWidget { + Q_OBJECT +public: + EmbeddedFileChooser(QWidget* parent = 0); + + const QString& getFilename(); + const QString& getPreviousValue(); + void setFilename(const QString& s); +signals: + void changed(); +private: + QLabel* file_label; + QString filename; + QString old_filename; + void update_label(); +private slots: + void browse(); +}; + +#endif // EMBEDDEDFILECHOOSER_H