From a24eaa0cd90a721dcb4837dad4a48efdfb9d3d39 Mon Sep 17 00:00:00 2001 From: jthedering <51533355+jthedering@users.noreply.github.com> Date: Sat, 17 Jun 2017 13:04:35 +0000 Subject: [PATCH] Added: VST Plugin support. Add and configure plugins via Configuration Editor. The plugin's architecture has to match the OS architecture, so most people will need 64-bit plugins. The subdirectory plugins is offered to put plugins into, but they can be opened from any directory with sufficient permissions. Fixed: When trying to add a high-pass filter in the Configuration Editor, it was instead configured to be a low-pass filter. --- Benchmark/Benchmark.cpp | 139 ++--- Benchmark/Benchmark.vcxproj | 21 +- Benchmark/Benchmark.vcxproj.filters | 33 ++ Configurator/Configurator.vcxproj | 1 + Configurator/Configurator.vcxproj.filters | 1 + Editor/AnalysisThread.cpp | 41 +- Editor/AnalysisThread.h | 2 +- Editor/Editor.pro | 26 +- Editor/FilterTable.cpp | 9 + Editor/FilterTable.h | 1 + Editor/MainWindow.cpp | 14 +- Editor/MainWindow.h | 2 +- Editor/guis/BiQuadFilterGUIFactory.cpp | 2 +- Editor/guis/ConvolutionFilterGUI.ui | 13 +- Editor/guis/CopyFilterGUIConnectionItem.cpp | 11 +- Editor/guis/CopyFilterGUIConnectionItem.h | 1 + Editor/guis/CopyFilterGUIScene.cpp | 25 +- Editor/guis/CopyFilterGUIScene.h | 1 + Editor/guis/IncludeFilterGUI.ui | 9 +- Editor/guis/VSTPluginFilterGUI.cpp | 480 ++++++++++++++++++ Editor/guis/VSTPluginFilterGUI.h | 66 +++ Editor/guis/VSTPluginFilterGUI.ui | 203 ++++++++ Editor/guis/VSTPluginFilterGUIDialog.cpp | 75 +++ Editor/guis/VSTPluginFilterGUIDialog.h | 49 ++ Editor/guis/VSTPluginFilterGUIDialog.ui | 87 ++++ Editor/guis/VSTPluginFilterGUIFactory.cpp | 62 +++ Editor/guis/VSTPluginFilterGUIFactory.h | 30 ++ Editor/translations/Editor_de.qm | Bin 23276 -> 26232 bytes Editor/translations/Editor_de.ts | 250 ++++++++- Editor/widgets/EscapableLineEdit.cpp | 6 +- Editor/widgets/EscapableLineEdit.h | 1 + Editor/widgets/ResizingLineEdit.cpp | 11 +- Editor/widgets/ResizingLineEdit.h | 9 +- EqualizerAPO.licenseheader | 19 + EqualizerAPO.vcxproj | 20 +- EqualizerAPO.vcxproj.filters | 34 ++ FilterEngine.cpp | 4 +- IFilter.h | 2 +- Setup/Setup.nsi | 1 + VoicemeeterClient/VoicemeeterClient.cpp | 19 + VoicemeeterClient/VoicemeeterClient.h | 19 + VoicemeeterClient/VoicemeeterClient.vcxproj | 21 +- .../VoicemeeterClient.vcxproj.filters | 33 ++ filters/VSTPluginFilter.cpp | 234 +++++++++ filters/VSTPluginFilter.h | 59 +++ filters/VSTPluginFilterFactory.cpp | 116 +++++ filters/VSTPluginFilterFactory.h | 31 ++ helpers/AbstractLibrary.cpp | 113 +++++ helpers/AbstractLibrary.h | 45 ++ helpers/GainIterator.cpp | 6 +- helpers/LogHelper.cpp | 2 + helpers/StringHelper.cpp | 34 ++ helpers/StringHelper.h | 1 + helpers/VSTPluginInstance.cpp | 397 +++++++++++++++ helpers/VSTPluginInstance.h | 78 +++ helpers/VSTPluginLibrary.cpp | 76 +++ helpers/VSTPluginLibrary.h | 49 ++ helpers/aeffectx.h | 391 ++++++++++++++ 58 files changed, 3334 insertions(+), 151 deletions(-) create mode 100644 Editor/guis/VSTPluginFilterGUI.cpp create mode 100644 Editor/guis/VSTPluginFilterGUI.h create mode 100644 Editor/guis/VSTPluginFilterGUI.ui create mode 100644 Editor/guis/VSTPluginFilterGUIDialog.cpp create mode 100644 Editor/guis/VSTPluginFilterGUIDialog.h create mode 100644 Editor/guis/VSTPluginFilterGUIDialog.ui create mode 100644 Editor/guis/VSTPluginFilterGUIFactory.cpp create mode 100644 Editor/guis/VSTPluginFilterGUIFactory.h create mode 100644 EqualizerAPO.licenseheader create mode 100644 filters/VSTPluginFilter.cpp create mode 100644 filters/VSTPluginFilter.h create mode 100644 filters/VSTPluginFilterFactory.cpp create mode 100644 filters/VSTPluginFilterFactory.h create mode 100644 helpers/AbstractLibrary.cpp create mode 100644 helpers/AbstractLibrary.h create mode 100644 helpers/VSTPluginInstance.cpp create mode 100644 helpers/VSTPluginInstance.h create mode 100644 helpers/VSTPluginLibrary.cpp create mode 100644 helpers/VSTPluginLibrary.h create mode 100644 helpers/aeffectx.h diff --git a/Benchmark/Benchmark.cpp b/Benchmark/Benchmark.cpp index 4601c7f..eaf6db1 100644 --- a/Benchmark/Benchmark.cpp +++ b/Benchmark/Benchmark.cpp @@ -162,78 +162,79 @@ int main(int argc, char** argv) PrecisionTimer timer; timer.start(); - - FilterEngine engine; - wstring deviceName = StringHelper::toWString(devicenameArg.getValue(), CP_ACP); - wstring connectionName = StringHelper::toWString(connectionnameArg.getValue(), CP_ACP); - wstring deviceGuid = StringHelper::toWString(guidArg.getValue(), CP_ACP); - engine.setDeviceInfo(false, true, deviceName, connectionName, deviceGuid, deviceName + L" " + connectionName + L" " + deviceGuid); - engine.initialize((float)sampleRate, channelCount, channelCount, channelCount, channelMask, batchsize); - - double initTime = timer.stop(); - if (!verbose) - printf("\nLoading configuration took %g ms\n", initTime * 1000.0); - - printf("\nProcessing %d frames from %d channel(s)\n", frameCount, channelCount); - - timer.start(); - - for (unsigned i = 0; i < frameCount; i += batchsize) { - engine.process(buf2 + i * channelCount, buf + i * channelCount, min(batchsize, frameCount - i)); + FilterEngine engine; + wstring deviceName = StringHelper::toWString(devicenameArg.getValue(), CP_ACP); + wstring connectionName = StringHelper::toWString(connectionnameArg.getValue(), CP_ACP); + wstring deviceGuid = StringHelper::toWString(guidArg.getValue(), CP_ACP); + engine.setDeviceInfo(false, true, deviceName, connectionName, deviceGuid, deviceName + L" " + connectionName + L" " + deviceGuid); + engine.initialize((float)sampleRate, channelCount, channelCount, channelCount, channelMask, batchsize); + + double initTime = timer.stop(); + if(!verbose) + printf("\nLoading configuration took %g ms\n", initTime * 1000.0); + + printf("\nProcessing %d frames from %d channel(s)\n", frameCount, channelCount); + + timer.start(); + + for(unsigned i = 0; i < frameCount; i += batchsize) + { + engine.process(buf2 + i * channelCount, buf + i * channelCount, min(batchsize, frameCount - i)); + } + + double time = timer.stop(); + + printf("%d samples processed in %f seconds\n", frameCount * channelCount, time); + printf("This is equivalent to %.2f%% CPU load (one core) when processing in real time\n", 100.0f * time / length); + + unsigned clipCount = 0; + float max = 0; + for(unsigned i = 0; i < frameCount * channelCount; i++) + { + float f = fabs(buf2[i]); + if(f > max) + max = f; + if(f > 1.0f) + clipCount++; + } + + printf("Max output level: %f (%f dB)", max, log10(max) * 20.0f); + if(clipCount > 0) + printf(" (%d samples clipped!)", clipCount); + printf("\n"); + + string output = outputArg.getValue(); + if(output == "") + { + char temp[255]; + GetTempPathA(sizeof(temp) / sizeof(wchar_t), temp); + + output = temp; + output += "testout.wav"; + } + + printf("\nWriting output to %s\n", output.c_str()); + + SF_INFO info = {frameCount, (int)sampleRate, (int)channelCount, SF_FORMAT_WAV | SF_FORMAT_PCM_16, 0}; + SNDFILE* outFile = sf_open(output.c_str(), SFM_WRITE, &info); + if(outFile == NULL) + { + fprintf(stderr, "%s", sf_strerror(outFile)); + return 1; + } + + sf_count_t numWritten = 0; + while(numWritten < frameCount) + numWritten += sf_writef_float(outFile, buf2 + numWritten * channelCount, frameCount - numWritten); + + sf_close(outFile); + outFile = NULL; + + delete[]buf; + delete[]buf2; } - double time = timer.stop(); - - printf("%d samples processed in %f seconds\n", frameCount * channelCount, time); - printf("This is equivalent to %.2f%% CPU load (one core) when processing in real time\n", 100.0f * time / length); - - unsigned clipCount = 0; - float max = 0; - for (unsigned i = 0; i < frameCount * channelCount; i++) - { - float f = fabs(buf2[i]); - if (f > max) - max = f; - if (f > 1.0f) - clipCount++; - } - - printf("Max output level: %f (%f dB)", max, log10(max) * 20.0f); - if (clipCount > 0) - printf(" (%d samples clipped!)", clipCount); - printf("\n"); - - string output = outputArg.getValue(); - if (output == "") - { - char temp[255]; - GetTempPathA(sizeof(temp) / sizeof(wchar_t), temp); - - output = temp; - output += "testout.wav"; - } - - printf("\nWriting output to %s\n", output.c_str()); - - SF_INFO info = {frameCount, (int)sampleRate, (int)channelCount, SF_FORMAT_WAV | SF_FORMAT_PCM_16, 0}; - SNDFILE* outFile = sf_open(output.c_str(), SFM_WRITE, &info); - if (outFile == NULL) - { - fprintf(stderr, "%s", sf_strerror(outFile)); - return 1; - } - - sf_count_t numWritten = 0; - while (numWritten < frameCount) - numWritten += sf_writef_float(outFile, buf2 + numWritten * channelCount, frameCount - numWritten); - - sf_close(outFile); - outFile = NULL; - - delete[]buf; - delete[]buf2; - if (!noPauseArg.getValue()) system("pause"); diff --git a/Benchmark/Benchmark.vcxproj b/Benchmark/Benchmark.vcxproj index d02bc6f..c943573 100644 --- a/Benchmark/Benchmark.vcxproj +++ b/Benchmark/Benchmark.vcxproj @@ -97,7 +97,7 @@ Console true - muparserxd.lib;version.lib;Shlwapi.lib;authz.lib;libsndfile-1.lib;libfftw3f-3.lib;%(AdditionalDependencies) + muparserxd.lib;version.lib;Shlwapi.lib;authz.lib;crypt32.lib;dbghelp.lib;libsndfile-1.lib;libfftw3f-3.lib;%(AdditionalDependencies) @@ -110,7 +110,7 @@ Console true - muparserxd.lib;version.lib;Shlwapi.lib;authz.lib;libsndfile-1.lib;libfftw3f-3.lib;%(AdditionalDependencies) + muparserxd.lib;version.lib;Shlwapi.lib;authz.lib;crypt32.lib;dbghelp.lib;libsndfile-1.lib;libfftw3f-3.lib;%(AdditionalDependencies) @@ -128,7 +128,7 @@ true true true - muparserx.lib;version.lib;Shlwapi.lib;authz.lib;libsndfile-1.lib;libfftw3f-3.lib;%(AdditionalDependencies) + muparserx.lib;version.lib;Shlwapi.lib;authz.lib;crypt32.lib;dbghelp.lib;libsndfile-1.lib;libfftw3f-3.lib;%(AdditionalDependencies) @@ -146,7 +146,7 @@ true true true - muparserx.lib;version.lib;Shlwapi.lib;authz.lib;libsndfile-1.lib;libfftw3f-3.lib;%(AdditionalDependencies) + muparserx.lib;version.lib;Shlwapi.lib;authz.lib;crypt32.lib;dbghelp.lib;libsndfile-1.lib;libfftw3f-3.lib;%(AdditionalDependencies) @@ -174,12 +174,17 @@ + + + + + @@ -218,6 +223,9 @@ + + + @@ -227,6 +235,8 @@ + + @@ -235,6 +245,9 @@ + + + diff --git a/Benchmark/Benchmark.vcxproj.filters b/Benchmark/Benchmark.vcxproj.filters index 6765e0c..032620c 100644 --- a/Benchmark/Benchmark.vcxproj.filters +++ b/Benchmark/Benchmark.vcxproj.filters @@ -102,6 +102,21 @@ filters + + helpers + + + filters + + + filters + + + helpers + + + helpers + @@ -214,6 +229,21 @@ helpers + + helpers + + + filters + + + filters + + + helpers + + + helpers + @@ -229,4 +259,7 @@ {35b3b6f1-e4df-4214-bd4c-fa058942e547} + + + \ No newline at end of file diff --git a/Configurator/Configurator.vcxproj b/Configurator/Configurator.vcxproj index 9196a3d..4e93750 100644 --- a/Configurator/Configurator.vcxproj +++ b/Configurator/Configurator.vcxproj @@ -53,6 +53,7 @@ + diff --git a/Configurator/Configurator.vcxproj.filters b/Configurator/Configurator.vcxproj.filters index 75d00b7..984e266 100644 --- a/Configurator/Configurator.vcxproj.filters +++ b/Configurator/Configurator.vcxproj.filters @@ -54,6 +54,7 @@ + diff --git a/Editor/AnalysisThread.cpp b/Editor/AnalysisThread.cpp index ecde39d..bf1f13d 100644 --- a/Editor/AnalysisThread.cpp +++ b/Editor/AnalysisThread.cpp @@ -109,7 +109,7 @@ double AnalysisThread::getProcessingTime() const return processingTime; } -int AnalysisThread::getProcessedFrames() const +unsigned AnalysisThread::getProcessedFrames() const { return processedFrames; } @@ -153,6 +153,7 @@ void AnalysisThread::run() unsigned sampleRate = device->getSampleRate(); qint64 startTime = timer.nsecsElapsed(); + FilterEngine engine; engine.setDeviceInfo(device->isInput(), true, device->getDeviceName(), device->getConnectionName(), device->getDeviceGuid(), device->getDeviceString()); engine.initialize(sampleRate, channelCount, channelCount, channelCount, channelMask, frameCount, configPath.toStdWString()); @@ -193,8 +194,9 @@ void AnalysisThread::run() int latency = 0; int startFrame = -1; double processingTime = 0.0; - int processedFrames = 0; - while (true) + unsigned processedFrames = 0; + // stop searching for startFrame after 10 seconds of audio data + while (processedFrames < 10 * sampleRate) { qint64 startTime = timer.nsecsElapsed(); engine.process(buf2, buf, frameCount); @@ -240,20 +242,31 @@ void AnalysisThread::run() if (startFrame == -1) latency += frameCount; } - latency += startFrame; - fftwf_execute(planForward); - - double peakGain = -DBL_MAX; - - for (int i = 0; i < frameCount / 2; i++) + double peakGain; + if (startFrame != -1) { - float sqrGain = freqData[i][0] * freqData[i][0] + freqData[i][1] * freqData[i][1]; - if (sqrGain > peakGain) - peakGain = sqrGain; + latency += startFrame; + + fftwf_execute(planForward); + + peakGain = -DBL_MAX; + + for (int i = 0; i < frameCount / 2; i++) + { + float sqrGain = freqData[i][0] * freqData[i][0] + freqData[i][1] * freqData[i][1]; + if (sqrGain > peakGain) + peakGain = sqrGain; + } + peakGain = sqrt(peakGain); + peakGain = log10(peakGain) * 20.0; + } + else + { + latency = 0; + peakGain = -numeric_limits::infinity(); + memset(freqData, 0, frameCount * sizeof(fftwf_complex)); } - peakGain = sqrt(peakGain); - peakGain = log10(peakGain) * 20.0; mutex.lock(); if (this->freqDataLength != frameCount) diff --git a/Editor/AnalysisThread.h b/Editor/AnalysisThread.h index 6acd88a..32c0720 100644 --- a/Editor/AnalysisThread.h +++ b/Editor/AnalysisThread.h @@ -43,7 +43,7 @@ public: int getLatency() const; double getInitializationTime() const; double getProcessingTime() const; - int getProcessedFrames() const; + unsigned getProcessedFrames() const; signals: void analysisFinished(); diff --git a/Editor/Editor.pro b/Editor/Editor.pro index 7323025..8f58c3f 100644 --- a/Editor/Editor.pro +++ b/Editor/Editor.pro @@ -116,7 +116,15 @@ SOURCES += main.cpp\ helpers/DPIHelper.cpp \ CustomStyle.cpp \ ../AbstractAPOInfo.cpp \ - ../VoicemeeterAPOInfo.cpp + ../VoicemeeterAPOInfo.cpp \ + ../helpers/AbstractLibrary.cpp \ + ../helpers/VSTPluginLibrary.cpp \ + guis/VSTPluginFilterGUI.cpp \ + guis/VSTPluginFilterGUIFactory.cpp \ + guis/VSTPluginFilterGUIDialog.cpp \ + ../filters/VSTPluginFilter.cpp \ + ../filters/VSTPluginFilterFactory.cpp \ + ../helpers/VSTPluginInstance.cpp HEADERS += \ ../helpers/LogHelper.h \ @@ -221,7 +229,15 @@ HEADERS += \ helpers/DPIHelper.h \ CustomStyle.h \ ../AbstractAPOInfo.h \ - ../VoicemeeterAPOInfo.h + ../VoicemeeterAPOInfo.h \ + ../helpers/AbstractLibrary.h \ + ../helpers/VSTPluginLibrary.h \ + guis/VSTPluginFilterGUI.h \ + guis/VSTPluginFilterGUIFactory.h \ + guis/VSTPluginFilterGUIDialog.h \ + ../filters/VSTPluginFilter.h \ + ../filters/VSTPluginFilterFactory.h \ + ../helpers/VSTPluginInstance.h FORMS += \ guis/PreampFilterGUI.ui \ @@ -239,10 +255,12 @@ FORMS += \ guis/GraphicEQFilterGUI.ui \ guis/ConvolutionFilterGUI.ui \ MainWindow.ui \ - guis/StageFilterGUI.ui + guis/StageFilterGUI.ui \ + guis/VSTPluginFilterGUI.ui \ + guis/VSTPluginFilterGUIDialog.ui INCLUDEPATH += $$PWD/.. "C:/Program Files/libsndfile/include" "C:/Program Files/fftw3" "C:/Program Files/muparserx_v3_0_1/parser" -LIBS += user32.lib advapi32.lib version.lib ole32.lib Shlwapi.lib authz.lib libsndfile-1.lib libfftw3f-3.lib +LIBS += user32.lib advapi32.lib version.lib ole32.lib Shlwapi.lib authz.lib crypt32.lib dbghelp.lib libsndfile-1.lib libfftw3f-3.lib build_pass:CONFIG(debug, debug|release) { LIBS += muparserxd.lib diff --git a/Editor/FilterTable.cpp b/Editor/FilterTable.cpp index ca62bbd..7e1065f 100644 --- a/Editor/FilterTable.cpp +++ b/Editor/FilterTable.cpp @@ -48,6 +48,7 @@ #include "guis/IncludeFilterGUIFactory.h" #include "guis/GraphicEQFilterGUIFactory.h" #include "guis/ConvolutionFilterGUIFactory.h" +#include "guis/VSTPluginFilterGUIFactory.h" #include "Editor/helpers/DPIHelper.h" #include "helpers/StringHelper.h" #include "helpers/LogHelper.h" @@ -79,10 +80,18 @@ FilterTable::FilterTable(MainWindow* mainWindow, QWidget* parent) factories.append(new CopyFilterGUIFactory); factories.append(new GraphicEQFilterGUIFactory); factories.append(new ConvolutionFilterGUIFactory); + factories.append(new VSTPluginFilterGUIFactory); QApplication::instance()->installEventFilter(this); } +FilterTable::~FilterTable() +{ + for (IFilterGUIFactory* factory : factories) + delete factory; + factories.clear(); +} + void FilterTable::initialize(QScrollArea* scrollArea, const QList >& outputDevices, const QList >& inputDevices) { this->scrollArea = scrollArea; diff --git a/Editor/FilterTable.h b/Editor/FilterTable.h index 8352627..d214ce6 100644 --- a/Editor/FilterTable.h +++ b/Editor/FilterTable.h @@ -56,6 +56,7 @@ public: }; explicit FilterTable(MainWindow* mainWindow, QWidget* parent = 0); + ~FilterTable(); void initialize(QScrollArea* scrollArea, const QList >& outputDevices, const QList >& inputDevices); void updateDeviceAndChannelMask(std::shared_ptr selectedDevice, int selectedChannelMask); void updateGuis(); diff --git a/Editor/MainWindow.cpp b/Editor/MainWindow.cpp index 2d3ee32..644ea38 100644 --- a/Editor/MainWindow.cpp +++ b/Editor/MainWindow.cpp @@ -427,6 +427,10 @@ void MainWindow::deviceSelected(int index) else channelConfigurationComboBox->addItem(tr("From device") + " (" + apoInfo->getChannelCount() + " channels)", 0); } + else + { + channelConfigurationComboBox->addItem(tr("From device") + " (?)", 0); + } for (const GUIChannelHelper::ChannelConfigurationInfo& info : infos) channelConfigurationComboBox->addItem(info.name, info.channelMask); @@ -469,6 +473,8 @@ void MainWindow::channelConfigurationSelected(int index) ui->analysisChannelComboBox->addItem(QString::fromStdWString(channelName)); } } + + startAnalysis(); } void MainWindow::linesChanged() @@ -723,6 +729,9 @@ void MainWindow::on_mainToolBar_visibilityChanged(bool visible) void MainWindow::on_analysisDockWidget_visibilityChanged(bool visible) { ui->actionAnalysisPanel->setChecked(visible); + + if (visible) + startAnalysis(); } void MainWindow::on_actionToolbar_triggered(bool checked) @@ -836,7 +845,7 @@ void MainWindow::getDeviceAndChannelMask(shared_ptr* selectedDe *selectedDevice = defaultOutputDevice; *channelMask = channelConfigurationComboBox->currentData().toInt(); - if (*channelMask == 0 && selectedDevice != NULL) + if (*channelMask == 0 && selectedDevice->get() != NULL) { *channelMask = (*selectedDevice)->getChannelMask(); @@ -894,6 +903,9 @@ bool MainWindow::askForClose(int tabIndex) void MainWindow::startAnalysis() { + if (!ui->analysisDockWidget->isVisible()) + return; + shared_ptr selectedDevice; int channelMask; diff --git a/Editor/MainWindow.h b/Editor/MainWindow.h index 2e694bb..f93b9fc 100644 --- a/Editor/MainWindow.h +++ b/Editor/MainWindow.h @@ -108,7 +108,7 @@ private: QList > inputDevices; std::shared_ptr defaultOutputDevice; AnalysisPlotScene* analysisPlotScene; - AnalysisThread* analysisThread; + AnalysisThread* analysisThread = NULL; bool restart = false; bool noSavePreferences = false; bool noSaveFilePreferences = false; diff --git a/Editor/guis/BiQuadFilterGUIFactory.cpp b/Editor/guis/BiQuadFilterGUIFactory.cpp index 7000673..b874291 100644 --- a/Editor/guis/BiQuadFilterGUIFactory.cpp +++ b/Editor/guis/BiQuadFilterGUIFactory.cpp @@ -32,7 +32,7 @@ QList BiQuadFilterGUIFactory::createFilterTemplates() QList list; list.append(FilterTemplate(tr("Peaking filter"), "Filter: ON PK Fc 100 Hz Gain 0 dB Q 10", path)); list.append(FilterTemplate(tr("Low-pass filter"), "Filter: ON LP Fc 100 Hz", path)); - list.append(FilterTemplate(tr("High-pass filter"), "Filter: ON LP Fc 100 Hz", path)); + list.append(FilterTemplate(tr("High-pass filter"), "Filter: ON HP Fc 100 Hz", path)); list.append(FilterTemplate(tr("Band-pass filter"), "Filter: ON BP Fc 100 Hz Q 10", path)); list.append(FilterTemplate(tr("Low-shelf filter"), "Filter: ON LS Fc 100 Hz Gain 0 dB", path)); list.append(FilterTemplate(tr("High-shelf filter"), "Filter: ON HS Fc 100 Hz Gain 0 dB", path)); diff --git a/Editor/guis/ConvolutionFilterGUI.ui b/Editor/guis/ConvolutionFilterGUI.ui index 35ec7bd..0b83cf1 100644 --- a/Editor/guis/ConvolutionFilterGUI.ui +++ b/Editor/guis/ConvolutionFilterGUI.ui @@ -6,8 +6,8 @@ 0 0 - 652 - 65 + 665 + 79 @@ -58,7 +58,14 @@ - + + + + 0 + 0 + + + diff --git a/Editor/guis/CopyFilterGUIConnectionItem.cpp b/Editor/guis/CopyFilterGUIConnectionItem.cpp index 9f3b861..3a1649b 100644 --- a/Editor/guis/CopyFilterGUIConnectionItem.cpp +++ b/Editor/guis/CopyFilterGUIConnectionItem.cpp @@ -175,8 +175,9 @@ void CopyFilterGUIConnectionItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent if (isDecibel) text += " dB"; - ResizingLineEdit* lineEdit = new ResizingLineEdit(""); + ResizingLineEdit* lineEdit = new ResizingLineEdit(text, true); connect(lineEdit, SIGNAL(editingFinished()), this, SLOT(lineEditEditingFinished())); + connect(lineEdit, SIGNAL(editingCanceled()), this, SLOT(lineEditEditingCanceled())); QGraphicsProxyWidget* proxyItem = scene()->addWidget(lineEdit); QLineF l = line(); QPointF center = (l.p1() + l.p2()) / 2; @@ -233,6 +234,14 @@ void CopyFilterGUIConnectionItem::lineEditEditingFinished() emit((CopyFilterGUIScene*)scene())->updateModel(); } +void CopyFilterGUIConnectionItem::lineEditEditingCanceled() +{ + QLineEdit* lineEdit = qobject_cast(QObject::sender()); + + lineEdit->setVisible(false); + lineEdit->deleteLater(); +} + CopyFilterGUIChannelItem* CopyFilterGUIConnectionItem::getSource() const { return source; diff --git a/Editor/guis/CopyFilterGUIConnectionItem.h b/Editor/guis/CopyFilterGUIConnectionItem.h index 9e5036d..2aa28b8 100644 --- a/Editor/guis/CopyFilterGUIConnectionItem.h +++ b/Editor/guis/CopyFilterGUIConnectionItem.h @@ -55,6 +55,7 @@ protected: private slots: void lineEditEditingFinished(); + void lineEditEditingCanceled(); private: CopyFilterGUIChannelItem* source = NULL; diff --git a/Editor/guis/CopyFilterGUIScene.cpp b/Editor/guis/CopyFilterGUIScene.cpp index f50b193..d5cf1f2 100644 --- a/Editor/guis/CopyFilterGUIScene.cpp +++ b/Editor/guis/CopyFilterGUIScene.cpp @@ -162,8 +162,9 @@ std::vector CopyFilterGUIScene::buildAssignments() void CopyFilterGUIScene::addOutputChannel() { - ResizingLineEdit* lineEdit = new ResizingLineEdit(""); + ResizingLineEdit* lineEdit = new ResizingLineEdit("", true); connect(lineEdit, SIGNAL(editingFinished()), this, SLOT(lineEditEditingFinished())); + connect(lineEdit, SIGNAL(editingCanceled()), this, SLOT(lineEditEditingCanceled())); QGraphicsProxyWidget* proxyItem = addWidget(lineEdit); QPointF point = getNextChannelPoint(lastOutputItem, true); QRectF rect; @@ -183,13 +184,25 @@ void CopyFilterGUIScene::lineEditEditingFinished() return; QString text = lineEdit->text(); + if (text != "") + { + CopyFilterGUIChannelItem* item = new CopyFilterGUIChannelItem(text, true); + item->setPos(getNextChannelPoint(lastOutputItem, true)); + addItem(item); + lastOutputItem = item; - CopyFilterGUIChannelItem* item = new CopyFilterGUIChannelItem(text, true); - item->setPos(getNextChannelPoint(lastOutputItem, true)); - addItem(item); - lastOutputItem = item; + addProxyItem->setPos(getNextChannelPoint(lastOutputItem, true)); + } + addProxyItem->setVisible(true); + + lineEdit->setVisible(false); + lineEdit->deleteLater(); +} + +void CopyFilterGUIScene::lineEditEditingCanceled() +{ + QLineEdit* lineEdit = qobject_cast(QObject::sender()); - addProxyItem->setPos(getNextChannelPoint(lastOutputItem, true)); addProxyItem->setVisible(true); lineEdit->setVisible(false); diff --git a/Editor/guis/CopyFilterGUIScene.h b/Editor/guis/CopyFilterGUIScene.h index 1f4688b..c1ea837 100644 --- a/Editor/guis/CopyFilterGUIScene.h +++ b/Editor/guis/CopyFilterGUIScene.h @@ -43,6 +43,7 @@ protected: private slots: void addOutputChannel(); void lineEditEditingFinished(); + void lineEditEditingCanceled(); private: QGraphicsItem* lastOutputItem; diff --git a/Editor/guis/IncludeFilterGUI.ui b/Editor/guis/IncludeFilterGUI.ui index 03fd332..0289b69 100644 --- a/Editor/guis/IncludeFilterGUI.ui +++ b/Editor/guis/IncludeFilterGUI.ui @@ -51,7 +51,14 @@ - + + + + 0 + 0 + + + diff --git a/Editor/guis/VSTPluginFilterGUI.cpp b/Editor/guis/VSTPluginFilterGUI.cpp new file mode 100644 index 0000000..2a49cd6 --- /dev/null +++ b/Editor/guis/VSTPluginFilterGUI.cpp @@ -0,0 +1,480 @@ +/* + This file is part of Equalizer APO, a system-wide equalizer. + Copyright (C) 2017 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include +#include +#include +#include +#include + +#define WIN32_LEAN_AND_MEAN +#include +#include "helpers/aeffectx.h" +#include "helpers/StringHelper.h" +#include "Editor/helpers/DPIHelper.h" +#include "Editor/MainWindow.h" +#include "VSTPluginFilterGUIDialog.h" +#include "VSTPluginFilterGUI.h" +#include "ui_VSTPluginFilterGUI.h" + +using namespace std; +using namespace std::placeholders; + +VSTPluginFilterGUI::VSTPluginFilterGUI(std::shared_ptr library, const std::wstring& chunkData, const std::unordered_map& paramMap) + : ui(new Ui::VSTPluginFilterGUI), library(library), chunkData(chunkData), paramMap(paramMap) +{ + ui->setupUi(this); + ui->frame->setVisible(false); + updatePermissionWarning(); + + QString absolutePath = QString::fromStdWString(library->getLibPath()); + QDir pluginsDir(QString::fromStdWString(VSTPluginLibrary::getDefaultPluginPath())); + QString relativePath = QDir::toNativeSeparators(pluginsDir.relativeFilePath(absolutePath)); + if (relativePath.startsWith(QDir::toNativeSeparators("../../"))) + relativePath = absolutePath; + ui->pathLineEdit->setText(relativePath); + + QMenu* menu = new QMenu(ui->optionsButton); + menu->addAction(ui->embedAction); + ui->optionsButton->setMenu(menu); +} + +VSTPluginFilterGUI::~VSTPluginFilterGUI() +{ + if (effect != NULL) + { + if (ui->embedAction->isChecked()) + on_embedAction_toggled(false); + delete effect; + effect = NULL; + } + + delete ui; +} + +void VSTPluginFilterGUI::store(QString& command, QString& parameters) +{ + command = "VSTPlugin"; + + QString absolutePath = QString::fromStdWString(library->getLibPath()); + QDir pluginsDir(QString::fromStdWString(VSTPluginLibrary::getDefaultPluginPath())); + QString relativePath = QDir::toNativeSeparators(pluginsDir.relativeFilePath(absolutePath)); + if (relativePath.startsWith(QDir::toNativeSeparators("../../"))) + relativePath = absolutePath; + + if (relativePath.contains(" ")) + relativePath = "\"" + relativePath + "\""; + parameters = "Library " + relativePath; + + if (chunkData != L"") + { + parameters += " ChunkData \"" + QString::fromStdWString(chunkData) + "\""; + } + else + { + for (auto it : paramMap) + { + QString name = QString::fromStdWString(it.first); + if (name.contains(" ") || name.contains("\"")) + name = "\"" + name.replace("\"", "\"\"") + "\""; + parameters += " " + name + " " + QString("%1").arg(it.second); + } + } +} + +void VSTPluginFilterGUI::loadPreferences(const QVariantMap& prefs) +{ + autoApplyDialog = prefs.value("autoApplyDialog").toBool(); + + if (prefs.value("embed").toBool()) + // will also call initPlugin + ui->embedAction->setChecked(true); + else + initPlugin(); +} + +void VSTPluginFilterGUI::storePreferences(QVariantMap& prefs) +{ + prefs.insert("embed", ui->embedAction->isChecked()); + prefs.insert("autoApplyDialog", autoApplyDialog); +} + +void VSTPluginFilterGUI::on_openPanelButton_clicked() +{ + initPlugin(); + + if (effect != NULL) + { + effect->writeToEffect(chunkData, paramMap); + + VSTPluginFilterGUIDialog dialog(this, effect, autoApplyDialog); + connect(dialog.getApplyButton(), SIGNAL(pressed()), SLOT(applyDialog())); + connect(dialog.getAutoApplyCheckBox(), SIGNAL(toggled(bool)), SLOT(autoApplyToggled(bool))); + connect(QAbstractEventDispatcher::instance(), SIGNAL(aboutToBlock()), SLOT(on_idle())); + + if (dialog.exec() == QDialog::Accepted) + { + effect->readFromEffect(chunkData, paramMap); + updateModel(); + updatePermissionWarning(); + } + disconnect(QAbstractEventDispatcher::instance(), SIGNAL(aboutToBlock()), this, SLOT(on_idle())); + } +} + +void VSTPluginFilterGUI::applyDialog() +{ + effect->readFromEffect(chunkData, paramMap); + updateModel(); + updatePermissionWarning(); +} + +void VSTPluginFilterGUI::autoApplyToggled(bool checked) +{ + autoApplyDialog = checked; +} + +void VSTPluginFilterGUI::initPlugin() +{ + if (effect != NULL) + return; + + QColor color; + QString text; + if (library->getLibPath() == L"") + { + text = tr("No file selected."); + color = Qt::red; + } + else + { + int result = library->initialize(); + if (result < 0) + { + color = Qt::red; + + switch (result) + { + case AbstractLibrary::FILE_NOT_FOUND: + text = tr("File not found."); + break; + case AbstractLibrary::LOADING_FAILED: + text = tr("Library could not be loaded."); + break; + case AbstractLibrary::FUNCTIONS_MISSING: + text = tr("Library does not contain needed functions."); + break; + case AbstractLibrary::WRONG_ARCHITECTURE: +#ifdef _WIN64 + int bitDepth = 64; +#else + int bitDepth = 32; +#endif + text = tr("Library has the wrong architecture. Only %1-bit libraries are supported.").arg(bitDepth); + break; + } + } + else + { + effect = new VSTPluginInstance(library, 1); + if (effect->initialize()) + { + effect->setLanguage(QLocale().language() == QLocale::German ? 2 : 1); + effect->setAutomateFunc(bind(&VSTPluginFilterGUI::onAutomate, this)); + + color = Qt::black; + text = QString::fromStdWString(effect->getName()); + } + else + { + delete effect; + effect = NULL; + + color = Qt::red; + text = tr("Plugin crashed during initialization."); + } + } + } + + QPalette palette = ui->statusLabel->palette(); + palette.setColor(QPalette::Active, QPalette::WindowText, color); + palette.setColor(QPalette::Inactive, QPalette::WindowText, color); + ui->statusLabel->setPalette(palette); + ui->statusLabel->setText(text); +} + +void VSTPluginFilterGUI::on_pathLineEdit_editingFinished() +{ + if (QString::fromStdWString(library->getLibPath()) != ui->pathLineEdit->text()) + { + int oldId = 0; + if (effect != NULL) + { + oldId = effect->uniqueID(); + if (ui->embedAction->isChecked()) + on_embedAction_toggled(false); + delete effect; + effect = NULL; + } + + QDir pluginsDir(QString::fromStdWString(VSTPluginLibrary::getDefaultPluginPath())); + QString path = ui->pathLineEdit->text(); + if (path.length() > 0) + path = QDir::toNativeSeparators(QFileInfo(pluginsDir, ui->pathLineEdit->text()).absoluteFilePath()); + library = VSTPluginLibrary::getInstance(path.toStdWString()); + initPlugin(); + + if (effect == NULL || oldId == 0 || effect->uniqueID() != oldId) + { + chunkData = L""; + paramMap.clear(); + } + + updateModel(); + updatePermissionWarning(); + + if (ui->embedAction->isChecked()) + on_embedAction_toggled(true); + } +} + +void VSTPluginFilterGUI::on_selectButton_clicked() +{ + QDir pluginsDir(QString::fromStdWString(VSTPluginLibrary::getDefaultPluginPath())); + + QSettings settings(QString::fromWCharArray(EDITOR_REGPATH), QSettings::NativeFormat); + QString lastDir = settings.value("vst/lastDir", "").toString(); + if (lastDir == "") + lastDir = pluginsDir.absolutePath(); + + QFileInfo fileInfo(lastDir); + QString path = ui->pathLineEdit->text(); + if (path.length() > 0) + fileInfo.setFile(pluginsDir, path); + + QFileDialog dialog(this, tr("Select VST plugin"), fileInfo.absoluteFilePath(), "*.dll"); + dialog.setFileMode(QFileDialog::ExistingFile); + dialog.setNameFilter(tr("VST plugins (*.dll)")); + if (path.length() > 0) + dialog.selectFile(fileInfo.fileName()); + if (dialog.exec() == QDialog::Accepted) + { + QString absolutePath = dialog.selectedFiles().first(); + settings.setValue("vst/lastDir", QDir::toNativeSeparators(QFileInfo(absolutePath).absolutePath())); + QString relativePath = pluginsDir.relativeFilePath(absolutePath); + if (relativePath.startsWith("../../")) + relativePath = absolutePath; + ui->pathLineEdit->setText(QDir::toNativeSeparators(relativePath)); + on_pathLineEdit_editingFinished(); + } +} + +void VSTPluginFilterGUI::on_embedAction_toggled(bool checked) +{ + initPlugin(); + + ui->openPanelButton->setVisible(!checked); + + bool enable = checked; + if (effect == NULL) + enable = false; + + bool wasEnabled = ui->frame->isVisible(); + if (enable != wasEnabled) + { + ui->frame->setVisible(enable); + ui->statusLabel->setVisible(!enable); + + if (enable) + { + if (embedPlugin()) + { + effect->setSizeWindowFunc(bind(&VSTPluginFilterGUI::onSizeWindow, this, _1, _2)); + connect(QAbstractEventDispatcher::instance(), SIGNAL(aboutToBlock()), SLOT(on_idle())); + } + else + { + ui->frame->setVisible(false); + ui->statusLabel->setVisible(true); + + QPalette palette = ui->statusLabel->palette(); + palette.setColor(QPalette::Active, QPalette::WindowText, Qt::red); + palette.setColor(QPalette::Inactive, QPalette::WindowText, Qt::red); + ui->statusLabel->setPalette(palette); + ui->statusLabel->setText(tr("Plugin crashed when opening panel.")); + } + } + else + { + if (effect != NULL) + { + effect->stopEditing(); + effect->setSizeWindowFunc(nullptr); + } + + disconnect(QAbstractEventDispatcher::instance(), SIGNAL(aboutToBlock()), this, SLOT(on_idle())); + } + } +} + +void VSTPluginFilterGUI::on_idle() +{ + if (effect != NULL) + { + effect->doIdle(); + + if (ui->frame->isVisible() || autoApplyDialog) + { + if (!lastReadTimer.isValid() || lastReadTimer.elapsed() > 1000) + { + wstring newChunkData; + unordered_map newParamMap; + effect->readFromEffect(newChunkData, newParamMap); + if (newChunkData != chunkData || newParamMap != paramMap) + { + chunkData = newChunkData; + paramMap = newParamMap; + updateModel(); + updatePermissionWarning(); + } + lastReadTimer.restart(); + } + } + } +} + +void VSTPluginFilterGUI::onAutomate() +{ + if (ui->frame->isVisible() || autoApplyDialog) + { + effect->readFromEffect(chunkData, paramMap); + updateModel(); + updatePermissionWarning(); + } +} + +void VSTPluginFilterGUI::onSizeWindow(int w, int h) +{ + if (ui->frame->isVisible()) + ui->frame->setFixedSize(w, h); +} + +bool VSTPluginFilterGUI::embedPlugin() +{ + bool result = true; + + __try + { + effect->writeToEffect(chunkData, paramMap); + + HWND hwnd = (HWND)ui->frame->winId(); + short width, height; + + effect->startEditing(hwnd, &width, &height); + + ui->frame->setFixedSize(width, height); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + result = false; + } + + return result; +} + +void VSTPluginFilterGUI::updatePermissionWarning() +{ + if (effect == NULL) + { + ui->warningTextEdit->setVisible(false); + return; + } + + ACCESS_MASK mask = GENERIC_READ; + try + { + mask = RegistryHelper::getFileAccessForUser(library->getLibPath(), SECURITY_LOCAL_SERVICE_RID); + } + catch (RegistryException e) + { + // ignore + } + + if ((mask & GENERIC_READ) != GENERIC_READ && (mask & FILE_GENERIC_READ) != FILE_GENERIC_READ) + { + QString text = tr("The library is not readable by the audio service.\nChange the file permissions or copy the file to the plugins directory."); + + ui->warningTextEdit->setPlainText(text); + QSize textSize = ui->warningTextEdit->fontMetrics().size(0, text); + ui->warningTextEdit->setFixedSize(textSize + DPIHelper::scale(QSize(40, 15))); + ui->warningTextEdit->setVisible(true); + return; + } + + QStringList files; + if (chunkData != L"" && chunkData.length() < 100000) + { + QByteArray bytes = QByteArray::fromBase64(QString::fromStdWString(chunkData).toUtf8()); + QString string = QString::fromUtf8(bytes.data(), bytes.length()); + QRegExp regexp("[A-Za-z]:(?:\\\\[\\w \\(\\)-]+)+\\.[A-Za-z]{3}"); + int offset = 0; + while (offset >= 0) + { + offset = regexp.indexIn(string, offset); + if (offset >= 0) + { + QString path = regexp.cap(); + QFile file(path); + if (file.exists()) + { + ACCESS_MASK mask = GENERIC_READ; + try + { + mask = RegistryHelper::getFileAccessForUser(path.toStdWString(), SECURITY_LOCAL_SERVICE_RID); + } + catch (RegistryException e) + { + // ignore + } + + if ((mask & GENERIC_READ) != GENERIC_READ && (mask & FILE_GENERIC_READ) != FILE_GENERIC_READ) + files.append(path); + } + offset += regexp.matchedLength(); + } + } + } + + if (files.isEmpty()) + { + ui->warningTextEdit->setVisible(false); + ui->warningTextEdit->setPlainText(""); + } + else + { + files.removeDuplicates(); + QString text = tr("The plugin seemingly accesses these files not readable by the audio service:\n" + "%0\n" + "Change the file permissions or copy the files to the config directory.").arg(files.join("\n")); + ui->warningTextEdit->setPlainText(text); + QSize textSize = ui->warningTextEdit->fontMetrics().size(0, text); + ui->warningTextEdit->setFixedSize(textSize + DPIHelper::scale(QSize(40, 15))); + ui->warningTextEdit->setVisible(true); + } +} diff --git a/Editor/guis/VSTPluginFilterGUI.h b/Editor/guis/VSTPluginFilterGUI.h new file mode 100644 index 0000000..1390911 --- /dev/null +++ b/Editor/guis/VSTPluginFilterGUI.h @@ -0,0 +1,66 @@ +/* + This file is part of Equalizer APO, a system-wide equalizer. + Copyright (C) 2017 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include +#include +#include "Editor/IFilterGUI.h" +#include "helpers/VSTPluginLibrary.h" + +namespace Ui { +class VSTPluginFilterGUI; +} + +class VSTPluginFilterGUI : public IFilterGUI +{ + Q_OBJECT + +public: + explicit VSTPluginFilterGUI(std::shared_ptr library, const std::wstring& chunkData, const std::unordered_map& paramMap); + ~VSTPluginFilterGUI(); + + void store(QString& command, QString& parameters) override; + void loadPreferences(const QVariantMap& prefs) override; + void storePreferences(QVariantMap& prefs) override; + void onAutomate(); + void onSizeWindow(int w, int h); + +private slots: + void on_openPanelButton_clicked(); + void applyDialog(); + void autoApplyToggled(bool checked); + void on_pathLineEdit_editingFinished(); + void on_selectButton_clicked(); + void on_embedAction_toggled(bool checked); + void on_idle(); + +private: + void initPlugin(); + bool embedPlugin(); + void updatePermissionWarning(); + + Ui::VSTPluginFilterGUI* ui; + std::shared_ptr library; + VSTPluginInstance* effect = NULL; + std::wstring chunkData; + std::unordered_map paramMap; + bool autoApplyDialog = false; + QElapsedTimer lastReadTimer; +}; diff --git a/Editor/guis/VSTPluginFilterGUI.ui b/Editor/guis/VSTPluginFilterGUI.ui new file mode 100644 index 0000000..2f1516c --- /dev/null +++ b/Editor/guis/VSTPluginFilterGUI.ui @@ -0,0 +1,203 @@ + + + VSTPluginFilterGUI + + + + 0 + 0 + 542 + 166 + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 5 + + + + + + + + + 0 + 0 + + + + Open panel + + + + + + + + 0 + 0 + + + + VST plugin: + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Options + + + QToolButton::MenuButtonPopup + + + Qt::ToolButtonTextOnly + + + + + + + + + + + :/icons/document-open.ico:/icons/document-open.ico + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + 0 + 0 + + + + + + + + + 255 + 255 + 127 + + + + + + + + + 255 + 255 + 127 + + + + + + + + + 240 + 240 + 240 + + + + + + + + QAbstractScrollArea::AdjustToContents + + + + + + + true + + + Embed + + + + + + ResizingLineEdit + QLineEdit +
Editor/widgets/ResizingLineEdit.h
+
+
+ + + + + + optionsButton + clicked() + optionsButton + showMenu() + + + 288 + 12 + + + 288 + 12 + + + + +
diff --git a/Editor/guis/VSTPluginFilterGUIDialog.cpp b/Editor/guis/VSTPluginFilterGUIDialog.cpp new file mode 100644 index 0000000..76b8b35 --- /dev/null +++ b/Editor/guis/VSTPluginFilterGUIDialog.cpp @@ -0,0 +1,75 @@ +/* + This file is part of Equalizer APO, a system-wide equalizer. + Copyright (C) 2017 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include +#include "VSTPluginFilterGUIDialog.h" +#include "ui_VSTPluginFilterGUIDialog.h" + +using namespace std; +using namespace std::placeholders; + +VSTPluginFilterGUIDialog::VSTPluginFilterGUIDialog(QWidget* parent, VSTPluginInstance* effect, bool autoApply) + : QDialog(parent, Qt::WindowCloseButtonHint), + ui(new Ui::VSTPluginFilterGUIDialog), + effect(effect) +{ + ui->setupUi(this); + ui->autoApplyCheckBox->setChecked(autoApply); + + QString name = QString::fromStdWString(effect->getName()); + setWindowTitle(name); + + HWND hwnd = (HWND)ui->frame->winId(); + + short width, height; + + effect->startEditing(hwnd, &width, &height); + + ui->frame->setFixedSize(width, height); + effect->setSizeWindowFunc(bind(&VSTPluginFilterGUIDialog::onSizeWindow, this, _1, _2)); +} + +VSTPluginFilterGUIDialog::~VSTPluginFilterGUIDialog() +{ + effect->stopEditing(); + effect->setSizeWindowFunc(nullptr); + + delete ui; +} + +QPushButton* VSTPluginFilterGUIDialog::getApplyButton() +{ + return ui->buttonBox->button(QDialogButtonBox::Apply); +} + +QCheckBox* VSTPluginFilterGUIDialog::getAutoApplyCheckBox() +{ + return ui->autoApplyCheckBox; +} + +void VSTPluginFilterGUIDialog::onSizeWindow(int w, int h) +{ + ui->frame->setFixedSize(w, h); +} + +void VSTPluginFilterGUIDialog::on_autoApplyCheckBox_clicked(bool checked) +{ + if (checked) + getApplyButton()->click(); +} diff --git a/Editor/guis/VSTPluginFilterGUIDialog.h b/Editor/guis/VSTPluginFilterGUIDialog.h new file mode 100644 index 0000000..302cc35 --- /dev/null +++ b/Editor/guis/VSTPluginFilterGUIDialog.h @@ -0,0 +1,49 @@ +/* + This file is part of Equalizer APO, a system-wide equalizer. + Copyright (C) 2017 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include +#include +#include "helpers/VSTPluginInstance.h" + +namespace Ui { +class VSTPluginFilterGUIDialog; +} + +class VSTPluginFilterGUIDialog : public QDialog +{ + Q_OBJECT + +public: + explicit VSTPluginFilterGUIDialog(QWidget* parent, VSTPluginInstance* effect, bool autoApply); + ~VSTPluginFilterGUIDialog(); + + QPushButton* getApplyButton(); + QCheckBox* getAutoApplyCheckBox(); + + void onSizeWindow(int w, int h); + +private slots: + void on_autoApplyCheckBox_clicked(bool checked); + +private: + Ui::VSTPluginFilterGUIDialog* ui; + VSTPluginInstance* effect; +}; diff --git a/Editor/guis/VSTPluginFilterGUIDialog.ui b/Editor/guis/VSTPluginFilterGUIDialog.ui new file mode 100644 index 0000000..9993ae9 --- /dev/null +++ b/Editor/guis/VSTPluginFilterGUIDialog.ui @@ -0,0 +1,87 @@ + + + VSTPluginFilterGUIDialog + + + + 0 + 0 + 459 + 300 + + + + + + + + + + Apply automatically + + + + + + + + 0 + 0 + + + + Qt::Horizontal + + + QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + + buttonBox + accepted() + VSTPluginFilterGUIDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + VSTPluginFilterGUIDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/Editor/guis/VSTPluginFilterGUIFactory.cpp b/Editor/guis/VSTPluginFilterGUIFactory.cpp new file mode 100644 index 0000000..62a1336 --- /dev/null +++ b/Editor/guis/VSTPluginFilterGUIFactory.cpp @@ -0,0 +1,62 @@ +/* + This file is part of Equalizer APO, a system-wide equalizer. + Copyright (C) 2017 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "helpers/VSTPluginLibrary.h" +#include "helpers/StringHelper.h" +#include "filters/VSTPluginFilterFactory.h" +#include "filters/VSTPluginFilter.h" +#include "VSTPluginFilterGUI.h" +#include "VSTPluginFilterGUIFactory.h" + +using namespace std; + +QList VSTPluginFilterGUIFactory::createFilterTemplates() +{ + QList list; + list.append(FilterTemplate(tr("VST plugin"), "VSTPlugin:", QStringList(tr("Plugins")))); + return list; +} + +IFilterGUI* VSTPluginFilterGUIFactory::createFilterGUI(QString& command, QString& parameters) +{ + VSTPluginFilterGUI* result = NULL; + + if (command == "VSTPlugin") + { + VSTPluginFilterFactory factory; + std::vector filters = factory.createFilter(L"", command.toStdWString(), parameters.toStdWString()); + if (!filters.empty()) + { + VSTPluginFilter* filter = (VSTPluginFilter*)filters[0]; + result = new VSTPluginFilterGUI(filter->getLibrary(), filter->getChunkData(), filter->getParamMap()); + } + else + { + result = new VSTPluginFilterGUI(VSTPluginLibrary::getInstance(L""), L"", unordered_map()); + } + + for (IFilter* f : filters) + { + f->~IFilter(); + MemoryHelper::free(f); + } + } + + return result; +} diff --git a/Editor/guis/VSTPluginFilterGUIFactory.h b/Editor/guis/VSTPluginFilterGUIFactory.h new file mode 100644 index 0000000..a5e2881 --- /dev/null +++ b/Editor/guis/VSTPluginFilterGUIFactory.h @@ -0,0 +1,30 @@ +/* + This file is part of Equalizer APO, a system-wide equalizer. + Copyright (C) 2017 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ +#pragma once + +#include "Editor/IFilterGUIFactory.h" + +class VSTPluginFilterGUIFactory : public IFilterGUIFactory +{ + Q_OBJECT + +public: + QList createFilterTemplates() override; + IFilterGUI* createFilterGUI(QString& command, QString& parameters) override; +}; diff --git a/Editor/translations/Editor_de.qm b/Editor/translations/Editor_de.qm index a3db98c0232f49425571d7d64a9ef0d9ab2aa377..4d98465744409f6dbd039a783492be57fd006ee6 100644 GIT binary patch delta 2289 zcmah~U1%It6h4!5cWPpqv`N~=pY|Hrk_2~Y5Fu`aTHB_LmL!-TZAgN%Gk15dnaqsy z)3{N)FM@p#OuZlm{K1!kFGYy;RTRZMRH1^33hF~16?{U%XW)0 zm&+~tP91`CAi#kfa0v0o!PyV^m&BGhw~*tOj&Fyi#wW(?s34pnVTas5KRK)+YUq=1 z4fISyrV`SMT!p8hQsZ8 z)!PQ6;NfPec9r%O59p(w(z0Ik4Cb_vz5>QmT?<7Z)mfS22T4h+G8o2T5=AIUX&OoF zE9MJ0ZeU0Odt(0mZTzQV(=yXHf+E(L75M`B7mf&pVT&b$fY#Prd7H(jcSTA-7ZAd? zSa125gAL#TJ}aCew7p<olhJb zS2e)!1Y@2$!s_Uv9fPxqU{>pa*aGr;_Qw`)i*A8@b@2Y<&re`s$Aw#BBKEh>4a>-3 z1?Ecf*5HLgoH<~Y@Ub9Wzf^BOS{_jtinp5mU=O*Nc&FouSdEbRP2UH2Uf#%_c`!CA ztw7eKa-j-a!nxE6SUl95SN&ONLhqlHpWIb$#Cg&iJT>+P?I#9|2I32-0UFdWO&$bj zOtf*pmA(GARb_D_vV@C){$*kEY*Xr_{CO~~1@g{E?k(?jj~w1nz-+}*%~kcXb>xE+ zJFY_o*aLl#DWk1y{Pk8AZm%vJu%5n*W!t^=67=9%8X>PPRzC4dh9pf$3rF2fPJBC6 zGo1E3qAv>Fg~RAK)3EIlr8ru-Q|0aUj?96Y+6^l4dhCsWLb|FU_qBA6cI>pW2lg0&DlP(5NlZ^(MlP(9c0#lQ-;s=vc2Z#Uw00IF499Faltung mit Impulsantwort: - + File information Dateiinformationen - + Sample rate: Abtastrate: - - + + No file selected Keine Datei gewählt - + Length: Länge: @@ -661,7 +661,7 @@ Wählen Sie eine andere Datei oder ändern Sie die Konfiguration des Geräts. FilterTable - + Add filter Filter hinzufügen @@ -838,12 +838,12 @@ Wählen Sie eine andere Datei oder ändern Sie die Konfiguration des Geräts. IncludeFilterGUI - + Include: Einbinden: - + Select file Datei auswählen @@ -1198,11 +1198,12 @@ Möchten Sie die Configurator-Anwendung starten, um das Problem zu beheben? + From device Von Gerät - + %0 dB %0 dB @@ -1211,7 +1212,7 @@ Möchten Sie die Configurator-Anwendung starten, um das Problem zu beheben?%0 ms (%1 Samp.) - + %0 ms (%1 s.) %0 ms (%1 S.) @@ -1220,46 +1221,46 @@ Möchten Sie die Configurator-Anwendung starten, um das Problem zu beheben?%0 ms (%1 Samples) - + %0 ms %0 ms - + %0 % (one core) %0 % (ein Kern) - - - + + + Restart required Neustart erforderlich - - - + + + Configuration Editor will be restarted to apply the changed settings. Proceed? Konfigurationseditor wird neugestartet, um die geänderten Einstellungen zu übernehmen. Fortsetzen? - + Unsaved changes Ungespeicherte Änderungen - + The configuration file %0 has unsaved changes. Die Konfigurationsdatei %0 hat ungespeicherte Änderungen. - + Do you want to save the changes before closing the file? Möchten Sie die Änderungen speichern, bevor die Datei geschlossen wird? - + Open file Datei öffnen @@ -1269,22 +1270,110 @@ Möchten Sie die Configurator-Anwendung starten, um das Problem zu beheben?Automatisch (%0) - - + + E-APO configurations (*.txt) E-APO-Konfigurationen (*.txt) - + Save file as Datei speichern unter - + Unsaved Ungespeichert + + MathAudioFilterGUI + + Open panel + Panel öffnen + + + Status information + Statusinformationen + + + File %1 not found. + Datei %1 nicht gefunden. + + + Library %1 could not be loaded. + Bibliothek %1 konnte nicht geladen werden. + + + Library %1 does not contain needed functions. + Bibliothek %1 enthält nicht die benötigten Funktionen. + + + Library %1 could not be initialized. + Bibliothek %1 konnte nicht initialisiert werden. + + + Get library from <a href="%1">download page</a> and<br>extract %2-bit version of %3 into <a href="%4">plugins</a> directory. + Bibliothek von der <a href="%1">Downloadseite</a> laden und<br>die %2-bit-Version von %3 in das Verzeichnis <a href="%4">plugins</a> extrahieren. + + + Library loaded successfully. Version: %1 + Bibliothek erfolgreich geladen. Version: %1 + + + For updates, get new version from <a href="%1">download page</a>, close Configuration Editor and<br>extract %2-bit version of %3 into <a href="%4">plugins</a> directory. + Für Updates, neue Version von der <a href="%1">Downloadseite</a> laden, den Konfigurationseditor schließen und<br>die %2-bit-Version von %3 in das Verzeichnis <a href="%4">plugins</a> extrahieren. + + + + MathAudioFilterGUIFactory + + Plugins + Plugins + + + + MathAudioHeadphoneEQFilterGUI + + Open panel + Panel öffnen + + + Status information + Statusinformationen + + + File %1 not found. + Datei %1 nicht gefunden. + + + Library %1 could not be loaded. + Bibliothek %1 konnte nicht geladen werden. + + + Library %1 does not contain needed functions. + Bibliothek %1 enthält nicht die benötigten Funktionen. + + + Get library from <a href="%1">download page</a> and<br>extract %2-bit version of %3 into <a href="%4">plugins</a> directory. + Bibliothek von der <a href="%1">Downloadseite</a> laden und<br>die %2-bit-Version von %3 in das Verzeichnis <a href="%4">plugins</a> extrahieren. + + + Library loaded successfully. + Bibliothek erfolgreich geladen. + + + For updates, get new version from <a href="%1">download page</a>, close Configuration Editor and<br>extract %2-bit version of %3 into <a href="%4">plugins</a> directory. + Für Updates, neue Version von der <a href="%1">Downloadseite</a> laden, den Konfigurationseditor schließen und<br>die %2-bit-Version von %3 in das Verzeichnis <a href="%4">plugins</a> extrahieren. + + + + MathAudioHeadphoneEQFilterGUIFactory + + Plugins + Plugins + + PreampFilterGUI @@ -1374,4 +1463,113 @@ Möchten Sie die Configurator-Anwendung starten, um das Problem zu beheben?Steuerung + + VSTPluginFilterGUI + + + Options + Optionen + + + + Open panel + Panel öffnen + + + + VST plugin: + VST-Plugin: + + + + Embed + Einbetten + + + No file selected + Keine Datei gewählt + + + + No file selected. + Keine Datei gewählt. + + + + File not found. + Datei nicht gefunden. + + + + Library could not be loaded. + Bibliothek konnte nicht geladen werden. + + + + Library does not contain needed functions. + Bibliothek enthält benötigte Funktionen nicht. + + + + Library has the wrong architecture. Only %1-bit libraries are supported. + Bibliothek hat die falsche Architektur. Nur %1-bit-Bibliotheken werden unterstützt. + + + + Plugin crashed during initialization. + Plugin ist bei der Initialisierung abgestürzt. + + + + Select VST plugin + VST-Plugin wählen + + + + VST plugins (*.dll) + VST-Plugins (*.dll) + + + + Plugin crashed when opening panel. + Plugin ist beim Öffnen des Panels abgestürzt. + + + + The library is not readable by the audio service. +Change the file permissions or copy the file to the plugins directory. + Die Bibliothek ist für den Audiodienst nicht lesbar. +Ändern Sie die Dateiberechtigungen oder kopieren Sie die Datei in das plugins-Verzeichnis. + + + + The plugin seemingly accesses these files not readable by the audio service: +%0 +Change the file permissions or copy the files to the config directory. + Das Plugin scheint auf diese für den Audiodienst nicht lesbaren Dateien zuzugreifen: +%0 +Ändern Sie die Dateiberechtigungen oder kopieren Sie die Dateien in das config-Verzeichnis. + + + + VSTPluginFilterGUIDialog + + + Apply automatically + Automatisch anwenden + + + + VSTPluginFilterGUIFactory + + + VST plugin + VST-Plugin + + + + Plugins + Plugins + + diff --git a/Editor/widgets/EscapableLineEdit.cpp b/Editor/widgets/EscapableLineEdit.cpp index ec78b36..e1b5660 100644 --- a/Editor/widgets/EscapableLineEdit.cpp +++ b/Editor/widgets/EscapableLineEdit.cpp @@ -26,9 +26,13 @@ EscapableLineEdit::EscapableLineEdit(QWidget* parent) { } +EscapableLineEdit::EscapableLineEdit(const QString& text, QWidget* parent) + : QLineEdit(text, parent) +{ +} + void EscapableLineEdit::keyPressEvent(QKeyEvent* event) { - if (event->key() == Qt::Key_Escape) emit editingCanceled(); else diff --git a/Editor/widgets/EscapableLineEdit.h b/Editor/widgets/EscapableLineEdit.h index e201c28..866ba7c 100644 --- a/Editor/widgets/EscapableLineEdit.h +++ b/Editor/widgets/EscapableLineEdit.h @@ -26,6 +26,7 @@ class EscapableLineEdit : public QLineEdit Q_OBJECT public: explicit EscapableLineEdit(QWidget* parent = 0); + explicit EscapableLineEdit(const QString& text, QWidget* parent = 0); signals: void editingCanceled(); diff --git a/Editor/widgets/ResizingLineEdit.cpp b/Editor/widgets/ResizingLineEdit.cpp index c71447b..8211966 100644 --- a/Editor/widgets/ResizingLineEdit.cpp +++ b/Editor/widgets/ResizingLineEdit.cpp @@ -20,15 +20,15 @@ #include "ResizingLineEdit.h" ResizingLineEdit::ResizingLineEdit(QWidget* parent) - : QLineEdit(parent) + : EscapableLineEdit(parent) { connect(this, SIGNAL(textChanged(QString)), this, SLOT(readjustSize())); readjustSize(); } -ResizingLineEdit::ResizingLineEdit(const QString& text, QWidget* parent) - : QLineEdit(text, parent) +ResizingLineEdit::ResizingLineEdit(const QString& text, bool forceWidth, QWidget* parent) + : EscapableLineEdit(text, parent), forceWidth(forceWidth) { connect(this, SIGNAL(textChanged(QString)), this, SLOT(readjustSize())); @@ -50,5 +50,8 @@ QSize ResizingLineEdit::sizeHint() const void ResizingLineEdit::readjustSize() { - setFixedWidth(sizeHint().width()); + if (forceWidth) + setFixedWidth(sizeHint().width()); + else + setMinimumWidth(sizeHint().width()); } diff --git a/Editor/widgets/ResizingLineEdit.h b/Editor/widgets/ResizingLineEdit.h index 15304c7..c0de074 100644 --- a/Editor/widgets/ResizingLineEdit.h +++ b/Editor/widgets/ResizingLineEdit.h @@ -19,17 +19,20 @@ #pragma once -#include +#include "EscapableLineEdit.h" -class ResizingLineEdit : public QLineEdit +class ResizingLineEdit : public EscapableLineEdit { Q_OBJECT public: explicit ResizingLineEdit(QWidget* parent = 0); - explicit ResizingLineEdit(const QString& text, QWidget* parent = 0); + explicit ResizingLineEdit(const QString& text, bool forceWidth, QWidget* parent = 0); QSize sizeHint() const override; private slots: void readjustSize(); + +private: + bool forceWidth = false; }; diff --git a/EqualizerAPO.licenseheader b/EqualizerAPO.licenseheader new file mode 100644 index 0000000..890d04f --- /dev/null +++ b/EqualizerAPO.licenseheader @@ -0,0 +1,19 @@ +extensions: .cpp .h +/* + This file is part of Equalizer APO, a system-wide equalizer. + Copyright (C) %CurrentYear% Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ diff --git a/EqualizerAPO.vcxproj b/EqualizerAPO.vcxproj index 3acdb83..7279d3d 100644 --- a/EqualizerAPO.vcxproj +++ b/EqualizerAPO.vcxproj @@ -99,7 +99,7 @@ Windows true - muparserxd.lib;audioeng.lib;version.lib;wmcodecdspuuid.lib;AudioBaseProcessingObject.lib;audiomediatypecrt.lib;Shlwapi.lib;authz.lib;libsndfile-1.lib;libfftw3f-3.lib;%(AdditionalDependencies) + muparserxd.lib;audioeng.lib;version.lib;wmcodecdspuuid.lib;AudioBaseProcessingObject.lib;audiomediatypecrt.lib;Shlwapi.lib;authz.lib;crypt32.lib;dbghelp.lib;libsndfile-1.lib;libfftw3f-3.lib;%(AdditionalDependencies) EqualizerAPO.def @@ -116,7 +116,7 @@ Windows true - muparserxd.lib;audioeng.lib;version.lib;wmcodecdspuuid.lib;AudioBaseProcessingObject.lib;audiomediatypecrt.lib;Shlwapi.lib;authz.lib;libsndfile-1.lib;libfftw3f-3.lib;%(AdditionalDependencies) + muparserxd.lib;audioeng.lib;version.lib;wmcodecdspuuid.lib;AudioBaseProcessingObject.lib;audiomediatypecrt.lib;Shlwapi.lib;authz.lib;crypt32.lib;dbghelp.lib;libsndfile-1.lib;libfftw3f-3.lib;%(AdditionalDependencies) EqualizerAPO.def @@ -138,7 +138,7 @@ true true true - muparserx.lib;audioeng.lib;version.lib;wmcodecdspuuid.lib;AudioBaseProcessingObject.lib;audiomediatypecrt.lib;Shlwapi.lib;authz.lib;libsndfile-1.lib;libfftw3f-3.lib;%(AdditionalDependencies) + muparserx.lib;audioeng.lib;version.lib;wmcodecdspuuid.lib;AudioBaseProcessingObject.lib;audiomediatypecrt.lib;Shlwapi.lib;authz.lib;crypt32.lib;dbghelp.lib;libsndfile-1.lib;libfftw3f-3.lib;%(AdditionalDependencies) EqualizerAPO.def @@ -157,7 +157,7 @@ true true true - muparserx.lib;audioeng.lib;version.lib;wmcodecdspuuid.lib;AudioBaseProcessingObject.lib;audiomediatypecrt.lib;Shlwapi.lib;authz.lib;libsndfile-1.lib;libfftw3f-3.lib;%(AdditionalDependencies) + muparserx.lib;audioeng.lib;version.lib;wmcodecdspuuid.lib;AudioBaseProcessingObject.lib;audiomediatypecrt.lib;Shlwapi.lib;authz.lib;crypt32.lib;dbghelp.lib;libsndfile-1.lib;libfftw3f-3.lib;%(AdditionalDependencies) EqualizerAPO.def @@ -190,6 +190,10 @@ + + + + @@ -199,6 +203,8 @@ + + @@ -240,12 +246,17 @@ + + + + + @@ -261,6 +272,7 @@
+ diff --git a/EqualizerAPO.vcxproj.filters b/EqualizerAPO.vcxproj.filters index 38d3ae9..6151d39 100644 --- a/EqualizerAPO.vcxproj.filters +++ b/EqualizerAPO.vcxproj.filters @@ -107,6 +107,21 @@ + + helpers + + + filters + + + filters + + + helpers + + + helpers + @@ -226,6 +241,24 @@ helpers + + helpers + + + helpers + + + filters + + + filters + + + helpers + + + helpers + @@ -233,6 +266,7 @@ + diff --git a/FilterEngine.cpp b/FilterEngine.cpp index ab29759..553be6a 100644 --- a/FilterEngine.cpp +++ b/FilterEngine.cpp @@ -53,6 +53,7 @@ #include "filters/IncludeFilterFactory.h" #include "filters/ConvolutionFilterFactory.h" #include "filters/GraphicEQFilterFactory.h" +#include "filters/VSTPluginFilterFactory.h" using namespace std; using namespace mup; @@ -87,6 +88,7 @@ FilterEngine::FilterEngine() factories.push_back(new CopyFilterFactory()); factories.push_back(new ConvolutionFilterFactory()); factories.push_back(new GraphicEQFilterFactory()); + factories.push_back(new VSTPluginFilterFactory()); } FilterEngine::~FilterEngine() @@ -606,4 +608,4 @@ unsigned long __stdcall FilterEngine::notificationThread(void* parameter) CloseHandle(registryEvent); return 0; -} \ No newline at end of file +} diff --git a/IFilter.h b/IFilter.h index c8b38d3..8b63e8a 100644 --- a/IFilter.h +++ b/IFilter.h @@ -32,7 +32,7 @@ public: // request to get all channel names instead of selection virtual bool getAllChannels() {return false;} - // request that output and input do not point to the same memory locations + // return false to request that output and input do not point to the same memory locations virtual bool getInPlace() {return true;} // request that the channelNames returned by initialize become the new selection virtual bool getSelectChannels() {return false;} diff --git a/Setup/Setup.nsi b/Setup/Setup.nsi index 8a3f9a4..7d91442 100644 --- a/Setup/Setup.nsi +++ b/Setup/Setup.nsi @@ -145,6 +145,7 @@ Section "Install" SecInstall File "Configuration reference (online).url" CreateDirectory "$INSTDIR\config" + CreateDirectory "$INSTDIR\plugins" SetOverwrite off File /oname=config\config.txt "config\config.txt" diff --git a/VoicemeeterClient/VoicemeeterClient.cpp b/VoicemeeterClient/VoicemeeterClient.cpp index 6fbf058..acd8b3e 100644 --- a/VoicemeeterClient/VoicemeeterClient.cpp +++ b/VoicemeeterClient/VoicemeeterClient.cpp @@ -1,3 +1,22 @@ +/* + This file is part of Equalizer APO, a system-wide equalizer. + Copyright (C) 2017 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + #include "stdafx.h" #include #define WIN32_LEAN_AND_MEAN diff --git a/VoicemeeterClient/VoicemeeterClient.h b/VoicemeeterClient/VoicemeeterClient.h index a3bbe25..296e04d 100644 --- a/VoicemeeterClient/VoicemeeterClient.h +++ b/VoicemeeterClient/VoicemeeterClient.h @@ -1,3 +1,22 @@ +/* + This file is part of Equalizer APO, a system-wide equalizer. + Copyright (C) 2017 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + #pragma once #include diff --git a/VoicemeeterClient/VoicemeeterClient.vcxproj b/VoicemeeterClient/VoicemeeterClient.vcxproj index 54fd5c6..33eb4e4 100644 --- a/VoicemeeterClient/VoicemeeterClient.vcxproj +++ b/VoicemeeterClient/VoicemeeterClient.vcxproj @@ -96,7 +96,7 @@ Windows true - muparserxd.lib;version.lib;Shlwapi.lib;authz.lib;libsndfile-1.lib;libfftw3f-3.lib;%(AdditionalDependencies) + muparserxd.lib;version.lib;Shlwapi.lib;authz.lib;crypt32.lib;dbghelp.lib;libsndfile-1.lib;libfftw3f-3.lib;%(AdditionalDependencies) @@ -109,7 +109,7 @@ Windows true - muparserxd.lib;version.lib;Shlwapi.lib;authz.lib;libsndfile-1.lib;libfftw3f-3.lib;%(AdditionalDependencies) + muparserxd.lib;version.lib;Shlwapi.lib;authz.lib;crypt32.lib;dbghelp.lib;libsndfile-1.lib;libfftw3f-3.lib;%(AdditionalDependencies) @@ -127,7 +127,7 @@ true true true - muparserx.lib;version.lib;Shlwapi.lib;authz.lib;libsndfile-1.lib;libfftw3f-3.lib;%(AdditionalDependencies) + muparserx.lib;version.lib;Shlwapi.lib;authz.lib;crypt32.lib;dbghelp.lib;libsndfile-1.lib;libfftw3f-3.lib;%(AdditionalDependencies) @@ -145,7 +145,7 @@ true true true - muparserx.lib;version.lib;Shlwapi.lib;authz.lib;libsndfile-1.lib;libfftw3f-3.lib;%(AdditionalDependencies) + muparserx.lib;version.lib;Shlwapi.lib;authz.lib;crypt32.lib;dbghelp.lib;libsndfile-1.lib;libfftw3f-3.lib;%(AdditionalDependencies) @@ -175,6 +175,9 @@ + + + @@ -183,6 +186,8 @@ + + @@ -220,12 +225,17 @@ + + + + + @@ -243,6 +253,9 @@ + + + diff --git a/VoicemeeterClient/VoicemeeterClient.vcxproj.filters b/VoicemeeterClient/VoicemeeterClient.vcxproj.filters index f51dde6..8f26228 100644 --- a/VoicemeeterClient/VoicemeeterClient.vcxproj.filters +++ b/VoicemeeterClient/VoicemeeterClient.vcxproj.filters @@ -127,6 +127,21 @@ helpers + + helpers + + + filters + + + filters + + + helpers + + + helpers + @@ -232,8 +247,26 @@ + + helpers + + + filters + + + filters + + + helpers + + + helpers + + + + \ No newline at end of file diff --git a/filters/VSTPluginFilter.cpp b/filters/VSTPluginFilter.cpp new file mode 100644 index 0000000..158e331 --- /dev/null +++ b/filters/VSTPluginFilter.cpp @@ -0,0 +1,234 @@ +/* + This file is part of Equalizer APO, a system-wide equalizer. + Copyright (C) 2017 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "stdafx.h" +#include "helpers/StringHelper.h" +#include "helpers/LogHelper.h" +#include "VSTPluginFilter.h" + +using namespace std; + +VSTPluginFilter::VSTPluginFilter(std::shared_ptr library, std::wstring chunkData, std::unordered_map paramMap) + : library(library), chunkData(chunkData), paramMap(paramMap) +{ + libPath = library->getLibPath(); +} + +VSTPluginFilter::~VSTPluginFilter() +{ + cleanup(); +} + +std::vector VSTPluginFilter::initialize(float sampleRate, unsigned maxFrameCount, std::vector channelNames) +{ + cleanup(); + + channelCount = channelNames.size(); + if (channelCount == 0) + return channelNames; + + skipProcessing = false; + + void* mem = MemoryHelper::alloc(sizeof(VSTPluginInstance)); + VSTPluginInstance* firstEffect = new(mem) VSTPluginInstance(library, 2); + if (!firstEffect->initialize()) + { + LogF(L"The VST plugin %s crashed during initialization.", libPath.c_str()); + skipProcessing = true; + } + + effectChannelCount = max(firstEffect->numInputs(), firstEffect->numOutputs()); + // round up + effectCount = (channelCount + (effectChannelCount - 1)) / effectChannelCount; + effects = (VSTPluginInstance**)MemoryHelper::alloc(effectCount * sizeof(VSTPluginInstance*)); + effects[0] = firstEffect; + for (unsigned i = 1; i < effectCount; i++) + { + mem = MemoryHelper::alloc(sizeof(VSTPluginInstance)); + effects[i] = new(mem) VSTPluginInstance(library, 2); + if (!effects[i]->initialize() && !skipProcessing) + { + LogF(L"The VST plugin %s crashed during initialization.", libPath.c_str()); + skipProcessing = true; + } + } + + prepareForProcessing(sampleRate, maxFrameCount); + + // 2 times for input and output + emptyChannelCount = 2 * (effectCount * effectChannelCount - channelCount); + emptyChannels = (float**)MemoryHelper::alloc(emptyChannelCount * sizeof(float*)); + for (unsigned i = 0; i < emptyChannelCount; i++) + { + emptyChannels[i] = (float*)MemoryHelper::alloc(maxFrameCount * sizeof(float)); + memset(emptyChannels[i], 0, maxFrameCount * sizeof(float)); + } + + inputArray = (float**)MemoryHelper::alloc(firstEffect->numInputs() * sizeof(float*)); + outputArray = (float**)MemoryHelper::alloc(firstEffect->numOutputs() * sizeof(float*)); + + return channelNames; +} + +void VSTPluginFilter::prepareForProcessing(float sampleRate, unsigned maxFrameCount) +{ + __try + { + for (unsigned i = 0; i < effectCount; i++) + { + VSTPluginInstance* effect = effects[i]; + + if (i == effectCount - 1 && (channelCount % effectChannelCount) != 0) + effect->setUsedChannelCount(channelCount % effectChannelCount); + else + effect->setUsedChannelCount(effectChannelCount); + effect->prepareForProcessing(sampleRate, maxFrameCount); + effect->writeToEffect(chunkData, paramMap); + effect->startProcessing(); + } + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + LogF(L"The VST plugin %s crashed while preparing for processing.", libPath.c_str()); + skipProcessing = true; + } +} + +#pragma AVRT_CODE_BEGIN +void VSTPluginFilter::process(float** output, float** input, unsigned frameCount) +{ + if (skipProcessing) + { + for (unsigned i = 0; i < channelCount; i++) + memcpy(output[i], input[i], frameCount * sizeof(float)); + return; + } + + __try + { + unsigned channelOffset = 0; + unsigned emptyChannelIndex = 0; + for (unsigned i = 0; i < effectCount; i++) + { + VSTPluginInstance* effect = effects[i]; + for (int j = 0; j < effect->numInputs(); j++) + { + if (channelOffset + j < channelCount) + inputArray[j] = input[channelOffset + j]; + else + inputArray[j] = emptyChannels[emptyChannelIndex++]; + } + + for (int j = 0; j < effect->numOutputs(); j++) + { + if (channelOffset + j < channelCount) + outputArray[j] = output[channelOffset + j]; + else + outputArray[j] = emptyChannels[emptyChannelIndex++]; + } + + if (effect->canReplacing()) + { + effect->processReplacing(inputArray, outputArray, frameCount); + } + else + { + for (int j = 0; j < effect->numOutputs(); j++) + memset(outputArray[j], 0, frameCount * sizeof(float)); + effect->process(inputArray, outputArray, frameCount); + } + + if (effect->numOutputs() < effect->numInputs()) + { + for (int j = effect->numOutputs(); j < effect->numInputs(); j++) + { + if (channelOffset + j < channelCount) + memset(output[channelOffset + j], 0, frameCount * sizeof(float)); + } + } + + channelOffset += effectChannelCount; + } + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + if (reportCrash) + { + LogF(L"The VST plugin %s crashed during audio processing.", libPath.c_str()); + reportCrash = false; + } + + for (unsigned i = 0; i < channelCount; i++) + memcpy(output[i], input[i], frameCount * sizeof(float)); + } +} +#pragma AVRT_CODE_END + +std::shared_ptr VSTPluginFilter::getLibrary() const +{ + return library; +} + +std::wstring VSTPluginFilter::getChunkData() const +{ + return chunkData; +} + +std::unordered_map VSTPluginFilter::getParamMap() const +{ + return paramMap; +} + +void VSTPluginFilter::cleanup() +{ + if (effects != NULL) + { + for (unsigned i = 0; i < effectCount; i++) + { + VSTPluginInstance* effect = effects[i]; + effect->stopProcessing(); + effect->~VSTPluginInstance(); + MemoryHelper::free(effect); + } + MemoryHelper::free(effects); + effects = NULL; + } + effectCount = 0; + + if (emptyChannels != NULL) + { + for (unsigned i = 0; i < emptyChannelCount; i++) + MemoryHelper::free(emptyChannels[i]); + MemoryHelper::free(emptyChannels); + emptyChannels = NULL; + } + emptyChannelCount = 0; + + if (inputArray != NULL) + { + MemoryHelper::free(inputArray); + inputArray = NULL; + } + + if (outputArray != NULL) + { + MemoryHelper::free(outputArray); + outputArray = NULL; + } +} diff --git a/filters/VSTPluginFilter.h b/filters/VSTPluginFilter.h new file mode 100644 index 0000000..4f245c2 --- /dev/null +++ b/filters/VSTPluginFilter.h @@ -0,0 +1,59 @@ +/* + This file is part of Equalizer APO, a system-wide equalizer. + Copyright (C) 2017 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "IFilter.h" +#include "helpers/VSTPluginLibrary.h" + +#pragma AVRT_VTABLES_BEGIN +class VSTPluginFilter : public IFilter +{ +public: + VSTPluginFilter(std::shared_ptr library, std::wstring chunkData, std::unordered_map paramMap); + ~VSTPluginFilter(); + + bool getInPlace() override {return false;} + std::vector initialize(float sampleRate, unsigned maxFrameCount, std::vector channelNames) override; + void prepareForProcessing(float sampleRate, unsigned maxFrameCount); + void process(float** output, float** input, unsigned frameCount) override; + + std::shared_ptr getLibrary() const; + std::wstring getChunkData() const; + std::unordered_map getParamMap() const; + +private: + void cleanup(); + + std::shared_ptr library; + std::wstring libPath; + std::wstring chunkData; + std::unordered_map paramMap; + size_t channelCount; + unsigned effectChannelCount; + size_t effectCount = 0; + VSTPluginInstance** effects = NULL; + size_t emptyChannelCount = 0; + float** emptyChannels = NULL; + float** inputArray = NULL; + float** outputArray = NULL; + bool skipProcessing = false; + bool reportCrash = true; +}; +#pragma AVRT_VTABLES_END diff --git a/filters/VSTPluginFilterFactory.cpp b/filters/VSTPluginFilterFactory.cpp new file mode 100644 index 0000000..6691234 --- /dev/null +++ b/filters/VSTPluginFilterFactory.cpp @@ -0,0 +1,116 @@ +/* + This file is part of Equalizer APO, a system-wide equalizer. + Copyright (C) 2017 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "stdafx.h" +#include "helpers/StringHelper.h" +#include "helpers/VSTPluginLibrary.h" +#include "helpers/LogHelper.h" +#include "VSTPluginFilter.h" +#include "VSTPluginFilterFactory.h" + +using namespace std; + +vector VSTPluginFilterFactory::createFilter(const wstring& configPath, wstring& command, wstring& parameters) +{ + VSTPluginFilter* filter = NULL; + + if (command == L"VSTPlugin") + { + shared_ptr library; + wstring chunkData; + unordered_map paramMap; + vector parts = StringHelper::splitQuoted(parameters, ' '); + for (unsigned i = 0; i + 1 < parts.size(); i += 2) + { + wstring key = parts[i]; + wstring value = parts[i + 1]; + + if (key == L"Library") + { + wstring libPath; + if (PathIsRelativeW(value.c_str())) + { + wchar_t filePath[MAX_PATH]; + wstring pluginPath = VSTPluginLibrary::getDefaultPluginPath(); + pluginPath._Copy_s(filePath, sizeof(filePath) / sizeof(wchar_t), MAX_PATH); + if (pluginPath.size() < MAX_PATH) + filePath[pluginPath.size()] = L'\0'; + else + filePath[MAX_PATH - 1] = L'\0'; + PathAppendW(filePath, value.c_str()); + libPath = filePath; + } + else + libPath = value; + + library = VSTPluginLibrary::getInstance(libPath); + } + else if (key == L"ChunkData") + { + chunkData = value; + } + else + { + float f = wcstof(value.c_str(), NULL); + paramMap[key] = f; + } + } + + if (library != NULL) + { + bool create = true; + if (configPath != L"") + { + create = false; + TraceF(L"Adding VST plugin %s", library->getLibPath().c_str()); + int res = library->initialize(); + if (res < 0) + { +#ifdef _WIN64 + int bitDepth = 64; +#else + int bitDepth = 32; +#endif + if (res == AbstractLibrary::FILE_NOT_FOUND) + LogF(L"File %s not found", library->getLibPath()); + else if (res == AbstractLibrary::LOADING_FAILED) + LogF(L"Library %s could not be loaded", library->getLibPath()); + else if (res == AbstractLibrary::FUNCTIONS_MISSING) + LogF(L"Library %s does not contain needed functions", library->getLibPath()); + else if (res == AbstractLibrary::WRONG_ARCHITECTURE) + LogF(L"Library %s has wrong architecture, must be %d-bit", library->getLibPath(), bitDepth); + } + else + { + create = true; + } + } + + if (create) + { + void* mem = MemoryHelper::alloc(sizeof(VSTPluginFilter)); + filter = new(mem) VSTPluginFilter(library, chunkData, paramMap); + } + } + } + + if (filter == NULL) + return vector(0); + return vector(1, filter); +} diff --git a/filters/VSTPluginFilterFactory.h b/filters/VSTPluginFilterFactory.h new file mode 100644 index 0000000..4011fab --- /dev/null +++ b/filters/VSTPluginFilterFactory.h @@ -0,0 +1,31 @@ +/* + This file is part of Equalizer APO, a system-wide equalizer. + Copyright (C) 2017 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include + +#include "IFilterFactory.h" +#include "IFilter.h" + +class VSTPluginFilterFactory : public IFilterFactory +{ +public: + std::vector createFilter(const std::wstring& configPath, std::wstring& command, std::wstring& parameters) override; +}; diff --git a/helpers/AbstractLibrary.cpp b/helpers/AbstractLibrary.cpp new file mode 100644 index 0000000..5012380 --- /dev/null +++ b/helpers/AbstractLibrary.cpp @@ -0,0 +1,113 @@ +/* + This file is part of Equalizer APO, a system-wide equalizer. + Copyright (C) 2017 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "stdafx.h" +#include +#include "AbstractLibrary.h" +#include "helpers/LogHelper.h" + +using namespace std; + +AbstractLibrary::~AbstractLibrary() +{ + if (module != NULL) + { + wchar_t path[MAX_PATH]; + GetModuleFileNameW(module, path, MAX_PATH); + + FreeLibrary(module); + module = NULL; + + TraceF(L"Unloaded library %s", path); + } +} + +int AbstractLibrary::initialize() +{ + if (module == NULL) + { + wstring libPath = getLibPath(); + if (GetFileAttributesW(libPath.c_str()) == INVALID_FILE_ATTRIBUTES) + return FILE_NOT_FOUND; + module = LoadLibraryW(libPath.c_str()); + if (module == NULL) + { + unsigned short arch = getFileArchitecture(libPath); +#ifdef _WIN64 + int bitDepth = 64; +#else + int bitDepth = 32; +#endif + if (arch != 0 && (bitDepth == 64 && arch != IMAGE_FILE_MACHINE_AMD64 + || bitDepth == 32 && arch != IMAGE_FILE_MACHINE_I386)) + return WRONG_ARCHITECTURE; + + return LOADING_FAILED; + } + + if (!loadFunctions()) + { + FreeLibrary(module); + module = NULL; + return FUNCTIONS_MISSING; + } + + int res = customInitialize(); + if (res < 0) + return res; + + TraceF(L"Loaded library %s", libPath.c_str()); + + return 1; + } + + return 0; +} + +int AbstractLibrary::customInitialize() +{ + // overwrite if needed + return 0; +} + +unsigned short AbstractLibrary::getFileArchitecture(const wstring& filePath) +{ + unsigned short result = 0; + + HANDLE hFile = CreateFileW(filePath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile != INVALID_HANDLE_VALUE) + { + HANDLE hMap = CreateFileMappingW(hFile, NULL, PAGE_READONLY, 0, 0, NULL); + if (hMap != NULL) + { + void* mapAddr = MapViewOfFileEx(hMap, FILE_MAP_READ, 0, 0, 0, NULL); + if (mapAddr != NULL) + { + PIMAGE_NT_HEADERS ntHeaders = ImageNtHeader(mapAddr); + if (ntHeaders != NULL) + result = ntHeaders->FileHeader.Machine; + UnmapViewOfFile(mapAddr); + } + CloseHandle(hMap); + } + CloseHandle(hFile); + } + + return result; +} diff --git a/helpers/AbstractLibrary.h b/helpers/AbstractLibrary.h new file mode 100644 index 0000000..2630453 --- /dev/null +++ b/helpers/AbstractLibrary.h @@ -0,0 +1,45 @@ +/* + This file is part of Equalizer APO, a system-wide equalizer. + Copyright (C) 2017 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include + +class AbstractLibrary +{ +public: + static const int FILE_NOT_FOUND = -1; + static const int LOADING_FAILED = -2; + static const int FUNCTIONS_MISSING = -3; + static const int WRONG_ARCHITECTURE = -4; + + virtual ~AbstractLibrary(); + + int initialize(); + virtual std::wstring getLibPath() = 0; + +protected: + virtual bool loadFunctions() = 0; + virtual int customInitialize(); + + HMODULE module = NULL; + +private: + unsigned short getFileArchitecture(const std::wstring& filePath); +}; diff --git a/helpers/GainIterator.cpp b/helpers/GainIterator.cpp index 40d5c03..6a5ca74 100644 --- a/helpers/GainIterator.cpp +++ b/helpers/GainIterator.cpp @@ -85,7 +85,11 @@ double GainIterator::gainAt(double freq) else { double t = (log(freq) - logLeft) / logRightMinusLeft; - dbGain = nodeLeft->dbGain + t * (nodeRight->dbGain - nodeLeft->dbGain); + // to support dbGain == -INF for both nodes + if (nodeLeft->dbGain == nodeRight->dbGain) + dbGain = nodeLeft->dbGain; + else + dbGain = nodeLeft->dbGain + t * (nodeRight->dbGain - nodeLeft->dbGain); } return dbGain; diff --git a/helpers/LogHelper.cpp b/helpers/LogHelper.cpp index 9a8b013..b01e934 100644 --- a/helpers/LogHelper.cpp +++ b/helpers/LogHelper.cpp @@ -109,6 +109,8 @@ void LogHelper::log(const char* file, int line, const void* caller, bool trace, if (presetFP == NULL) fclose(fp); + else + fflush(fp); } void LogHelper::reset() diff --git a/helpers/StringHelper.cpp b/helpers/StringHelper.cpp index 439b333..ee52e01 100644 --- a/helpers/StringHelper.cpp +++ b/helpers/StringHelper.cpp @@ -163,3 +163,37 @@ wstring StringHelper::getSystemErrorString(long status) else return L""; } + +vector StringHelper::splitQuoted(const wstring& s, wchar_t splitChar, wchar_t quoteChar) +{ + vector result; + bool inQuotes = false; + wstring current; + for (size_t i = 0; i < s.length(); i++) + { + wchar_t c = s[i]; + if (c == splitChar && !inQuotes) + { + if (current != L"") + { + result.push_back(current); + current = L""; + } + } + else if (c == quoteChar) + { + inQuotes = !inQuotes; + if (inQuotes && i > 0 && s[i - 1] == quoteChar) + current += quoteChar; + } + else + { + current += c; + } + } + + if (current != L"") + result.push_back(current); + + return result; +} diff --git a/helpers/StringHelper.h b/helpers/StringHelper.h index 374f2c0..929ad0e 100644 --- a/helpers/StringHelper.h +++ b/helpers/StringHelper.h @@ -35,4 +35,5 @@ public: static std::vector split(const std::wstring& s, wchar_t splitChar, bool skipEmpty = true); static std::wstring join(const std::vector& strings, const std::wstring& separator); static std::wstring getSystemErrorString(long status); + static std::vector splitQuoted(const std::wstring& s, wchar_t splitChar, wchar_t quoteChar = '"'); }; diff --git a/helpers/VSTPluginInstance.cpp b/helpers/VSTPluginInstance.cpp new file mode 100644 index 0000000..e6a9366 --- /dev/null +++ b/helpers/VSTPluginInstance.cpp @@ -0,0 +1,397 @@ +/* + This file is part of Equalizer APO, a system-wide equalizer. + Copyright (C) 2017 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "stdafx.h" +#include +#include "StringHelper.h" +#include "version.h" +#include "VSTPluginLibrary.h" +#include "VSTPluginInstance.h" + +using namespace std; + +#define equalizerApoVSTID CCONST('E', 'A', 'P', 'O'); + +static intptr_t callback(AEffect* effect, int32_t opcode, int32_t index, intptr_t value, void* ptr, float opt) +{ + VSTPluginInstance* instance = effect != NULL ? (VSTPluginInstance*)effect->user : NULL; +#ifdef _DEBUG + printf("vst: %p opcode: %d index: %d value: %d ptr: %p opt: %f user: %p\n", + effect, opcode, index, value, ptr, opt, effect != NULL ? effect->user : NULL); + fflush(stdout); +#endif + + switch (opcode) + { + case audioMasterVersion: + return 2400; + + case audioMasterCurrentId: + return equalizerApoVSTID; + + case audioMasterGetProductString: + strcpy_s((char*) ptr, 64, "Equalizer APO"); + return 1; + + case audioMasterGetVendorVersion: + return (intptr_t) (MAJOR << 24 | MINOR << 16 | REVISION << 8 | 0); + + case audioMasterPinConnected: + if (instance != NULL) + return index < instance->getUsedChannelCount() ? 0 : 1; + else + return 0; + + case audioMasterNeedIdle: + return effect->dispatcher(effect, effIdle, 0, 0, NULL, 0.0f); + + case audioMasterUpdateDisplay: + return effect->dispatcher(effect, effEditIdle, 0, 0, NULL, 0.0f); + + case audioMasterGetTime: + return 0; + + case audioMasterGetSampleRate: + if (instance != NULL) + return (intptr_t)instance->getSampleRate(); + return 0; + + case audioMasterGetCurrentProcessLevel: + if (instance != NULL) + return instance->getProcessLevel(); + return 0; + + case audioMasterGetLanguage: + if (instance != NULL) + return instance->getLanguage(); + return 0; + + case audioMasterWillReplaceOrAccumulate: + return 1; + + case audioMasterSizeWindow: + if (instance != NULL) + instance->onSizeWindow((int)index, (int)value); + return 1; + + case audioMasterCanDo: + { + char* s = (char*)ptr; +#ifdef _DEBUG + printf("VST canDo: %s\n", s); + fflush(stdout); +#endif + if (strcmp(s, "startStopProcess") == 0 || + strcmp(s, "sizeWindow") == 0) + return 1; + } + return 0; + + case audioMasterAutomate: + if (instance != NULL) + instance->onAutomate(); + return 0; + + case audioMasterBeginEdit: + case audioMasterEndEdit: + case audioMasterIdle: + case audioMasterWantMidi: + return 0; + } + + return 0; +} + +VSTPluginInstance::VSTPluginInstance(const std::shared_ptr& library, int processLevel) + : library(library), processLevel(processLevel) +{ +} + +VSTPluginInstance::~VSTPluginInstance() +{ + if (effect != NULL) + { + effect->dispatcher(effect, effClose, 0, 0, NULL, 0.0f); + effect = NULL; + } +} + +bool VSTPluginInstance::initialize() +{ + bool result = true; + + __try + { + effect = library->VSTPluginMain(callback); + effect->user = this; + if (effect->magic == kEffectMagic) + { + effect->dispatcher(effect, effOpen, 0, 0, NULL, 0.0f); + + usedChannelCount = max(numInputs(), numOutputs()); + } + else + { + effect = NULL; + } + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + result = false; + } + + return result; +} + +int VSTPluginInstance::numInputs() const +{ + if (effect == NULL) + return 0; + + return effect->numInputs; +} + +int VSTPluginInstance::numOutputs() const +{ + if (effect == NULL) + return 0; + + return effect->numOutputs; +} + +bool VSTPluginInstance::canReplacing() const +{ + if (effect == NULL) + return true; + + return (effect->flags & effFlagsCanReplacing) != 0; +} + +int VSTPluginInstance::uniqueID() const +{ + if (effect == NULL) + return 0; + + return effect->uniqueID; +} + +std::wstring VSTPluginInstance::getName() const +{ + if (effect == NULL) + return L""; + + char buf[256]; + memset(buf, 0, sizeof(buf)); + effect->dispatcher(effect, effGetEffectName, 0, 0, buf, 0.0f); + + return StringHelper::toWString(buf, CP_UTF8); +} + +int VSTPluginInstance::getUsedChannelCount() const +{ + return usedChannelCount; +} + +void VSTPluginInstance::setUsedChannelCount(int count) +{ + usedChannelCount = count; +} + +float VSTPluginInstance::getSampleRate() const +{ + return sampleRate; +} + +int VSTPluginInstance::getProcessLevel() const +{ + return processLevel; +} + +void VSTPluginInstance::setProcessLevel(int value) +{ + processLevel = value; +} + +int VSTPluginInstance::getLanguage() const +{ + return language; +} + +void VSTPluginInstance::setLanguage(int value) +{ + language = value; +} + +void VSTPluginInstance::prepareForProcessing(float sampleRate, int blockSize) +{ + if (effect == NULL) + return; + + this->sampleRate = sampleRate; + effect->dispatcher(effect, effSetSampleRate, 0, 0, NULL, sampleRate); + effect->dispatcher(effect, effSetBlockSize, 0, blockSize, NULL, 0.0f); +} + +void VSTPluginInstance::writeToEffect(const std::wstring& chunkData, const std::unordered_map& paramMap) +{ + if (effect == NULL) + return; + + if (effect->flags & effFlagsProgramChunks) + { + if (chunkData != L"") + { + DWORD bufSize = 0; + CryptStringToBinaryW(chunkData.c_str(), 0, CRYPT_STRING_BASE64, NULL, &bufSize, NULL, NULL); + BYTE* buf = new BYTE[bufSize]; + if (CryptStringToBinaryW(chunkData.c_str(), 0, CRYPT_STRING_BASE64, buf, &bufSize, NULL, NULL) == TRUE) + effect->dispatcher(effect, effSetChunk, 1, bufSize, buf, 0.0f); + delete[]buf; + } + } + else + { + for (int i = 0; i < effect->numParams; i++) + { + char buf[256]; + effect->dispatcher(effect, effGetParamName, i, 0, buf, 0.0f); + wstring name = StringHelper::toWString(buf, CP_UTF8); + auto it = paramMap.find(name); + if (it != paramMap.end()) + effect->setParameter(effect, i, it->second); + } + } +} + +void VSTPluginInstance::readFromEffect(std::wstring& chunkData, std::unordered_map& paramMap) const +{ + if (effect == NULL) + return; + + chunkData = L""; + paramMap.clear(); + + if (effect->flags & effFlagsProgramChunks) + { + BYTE* chunk = NULL; + int size = (int)effect->dispatcher(effect, effGetChunk, 1, 0, &chunk, 0.0f); + DWORD stringLength; + CryptBinaryToStringW(chunk, size, CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, NULL, &stringLength); + wchar_t* string = new wchar_t[stringLength]; + if (CryptBinaryToStringW(chunk, size, CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, string, &stringLength) == TRUE) + chunkData = string; + delete[]string; + } + else + { + for (int i = 0; i < effect->numParams; i++) + { + char buf[256]; + effect->dispatcher(effect, effGetParamName, i, 0, buf, 0.0f); + float value = effect->getParameter(effect, i); + paramMap[StringHelper::toWString(buf, CP_UTF8)] = value; + } + } +} + +void VSTPluginInstance::startProcessing() +{ + if (effect == NULL) + return; + + effect->dispatcher(effect, effMainsChanged, 0, 1, NULL, 0.0f); + effect->dispatcher(effect, effStartProcess, 0, 0, NULL, 0.0f); +} + +void VSTPluginInstance::processReplacing(float** inputArray, float** outputArray, int frameCount) +{ + if (effect == NULL) + return; + + effect->processReplacing(effect, inputArray, outputArray, frameCount); +} + +void VSTPluginInstance::process(float** inputArray, float** outputArray, int frameCount) +{ + if (effect == NULL) + return; + + effect->process(effect, inputArray, outputArray, frameCount); +} + +void VSTPluginInstance::stopProcessing() +{ + if (effect == NULL) + return; + + effect->dispatcher(effect, effStopProcess, 0, 0, NULL, 0.0f); + effect->dispatcher(effect, effMainsChanged, 0, 0, NULL, 0.0f); +} + +void VSTPluginInstance::startEditing(HWND hWnd, short* width, short* height) +{ + if (effect == NULL) + return; + + VstRect* rect; + effect->dispatcher(effect, effEditGetRect, 0, 0, &rect, 0.0f); + effect->dispatcher(effect, effEditOpen, 0, 0, hWnd, 0.0f); + effect->dispatcher(effect, effEditGetRect, 0, 0, &rect, 0.0f); + + *width = rect->right - rect->left; + *height = rect->bottom - rect->top; +} + +void VSTPluginInstance::doIdle() +{ + if (effect == NULL) + return; + + effect->dispatcher(effect, effEditIdle, 0, 0, NULL, 0.0f); +} + +void VSTPluginInstance::stopEditing() +{ + if (effect == NULL) + return; + + effect->dispatcher(effect, effEditClose, 0, 0, NULL, 0.0f); +} + +void VSTPluginInstance::setAutomateFunc(std::function func) +{ + automateFunc = func; +} + +void VSTPluginInstance::onAutomate() +{ + if (automateFunc) + automateFunc(); +} + +void VSTPluginInstance::setSizeWindowFunc(std::function func) +{ + sizeWindowFunc = func; +} + +void VSTPluginInstance::onSizeWindow(int w, int h) +{ + if (sizeWindowFunc) + sizeWindowFunc(w, h); +} diff --git a/helpers/VSTPluginInstance.h b/helpers/VSTPluginInstance.h new file mode 100644 index 0000000..720863e --- /dev/null +++ b/helpers/VSTPluginInstance.h @@ -0,0 +1,78 @@ +/* + This file is part of Equalizer APO, a system-wide equalizer. + Copyright (C) 2017 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include +#include +#include +#include "aeffectx.h" + +class VSTPluginLibrary; + +class VSTPluginInstance +{ +public: + VSTPluginInstance(const std::shared_ptr& library, int processLevel); + ~VSTPluginInstance(); + + bool initialize(); + + int numInputs() const; + int numOutputs() const; + bool canReplacing() const; + int uniqueID() const; + std::wstring getName() const; + int getUsedChannelCount() const; + void setUsedChannelCount(int count); + float getSampleRate() const; + int getProcessLevel() const; + void setProcessLevel(int value); + int getLanguage() const; + void setLanguage(int value); + + void prepareForProcessing(float sampleRate, int blockSize); + void writeToEffect(const std::wstring& chunkData, const std::unordered_map& paramMap); + void readFromEffect(std::wstring& chunkData, std::unordered_map& paramMap) const; + + void startProcessing(); + void processReplacing(float** inputArray, float** outputArray, int frameCount); + void process(float** inputArray, float** outputArray, int frameCount); + void stopProcessing(); + + void startEditing(HWND hWnd, short* width, short* height); + void doIdle(); + void stopEditing(); + + void setAutomateFunc(std::function func); + void onAutomate(); + + void setSizeWindowFunc(std::function func); + void onSizeWindow(int w, int h); + +private: + std::shared_ptr library; + AEffect* effect; + std::function automateFunc; + std::function sizeWindowFunc; + float sampleRate = 0.0f; + int usedChannelCount; + int processLevel = 0; + int language = 1; +}; diff --git a/helpers/VSTPluginLibrary.cpp b/helpers/VSTPluginLibrary.cpp new file mode 100644 index 0000000..d630f63 --- /dev/null +++ b/helpers/VSTPluginLibrary.cpp @@ -0,0 +1,76 @@ +/* + This file is part of Equalizer APO, a system-wide equalizer. + Copyright (C) 2017 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "stdafx.h" +#include "RegistryHelper.h" +#include "LogHelper.h" +#include "VSTPluginLibrary.h" + +using namespace std; + +std::unordered_map > VSTPluginLibrary::instanceMap; +std::wstring VSTPluginLibrary::defaultPluginPath; + +std::shared_ptr VSTPluginLibrary::getInstance(const wstring& libPath) +{ + shared_ptr ptr; + + auto it = instanceMap.find(libPath); + if (it != instanceMap.end()) + { + weak_ptr instance = it->second; + ptr = instance.lock(); + } + + if (ptr == NULL) + { + ptr = shared_ptr(new VSTPluginLibrary(libPath)); + instanceMap[libPath] = ptr; + } + + return ptr; +} + +wstring VSTPluginLibrary::getDefaultPluginPath() +{ + if (defaultPluginPath == L"") + { + wstring installPath = RegistryHelper::readValue(APP_REGPATH, L"InstallPath"); + defaultPluginPath = installPath + L"\\plugins"; + } + + return defaultPluginPath; +} + +std::wstring VSTPluginLibrary::getLibPath() +{ + return libPath; +} + +bool VSTPluginLibrary::loadFunctions() +{ + VSTPluginMain = (vstPluginMain)GetProcAddress(module, "VSTPluginMain"); + + return VSTPluginMain != NULL; +} + +VSTPluginLibrary::VSTPluginLibrary(const wstring& libPath) + : libPath(libPath) +{ +} diff --git a/helpers/VSTPluginLibrary.h b/helpers/VSTPluginLibrary.h new file mode 100644 index 0000000..1c332ba --- /dev/null +++ b/helpers/VSTPluginLibrary.h @@ -0,0 +1,49 @@ +/* + This file is part of Equalizer APO, a system-wide equalizer. + Copyright (C) 2017 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include +#include +#include +#include "AbstractLibrary.h" +#include "aeffectx.h" +#include +#include "VSTPluginInstance.h" + +class VSTPluginLibrary : public AbstractLibrary +{ +public: + static std::shared_ptr getInstance(const std::wstring& libPath); + static std::wstring getDefaultPluginPath(); + + std::wstring getLibPath() override; + + typedef AEffect* (* vstPluginMain)(audioMasterCallback audioMaster); + vstPluginMain VSTPluginMain; + +protected: + bool loadFunctions() override; + +private: + VSTPluginLibrary(const std::wstring& libPath); + static std::unordered_map > instanceMap; + static std::wstring defaultPluginPath; + std::wstring libPath; +}; diff --git a/helpers/aeffectx.h b/helpers/aeffectx.h new file mode 100644 index 0000000..c9e5a20 --- /dev/null +++ b/helpers/aeffectx.h @@ -0,0 +1,391 @@ +/* + * aeffectx.h - simple header to allow VeSTige compilation and eventually work + * + * Copyright (c) 2006 Javier Serrano Polo + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + + +#ifndef _AEFFECTX_H +#define _AEFFECTX_H + +#define CCONST(a, b, c, d)( ( ( (int) a ) << 24 ) | \ + ( ( (int) b ) << 16 ) | \ + ( ( (int) c ) << 8 ) | \ + ( ( (int) d ) << 0 ) ) + +const int audioMasterAutomate = 0; +const int audioMasterVersion = 1; +const int audioMasterCurrentId = 2; +const int audioMasterIdle = 3; +const int audioMasterPinConnected = 4; +// unsupported? 5 +const int audioMasterWantMidi = 6; +const int audioMasterGetTime = 7; +const int audioMasterProcessEvents = 8; +const int audioMasterSetTime = 9; +const int audioMasterTempoAt = 10; +const int audioMasterGetNumAutomatableParameters = 11; +const int audioMasterGetParameterQuantization = 12; +const int audioMasterIOChanged = 13; +const int audioMasterNeedIdle = 14; +const int audioMasterSizeWindow = 15; +const int audioMasterGetSampleRate = 16; +const int audioMasterGetBlockSize = 17; +const int audioMasterGetInputLatency = 18; +const int audioMasterGetOutputLatency = 19; +const int audioMasterGetPreviousPlug = 20; +const int audioMasterGetNextPlug = 21; +const int audioMasterWillReplaceOrAccumulate = 22; +const int audioMasterGetCurrentProcessLevel = 23; +const int audioMasterGetAutomationState = 24; +const int audioMasterOfflineStart = 25; +const int audioMasterOfflineRead = 26; +const int audioMasterOfflineWrite = 27; +const int audioMasterOfflineGetCurrentPass = 28; +const int audioMasterOfflineGetCurrentMetaPass = 29; +const int audioMasterSetOutputSampleRate = 30; +// unsupported? 31 +const int audioMasterGetSpeakerArrangement = 31; // deprecated in 2.4? +const int audioMasterGetVendorString = 32; +const int audioMasterGetProductString = 33; +const int audioMasterGetVendorVersion = 34; +const int audioMasterVendorSpecific = 35; +const int audioMasterSetIcon = 36; +const int audioMasterCanDo = 37; +const int audioMasterGetLanguage = 38; +const int audioMasterOpenWindow = 39; +const int audioMasterCloseWindow = 40; +const int audioMasterGetDirectory = 41; +const int audioMasterUpdateDisplay = 42; +const int audioMasterBeginEdit = 43; +const int audioMasterEndEdit = 44; +const int audioMasterOpenFileSelector = 45; +const int audioMasterCloseFileSelector = 46; // currently unused +const int audioMasterEditFile = 47; // currently unused +const int audioMasterGetChunkFile = 48; // currently unused +const int audioMasterGetInputSpeakerArrangement = 49; // currently unused + +const int effFlagsHasEditor = 1; +const int effFlagsCanReplacing = 1 << 4; // very likely +const int effFlagsProgramChunks = 1 << 5; // from Ardour +const int effFlagsIsSynth = 1 << 8; // currently unused + +const int effOpen = 0; +const int effClose = 1; // currently unused +const int effSetProgram = 2; // currently unused +const int effGetProgram = 3; // currently unused +// The next one was gleaned from http://www.kvraudio.com/forum/viewtopic.php?p=1905347 +const int effSetProgramName = 4; +const int effGetProgramName = 5; // currently unused +// The next two were gleaned from http://www.kvraudio.com/forum/viewtopic.php?p=1905347 +const int effGetParamLabel = 6; +const int effGetParamDisplay = 7; +const int effGetParamName = 8; // currently unused +const int effSetSampleRate = 10; +const int effSetBlockSize = 11; +const int effMainsChanged = 12; +const int effEditGetRect = 13; +const int effEditOpen = 14; +const int effEditClose = 15; +const int effEditIdle = 19; +const int effEditTop = 20; +const int effIdentify = 22; // from http://www.asseca.org/vst-24-specs/efIdentify.html +const int effGetChunk = 23; // from Ardour +const int effSetChunk = 24; // from Ardour +const int effProcessEvents = 25; +// The next one was gleaned from http://www.asseca.org/vst-24-specs/efCanBeAutomated.html +const int effCanBeAutomated = 26; +// The next one was gleaned from http://www.kvraudio.com/forum/viewtopic.php?p=1905347 +const int effGetProgramNameIndexed = 29; +// The next one was gleaned from http://www.asseca.org/vst-24-specs/efGetPlugCategory.html +const int effGetPlugCategory = 35; +const int effGetEffectName = 45; +const int effGetParameterProperties = 56; // missing +const int effGetVendorString = 47; +const int effGetProductString = 48; +const int effGetVendorVersion = 49; +const int effCanDo = 51; // currently unused +// The next one was gleaned from http://www.asseca.org/vst-24-specs/efIdle.html +const int effIdle = 53; +const int effGetVstVersion = 58; // currently unused +// The next one was gleaned from http://www.asseca.org/vst-24-specs/efBeginSetProgram.html +const int effBeginSetProgram = 67; +// The next one was gleaned from http://www.asseca.org/vst-24-specs/efEndSetProgram.html +const int effEndSetProgram = 68; +// The next one was gleaned from http://www.asseca.org/vst-24-specs/efShellGetNextPlugin.html +const int effShellGetNextPlugin = 70; +// The next one was gleaned from http://www.asseca.org/vst-24-specs/efBeginLoadBank.html +const int effBeginLoadBank = 75; +// The next one was gleaned from http://www.asseca.org/vst-24-specs/efBeginLoadProgram.html +const int effBeginLoadProgram = 76; + +// The next two were gleaned from http://www.kvraudio.com/forum/printview.php?t=143587&start=0 +const int effStartProcess = 71; +const int effStopProcess = 72; + +const int kEffectMagic = CCONST( 'V', 's', 't', 'P' ); +const int kVstLangEnglish = 1; +const int kVstMidiType = 1; + +const int kVstNanosValid = 1 << 8; +const int kVstPpqPosValid = 1 << 9; +const int kVstTempoValid = 1 << 10; +const int kVstBarsValid = 1 << 11; +const int kVstCyclePosValid = 1 << 12; +const int kVstTimeSigValid = 1 << 13; +const int kVstSmpteValid = 1 << 14; // from Ardour +const int kVstClockValid = 1 << 15; // from Ardour + +const int kVstTransportPlaying = 1 << 1; +const int kVstTransportCycleActive = 1 << 2; +const int kVstTransportChanged = 1; + + +class RemoteVstPlugin; + + +class VstMidiEvent +{ +public: + // 00 + int type; + // 04 + int byteSize; + // 08 + int deltaFrames; + // 0c? + int flags; + // 10? + int noteLength; + // 14? + int noteOffset; + // 18 + char midiData[4]; + // 1c? + char detune; + // 1d? + char noteOffVelocity; + // 1e? + char reserved1; + // 1f? + char reserved2; + +} ; + + + + +class VstEvent +{ + char dump[sizeof( VstMidiEvent )]; + +} ; + + + + +class VstEvents +{ +public: + // 00 + int numEvents; + // 04 + void *reserved; + // 08 + VstEvent* events[1]; + +} ; + + + + +// Not finished, neither really used +class VstParameterProperties +{ +public: +/* float stepFloat; + char label[64]; + int flags; + int minInteger; + int maxInteger; + int stepInteger; + char shortLabel[8]; + int category; + char categoryLabel[24]; + char empty[128];*/ + + float stepFloat; + float smallStepFloat; + float largeStepFloat; + char label[64]; + unsigned int flags; + unsigned int minInteger; + unsigned int maxInteger; + unsigned int stepInteger; + unsigned int largeStepInteger; + char shortLabel[8]; + unsigned short displayIndex; + unsigned short category; + unsigned short numParametersInCategory; + unsigned short reserved; + char categoryLabel[24]; + char future[16]; + +} ; + + +#include + +class AEffect +{ +public: + // Never use virtual functions!!! + // 00-03 + int magic; + // dispatcher 04-07 + intptr_t (* dispatcher)( AEffect * , int , int , intptr_t, void * , float ); + // process, quite sure 08-0b + void (* process)( AEffect * , float * * , float * * , int ); + // setParameter 0c-0f + void (* setParameter)( AEffect * , int , float ); + // getParameter 10-13 + float (* getParameter)( AEffect * , int ); + // programs 14-17 + int numPrograms; + // Params 18-1b + int numParams; + // Input 1c-1f + int numInputs; + // Output 20-23 + int numOutputs; + // flags 24-27 + int flags; + // Fill somewhere 28-2b + void * ptr1; + void * ptr2; + int initialDelay; + // Zeroes 34-37 38-3b + int empty3a; + int empty3b; + // 1.0f 3c-3f + float unkown_float; + // An object? pointer 40-43 + void *ptr3; + // Zeroes 44-47 + void *user; + // Id 48-4b + int32_t uniqueID; + int32_t version; + // processReplacing 50-53 + void (* processReplacing)( AEffect * , float * * , float * * , int ); + +} ; + + + + +class VstTimeInfo +{ +public: + // 00 + double samplePos; + // 08 + double sampleRate; + // 10 + double nanoSeconds; + // 18 + double ppqPos; + // 20? + double tempo; + // 28 + double barStartPos; + // 30? + double cycleStartPos; + // 38? + double cycleEndPos; + // 40? + int timeSigNumerator; + // 44? + int timeSigDenominator; + // unconfirmed 48 4c 50 + char empty3[4 + 4 + 4]; + // 54 + int flags; + +} ; + + + +typedef intptr_t (* audioMasterCallback)( AEffect * , int32_t, int32_t, intptr_t, void * , float ); + + +// from http://www.asseca.org/vst-24-specs/efGetParameterProperties.html +enum VstParameterFlags +{ + kVstParameterIsSwitch = 1 << 0, // parameter is a switch (on/off) + kVstParameterUsesIntegerMinMax = 1 << 1, // minInteger, maxInteger valid + kVstParameterUsesFloatStep = 1 << 2, // stepFloat, smallStepFloat, largeStepFloat valid + kVstParameterUsesIntStep = 1 << 3, // stepInteger, largeStepInteger valid + kVstParameterSupportsDisplayIndex = 1 << 4, // displayIndex valid + kVstParameterSupportsDisplayCategory = 1 << 5, // category, etc. valid + kVstParameterCanRamp = 1 << 6 // set if parameter value can ramp up/down +}; + +// from http://www.asseca.org/vst-24-specs/efBeginLoadProgram.html +struct VstPatchChunkInfo +{ + int32_t version; // Format Version (should be 1) + int32_t pluginUniqueID; // UniqueID of the plug-in + int32_t pluginVersion; // Plug-in Version + int32_t numElements; // Number of Programs (Bank) or Parameters (Program) + char future[48]; // Reserved for future use +}; + +// from http://www.asseca.org/vst-24-specs/efGetPlugCategory.html +enum VstPlugCategory +{ + kPlugCategUnknown = 0, // 0=Unknown, category not implemented + kPlugCategEffect, // 1=Simple Effect + kPlugCategSynth, // 2=VST Instrument (Synths, samplers,...) + kPlugCategAnalysis, // 3=Scope, Tuner, ... + kPlugCategMastering, // 4=Dynamics, ... + kPlugCategSpacializer, // 5=Panners, ... + kPlugCategRoomFx, // 6=Delays and Reverbs + kPlugSurroundFx, // 7=Dedicated surround processor + kPlugCategRestoration, // 8=Denoiser, ... + kPlugCategOfflineProcess, // 9=Offline Process + kPlugCategShell, // 10=Plug-in is container of other plug-ins @see effShellGetNextPlugin() + kPlugCategGenerator, // 11=ToneGenerator, ... + kPlugCategMaxCount // 12=Marker to count the categories +}; + + + +class VstRect +{ +public: + short top; + short left; + short bottom; + short right; +} ; + +#endif