2228 lines
74 KiB
C
2228 lines
74 KiB
C
/*
|
|
* Tibia
|
|
*
|
|
* Copyright (C) 2023, 2024 Orastron Srl unipersonale
|
|
*
|
|
* Tibia 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, version 3 of the License.
|
|
*
|
|
* Tibia 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 Tibia. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* File author: Stefano D'Angelo, Paolo Marrone
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
|
|
typedef struct {
|
|
void * handle;
|
|
const char * format;
|
|
const char * (*get_bindir)(void *handle);
|
|
const char * (*get_datadir)(void *handle);
|
|
} plugin_callbacks;
|
|
|
|
typedef struct {
|
|
void * handle;
|
|
const char * format;
|
|
const char * (*get_bindir)(void *handle);
|
|
const char * (*get_datadir)(void *handle);
|
|
void (*set_parameter)(void *handle, size_t index, float value);
|
|
} plugin_ui_callbacks;
|
|
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wpedantic"
|
|
#include "vst3_c_api.h"
|
|
#pragma GCC diagnostic pop
|
|
|
|
#include "data.h"
|
|
#include "plugin.h"
|
|
#ifdef DATA_UI
|
|
# include "plugin_ui.h"
|
|
#endif
|
|
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stddef.h>
|
|
#include <math.h>
|
|
#if defined(_WIN32) || defined(__CYGWIN__)
|
|
# include <windows.h>
|
|
#else
|
|
# include <dlfcn.h>
|
|
#endif
|
|
#ifdef __APPLE__
|
|
# include <CoreFoundation/CoreFoundation.h>
|
|
#endif
|
|
|
|
#if defined(__i386__) || defined(__x86_64__)
|
|
#include <xmmintrin.h>
|
|
#include <pmmintrin.h>
|
|
#endif
|
|
|
|
// COM in C doc:
|
|
// https://github.com/rubberduck-vba/Rubberduck/wiki/COM-in-plain-C
|
|
// https://devblogs.microsoft.com/oldnewthing/20040205-00/?p=40733
|
|
|
|
//#define TIBIA_TRACE
|
|
#ifdef TIBIA_TRACE
|
|
# define TRACE(...) printf(__VA_ARGS__); fflush(stdout);
|
|
#else
|
|
# define TRACE(...) /* do nothing */
|
|
#endif
|
|
|
|
#ifdef DATA_UI
|
|
# ifdef __linux__
|
|
// Why generate the C interface when you can just not give a fuck? Thank you Steinberg!
|
|
|
|
typedef struct Steinberg_ITimerHandlerVtbl
|
|
{
|
|
/* methods derived from "Steinberg_FUnknown": */
|
|
Steinberg_tresult (SMTG_STDMETHODCALLTYPE* queryInterface) (void* thisInterface, const Steinberg_TUID iid, void** obj);
|
|
Steinberg_uint32 (SMTG_STDMETHODCALLTYPE* addRef) (void* thisInterface);
|
|
Steinberg_uint32 (SMTG_STDMETHODCALLTYPE* release) (void* thisInterface);
|
|
|
|
/* methods derived from "Steinberg_ITimerHandler": */
|
|
void (SMTG_STDMETHODCALLTYPE* onTimer) (void* thisInterface);
|
|
} Steinberg_ITimerHandlerVtbl;
|
|
|
|
typedef struct Steinberg_ITimerHandler
|
|
{
|
|
struct Steinberg_ITimerHandlerVtbl* lpVtbl;
|
|
} Steinberg_ITimerHandler;
|
|
|
|
static const Steinberg_TUID Steinberg_ITimerHandler_iid = SMTG_INLINE_UID (0x10BDD94F, 0x41424774, 0x821FAD8F, 0xECA72CA9);
|
|
|
|
typedef struct Steinberg_IEventHandlerVtbl
|
|
{
|
|
/* methods derived from "Steinberg_FUnknown": */
|
|
Steinberg_tresult (SMTG_STDMETHODCALLTYPE* queryInterface) (void* thisInterface, const Steinberg_TUID iid, void** obj);
|
|
Steinberg_uint32 (SMTG_STDMETHODCALLTYPE* addRef) (void* thisInterface);
|
|
Steinberg_uint32 (SMTG_STDMETHODCALLTYPE* release) (void* thisInterface);
|
|
|
|
/* methods derived from "Steinberg_IEventHandler": */
|
|
void (SMTG_STDMETHODCALLTYPE* onFDIsSet) (void* thisInterface, int fd);
|
|
} Steinberg_IEventHandlerVtbl;
|
|
|
|
typedef struct Steinberg_IEventHandler
|
|
{
|
|
struct Steinberg_IEventHandlerVtbl* lpVtbl;
|
|
} Steinberg_IEventHandler;
|
|
|
|
// not used
|
|
//static const Steinberg_TUID Steinberg_IEventHandler_iid = SMTG_INLINE_UID (0x561E65C9, 0x13A0496F, 0x813A2C35, 0x654D7983);
|
|
|
|
typedef struct Steinberg_IRunLoopVtbl
|
|
{
|
|
/* methods derived from "Steinberg_FUnknown": */
|
|
Steinberg_tresult (SMTG_STDMETHODCALLTYPE* queryInterface) (void* thisInterface, const Steinberg_TUID iid, void** obj);
|
|
Steinberg_uint32 (SMTG_STDMETHODCALLTYPE* addRef) (void* thisInterface);
|
|
Steinberg_uint32 (SMTG_STDMETHODCALLTYPE* release) (void* thisInterface);
|
|
|
|
/* methods derived from "Steinberg_IRunLoop": */
|
|
Steinberg_tresult (SMTG_STDMETHODCALLTYPE* registerEventHandler) (void* thisInterface, struct Steinberg_IEventHandler* handler, int fd);
|
|
Steinberg_tresult (SMTG_STDMETHODCALLTYPE* unregisterEventHandler) (void* thisInterface, struct Steinberg_IEventHandler* handler);
|
|
Steinberg_tresult (SMTG_STDMETHODCALLTYPE* registerTimer) (void* thisInterface, struct Steinberg_ITimerHandler* handler, uint64_t milliseconds);
|
|
Steinberg_tresult (SMTG_STDMETHODCALLTYPE* unregisterTimer) (void* thisInterface, struct Steinberg_ITimerHandler* handler);
|
|
} Steinberg_IRunLoopVtbl;
|
|
|
|
typedef struct Steinberg_IRunLoop
|
|
{
|
|
struct Steinberg_IRunLoopVtbl* lpVtbl;
|
|
} Steinberg_IRunLoop;
|
|
|
|
static const Steinberg_TUID Steinberg_IRunLoop_iid = SMTG_INLINE_UID (0x18C35366, 0x97764F1A, 0x9C5B8385, 0x7A871389);
|
|
# endif
|
|
#endif
|
|
|
|
#if defined(__linux__) || defined(__APPLE__)
|
|
static char *x_asprintf(const char * restrict format, ...) {
|
|
va_list args, tmp;
|
|
va_start(args, format);
|
|
va_copy(tmp, args);
|
|
int len = vsnprintf(NULL, 0, format, tmp);
|
|
va_end(tmp);
|
|
char *s = malloc(len + 1);
|
|
if (s != NULL)
|
|
vsprintf(s, format, args);
|
|
va_end(args);
|
|
return s;
|
|
}
|
|
#endif
|
|
|
|
static char *bindir;
|
|
static char *datadir;
|
|
|
|
static const char * get_bindir_cb(void *handle) {
|
|
(void)handle;
|
|
return bindir;
|
|
}
|
|
|
|
static const char * get_datadir_cb(void *handle) {
|
|
(void)handle;
|
|
return datadir;
|
|
}
|
|
|
|
static double clamp(double x, double m, double M) {
|
|
return x < m ? m : (x > M ? M : x);
|
|
}
|
|
|
|
static int parameterGetIndexById(Steinberg_Vst_ParamID id) {
|
|
for (int i = 0; i < DATA_PRODUCT_PARAMETERS_N + 3 * DATA_PRODUCT_BUSES_MIDI_INPUT_N; i++)
|
|
if (parameterInfo[i].id == id)
|
|
return i;
|
|
return -1;
|
|
}
|
|
|
|
static double parameterMap(int i, double v) {
|
|
return parameterData[i].flags & DATA_PARAM_MAP_LOG ? parameterData[i].min * exp(parameterData[i].mapK * v) : parameterData[i].min + (parameterData[i].max - parameterData[i].min) * v;
|
|
}
|
|
|
|
static double parameterUnmap(int i, double v) {
|
|
return parameterData[i].flags & DATA_PARAM_MAP_LOG ? log(v / parameterData[i].min) / parameterData[i].mapK : (v - parameterData[i].min) / (parameterData[i].max - parameterData[i].min);
|
|
}
|
|
|
|
static double parameterAdjust(int i, double v) {
|
|
v = parameterData[i].flags & (DATA_PARAM_BYPASS | DATA_PARAM_TOGGLED) ? (v >= 0.5 ? 1.0 : 0.0)
|
|
: (parameterData[i].flags & DATA_PARAM_INTEGER ? (int32_t)(v + (v >= 0.0 ? 0.5 : -0.5)) : v);
|
|
return clamp(v, parameterData[i].min, parameterData[i].max);
|
|
}
|
|
|
|
typedef struct pluginInstance {
|
|
Steinberg_Vst_IComponentVtbl * vtblIComponent;
|
|
Steinberg_Vst_IAudioProcessorVtbl * vtblIAudioProcessor;
|
|
Steinberg_Vst_IProcessContextRequirementsVtbl * vtblIProcessContextRequirements;
|
|
Steinberg_uint32 refs;
|
|
Steinberg_FUnknown * context;
|
|
plugin p;
|
|
float sampleRate;
|
|
#if DATA_PRODUCT_PARAMETERS_N > 0
|
|
float parameters[DATA_PRODUCT_PARAMETERS_N];
|
|
#endif
|
|
#if DATA_PRODUCT_CHANNELS_AUDIO_INPUT_N > 0
|
|
const float * inputs[DATA_PRODUCT_CHANNELS_AUDIO_INPUT_N];
|
|
#endif
|
|
#if DATA_PRODUCT_CHANNELS_AUDIO_OUTPUT_N > 0
|
|
float * outputs[DATA_PRODUCT_CHANNELS_AUDIO_OUTPUT_N];
|
|
#endif
|
|
#if DATA_PRODUCT_BUSES_AUDIO_INPUT_N > 0
|
|
char inputsActive[DATA_PRODUCT_BUSES_AUDIO_INPUT_N];
|
|
#endif
|
|
#if DATA_PRODUCT_BUSES_AUDIO_OUTPUT_N > 0
|
|
char outputsActive[DATA_PRODUCT_BUSES_AUDIO_OUTPUT_N];
|
|
#endif
|
|
#if DATA_PRODUCT_BUSES_MIDI_INPUT_N > 0
|
|
char midiInputsActive[DATA_PRODUCT_BUSES_MIDI_INPUT_N];
|
|
#endif
|
|
#if DATA_PRODUCT_BUSES_MIDI_OUTPUT_N > 0
|
|
char midiOutputsActive[DATA_PRODUCT_BUSES_MIDI_OUTPUT_N];
|
|
#endif
|
|
void * mem;
|
|
} pluginInstance;
|
|
|
|
static Steinberg_Vst_IComponentVtbl pluginVtblIComponent;
|
|
static Steinberg_Vst_IAudioProcessorVtbl pluginVtblIAudioProcessor;
|
|
|
|
static Steinberg_tresult pluginQueryInterface(pluginInstance *p, const Steinberg_TUID iid, void ** obj) {
|
|
// This seems to violate the way multiple inheritance should work in COM, but hosts like it, so what do I know...
|
|
size_t offset;
|
|
if (memcmp(iid, Steinberg_FUnknown_iid, sizeof(Steinberg_TUID)) == 0
|
|
|| memcmp(iid, Steinberg_IPluginBase_iid, sizeof(Steinberg_TUID)) == 0
|
|
|| memcmp(iid, Steinberg_Vst_IComponent_iid, sizeof(Steinberg_TUID)) == 0)
|
|
offset = offsetof(pluginInstance, vtblIComponent);
|
|
else if (memcmp(iid, Steinberg_Vst_IAudioProcessor_iid, sizeof(Steinberg_TUID)) == 0)
|
|
offset = offsetof(pluginInstance, vtblIAudioProcessor);
|
|
else if (memcmp(iid, Steinberg_Vst_IProcessContextRequirements_iid, sizeof(Steinberg_TUID)) == 0)
|
|
offset = offsetof(pluginInstance, vtblIProcessContextRequirements);
|
|
else {
|
|
TRACE(" not supported\n");
|
|
for (int i = 0; i < 16; i++)
|
|
TRACE(" %x", iid[i]);
|
|
TRACE("\n");
|
|
*obj = NULL;
|
|
return Steinberg_kNoInterface;
|
|
}
|
|
*obj = (void *)((char *)p + offset);
|
|
p->refs++;
|
|
return Steinberg_kResultOk;
|
|
}
|
|
|
|
static Steinberg_uint32 pluginAddRef(pluginInstance *p) {
|
|
p->refs++;
|
|
return p->refs;
|
|
}
|
|
|
|
static Steinberg_uint32 pluginRelease(pluginInstance *p) {
|
|
p->refs--;
|
|
if (p->refs == 0) {
|
|
TRACE(" free %p\n", (void *)p);
|
|
free(p);
|
|
return 0;
|
|
}
|
|
return p->refs;
|
|
}
|
|
|
|
static Steinberg_tresult pluginIComponentQueryInterface(void *thisInterface, const Steinberg_TUID iid, void ** obj) {
|
|
TRACE("plugin IComponent queryInterface %p\n", thisInterface);
|
|
return pluginQueryInterface((pluginInstance *)((char *)thisInterface - offsetof(pluginInstance, vtblIComponent)), iid, obj);
|
|
}
|
|
|
|
static Steinberg_uint32 pluginIComponentAddRef(void *thisInterface) {
|
|
TRACE("plugin IComponent addRef %p\n", thisInterface);
|
|
return pluginAddRef((pluginInstance *)((char *)thisInterface - offsetof(pluginInstance, vtblIComponent)));
|
|
}
|
|
|
|
static Steinberg_uint32 pluginIComponentRelease(void *thisInterface) {
|
|
TRACE("plugin IComponent release %p\n", thisInterface);
|
|
return pluginRelease((pluginInstance *)((char *)thisInterface - offsetof(pluginInstance, vtblIComponent)));
|
|
}
|
|
|
|
static Steinberg_tresult pluginInitialize(void *thisInterface, struct Steinberg_FUnknown *context) {
|
|
TRACE("plugin initialize\n");
|
|
|
|
pluginInstance *p = (pluginInstance *)((char *)thisInterface - offsetof(pluginInstance, vtblIComponent));
|
|
if (p->context != NULL)
|
|
return Steinberg_kResultFalse;
|
|
p->context = context;
|
|
|
|
plugin_callbacks cbs = {
|
|
/* .handle = */ (void *)p,
|
|
/* .format = */ "vst3",
|
|
/* .get_bindir = */ get_bindir_cb,
|
|
/* .get_datadir = */ get_datadir_cb
|
|
};
|
|
plugin_init(&p->p, &cbs);
|
|
#if DATA_PRODUCT_PARAMETERS_N > 0
|
|
for (size_t i = 0; i < DATA_PRODUCT_PARAMETERS_N; i++) {
|
|
p->parameters[i] = parameterData[i].def;
|
|
if (!(parameterInfo[i].flags & Steinberg_Vst_ParameterInfo_ParameterFlags_kIsReadOnly))
|
|
plugin_set_parameter(&p->p, parameterData[i].index, parameterData[i].def);
|
|
}
|
|
#endif
|
|
#if DATA_PRODUCT_BUSES_AUDIO_INPUT_N > 0
|
|
for (size_t i = 0; i < DATA_PRODUCT_BUSES_AUDIO_INPUT_N; i++)
|
|
p->inputsActive[i] = 0;
|
|
#endif
|
|
#if DATA_PRODUCT_BUSES_AUDIO_OUTPUT_N > 0
|
|
for (size_t i = 0; i < DATA_PRODUCT_BUSES_AUDIO_OUTPUT_N; i++)
|
|
p->outputsActive[i] = 0;
|
|
#endif
|
|
#if DATA_PRODUCT_BUSES_MIDI_INPUT_N > 0
|
|
for (size_t i = 0; i < DATA_PRODUCT_BUSES_MIDI_INPUT_N; i++)
|
|
p->midiInputsActive[i] = 0;
|
|
#endif
|
|
#if DATA_PRODUCT_BUSES_MIDI_OUTPUT_N > 0
|
|
for (size_t i = 0; i < DATA_PRODUCT_BUSES_MIDI_OUTPUT_N; i++)
|
|
p->midiOutputsActive[i] = 0;
|
|
#endif
|
|
p->mem = NULL;
|
|
return Steinberg_kResultOk;
|
|
}
|
|
|
|
static Steinberg_tresult pluginTerminate(void *thisInterface) {
|
|
TRACE("plugin terminate\n");
|
|
pluginInstance *p = (pluginInstance *)((char *)thisInterface - offsetof(pluginInstance, vtblIComponent));
|
|
p->context = NULL;
|
|
plugin_fini(&p->p);
|
|
if (p->mem)
|
|
free(p->mem);
|
|
return Steinberg_kResultOk;
|
|
}
|
|
|
|
static Steinberg_tresult pluginGetControllerClassId(void *thisInterface, Steinberg_TUID classId) {
|
|
(void)thisInterface;
|
|
|
|
TRACE("plugin get controller class id %p %p\n", thisInterface, classId);
|
|
if (classId != NULL) {
|
|
memcpy(classId, dataControllerCID, sizeof(Steinberg_TUID));
|
|
return Steinberg_kResultTrue;
|
|
}
|
|
return Steinberg_kResultFalse;
|
|
}
|
|
|
|
static Steinberg_tresult pluginSetIoMode(void *thisInterface, Steinberg_Vst_IoMode mode) {
|
|
(void)thisInterface;
|
|
(void)mode;
|
|
|
|
TRACE("plugin set io mode\n");
|
|
return Steinberg_kNotImplemented;
|
|
}
|
|
|
|
static Steinberg_int32 pluginGetBusCount(void *thisInterface, Steinberg_Vst_MediaType type, Steinberg_Vst_BusDirection dir) {
|
|
(void)thisInterface;
|
|
|
|
TRACE("plugin get bus count\n");
|
|
if (type == Steinberg_Vst_MediaTypes_kAudio) {
|
|
if (dir == Steinberg_Vst_BusDirections_kInput)
|
|
return DATA_PRODUCT_BUSES_AUDIO_INPUT_N;
|
|
else if (dir == Steinberg_Vst_BusDirections_kOutput)
|
|
return DATA_PRODUCT_BUSES_AUDIO_OUTPUT_N;
|
|
} else if (type == Steinberg_Vst_MediaTypes_kEvent) {
|
|
if (dir == Steinberg_Vst_BusDirections_kInput)
|
|
return DATA_PRODUCT_BUSES_MIDI_INPUT_N;
|
|
else if (dir == Steinberg_Vst_BusDirections_kOutput)
|
|
return DATA_PRODUCT_BUSES_MIDI_OUTPUT_N;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static Steinberg_tresult pluginGetBusInfo(void* thisInterface, Steinberg_Vst_MediaType type, Steinberg_Vst_BusDirection dir, Steinberg_int32 index, struct Steinberg_Vst_BusInfo* bus) {
|
|
(void)thisInterface;
|
|
|
|
TRACE("plugin get bus info\n");
|
|
if (index < 0)
|
|
return Steinberg_kInvalidArgument;
|
|
if (type == Steinberg_Vst_MediaTypes_kAudio) {
|
|
if (dir == Steinberg_Vst_BusDirections_kInput) {
|
|
#if DATA_PRODUCT_BUSES_AUDIO_INPUT_N > 0
|
|
if (index >= DATA_PRODUCT_BUSES_AUDIO_INPUT_N)
|
|
return Steinberg_kInvalidArgument;
|
|
*bus = busInfoAudioInput[index];
|
|
return Steinberg_kResultTrue;
|
|
#endif
|
|
} else if (dir == Steinberg_Vst_BusDirections_kOutput) {
|
|
#if DATA_PRODUCT_BUSES_AUDIO_OUTPUT_N > 0
|
|
if (index >= DATA_PRODUCT_BUSES_AUDIO_OUTPUT_N)
|
|
return Steinberg_kInvalidArgument;
|
|
*bus = busInfoAudioOutput[index];
|
|
return Steinberg_kResultTrue;
|
|
#endif
|
|
}
|
|
} else if (type == Steinberg_Vst_MediaTypes_kEvent) {
|
|
if (dir == Steinberg_Vst_BusDirections_kInput) {
|
|
#if DATA_PRODUCT_BUSES_MIDI_INPUT_N > 0
|
|
if (index >= DATA_PRODUCT_BUSES_MIDI_INPUT_N)
|
|
return Steinberg_kInvalidArgument;
|
|
*bus = busInfoMidiInput[index];
|
|
return Steinberg_kResultTrue;
|
|
#endif
|
|
} else if (dir == Steinberg_Vst_BusDirections_kOutput) {
|
|
#if DATA_PRODUCT_BUSES_MIDI_OUTPUT_N > 0
|
|
if (index >= DATA_PRODUCT_BUSES_MIDI_OUTPUT_N)
|
|
return Steinberg_kInvalidArgument;
|
|
*bus = busInfoMidiOutput[index];
|
|
return Steinberg_kResultTrue;
|
|
#endif
|
|
}
|
|
}
|
|
return Steinberg_kInvalidArgument;
|
|
}
|
|
|
|
static Steinberg_tresult pluginGetRoutingInfo(void* thisInterface, struct Steinberg_Vst_RoutingInfo* inInfo, struct Steinberg_Vst_RoutingInfo* outInfo) {
|
|
(void)thisInterface;
|
|
(void)inInfo;
|
|
(void)outInfo;
|
|
|
|
TRACE("plugin get routing info\n");
|
|
return Steinberg_kNotImplemented;
|
|
}
|
|
|
|
static Steinberg_tresult pluginActivateBus(void* thisInterface, Steinberg_Vst_MediaType type, Steinberg_Vst_BusDirection dir, Steinberg_int32 index, Steinberg_TBool state) {
|
|
TRACE("plugin activate bus\n");
|
|
if (index < 0)
|
|
return Steinberg_kInvalidArgument;
|
|
pluginInstance *p = (pluginInstance *)((char *)thisInterface - offsetof(pluginInstance, vtblIComponent));
|
|
if (type == Steinberg_Vst_MediaTypes_kAudio) {
|
|
if (dir == Steinberg_Vst_BusDirections_kInput) {
|
|
#if DATA_PRODUCT_BUSES_AUDIO_INPUT_N > 0
|
|
if (index >= DATA_PRODUCT_BUSES_AUDIO_INPUT_N)
|
|
return Steinberg_kInvalidArgument;
|
|
p->inputsActive[index] = state;
|
|
return Steinberg_kResultTrue;
|
|
#endif
|
|
} else if (dir == Steinberg_Vst_BusDirections_kOutput) {
|
|
#if DATA_PRODUCT_BUSES_AUDIO_OUTPUT_N > 0
|
|
if (index >= DATA_PRODUCT_BUSES_AUDIO_OUTPUT_N)
|
|
return Steinberg_kInvalidArgument;
|
|
p->outputsActive[index] = state;
|
|
return Steinberg_kResultTrue;
|
|
#endif
|
|
}
|
|
} else if (type == Steinberg_Vst_MediaTypes_kEvent) {
|
|
if (dir == Steinberg_Vst_BusDirections_kInput) {
|
|
#if DATA_PRODUCT_BUSES_MIDI_INPUT_N > 0
|
|
if (index >= DATA_PRODUCT_BUSES_MIDI_INPUT_N)
|
|
return Steinberg_kInvalidArgument;
|
|
p->midiInputsActive[index] = state;
|
|
return Steinberg_kResultTrue;
|
|
#endif
|
|
} else if (dir == Steinberg_Vst_BusDirections_kOutput) {
|
|
#if DATA_PRODUCT_BUSES_MIDI_OUTPUT_N > 0
|
|
if (index >= DATA_PRODUCT_BUSES_MIDI_OUTPUT_N)
|
|
return Steinberg_kInvalidArgument;
|
|
p->midiOutputsActive[index] = state;
|
|
return Steinberg_kResultTrue;
|
|
#endif
|
|
}
|
|
}
|
|
return Steinberg_kInvalidArgument;
|
|
}
|
|
|
|
static Steinberg_tresult pluginSetActive(void* thisInterface, Steinberg_TBool state) {
|
|
TRACE("plugin set active\n");
|
|
pluginInstance *p = (pluginInstance *)((char *)thisInterface - offsetof(pluginInstance, vtblIComponent));
|
|
if (p->mem != NULL) {
|
|
free(p->mem);
|
|
p->mem = NULL;
|
|
}
|
|
if (state) {
|
|
#if DATA_PRODUCT_BUSES_AUDIO_INPUT_N > 0
|
|
for (size_t i = 0; i < DATA_PRODUCT_BUSES_AUDIO_INPUT_N; i++)
|
|
if (!p->inputsActive[i] && (busInfoAudioInput[i].flags & Steinberg_Vst_BusInfo_BusFlags_kDefaultActive))
|
|
return Steinberg_kResultFalse;
|
|
#endif
|
|
#if DATA_PRODUCT_BUSES_AUDIO_OUTPUT_N > 0
|
|
for (size_t i = 0; i < DATA_PRODUCT_BUSES_AUDIO_OUTPUT_N; i++)
|
|
if (!p->outputsActive[i] && (busInfoAudioOutput[i].flags & Steinberg_Vst_BusInfo_BusFlags_kDefaultActive))
|
|
return Steinberg_kResultFalse;
|
|
#endif
|
|
#if DATA_PRODUCT_BUSES_MIDI_INPUT_N > 0
|
|
for (size_t i = 0; i < DATA_PRODUCT_BUSES_MIDI_INPUT_N; i++)
|
|
if (!p->midiInputsActive[i] && (busInfoMidiInput[i].flags & Steinberg_Vst_BusInfo_BusFlags_kDefaultActive))
|
|
return Steinberg_kResultFalse;
|
|
#endif
|
|
#if DATA_PRODUCT_BUSES_MIDI_OUTPUT_N > 0
|
|
for (size_t i = 0; i < DATA_PRODUCT_BUSES_MIDI_OUTPUT_N; i++)
|
|
if (!p->midiOutputsActive[i] && (busInfoMidiOutput[i].flags & Steinberg_Vst_BusInfo_BusFlags_kDefaultActive))
|
|
return Steinberg_kResultFalse;
|
|
#endif
|
|
plugin_set_sample_rate(&p->p, p->sampleRate);
|
|
size_t req = plugin_mem_req(&p->p);
|
|
if (req != 0) {
|
|
p->mem = malloc(req);
|
|
if (p->mem == NULL)
|
|
return Steinberg_kOutOfMemory;
|
|
plugin_mem_set(&p->p, p->mem);
|
|
}
|
|
plugin_reset(&p->p);
|
|
}
|
|
return Steinberg_kResultOk;
|
|
}
|
|
|
|
// https://stackoverflow.com/questions/2100331/macro-definition-to-determine-big-endian-or-little-endian-machine
|
|
#define IS_BIG_ENDIAN (!(union { uint16_t u16; unsigned char c; }){ .u16 = 1 }.c)
|
|
// https://stackoverflow.com/questions/2182002/how-to-convert-big-endian-to-little-endian-in-c-without-using-library-functions
|
|
#define SWAP_UINT32(x) (((x) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | ((x) << 24))
|
|
|
|
static Steinberg_tresult pluginSetState(void* thisInterface, struct Steinberg_IBStream* state) {
|
|
TRACE("plugin set state\n");
|
|
if (state == NULL)
|
|
return Steinberg_kResultFalse;
|
|
#if DATA_PRODUCT_PARAMETERS_N > 0
|
|
pluginInstance *p = (pluginInstance *)((char *)thisInterface - offsetof(pluginInstance, vtblIComponent));
|
|
for (size_t i = 0; i < DATA_PRODUCT_PARAMETERS_N; i++) {
|
|
if (parameterInfo[i].flags & Steinberg_Vst_ParameterInfo_ParameterFlags_kIsReadOnly)
|
|
continue;
|
|
union { float f; uint32_t u; } v;
|
|
Steinberg_int32 n;
|
|
state->lpVtbl->read(state, &v, 4, &n);
|
|
if (n != 4)
|
|
return Steinberg_kResultFalse;
|
|
if (IS_BIG_ENDIAN)
|
|
v.u = SWAP_UINT32(v.u);
|
|
p->parameters[i] = parameterAdjust(i, v.f);
|
|
plugin_set_parameter(&p->p, parameterData[i].index, p->parameters[i]);
|
|
}
|
|
#endif
|
|
return Steinberg_kResultOk;
|
|
}
|
|
|
|
static Steinberg_tresult pluginGetState(void* thisInterface, struct Steinberg_IBStream* state) {
|
|
TRACE("plugin get state\n");
|
|
if (state == NULL)
|
|
return Steinberg_kResultFalse;
|
|
#if DATA_PRODUCT_PARAMETERS_N > 0
|
|
pluginInstance *p = (pluginInstance *)((char *)thisInterface - offsetof(pluginInstance, vtblIComponent));
|
|
for (size_t i = 0; i < DATA_PRODUCT_PARAMETERS_N; i++) {
|
|
if (parameterInfo[i].flags & Steinberg_Vst_ParameterInfo_ParameterFlags_kIsReadOnly)
|
|
continue;
|
|
union { float f; uint32_t u; } v;
|
|
v.f = p->parameters[i];
|
|
if (IS_BIG_ENDIAN)
|
|
v.u = SWAP_UINT32(v.u);
|
|
Steinberg_int32 n;
|
|
state->lpVtbl->write(state, &v, 4, &n);
|
|
if (n != 4)
|
|
return Steinberg_kResultFalse;
|
|
}
|
|
#endif
|
|
return Steinberg_kResultOk;
|
|
}
|
|
|
|
static Steinberg_Vst_IComponentVtbl pluginVtblIComponent = {
|
|
/* FUnknown */
|
|
/* .queryInterface = */ pluginIComponentQueryInterface,
|
|
/* .addRef = */ pluginIComponentAddRef,
|
|
/* .release = */ pluginIComponentRelease,
|
|
|
|
/* IPluginBase */
|
|
/* .initialize = */ pluginInitialize,
|
|
/* .terminate = */ pluginTerminate,
|
|
|
|
/* IComponent */
|
|
/* .getControllerClassId = */ pluginGetControllerClassId,
|
|
/* .setIoMode = */ pluginSetIoMode,
|
|
/* .getBusCount = */ pluginGetBusCount,
|
|
/* .getBusInfo = */ pluginGetBusInfo,
|
|
/* .getRoutingInfo = */ pluginGetRoutingInfo,
|
|
/* .activateBus = */ pluginActivateBus,
|
|
/* .setActive = */ pluginSetActive,
|
|
/* .setState = */ pluginSetState,
|
|
/* .getState = */ pluginGetState
|
|
};
|
|
|
|
static Steinberg_tresult pluginIAudioProcessorQueryInterface(void *thisInterface, const Steinberg_TUID iid, void ** obj) {
|
|
TRACE("plugin IAudioProcessor queryInterface %p\n", thisInterface);
|
|
return pluginQueryInterface((pluginInstance *)((char *)thisInterface - offsetof(pluginInstance, vtblIAudioProcessor)), iid, obj);
|
|
}
|
|
|
|
static Steinberg_uint32 pluginIAudioProcessorAddRef(void *thisInterface) {
|
|
TRACE("plugin IAudioProcessor addRef %p\n", thisInterface);
|
|
return pluginAddRef((pluginInstance *)((char *)thisInterface - offsetof(pluginInstance, vtblIAudioProcessor)));
|
|
}
|
|
|
|
static Steinberg_uint32 pluginIAudioProcessorRelease(void *thisInterface) {
|
|
TRACE("plugin IAudioProcessor release %p\n", thisInterface);
|
|
return pluginRelease((pluginInstance *)((char *)thisInterface - offsetof(pluginInstance, vtblIAudioProcessor)));
|
|
}
|
|
|
|
static Steinberg_tresult pluginSetBusArrangements(void* thisInterface, Steinberg_Vst_SpeakerArrangement* inputs, Steinberg_int32 numIns, Steinberg_Vst_SpeakerArrangement* outputs, Steinberg_int32 numOuts) {
|
|
(void)thisInterface;
|
|
|
|
TRACE("plugin IAudioProcessor set bus arrangements\n");
|
|
if (numIns < 0 || numOuts < 0)
|
|
return Steinberg_kInvalidArgument;
|
|
if (numIns != DATA_PRODUCT_BUSES_AUDIO_INPUT_N || numOuts != DATA_PRODUCT_BUSES_AUDIO_OUTPUT_N)
|
|
return Steinberg_kResultFalse;
|
|
|
|
#if DATA_PRODUCT_BUSES_AUDIO_INPUT_N > 0
|
|
for (Steinberg_int32 i = 0; i < numIns; i++)
|
|
if ((busInfoAudioInput[i].channelCount == 1 && inputs[i] != Steinberg_Vst_SpeakerArr_kMono)
|
|
|| (busInfoAudioInput[i].channelCount == 2 && inputs[i] != Steinberg_Vst_SpeakerArr_kStereo))
|
|
return Steinberg_kResultFalse;
|
|
#else
|
|
(void)inputs;
|
|
#endif
|
|
|
|
#if DATA_PRODUCT_BUSES_AUDIO_OUTPUT_N > 0
|
|
for (Steinberg_int32 i = 0; i < numOuts; i++)
|
|
if ((busInfoAudioOutput[i].channelCount == 1 && outputs[i] != Steinberg_Vst_SpeakerArr_kMono)
|
|
|| (busInfoAudioOutput[i].channelCount == 2 && outputs[i] != Steinberg_Vst_SpeakerArr_kStereo))
|
|
return Steinberg_kResultFalse;
|
|
#else
|
|
(void)outputs;
|
|
#endif
|
|
|
|
return Steinberg_kResultTrue;
|
|
}
|
|
|
|
static Steinberg_tresult pluginGetBusArrangement(void* thisInterface, Steinberg_Vst_BusDirection dir, Steinberg_int32 index, Steinberg_Vst_SpeakerArrangement* arr) {
|
|
(void)thisInterface;
|
|
|
|
TRACE("plugin IAudioProcessor get bus arrangement\n");
|
|
|
|
#if DATA_PRODUCT_BUSES_AUDIO_INPUT_N > 0
|
|
if (dir == Steinberg_Vst_BusDirections_kInput && index >= 0 && index < DATA_PRODUCT_BUSES_AUDIO_INPUT_N) {
|
|
*arr = busInfoAudioInput[index].channelCount == 1 ? Steinberg_Vst_SpeakerArr_kMono : Steinberg_Vst_SpeakerArr_kStereo;
|
|
return Steinberg_kResultTrue;
|
|
}
|
|
#endif
|
|
|
|
#if DATA_PRODUCT_BUSES_AUDIO_OUTPUT_N > 0
|
|
if (dir == Steinberg_Vst_BusDirections_kOutput && index >= 0 && index < DATA_PRODUCT_BUSES_AUDIO_OUTPUT_N) {
|
|
*arr = busInfoAudioOutput[index].channelCount == 1 ? Steinberg_Vst_SpeakerArr_kMono : Steinberg_Vst_SpeakerArr_kStereo;
|
|
return Steinberg_kResultTrue;
|
|
}
|
|
#endif
|
|
|
|
return Steinberg_kInvalidArgument;
|
|
}
|
|
|
|
static Steinberg_tresult pluginCanProcessSampleSize(void* thisInterface, Steinberg_int32 symbolicSampleSize) {
|
|
(void)thisInterface;
|
|
|
|
TRACE("plugin IAudioProcessor can process sample size\n");
|
|
return symbolicSampleSize == Steinberg_Vst_SymbolicSampleSizes_kSample32 ? Steinberg_kResultOk : Steinberg_kNotImplemented;
|
|
}
|
|
|
|
static Steinberg_uint32 pluginGetLatencySamples(void* thisInterface) {
|
|
(void)thisInterface;
|
|
|
|
TRACE("plugin IAudioProcessor get latency samples\n");
|
|
//TBD
|
|
return 0;
|
|
}
|
|
|
|
static Steinberg_tresult pluginSetupProcessing(void* thisInterface, struct Steinberg_Vst_ProcessSetup* setup) {
|
|
TRACE("plugin IAudioProcessor setup processing\n");
|
|
pluginInstance *p = (pluginInstance *)((char *)thisInterface - offsetof(pluginInstance, vtblIAudioProcessor));
|
|
p->sampleRate = (float)setup->sampleRate;
|
|
return Steinberg_kResultOk;
|
|
}
|
|
|
|
static Steinberg_tresult pluginSetProcessing(void* thisInterface, Steinberg_TBool state) {
|
|
(void)thisInterface;
|
|
(void)state;
|
|
|
|
TRACE("plugin IAudioProcessor set processing\n");
|
|
return Steinberg_kNotImplemented;
|
|
}
|
|
|
|
static void processParams(pluginInstance *p, struct Steinberg_Vst_ProcessData *data, char before) {
|
|
#if DATA_PRODUCT_PARAMETERS_N + DATA_PRODUCT_BUSES_MIDI_INPUT_N > 0
|
|
if (data->inputParameterChanges == NULL)
|
|
return;
|
|
Steinberg_int32 n = data->inputParameterChanges->lpVtbl->getParameterCount(data->inputParameterChanges);
|
|
for (Steinberg_int32 i = 0; i < n; i++) {
|
|
struct Steinberg_Vst_IParamValueQueue *q = data->inputParameterChanges->lpVtbl->getParameterData(data->inputParameterChanges, i);
|
|
if (q == NULL)
|
|
continue;
|
|
Steinberg_int32 c = q->lpVtbl->getPointCount(q);
|
|
if (c <= 0)
|
|
continue;
|
|
Steinberg_int32 o;
|
|
Steinberg_Vst_ParamValue v;
|
|
if (q->lpVtbl->getPoint(q, before ? 0 : c - 1, &o, &v) != Steinberg_kResultTrue)
|
|
continue;
|
|
if (before ? o != 0 : o <= 0)
|
|
continue;
|
|
Steinberg_Vst_ParamID id = q->lpVtbl->getParameterId(q);
|
|
int pi = parameterGetIndexById(id);
|
|
# if DATA_PRODUCT_BUSES_MIDI_INPUT_N > 0
|
|
if (pi >= DATA_PRODUCT_PARAMETERS_N) {
|
|
size_t j = pi - DATA_PRODUCT_PARAMETERS_N;
|
|
size_t index = j / 3;
|
|
uint8_t data[3];
|
|
switch (j & 0x3) {
|
|
case 0:
|
|
// channel pressure
|
|
data[0] = 0xd0 /* | channel */;
|
|
data[1] = (uint8_t)(127.0 * v);
|
|
data[2] = 0;
|
|
break;
|
|
case 1:
|
|
{
|
|
// pitch bend change
|
|
uint16_t x = (uint16_t)(16382.0 * v) + 1;
|
|
data[0] = 0xe0 /* | channel */;
|
|
data[1] = (x >> 7) & 0x7f;
|
|
data[2] = x & 0x7f;
|
|
}
|
|
break;
|
|
case 2:
|
|
// mod wheel
|
|
data[0] = 0xb0 /* | channel */;
|
|
data[1] = 1;
|
|
data[2] = (uint8_t)(127.0 * v);
|
|
break;
|
|
default:
|
|
continue;
|
|
}
|
|
plugin_midi_msg_in(&p->p, midiInIndex[index], data);
|
|
continue;
|
|
}
|
|
# endif
|
|
v = parameterAdjust(pi, parameterMap(pi, v));
|
|
if (v != p->parameters[pi]) {
|
|
p->parameters[pi] = v;
|
|
plugin_set_parameter(&p->p, parameterData[pi].index, p->parameters[pi]);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static Steinberg_tresult pluginProcess(void* thisInterface, struct Steinberg_Vst_ProcessData* data) {
|
|
TRACE("plugin IAudioProcessor process\n");
|
|
|
|
#if defined(__aarch64__)
|
|
uint64_t fpcr;
|
|
__asm__ __volatile__ ("mrs %0, fpcr" : "=r"(fpcr));
|
|
__asm__ __volatile__ ("msr fpcr, %0" :: "r"(fpcr | 0x1000000)); // enable FZ
|
|
#elif defined(__i386__) || defined(__x86_64__)
|
|
const unsigned int flush_zero_mode = _MM_GET_FLUSH_ZERO_MODE();
|
|
const unsigned int denormals_zero_mode = _MM_GET_DENORMALS_ZERO_MODE();
|
|
|
|
_MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
|
|
_MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON);
|
|
#endif
|
|
|
|
pluginInstance *p = (pluginInstance *)((char *)thisInterface - offsetof(pluginInstance, vtblIAudioProcessor));
|
|
|
|
processParams(p, data, 1);
|
|
|
|
#if DATA_PRODUCT_BUSES_MIDI_INPUT_N > 0
|
|
if (data->inputEvents != NULL) {
|
|
Steinberg_int32 n = data->inputEvents->lpVtbl->getEventCount(data->inputEvents);
|
|
for (Steinberg_int32 i = 0; i < n; i++) {
|
|
struct Steinberg_Vst_Event ev;
|
|
if (data->inputEvents->lpVtbl->getEvent(data->inputEvents, i, &ev) != Steinberg_kResultOk)
|
|
continue;
|
|
switch (ev.type) {
|
|
case Steinberg_Vst_Event_EventTypes_kNoteOnEvent:
|
|
{
|
|
const uint8_t data[3] = { 0x90 | ev.Steinberg_Vst_Event_noteOn.channel, ev.Steinberg_Vst_Event_noteOn.pitch, (uint8_t)(127.f * ev.Steinberg_Vst_Event_noteOn.velocity) };
|
|
plugin_midi_msg_in(&p->p, midiInIndex[ev.busIndex], data);
|
|
}
|
|
break;
|
|
case Steinberg_Vst_Event_EventTypes_kNoteOffEvent:
|
|
{
|
|
const uint8_t data[3] = { 0x80 | ev.Steinberg_Vst_Event_noteOff.channel, ev.Steinberg_Vst_Event_noteOff.pitch, (uint8_t)(127.f * ev.Steinberg_Vst_Event_noteOff.velocity) };
|
|
plugin_midi_msg_in(&p->p, midiInIndex[ev.busIndex], data);
|
|
}
|
|
break;
|
|
case Steinberg_Vst_Event_EventTypes_kPolyPressureEvent:
|
|
{
|
|
const uint8_t data[3] = { 0xa0 | ev.Steinberg_Vst_Event_polyPressure.channel, ev.Steinberg_Vst_Event_polyPressure.pitch, (uint8_t)(127.f * ev.Steinberg_Vst_Event_polyPressure.pressure) };
|
|
plugin_midi_msg_in(&p->p, midiInIndex[ev.busIndex], data);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if DATA_PRODUCT_CHANNELS_AUDIO_INPUT_N > 0
|
|
const float **inputs = p->inputs;
|
|
Steinberg_int32 ki = 0;
|
|
for (Steinberg_int32 i = 0; i < data->numInputs; i++)
|
|
for (Steinberg_int32 j = 0; j < data->inputs[i].numChannels; j++, ki++)
|
|
inputs[ki] = data->inputs[i].Steinberg_Vst_AudioBusBuffers_channelBuffers32[j];
|
|
#else
|
|
const float **inputs = NULL;
|
|
#endif
|
|
|
|
#if DATA_PRODUCT_CHANNELS_AUDIO_OUTPUT_N > 0
|
|
float **outputs = p->outputs;
|
|
Steinberg_int32 ko = 0;
|
|
for (Steinberg_int32 i = 0; i < data->numOutputs; i++)
|
|
for (Steinberg_int32 j = 0; j < data->outputs[i].numChannels; j++, ko++)
|
|
outputs[ko] = data->outputs[i].Steinberg_Vst_AudioBusBuffers_channelBuffers32[j];
|
|
#else
|
|
float **outputs = NULL;
|
|
#endif
|
|
|
|
plugin_process(&p->p, inputs, outputs, data->numSamples);
|
|
|
|
processParams(p, data, 0);
|
|
|
|
#if DATA_PRODUCT_PARAMETERS_N > 0
|
|
for (Steinberg_int32 i = 0; i < DATA_PRODUCT_PARAMETERS_N; i++) {
|
|
if (!(parameterInfo[i].flags & Steinberg_Vst_ParameterInfo_ParameterFlags_kIsReadOnly))
|
|
continue;
|
|
float v = plugin_get_parameter(&p->p, parameterData[i].index);
|
|
if (v == p->parameters[i])
|
|
continue;
|
|
p->parameters[i] = v;
|
|
if (data->outputParameterChanges == NULL)
|
|
continue;
|
|
Steinberg_Vst_ParamID id = parameterInfo[i].id;
|
|
Steinberg_int32 index;
|
|
struct Steinberg_Vst_IParamValueQueue *q = data->outputParameterChanges->lpVtbl->addParameterData(data->outputParameterChanges, &id, &index);
|
|
if (q == NULL)
|
|
continue;
|
|
q->lpVtbl->addPoint(q, data->numSamples - 1, parameterUnmap(i, v), &index);
|
|
}
|
|
#endif
|
|
|
|
// TBD: latency + IComponentHandler::restartComponent (kLatencyChanged), see https://steinbergmedia.github.io/vst3_dev_portal/pages/Technical+Documentation/Workflow+Diagrams/Get+Latency+Call+Sequence.html
|
|
|
|
#if defined(__aarch64__)
|
|
__asm__ __volatile__ ("msr fpcr, %0" : : "r"(fpcr));
|
|
#elif defined(__i386__) || defined(__x86_64__)
|
|
_MM_SET_FLUSH_ZERO_MODE(flush_zero_mode);
|
|
_MM_SET_DENORMALS_ZERO_MODE(denormals_zero_mode);
|
|
#endif
|
|
|
|
return Steinberg_kResultOk;
|
|
}
|
|
|
|
static Steinberg_uint32 pluginGetTailSamples(void* thisInterface) {
|
|
(void)thisInterface;
|
|
|
|
TRACE("plugin IAudioProcessor get tail samples\n");
|
|
//TBD
|
|
return 0;
|
|
}
|
|
|
|
static Steinberg_Vst_IAudioProcessorVtbl pluginVtblIAudioProcessor = {
|
|
/* FUnknown */
|
|
/* .queryInterface = */ pluginIAudioProcessorQueryInterface,
|
|
/* .addRef = */ pluginIAudioProcessorAddRef,
|
|
/* .release = */ pluginIAudioProcessorRelease,
|
|
|
|
/* IAudioProcessor */
|
|
/* .setBusArrangements = */ pluginSetBusArrangements,
|
|
/*. getBusArrangement = */ pluginGetBusArrangement,
|
|
/* .canProcessSampleSize = */ pluginCanProcessSampleSize,
|
|
/* .getLatencySamples = */ pluginGetLatencySamples,
|
|
/* .setupProcessing = */ pluginSetupProcessing,
|
|
/* .setProcessing = */ pluginSetProcessing,
|
|
/* .process = */ pluginProcess,
|
|
/* .getTailSamples = */ pluginGetTailSamples
|
|
};
|
|
|
|
static Steinberg_tresult pluginIProcessContextRequirementsQueryInterface(void *thisInterface, const Steinberg_TUID iid, void ** obj) {
|
|
TRACE("plugin IProcessContextRequirements queryInterface %p\n", thisInterface);
|
|
return pluginQueryInterface((pluginInstance *)((char *)thisInterface - offsetof(pluginInstance, vtblIProcessContextRequirements)), iid, obj);
|
|
}
|
|
|
|
static Steinberg_uint32 pluginIProcessContextRequirementsAddRef(void *thisInterface) {
|
|
TRACE("plugin IComponent addRef %p\n", thisInterface);
|
|
return pluginAddRef((pluginInstance *)((char *)thisInterface - offsetof(pluginInstance, vtblIProcessContextRequirements)));
|
|
}
|
|
|
|
static Steinberg_uint32 pluginIProcessContextRequirementsRelease(void *thisInterface) {
|
|
TRACE("plugin IComponent release %p\n", thisInterface);
|
|
return pluginRelease((pluginInstance *)((char *)thisInterface - offsetof(pluginInstance, vtblIProcessContextRequirements)));
|
|
}
|
|
|
|
static Steinberg_uint32 pluginGetProcessContextRequirements(void* thisInterface) {
|
|
(void)thisInterface;
|
|
|
|
// TBD
|
|
return 0;
|
|
}
|
|
|
|
static Steinberg_Vst_IProcessContextRequirementsVtbl pluginVtblIProcessContextRequirements = {
|
|
/* FUnknown */
|
|
/* .queryInterface = */ pluginIProcessContextRequirementsQueryInterface,
|
|
/* .addRef = */ pluginIProcessContextRequirementsAddRef,
|
|
/* .release = */ pluginIProcessContextRequirementsRelease,
|
|
|
|
/* IProcessContextRequirements */
|
|
/* .getProcessContextRequirements = */ pluginGetProcessContextRequirements
|
|
};
|
|
|
|
typedef struct plugView plugView;
|
|
|
|
typedef struct controller {
|
|
Steinberg_Vst_IEditControllerVtbl * vtblIEditController;
|
|
Steinberg_Vst_IMidiMappingVtbl * vtblIMidiMapping;
|
|
#ifdef DATA_UI
|
|
//Steinberg_Vst_IConnectionPointVtbl * vtblIConnectionPoint;
|
|
#endif
|
|
Steinberg_uint32 refs;
|
|
Steinberg_FUnknown * context;
|
|
#if DATA_PRODUCT_PARAMETERS_N + DATA_PRODUCT_BUSES_MIDI_INPUT_N > 0
|
|
double parameters[DATA_PRODUCT_PARAMETERS_N + 3 * DATA_PRODUCT_BUSES_MIDI_INPUT_N];
|
|
#endif
|
|
struct Steinberg_Vst_IComponentHandler * componentHandler;
|
|
#ifdef DATA_UI
|
|
plugView ** views;
|
|
size_t viewsCount;
|
|
#endif
|
|
} controller;
|
|
|
|
static Steinberg_Vst_IEditControllerVtbl controllerVtblIEditController;
|
|
|
|
#ifdef DATA_UI
|
|
|
|
# ifdef __linux__
|
|
|
|
# include <X11/Xlib.h>
|
|
|
|
typedef struct {
|
|
Steinberg_ITimerHandlerVtbl * vtblITimerHandler;
|
|
Steinberg_uint32 refs;
|
|
void * data;
|
|
void (*cb)(void *data);
|
|
} timerHandler;
|
|
|
|
static Steinberg_tresult timerHandlerQueryInterface(void *thisInterface, const Steinberg_TUID iid, void ** obj) {
|
|
TRACE("timerHandler queryInterface %p\n", thisInterface);
|
|
// Same as above (pluginQueryInterface)
|
|
size_t offset;
|
|
if (memcmp(iid, Steinberg_FUnknown_iid, sizeof(Steinberg_TUID)) == 0
|
|
|| memcmp(iid, Steinberg_ITimerHandler_iid, sizeof(Steinberg_TUID)) == 0)
|
|
offset = offsetof(timerHandler, vtblITimerHandler);
|
|
else {
|
|
TRACE(" not supported\n");
|
|
for (int i = 0; i < 16; i++)
|
|
TRACE(" %x", iid[i]);
|
|
TRACE("\n");
|
|
*obj = NULL;
|
|
return Steinberg_kNoInterface;
|
|
}
|
|
timerHandler *t = (timerHandler *)((char *)thisInterface - offsetof(timerHandler, vtblITimerHandler));
|
|
*obj = (void *)((char *)t + offset);
|
|
t->refs++;
|
|
return Steinberg_kResultOk;
|
|
}
|
|
|
|
static Steinberg_uint32 timerHandlerAddRef(void *thisInterface) {
|
|
TRACE("timerHandler addRef %p\n", thisInterface);
|
|
timerHandler *t = (timerHandler *)((char *)thisInterface - offsetof(timerHandler, vtblITimerHandler));
|
|
t->refs++;
|
|
return t->refs;
|
|
}
|
|
|
|
static Steinberg_uint32 timerHandlerRelease(void *thisInterface) {
|
|
TRACE("timerHandler release %p\n", thisInterface);
|
|
timerHandler *t = (timerHandler *)((char *)thisInterface - offsetof(timerHandler, vtblITimerHandler));
|
|
t->refs--;
|
|
if (t->refs == 0) {
|
|
TRACE(" free %p\n", (void *)t);
|
|
free(t);
|
|
return 0;
|
|
}
|
|
return t->refs;
|
|
}
|
|
|
|
static void timerHandlerOnTimer(void* thisInterface) {
|
|
TRACE("timerHandler onTimer %p\n", thisInterface);
|
|
timerHandler *t = (timerHandler *)((char *)thisInterface - offsetof(timerHandler, vtblITimerHandler));
|
|
t->cb(t->data);
|
|
}
|
|
|
|
static Steinberg_ITimerHandlerVtbl timerHandlerVtblITimerHandler = {
|
|
/* FUnknown */
|
|
/* .queryInterface = */ timerHandlerQueryInterface,
|
|
/* .addRef = */ timerHandlerAddRef,
|
|
/* .release = */ timerHandlerRelease,
|
|
|
|
/* ITimerHandler */
|
|
/* .onTimer = */ timerHandlerOnTimer
|
|
};
|
|
|
|
# elif defined(__APPLE__)
|
|
|
|
# include <objc/objc.h>
|
|
# include <objc/runtime.h>
|
|
# include <objc/message.h>
|
|
|
|
# endif
|
|
|
|
typedef struct plugView {
|
|
Steinberg_IPlugViewVtbl * vtblIPlugView;
|
|
Steinberg_uint32 refs;
|
|
Steinberg_IPlugFrame * frame;
|
|
plugin_ui * ui;
|
|
controller * ctrl;
|
|
# ifdef __linux__
|
|
Steinberg_IRunLoop * runLoop;
|
|
timerHandler timer;
|
|
Display * display;
|
|
# elif defined(__APPLE__)
|
|
CFRunLoopTimerRef timer;
|
|
# elif defined(_WIN32) || defined(__CYGWIN__)
|
|
UINT_PTR timer;
|
|
# endif
|
|
} plugView;
|
|
|
|
static Steinberg_tresult plugViewRemoved(void* thisInterface);
|
|
|
|
static Steinberg_tresult plugViewQueryInterface(void *thisInterface, const Steinberg_TUID iid, void ** obj) {
|
|
TRACE("plugView queryInterface %p\n", thisInterface);
|
|
// Same as above (pluginQueryInterface)
|
|
size_t offset;
|
|
if (memcmp(iid, Steinberg_FUnknown_iid, sizeof(Steinberg_TUID)) == 0
|
|
|| memcmp(iid, Steinberg_IPlugView_iid, sizeof(Steinberg_TUID)) == 0)
|
|
offset = offsetof(plugView, vtblIPlugView);
|
|
else {
|
|
TRACE(" not supported\n");
|
|
for (int i = 0; i < 16; i++)
|
|
TRACE(" %x", iid[i]);
|
|
TRACE("\n");
|
|
*obj = NULL;
|
|
return Steinberg_kNoInterface;
|
|
}
|
|
plugView *v = (plugView *)((char *)thisInterface - offsetof(plugView, vtblIPlugView));
|
|
*obj = (void *)((char *)v + offset);
|
|
v->refs++;
|
|
return Steinberg_kResultOk;
|
|
}
|
|
|
|
static Steinberg_uint32 plugViewAddRef(void *thisInterface) {
|
|
TRACE("plugView addRef %p\n", thisInterface);
|
|
plugView *v = (plugView *)((char *)thisInterface - offsetof(plugView, vtblIPlugView));
|
|
v->refs++;
|
|
return v->refs;
|
|
}
|
|
|
|
static Steinberg_uint32 plugViewRelease(void *thisInterface) {
|
|
TRACE("plugView IEditController release %p\n", thisInterface);
|
|
plugView *v = (plugView *)((char *)thisInterface - offsetof(plugView, vtblIPlugView));
|
|
v->refs--;
|
|
if (v->refs == 0) {
|
|
TRACE(" free %p\n", (void *)v);
|
|
if (v->ui)
|
|
plugViewRemoved(thisInterface);
|
|
for (size_t i = 0; i < v->ctrl->viewsCount; i++)
|
|
if (v->ctrl->views[i] == v) {
|
|
v->ctrl->views[i] = NULL;
|
|
break;
|
|
}
|
|
free(v);
|
|
return 0;
|
|
}
|
|
return v->refs;
|
|
}
|
|
|
|
static Steinberg_tresult plugViewIsPlatformTypeSupported(void* thisInterface, Steinberg_FIDString type) {
|
|
(void)thisInterface;
|
|
|
|
TRACE("plugView isPlatformTypeSupported %p %s\n", thisInterface, type);
|
|
# if defined(_WIN32) || defined(__CYGWIN__)
|
|
return strcmp(type, "HWND") ? Steinberg_kResultFalse : Steinberg_kResultTrue;
|
|
# elif defined(__APPLE__) && defined(__MACH__)
|
|
return strcmp(type, "NSView") ? Steinberg_kResultFalse : Steinberg_kResultTrue;
|
|
# elif defined(__linux__)
|
|
return strcmp(type, "X11EmbedWindowID") ? Steinberg_kResultFalse : Steinberg_kResultTrue;
|
|
# else
|
|
(void)type;
|
|
|
|
return Steinberg_kResultFalse;
|
|
# endif
|
|
}
|
|
|
|
# if DATA_PRODUCT_PARAMETERS_N > 0
|
|
static void plugViewUpdateParameter(plugView *view, size_t index) {
|
|
if (view->ui)
|
|
plugin_ui_set_parameter(view->ui, parameterData[index].index, view->ctrl->parameters[index]);
|
|
}
|
|
|
|
static void plugViewUpdateAllParameters(plugView *view) {
|
|
if (view->ui == NULL)
|
|
return;
|
|
for (size_t i = 0; i < DATA_PRODUCT_PARAMETERS_N; i++)
|
|
plugin_ui_set_parameter(view->ui, parameterData[i].index, view->ctrl->parameters[i]);
|
|
}
|
|
|
|
static void plugViewSetParameterCb(void *handle, size_t index, float value) {
|
|
TRACE("set parameter cb\n");
|
|
|
|
# ifdef DATA_PARAM_LATENCY_INDEX
|
|
if (index == DATA_PARAM_LATENCY_INDEX)
|
|
return;
|
|
index = index >= DATA_PARAM_LATENCY_INDEX ? index - 1 : index;
|
|
# endif
|
|
plugView *v = (plugView *)handle;
|
|
v->ctrl->parameters[index] = parameterAdjust(index, value); // let Reaper find the updated value
|
|
v->ctrl->componentHandler->lpVtbl->beginEdit(v->ctrl->componentHandler, parameterInfo[index].id);
|
|
v->ctrl->componentHandler->lpVtbl->performEdit(v->ctrl->componentHandler, parameterInfo[index].id, parameterUnmap(index, v->ctrl->parameters[index]));
|
|
v->ctrl->componentHandler->lpVtbl->endEdit(v->ctrl->componentHandler, parameterInfo[index].id);
|
|
}
|
|
# endif
|
|
|
|
# ifdef __APPLE__
|
|
static void plugViewTimerCb(CFRunLoopTimerRef timer, void *info) {
|
|
(void)timer;
|
|
|
|
plugin_ui_idle(((plugView *)info)->ui);
|
|
}
|
|
# elif defined(_WIN32) || defined(__CYGWIN__)
|
|
static void plugViewTimerCb(HWND p1, UINT p2, UINT_PTR p3, DWORD p4) {
|
|
(void)p1;
|
|
(void)p2;
|
|
(void)p4;
|
|
|
|
plugin_ui_idle(((plugView *)p3)->ui);
|
|
}
|
|
# endif
|
|
|
|
static Steinberg_tresult plugViewAttached(void* thisInterface, void* parent, Steinberg_FIDString type) {
|
|
// GUI needs to be created here, see https://forums.steinberg.net/t/vst-and-hidpi/201916/3
|
|
TRACE("plugView attached %p\n", thisInterface);
|
|
|
|
if (plugViewIsPlatformTypeSupported(thisInterface, type) != Steinberg_kResultOk)
|
|
return Steinberg_kInvalidArgument;
|
|
|
|
plugView *v = (plugView *)((char *)thisInterface - offsetof(plugView, vtblIPlugView));
|
|
if (v->ui)
|
|
return Steinberg_kInvalidArgument;
|
|
|
|
plugin_ui_callbacks cbs = {
|
|
/* .handle = */ (void *)v,
|
|
/* .format = */ "vst3",
|
|
/* .get_bindir = */ get_bindir_cb,
|
|
/* .get_datadir = */ get_datadir_cb,
|
|
# if DATA_PRODUCT_PARAMETERS_N > 0
|
|
/* .set_parameter = */ plugViewSetParameterCb
|
|
# else
|
|
/* .set_parameter = */ NULL
|
|
# endif
|
|
};
|
|
v->ui = plugin_ui_create(1, parent, &cbs);
|
|
if (!v->ui)
|
|
return Steinberg_kResultFalse;
|
|
|
|
# ifdef __linux__
|
|
v->display = XOpenDisplay(NULL);
|
|
if (v->display == NULL) {
|
|
plugin_ui_free(v->ui);
|
|
v->ui = NULL;
|
|
return Steinberg_kResultFalse;
|
|
}
|
|
if (v->runLoop->lpVtbl->registerTimer(v->runLoop, (struct Steinberg_ITimerHandler *)&v->timer, 20) != Steinberg_kResultOk) {
|
|
XCloseDisplay(v->display);
|
|
plugin_ui_free(v->ui);
|
|
v->ui = NULL;
|
|
return Steinberg_kResultFalse;
|
|
}
|
|
# elif defined(__APPLE__)
|
|
CFRunLoopTimerContext ctx = {
|
|
/* .version = */ 0,
|
|
/* .info = */ v,
|
|
/* .retain = */ NULL,
|
|
/* .release = */ NULL,
|
|
/* .copyDescription = */ NULL
|
|
};
|
|
v->timer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent(), 20.0 / 1000.0, 0, 0, plugViewTimerCb, &ctx);
|
|
CFRunLoopAddTimer(CFRunLoopGetCurrent(), v->timer, kCFRunLoopCommonModes);
|
|
# elif defined(_WIN32) || defined(__CYGWIN__)
|
|
v->timer = SetTimer((HWND)*((char **)v->ui), (UINT_PTR)v, 20, (TIMERPROC)plugViewTimerCb);
|
|
if (v->timer == 0) {
|
|
plugin_ui_free(v->ui);
|
|
v->ui = NULL;
|
|
return Steinberg_kResultFalse;
|
|
}
|
|
# endif
|
|
# if DATA_PRODUCT_PARAMETERS_N > 0
|
|
plugViewUpdateAllParameters(v);
|
|
# endif
|
|
return Steinberg_kResultTrue;
|
|
}
|
|
|
|
static Steinberg_tresult plugViewRemoved(void* thisInterface) {
|
|
TRACE("plugView removed %p\n", thisInterface);
|
|
plugView *v = (plugView *)((char *)thisInterface - offsetof(plugView, vtblIPlugView));
|
|
# ifdef __linux__
|
|
v->runLoop->lpVtbl->unregisterTimer(v->runLoop, (struct Steinberg_ITimerHandler *)&v->timer);
|
|
XCloseDisplay(v->display);
|
|
# elif defined(__APPLE__)
|
|
CFRunLoopTimerInvalidate(v->timer);
|
|
CFRelease(v->timer);
|
|
# elif defined(_WIN32) || defined(__CYGWIN__)
|
|
KillTimer((HWND)(*((char **)v->ui)), (UINT_PTR)v);
|
|
# endif
|
|
plugin_ui_free(v->ui);
|
|
v->ui = NULL;
|
|
return Steinberg_kResultTrue;
|
|
}
|
|
|
|
static Steinberg_tresult plugViewOnWheel(void* thisInterface, float distance) {
|
|
(void)thisInterface;
|
|
(void)distance;
|
|
|
|
TRACE("plugView onWheel %p\n", thisInterface);
|
|
//TODO
|
|
return Steinberg_kResultFalse;
|
|
}
|
|
|
|
static Steinberg_tresult plugViewOnKeyDown(void* thisInterface, Steinberg_char16 key, Steinberg_int16 keyCode, Steinberg_int16 modifiers) {
|
|
(void)thisInterface;
|
|
(void)key;
|
|
(void)keyCode;
|
|
(void)modifiers;
|
|
|
|
TRACE("plugView onKeyDown %p\n", thisInterface);
|
|
//TODO
|
|
return Steinberg_kResultFalse;
|
|
}
|
|
|
|
static Steinberg_tresult plugViewOnKeyUp(void* thisInterface, Steinberg_char16 key, Steinberg_int16 keyCode, Steinberg_int16 modifiers) {
|
|
(void)thisInterface;
|
|
(void)key;
|
|
(void)keyCode;
|
|
(void)modifiers;
|
|
|
|
TRACE("plugView onKeyUp %p\n", thisInterface);
|
|
//TODO
|
|
return Steinberg_kResultFalse;
|
|
}
|
|
|
|
static Steinberg_tresult plugViewGetSize(void* thisInterface, struct Steinberg_ViewRect* size) {
|
|
TRACE("plugView getSize %p %p\n", thisInterface, (void*) size);
|
|
if (!size)
|
|
return Steinberg_kInvalidArgument;
|
|
plugView *v = (plugView *)((char *)thisInterface - offsetof(plugView, vtblIPlugView));
|
|
size->left = 0;
|
|
size->top = 0;
|
|
if (!v->ui) {
|
|
uint32_t width, height;
|
|
plugin_ui_get_default_size(&width, &height);
|
|
size->right = width;
|
|
size->bottom = height;
|
|
} else {
|
|
# ifdef __linux__
|
|
XWindowAttributes attr;
|
|
TRACE(" window %u\n", (Window)(*((char **)v->ui)));
|
|
XGetWindowAttributes(v->display, (Window)(*((char **)v->ui)), &attr);
|
|
size->right = attr.width;
|
|
size->bottom = attr.height;
|
|
# endif
|
|
}
|
|
TRACE(" %u x %u\n", size->right, size->bottom);
|
|
return Steinberg_kResultTrue;
|
|
}
|
|
|
|
static Steinberg_tresult plugViewOnSize(void* thisInterface, struct Steinberg_ViewRect* newSize) {
|
|
TRACE("plugView onSize %p\n", thisInterface);
|
|
plugView *v = (plugView *)((char *)thisInterface - offsetof(plugView, vtblIPlugView));
|
|
// TODO: if not resizable by both user and plugin just return false
|
|
if (!v->ui)
|
|
return Steinberg_kResultFalse;
|
|
# ifdef __linux__
|
|
TRACE(" window %u\n", (Window)(*((char **)v->ui)));
|
|
XResizeWindow(v->display, (Window)(*((char **)v->ui)), newSize->right - newSize->left, newSize->bottom - newSize->top);
|
|
# elif defined(__APPLE__)
|
|
CGSize size = { newSize->right - newSize->left, newSize->bottom - newSize->top };
|
|
void (*f)(id, SEL, CGSize) = (void (*)(id, SEL, CGSize))objc_msgSend;
|
|
f((id)(*((char **)v->ui)), sel_getUid("setFrameSize:"), size);
|
|
# elif defined(_WIN32) || defined(__CYGWIN__)
|
|
SetWindowPos((HWND)*((char **)v->ui), HWND_TOP, 0, 0, newSize->right - newSize->left, newSize->bottom - newSize->top, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER);
|
|
# endif
|
|
return Steinberg_kResultTrue;
|
|
}
|
|
|
|
static Steinberg_tresult plugViewOnFocus(void* thisInterface, Steinberg_TBool state) {
|
|
(void)thisInterface;
|
|
(void)state;
|
|
|
|
TRACE("plugView onFocus %p\n", thisInterface);
|
|
//TODO
|
|
return Steinberg_kResultFalse;
|
|
}
|
|
|
|
static Steinberg_tresult plugViewSetFrame(void* thisInterface, struct Steinberg_IPlugFrame* frame) {
|
|
TRACE("plugView setFrame %p\n", thisInterface);
|
|
plugView *v = (plugView *)((char *)thisInterface - offsetof(plugView, vtblIPlugView));
|
|
v->frame = frame;
|
|
# ifdef __linux__
|
|
if (v->frame) {
|
|
if (v->frame->lpVtbl->queryInterface(v->frame, Steinberg_IRunLoop_iid, (void **)&v->runLoop) != Steinberg_kResultOk)
|
|
return Steinberg_kResultFalse;
|
|
v->frame->lpVtbl->release(v->frame);
|
|
}
|
|
# endif
|
|
return Steinberg_kResultTrue;
|
|
}
|
|
|
|
static Steinberg_tresult plugViewCanResize(void* thisInterface) {
|
|
(void) thisInterface;
|
|
|
|
TRACE("plugView canResize %p\n", thisInterface);
|
|
# if DATA_UI_USER_RESIZABLE
|
|
return Steinberg_kResultTrue;
|
|
# else
|
|
return Steinberg_kResultFalse;
|
|
# endif
|
|
}
|
|
|
|
static Steinberg_tresult plugViewCheckSizeConstraint(void* thisInterface, struct Steinberg_ViewRect* rect) {
|
|
(void)thisInterface;
|
|
(void)rect;
|
|
|
|
TRACE("plugView chekSizeContraint %p\n", thisInterface);
|
|
# if DATA_UI_USER_RESIZABLE
|
|
return Steinberg_kResultTrue;
|
|
# else
|
|
# ifdef __linux__
|
|
plugView *v = (plugView *)((char *)thisInterface - offsetof(plugView, vtblIPlugView));
|
|
|
|
XWindowAttributes attr;
|
|
XGetWindowAttributes(v->display, (Window)(*((char **)v->ui)), &attr);
|
|
rect->right = rect->left + attr.width;
|
|
rect->bottom = rect->top + attr.height;
|
|
# endif
|
|
return Steinberg_kResultFalse;
|
|
# endif
|
|
}
|
|
|
|
# ifdef __linux__
|
|
static void plugViewOnTimer(void *thisInterface) {
|
|
TRACE("plugView onTimer %p\n", thisInterface);
|
|
plugView *v = (plugView *)((char *)thisInterface - offsetof(plugView, vtblIPlugView));
|
|
|
|
// Bitwig doesn't call onSize() as it should, so we compare the editor and parent window and resize if needed
|
|
Window w = (Window)(*((char **)v->ui));
|
|
Window root, parent, *children;
|
|
unsigned nchildren;
|
|
if (XQueryTree(v->display, w, &root, &parent, &children, &nchildren)) {
|
|
if (children)
|
|
XFree(children);
|
|
XWindowAttributes parent_attr, editor_attr;
|
|
XGetWindowAttributes(v->display, parent, &parent_attr);
|
|
XGetWindowAttributes(v->display, w, &editor_attr);
|
|
if (parent_attr.width != editor_attr.width || parent_attr.height != editor_attr.height)
|
|
XResizeWindow(v->display, w, parent_attr.width, parent_attr.height);
|
|
}
|
|
|
|
plugin_ui_idle(v->ui);
|
|
}
|
|
# endif
|
|
|
|
static Steinberg_IPlugViewVtbl plugViewVtblIPlugView = {
|
|
/* FUnknown */
|
|
/* .queryInterface = */ plugViewQueryInterface,
|
|
/* .addRef = */ plugViewAddRef,
|
|
/* .release = */ plugViewRelease,
|
|
|
|
/* IPlugView */
|
|
/* .isPlatformTypeSupported = */ plugViewIsPlatformTypeSupported,
|
|
/* .attached = */ plugViewAttached,
|
|
/* .removed = */ plugViewRemoved,
|
|
/* .onWheel = */ plugViewOnWheel,
|
|
/* .onKeyDown = */ plugViewOnKeyDown,
|
|
/* .onKeyUp = */ plugViewOnKeyUp,
|
|
/* .getSize = */ plugViewGetSize,
|
|
/* .onSize = */ plugViewOnSize,
|
|
/* .onFocus = */ plugViewOnFocus,
|
|
/* .setFrame = */ plugViewSetFrame,
|
|
/* .canResize = */ plugViewCanResize,
|
|
/* .checkSizeConstraint = */ plugViewCheckSizeConstraint
|
|
};
|
|
#endif
|
|
|
|
static Steinberg_Vst_IMidiMappingVtbl controllerVtblIMidiMapping;
|
|
#ifdef DATA_UI
|
|
//static Steinberg_Vst_IConnectionPointVtbl controllerVtblIConnectionPoint;
|
|
#endif
|
|
|
|
static Steinberg_tresult controllerQueryInterface(controller *c, const Steinberg_TUID iid, void ** obj) {
|
|
// Same as above (pluginQueryInterface)
|
|
size_t offset;
|
|
if (memcmp(iid, Steinberg_FUnknown_iid, sizeof(Steinberg_TUID)) == 0
|
|
|| memcmp(iid, Steinberg_IPluginBase_iid, sizeof(Steinberg_TUID)) == 0
|
|
|| memcmp(iid, Steinberg_Vst_IEditController_iid, sizeof(Steinberg_TUID)) == 0)
|
|
offset = offsetof(controller, vtblIEditController);
|
|
else if (memcmp(iid, Steinberg_Vst_IMidiMapping_iid, sizeof(Steinberg_TUID)) == 0)
|
|
offset = offsetof(controller, vtblIMidiMapping);
|
|
#ifdef DATA_UI
|
|
/*else if (memcmp(iid, Steinberg_Vst_IConnectionPoint_iid, sizeof(Steinberg_TUID)) == 0)
|
|
offset = offsetof(controller, vtblIConnectionPoint);*/
|
|
#endif
|
|
else {
|
|
TRACE(" not supported\n");
|
|
for (int i = 0; i < 16; i++)
|
|
TRACE(" %x", iid[i]);
|
|
TRACE("\n");
|
|
*obj = NULL;
|
|
return Steinberg_kNoInterface;
|
|
}
|
|
*obj = (void *)((char *)c + offset);
|
|
c->refs++;
|
|
return Steinberg_kResultOk;
|
|
}
|
|
|
|
static Steinberg_uint32 controllerAddRef(controller *c) {
|
|
c->refs++;
|
|
return c->refs;
|
|
}
|
|
|
|
static Steinberg_uint32 controllerRelease(controller *c) {
|
|
c->refs--;
|
|
if (c->refs == 0) {
|
|
TRACE(" free %p\n", (void *)c);
|
|
#ifdef DATA_UI
|
|
if (c->views) {
|
|
for (size_t i = 0; i < c->viewsCount; i++)
|
|
if (c->views[i]) // this should not happen but you never know
|
|
plugViewRelease(c->views[i]);
|
|
free(c->views);
|
|
}
|
|
#endif
|
|
free(c);
|
|
return 0;
|
|
}
|
|
return c->refs;
|
|
}
|
|
|
|
static Steinberg_tresult controllerIEditControllerQueryInterface(void* thisInterface, const Steinberg_TUID iid, void** obj) {
|
|
TRACE("controller IEditController queryInterface %p\n", thisInterface);
|
|
return controllerQueryInterface((controller *)((char *)thisInterface - offsetof(controller, vtblIEditController)), iid, obj);
|
|
}
|
|
|
|
static Steinberg_uint32 controllerIEditControllerAddRef(void* thisInterface) {
|
|
TRACE("controller IEditController addRef %p\n", thisInterface);
|
|
return controllerAddRef((controller *)((char *)thisInterface - offsetof(controller, vtblIEditController)));
|
|
}
|
|
|
|
static Steinberg_uint32 controllerIEditControllerRelease(void* thisInterface) {
|
|
TRACE("controller IEditController release %p\n", thisInterface);
|
|
return controllerRelease((controller *)((char *)thisInterface - offsetof(controller, vtblIEditController)));
|
|
}
|
|
|
|
static Steinberg_tresult controllerInitialize(void* thisInterface, struct Steinberg_FUnknown* context) {
|
|
TRACE("controller initialize\n");
|
|
controller *c = (controller *)((char *)thisInterface - offsetof(controller, vtblIEditController));
|
|
if (c->context != NULL)
|
|
return Steinberg_kResultFalse;
|
|
c->context = context;
|
|
#if DATA_PRODUCT_PARAMETERS_N > 0
|
|
for (int i = 0; i < DATA_PRODUCT_PARAMETERS_N; i++)
|
|
c->parameters[i] = parameterData[i].def;
|
|
#endif
|
|
#if DATA_PRODUCT_BUSES_MIDI_INPUT_N > 0
|
|
for (int i = DATA_PRODUCT_PARAMETERS_N; i < DATA_PRODUCT_PARAMETERS_N + 3 * DATA_PRODUCT_BUSES_MIDI_INPUT_N; i += 3) {
|
|
c->parameters[i] = 0.0;
|
|
c->parameters[i + 1] = 0.5;
|
|
c->parameters[i + 2] = 0.0;
|
|
}
|
|
#endif
|
|
return Steinberg_kResultOk;
|
|
}
|
|
|
|
static Steinberg_tresult controllerTerminate(void* thisInterface) {
|
|
TRACE("controller terminate\n");
|
|
controller *c = (controller *)((char *)thisInterface - offsetof(controller, vtblIEditController));
|
|
c->context = NULL;
|
|
return Steinberg_kResultOk;
|
|
}
|
|
|
|
static Steinberg_tresult controllerSetComponentState(void* thisInterface, struct Steinberg_IBStream* state) {
|
|
TRACE("controller set component state %p %p\n", thisInterface, (void *)state);
|
|
if (state == NULL)
|
|
return Steinberg_kResultFalse;
|
|
#if DATA_PRODUCT_PARAMETERS_N > 0
|
|
controller *c = (controller *)((char *)thisInterface - offsetof(controller, vtblIEditController));
|
|
for (size_t i = 0; i < DATA_PRODUCT_PARAMETERS_N; i++) {
|
|
if (parameterInfo[i].flags & Steinberg_Vst_ParameterInfo_ParameterFlags_kIsReadOnly)
|
|
continue;
|
|
union { float f; uint32_t u; } v;
|
|
Steinberg_int32 n;
|
|
state->lpVtbl->read(state, &v, 4, &n);
|
|
if (n != 4)
|
|
return Steinberg_kResultFalse;
|
|
if (IS_BIG_ENDIAN)
|
|
v.u = SWAP_UINT32(v.u);
|
|
c->parameters[i] = parameterAdjust(i, v.f);
|
|
}
|
|
#endif
|
|
TRACE(" ok\n");
|
|
return Steinberg_kResultTrue;
|
|
}
|
|
|
|
static Steinberg_tresult controllerSetState(void* thisInterface, struct Steinberg_IBStream* state) {
|
|
(void)thisInterface;
|
|
(void)state;
|
|
|
|
TRACE("controller set state\n");
|
|
return Steinberg_kNotImplemented;
|
|
}
|
|
|
|
static Steinberg_tresult controllerGetState(void* thisInterface, struct Steinberg_IBStream* state) {
|
|
(void)thisInterface;
|
|
(void)state;
|
|
|
|
TRACE("controller get state\n");
|
|
return Steinberg_kNotImplemented;
|
|
}
|
|
|
|
static Steinberg_int32 controllerGetParameterCount(void* thisInterface) {
|
|
(void)thisInterface;
|
|
|
|
TRACE("controller get parameter count\n");
|
|
return DATA_PRODUCT_PARAMETERS_N + 3 * DATA_PRODUCT_BUSES_MIDI_INPUT_N;
|
|
}
|
|
|
|
static Steinberg_tresult controllerGetParameterInfo(void* thisInterface, Steinberg_int32 paramIndex, struct Steinberg_Vst_ParameterInfo* info) {
|
|
(void)thisInterface;
|
|
|
|
TRACE("controller get parameter info\n");
|
|
if (paramIndex < 0 || paramIndex >= DATA_PRODUCT_PARAMETERS_N + 3 * DATA_PRODUCT_BUSES_MIDI_INPUT_N)
|
|
return Steinberg_kResultFalse;
|
|
*info = parameterInfo[paramIndex];
|
|
return Steinberg_kResultTrue;
|
|
}
|
|
|
|
static void dToStr(double v, Steinberg_Vst_String128 s, int precision) {
|
|
int i = 0;
|
|
|
|
if (v < 0.0) {
|
|
s[0] = '-';
|
|
v = -v;
|
|
i++;
|
|
}
|
|
|
|
if (v < 1.0) {
|
|
s[i] = '0';
|
|
i++;
|
|
} else {
|
|
double x = 1.0;
|
|
while (x <= v)
|
|
x *= 10.0;
|
|
x *= 0.1;
|
|
while (x >= 1.0) {
|
|
char c = v / x;
|
|
s[i] = c + '0';
|
|
i++;
|
|
v -= c * x;
|
|
x *= 0.1;
|
|
}
|
|
}
|
|
|
|
s[i] = '.';
|
|
i++;
|
|
|
|
double x = 0.1;
|
|
while (precision != 0) {
|
|
char c = v / x;
|
|
s[i] = c + '0';
|
|
i++;
|
|
v -= c * x;
|
|
x *= 0.1;
|
|
precision--;
|
|
}
|
|
|
|
s[i] = '\0';
|
|
}
|
|
|
|
static Steinberg_tresult controllerGetParamStringByValue(void* thisInterface, Steinberg_Vst_ParamID id, Steinberg_Vst_ParamValue valueNormalized, Steinberg_Vst_String128 string) {
|
|
(void)thisInterface;
|
|
|
|
TRACE("controller get param string by value\n");
|
|
int pi = parameterGetIndexById(id);
|
|
if (pi >= DATA_PRODUCT_PARAMETERS_N + 3 * DATA_PRODUCT_BUSES_MIDI_INPUT_N || pi < 0)
|
|
return Steinberg_kResultFalse;
|
|
dToStr(pi >= DATA_PRODUCT_PARAMETERS_N ? valueNormalized : parameterMap(pi, valueNormalized), string, 2);
|
|
return Steinberg_kResultTrue;
|
|
}
|
|
|
|
void TCharToD(Steinberg_Vst_TChar* s, double *v) {
|
|
int i = 0;
|
|
*v = 0.0;
|
|
|
|
if (s[0] == '-') {
|
|
*v = -0.0;
|
|
i++;
|
|
}
|
|
|
|
while (s[i] >= '0' && s[i] <= '9') {
|
|
char d = s[i] - '0';
|
|
i++;
|
|
*v = 10.0 * *v + d;
|
|
}
|
|
|
|
if (s[i] != '.')
|
|
return;
|
|
i++;
|
|
|
|
double x = 1.0;
|
|
while (s[i] >= '0' && s[i] <= '9') {
|
|
char d = s[i] - '0';
|
|
i++;
|
|
x *= 0.1;
|
|
*v = *v + d * x;
|
|
}
|
|
}
|
|
|
|
static Steinberg_tresult controllerGetParamValueByString(void* thisInterface, Steinberg_Vst_ParamID id, Steinberg_Vst_TChar* string, Steinberg_Vst_ParamValue* valueNormalized) {
|
|
(void)thisInterface;
|
|
|
|
TRACE("controller get param value by string\n");
|
|
int pi = parameterGetIndexById(id);
|
|
if (pi >= DATA_PRODUCT_PARAMETERS_N + 3 * DATA_PRODUCT_BUSES_MIDI_INPUT_N || pi < 0)
|
|
return Steinberg_kResultFalse;
|
|
double v;
|
|
TCharToD(string, &v);
|
|
*valueNormalized = pi >= DATA_PRODUCT_PARAMETERS_N ? v : parameterUnmap(pi, v);
|
|
return Steinberg_kResultTrue;
|
|
}
|
|
|
|
static Steinberg_Vst_ParamValue controllerNormalizedParamToPlain(void* thisInterface, Steinberg_Vst_ParamID id, Steinberg_Vst_ParamValue valueNormalized) {
|
|
(void)thisInterface;
|
|
|
|
TRACE("controller normalized param to plain\n");
|
|
int pi = parameterGetIndexById(id);
|
|
return pi >= DATA_PRODUCT_PARAMETERS_N ? valueNormalized : parameterMap(pi, valueNormalized);
|
|
}
|
|
|
|
static Steinberg_Vst_ParamValue controllerPlainParamToNormalized(void* thisInterface, Steinberg_Vst_ParamID id, Steinberg_Vst_ParamValue plainValue) {
|
|
(void)thisInterface;
|
|
|
|
TRACE("controller plain param to normalized\n");
|
|
int pi = parameterGetIndexById(id);
|
|
return pi >= DATA_PRODUCT_PARAMETERS_N ? plainValue : parameterUnmap(pi, plainValue);
|
|
}
|
|
|
|
static Steinberg_Vst_ParamValue controllerGetParamNormalized(void* thisInterface, Steinberg_Vst_ParamID id) {
|
|
TRACE("controller get param normalized\n");
|
|
#if DATA_PRODUCT_PARAMETERS_N + DATA_PRODUCT_BUSES_MIDI_INPUT_N > 0
|
|
controller *c = (controller *)((char *)thisInterface - offsetof(controller, vtblIEditController));
|
|
int pi = parameterGetIndexById(id);
|
|
# if DATA_PRODUCT_BUSES_MIDI_INPUT_N > 0
|
|
return pi >= DATA_PRODUCT_PARAMETERS_N ? c->parameters[pi] : parameterUnmap(pi, c->parameters[pi]);
|
|
# else
|
|
return parameterUnmap(pi, c->parameters[pi]);
|
|
# endif
|
|
#else
|
|
(void)thisInterface;
|
|
(void)id;
|
|
return 0.0;
|
|
#endif
|
|
}
|
|
|
|
static Steinberg_tresult controllerSetParamNormalized(void* thisInterface, Steinberg_Vst_ParamID id, Steinberg_Vst_ParamValue value) {
|
|
TRACE("controller set param normalized\n");
|
|
#if DATA_PRODUCT_PARAMETERS_N + DATA_PRODUCT_BUSES_MIDI_INPUT_N > 0
|
|
int pi = parameterGetIndexById(id);
|
|
if (pi >= DATA_PRODUCT_PARAMETERS_N + 3 * DATA_PRODUCT_BUSES_MIDI_INPUT_N || pi < 0)
|
|
return Steinberg_kResultFalse;
|
|
controller *c = (controller *)((char *)thisInterface - offsetof(controller, vtblIEditController));
|
|
c->parameters[pi] = pi >= DATA_PRODUCT_PARAMETERS_N ? value : parameterAdjust(pi, parameterMap(pi, value));
|
|
# ifdef DATA_UI
|
|
for (size_t i = 0; i < c->viewsCount; i++)
|
|
if(c->views[i])
|
|
plugViewUpdateParameter(c->views[i], pi);
|
|
# endif
|
|
return Steinberg_kResultTrue;
|
|
#else
|
|
(void)thisInterface;
|
|
(void)id;
|
|
(void)value;
|
|
return Steinberg_kResultFalse;
|
|
#endif
|
|
}
|
|
|
|
static Steinberg_tresult controllerSetComponentHandler(void* thisInterface, struct Steinberg_Vst_IComponentHandler* handler) {
|
|
TRACE("controller set component handler\n");
|
|
controller *c = (controller *)((char *)thisInterface - offsetof(controller, vtblIEditController));
|
|
if (c->componentHandler != handler) {
|
|
if (c->componentHandler != NULL)
|
|
c->componentHandler->lpVtbl->release(c->componentHandler);
|
|
c->componentHandler = handler;
|
|
if (c->componentHandler != NULL)
|
|
c->componentHandler->lpVtbl->addRef(c->componentHandler);
|
|
}
|
|
return Steinberg_kResultTrue;
|
|
}
|
|
|
|
static struct Steinberg_IPlugView* controllerCreateView(void* thisInterface, Steinberg_FIDString name) {
|
|
TRACE("controller create view %s\n", name);
|
|
|
|
#ifdef DATA_UI
|
|
if (strcmp(name, "editor"))
|
|
return NULL;
|
|
|
|
plugView *view = malloc(sizeof(plugView));
|
|
if (view == NULL)
|
|
return NULL;
|
|
|
|
controller *c = (controller *)((char *)thisInterface - offsetof(controller, vtblIEditController));
|
|
size_t i;
|
|
for (i = 0; i < c->viewsCount; c++)
|
|
if (c->views[i] == NULL)
|
|
break;
|
|
if (i == c->viewsCount) {
|
|
size_t cnt = i + 1;
|
|
plugView **views = realloc(c->views, cnt * sizeof(plugView **));
|
|
if (views == NULL) {
|
|
free(view);
|
|
return NULL;
|
|
}
|
|
c->views = views;
|
|
c->viewsCount = cnt;
|
|
}
|
|
c->views[i] = view;
|
|
|
|
view->vtblIPlugView = &plugViewVtblIPlugView;
|
|
view->refs = 1;
|
|
view->frame = NULL;
|
|
view->ui = NULL;
|
|
view->ctrl = c;
|
|
# ifdef __linux__
|
|
view->timer.vtblITimerHandler = &timerHandlerVtblITimerHandler;
|
|
view->timer.refs = 1;
|
|
view->timer.cb = plugViewOnTimer;
|
|
view->timer.data = view;
|
|
# endif
|
|
|
|
return (struct Steinberg_IPlugView *)view;
|
|
#else
|
|
(void)thisInterface;
|
|
(void)name;
|
|
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|
|
static Steinberg_Vst_IEditControllerVtbl controllerVtblIEditController = {
|
|
/* FUnknown */
|
|
/* .queryInterface = */ controllerIEditControllerQueryInterface,
|
|
/* .addRef = */ controllerIEditControllerAddRef,
|
|
/* .release = */ controllerIEditControllerRelease,
|
|
|
|
/* IPluginBase */
|
|
/* .initialize = */ controllerInitialize,
|
|
/* .terminate = */ controllerTerminate,
|
|
|
|
/* IEditController */
|
|
/* .setComponentState = */ controllerSetComponentState,
|
|
/* .setState = */ controllerSetState,
|
|
/* .getState = */ controllerGetState,
|
|
/* .getParameterCount = */ controllerGetParameterCount,
|
|
/* .getParameterInfo = */ controllerGetParameterInfo,
|
|
/* .getParamStringByValue = */ controllerGetParamStringByValue,
|
|
/* .getParamValueByString = */ controllerGetParamValueByString,
|
|
/* .normalizedParamToPlain = */ controllerNormalizedParamToPlain,
|
|
/* .plainParamToNormalized = */ controllerPlainParamToNormalized,
|
|
/* .getParamNormalized = */ controllerGetParamNormalized,
|
|
/* .setParamNormalized = */ controllerSetParamNormalized,
|
|
/* .setComponentHandler = */ controllerSetComponentHandler,
|
|
/* .createView = */ controllerCreateView
|
|
};
|
|
|
|
static Steinberg_tresult controllerIMidiMappingQueryInterface(void* thisInterface, const Steinberg_TUID iid, void** obj) {
|
|
TRACE("controller IMidiMapping queryInterface %p\n", thisInterface);
|
|
return controllerQueryInterface((controller *)((char *)thisInterface - offsetof(controller, vtblIMidiMapping)), iid, obj);
|
|
}
|
|
|
|
static Steinberg_uint32 controllerIMidiMappingAddRef(void* thisInterface) {
|
|
TRACE("controller IMidiMapping addRef %p\n", thisInterface);
|
|
return controllerAddRef((controller *)((char *)thisInterface - offsetof(controller, vtblIMidiMapping)));
|
|
}
|
|
|
|
static Steinberg_uint32 controllerIMidiMappingRelease(void* thisInterface) {
|
|
TRACE("controller IMidiMapping release %p\n", thisInterface);
|
|
return controllerRelease((controller *)((char *)thisInterface - offsetof(controller, vtblIMidiMapping)));
|
|
}
|
|
|
|
static Steinberg_tresult controllerGetMidiControllerAssignment(void* thisInterface, Steinberg_int32 busIndex, Steinberg_int16 channel, Steinberg_Vst_CtrlNumber midiControllerNumber, Steinberg_Vst_ParamID* id) {
|
|
(void)thisInterface;
|
|
(void)channel;
|
|
|
|
TRACE("controller getMidiControllerAssignment\n");
|
|
if (busIndex < 0 || busIndex >= DATA_PRODUCT_BUSES_MIDI_INPUT_N)
|
|
return Steinberg_kInvalidArgument;
|
|
switch (midiControllerNumber) {
|
|
case Steinberg_Vst_ControllerNumbers_kAfterTouch:
|
|
*id = parameterInfo[DATA_PRODUCT_PARAMETERS_N + 3 * busIndex].id;
|
|
return Steinberg_kResultTrue;
|
|
break;
|
|
case Steinberg_Vst_ControllerNumbers_kPitchBend:
|
|
*id = parameterInfo[DATA_PRODUCT_PARAMETERS_N + 3 * busIndex + 1].id;
|
|
return Steinberg_kResultTrue;
|
|
break;
|
|
case Steinberg_Vst_ControllerNumbers_kCtrlModWheel:
|
|
*id = parameterInfo[DATA_PRODUCT_PARAMETERS_N + 3 * busIndex + 2].id;
|
|
return Steinberg_kResultTrue;
|
|
break;
|
|
default:
|
|
return Steinberg_kResultFalse;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static Steinberg_Vst_IMidiMappingVtbl controllerVtblIMidiMapping = {
|
|
/* FUnknown */
|
|
/* .queryInterface = */ controllerIMidiMappingQueryInterface,
|
|
/* .addRef = */ controllerIMidiMappingAddRef,
|
|
/* .release = */ controllerIMidiMappingRelease,
|
|
|
|
/* IMidiMapping */
|
|
/* .getMidiControllerAssignment = */ controllerGetMidiControllerAssignment
|
|
};
|
|
|
|
#ifdef DATA_UI
|
|
# if 0
|
|
static Steinberg_tresult controllerIConnectionPointQueryInterface(void* thisInterface, const Steinberg_TUID iid, void** obj) {
|
|
TRACE("controller IConnectionPoint queryInterface %p\n", thisInterface);
|
|
return controllerQueryInterface((controller *)((char *)thisInterface - offsetof(controller, vtblIConnectionPoint)), iid, obj);
|
|
}
|
|
|
|
static Steinberg_uint32 controllerIConnectionPointAddRef(void* thisInterface) {
|
|
TRACE("controller IConnectionPoint addRef %p\n", thisInterface);
|
|
return controllerAddRef((controller *)((char *)thisInterface - offsetof(controller, vtblIConnectionPoint)));
|
|
}
|
|
|
|
static Steinberg_uint32 controllerIConnectionPointRelease(void* thisInterface) {
|
|
TRACE("controller IConnectionPoint release %p\n", thisInterface);
|
|
return controllerRelease((controller *)((char *)thisInterface - offsetof(controller, vtblIConnectionPoint)));
|
|
}
|
|
|
|
static Steinberg_tresult controllerIConnectionPointConnect(void* thisInterface, struct Steinberg_Vst_IConnectionPoint* other) {
|
|
(void)thisInterface;
|
|
|
|
return other ? Steinberg_kResultOk : Steinberg_kInvalidArgument;
|
|
}
|
|
|
|
static Steinberg_tresult controllerIConnectionPointDisconnect(void* thisInterface, struct Steinberg_Vst_IConnectionPoint* other) {
|
|
(void)thisInterface;
|
|
(void)other;
|
|
|
|
return Steinberg_kResultOk;
|
|
}
|
|
|
|
static Steinberg_tresult controllerIConnectionPointNotify(void* thisInterface, struct Steinberg_Vst_IMessage* message) {
|
|
(void)thisInterface;
|
|
(void)message;
|
|
|
|
return Steinberg_kResultOk;
|
|
}
|
|
|
|
static Steinberg_Vst_IConnectionPointVtbl controllerVtblIConnectionPoint = {
|
|
/* FUnknown */
|
|
/* .queryInterface = */ controllerIConnectionPointQueryInterface,
|
|
/* .addRef = */ controllerIConnectionPointAddRef,
|
|
/* .release = */ controllerIConnectionPointRelease,
|
|
|
|
/* IConnectionPoint */
|
|
/* .connect = */ controllerIConnectionPointConnect,
|
|
/* .disconnect = */ controllerIConnectionPointDisconnect,
|
|
/* .notify = */ controllerIConnectionPointNotify
|
|
};
|
|
# endif
|
|
#endif
|
|
|
|
static Steinberg_tresult factoryQueryInterface(void *thisInterface, const Steinberg_TUID iid, void ** obj) {
|
|
TRACE("factory queryInterface\n");
|
|
if (memcmp(iid, Steinberg_FUnknown_iid, sizeof(Steinberg_TUID))
|
|
&& memcmp(iid, Steinberg_IPluginFactory_iid, sizeof(Steinberg_TUID))
|
|
&& memcmp(iid, Steinberg_IPluginFactory2_iid, sizeof(Steinberg_TUID))
|
|
&& memcmp(iid, Steinberg_IPluginFactory3_iid, sizeof(Steinberg_TUID))) {
|
|
TRACE(" not supported\n");
|
|
*obj = NULL;
|
|
return Steinberg_kNoInterface;
|
|
}
|
|
*obj = thisInterface;
|
|
return Steinberg_kResultOk;
|
|
}
|
|
|
|
static Steinberg_uint32 factoryAddRef(void *thisInterface) {
|
|
(void)thisInterface;
|
|
|
|
TRACE("factory add ref\n");
|
|
return 1;
|
|
}
|
|
|
|
static Steinberg_uint32 factoryRelease(void *thisInterface) {
|
|
(void)thisInterface;
|
|
|
|
TRACE("factory release\n");
|
|
return 1;
|
|
}
|
|
|
|
static Steinberg_tresult factoryGetFactoryInfo(void *thisInterface, struct Steinberg_PFactoryInfo * info) {
|
|
(void)thisInterface;
|
|
|
|
TRACE("getFactoryInfo\n");
|
|
strcpy(info->vendor, DATA_COMPANY_NAME);
|
|
strcpy(info->url, DATA_COMPANY_URL);
|
|
strcpy(info->email, DATA_COMPANY_EMAIL);
|
|
info->flags = Steinberg_PFactoryInfo_FactoryFlags_kUnicode;
|
|
return Steinberg_kResultOk;
|
|
}
|
|
|
|
static Steinberg_int32 factoryCountClasses(void *thisInterface) {
|
|
(void)thisInterface;
|
|
|
|
TRACE("countClasses\n");
|
|
return 2;
|
|
}
|
|
|
|
static Steinberg_tresult factoryGetClassInfo(void *thisInterface, Steinberg_int32 index, struct Steinberg_PClassInfo * info) {
|
|
(void)thisInterface;
|
|
|
|
TRACE("getClassInfo\n");
|
|
switch (index) {
|
|
case 0:
|
|
TRACE(" class 0\n");
|
|
memcpy(info->cid, dataPluginCID, sizeof(Steinberg_TUID));
|
|
info->cardinality = Steinberg_PClassInfo_ClassCardinality_kManyInstances;
|
|
strcpy(info->category, "Audio Module Class");
|
|
strcpy(info->name, DATA_PRODUCT_NAME);
|
|
break;
|
|
case 1:
|
|
TRACE(" class 1\n");
|
|
memcpy(info->cid, dataControllerCID, sizeof(Steinberg_TUID));
|
|
info->cardinality = Steinberg_PClassInfo_ClassCardinality_kManyInstances;
|
|
strcpy(info->category, "Component Controller Class");
|
|
strcpy(info->name, DATA_PRODUCT_NAME " Controller");
|
|
break;
|
|
default:
|
|
return Steinberg_kInvalidArgument;
|
|
break;
|
|
}
|
|
return Steinberg_kResultOk;
|
|
}
|
|
|
|
static Steinberg_tresult factoryCreateInstance(void *thisInterface, Steinberg_FIDString cid, Steinberg_FIDString iid, void ** obj) {
|
|
(void)thisInterface;
|
|
|
|
TRACE("createInstance\n");
|
|
if (memcmp(cid, dataPluginCID, sizeof(Steinberg_TUID)) == 0) {
|
|
TRACE(" plugin\n");
|
|
size_t offset; // does it actually work like this? or is offset always 0? this seems to be correct and works...
|
|
if ((memcmp(iid, Steinberg_FUnknown_iid, sizeof(Steinberg_TUID)) == 0)
|
|
|| (memcmp(iid, Steinberg_IPluginBase_iid, sizeof(Steinberg_TUID)) != 0)
|
|
|| (memcmp(iid, Steinberg_Vst_IComponent_iid, sizeof(Steinberg_TUID)) != 0)) {
|
|
TRACE(" IComponent\n");
|
|
offset = offsetof(pluginInstance, vtblIComponent);
|
|
} else if (memcmp(iid, Steinberg_Vst_IAudioProcessor_iid, sizeof(Steinberg_TUID)) != 0) {
|
|
TRACE(" IAudioProcessor\n");
|
|
offset = offsetof(pluginInstance, vtblIAudioProcessor);
|
|
} else if (memcmp(iid, Steinberg_Vst_IProcessContextRequirements_iid, sizeof(Steinberg_TUID)) != 0) {
|
|
TRACE(" IProcessContextRequirements\n");
|
|
offset = offsetof(pluginInstance, vtblIProcessContextRequirements);
|
|
} else {
|
|
TRACE(" INothing :(\n");
|
|
for (int i = 0; i < 16; i++)
|
|
TRACE(" %x", iid[i]);
|
|
TRACE("\n");
|
|
return Steinberg_kNoInterface;
|
|
}
|
|
pluginInstance *p = malloc(sizeof(pluginInstance));
|
|
if (p == NULL)
|
|
return Steinberg_kOutOfMemory;
|
|
p->vtblIComponent = &pluginVtblIComponent;
|
|
p->vtblIAudioProcessor = &pluginVtblIAudioProcessor;
|
|
p->vtblIProcessContextRequirements = &pluginVtblIProcessContextRequirements;
|
|
p->refs = 1;
|
|
p->context = NULL;
|
|
*obj = (void *)((char *)p + offset);
|
|
TRACE(" instance: %p\n", (void *)p);
|
|
} else if (memcmp(cid, dataControllerCID, sizeof(Steinberg_TUID)) == 0) {
|
|
TRACE(" controller\n");
|
|
if (memcmp(iid, Steinberg_FUnknown_iid, sizeof(Steinberg_TUID))
|
|
&& memcmp(iid, Steinberg_IPluginBase_iid, sizeof(Steinberg_TUID))
|
|
&& memcmp(iid, Steinberg_Vst_IEditController_iid, sizeof(Steinberg_TUID)))
|
|
return Steinberg_kNoInterface;
|
|
controller *c = malloc(sizeof(controller));
|
|
if (c == NULL)
|
|
return Steinberg_kOutOfMemory;
|
|
c->vtblIEditController = &controllerVtblIEditController;
|
|
c->vtblIMidiMapping = &controllerVtblIMidiMapping;
|
|
#ifdef DATA_UI
|
|
//c->vtblIConnectionPoint = &controllerVtblIConnectionPoint;
|
|
#endif
|
|
c->refs = 1;
|
|
c->context = NULL;
|
|
c->componentHandler = NULL;
|
|
#ifdef DATA_UI
|
|
c->views = NULL;
|
|
c->viewsCount = 0;
|
|
#endif
|
|
*obj = c;
|
|
TRACE(" instance: %p\n", (void *)c);
|
|
} else {
|
|
*obj = NULL;
|
|
return Steinberg_kNoInterface;
|
|
}
|
|
return Steinberg_kResultOk;
|
|
}
|
|
|
|
static Steinberg_tresult factoryGetClassInfo2(void* thisInterface, Steinberg_int32 index, struct Steinberg_PClassInfo2* info) {
|
|
(void)thisInterface;
|
|
|
|
TRACE("getClassInfo2\n");
|
|
switch (index) {
|
|
case 0:
|
|
TRACE(" class 0\n");
|
|
memcpy(info->cid, dataPluginCID, sizeof(Steinberg_TUID));
|
|
info->cardinality = Steinberg_PClassInfo_ClassCardinality_kManyInstances;
|
|
strcpy(info->category, "Audio Module Class");
|
|
strcpy(info->name, DATA_PRODUCT_NAME);
|
|
info->classFlags = Steinberg_Vst_ComponentFlags_kDistributable;
|
|
strcpy(info->subCategories, DATA_VST3_SUBCATEGORY);
|
|
*info->vendor = '\0';
|
|
strcpy(info->version, DATA_PRODUCT_VERSION);
|
|
strcpy(info->sdkVersion, "VST " DATA_VST3_SDK_VERSION " | Tibia");
|
|
break;
|
|
case 1:
|
|
TRACE(" class 1\n");
|
|
memcpy(info->cid, dataControllerCID, sizeof(Steinberg_TUID));
|
|
info->cardinality = Steinberg_PClassInfo_ClassCardinality_kManyInstances;
|
|
strcpy(info->category, "Component Controller Class");
|
|
strcpy(info->name, DATA_PRODUCT_NAME " Controller");
|
|
info->classFlags = 0;
|
|
*info->subCategories = '\0';
|
|
*info->vendor = '\0';
|
|
strcpy(info->version, DATA_PRODUCT_VERSION);
|
|
strcpy(info->sdkVersion, "VST " DATA_VST3_SDK_VERSION " | Tibia");
|
|
break;
|
|
default:
|
|
return Steinberg_kInvalidArgument;
|
|
break;
|
|
}
|
|
return Steinberg_kResultOk;
|
|
}
|
|
|
|
static Steinberg_tresult factoryGetClassInfoUnicode(void* thisInterface, Steinberg_int32 index, struct Steinberg_PClassInfoW* info) {
|
|
(void)thisInterface;
|
|
|
|
TRACE("getClassInfo unicode\n");
|
|
switch (index) {
|
|
case 0:
|
|
TRACE(" class 0\n");
|
|
memcpy(info->cid, dataPluginCID, sizeof(Steinberg_TUID));
|
|
info->cardinality = Steinberg_PClassInfo_ClassCardinality_kManyInstances;
|
|
strcpy(info->category, "Audio Module Class");
|
|
memcpy(info->name, dataProductNameW, 64 * sizeof(Steinberg_char16));
|
|
info->classFlags = Steinberg_Vst_ComponentFlags_kDistributable;
|
|
strcpy(info->subCategories, DATA_VST3_SUBCATEGORY);
|
|
*info->vendor = '\0';
|
|
memcpy(info->version, dataProductVersionW, 64 * sizeof(Steinberg_char16));
|
|
memcpy(info->sdkVersion, dataVST3SDKVersionW, 64 * sizeof(Steinberg_char16));
|
|
break;
|
|
case 1:
|
|
TRACE(" class 1\n");
|
|
memcpy(info->cid, dataControllerCID, sizeof(Steinberg_TUID));
|
|
info->cardinality = Steinberg_PClassInfo_ClassCardinality_kManyInstances;
|
|
strcpy(info->category, "Component Controller Class");
|
|
memcpy(info->name, dataVST3ControllerNameW, 64 * sizeof(Steinberg_char16));
|
|
info->classFlags = 0;
|
|
*info->subCategories = '\0';
|
|
*info->vendor = '\0';
|
|
memcpy(info->version, dataProductVersionW, 64 * sizeof(Steinberg_char16));
|
|
memcpy(info->sdkVersion, dataVST3SDKVersionW, 64 * sizeof(Steinberg_char16));
|
|
break;
|
|
default:
|
|
return Steinberg_kInvalidArgument;
|
|
break;
|
|
}
|
|
return Steinberg_kResultOk;
|
|
}
|
|
|
|
static Steinberg_tresult factorySetHostContext(void* thisInterface, struct Steinberg_FUnknown* context) {
|
|
(void)thisInterface;
|
|
(void)context;
|
|
|
|
TRACE("factory set host context %p %p\n", thisInterface, (void*) context);
|
|
|
|
return Steinberg_kNotImplemented;
|
|
}
|
|
|
|
static Steinberg_IPluginFactory3Vtbl factoryVtbl = {
|
|
/* FUnknown */
|
|
/* .queryInterface = */ factoryQueryInterface,
|
|
/* .addRef = */ factoryAddRef,
|
|
/* .release = */ factoryRelease,
|
|
|
|
/* IPluginFactory */
|
|
/* .getFactoryInfo = */ factoryGetFactoryInfo,
|
|
/* .countClasses = */ factoryCountClasses,
|
|
/* .getClassInfo = */ factoryGetClassInfo,
|
|
/* .createInstance = */ factoryCreateInstance,
|
|
|
|
/* IPluginFactory2 */
|
|
/* .getClassInfo2 = */ factoryGetClassInfo2,
|
|
|
|
/* IPluginFactory3 */
|
|
/* .getClassInfoUnicode = */ factoryGetClassInfoUnicode,
|
|
/* .setHostContext = */ factorySetHostContext
|
|
};
|
|
static Steinberg_IPluginFactory3 factory = { &factoryVtbl };
|
|
|
|
#if defined(_WIN32) || defined(__CYGWIN__)
|
|
# define EXPORT __declspec(dllexport)
|
|
#else
|
|
# define EXPORT __attribute__((visibility("default")))
|
|
#endif
|
|
|
|
EXPORT
|
|
Steinberg_IPluginFactory * GetPluginFactory(void) {
|
|
return (Steinberg_IPluginFactory *)&factory;
|
|
}
|
|
|
|
static int refs = 0;
|
|
|
|
static char vstExit(void) {
|
|
refs--;
|
|
if (refs == 0) {
|
|
free(bindir);
|
|
free(datadir);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
#if defined(_WIN32) || defined(__CYGWIN__)
|
|
|
|
EXPORT APIENTRY BOOL
|
|
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) {
|
|
(void)hInstance;
|
|
(void)lpReserved;
|
|
if (dwReason == DLL_PROCESS_ATTACH) {
|
|
if (refs == 0) {
|
|
int pathLength;
|
|
char path[260];
|
|
HMODULE hm = NULL;
|
|
if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCSTR) &bindir, &hm) == 0) {
|
|
TRACE("GetModuleHandle failed, error = %lu\n", GetLastError());
|
|
return 0;
|
|
}
|
|
if ((pathLength = GetModuleFileName(hm, path, sizeof(path))) == 0) {
|
|
TRACE("GetModuleFileName failed, error = %lu\n", GetLastError());
|
|
return 0;
|
|
}
|
|
char *c = strrchr(path, '\\');
|
|
*c = '\0';
|
|
bindir = (char*) malloc(strlen(path) + 1);
|
|
memcpy(bindir, path, strlen(path));
|
|
bindir[strlen(path)] = '\0';
|
|
c = strrchr(path, '\\');
|
|
*c = '\0';
|
|
static char* res = "\\Resources";
|
|
datadir = malloc(strlen(path) + strlen(res) + 1);
|
|
sprintf(datadir, "%s%s", path, res);
|
|
TRACE("bindir = %s \ndatadir = %s\n", bindir, datadir);
|
|
}
|
|
refs++;
|
|
} else if (dwReason == DLL_PROCESS_DETACH) {
|
|
vstExit();
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
#elif defined(__APPLE__) || defined(__linux__)
|
|
|
|
EXPORT
|
|
# if defined(__APPLE__)
|
|
char bundleEntry(CFBundleRef ref) {
|
|
(void)ref;
|
|
# else
|
|
char ModuleEntry(void *handle) {
|
|
(void)handle;
|
|
# endif
|
|
char *file;
|
|
if (refs == 0) {
|
|
Dl_info info;
|
|
union { void* d; char (*f)(void); } v;
|
|
v.f = vstExit;
|
|
if (dladdr((void*) v.d, &info) == 0)
|
|
return 0;
|
|
file = realpath(info.dli_fname, NULL);
|
|
if (file == NULL)
|
|
return 0;
|
|
char *c = strrchr(file, '/');
|
|
*c = '\0';
|
|
bindir = strdup(file);
|
|
if (bindir == NULL)
|
|
goto err_bindir;
|
|
c = strrchr(file, '/');
|
|
*c = '\0';
|
|
datadir = x_asprintf("%s/Resources", file);
|
|
if (datadir == NULL)
|
|
goto err_datadir;
|
|
free(file);
|
|
TRACE("bindir = %s \ndatadir = %s\n", bindir, datadir);
|
|
}
|
|
refs++;
|
|
return 1;
|
|
|
|
err_datadir:
|
|
free(bindir);
|
|
err_bindir:
|
|
free(file);
|
|
return 0;
|
|
}
|
|
|
|
EXPORT
|
|
# if defined(__APPLE__)
|
|
char bundleExit(void) {
|
|
# else
|
|
char ModuleExit(void) {
|
|
# endif
|
|
return vstExit();
|
|
}
|
|
|
|
#endif
|