initial import

This commit is contained in:
Stefano D'Angelo 2023-12-11 18:54:54 +01:00
commit 81cdabbfe2
16 changed files with 1104 additions and 0 deletions

5
LICENSE Normal file
View File

@ -0,0 +1,5 @@
Copyright 2023 Stefano D'Angelo
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

8
TODO Normal file
View File

@ -0,0 +1,8 @@
* recursive object merge in tibia
* copyrights
* dotjs error on undefined or JSON schema
* type audio + sidechain + cv... to think about
* check all return codes and algos wrt official impl
* escape string and max lengths
* printf -> trace
* EXPORT symbols, visibility hidden

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleName</key>
<string>{{=it.plugin.bundleName}}</string>
<key>CFBundleExecutable</key>
<string>{{=it.plugin.bundleName}}</string>
</dict>
</plist>

View File

@ -0,0 +1 @@
BNDL????

View File

@ -0,0 +1,2 @@
[.ShellClassInfo]
IconResource=Plugin.ico,0

View File

@ -0,0 +1,25 @@
include vars.mk
CC = gcc
CFLAGS = -fPIC -Wall -Wpedantic -Wextra -Wno-unused-parameter
BUNDLE_DIR = ${BUNDLE_NAME}.vst3
SO_DIR := $(shell uname -m)-linux
SO_FILE := ${SO_DIR}/${BUNDLE_NAME}.so
all: build/${BUNDLE_DIR}/Contents/Info.plist build/${BUNDLE_DIR}/Contents/${SO_FILE}
build/${BUNDLE_DIR}/Contents/Info.plist: data/Info.plist | build/${BUNDLE_DIR}/Contents
cp $^ $@
build/${BUNDLE_DIR}/Contents/${SO_FILE}: src/vst3.c | build/${BUNDLE_DIR}/Contents/${SO_DIR}
${CC} $^ -o $@ ${CFLAGS} -I${VST3_C_API_DIR} -shared
build/${BUNDLE_DIR}/Contents build/${BUNDLE_DIR}/Contents/${SO_DIR}:
mkdir -p $@
clean:
rm -fr build
.PHONY: clean

View File

@ -0,0 +1,2 @@
BUNDLE_NAME := {{=it.plugin.bundleName}}
VST3_C_API_DIR := ../../vst3_c_api

102
templates/vst3/src/data.h Normal file
View File

@ -0,0 +1,102 @@
#define DATA_COMPANY_NAME "{{=it.company.name}}"
#define DATA_COMPANY_URL "{{=it.company.url}}"
#define DATA_COMPANY_EMAIL "{{=it.company.email}}"
#define DATA_PLUGIN_NAME "{{=it.plugin.name}}"
#define DATA_PLUGIN_VERSION "{{=it.plugin.version}}"
#define DATA_VST3_PLUGIN_CID_1 0x{{=it.vst3.plugin.cid.substring(0, 8)}}
#define DATA_VST3_PLUGIN_CID_2 0x{{=it.vst3.plugin.cid.substring(8, 16)}}
#define DATA_VST3_PLUGIN_CID_3 0x{{=it.vst3.plugin.cid.substring(16, 24)}}
#define DATA_VST3_PLUGIN_CID_4 0x{{=it.vst3.plugin.cid.substring(24, 32)}}
#define DATA_VST3_CONTROLLER_CID_1 0x{{=it.vst3.controller.cid.substring(0, 8)}}
#define DATA_VST3_CONTROLLER_CID_2 0x{{=it.vst3.controller.cid.substring(8, 16)}}
#define DATA_VST3_CONTROLLER_CID_3 0x{{=it.vst3.controller.cid.substring(16, 24)}}
#define DATA_VST3_CONTROLLER_CID_4 0x{{=it.vst3.controller.cid.substring(24, 32)}}
#define DATA_VST3_SUBCATEGORY "{{=it.vst3.subCategory}}"
#define DATA_PLUGIN_BUSES_AUDIO_INPUT_N {{=it.plugin.buses.filter(x => x.type == "audio" && x.direction == "input").length}}
#define DATA_PLUGIN_BUSES_AUDIO_OUTPUT_N {{=it.plugin.buses.filter(x => x.type == "audio" && x.direction == "output").length}}
#define DATA_PLUGIN_BUSES_EVENT_INPUT_N {{=it.plugin.buses.filter(x => x.type == "event" && x.direction == "input").length}}
#define DATA_PLUGIN_BUSES_EVENT_OUTPUT_N {{=it.plugin.buses.filter(x => x.type == "event" && x.direction == "output").length}}
#if DATA_PLUGIN_BUSES_AUDIO_INPUT_N > 0
static struct Steinberg_Vst_BusInfo busInfoAudioInput[DATA_PLUGIN_BUSES_AUDIO_INPUT_N] = {
{{~it.plugin.buses.filter(x => x.type == "audio" && x.direction == "input") :b}}
{
/* .mediaType = */ Steinberg_Vst_MediaTypes_kAudio,
/* .direction = */ Steinberg_Vst_BusDirections_kInput,
/* .channelCount = */ {{=b.channels}},
/* .name = */ { {{~Array.from(b.name) :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .busType = */ {{?b.aux}}Steinberg_Vst_BusTypes_kAux{{??}}Steinberg_Vst_BusTypes_kMain{{?}},
/* .flags = */ {{?b.cv}}Steinberg_Vst_BusInfo_BusFlags_kIsControlVoltage{{??}}Steinberg_Vst_BusInfo_BusFlags_kDefaultActive{{?}}
},
{{~}}
};
#endif
#if DATA_PLUGIN_BUSES_AUDIO_OUTPUT_N > 0
static struct Steinberg_Vst_BusInfo busInfoAudioOutput[DATA_PLUGIN_BUSES_AUDIO_OUTPUT_N] = {
{{~it.plugin.buses.filter(x => x.type == "audio" && x.direction == "output") :b}}
{
/* .mediaType = */ Steinberg_Vst_MediaTypes_kAudio,
/* .direction = */ Steinberg_Vst_BusDirections_kOutput,
/* .channelCount = */ {{=b.channels}},
/* .name = */ { {{~Array.from(b.name) :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .busType = */ {{?b.aux}}Steinberg_Vst_BusTypes_kAux{{??}}Steinberg_Vst_BusTypes_kMain{{?}},
/* .flags = */ {{?b.cv}}Steinberg_Vst_BusInfo_BusFlags_kIsControlVoltage{{??}}Steinberg_Vst_BusInfo_BusFlags_kDefaultActive{{?}}
},
{{~}}
};
#endif
#if DATA_PLUGIN_BUSES_EVENT_INPUT_N > 0
static struct Steinberg_Vst_BusInfo busInfoEventInput[DATA_PLUGIN_BUSES_EVENT_INPUT_N] = {
{{~it.plugin.buses.filter(x => x.type == "event" && x.direction == "input") :b}}
{
/* .mediaType = */ Steinberg_Vst_MediaTypes_kEvent,
/* .direction = */ Steinberg_Vst_BusDirections_kInput,
/* .channelCount = */ {{=b.channels}},
/* .name = */ { {{~Array.from(b.name) :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .busType = */ Steinberg_Vst_BusTypes_kMain,
/* .flags = */ Steinberg_Vst_BusInfo_BusFlags_kDefaultActive
},
{{~}}
};
#endif
#if DATA_PLUGIN_BUSES_EVENT_OUTPUT_N > 0
static struct Steinberg_Vst_BusInfo busInfoAudioInput[DATA_PLUGIN_BUSES_EVENT_OUTPUT_N] = {
{{~it.plugin.buses.filter(x => x.type == "event" && x.direction == "output") :b}}
{
/* .mediaType = */ Steinberg_Vst_MediaTypes_kEvent,
/* .direction = */ Steinberg_Vst_BusDirections_kOutput,
/* .channelCount = */ {{=b.channels}},
/* .name = */ { {{~Array.from(b.name) :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .busType = */ Steinberg_Vst_BusTypes_kMain,
/* .flags = */ Steinberg_Vst_BusInfo_BusFlags_kDefaultActive
},
{{~}}
};
#endif
#define DATA_PLUGIN_PARAMETERS_N {{=it.plugin.parameters.length}}
#if DATA_PLUGIN_PARAMETERS_N > 0
static struct Steinberg_Vst_ParameterInfo parameterInfo[DATA_PLUGIN_PARAMETERS_N] = {
{{~it.plugin.parameters :p:i}}
{
/* .id = */ {{=i}},
/* .title = */ { {{~Array.from(p.name) :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .shortTitle = */ { {{~Array.from(p.shortName) :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .units = */ { {{~Array.from(p.units) :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .stepCount = */ {{=p.steps}},
/* .defaultNormalizedValue = */ {{=p.defaultValue}},
/* .unitId = */ 0,
/* .flags = */ {{?p.isBypass}}Steinberg_Vst_ParameterInfo_ParameterFlags_kIsBypass | {{?}}{{?p.direction == "input"}}Steinberg_Vst_ParameterInfo_ParameterFlags_kCanAutomate{{??}}Steinberg_Vst_ParameterInfo_ParameterFlags_kIsReadOnly{{?}}
},
{{~}}
};
#endif

View File

@ -0,0 +1,3 @@
typedef struct plugin {
char abc;
} plugin;

800
templates/vst3/src/vst3.c Normal file
View File

@ -0,0 +1,800 @@
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "vst3_c_api.h"
#include "data.h"
#include "plugin.h"
#include <stdio.h>
// COM in C doc:
// https://github.com/rubberduck-vba/Rubberduck/wiki/COM-in-plain-C
// https://devblogs.microsoft.com/oldnewthing/20040205-00/?p=40733
#ifdef NDEBUG
# define TRACE(...) /* do nothing */
#else
# define TRACE(...) printf(__VA_ARGS__)
#endif
static const Steinberg_TUID pluginCID = SMTG_INLINE_UID(DATA_VST3_PLUGIN_CID_1, DATA_VST3_PLUGIN_CID_2, DATA_VST3_PLUGIN_CID_3, DATA_VST3_PLUGIN_CID_4);
static const Steinberg_TUID controllerCID = SMTG_INLINE_UID(DATA_VST3_CONTROLLER_CID_1, DATA_VST3_CONTROLLER_CID_2, DATA_VST3_CONTROLLER_CID_3, DATA_VST3_CONTROLLER_CID_4);
typedef struct pluginInstance {
Steinberg_Vst_IComponentVtbl * vtblIComponent;
Steinberg_Vst_IAudioProcessorVtbl * vtblIAudioProcessor;
Steinberg_Vst_IProcessContextRequirementsVtbl * vtblIProcessContextRequirements;
Steinberg_uint32 refs;
Steinberg_FUnknown * context;
plugin p;
} 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");
*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 *)thisInterface;
if (p->context != NULL)
return Steinberg_kResultFalse;
p->context = context;
//TBD
return Steinberg_kResultOk;
}
static Steinberg_tresult pluginTerminate(void *thisInterface) {
TRACE("plugin terminate\n");
pluginInstance *p = (pluginInstance *)thisInterface;
p->context = NULL;
//TBD
return Steinberg_kResultOk;
}
static Steinberg_tresult pluginGetControllerClassId(void *thisInterface, Steinberg_TUID classId) {
TRACE("plugin get controller class id %p %p\n", thisInterface, classId);
if (classId != NULL) {
memcpy(classId, controllerCID, sizeof(Steinberg_TUID));
return Steinberg_kResultTrue;
}
return Steinberg_kResultFalse;
}
static Steinberg_tresult pluginSetIoMode(void *thisInterface, Steinberg_Vst_IoMode mode) {
TRACE("plugin set io mode\n");
return Steinberg_kNotImplemented;
}
static Steinberg_int32 pluginGetBusCount(void *thisInterface, Steinberg_Vst_MediaType type, Steinberg_Vst_BusDirection dir) {
TRACE("plugin get bus count\n");
if (type == Steinberg_Vst_MediaTypes_kAudio) {
if (dir == Steinberg_Vst_BusDirections_kInput)
return DATA_PLUGIN_BUSES_AUDIO_INPUT_N;
else if (dir == Steinberg_Vst_BusDirections_kOutput)
return DATA_PLUGIN_BUSES_AUDIO_OUTPUT_N;
} else if (type == Steinberg_Vst_MediaTypes_kEvent) {
if (dir == Steinberg_Vst_BusDirections_kInput)
return DATA_PLUGIN_BUSES_EVENT_INPUT_N;
else if (dir == Steinberg_Vst_BusDirections_kOutput)
return DATA_PLUGIN_BUSES_EVENT_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) {
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_PLUGIN_BUSES_AUDIO_INPUT_N > 0
if (index >= DATA_PLUGIN_BUSES_AUDIO_INPUT_N)
return Steinberg_kInvalidArgument;
*bus = busInfoAudioInput[index];
return Steinberg_kResultTrue;
#endif
} else if (dir == Steinberg_Vst_BusDirections_kOutput) {
#if DATA_PLUGIN_BUSES_AUDIO_OUTPUT_N > 0
if (index >= DATA_PLUGIN_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_PLUGIN_BUSES_EVENT_INPUT_N > 0
if (index >= DATA_PLUGIN_BUSES_AUDIO_INPUT_N)
return Steinberg_kInvalidArgument;
*bus = busInfoEventInput[index];
return Steinberg_kResultTrue;
#endif
} else if (dir == Steinberg_Vst_BusDirections_kOutput) {
#if DATA_PLUGIN_BUSES_EVENT_OUTPUT_N > 0
if (index >= DATA_PLUGIN_BUSES_AUDIO_OUTPUT_N)
return Steinberg_kInvalidArgument;
*bus = busInfoEventOutput[index];
return Steinberg_kResultTrue;
#endif
}
}
return Steinberg_kInvalidArgument;
}
static Steinberg_tresult pluginGetRoutingInfo(void* thisInterface, struct Steinberg_Vst_RoutingInfo* inInfo, struct Steinberg_Vst_RoutingInfo* 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;
if (type == Steinberg_Vst_MediaTypes_kAudio) {
if (dir == Steinberg_Vst_BusDirections_kInput) {
#if DATA_PLUGIN_BUSES_AUDIO_INPUT_N > 0
if (index >= DATA_PLUGIN_BUSES_AUDIO_INPUT_N)
return Steinberg_kInvalidArgument;
// TBD
return Steinberg_kResultTrue;
#endif
} else if (dir == Steinberg_Vst_BusDirections_kOutput) {
#if DATA_PLUGIN_BUSES_AUDIO_OUTPUT_N > 0
if (index >= DATA_PLUGIN_BUSES_AUDIO_OUTPUT_N)
return Steinberg_kInvalidArgument;
// TBD
return Steinberg_kResultTrue;
#endif
}
} else if (type == Steinberg_Vst_MediaTypes_kEvent) {
if (dir == Steinberg_Vst_BusDirections_kInput) {
#if DATA_PLUGIN_BUSES_EVENT_INPUT_N > 0
if (index >= DATA_PLUGIN_BUSES_AUDIO_INPUT_N)
return Steinberg_kInvalidArgument;
// TBD
return Steinberg_kResultTrue;
#endif
} else if (dir == Steinberg_Vst_BusDirections_kOutput) {
#if DATA_PLUGIN_BUSES_EVENT_OUTPUT_N > 0
if (index >= DATA_PLUGIN_BUSES_AUDIO_OUTPUT_N)
return Steinberg_kInvalidArgument;
// TBD
return Steinberg_kResultTrue;
#endif
}
}
return Steinberg_kInvalidArgument;
}
static Steinberg_tresult pluginSetActive(void* thisInterface, Steinberg_TBool state) {
TRACE("plugin set active\n");
//TBD
return Steinberg_kResultOk;
}
static Steinberg_tresult pluginSetState(void* thisInterface, struct Steinberg_IBStream* state) {
TRACE("plugin set state\n");
//TBD
return Steinberg_kResultOk;
}
static Steinberg_tresult pluginGetState(void* thisInterface, struct Steinberg_IBStream* state) {
TRACE("plugin get state\n");
//TBD
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) {
TRACE("plugin IAudioProcessor set bus arrangements\n");
//TBD
return Steinberg_kResultOk;
}
static Steinberg_tresult pluginGetBusArrangement(void* thisInterface, Steinberg_Vst_BusDirection dir, Steinberg_int32 index, Steinberg_Vst_SpeakerArrangement* arr) {
TRACE("plugin IAudioProcessor get bus arrangement\n");
//TBD
return Steinberg_kResultOk;
}
static Steinberg_tresult pluginCanProcessSampleSize(void* thisInterface, Steinberg_int32 symbolicSampleSize) {
TRACE("plugin IAudioProcessor can process sample size\n");
//TBD
return Steinberg_kResultOk;
}
static Steinberg_uint32 pluginGetLatencySamples(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");
//TBD
return Steinberg_kResultOk;
}
static Steinberg_tresult pluginSetProcessing(void* thisInterface, Steinberg_TBool state) {
TRACE("plugin IAudioProcessor set processing\n");
//TBD
return Steinberg_kResultOk;
}
static Steinberg_tresult pluginProcess(void* thisInterface, struct Steinberg_Vst_ProcessData* data) {
TRACE("plugin IAudioProcessor process\n");
//TBD
return Steinberg_kResultOk;
}
static Steinberg_uint32 pluginGetTailSamples(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) {
// TBD
return 0;
}
static Steinberg_Vst_IProcessContextRequirementsVtbl pluginVtblIProcessContextRequirements = {
/* FUnknown */
/* .queryInterface = */ pluginIProcessContextRequirementsQueryInterface,
/* .addRef = */ pluginIProcessContextRequirementsAddRef,
/* .release = */ pluginIProcessContextRequirementsRelease,
/* IProcessContextRequirements */
/* .getProcessContextRequirements = */ pluginGetProcessContextRequirements
};
typedef struct controller {
Steinberg_Vst_IEditControllerVtbl * vtblIEditController;
Steinberg_uint32 refs;
Steinberg_FUnknown * context;
double parameters[DATA_PLUGIN_PARAMETERS_N];
} controller;
static Steinberg_tresult controllerQueryInterface(void* thisInterface, const Steinberg_TUID iid, void** obj) {
TRACE("controller queryInterface %p\n", thisInterface);
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))) {
TRACE(" oooo\n");
*obj = NULL;
return Steinberg_kNoInterface;
}
*obj = thisInterface;
controller *c = (controller *)thisInterface;
c->refs++;
return Steinberg_kResultOk;
}
static Steinberg_uint32 controllerAddRef(void* thisInterface) {
TRACE("controller addRef %p\n", thisInterface);
controller *c = (controller *)thisInterface;
c->refs++;
return c->refs;
}
static Steinberg_uint32 controllerRelease (void* thisInterface) {
TRACE("controller release %p\n", thisInterface);
controller *c = (controller *)thisInterface;
c->refs--;
if (c->refs == 0) {
free(c);
return 0;
}
return c->refs;
}
static Steinberg_tresult controllerInitialize(void* thisInterface, struct Steinberg_FUnknown* context) {
TRACE("controller initialize\n");
controller *c = (controller *)thisInterface;
if (c->context != NULL)
return Steinberg_kResultFalse;
c->context = context;
//TBD
return Steinberg_kResultOk;
}
static Steinberg_tresult controllerTerminate(void* thisInterface) {
TRACE("controller terminate\n");
controller *c = (controller *)thisInterface;
c->context = NULL;
//TBD
return Steinberg_kResultOk;
}
static Steinberg_tresult controllerSetComponentState(void* thisInterface, struct Steinberg_IBStream* state) {
TRACE("controller set component state\n");
if (state == NULL)
return Steinberg_kResultFalse;
//TBD
return Steinberg_kResultTrue;
}
static Steinberg_tresult controllerSetState(void* thisInterface, struct Steinberg_IBStream* state) {
TRACE("controller set state\n");
return Steinberg_kNotImplemented;
}
static Steinberg_tresult controllerGetState(void* thisInterface, struct Steinberg_IBStream* state) {
TRACE("controller get state\n");
return Steinberg_kNotImplemented;
}
static Steinberg_int32 controllerGetParameterCount(void* thisInterface) {
TRACE("controller get parameter count\n");
return DATA_PLUGIN_PARAMETERS_N;
}
static Steinberg_tresult controllerGetParameterInfo(void* thisInterface, Steinberg_int32 paramIndex, struct Steinberg_Vst_ParameterInfo* info) {
TRACE("controller get parameter info\n");
if (paramIndex < 0 || paramIndex >= DATA_PLUGIN_PARAMETERS_N)
return Steinberg_kResultFalse;
*info = parameterInfo[paramIndex];
return Steinberg_kResultOk;
}
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) {
TRACE("controller get param string by value\n");
if (id >= DATA_PLUGIN_PARAMETERS_N)
return Steinberg_kResultFalse;
//mapping TBD
dToStr(valueNormalized, string, 2);
return Steinberg_kResultOk;
}
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) {
TRACE("controller get param value by string\n");
if (id >= DATA_PLUGIN_PARAMETERS_N)
return Steinberg_kResultFalse;
//mapping TBD
TCharToD(string, valueNormalized);
return Steinberg_kResultOk;
}
static Steinberg_Vst_ParamValue controllerNormalizedParamToPlain(void* thisInterface, Steinberg_Vst_ParamID id, Steinberg_Vst_ParamValue valueNormalized) {
TRACE("controller normalized param to plain\n");
//mapping TBD
return valueNormalized;
}
static Steinberg_Vst_ParamValue controllerPlainParamToNormalized(void* thisInterface, Steinberg_Vst_ParamID id, Steinberg_Vst_ParamValue plainValue) {
TRACE("controller plain param to normalized\n");
//mapping TBD
return plainValue;
}
static Steinberg_Vst_ParamValue controllerGetParamNormalized(void* thisInterface, Steinberg_Vst_ParamID id) {
TRACE("controller get param normalized\n");
controller *c = (controller *)thisInterface;
return c->parameters[id];
}
static Steinberg_tresult controllerSetParamNormalized(void* thisInterface, Steinberg_Vst_ParamID id, Steinberg_Vst_ParamValue value) {
TRACE("controller set param normalized\n");
if (id >= DATA_PLUGIN_PARAMETERS_N)
return Steinberg_kResultFalse;
controller *c = (controller *)thisInterface;
c->parameters[id] = value;
return Steinberg_kResultOk;
}
static Steinberg_tresult controllerSetComponentHandler(void* thisInterface, struct Steinberg_Vst_IComponentHandler* handler) {
TRACE("controller set component handler\n");
//TBD
return Steinberg_kResultOk;
}
static struct Steinberg_IPlugView* controllerCreateView(void* thisInterface, Steinberg_FIDString name) {
TRACE("controller create view\n");
//TBD
return NULL;
}
static Steinberg_Vst_IEditControllerVtbl controllerVtbl = {
/* FUnknown */
/* .queryInterface = */ controllerQueryInterface,
/* .addRef = */ controllerAddRef,
/* .release = */ controllerRelease,
/* 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 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) {
TRACE("factory add ref\n");
return 1;
}
static Steinberg_uint32 factoryRelease(void *thisInterface) {
TRACE("factory release\n");
return 1;
}
static Steinberg_tresult factoryGetFactoryInfo(void *thisInterface, struct Steinberg_PFactoryInfo * info) {
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) {
TRACE("countClasses\n");
return 2;
}
static Steinberg_tresult factoryGetClassInfo(void *thisInterface, Steinberg_int32 index, struct Steinberg_PClassInfo * info) {
TRACE("getClassInfo\n");
switch (index) {
case 0:
TRACE(" class 0\n");
memcpy(info->cid, pluginCID, sizeof(Steinberg_TUID));
info->cardinality = Steinberg_PClassInfo_ClassCardinality_kManyInstances;
strcpy(info->category, "Audio Module Class");
strcpy(info->name, DATA_PLUGIN_NAME);
break;
case 1:
TRACE(" class 1\n");
memcpy(info->cid, controllerCID, sizeof(Steinberg_TUID));
info->cardinality = Steinberg_PClassInfo_ClassCardinality_kManyInstances;
strcpy(info->category, "Component Controller Class");
strcpy(info->name, DATA_PLUGIN_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) {
TRACE("createInstance\n");
if (memcmp(cid, pluginCID, sizeof(Steinberg_TUID)) == 0) {
TRACE(" plugin\n");
size_t offset; // FIXME: does it actually work like this? or is offset always 0?
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(" IAudioProcessor\n");
offset = offsetof(pluginInstance, vtblIProcessContextRequirements);
} else {
TRACE(" INothing :(\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, controllerCID, 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 = &controllerVtbl;
c->refs = 1;
c->context = NULL;
for (int i = 0; i < DATA_PLUGIN_PARAMETERS_N; i++)
c->parameters[i] = parameterInfo[i].defaultNormalizedValue;
*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) {
TRACE("getClassInfo2\n");
switch (index) {
case 0:
TRACE(" class 0\n");
memcpy(info->cid, pluginCID, sizeof(Steinberg_TUID));
info->cardinality = Steinberg_PClassInfo_ClassCardinality_kManyInstances;
strcpy(info->category, "Audio Module Class");
strcpy(info->name, DATA_PLUGIN_NAME);
info->classFlags = Steinberg_Vst_ComponentFlags_kDistributable;
strcpy(info->subCategories, DATA_VST3_SUBCATEGORY);
*info->vendor = '\0';
strcpy(info->version, DATA_PLUGIN_VERSION);
strcpy(info->sdkVersion, "VST 3.7.4 | Tibia");
break;
case 1:
TRACE(" class 1\n");
memcpy(info->cid, controllerCID, sizeof(Steinberg_TUID));
info->cardinality = Steinberg_PClassInfo_ClassCardinality_kManyInstances;
strcpy(info->category, "Component Controller Class");
strcpy(info->name, DATA_PLUGIN_NAME " Controller");
info->classFlags = 0;
*info->subCategories = '\0';
*info->vendor = '\0';
strcpy(info->version, DATA_PLUGIN_VERSION);
strcpy(info->sdkVersion, "VST 3.7.4 | Tibia");
break;
default:
return Steinberg_kInvalidArgument;
break;
}
return Steinberg_kResultOk;
}
static Steinberg_tresult factoryGetClassInfoUnicode(void* thisInterface, Steinberg_int32 index, struct Steinberg_PClassInfoW* info) {
TRACE("getClassInfo unicode\n");
return Steinberg_kResultOk;
}
static Steinberg_tresult factorySetHostContext(void* thisInterface, struct Steinberg_FUnknown* context) {
TRACE("factory set host context\n");
return Steinberg_kResultOk;
}
static Steinberg_IPluginFactory2Vtbl 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_IPluginFactory2 factory = { &factoryVtbl };
Steinberg_IPluginFactory * GetPluginFactory() {
return (Steinberg_IPluginFactory *)&factory;
}
char ModuleEntry (void *handle) {
return 1;
}
char ModuleExit () {
return 1;
}

View File

@ -0,0 +1,11 @@
var path = require("path");
var sep = path.sep;
module.exports = function (data, api) {
api.generateFileFromTemplateFile(`data${sep}Info.plist`, `data${sep}Info.plist`, data);
api.copyFile(`src${sep}vst3.c`, `src${sep}vst3.c`);
api.generateFileFromTemplateFile(`src${sep}data.h`, `src${sep}data.h`, data);
api.copyFileIfNotExists(`src${sep}plugin.h`, `src${sep}plugin.h`);
api.copyFile(`make${sep}Makefile`, `Makefile`);
api.generateFileFromTemplateFile(`make${sep}vars.mk`, `vars.mk`, data);
};

7
test/company.json Normal file
View File

@ -0,0 +1,7 @@
{
"company": {
"name": "Orastron",
"url": "https://www.orastron.com",
"email": "info@orastron.com"
}
}

45
test/product.json Normal file
View File

@ -0,0 +1,45 @@
{
"plugin": {
"name": "Tibia test plugin",
"version": "1.0.0.1",
"bundleName": "tibia_test",
"buses": [
{
"type": "audio",
"direction": "input",
"channels": 1,
"name": "Input",
"sidechain": false,
"cv": false
},
{
"type": "audio",
"direction": "output",
"channels": 1,
"name": "Output",
"sidechain": false,
"cv": false
}
],
"parameters": [
{
"name": "Gain",
"shortName": "Gain",
"units": "dB",
"steps": 0,
"direction": "input",
"isBypass": false,
"defaultValue": 1.0
},
{
"name": "Bypass",
"shortName": "Bypass",
"units": "",
"steps": 1,
"direction": "input",
"isBypass": true,
"defaultValue": 0.0
}
]
}
}

4
test/run.sh Executable file
View File

@ -0,0 +1,4 @@
#!/bin/sh
dir=`dirname $0`
$dir/../tibia $dir/product.json,$dir/company.json,$dir/vst3.json $dir/../templates/vst3 $dir/../out

11
test/vst3.json Normal file
View File

@ -0,0 +1,11 @@
{
"vst3": {
"plugin": {
"cid": "66994c64bc3449f2b97fd96e36e18ba1"
},
"controller": {
"cid": "ecf4f431312f44fbb37b88c545ae9993"
},
"subCategory": "FX"
}
}

66
tibia Executable file
View File

@ -0,0 +1,66 @@
#!/usr/bin/env node
if (process.argv.length != 5) {
console.log("Usage: tibia file1.json,file2.json,...filen.json template outDirectory");
process.exit(1);
}
var fs = require("fs");
var path = require("path");
var jsonFiles = process.argv[2].split(",");
var template = process.argv[3];
var outputDir = process.argv[4];
var data = {};
for (var i = 0; i < jsonFiles.length; i++) {
var d = JSON.parse(fs.readFileSync(jsonFiles[i], { encoding: "utf-8" }));
for (var k in d)
data[k] = d[k];
}
var doT = require("dot");
doT.templateSettings.strip = false;
var api = {
// https://coderrocketfuel.com/article/recursively-list-all-the-files-in-a-directory-using-node-js
getAllFiles: function (dirPath, arrayOfFiles, relDir) {
var files = fs.readdirSync(dirPath);
var arrayOfFiles = arrayOfFiles || [];
var relDir = relDir || "";
files.forEach(function(file) {
if (fs.statSync(dirPath + path.sep + file).isDirectory())
arrayOfFiles = api.getAllFiles(dirPath + path.sep + file, arrayOfFiles, relDir + file + path.sep);
else
arrayOfFiles.push(relDir + file);
});
return arrayOfFiles
},
generateFileFromTemplateFile: function (templateFile, outFile, data) {
var dir = outputDir + path.sep + path.dirname(outFile);
fs.mkdirSync(dir, { recursive: true });
var t = doT.template(fs.readFileSync(template + path.sep + templateFile, { encoding: "utf-8" }));
fs.writeFileSync(outputDir + path.sep + outFile, t(data), { encoding: "utf-8" });
},
copyFile: function (inFile, outFile) {
var dir = outputDir + path.sep + path.dirname(outFile);
fs.mkdirSync(dir, { recursive: true });
fs.copyFileSync(template + path.sep + inFile, outputDir + path.sep + outFile);
},
copyFileIfNotExists: function (inFile, outFile) {
var p = outputDir + path.sep + outFile;
if (fs.existsSync(p))
return;
var dir = outputDir + path.sep + path.dirname(outFile);
fs.mkdirSync(dir, { recursive: true });
fs.copyFileSync(template + path.sep + inFile, p);
}
};
require(path.resolve(template + path.sep + "tibia-index.js"))(data, api);