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 a3db98c..4d98465 100644
Binary files a/Editor/translations/Editor_de.qm and b/Editor/translations/Editor_de.qm differ
diff --git a/Editor/translations/Editor_de.ts b/Editor/translations/Editor_de.ts
index 9acdecb..6e92f57 100644
--- a/Editor/translations/Editor_de.ts
+++ b/Editor/translations/Editor_de.ts
@@ -281,24 +281,24 @@
Faltung 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