Compare commits

...

129 Commits
v0.0.3 ... main

Author SHA1 Message Date
a5727492cd android/ios fix target version handling 2025-04-17 12:17:19 +02:00
402df416f8 remove old file 2025-04-17 12:05:23 +02:00
a437e54408 update android test versions 2025-04-17 12:02:21 +02:00
Stefano D'Angelo
261db0e579 vst3 windows fix machine detection 2025-04-16 08:27:10 +02:00
Stefano D'Angelo
fc7b20a956 removed uselss definition in android-make, manifest strip in lv2 2025-04-14 07:04:33 +02:00
0417448cbd fix license copyright year 2025-03-27 13:28:24 +01:00
1f620b46fd fix copyright year in test/plugin_ui.h + remove old stuff 2025-03-27 13:26:44 +01:00
786a999c5e fix plugin ui initialization 2025-03-25 07:01:26 +01:00
7311d47b9a test makefiles to compile vinci-cocoa 2025-03-24 16:52:19 +01:00
Paolo Marrone
429543e7af use new vinci xcb - currently buggy 2025-03-19 23:45:41 +01:00
bdfcf19fdf test/plugin_ui now uses vinci windowing system + raw pixel drawing. Tested on windows only for now. 2025-03-19 19:51:38 +01:00
44b21010f3 maocs wip 2025-03-18 14:53:57 +01:00
1a4d158bc0 trying without pugl 2025-03-18 13:53:40 +01:00
b3da04681d arm64x -> arm64 2025-03-17 20:40:37 +01:00
33e4dc39ce fix vst3 platform directory on windows 2025-03-17 20:01:45 +01:00
5ddc28440c fix path escaping on windows makefiles 2025-03-17 19:35:55 +01:00
5211cf5a3e update to latest android sdk stuff + allow building android app on non-linux 2025-03-17 16:49:08 +01:00
35f9c0c091 vst3 stricter NULL checks to make pluginval happy 2025-03-12 16:35:13 +01:00
f5c9ebab6a pass sample rate to state save/load 2025-03-08 15:31:31 +01:00
c81cc828aa update plugin_ui.h to latest pugl 2025-02-23 16:21:46 +01:00
Paolo Marrone
ecded4fd65 ui setparam begin/end cbs now receive and handle a value arg 2025-02-10 11:09:44 +01:00
691e0d633d update to latest android packages + fix unused func warning in vst3 2025-02-01 09:11:33 +01:00
2cefabb82f fix conditional code in vst3 2025-01-30 16:21:30 +01:00
83f333ef6b plugViewGetSize impl for apple and windows 2025-01-21 15:48:53 +01:00
c6de99898d fix state sync in lv2 and vst3 2025-01-21 10:05:52 +01:00
85f70c29c1 remove references to rutex 2025-01-21 08:28:49 +01:00
aa9597b5c2 updated all templates + not using math.h in example 2025-01-21 08:16:10 +01:00
efd6033c4b simplied state sync in lv2 + implemented it in vst3 2025-01-21 07:47:45 +01:00
5663b4389c refined state api and implemented in lv2 and vst3 2025-01-20 20:25:39 +01:00
440b49c14d more conditional cbs in api + small lv2 improvements 2025-01-20 12:53:46 +01:00
1af0fb6261 api: conditionally define callbacks 2025-01-20 11:55:09 +01:00
f942ed5dbb lv2: using lv2:optionalFeature state:threadSafeRestore 2025-01-20 10:57:29 +01:00
8f6285a884 lv2 thread-safe store/restore, all other templates are now broken 2025-01-20 10:42:06 +01:00
ae513dae30 lv2: log and urid now always optional, fixed ui-related feature request in ttl, include with angled brackets 2025-01-19 08:28:25 +01:00
63391b158f lv2: fix optionalfeature urid if all midi ports are optional 2025-01-19 07:08:48 +01:00
6e9bdb0ad9 lv2 state and fixes 2025-01-18 18:16:00 +01:00
355cc690fb lv2 mailto and better uris (no need for ui uri) + expertimental lv2-next 2025-01-17 17:13:26 +01:00
10896d7d5b vst3 fixed typos 2025-01-16 21:04:33 +01:00
84888a6433 vst3 split internal parameter data and possibly fixed a bunch of issues 2025-01-16 20:58:23 +01:00
50f5b4b378 vst3 better hash strategy + parameter indices doc 2025-01-16 09:42:37 +01:00
dae279564e vst3: don't link with -ldl, build with rutex, proper shortTitle for MIDI params 2025-01-16 08:10:48 +01:00
a1281745c9 fix tinywav-related typo in cmd-make Makefile 2025-01-15 10:31:51 +01:00
3f55be309d fixed custom state load/save in test plugin 2025-01-13 10:42:38 +01:00
18ca2f9cb5 fix bad setting of instance->gain in example 2025-01-13 10:14:35 +01:00
a13f293479 custom dsp state set/get (vst3 only right now) 2025-01-13 01:15:58 +01:00
3c6719097e added needed ifdef in controller state stuff 2025-01-12 21:57:18 +01:00
211d86f5e9 implemented vst3 controller get/set state 2025-01-12 21:56:16 +01:00
bda8b75c78 sdbm moved to tibia 2025-01-10 17:17:08 +01:00
762c670560 updated android tools versions 2025-01-05 11:08:32 +01:00
cbe446dd20 update copyright notices 2025-01-04 11:35:06 +01:00
acf85a35cf strip web-demo, do not overwrite STRIP_PHONY 2025-01-04 11:16:55 +01:00
7bdceca9dd modular strip (except android yet, ios, daisy-seed) 2025-01-04 09:50:15 +01:00
dce3373d9b beginning of strip 2025-01-02 14:39:48 +01:00
21df671d15 command line data overrides 2024-12-29 11:04:25 +01:00
1be33ad3f0 avoid makefile variable clash with vino in vst3 2024-12-28 10:41:06 +01:00
471d6739ef ios make now uses extra vars from extra mkfiles just like the other templates 2024-12-20 15:45:27 +01:00
fb24537804 ios - open urls externally 2024-12-19 15:09:57 +01:00
6abbbf11ff ios resources are now bundled with xcodegen beneplacito 2024-12-18 18:15:34 +01:00
322e7e0239 ios make add build bin/data dirs 2024-12-16 12:15:45 +01:00
d479f6493f Merge remote-tracking branch 'origin/main' 2024-12-16 11:27:03 +01:00
b74f4c3e87 fix typo bug for objc files in 2nd expansion 2024-12-16 11:26:41 +01:00
Stefano D'Angelo
3958c0d22d remove duplicated dir from lv2 DIRS 2024-12-16 11:16:43 +01:00
f803bbb009 ios - support optional target dependencies 2024-12-16 11:03:36 +01:00
b0f72d6a7d ios - support optional development team 2024-12-16 10:40:35 +01:00
a89dcbcd20 fix lv2 template bug (ui with no output params was broken) 2024-12-15 23:29:21 +01:00
7e1d3a2be2 using DIRS for mkdir -p 2024-12-14 06:59:42 +01:00
709a9b7fae renamed web-demo/*wextra*mk and reorganized test 2024-12-13 14:21:27 +01:00
6b0037a332 added objective-c support in lv2 and vst3 make 2024-12-13 11:51:26 +01:00
3277c4ce08 moved BUILD_BIN/DATA_DIR and made extra non-exclusive 2024-12-13 11:38:34 +01:00
792372e5f1 BUILD_BIN/DATA_DIR 2024-12-13 08:40:48 +01:00
Stefano D'Angelo
f02a9fa554 renamed template common as api, added API_DIR 2024-12-12 12:06:17 +01:00
Stefano D'Angelo
98b7ab1354 added make.extra 2024-12-12 11:33:52 +01:00
c55a1385f2 updated makefiles, updated android versions (ios untested) 2024-12-12 07:39:25 +01:00
0782f8b05d towards makefile-based definitions 2024-12-11 20:18:43 +01:00
d8323436f1 fix web-make directory prereq 2024-12-11 09:40:50 +01:00
Stefano D'Angelo
0be1b41597 web-make, web-demo: moved to "plugin folder" structure 2024-12-09 16:07:06 +01:00
ed187c94dd plugin api in common template and plugin_parameter enum 2024-08-26 17:29:35 +02:00
3dd1a83501 ios doc 2024-08-05 09:31:28 +02:00
Stefano D'Angelo
c53cc855b9 fix vst3 processing with inactive needed buses 2024-07-30 12:23:32 +02:00
24f2d6601b fix vst3 makefile 2024-07-29 16:26:42 +02:00
Stefano D'Angelo
78bb6c54f7 implemented lv2 ui:touch 2024-07-17 11:17:21 +02:00
Stefano D'Angelo
12650980b0 split ui set parameter in begin/perform/end phases 2024-07-17 10:40:55 +02:00
3d1fdd05ee vst3 sync param values between custom and generated UIs in Reaper 2024-07-13 18:42:06 +02:00
Stefano D'Angelo
005a3975a7 use pkg-config in lv2-make 2024-07-08 15:21:28 +02:00
Stefano D'Angelo
a2fced6adb fix apple vst3 2024-07-08 15:13:19 +02:00
Stefano D'Angelo
9843201ef5 cmd ids/lv2 symbols -> product bus/parameter ids 2024-07-02 17:36:20 +02:00
6c19e01254 fix lv2 logging 2024-07-02 05:09:14 +02:00
72d2457826 add missing include vst3 2024-07-01 12:09:03 +02:00
Stefano D'Angelo
4798e79e79 tentative fix for make install on macos 2024-06-29 13:12:19 +02:00
ef33070526 avoid warning 2024-06-20 18:03:55 +02:00
Paolo
0421f5dec9 vst3 bug fix in dllmain 2024-06-20 17:47:16 +02:00
9dcf07147d vst3 macos bindir & datadir in BundleEntry 2024-06-20 15:58:05 +02:00
064820841b vst3 win bindir & datadir set in DllMain 2024-06-20 15:16:49 +02:00
e9d41ecdd0 minor warning fix 2024-06-17 14:25:18 +02:00
Stefano D'Angelo
8d605e0512 oops, typo 2024-06-17 10:52:20 +02:00
Stefano D'Angelo
c833bae2af fixed all targets (linux only), to test android and ios 2024-06-17 10:30:56 +02:00
Stefano D'Angelo
c73b6ecbe3 fix vst3/linux 2024-06-17 09:56:33 +02:00
e01043d24e changing plugin callbacks, breaking everything 2024-06-17 08:18:37 +02:00
b8a16c226c improved all makefiles 2024-06-16 12:04:02 +02:00
62767a4397 improved vst3 makefile 2024-06-16 11:18:29 +02:00
90a739181e improved lv2 Makefile 2024-06-16 10:13:01 +02:00
Stefano D'Angelo
7c6589302a win32 gui :-) 2024-05-31 17:27:58 +02:00
d8b5016960 vst3/macos resize 2024-05-31 10:05:44 +02:00
4e912fba3d fix needed linker flags for vst3/macos with gui 2024-05-30 17:07:34 +02:00
Stefano D'Angelo
99fe4d7044 tentative merge vst3/macos timer 2024-05-30 16:57:21 +02:00
Stefano D'Angelo
cfc1c69b4f beginning of vst3/mac merge 2024-05-30 15:38:13 +02:00
Stefano D'Angelo
d0beba0e0b -rpath in test 2024-05-30 11:54:54 +02:00
430f6e591c fix ui resize in bitwig/linux/vst3 2024-05-30 06:46:16 +02:00
Stefano D'Angelo
dedff39372 gui fixes, vst3 checkSizeConstraints 2024-05-28 14:30:22 +02:00
Stefano D'Angelo
4fcaedf689 fancy javascript for vars.mk 2024-05-28 13:41:47 +02:00
6cab7f43fc removed bogus midi out bus 2024-05-22 08:53:01 +02:00
b8cacab8b8 lv2/vst3: fix warnings and add needed header 2024-05-22 08:38:41 +02:00
39d3bf9e41 split plugin ui from plugin 2024-05-21 08:50:28 +02:00
7b19769a65 fix vst3 for plugins without ui 2024-05-18 17:58:18 +02:00
00cb0c37d8 lv2 some optimization if no ctrl ins or outs are present 2024-05-18 15:30:35 +02:00
94ab135050 vst3 linux gui should be ok now 2024-05-18 15:17:22 +02:00
Stefano D'Angelo
001bfe11da lv2 gui done it seems 2024-05-13 10:24:41 +02:00
Stefano D'Angelo
30587e26d1 a tiny bit more of the example gui 2024-05-12 13:21:01 +02:00
Stefano D'Angelo
df88cc213e lv2 plugin parameter write cb in place 2024-05-12 13:10:55 +02:00
Stefano D'Angelo
b6500cddff some test gui work, lv2 port notifications start to work 2024-05-11 11:30:39 +02:00
Stefano D'Angelo
3259b1e2fc now for real 2024-05-10 18:02:51 +02:00
Stefano D'Angelo
bb463ccc7b vst3 ui resize on linux seems to work now 2024-05-10 17:54:13 +02:00
Stefano D'Angelo
e2d244f4e7 lv2 ui resize 2024-05-10 12:04:04 +02:00
Stefano D'Angelo
c8fb9082cd added lv2 ui:idleInterface as lv2:extensionData 2024-05-10 10:37:38 +02:00
Stefano D'Angelo
6523ef21b4 vst3 gui linux timer 2024-05-10 07:09:26 +02:00
Stefano D'Angelo
8c5fe9cb84 more lv2 gui (w/ pugl example) 2024-05-09 18:04:47 +02:00
Stefano D'Angelo
292e83dcea more lv2 gui 2024-05-08 16:19:10 +02:00
Stefano D'Angelo
b529435748 make vst3 gui optional and beginning of lv2 gui 2024-05-08 15:08:11 +02:00
Stefano D'Angelo
6d31776d56 beginning of vst gui 2024-05-08 13:11:08 +02:00
66 changed files with 4329 additions and 763 deletions

View File

@ -20,7 +20,7 @@ Feel free to try it out anyway and perhaps give us some feedback (we'd appreciat
## Legal
Copyright (C) 2021-2024 Orastron Srl unipersonale.
Copyright (C) 2021-2025 Orastron Srl unipersonale.
Authors: Stefano D'Angelo, Paolo Marrone.

46
notes
View File

@ -5,18 +5,21 @@ company {
web: not used
cmd: not used
android: not used
ios: not used
url:
VST3: PFactoryInfo.url
LV2: manifest.ttl doap:maintainer rdfs:seeAlso
web: not used
cmd: not used
android: not used
ios: not used
email:
VST3: PFactoryInfo.email
LV2: manifest.ttl doap:maintainer foaf:mbox
web: not used
cmd: not used
android: not used
ios: not used
}
product {
@ -26,24 +29,28 @@ product {
web: web-demo <title> and <h1>
cmd: not used
android: index.html <title>, AndroidManifest.xml <application> <activity> android:label
ios: index.html <title>
version:
VST3: PClassInfo{2,W}.version (first 3 numbers)
LV2: not used
web: not used
cmd: not used
android: not used
ios: not used
buildVersion:
VST3: PClassInfo{2,W}.version (last number)
LV2: not used
web: not used
cmd: not used
android: not used
ios: not used
bundleName:
VST3: plugin folder name, plugin .dll name, Info.plist
LV2: plugin folder name, plugin .dll name, manifest.ttl plugin lv2:binary
web: registerProcessor(), output file names
cmd: executable file name
android: .so/.apk filenames
ios: xcodegen name and target name, xcodeproj filename
buses: [
{
name:
@ -53,6 +60,7 @@ product {
web: not used
cmd: not used
android: not used
ios: not used
shortName:
bus short name string, required
VST3: not used
@ -60,6 +68,15 @@ product {
web: not used
cmd: not used
android: not used
ios: not used
id:
bus unique id string, required
VST3; not used
LV2: manifest.ttl lv2:port lv2:symbol (resulting ports can have _l or _r appended)
web: not used
cmd: not used
android: not used
ios: not used
direction:
"input" or "output", required
VST3: BusInfo flags - lots of implications
@ -67,6 +84,7 @@ product {
web: AudioWorkletNode.{numberOfInputs,numberOfOutputs,outputChannelCount} - lots of implications
cmd: lots of places
android: lots of places
ios: lots of places
type:
"audio" or "midi", required
VST3: BusInfo mediaType, ParameterInfo (channel pressure, pitch bend params) - lots of implications
@ -74,6 +92,7 @@ product {
web: AudioWorkletNode.{numberOfInputs,numberOfOutputs,outputChannelCount} - lots of implications
cmd: lots of places
android: lots of places
ios: lots of places
channels:
"mono" or "stereo", audio type only, required
VST3: BusInfo channelCount, plugin get/set bus arrangements
@ -81,6 +100,7 @@ product {
web: AudioWorkletNode.outputChannelCount - lots of implications
cmd: lots of places
android: lots of places
ios: lots of places
sidechain:
bus is not part of main audio path (sidechain)? boolean, default false
VST3: BusInfo busType
@ -95,6 +115,7 @@ product {
web: web-demo choice of audio I/O buses
cmd: choice of audio I/O buses
android: choice of audio I/O buses
ios: choice of audio I/O buses
control:
bus is the "primary control channel" (send cmds, receive responses)? boolean, midi type only, default false
VST3: not used
@ -102,6 +123,7 @@ product {
web: not used
cmd: not used
android: not used
ios: not used
optional:
bus is optionally connected? boolean, default false
VST3: BusInfo flags, plugin initialize, activate bus, set active
@ -109,6 +131,7 @@ product {
web: not used
cmd: whether to pass NULLs if not chosen audio I/O buses
android: whether to pass NULLs if not chosen audio I/O buses
ios: whether to pass NULLs if not chosen audio I/O buses
}
]
parameters: [
@ -120,6 +143,7 @@ product {
web: AudioWorkletProcessor.parameterDescriptors, web-demo <label>
cmd: not used
android: index.html <label>
ios: index.html <label>
shortName:
parameter short name string, required
VST3: ParameterInfo shortTitle
@ -127,6 +151,15 @@ product {
web: not used
cmd: not used
android: not used
ios: not used
id:
parameter unique id string, required
VST3; not used
LV2: manifest.ttl lv2:port lv2:symbol (bypass ports used "enabled")
web: not used
cmd: command line parameter name etc.
android: not used
ios: not used
direction:
"input" or "output", required
VST3: ParameterInfo flags - lots of implications
@ -134,6 +167,7 @@ product {
web: AudioWorkletProcessor.parameterDescriptors, web-demo <range> readonly/input listener - lots of implications
cmd: lots of places
android: lots of places
ios: lots of places
isBypass:
parameter is bypass/enabled? boolean - lots of implications, default false
VST3: ParameterInfo, controller get/set parameter/state
@ -141,6 +175,7 @@ product {
web: AudoWorkletProcessor.process(), web-demo <range> min/max/step
cmd: set parameter
android: JNI set parameter, index.html <range> min/max/step
ios: native set parameter, index.html <range> min/max/step
isLatency:
parameter is latency output? boolean - lots of implications, default false
VST3: TBD
@ -148,6 +183,7 @@ product {
web: not (yet) used
cmd: not (yet) used
android: not (yet) used
ios: not (yet) used
defaultValue:
default value, number, mapped, required for non-bypass
VST3: ParameterInfo defaultNormalizedValue, controller initialize
@ -155,6 +191,7 @@ product {
web: AudioWorkletProcessor.parameterDescriptors, processor_new(), web-demo initial <range> value and value <span> innerText
cmd: default parameter value
android: JNI set parameter initial value, index.html initial <range> value
ios: native set parameter initial value, index.html initial <range> value
minimum:
minimum value, number, mapped, required for non-bypass
VST3: ParameterInfo stepCount, defaultNormalizedValue, controller get/set parameter (value clamped)
@ -162,6 +199,7 @@ product {
web: AudioWorkletProcessor.parameterDescriptors, AudioWorkletProcessor.process() (value clamped), web-demo <range> mapping
cmd: set parameter (value clamped)
android: JNI set parameter (value clamped), index.html <range> mapping
ios: native set parameter (value clamped), index.html <range> mapping
maximum:
maximum value, number, mapped, required for non-bypass
VST3: ParameterInfo stepCount, defaultNormalizedValue, controller get/set parameter (value clamped)
@ -169,6 +207,7 @@ product {
web: AudioWorkletProcessor.parameterDescriptors, AudioWorkletProcessor.process() (value clamped), web-demo <range> mapping
cmd: set parameter (value clamped)
android: JNI set parameter (value clamped), index.html <range> mapping
ios: native set parameter (value clamped), index.html <range> mapping
toggled:
parameter is on/off? boolean, default false
VST3: ParameterInfo stepCount, controller set parameter/state
@ -176,6 +215,7 @@ product {
web: AudoWorkletProcessor.process(), web-demo <range> min/max/step
cmd: set parameter
android: JNI set parameter, index.html <range> min/max/step
ios: native set parameter, index.html <range> min/max/step
optional:
parameter is optionally connected? boolean, default false
VST3: not used
@ -183,6 +223,7 @@ product {
web: not used
cmd: not used
android: not used
ios: not used
integer:
parameter values are integers? boolean, default false
VST3: ParameterInfo stepCount, controller set parameter/state
@ -190,6 +231,7 @@ product {
web: AudoWorkletProcessor.process(), web-demo <range> step
cmd: set parameter
android: JNI set parameter, index.html <range> step
ios: native set parameter, index.html <range> step
scalePoints:
{ "label1": value1, "label2", value2, ... }
labeled values, default none
@ -198,6 +240,7 @@ product {
web: not (yet) used
cmd: not (yet) used
android: not (yet) used
ios: not (yet) used
list:
parameter is a list (using scalePoints values)? default false
VST3: TBD (+approx to closest?)
@ -205,6 +248,7 @@ product {
web: TBD (approx to closest? dropdown? both?)
cmd: not (yet) used
android: not (yet) used
ios: not (yet) used
unit:
unit of measure (from predefined list, see tibia-index.js), default ""
VST3: ParameterInfo units
@ -212,6 +256,7 @@ product {
web: web-demo value <span> innerText
cmd: not (yet) used
android: not (yet) used
ios: not (yet) used
map:
"linear" vs "logarithmic"
VST3: many places (requires libm)
@ -219,6 +264,7 @@ product {
web: web-demo <range> values
cmd: not used
android: index.html <range> values
ios: index.html <range> values
}
]
}

View File

@ -18,104 +18,143 @@
# File author: Stefano D'Angelo
#
TEMPLATE := android
include vars.mk
ifeq (${HAS_MIDI_IN}, yes)
MIN_API := 29
COMMON_DIR ?= .
DATA_DIR ?= .
PLUGIN_DIR ?= src
API_DIR ?= $(PLUGIN_DIR)
MKINC_DIR ?= $(COMMON_DIR)
BUILD_BIN_DIR := build/apk/lib/armeabi-v7a
BUILD_DATA_DIR := build/assets/index.html
include $(MKINC_DIR)/vars-pre.mk
ifeq ($(HAS_MIDI_IN), yes)
MIN_API := 29
else
MIN_API := 26
MIN_API := 26
endif
CC = ${ANDROID_NDK_DIR}/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi${MIN_API}-clang
CXX = ${ANDROID_NDK_DIR}/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi${MIN_API}-clang++
JC = javac
NDK_DIR := $(SDK_DIR)/ndk/$(NDK_VERSION)
BUILD_TOOLS_DIR := $(SDK_DIR)/build-tools/$(BUILD_TOOLS_VERSION)
APKSIGNER = ${BUILD_TOOLS_DIR}/apksigner
ZIPALIGN = ${BUILD_TOOLS_DIR}/zipalign
AAPT = ${BUILD_TOOLS_DIR}/aapt
D8 = ${BUILD_TOOLS_DIR}/d8
ADB = ${ANDROID_SDK_DIR}/platform-tools/adb
ANDROID_JAR_FILE := $(SDK_DIR)/platforms/android-$(ANDROID_VERSION)/android.jar
ANDROIDX_CORE_FILE := $(ANDROIDX_DIR)/core-$(ANDROIDX_CORE_VERSION).jar
ANDROIDX_LIFECYCLE_COMMON_FILE := $(ANDROIDX_DIR)/lifecycle-common-$(ANDROIDX_LIFECYCLE_COMMON_VERSION).jar
ANDROIDX_VERSIONEDPARCELABLE_FILE := $(ANDROIDX_DIR)/versionedparcelable-$(ANDROIDX_VERSIONEDPARCELABLE_VERSION).jar
KOTLIN_STDLIB_FILE := $(KOTLIN_DIR)/kotlin-stdlib-$(KOTLIN_STDLIB_VERSION).jar
KOTLINX_COROUTINES_CORE_FILE := $(KOTLIN_DIR)/kotlinx-coroutines-core-$(KOTLINX_COROUTINES_CORE_VERSION).jar
KOTLINX_COROUTINES_CORE_JVM_FILE := $(KOTLIN_DIR)/kotlinx-coroutines-core-jvm-$(KOTLINX_COROUTINES_CORE_JVM_VERSION).jar
ifeq ($(OS), Windows_NT)
NDK_BIN_DIR ?= $(NDK_DIR)/toolchains/llvm/prebuilt/windows-x86_64/bin/
else
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S), Darwin)
NDK_BIN_DIR ?= $(NDK_DIR)/toolchains/llvm/prebuilt/darwin-x86_64/bin/
else
NDK_BIN_DIR ?= $(NDK_DIR)/toolchains/llvm/prebuilt/linux-x86_64/bin/
endif
endif
CC := $(NDK_BIN_DIR)/armv7a-linux-androideabi$(MIN_API)-clang
CXX := $(NDK_BIN_DIR)/armv7a-linux-androideabi$(MIN_API)-clang++
JC := javac
APKSIGNER := $(BUILD_TOOLS_DIR)/apksigner
ZIPALIGN := $(BUILD_TOOLS_DIR)/zipalign
AAPT := $(BUILD_TOOLS_DIR)/aapt
D8 := $(BUILD_TOOLS_DIR)/d8
ADB := $(SDK_DIR)/platform-tools/adb
JARS := \
${ANDROID_JAR_FILE} \
${ANDROIDX_CORE_FILE} \
${ANDROIDX_LIFECYCLE_COMMON_FILE} \
${ANDROIDX_VERSIONEDPARCELABLE_FILE} \
${KOTLIN_STDLIB_FILE} \
${KOTLINX_COROUTINES_CORE_FILE} \
${KOTLINX_COROUTINES_CORE_JVM_FILE}
$(ANDROID_JAR_FILE) \
$(ANDROIDX_CORE_FILE) \
$(ANDROIDX_LIFECYCLE_COMMON_FILE) \
$(ANDROIDX_VERSIONEDPARCELABLE_FILE) \
$(KOTLIN_STDLIB_FILE) \
$(KOTLINX_COROUTINES_CORE_FILE) \
$(KOTLINX_COROUTINES_CORE_JVM_FILE)
CLASSES_PATH := $(subst .,/,$(JAVA_PACKAGE_NAME))
CLASSES := \
MainActivity \
MainActivity$$WebAppInterface
ifeq (${HAS_MIDI_IN}, yes)
ifeq ($(HAS_MIDI_IN), yes)
CLASSES += MainActivity$$WebAppInterface$$MidiDeviceCallback MainActivity$$WebAppInterface$$1
endif
COMMON_DIR := $(or $(COMMON_DIR),.)
DATA_DIR := $(or $(DATA_DIR),.)
PLUGIN_DIR := $(or $(PLUGIN_DIR),src)
CFLAGS := -O3 -Wall -Wpedantic -Wextra
CFLAGS_ALL := -I$(DATA_DIR)/src -I$(PLUGIN_DIR) -I$(API_DIR) -fPIC $(CFLAGS_EXTRA) $(CFLAGS)
CFLAGS = -O3 -Wall -Wpedantic -Wextra
CFLAGS_ALL = -I${DATA_DIR}/src -I${PLUGIN_DIR} -fPIC ${CFLAGS} ${CFLAGS_EXTRA}
CXXFLAGS := $(CFLAGS)
CXXFLAGS_ALL := -I$(DATA_DIR)/src -I$(PLUGIN_DIR) -I$(API_DIR) -fPIC -std=c++11 $(CXXFLAGS_EXTRA) $(CXXFLAGS)
CXXFLAGS = ${CFLAGS}
CXXFLAGS_ALL = -I${DATA_DIR}/src -I${PLUGIN_DIR} -fPIC -std=c++11 ${CXXFLAGS} ${CXXFLAGS_EXTRA}
LDFLAGS =
LDFLAGS_ALL = -shared -static-libstdc++ -landroid
ifeq (${HAS_MIDI_IN}, yes)
LDFLAGS :=
LDFLAGS_ALL := -shared -static-libstdc++ -landroid
ifeq ($(HAS_MIDI_IN), yes)
LDFLAGS += -lamidi
endif
LDFLAGS_ALL += ${LDFLAGS} ${LDFLAGS_EXTRA}
LDFLAGS_ALL += $(LDFLAGS_EXTRA) $(LDFLAGS)
JFLAGS =
JFLAGS_ALL = ${JFLAGS} ${JFLAGS_EXTRA}
JFLAGS :=
JFLAGS_ALL := $(JFLAGS_EXTRA) $(JFLAGS)
C_SRCS = ${C_SRCS_EXTRA}
C_OBJS = $(addprefix build/obj/, $(notdir $(C_SRCS:.c=.o)))
C_SRCS := $(C_SRCS_EXTRA)
C_OBJS := $(addprefix build/obj/, $(notdir $(C_SRCS:.c=.o)))
CXX_SRCS = ${COMMON_DIR}/src/jni.cpp ${CXX_SRCS_EXTRA}
CXX_OBJS = $(addprefix build/obj/, $(notdir $(CXX_SRCS:.cpp=.o)))
CXX_SRCS := $(COMMON_DIR)/src/jni.cpp $(CXX_SRCS_EXTRA)
CXX_OBJS := $(addprefix build/obj/, $(notdir $(CXX_SRCS:.cpp=.o)))
all: build/${BUNDLE_NAME}.apk
DIRS := build build/gen build/apk build/obj build/apk/lib build/apk/lib/armeabi-v7a build/assets
build/${BUNDLE_NAME}.apk: build/gen/${BUNDLE_NAME}.aligned.apk ${KEY_STORE}
${APKSIGNER} sign --ks ${KEY_STORE} --ks-key-alias ${KEY_ALIAS} --ks-pass pass:${STORE_PASS} --key-pass pass:${KEY_PASS} --out $@ build/gen/${BUNDLE_NAME}.aligned.apk
ALL := build/$(BUNDLE_NAME).apk
build/gen/${BUNDLE_NAME}.aligned.apk: build/gen/${BUNDLE_NAME}.unsigned.apk
${ZIPALIGN} -f -p 4 $^ $@
-include $(MKINC_DIR)/vars-extra.mk
build/gen/${BUNDLE_NAME}.unsigned.apk: build/apk/classes.dex build/apk/lib/armeabi-v7a/lib${BUNDLE_NAME}.so ${DATA_DIR}/data/AndroidManifest.xml build/assets/index.html | build/gen
${AAPT} package -f -M ${DATA_DIR}/data/AndroidManifest.xml -A build/assets $(foreach jar,$(JARS),-I $(jar)) -F $@ build/apk
all: $(ALL)
build/$(BUNDLE_NAME).apk: build/gen/$(BUNDLE_NAME).aligned.apk $(KEY_STORE)
$(APKSIGNER) sign --ks $(KEY_STORE) --ks-key-alias $(KEY_ALIAS) --ks-pass pass:$(STORE_PASS) --key-pass pass:$(KEY_PASS) --out $@ build/gen/$(BUNDLE_NAME).aligned.apk
build/gen/$(BUNDLE_NAME).aligned.apk: build/gen/$(BUNDLE_NAME).unsigned.apk
$(ZIPALIGN) -f -p 4 $^ $@
build/gen/$(BUNDLE_NAME).unsigned.apk: build/apk/classes.dex build/apk/lib/armeabi-v7a/lib$(BUNDLE_NAME).so $(DATA_DIR)/data/AndroidManifest.xml build/assets/index.html | build/gen
$(AAPT) package -f -M $(DATA_DIR)/data/AndroidManifest.xml -A build/assets $(foreach jar,$(JARS),-I $(jar)) -F $@ build/apk
build/apk/classes.dex: build/apk/my_classes.jar
cd build/apk && ${D8} --min-api ${MIN_API} ../../$^ ${JARS} && cd ../..
cd build/apk && $(D8) --min-api $(MIN_API) ../../$^ $(JARS) && cd ../..
build/apk/my_classes.jar: $(foreach class,$(CLASSES),build/obj/$(CLASSES_PATH)/$(class).class) | build/apk
@echo ${CLASSES_PATH}
${D8} $(foreach class,$(CLASSES),'build/obj/$(CLASSES_PATH)/$(class).class') --min-api ${MIN_API} --output $@ --no-desugaring
@echo $(CLASSES_PATH)
$(D8) $(foreach class,$(CLASSES),'build/obj/$(CLASSES_PATH)/$(class).class') --min-api $(MIN_API) --output $@ --no-desugaring
build/obj/${CLASSES_PATH}/MainActivity.class: ${DATA_DIR}/src/MainActivity.java | build/obj
${JC} ${JFLAGS_ALL} -classpath "$(subst $() $(),:,$(JARS))" -d build/obj $^
build/obj/$(CLASSES_PATH)/MainActivity.class: $(DATA_DIR)/src/MainActivity.java | build/obj
$(JC) $(JFLAGS_ALL) -classpath "$(subst $() $(),:,$(JARS))" -d build/obj $^
build/apk/lib/armeabi-v7a/lib${BUNDLE_NAME}.so: ${C_OBJS} ${CXX_OBJS} | build/apk/lib/armeabi-v7a
${CXX} $^ -o $@ ${CFLAGS_ALL} ${CXXFLAGS_ALL} ${LDFLAGS_ALL}
build/apk/lib/armeabi-v7a/lib$(BUNDLE_NAME).so: $(C_OBJS) $(CXX_OBJS) | build/apk/lib/armeabi-v7a
$(CXX) $^ -o $@ $(CFLAGS_ALL) $(CXXFLAGS_ALL) $(LDFLAGS_ALL)
build/assets/index.html: ${DATA_DIR}/src/index.html | build/assets
build/assets/index.html: $(DATA_DIR)/src/index.html | build/assets
cp $^ $@
build/gen build/apk build/obj build/apk/lib/armeabi-v7a build/assets:
$(DIRS):
mkdir -p $@
clean:
rm -fr build
install: build/${BUNDLE_NAME}.apk
[ -n "`${ADB} shell pm list packages | grep ^package:${JAVA_PACKAGE_NAME}`" ] && ${ADB} uninstall ${JAVA_PACKAGE_NAME}; exit 0
${ADB} install $^
install: build/$(BUNDLE_NAME).apk
[ -n "`$(ADB) shell pm list packages | grep ^package:$(JAVA_PACKAGE_NAME)`" ] && $(ADB) uninstall $(JAVA_PACKAGE_NAME); exit 0
$(ADB) install $^
-include $(MKINC_DIR)/rules-extra.mk
.PHONY: all clean install
@ -124,7 +163,9 @@ install: build/${BUNDLE_NAME}.apk
PERCENT := %
$(C_OBJS): build/obj/%.o: $$(filter $$(PERCENT)/$$(basename $$(notdir $$@)).c,$$(C_SRCS)) | build/obj
${CC} $^ -o $@ -c ${CFLAGS_ALL}
$(CC) $^ -o $@ -c $(CFLAGS_ALL)
$(CXX_OBJS): build/obj/%.o: $$(filter $$(PERCENT)/$$(basename $$(notdir $$@)).cpp,$$(CXX_SRCS)) | build/obj
${CXX} $^ -o $@ -c ${CXXFLAGS_ALL}
$(CXX) $^ -o $@ -c $(CXXFLAGS_ALL)
-include $(MKINC_DIR)/rules-secondexp-extra.mk

View File

@ -20,36 +20,29 @@
BUNDLE_NAME := {{=it.product.bundleName}}
JAVA_PACKAGE_NAME := {{=it.android.javaPackageName}}
ANDROID_VERSION := {{=it.android.androidVersion}}
CFLAGS_EXTRA := {{=it.make && it.make.cflags ? it.make.cflags : ""}} {{=it.android_make && it.android_make.cflags ? it.android_make.cflags : ""}}
CXXFLAGS_EXTRA := {{=it.make && it.make.cxxflags ? it.make.cxxflags : ""}} {{=it.android_make && it.android_make.cxxflags ? it.android_make.cxxflags : ""}}
JFLAGS_EXTRA := {{=it.make && it.make.jflags ? it.make.jflags : ""}} {{=it.android_make && it.android_make.jflags ? it.android_make.jflags : ""}}
LDFLAGS_EXTRA := {{=it.make && it.make.ldflags ? it.make.ldflags : ""}} {{=it.android_make && it.android_make.ldflags ? it.android_make.ldflags : ""}}
C_SRCS_EXTRA := {{=it.make && it.make.cSrcs ? it.make.cSrcs : ""}} {{=it.android_make && it.android_make.cSrcs ? it.android_make.cSrcs : ""}}
CXX_SRCS_EXTRA := {{=it.make && it.make.cxxSrcs ? it.make.cxxSrcs : ""}} {{=it.android_make && it.android_make.cxxSrcs ? it.android_make.cxxSrcs : ""}}
COMMON_DIR := {{=it.android_make && it.android_make.commonDir ? it.android_make.commonDir : (it.make && it.make.commonDir ? it.make.commonDir : "")}}
DATA_DIR := {{=it.android_make && it.android_make.dataDir ? it.android_make.dataDir : (it.make && it.make.dataDir ? it.make.dataDir : "")}}
PLUGIN_DIR := {{=it.android_make && it.android_make.pluginDir ? it.android_make.pluginDir : (it.make && it.make.pluginDir ? it.make.pluginDir : "")}}
KEY_STORE := {{=it.android_make.keyStore}}
KEY_ALIAS := {{=it.android_make.keyAlias}}
STORE_PASS := {{=it.android_make.storePass}}
KEY_PASS := {{=it.android_make.keyPass}}
ANDROID_SDK_DIR := {{=it.android_make.sdkDir}}
ANDROID_NDK_DIR := ${ANDROID_SDK_DIR}/ndk/{{=it.android_make.ndkVersion}}
BUILD_TOOLS_DIR := ${ANDROID_SDK_DIR}/build-tools/{{=it.android_make.buildToolsVersion}}
ANDROIDX_DIR := {{=it.android_make.androidxDir}}
KOTLIN_DIR := {{=it.android_make.kotlinDir}}
ANDROID_JAR_FILE := ${ANDROID_SDK_DIR}/platforms/android-{{=it.android_make.androidVersion}}/android.jar
ANDROIDX_CORE_FILE := ${ANDROIDX_DIR}/core-{{=it.android_make.androidxCoreVersion}}.jar
ANDROIDX_LIFECYCLE_COMMON_FILE := ${ANDROIDX_DIR}/lifecycle-common-{{=it.android_make.androidxLifecycleCommonVersion}}.jar
ANDROIDX_VERSIONEDPARCELABLE_FILE := ${ANDROIDX_DIR}/versionedparcelable-{{=it.android_make.androidxVersionedparcelableVersion}}.jar
KOTLIN_STDLIB_FILE := ${KOTLIN_DIR}/kotlin-stdlib-{{=it.android_make.kotlinStdlibVersion}}.jar
KOTLINX_COROUTINES_CORE_FILE := ${KOTLIN_DIR}/kotlinx-coroutines-core-{{=it.android_make.kotlinxCoroutinesCoreVersion}}.jar
KOTLINX_COROUTINES_CORE_JVM_FILE := ${KOTLIN_DIR}/kotlinx-coroutines-core-jvm-{{=it.android_make.kotlinxCoroutinesCoreJVMVersion}}.jar
{{?(it.android_make?.commonDir || it.make?.commonDir)}}
COMMON_DIR := {{=it.android_make?.commonDir ?? (it.make?.commonDir ?? "")}}
{{?}}
{{?(it.android_make?.dataDir || it.make?.dataDir)}}
DATA_DIR := {{=it.android_make?.dataDir ?? (it.make?.dataDir ?? "")}}
{{?}}
{{?(it.android_make?.pluginDir || it.make?.pluginDir)}}
PLUGIN_DIR := {{=it.android_make?.pluginDir ?? (it.make?.pluginDir ?? "")}}
{{?}}
{{?(it.android_make?.apiDir || it.make?.apiDir)}}
API_DIR := {{=it.android_make?.apiDir ?? (it.make?.apiDir ?? "")}}
{{?}}
{{?(it.android_make?.mkincDir || it.make?.mkincDir)}}
MKINC_DIR := {{=it.android_make?.mkincDir ?? (it.make?.mkincDir ?? "")}}
{{?}}
HAS_MIDI_IN := {{=it.product.buses.filter(x => x.type == "midi" && x.direction == "input").length > 0 ? "yes" : "no"}}
{{?it.make?.extra}}
{{=it.make.extra}}
{{?}}
{{?it.android_make?.extra}}
{{=it.android_make.extra}}
{{?}}

View File

@ -8,7 +8,7 @@
{{??}}
<uses-sdk android:minSdkVersion="26" /> <!-- for androidx core and AAudio -->
{{?}}
<uses-sdk android:targetSdkVersion="34" />
<uses-sdk android:targetSdkVersion="{{=it.android.androidVersion}}" />
<application android:label="{{=it.product.name}}">
<activity android:name=".MainActivity" android:label="{{=it.product.name}}" android:exported="true">
<intent-filter>

View File

@ -1,7 +1,7 @@
/*
* Tibia
*
* Copyright (C) 2024 Orastron Srl unipersonale
* Copyright (C) 2024, 2025 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
@ -83,3 +83,7 @@ static struct {
#endif
#define JNI_FUNC(x) Java_{{=it.android.javaPackageName.replaceAll("_", "_1").replaceAll(".", "_")}}_MainActivity_##x
{{?it.product.state && it.product.state.dspCustom}}
#define STATE_DSP_CUSTOM
{{?}}

View File

@ -1,7 +1,7 @@
/*
* Tibia
*
* Copyright (C) 2023, 2024 Orastron Srl unipersonale
* Copyright (C) 2023-2025 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
@ -22,6 +22,7 @@
#include <stdint.h>
#include "data.h"
#include "plugin_api.h"
#include "plugin.h"
#include <string.h>
@ -188,6 +189,10 @@ JNIEXPORT jboolean JNICALL
JNI_FUNC(nativeAudioStart)(JNIEnv* env, jobject thiz) {
(void)env;
(void)thiz;
#ifdef STATE_DSP_CUSTOM
(void)plugin_state_load;
(void)plugin_state_save;
#endif
#if NUM_CHANNELS_IN + NUM_CHANNELS_OUT > 0
# if NUM_CHANNELS_IN == 0
@ -225,7 +230,13 @@ JNI_FUNC(nativeAudioStart)(JNIEnv* env, jobject thiz) {
if (ma_device_init(NULL, &deviceConfig, &device) != MA_SUCCESS)
return false;
plugin_init(&instance);
plugin_callbacks cbs = {
/* .handle = */ NULL,
/* .format = */ "android",
/* .get_bindir = */ NULL,
/* .get_datadir = */ NULL
};
plugin_init(&instance, &cbs);
#if PARAMETERS_N > 0
for (size_t i = 0; i < PARAMETERS_N; i++) {

View File

@ -0,0 +1,64 @@
/*
* Tibia
*
* Copyright (C) 2024, 2025 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
*/
#ifndef PLUGIN_API_H
#define PLUGIN_API_H
typedef struct {
void * handle;
const char * format;
const char * (*get_bindir)(void *handle);
const char * (*get_datadir)(void *handle);
} plugin_callbacks;
{{?it.product.state && it.product.state.dspCustom}}
typedef struct {
void * handle;
void (*lock)(void *handle);
void (*unlock)(void *handle);
int (*write)(void *handle, const char *data, size_t length);
{{?it.product.parameters.find(x => x.direction == "input")}}
void (*set_parameter)(void *handle, size_t index, float value);
{{?}}
} plugin_state_callbacks;
{{?}}
typedef struct {
void * handle;
const char * format;
const char * (*get_bindir)(void *handle);
const char * (*get_datadir)(void *handle);
{{?it.product.parameters.find(x => x.direction == "input")}}
void (*set_parameter_begin)(void *handle, size_t index, float value);
void (*set_parameter)(void *handle, size_t index, float value);
void (*set_parameter_end)(void *handle, size_t index, float value);
{{?}}
} plugin_ui_callbacks;
{{?it.product.parameters.length > 0}}
enum {
{{~it.product.parameters :p}}
plugin_parameter_{{=p.id}},
{{~}}
plugin_parameter__count
};
{{?}}
#endif

View File

@ -0,0 +1,26 @@
/*
* Tibia
*
* Copyright (C) 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
*/
var path = require("path");
var sep = path.sep;
module.exports = function (data, api) {
api.generateFileFromTemplateFile(`src${sep}plugin_api.h`, `plugin_api.h`, data);
};

View File

@ -1,7 +1,7 @@
#
# Tibia
#
# Copyright (C) 2024 Orastron Srl unipersonale
# Copyright (C) 2024, 2025 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
@ -18,85 +18,114 @@
# File author: Stefano D'Angelo
#
TEMPLATE := cmd
include vars.mk
COMMON_DIR ?= .
DATA_DIR ?= .
PLUGIN_DIR ?= src
API_DIR ?= $(PLUGIN_DIR)
MKINC_DIR ?= $(COMMON_DIR)
BUILD_BIN_DIR := build
BUILD_DATA_DIR := build
-include $(MKINC_DIR)/vars-pre.mk
ifeq ($(OS), Windows_NT)
EXE_SUFFIX = .exe
EXE_SUFFIX := .exe
else
UNAME_S = $(shell uname -s)
EXE_SUFFIX =
PREFIX = /usr/local
BINDIR = ${PREFIX}/bin
UNAME_S := $(shell uname -s)
EXE_SUFFIX :=
PREFIX := /usr/local
BINDIR := $(PREFIX)/bin
endif
COMMON_DIR := $(or $(COMMON_DIR),.)
DATA_DIR := $(or $(DATA_DIR),.)
PLUGIN_DIR := $(or $(PLUGIN_DIR),src)
TINYWAV_DIR ?= ../tinywav
MIDI_PARSER_DIR ?= ../midi-parser
CC = gcc
CXX = g++
CC := gcc
CXX := g++
CFLAGS = -O3 -Wall -Wpedantic -Wextra
CFLAGS_ALL = -I${DATA_DIR}/src -I${PLUGIN_DIR} -I${TINYWAV_DIR} -I${MIDI_PARSER_DIR}/include -fPIC ${CFLAGS} ${CFLAGS_EXTRA}
CFLAGS := -O3 -Wall -Wpedantic -Wextra
CFLAGS_ALL := -I$(DATA_DIR)/src -I$(PLUGIN_DIR) -I$(API_DIR) -I$(TINYWAV_DIR) -I$(MIDI_PARSER_DIR)/include $(CFLAGS_EXTRA) $(CFLAGS)
LDFLAGS =
LDFLAGS_ALL = ${LDFLAGS} ${LDFLAGS_EXTRA}
LDFLAGS :=
LDFLAGS_ALL := $(LDFLAGS_EXTRA) $(LDFLAGS)
CXXFLAGS = ${CFLAGS}
CXXFLAGS_ALL = -I${DATA_DIR}/src -I${PLUGIN_DIR} -I${TINYWAV_DIR} -I${MIDI_PARSER_DIR}/include -fPIC ${CXXFLAGS} ${CXXFLAGS_EXTRA}
CXXFLAGS := $(CFLAGS)
CXXFLAGS_ALL := -I$(DATA_DIR)/src -I$(PLUGIN_DIR) -I$(API_DIR) -I$(TINYWAV_DIR) -I$(MIDI_PARSER_DIR)/include $(CXXFLAGS_EXTRA) $(CXXFLAGS)
ifeq ($(UNAME_S), Darwin)
CFLAGS_ALL := ${CFLAGS_ALL} -arch arm64 -arch x86_64
LDFLAGS_ALL := ${LDFLAGS_ALL} -arch arm64 -arch x86_64
CXXFLAGS_ALL := ${CXXFLAGS_ALL} -arch arm64 -arch x86_64
CFLAGS_ALL := $(CFLAGS_ALL) -arch arm64 -arch x86_64
LDFLAGS_ALL := $(LDFLAGS_ALL) -arch arm64 -arch x86_64
CXXFLAGS_ALL := $(CXXFLAGS_ALL) -arch arm64 -arch x86_64
endif
PROGRAM = ${BUNDLE_NAME}${EXE_SUFFIX}
PROGRAM := $(BUNDLE_NAME)$(EXE_SUFFIX)
C_SRCS = ${COMMON_DIR}/src/main.c ${C_SRCS_EXTRA}
C_SRCS := $(COMMON_DIR)/src/main.c $(C_SRCS_EXTRA)
ifeq ($(HAS_MIDI_IN), yes)
C_SRCS += ${MIDI_PARSER_DIR}/src/midi-parser.c
C_SRCS := $(C_SRCS) $(MIDI_PARSER_DIR)/src/midi-parser.c
endif
C_OBJS = $(addprefix build/obj/, $(notdir $(C_SRCS:.c=.o)))
C_OBJS := $(addprefix build/obj/, $(notdir $(C_SRCS:.c=.o)))
CXX_SRCS = ${CXX_SRCS_EXTRA}
CXX_OBJS = $(addprefix build/obj/, $(notdir $(CXX_SRCS:.cpp=.o)))
CXX_SRCS := $(CXX_SRCS_EXTRA)
CXX_OBJS := $(addprefix build/obj/, $(notdir $(CXX_SRCS:.cpp=.o)))
all: build/${PROGRAM}
DIRS := build build/obj
ALL := build/$(PROGRAM)
STRIP_ALL := build/$(PROGRAM)
STRIP_PHONY :=
STRIP_PREREQS := $(STRIP_ALL) $(STRIP_PHONY)
-include $(MKINC_DIR)/vars-extra.mk
all: $(ALL)
ifeq ($(CXX_OBJS),)
build/${PROGRAM}: ${C_OBJS} build/obj/tinywav.o | build
${CC} $^ -o $@ ${CFLAGS_ALL} ${LDFLAGS_ALL}
build/$(PROGRAM): $(C_OBJS) build/obj/tinywav.o | build
$(CC) $^ -o $@ $(CFLAGS_ALL) $(LDFLAGS_ALL)
else
build/${PROGRAM}: ${C_OBJS} ${CXX_OBJS} build/obj/tinywav.o | build
${CXX} $^ -o $@ ${CFLAGS_ALL} ${CXXFLAGS_ALL} ${LDFLAGS_ALL}
build/$(PROGRAM): $(C_OBJS) $(CXX_OBJS) build/obj/tinywav.o | build
$(CXX) $^ -o $@ $(CFLAGS_ALL) $(CXXFLAGS_ALL) $(LDFLAGS_ALL)
endif
build build/obj:
$(DIRS):
mkdir -p $@
clean:
rm -fr build
strip: $(STRIP_PREREQS)
strip build/$(PROGRAM)
ifeq ($(OS), Windows_NT)
.PHONY: all clean
.PHONY: all clean strip $(STRIP_PHONY)
else
install: all
mkdir -p -m 0755 ${BINDIR}
install -m 0755 build/${PROGRAM} ${BINDIR}
mkdir -m 0755 -p $(BINDIR)
install -m 0755 build/$(PROGRAM) $(BINDIR)
.PHONY: all clean install
.PHONY: all clean strip $(STRIP_PHONY) install
endif
-include $(MKINC_DIR)/rules-extra.mk
.SECONDEXPANSION:
PERCENT := %
build/obj/tinywav.o: ${TINYWAV_DIR}/tinywav.c
${CC} $^ -o $@ -c ${CFLAGS_ALL} -Wno-unused-result
build/obj/tinywav.o: $(TINYWAV_DIR)/tinywav.c
$(CC) $^ -o $@ -c $(CFLAGS_ALL) -Wno-unused-result
$(C_OBJS): build/obj/%.o: $$(filter $$(PERCENT)/$$(basename $$(notdir $$@)).c,$$(C_SRCS)) | build/obj
${CC} $^ -o $@ -c ${CFLAGS_ALL}
$(CC) $^ -o $@ -c $(CFLAGS_ALL)
$(CXX_OBJS): build/obj/%.o: $$(filter $$(PERCENT)/$$(basename $$(notdir $$@)).cpp,$$(CXX_SRCS)) | build/obj
${CXX} $^ -o $@ -c ${CXXFLAGS_ALL}
$(CXX) $^ -o $@ -c $(CXXFLAGS_ALL)
-include $(MKINC_DIR)/rules-secondexp-extra.mk

View File

@ -20,18 +20,27 @@
BUNDLE_NAME := {{=it.product.bundleName}}
CFLAGS_EXTRA := {{=it.make && it.make.cflags ? it.make.cflags : ""}} {{=it.cmd_make.cflags ? it.cmd_make.cflags : ""}}
CXXFLAGS_EXTRA := {{=it.make && it.make.cxxflags ? it.make.cxxflags : ""}} {{=it.cmd_make.cxxflags ? it.cmd_make.cxxflags : ""}}
LDFLAGS_EXTRA := {{=it.make && it.make.ldflags ? it.make.ldflags : ""}} {{=it.cmd_make.ldflags ? it.cmd_make.ldflags : ""}}
C_SRCS_EXTRA := {{=it.make && it.make.cSrcs ? it.make.cSrcs : ""}} {{=it.cmd_make && it.cmd_make.cSrcs ? it.cmd_make.cSrcs : ""}}
CXX_SRCS_EXTRA := {{=it.make && it.make.cxxSrcs ? it.make.cxxSrcs : ""}} {{=it.cmd_make && it.cmd_make.cxxSrcs ? it.cmd_make.cxxSrcs : ""}}
COMMON_DIR := {{=it.cmd_make && it.cmd_make.commonDir ? it.cmd_make.commonDir : (it.make && it.make.commonDir ? it.make.commonDir : "")}}
DATA_DIR := {{=it.cmd_make && it.cmd_make.dataDir ? it.cmd_make.dataDir : (it.make && it.make.dataDir ? it.make.dataDir : "")}}
PLUGIN_DIR := {{=it.cmd_make && it.cmd_make.pluginDir ? it.cmd_make.pluginDir : (it.make && it.make.pluginDir ? it.make.pluginDir : "")}}
TINYWAV_DIR := {{=it.cmd_make.tinywavDir}}
MIDI_PARSER_DIR := {{=it.cmd_make.midiParserDir}}
{{?(it.cmd_make?.commonDir || it.make?.commonDir)}}
COMMON_DIR := {{=it.cmd_make?.commonDir ?? (it.make?.commonDir ?? "")}}
{{?}}
{{?(it.cmd_make?.dataDir || it.make?.dataDir)}}
DATA_DIR := {{=it.cmd_make?.dataDir ?? (it.make?.dataDir ?? "")}}
{{?}}
{{?(it.cmd_make?.pluginDir || it.make?.pluginDir)}}
PLUGIN_DIR := {{=it.cmd_make?.pluginDir ?? (it.make?.pluginDir ?? "")}}
{{?}}
{{?(it.cmd_make?.apiDir || it.make?.apiDir)}}
API_DIR := {{=it.cmd_make?.apiDir ?? (it.make?.apiDir ?? "")}}
{{?}}
{{?(it.cmd_make?.mkincDir || it.make?.mkincDir)}}
MKINC_DIR := {{=it.cmd_make?.mkincDir ?? (it.make?.mkincDir ?? "")}}
{{?}}
HAS_MIDI_IN := {{=it.product.buses.filter(x => x.type == "midi" && x.direction == "input").length > 0 ? "yes" : "no"}}
{{?it.make?.extra}}
{{=it.make.extra}}
{{?}}
{{?it.cmd_make?.extra}}
{{=it.cmd_make.extra}}
{{?}}

View File

@ -1,7 +1,7 @@
/*
* Tibia
*
* Copyright (C) 2024 Orastron Srl unipersonale
* Copyright (C) 2024, 2025 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
@ -73,7 +73,7 @@ static struct {
} param_data[PARAMETERS_N] = {
{{~it.product.parameters :p:i}}
{
/* .id = */ "{{=it.cmd.parameterIds[i]}}",
/* .id = */ "{{=p.id}}",
/* .out = */ {{=p.direction == "output" ? 1 : 0}},
/* .def = */ {{=p.defaultValue.toExponential()}}f,
/* .min = */ {{=p.minimum.toExponential()}}f,
@ -83,3 +83,7 @@ static struct {
{{~}}
};
#endif
{{?it.product.state && it.product.state.dspCustom}}
#define STATE_DSP_CUSTOM
{{?}}

View File

@ -1,7 +1,7 @@
/*
* Tibia
*
* Copyright (C) 2024 Orastron Srl unipersonale
* Copyright (C) 2024, 2025 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
@ -22,6 +22,7 @@
#include <stdint.h>
#include "data.h"
#include "plugin_api.h"
#include "plugin.h"
#include <stdio.h>
@ -161,6 +162,11 @@ float clampf(float x, float m, float M) {
}
int main(int argc, char * argv[]) {
#ifdef STATE_DSP_CUSTOM
(void)plugin_state_load;
(void)plugin_state_save;
#endif
#if PARAMETERS_N > 0
for (size_t i = 0; i < PARAMETERS_N; i++)
param_values[i] = param_data[i].def;
@ -352,7 +358,13 @@ int main(int argc, char * argv[]) {
printf(" %s: %g\n", param_data[i].id, param_values[i]);
#endif
plugin_init(&instance);
plugin_callbacks cbs = {
/* .handle = */ NULL,
/* .format = */ "cmd",
/* .get_bindir = */ NULL,
/* .get_datadir = */ NULL
};
plugin_init(&instance, &cbs);
#if PARAMETERS_N > 0
for (size_t i = 0; i < PARAMETERS_N; i++)

View File

@ -18,22 +18,32 @@
# File author: Stefano D'Angelo
#
TEMPLATE := daisy-seed
include vars.mk
TARGET = ${BUNDLE_NAME}
COMMON_DIR ?= .
DATA_DIR ?= .
PLUGIN_DIR ?= src
API_DIR ?= $(PLUGIN_DIR)
MKINC_DIR ?= $(COMMON_DIR)
COMMON_DIR := $(or $(COMMON_DIR),.)
DATA_DIR := $(or $(DATA_DIR),.)
PLUGIN_DIR := $(or $(PLUGIN_DIR),src)
-include $(MKINC_DIR)/vars-pre.mk
CPP_SOURCES = ${COMMON_DIR}/src/main.cpp ${CXX_SRCS_EXTRA}
LIBDAISY_DIR ?= ../libDaisy
SYSTEM_FILES_DIR = ${LIBDAISY_DIR}/core
TARGET := $(BUNDLE_NAME)
include ${SYSTEM_FILES_DIR}/Makefile
CPP_SOURCES := $(COMMON_DIR)/src/main.cpp $(CXX_SRCS_EXTRA)
C_SOURCES += ${C_SRCS_EXTRA}
SYSTEM_FILES_DIR := $(LIBDAISY_DIR)/core
CFLAGS += -I${DATA_DIR}/src -I${PLUGIN_DIR} ${CFLAGS_EXTRA}
LDFLAGS += ${LDFLAGS_EXTRA}
CXXFLAGS += -I${DATA_DIR}/src -I${PLUGIN_DIR} ${CXXFLAGS_EXTRA}
include $(SYSTEM_FILES_DIR)/Makefile
C_SOURCES += $(C_SRCS_EXTRA)
CFLAGS += -I$(DATA_DIR)/src -I$(PLUGIN_DIR) -I$(API_DIR) $(CFLAGS_EXTRA)
LDFLAGS += $(LDFLAGS_EXTRA)
CXXFLAGS += -I$(DATA_DIR)/src -I$(PLUGIN_DIR) -I$(API_DIR) $(CXXFLAGS_EXTRA)
-include $(COMMON_DIR)/extra.mk

View File

@ -20,14 +20,25 @@
BUNDLE_NAME := {{=it.product.bundleName}}
CFLAGS_EXTRA := {{=it.make && it.make.cflags ? it.make.cflags : ""}} {{=it.daisy_seed_make.cflags ? it.daisy_seed_make.cflags : ""}}
LDFLAGS_EXTRA := {{=it.make && it.make.ldflags ? it.make.ldflags : ""}} {{=it.daisy_seed_make.ldflags ? it.daisy_seed_make.ldflags : ""}}
CXXFLAGS_EXTRA := {{=it.make && it.make.cxxflags ? it.make.cxxflags : ""}} {{=it.daisy_seed_make.cxxflags ? it.daisy_seed_make.cxxflags : ""}}
{{?(it.daisy_seed_make?.commonDir || it.make?.commonDir)}}
COMMON_DIR := {{=it.daisy_seed_make?.commonDir ?? (it.make?.commonDir ?? "")}}
{{?}}
{{?(it.daisy_seed_make?.dataDir || it.make?.dataDir)}}
DATA_DIR := {{=it.daisy_seed_make?.dataDir ?? (it.make?.dataDir ?? "")}}
{{?}}
{{?(it.daisy_seed_make?.pluginDir || it.make?.pluginDir)}}
PLUGIN_DIR := {{=it.daisy_seed_make?.pluginDir ?? (it.make?.pluginDir ?? "")}}
{{?}}
{{?(it.daisy_seed_make?.apiDir || it.make?.apiDir)}}
API_DIR := {{=it.daisy_seed_make?.apiDir ?? (it.make?.apiDir ?? "")}}
{{?}}
{{?(it.daisy_seed_make?.mkincDir || it.make?.mkincDir)}}
MKINC_DIR := {{=it.daisy_seed_make?.mkincDir ?? (it.make?.mkincDir ?? "")}}
{{?}}
C_SRCS_EXTRA := {{=it.make && it.make.cSrcs ? it.make.cSrcs : ""}} {{=it.daisy_seed_make && it.daisy_seed_make.cSrcs ? it.daisy_seed_make.cSrcs : ""}}
CXX_SRCS_EXTRA := {{=it.make && it.make.cxxSrcs ? it.make.cxxSrcs : ""}} {{=it.daisy_seed_make && it.daisy_seed_make.cxxSrcs ? it.daisy_seed_make.cxxSrcs : ""}}
COMMON_DIR := {{=it.daisy_seed_make && it.daisy_seed_make.commonDir ? it.daisy_seed_make.commonDir : (it.make && it.make.commonDir ? it.make.commonDir : "")}}
DATA_DIR := {{=it.daisy_seed_make && it.daisy_seed_make.dataDir ? it.daisy_seed_make.dataDir : (it.make && it.make.dataDir ? it.make.dataDir : "")}}
PLUGIN_DIR := {{=it.daisy_seed_make && it.daisy_seed_make.pluginDir ? it.daisy_seed_make.pluginDir : (it.make && it.make.pluginDir ? it.make.pluginDir : "")}}
LIBDAISY_DIR := {{=it.daisy_seed_make.libdaisyDir}}
{{?it.make?.extra}}
{{=it.make.extra}}
{{?}}
{{?it.daisy_seed_make?.extra}}
{{=it.daisy_seed_make.extra}}
{{?}}

View File

@ -1,7 +1,7 @@
/*
* Tibia
*
* Copyright (C) 2024 Orastron Srl unipersonale
* Copyright (C) 2024, 2025 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
@ -98,3 +98,7 @@ static int midi_cc_maps[NUM_PARAMETERS] = { {{~it.daisy_seed.midiCCMaps :p}}{{=p
# endif
#endif
{{?it.product.state && it.product.state.dspCustom}}
#define STATE_DSP_CUSTOM
{{?}}

View File

@ -1,7 +1,7 @@
/*
* Tibia
*
* Copyright (C) 2023, 2024 Orastron Srl unipersonale
* Copyright (C) 2023-2025 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
@ -22,6 +22,7 @@
#include <stdint.h>
#include "data.h"
#include "plugin_api.h"
#include "plugin.h"
#include <string.h>
@ -149,6 +150,11 @@ static void AudioCallback(
}
int main() {
#ifdef STATE_DSP_CUSTOM
(void)plugin_state_load;
(void)plugin_state_save;
#endif
hardware.Configure();
hardware.Init();
@ -168,7 +174,13 @@ int main() {
hardware.SetAudioBlockSize(BLOCK_SIZE);
float sample_rate = hardware.AudioSampleRate();
plugin_init(&instance);
plugin_callbacks cbs = {
/* .handle = */ NULL,
/* .format = */ "daisy-seed",
/* .get_bindir = */ NULL,
/* .get_datadir = */ NULL
};
plugin_init(&instance, &cbs);
plugin_set_sample_rate(&instance, sample_rate);
if (plugin_mem_req(&instance) != 0)

View File

@ -18,35 +18,70 @@
# File author: Stefano D'Angelo
#
TEMPLATE := ios
include vars.mk
COMMON_DIR := $(or $(COMMON_DIR),.)
DATA_DIR := $(or $(DATA_DIR),.)
PLUGIN_DIR := $(or $(PLUGIN_DIR),src)
COMMON_DIR ?= .
DATA_DIR ?= .
PLUGIN_DIR ?= src
API_DIR ?= $(PLUGIN_DIR)
MKINC_DIR ?= $(COMMON_DIR)
BUILD_BIN_DIR := build/gen/res
BUILD_DATA_DIR := build/gen/res
-include $(MKINC_DIR)/vars-pre.mk
CFLAGS := -O3 -Wall -Wpedantic -Wextra
CFLAGS_ALL := -I$(DATA_DIR)/src -I$(PLUGIN_DIR) -I$(API_DIR) $(CFLAGS_EXTRA) $(CFLAGS)
CXXFLAGS := $(CFLAGS)
CXXFLAGS_ALL := -I$(DATA_DIR)/src -I$(PLUGIN_DIR) -I$(API_DIR) $(CXXFLAGS_EXTRA) $(CXXFLAGS)
LDFLAGS :=
LDFLAGS_ALL := $(LDFLAGS_EXTRA) $(LDFLAGS)
SOURCES := \
${DATA_DIR}/src/data.h \
${DATA_DIR}/src/index.html \
${COMMON_DIR}/src/app.swift \
${COMMON_DIR}/src/native.mm \
${COMMON_DIR}/src/app-Bridging-Header.h \
${PLUGIN_DIR}/plugin.h \
${C_SRCS_EXTRA} \
${CXX_SRCS_EXTRA} \
${SRCS_EXTRA}
SOURCES_OUT = $(addprefix build/gen/src/, $(notdir $(SOURCES)))
$(C_SRCS_EXTRA) \
$(M_SRCS_EXTRA) \
$(CXX_SRCS_EXTRA) \
$(SRCS_EXTRA) \
$(DATA_DIR)/src/data.h \
$(COMMON_DIR)/src/app.swift \
$(COMMON_DIR)/src/native.mm \
$(COMMON_DIR)/src/app-Bridging-Header.h \
$(PLUGIN_DIR)/plugin.h \
$(API_DIR)/plugin_api.h
SOURCES_OUT := $(addprefix build/gen/src/, $(notdir $(SOURCES)))
all: build/gen/${BUNDLE_NAME}.xcodeproj
RESOURCES := $(PLUGIN_DIR)/index.html
RESOURCES_OUT := $(addprefix build/gen/res/, $(notdir $(RESOURCES)))
build/gen/${BUNDLE_NAME}.xcodeproj: ${SOURCES_OUT}
xcodegen generate --spec project.yml -r build/gen -p build/gen
DIRS := build build/gen build/gen/src build/gen/res
build/gen/src:
ALL := build/gen/$(BUNDLE_NAME).xcodeproj
-include $(MKINC_DIR)/vars-extra.mk
all: $(ALL)
build/gen/$(BUNDLE_NAME).xcodeproj: $(SOURCES_OUT) $(RESOURCES_OUT) build/project_out.yml
xcodegen generate --spec build/project_out.yml -r build/gen -p build/gen
build/project_out.yml: project.yml build/
sed -e 's|@CFLAGS_ALL@|$(CFLAGS_ALL)|g' \
-e 's|@CXXFLAGS_ALL@|$(CXXFLAGS_ALL)|g' \
-e 's|@LDFLAGS_ALL@|$(LDFLAGS_ALL)|g' $< > $@
$(DIRS):
mkdir -p $@
clean:
rm -fr build
-include $(MKINC_DIR)/rules-extra.mk
.PHONY: all clean
.SECONDEXPANSION:
@ -55,3 +90,8 @@ PERCENT := %
$(SOURCES_OUT): build/gen/src/%: $$(filter $$(PERCENT)/%,$$(SOURCES)) | build/gen/src
cp -R $^ $@
$(RESOURCES_OUT): build/gen/res/%: $$(filter $$(PERCENT)/%,$$(RESOURCES)) | build/gen/res
cp -R $^ $@
-include $(MKINC_DIR)/rules-secondexp-extra.mk

View File

@ -30,9 +30,26 @@ targets:
type: application
sources:
- path: src
- path: res
type: folder
{{?it.ios_make.dependencies}}
dependencies:
{{~it.ios_make.dependencies :d}}
- target: {{=d}}{{~}}
{{?}}
settings:
base:
PRODUCT_BUNDLE_IDENTIFIER: {{=it.ios.productBundleIdentifier}}
{{?it.ios_make.development_team}}
CODE_SIGN_STYLE: Automatic
DEVELOPMENT_TEAM: {{=it.ios_make.development_team}}
{{?}}
OTHER_CFLAGS: "$(inherited) @CFLAGS_ALL@"
OTHER_CPLUSPLUSFLAGS: "$(inherited) @CXXFLAGS_ALL@"
OTHER_LDFLAGS: "$(inherited) @LDFLAGS_ALL@"
SWIFT_OBJC_BRIDGING_HEADER: src/app-Bridging-Header.h
{{?it.ios_make.headerSearchPaths}}
HEADER_SEARCH_PATHS: {{~it.ios_make.headerSearchPaths :p}}

View File

@ -20,10 +20,25 @@
BUNDLE_NAME := {{=it.product.bundleName}}
C_SRCS_EXTRA := {{=it.make && it.make.cSrcs ? it.make.cSrcs : ""}} {{=it.ios_make && it.ios_make.cSrcs ? it.ios_make.cSrcs : ""}}
CXX_SRCS_EXTRA := {{=it.make && it.make.cxxSrcs ? it.make.cxxSrcs : ""}} {{=it.ios_make && it.ios_make.cxxSrcs ? it.ios_make.cxxSrcs : ""}}
SRCS_EXTRA := {{=it.ios_make && it.ios_make.srcsExtra ? it.ios_make.srcsExtra : ""}}
{{?(it.ios_make?.commonDir || it.make?.commonDir)}}
COMMON_DIR := {{=it.ios_make?.commonDir ?? (it.make?.commonDir ?? "")}}
{{?}}
{{?(it.ios_make?.dataDir || it.make?.dataDir)}}
DATA_DIR := {{=it.ios_make?.dataDir ?? (it.make?.dataDir ?? "")}}
{{?}}
{{?(it.ios_make?.pluginDir || it.make?.pluginDir)}}
PLUGIN_DIR := {{=it.ios_make?.pluginDir ?? (it.make?.pluginDir ?? "")}}
{{?}}
{{?(it.ios_make?.apiDir || it.make?.apiDir)}}
API_DIR := {{=it.ios_make?.apiDir ?? (it.make?.apiDir ?? "")}}
{{?}}
{{?(it.ios_make?.mkincDir || it.make?.mkincDir)}}
MKINC_DIR := {{=it.ios_make?.mkincDir ?? (it.make?.mkincDir ?? "")}}
{{?}}
COMMON_DIR := {{=it.ios_make && it.ios_make.commonDir ? it.ios_make.commonDir : (it.make && it.make.commonDir ? it.make.commonDir : "")}}
DATA_DIR := {{=it.ios_make && it.ios_make.dataDir ? it.ios_make.dataDir : (it.make && it.make.dataDir ? it.make.dataDir : "")}}
PLUGIN_DIR := {{=it.ios_make && it.ios_make.pluginDir ? it.ios_make.pluginDir : (it.make && it.make.pluginDir ? it.make.pluginDir : "")}}
{{?it.make?.extra}}
{{=it.make.extra}}
{{?}}
{{?it.ios_make?.extra}}
{{=it.ios_make.extra}}
{{?}}

View File

@ -19,11 +19,11 @@
*/
import SwiftUI
import WebKit
@preconcurrency import WebKit
import AVFoundation
struct WebView: UIViewRepresentable {
class Coordinator: NSObject, WKScriptMessageHandlerWithReply {
class Coordinator: NSObject, WKScriptMessageHandlerWithReply, WKNavigationDelegate {
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage, replyHandler: @escaping (Any?, String?) -> Void) {
guard let body = message.body as? [String : Any] else { return }
guard let name = body["name"] as? String else { return }
@ -57,6 +57,15 @@ struct WebView: UIViewRepresentable {
break;
}
}
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
if let url = navigationAction.request.url, navigationAction.navigationType == .linkActivated {
UIApplication.shared.open(url)
decisionHandler(.cancel)
} else {
decisionHandler(.allow)
}
}
}
func makeCoordinator() -> Coordinator {
@ -67,8 +76,10 @@ struct WebView: UIViewRepresentable {
func makeUIView(context: Context) -> WKWebView {
let configuration = WKWebViewConfiguration()
configuration.userContentController.addScriptMessageHandler(Coordinator(), contentWorld: .page, name: "listener")
configuration.userContentController.addScriptMessageHandler(Coordinator(), contentWorld: .page, name: "messageHandler")
configuration.setValue(true, forKey: "allowUniversalAccessFromFileURLs")
let webView = WKWebView(frame: .zero, configuration: configuration)
webView.navigationDelegate = context.coordinator
//webView.isInspectable = true
return webView
}
@ -81,8 +92,8 @@ struct WebView: UIViewRepresentable {
struct ContentView: View {
var body: some View {
let url = Bundle.main.url(forResource: "index", withExtension: "html")
WebView(url: url!)
let url = Bundle.main.url(forResource: "index", withExtension: "html", subdirectory: "res")
WebView(url: url!).ignoresSafeArea().preferredColorScheme(.dark)
}
}
@ -97,12 +108,12 @@ struct templateApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.onReceive(NotificationCenter.default.publisher(for: UIApplication.didEnterBackgroundNotification)) { _ in
audioPause()
}
.onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in
audioResume()
}
.onReceive(NotificationCenter.default.publisher(for: UIApplication.didEnterBackgroundNotification)) { _ in
audioPause()
}
.onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in
audioResume()
}
}
}
}

View File

@ -1,7 +1,7 @@
/*
* Tibia
*
* Copyright (C) 2024 Orastron Srl unipersonale
* Copyright (C) 2024, 2025 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
@ -81,3 +81,7 @@ static struct {
{{~}}
};
#endif
{{?it.product.state && it.product.state.dspCustom}}
#define STATE_DSP_CUSTOM
{{?}}

View File

@ -26,7 +26,7 @@
<title>{{=it.product.name}}</title>
<script type="text/javascript">
function request(data) {
return window.webkit.messageHandlers.listener.postMessage(data);
return window.webkit.messageHandlers.messageHandler.postMessage(data);
}
function needAudioPermission() {

View File

@ -1,7 +1,7 @@
/*
* Tibia
*
* Copyright (C) 2023, 2024 Orastron Srl unipersonale
* Copyright (C) 2023-2025 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
@ -22,6 +22,7 @@
#include <stdint.h>
#include "data.h"
#include "plugin_api.h"
#include "plugin.h"
#if PARAMETERS_N > 0
#include <algorithm>
@ -189,6 +190,11 @@ void (^midiReceiveBlock)(const MIDIEventList *evtlist, void *srcConnRefCon) = ^(
extern "C"
char audioStart() {
#ifdef STATE_DSP_CUSTOM
(void)plugin_state_load;
(void)plugin_state_save;
#endif
#if NUM_CHANNELS_IN + NUM_CHANNELS_OUT > 0
# if NUM_CHANNELS_IN == 0
deviceConfig = ma_device_config_init(ma_device_type_playback);
@ -251,9 +257,15 @@ char audioStart() {
}
}
#endif
plugin_init(&instance);
plugin_callbacks cbs = {
/* .handle = */ NULL,
/* .format = */ "ios",
/* .get_bindir = */ NULL,
/* .get_datadir = */ NULL
};
plugin_init(&instance, &cbs);
#if PARAMETERS_N > 0
for (size_t i = 0; i < PARAMETERS_N; i++) {
if (!param_data[i].out)
@ -356,7 +368,7 @@ void audioPause() {
extern "C"
void audioResume() {
// TODO: couldn't this could fail?...
// TODO: could this fail?...
if (device_inited) {
ma_device_init(NULL, &deviceConfig, &device);
ma_device_start(&device);

View File

@ -1,7 +1,7 @@
#
# Tibia
#
# Copyright (C) 2024 Orastron Srl unipersonale
# Copyright (C) 2024, 2025 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
@ -18,98 +18,157 @@
# File author: Stefano D'Angelo
#
TEMPLATE := lv2
include vars.mk
COMMON_DIR ?= .
DATA_DIR ?= .
PLUGIN_DIR ?= src
API_DIR ?= $(PLUGIN_DIR)
MKINC_DIR ?= $(COMMON_DIR)
BUNDLE_DIR := $(BUNDLE_NAME).lv2
BUILD_BIN_DIR := build/$(BUNDLE_DIR)
BUILD_DATA_DIR := build/$(BUNDLE_DIR)
-include $(MKINC_DIR)/vars-pre.mk
ifeq ($(OS), Windows_NT)
DLL_SUFFIX = .dll
LV2DIR = $(shell echo '${COMMONPROGRAMFILES}' | sed 's:\\:/:g')/LV2
LV2DIR_USER = $(shell echo '${APPDATA}' | sed 's:\\:/:g')/LV2
CC = gcc
CXX = g++
DLL_SUFFIX := .dll
UI_TYPE := WindowsUI
LV2DIR := $(subst \,/,$(COMMONPROGRAMFILES))/LV2
LV2DIR_USER := $(subst \,/,$(APPDATA))/LV2
CC := gcc
CXX := g++
else
UNAME_S = $(shell uname -s)
ifeq ($(UNAME_S), Darwin)
DLL_SUFFIX = .dylib
LV2DIR = /Library/Audio/Plug-Ins/LV2
LV2DIR_USER = ${HOME}/Library/Audio/Plug-Ins/LV2
CC = clang
CXX = clang++
DLL_SUFFIX := .dylib
UI_TYPE := CocoaUI
LV2DIR := /Library/Audio/Plug-Ins/LV2
LV2DIR_USER := $(HOME)/Library/Audio/Plug-Ins/LV2
CC := clang
CXX := clang++
else
DLL_SUFFIX = .so
PREFIX = /usr/local
LV2DIR = ${PREFIX}/lib/lv2
LV2DIR_USER = ${HOME}/.lv2
CC = gcc
CXX = g++
DLL_SUFFIX := .so
UI_TYPE := X11UI
PREFIX := /usr/local
LV2DIR := $(PREFIX)/lib/lv2
LV2DIR_USER := $(HOME)/.lv2
CC := gcc
CXX := g++
endif
endif
COMMON_DIR := $(or $(COMMON_DIR),.)
DATA_DIR := $(or $(DATA_DIR),.)
PLUGIN_DIR := $(or $(PLUGIN_DIR),src)
CFLAGS := -O3 -Wall -Wpedantic -Wextra
CFLAGS_ALL := -I$(DATA_DIR)/src -I$(PLUGIN_DIR) -I$(API_DIR) $(shell pkg-config --cflags lv2) -fPIC $(CFLAGS_EXTRA) $(CFLAGS)
CFLAGS = -O3 -Wall -Wpedantic -Wextra
CFLAGS_ALL = -I${DATA_DIR}/src -I${PLUGIN_DIR} -fPIC ${CFLAGS} ${CFLAGS_EXTRA}
LDFLAGS :=
LDFLAGS_ALL := -shared $(shell pkg-config --libs lv2) $(LDFLAGS_EXTRA) $(LDFLAGS)
LDFLAGS =
LDFLAGS_ALL = -shared ${LDFLAGS} ${LDFLAGS_EXTRA}
CXXFLAGS = ${CFLAGS}
CXXFLAGS_ALL = -I${DATA_DIR}/src -I${PLUGIN_DIR} -fPIC ${CXXFLAGS} ${CXXFLAGS_EXTRA}
CXXFLAGS := $(CFLAGS)
CXXFLAGS_ALL := -I$(DATA_DIR)/src -I$(PLUGIN_DIR) -I$(API_DIR) -fPIC $(CXXFLAGS_EXTRA) $(CXXFLAGS)
ifeq ($(UNAME_S), Darwin)
CFLAGS_ALL := ${CFLAGS_ALL} -arch arm64 -arch x86_64
LDFLAGS_ALL := ${LDFLAGS_ALL} -arch arm64 -arch x86_64
CXXFLAGS_ALL := ${CXXFLAGS_ALL} -arch arm64 -arch x86_64
CFLAGS_ALL := $(CFLAGS_ALL) -arch arm64 -arch x86_64
LDFLAGS_ALL := $(LDFLAGS_ALL) -arch arm64 -arch x86_64
CXXFLAGS_ALL := $(CXXFLAGS_ALL) -arch arm64 -arch x86_64
endif
BUNDLE_DIR = ${BUNDLE_NAME}.lv2
DLL_FILE := $(BUNDLE_NAME)$(DLL_SUFFIX)
DLL_FILE = ${BUNDLE_NAME}${DLL_SUFFIX}
C_SRCS := $(COMMON_DIR)/src/lv2.c $(C_SRCS_EXTRA)
C_OBJS := $(addprefix build/obj/, $(notdir $(C_SRCS:.c=.o)))
C_SRCS = ${COMMON_DIR}/src/lv2.c ${C_SRCS_EXTRA}
C_OBJS = $(addprefix build/obj/, $(notdir $(C_SRCS:.c=.o)))
M_SRCS := $(M_SRCS_EXTRA)
M_OBJS := $(addprefix build/obj/, $(notdir $(M_SRCS:.m=.o)))
CXX_SRCS = ${CXX_SRCS_EXTRA}
CXX_OBJS = $(addprefix build/obj/, $(notdir $(CXX_SRCS:.cpp=.o)))
CXX_SRCS := $(CXX_SRCS_EXTRA)
CXX_OBJS := $(addprefix build/obj/, $(notdir $(CXX_SRCS:.cpp=.o)))
all: build/${BUNDLE_DIR}/manifest.ttl build/${BUNDLE_DIR}/${DLL_FILE}
DIRS := build build/$(BUNDLE_DIR) build/obj
build/${BUNDLE_DIR}/manifest.ttl: ${DATA_DIR}/data/manifest.ttl.in | build/${BUNDLE_DIR}
cat $^ | sed s/@DLL_SUFFIX@/${DLL_SUFFIX}/g > $@
ALL := build/$(BUNDLE_DIR)/manifest.ttl build/$(BUNDLE_DIR)/$(DLL_FILE)
STRIP_ALL := build/$(BUNDLE_DIR)/$(DLL_FILE) build/$(BUNDLE_DIR)/manifest.ttl
STRIP_PHONY :=
STRIP_PREREQS := $(STRIP_ALL) $(STRIP_PHONY)
-include $(MKINC_DIR)/vars-extra.mk
all: $(ALL)
build/$(BUNDLE_DIR)/manifest.ttl: $(DATA_DIR)/data/manifest.ttl.in | build/$(BUNDLE_DIR)
cat $^ | sed s/@DLL_SUFFIX@/$(DLL_SUFFIX)/g | sed s/@UI_TYPE@/$(UI_TYPE)/g > $@
ifeq ($(CXX_OBJS),)
build/${BUNDLE_DIR}/${DLL_FILE}: ${C_OBJS} | build/${BUNDLE_DIR}
${CC} $^ -o $@ ${CFLAGS_ALL} ${LDFLAGS_ALL}
build/$(BUNDLE_DIR)/$(DLL_FILE): $(C_OBJS) $(M_OBJS) | build/$(BUNDLE_DIR)
$(CC) $^ -o $@ $(CFLAGS_ALL) $(LDFLAGS_ALL)
else
build/${BUNDLE_DIR}/${DLL_FILE}: ${C_OBJS} ${CXX_OBJS} | build/${BUNDLE_DIR}
${CXX} $^ -o $@ ${CFLAGS_ALL} ${CXXFLAGS_ALL} ${LDFLAGS_ALL}
build/$(BUNDLE_DIR)/$(DLL_FILE): $(C_OBJS) $(M_OBJS) $(CXX_OBJS) | build/$(BUNDLE_DIR)
$(CXX) $^ -o $@ $(CFLAGS_ALL) $(CXXFLAGS_ALL) $(LDFLAGS_ALL)
endif
build/${BUNDLE_DIR} build/obj:
$(DIRS):
mkdir -p $@
clean:
rm -fr build
strip: $(STRIP_PREREQS)
strip build/$(BUNDLE_DIR)/$(DLL_FILE)
#Reaper can't handle this
#rdfproc lv2_store parse build/$(BUNDLE_DIR)/manifest.ttl turtle || (rm lv2_store* && exit 1)
#rdfproc lv2_store serialize ntriples > build/$(BUNDLE_DIR)/manifest.ttl || (rm lv2_store* && exit 1)
#rm lv2_store*
#f=`cat build/$(BUNDLE_DIR)/manifest.ttl` && echo "$f" | sed '/^[[:space:]]**$/d' > build/$(BUNDLE_DIR)/manifest.ttl
f=`sed '/^[[:space:]]*$$/d' build/$(BUNDLE_DIR)/manifest.ttl` && echo "$$f" > build/$(BUNDLE_DIR)/manifest.ttl
install: all
mkdir -p -m 0755 "${LV2DIR}/${BUNDLE_DIR}"
install -m 0644 build/${BUNDLE_DIR}/manifest.ttl "${LV2DIR}/${BUNDLE_DIR}"
install -m 0755 build/${BUNDLE_DIR}/${DLL_FILE} "${LV2DIR}/${BUNDLE_DIR}"
@for d in `find build/$(BUNDLE_DIR) -type d`; do \
d=`echo $$d | sed 's:^build/::'` ; \
echo mkdir -m 0755 -p "$(LV2DIR)/$$d"; \
mkdir -m 0755 -p "$(LV2DIR)/$$d"; \
done
@for f in `find build/$(BUNDLE_DIR) -type f`; do \
m=`[ -x $$f ] && echo 0755 || echo 0644`; \
d=`echo $$f | sed 's:^build/::'` ; \
d=`dirname $$d`; \
echo install -m $$m $$f "$(LV2DIR)/$$d"; \
install -m $$m $$f "$(LV2DIR)/$$d"; \
done
install-user: all
mkdir -p -m 0755 "${LV2DIR_USER}/${BUNDLE_DIR}"
install -m 0644 build/${BUNDLE_DIR}/manifest.ttl "${LV2DIR_USER}/${BUNDLE_DIR}"
install -m 0755 build/${BUNDLE_DIR}/${DLL_FILE} "${LV2DIR_USER}/${BUNDLE_DIR}"
@for d in `find build/$(BUNDLE_DIR) -type d`; do \
d=`echo $$d | sed 's:^build/::'` ; \
echo mkdir -m 0755 -p "$(LV2DIR_USER)/$$d"; \
mkdir -m 0755 -p "$(LV2DIR_USER)/$$d"; \
done
@for f in `find build/$(BUNDLE_DIR) -type f`; do \
m=`[ -x $$f ] && echo 0755 || echo 0644`; \
d=`echo $$f | sed 's:^build/::'` ; \
d=`dirname $$d`; \
echo install -m $$m $$f "$(LV2DIR_USER)/$$d"; \
install -m $$m $$f "$(LV2DIR_USER)/$$d"; \
done
.PHONY: all clean install install-user
-include $(MKINC_DIR)/rules-extra.mk
.PHONY: all clean strip $(STRIP_PHONY) install install-user
.SECONDEXPANSION:
PERCENT := %
$(C_OBJS): build/obj/%.o: $$(filter $$(PERCENT)/$$(basename $$(notdir $$@)).c,$$(C_SRCS)) | build/obj
${CC} $^ -o $@ -c ${CFLAGS_ALL}
$(CC) $^ -o $@ -c $(CFLAGS_ALL)
$(M_OBJS): build/obj/%.o: $$(filter $$(PERCENT)/$$(basename $$(notdir $$@)).m,$$(M_SRCS)) | build/obj
$(CC) $^ -o $@ -c $(CFLAGS_ALL)
$(CXX_OBJS): build/obj/%.o: $$(filter $$(PERCENT)/$$(basename $$(notdir $$@)).cpp,$$(CXX_SRCS)) | build/obj
${CXX} $^ -o $@ -c ${CXXFLAGS_ALL}
$(CXX) $^ -o $@ -c $(CXXFLAGS_ALL)
-include $(MKINC_DIR)/rules-secondexp-extra.mk

View File

@ -20,13 +20,25 @@
BUNDLE_NAME := {{=it.product.bundleName}}
CFLAGS_EXTRA := {{=it.make && it.make.cflags ? it.make.cflags : ""}} {{=it.lv2_make && it.lv2_make.cflags ? it.lv2_make.cflags : ""}}
LDFLAGS_EXTRA := {{=it.make && it.make.ldflags ? it.make.ldflags : ""}} {{=it.lv2_make && it.lv2_make.ldflags ? it.lv2_make.ldflags : ""}}
CXXFLAGS_EXTRA := {{=it.make && it.make.cxxflags ? it.make.cxxflags : ""}} {{=it.lv2_make && it.lv2_make.cxxflags ? it.lv2_make.cxxflags : ""}}
{{?(it.lv2_make?.commonDir || it.make?.commonDir)}}
COMMON_DIR := {{=it.lv2_make?.commonDir ?? (it.make?.commonDir ?? "")}}
{{?}}
{{?(it.lv2_make?.dataDir || it.make?.dataDir)}}
DATA_DIR := {{=it.lv2_make?.dataDir ?? (it.make?.dataDir ?? "")}}
{{?}}
{{?(it.lv2_make?.pluginDir || it.make?.pluginDir)}}
PLUGIN_DIR := {{=it.lv2_make?.pluginDir ?? (it.make?.pluginDir ?? "")}}
{{?}}
{{?(it.lv2_make?.apiDir || it.make?.apiDir)}}
API_DIR := {{=it.lv2_make?.apiDir ?? (it.make?.apiDir ?? "")}}
{{?}}
{{?(it.lv2_make?.mkincDir || it.make?.mkincDir)}}
MKINC_DIR := {{=it.lv2_make?.mkincDir ?? (it.make?.mkincDir ?? "")}}
{{?}}
C_SRCS_EXTRA := {{=it.make && it.make.cSrcs ? it.make.cSrcs : ""}} {{=it.lv2_make && it.lv2_make.cSrcs ? it.lv2_make.cSrcs : ""}}
CXX_SRCS_EXTRA := {{=it.make && it.make.cxxSrcs ? it.make.cxxSrcs : ""}} {{=it.lv2_make && it.lv2_make.cxxSrcs ? it.lv2_make.cxxSrcs : ""}}
COMMON_DIR := {{=it.lv2_make && it.lv2_make.commonDir ? it.lv2_make.commonDir : (it.make && it.make.commonDir ? it.make.commonDir : "")}}
DATA_DIR := {{=it.lv2_make && it.lv2_make.dataDir ? it.lv2_make.dataDir : (it.make && it.make.dataDir ? it.make.dataDir : "")}}
PLUGIN_DIR := {{=it.lv2_make && it.lv2_make.pluginDir ? it.lv2_make.pluginDir : (it.make && it.make.pluginDir ? it.make.pluginDir : "")}}
{{?it.make?.extra}}
{{=it.make.extra}}
{{?}}
{{?it.lv2_make?.extra}}
{{=it.lv2_make.extra}}
{{?}}

View File

@ -0,0 +1 @@
This is experimental and broken, do not use this.

View File

@ -0,0 +1,95 @@
{{~it.tibia.lv2.prefixes :p}}
@prefix {{=p.id}}: <{{=p.uri}}> .
{{~}}
{{~it.product.parameters :p}}
plugin:{{=p.id}}
a lv2:Parameter ;
rdfs:label "{{=p.name}}" ;
lv2:name "{{=p.name}}" ;
{{?"shortName" in p}}
lv2:shortName "{{=p.shortName.substring(0, 16)}}" ;
{{?}}
lv2:symbol "{{=p.id}}" ;
rdfs:range atom:Float .
{{~}}
{{=it.tibia.lv2.ttlURI(it.lv2.uri)}}
a lv2:Plugin ;
{{~it.lv2.types :t}}
a {{=it.tibia.lv2.ttlURI(t)}} ;
{{~}}
{{?it.lv2.project}}
lv2:project {{=it.tibia.lv2.ttlURI(it.lv2.project)}} ;
{{?}}
lv2:binary <{{=it.product.bundleName}}@DLL_SUFFIX@> ;
doap:name "{{=it.product.name}}" ;
doap:maintainer [
a foaf:Organization ;
foaf:name "{{=it.company.name}}" ;
foaf:mbox <mailto:{{=it.company.email}}> ;
rdfs:seeAlso {{=it.tibia.lv2.ttlURI(it.company.url)}}
] ;
lv2:minorVersion {{=/^([0-9]+)\./.exec(it.lv2.version)[1]}} ;
lv2:microVersion {{=/^[0-9]+\.([0-9]+)/.exec(it.lv2.version)[1]}} ;
{{?it.tibia.lv2.ports.find(p => p.type == "midi" || p.type == "param")}}
lv2:requiredFeature urid:map ;
lv2:optionalFeature log:log ;
{{?}}
{{?it.product.parameters.find(p => p.direction == "input")}}
patch:writable {{=it.product.parameters.filter(p => p.direction == "input").map(p => "plugin:" + p.id).join(" , ")}} ;
{{?}}
{{?it.product.parameters.find(p => p.direction == "output")}}
patch:readable {{=it.product.parameters.filter(p => p.direction == "output").map(p => "plugin:" + p.id).join(" , ")}} ;
{{?}}
lv2:optionalFeature lv2:hardRTCapable ;
{{?it.product.ui}}
ui:ui plugin:ui ;
{{?}}
lv2:port [
{{~it.tibia.lv2.ports :p:i}}
a {{?p.type == "control"}}lv2:ControlPort{{??(p.type == "midi" || p.type == "param")}}atom:AtomPort{{??}}{{?p.cv}}lv2:CVPort{{??}}lv2:AudioPort{{?}}{{?}} ,
{{?p.direction == "input"}}lv2:InputPort{{??}}lv2:OutputPort{{?}} ;
lv2:name "{{=p.name}}" ;
{{?"shortName" in p}}
lv2:shortName "{{=p.shortName.substring(0, 16)}}" ;
{{?}}
lv2:symbol "{{=p.id}}" ;
{{?p.type == "param"}}
atom:bufferType atom:Sequence ;
atom:supports atom:Object ;
atom:supports patch:Message ;
{{??p.type == "midi"}}
atom:bufferType atom:Sequence ;
atom:supports midi:MidiEvent ;
{{?}}
{{?p.sidechain}}
lv2:portProperty lv2:isSideChain ;
{{?}}
{{?p.control}}
lv2:designation lv2:control ;
{{?}}
{{?p.optional}}
lv2:portProperty lv2:connectionOptional ;
{{?}}
lv2:index {{=i}}
{{?i < it.tibia.lv2.ports.length - 1}}
] , [
{{??}}
] .
{{?}}
{{~}}
{{?it.product.ui}}
plugin:ui
a ui:@UI_TYPE@ ;
ui:binary <{{=it.product.bundleName}}@DLL_SUFFIX@> ;
{{?!it.product.ui.userResizable}}
lv2:optionalFeature ui:noUserResize ; # doesn't work as lv2:requiredFeature, don't ask me why
{{?!it.product.ui.selfResizable}}
lv2:optionalFeature ui:fixedSize ;
{{?}}
{{?}}
lv2:requiredFeature ui:idleInterface ;
lv2:extensionData ui:idleInterface .
{{?}}

View File

@ -0,0 +1,78 @@
/*
* Tibia
*
* Copyright (C) 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
*/
#define DATA_LV2_URI "{{=it.tibia.CGetUTF8StringLiteral(it.tibia.lv2.expandURI(it.lv2.uri))}}"
#define DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "input").reduce((s, x) => s += x.channels == "mono" ? 1 : 2, 0)}}
#define DATA_PRODUCT_AUDIO_OUTPUT_CHANNELS_N {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "output").reduce((s, x) => s += x.channels == "mono" ? 1 : 2, 0)}}
#define DATA_PRODUCT_MIDI_INPUTS_N {{=it.product.buses.filter(x => x.type == "midi" && x.direction == "input").length}}
#define DATA_PRODUCT_MIDI_OUTPUTS_N {{=it.product.buses.filter(x => x.type == "midi" && x.direction == "output").length}}
#define DATA_PRODUCT_CONTROL_INPUTS_N {{=it.product.parameters.filter(x => x.direction == "input").length}}
#define DATA_PRODUCT_CONTROL_OUTPUTS_N {{=it.product.parameters.filter(x => x.direction == "output").length}}
#if DATA_PRODUCT_MIDI_INPUTS_N > 0
static uint32_t midi_in_index[DATA_PRODUCT_MIDI_INPUTS_N] = {
{{~it.tibia.lv2.ports.filter(x => x.type == "midi" && x.direction == "input") :p}}{{=p.busIndex}}, {{~}}
};
#endif
#if DATA_PRODUCT_CONTROL_INPUTS_N > 0
# define DATA_PARAM_BYPASS 1
# define DATA_PARAM_TOGGLED (1<<1)
# define DATA_PARAM_INTEGER (1<<2)
static struct {
uint32_t index;
float min;
float max;
float def;
uint32_t flags;
} param_data[DATA_PRODUCT_CONTROL_INPUTS_N] = {
{{~it.tibia.lv2.ports.filter(x => x.type == "control" && x.direction == "input") :p}}
{
/* .index = */ {{=p.paramIndex}},
/* .min = */ {{=p.minimum.toExponential()}}f,
/* .max = */ {{=p.maximum.toExponential()}}f,
/* .def = */ {{=p.defaultValue.toExponential()}}f,
/* .flags = */ {{?p.isBypass}}DATA_PARAM_BYPASS{{??p.isLatency}}DATA_PARAM_INTEGER{{??}}0{{?p.toggled}} | DATA_PARAM_TOGGLED{{?}}{{?p.integer}} | DATA_PARAM_INTEGER{{?}}{{?}}
},
{{~}}
};
#endif
#if DATA_PRODUCT_CONTROL_OUTPUTS_N > 0
static uint32_t param_out_index[DATA_PRODUCT_CONTROL_OUTPUTS_N] = {
{{~it.tibia.lv2.ports.filter(x => x.type == "control" && x.direction == "output") :p}}{{=p.paramIndex}}, {{~}}
};
#endif
{{?it.lv2.ui}}
#define DATA_UI
#define DATA_LV2_UI_URI "{{=it.tibia.CGetUTF8StringLiteral(it.tibia.lv2.expandURI(it.lv2.ui.uri))}}"
#define DATA_UI_USER_RESIZABLE {{=it.product.ui.userResizable ? 1 : 0}}
#if DATA_PRODUCT_CONTROL_INPUTS_N > 0
static uint32_t index_to_param[DATA_PRODUCT_CONTROL_INPUTS_N + DATA_PRODUCT_CONTROL_OUTPUTS_N] = {
{{~it.tibia.lv2.ports.filter(x => x.type == "control").map((e, i) => ({ i: i, pi: e.paramIndex })).sort((a, b) => a.pi - b.pi) :p}}{{=p.i + it.tibia.lv2.ports.filter(x => x.type != "control").length}}, {{~}}
};
#endif
{{?}}

View File

@ -0,0 +1,499 @@
/*
* Tibia
*
* Copyright (C) 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
*/
#include <stdlib.h>
#include <stdint.h>
#include "data.h"
#include "plugin_api.h"
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-function"
#include "plugin.h"
#ifdef DATA_UI
# include "plugin_ui.h"
#endif
#pragma GCC diagnostic pop
#include "lv2/core/lv2.h"
#include "lv2/core/lv2_util.h"
#include "lv2/log/log.h"
#include "lv2/log/logger.h"
#include "lv2/urid/urid.h"
#if DATA_PRODUCT_MIDI_INPUTS_N + DATA_PRODUCT_MIDI_OUTPUTS_N > 0
# include "lv2/atom/util.h"
# include "lv2/atom/atom.h"
# include "lv2/midi/midi.h"
#endif
#ifdef DATA_UI
# include "lv2/ui/ui.h"
#endif
#include <string.h>
#if defined(__i386__) || defined(__x86_64__)
# include <xmmintrin.h>
# include <pmmintrin.h>
#endif
static inline float clampf(float x, float m, float M) {
return x < m ? m : (x > M ? M : x);
}
static float adjust_param(size_t index, float value) {
if (param_data[index].flags & DATA_PARAM_BYPASS)
value = value > 0.f ? 0.f : 1.f;
else if (param_data[index].flags & DATA_PARAM_TOGGLED)
value = value > 0.f ? 1.f : 0.f;
else if (param_data[index].flags & DATA_PARAM_INTEGER)
value = (int32_t)(value + (value >= 0.f ? 0.5f : -0.5f));
return clampf(value, param_data[index].min, param_data[index].max);
}
typedef struct {
plugin p;
#if DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N > 0
const float * x[DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N];
#endif
#if DATA_PRODUCT_AUDIO_OUTPUT_CHANNELS_N > 0
float * y[DATA_PRODUCT_AUDIO_OUTPUT_CHANNELS_N];
#endif
#if DATA_PRODUCT_MIDI_INPUTS_N > 0
const LV2_Atom_Sequence * x_midi[DATA_PRODUCT_MIDI_INPUTS_N];
#endif
#if DATA_PRODUCT_MIDI_OUTPUTS_N > 0
LV2_Atom_Sequence * y_midi[DATA_PRODUCT_MIDI_OUTPUTS_N];
#endif
#if (DATA_PRODUCT_CONTROL_INPUTS_N + DATA_PRODUCT_CONTROL_OUTPUTS_N) > 0
float * c[DATA_PRODUCT_CONTROL_INPUTS_N + DATA_PRODUCT_CONTROL_OUTPUTS_N];
#endif
#if DATA_PRODUCT_CONTROL_INPUTS_N > 0
float params[DATA_PRODUCT_CONTROL_INPUTS_N];
#endif
void * mem;
char * bundle_path;
LV2_Log_Logger logger;
LV2_URID_Map * map;
#if DATA_PRODUCT_MIDI_INPUTS_N + DATA_PRODUCT_MIDI_OUTPUTS_N > 0
LV2_URID uri_midi_MidiEvent;
#endif
} plugin_instance;
static const char * get_bundle_path_cb(void *handle) {
plugin_instance *instance = (plugin_instance *)handle;
return instance->bundle_path;
}
static LV2_Handle instantiate(const struct LV2_Descriptor * descriptor, double sample_rate, const char * bundle_path, const LV2_Feature * const * features) {
(void)descriptor;
(void)bundle_path;
plugin_instance *instance = malloc(sizeof(plugin_instance));
if (instance == NULL)
goto err_instance;
instance->bundle_path = strdup(bundle_path);
if (instance->bundle_path == NULL)
goto err_bundle_path;
// from https://lv2plug.in/book
const char* missing = lv2_features_query(features,
LV2_LOG__log, &instance->logger.log, false,
LV2_URID__map, &instance->map, true,
NULL);
lv2_log_logger_set_map(&instance->logger, instance->map);
if (missing) {
lv2_log_error(&instance->logger, "Missing feature <%s>\n", missing);
goto err_urid;
}
#if DATA_PRODUCT_MIDI_INPUTS_N + DATA_PRODUCT_MIDI_OUTPUTS_N > 0
instance->uri_midi_MidiEvent = instance->map->map(instance->map->handle, LV2_MIDI__MidiEvent);
#endif
plugin_callbacks cbs = {
/* .handle = */ (void *)instance,
/* .format = */ "lv2",
/* .get_bindir = */ get_bundle_path_cb,
/* .get_datadir = */ get_bundle_path_cb
};
plugin_init(&instance->p, &cbs);
plugin_set_sample_rate(&instance->p, sample_rate);
size_t req = plugin_mem_req(&instance->p);
if (req != 0) {
instance->mem = malloc(req);
if (instance->mem == NULL) {
lv2_log_error(&instance->logger, "Not enough memory\n");
goto err_mem;
}
plugin_mem_set(&instance->p, instance->mem);
} else
instance->mem = NULL;
#if DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N > 0
for (uint32_t i = 0; i < DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N; i++)
instance->x[i] = NULL;
#endif
#if DATA_PRODUCT_AUDIO_OUTPUT_CHANNELS_N > 0
for (uint32_t i = 0; i < DATA_PRODUCT_AUDIO_OUTPUT_CHANNELS_N; i++)
instance->y[i] = NULL;
#endif
#if DATA_PRODUCT_MIDI_INPUTS_N > 0
for (uint32_t i = 0; i < DATA_PRODUCT_MIDI_INPUTS_N; i++)
instance->x_midi[i] = NULL;
#endif
#if DATA_PRODUCT_MIDI_OUTPUTS_N > 0
for (uint32_t i = 0; i < DATA_PRODUCT_MIDI_OUTPUTS_N; i++)
instance->y_midi[i] = NULL;
#endif
#if (DATA_PRODUCT_CONTROL_INPUTS_N + DATA_PRODUCT_CONTROL_OUTPUTS_N) > 0
for (uint32_t i = 0; i < DATA_PRODUCT_CONTROL_INPUTS_N + DATA_PRODUCT_CONTROL_OUTPUTS_N; i++)
instance->c[i] = NULL;
#endif
return instance;
err_mem:
plugin_fini(&instance->p);
err_urid:
free(instance->bundle_path);
err_bundle_path:
free(instance);
err_instance:
return NULL;
}
static void connect_port(LV2_Handle instance, uint32_t port, void * data_location) {
plugin_instance * i = (plugin_instance *)instance;
#if DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N > 0
if (port < DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N) {
i->x[port] = data_location;
return;
}
port -= DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N;
#endif
#if DATA_PRODUCT_AUDIO_OUTPUT_CHANNELS_N > 0
if (port < DATA_PRODUCT_AUDIO_OUTPUT_CHANNELS_N) {
i->y[port] = data_location;
return;
}
port -= DATA_PRODUCT_AUDIO_OUTPUT_CHANNELS_N;
#endif
#if DATA_PRODUCT_MIDI_INPUTS_N > 0
if (port < DATA_PRODUCT_MIDI_INPUTS_N) {
i->x_midi[port] = data_location;
return;
}
port -= DATA_PRODUCT_MIDI_INPUTS_N;
#endif
#if DATA_PRODUCT_MIDI_OUTPUTS_N > 0
if (port < DATA_PRODUCT_MIDI_OUTPUTS_N) {
i->y_midi[port] = data_location;
return;
}
port -= DATA_PRODUCT_MIDI_OUTPUTS_N;
#endif
#if (DATA_PRODUCT_CONTROL_INPUTS_N + DATA_PRODUCT_CONTROL_OUTPUTS_N) > 0
i->c[port] = data_location;
#endif
}
static void activate(LV2_Handle instance) {
plugin_instance * i = (plugin_instance *)instance;
#if DATA_PRODUCT_CONTROL_INPUTS_N > 0
for (uint32_t j = 0; j < DATA_PRODUCT_CONTROL_INPUTS_N; j++) {
i->params[j] = i->c[j] != NULL ? *i->c[j] : param_data[j].def;
plugin_set_parameter(&i->p, param_data[j].index, i->params[j]);
}
#endif
plugin_reset(&i->p);
}
static void run(LV2_Handle instance, uint32_t sample_count) {
plugin_instance * i = (plugin_instance *)instance;
#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
#if DATA_PRODUCT_CONTROL_INPUTS_N > 0
for (uint32_t j = 0; j < DATA_PRODUCT_CONTROL_INPUTS_N; j++) {
if (i->c[j] == NULL)
continue;
float v = adjust_param(j, *i->c[j]);
if (v != i->params[j]) {
i->params[j] = v;
plugin_set_parameter(&i->p, param_data[j].index, v);
}
}
#endif
// from https://lv2plug.in/book
#if DATA_PRODUCT_MIDI_INPUTS_N > 0
for (size_t j = 0; j < DATA_PRODUCT_MIDI_INPUTS_N; j++) {
if (i->x_midi[j] == NULL)
continue;
LV2_ATOM_SEQUENCE_FOREACH(i->x_midi[j], ev) {
if (ev->body.type == i->uri_midi_MidiEvent) {
const uint8_t * data = (const uint8_t *)(ev + 1);
if ((data[0] & 0xf0) != 0xf0)
plugin_midi_msg_in(&i->p, midi_in_index[j], data);
}
}
}
#endif
#if DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N > 0
const float ** x = i->x;
#else
const float ** x = NULL;
#endif
#if DATA_PRODUCT_AUDIO_OUTPUT_CHANNELS_N > 0
float ** y = i-> y;
#else
float ** y = NULL;
#endif
plugin_process(&i->p, x, y, sample_count);
#if DATA_PRODUCT_CONTROL_OUTPUTS_N > 0
for (uint32_t j = 0; j < DATA_PRODUCT_CONTROL_OUTPUTS_N; j++) {
uint32_t k = param_out_index[j];
if (i->c[k] != NULL)
*i->c[k] = plugin_get_parameter(&i->p, k);
}
#else
(void)plugin_get_parameter;
#endif
#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
}
static void cleanup(LV2_Handle instance) {
plugin_instance * i = (plugin_instance *)instance;
plugin_fini(&i->p);
if (i->mem)
free(i->mem);
free(i->bundle_path);
free(instance);
}
static const LV2_Descriptor descriptor = {
/* .URI = */ DATA_LV2_URI,
/* .instantiate = */ instantiate,
/* .connect_port = */ connect_port,
/* .activate = */ activate,
/* .run = */ run,
/* .deactivate = */ NULL,
/* .cleanup = */ cleanup,
/* .extension_data = */ NULL
};
LV2_SYMBOL_EXPORT const LV2_Descriptor * lv2_descriptor(uint32_t index) {
return index == 0 ? &descriptor : NULL;
}
#ifdef DATA_UI
typedef struct {
plugin_ui * ui;
char * bundle_path;
# if DATA_PRODUCT_CONTROL_INPUTS_N > 0
LV2UI_Write_Function write;
LV2UI_Controller controller;
char has_touch;
LV2UI_Touch touch;
# endif
} ui_instance;
# define CONTROL_INPUT_INDEX_OFFSET ( \
DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N \
+ DATA_PRODUCT_AUDIO_OUTPUT_CHANNELS_N \
+ DATA_PRODUCT_MIDI_INPUTS_N \
+ DATA_PRODUCT_MIDI_OUTPUTS_N )
# define CONTROL_OUTPUT_INDEX_OFFSET (CONTROL_INPUT_INDEX_OFFSET + DATA_PRODUCT_CONTROL_INPUTS_N)
static const char * ui_get_bundle_path_cb(void *handle) {
ui_instance *instance = (ui_instance *)handle;
return instance->bundle_path;
}
# if DATA_PRODUCT_CONTROL_INPUTS_N > 0
static void ui_set_parameter_begin_cb(void *handle, size_t index, float value) {
ui_instance *instance = (ui_instance *)handle;
if (instance->has_touch) {
index = index_to_param[index];
instance->touch.touch(instance->touch.handle, index, true);
value = adjust_param(index - CONTROL_INPUT_INDEX_OFFSET, value);
instance->write(instance->controller, index, sizeof(float), 0, &value);
}
}
static void ui_set_parameter_cb(void *handle, size_t index, float value) {
ui_instance *instance = (ui_instance *)handle;
index = index_to_param[index];
value = adjust_param(index - CONTROL_INPUT_INDEX_OFFSET, value);
instance->write(instance->controller, index, sizeof(float), 0, &value);
}
static void ui_set_parameter_end_cb(void *handle, size_t index, float value) {
ui_instance *instance = (ui_instance *)handle;
if (instance->has_touch) {
index = index_to_param[index];
value = adjust_param(index - CONTROL_INPUT_INDEX_OFFSET, value);
instance->write(instance->controller, index, sizeof(float), 0, &value);
instance->touch.touch(instance->touch.handle, index, false);
}
}
# endif
static LV2UI_Handle ui_instantiate(const LV2UI_Descriptor * descriptor, const char * plugin_uri, const char * bundle_path, LV2UI_Write_Function write_function, LV2UI_Controller controller, LV2UI_Widget * widget, const LV2_Feature * const * features) {
(void)descriptor;
(void)plugin_uri;
ui_instance *instance = malloc(sizeof(ui_instance));
if (instance == NULL)
goto err_instance;
instance->bundle_path = strdup(bundle_path);
if (instance->bundle_path == NULL)
goto err_bundle_path;
char has_parent = 0;
void *parent = NULL;
instance->has_touch = 0;
for (size_t i = 0; features[i] != NULL; i++) {
if (!strcmp(features[i]->URI, LV2_UI__parent)) {
has_parent = 1;
parent = features[i]->data;
}
if (!strcmp(features[i]->URI, LV2_UI__touch)) {
instance->has_touch = 1;
instance->touch = *((LV2UI_Touch *)features[i]->data);
}
}
plugin_ui_callbacks cbs = {
/* .handle = */ (void *)instance,
/* .format = */ "lv2",
/* .get_bindir = */ ui_get_bundle_path_cb,
/* .get_datadir = */ ui_get_bundle_path_cb,
# if DATA_PRODUCT_CONTROL_INPUTS_N > 0
/* .set_parameter_begin = */ ui_set_parameter_begin_cb,
/* .set_parameter = */ ui_set_parameter_cb,
/* .set_parameter_end = */ ui_set_parameter_end_cb
# else
/* .set_parameter_begin = */ NULL,
/* .set_parameter = */ NULL,
/* .set_parameter_end = */ NULL
# endif
};
# if DATA_PRODUCT_CONTROL_INPUTS_N > 0
instance->write = write_function;
instance->controller = controller;
# else
(void)write_function;
(void)controller;
# endif
instance->ui = plugin_ui_create(has_parent, parent, &cbs);
if (instance->ui == NULL)
goto err_create;
*widget = instance->ui->widget;
return instance;
err_create:
free(instance->bundle_path);
err_bundle_path:
free(instance);
err_instance:
*widget = NULL;
return NULL;
}
static void ui_cleanup(LV2UI_Handle handle) {
ui_instance *instance = (ui_instance *)handle;
plugin_ui_free(instance->ui);
free(instance->bundle_path);
free(instance);
}
# if DATA_PRODUCT_CONTROL_INPUTS_N + DATA_PRODUCT_CONTROL_OUTPUTS_N > 0
static void ui_port_event(LV2UI_Handle handle, uint32_t port_index, uint32_t buffer_size, uint32_t format, const void * buffer) {
(void)buffer_size;
(void)format;
ui_instance *instance = (ui_instance *)handle;
# if DATA_PRODUCT_CONTROL_INPUTS_N > 0
if (port_index < CONTROL_OUTPUT_INDEX_OFFSET) {
size_t index = port_index - CONTROL_INPUT_INDEX_OFFSET;
plugin_ui_set_parameter(instance->ui, param_data[index].index, adjust_param(index, *((float *)buffer)));
}
# endif
# if DATA_PRODUCT_CONTROL_OUTPUTS_N > 0
# if DATA_PRODUCT_CONTROL_INPUTS_N > 0
else
# endif
plugin_ui_set_parameter(instance->ui, param_out_index[port_index - CONTROL_OUTPUT_INDEX_OFFSET], *((float *)buffer));
# endif
}
# endif
static int ui_idle(LV2UI_Handle handle) {
ui_instance *instance = (ui_instance *)handle;
plugin_ui_idle(instance->ui);
return 0;
}
static const void * ui_extension_data(const char * uri) {
static const LV2UI_Idle_Interface idle = { ui_idle };
if (!strcmp(uri, LV2_UI__idleInterface))
return &idle;
return NULL;
}
static const LV2UI_Descriptor ui_descriptor = {
/* .URI = */ DATA_LV2_UI_URI,
/* .instantiate = */ ui_instantiate,
/* .cleanup = */ ui_cleanup,
# if DATA_PRODUCT_CONTROL_INPUTS_N + DATA_PRODUCT_CONTROL_OUTPUTS_N > 0
/* .port_event = */ ui_port_event,
# else
/* .port_event = */ NULL,
# endif
/* .extension_data = */ ui_extension_data
};
LV2_SYMBOL_EXPORT const LV2UI_Descriptor * lv2ui_descriptor(uint32_t index) {
return index == 0 ? &ui_descriptor : NULL;
}
#endif

View File

@ -0,0 +1,136 @@
/*
* Tibia
*
* Copyright (C) 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
*/
var path = require("path");
var sep = path.sep;
module.exports = function (data, api, outputCommon, outputData) {
if (outputData) {
data.tibia.lv2 = {
prefixes: [
{ id: "atom", uri: "http://lv2plug.in/ns/ext/atom#" },
{ id: "doap", uri: "http://usefulinc.com/ns/doap#" },
{ id: "foaf", uri: "http://xmlns.com/foaf/0.1/" },
{ id: "log", uri: "http://lv2plug.in/ns/ext/log#" },
{ id: "lv2", uri: "http://lv2plug.in/ns/lv2core#" },
{ id: "midi", uri: "http://lv2plug.in/ns/ext/midi#" },
{ id: "patch", uri: "http://lv2plug.in/ns/ext/patch#" },
{ id: "pprops", uri: "http://lv2plug.in/ns/ext/port-props#" },
{ id: "rdf", uri: "http://www.w3.org/1999/02/22-rdf-syntax-ns#" },
{ id: "rdfs", uri: "http://www.w3.org/2000/01/rdf-schema#" },
{ id: "ui", uri: "http://lv2plug.in/ns/extensions/ui#" },
{ id: "units", uri: "http://lv2plug.in/ns/extensions/units#" },
{ id: "urid", uri: "http://lv2plug.in/ns/ext/urid#" }
],
units: {
"bar": "@units:bar",
"beat": "@units:beat",
"bpm": "@units:bpm",
"cent": "@units:cent",
"cm": "@units:cm",
"coef": "@units:coef",
"db": "@units:db",
"degree": "@units:degree",
"frame": "@units:frame",
"hz": "@units:hz",
"inch": "@units:inch",
"khz": "@units:khz",
"km": "@units:km",
"m": "@units:m",
"mhz": "@units:mhz",
"midiNote": "@units:midiNote",
"mile": "@units:mile",
"min": "@units:min",
"mm": "@units:mm",
"ms": "@units:ms",
"oct": "@units:oct",
"pc": "@units:pc",
"s": "@units:s",
"semitone12TET": "@units:semitone12TET"
},
ports: [],
ttlURI: function (uri) {
if (uri.charAt(0) == "@") {
if (uri.indexOf("#") == -1)
return uri.substring(1);
uri = data.tibia.lv2.expandURI(uri);
}
return "<" + uri + ">";
},
expandURI: function (uri) {
if (uri.charAt(0) != "@")
return uri;
var i = uri.indexOf(":");
var p = uri.substring(1, uri.indexOf(":"));
return data.tibia.lv2.prefixes.find(x => x.id == p).uri + uri.substring(i + 1);
}
};
for (var id in data.lv2.prefixes)
data.tibia.lv2.prefixes.push({ id: id, uri: data.lv2.prefixes[id] });
data.tibia.lv2.prefixes.push({ id: "plugin", uri: data.tibia.lv2.expandURI(data.lv2.uri) + "#" });
var buses = data.product.buses;
var audioPorts = [];
var midiPorts = [];
for (var bi = 0; bi < buses.length; bi++) {
var b = buses[bi];
if (b.type == "audio") {
if (b.channels == "mono") {
var e = { type: "audio", direction: b.direction, name: b.name, sidechain: b.sidechain, cv: b.cv, optional: b.optional, busIndex: bi, id: b.id };
audioPorts.push(e);
} else {
var e = { type: "audio", direction: b.direction, name: b.name + " Left", shortName: b.shortName + " L", sidechain: b.sidechain, cv: b.cv, busIndex: bi, id: b.id + "_l" };
audioPorts.push(e);
var e = { type: "audio", direction: b.direction, name: b.name + " Right", shortName: b.shortName + " R", sidechain: b.sidechain, cv: b.cv, busIndex: bi, id: b.id + "_r" };
audioPorts.push(e);
}
} else {
var e = { type: "midi", direction: b.direction, name: b.name, sidechain: b.sidechain, control: b.control, optional: b.optional, busIndex: bi, id: b.id };
midiPorts.push(e);
}
}
audioPorts.sort((a, b) => a.direction != b.direction ? (a.direction == "input" ? -1 : 1) : 0);
midiPorts.sort((a, b) => a.direction != b.direction ? (a.direction == "input" ? -1 : 1) : 0);
data.tibia.lv2.ports.push.apply(data.tibia.lv2.ports, audioPorts);
data.tibia.lv2.ports.push.apply(data.tibia.lv2.ports, midiPorts);
var hasCtrlIn = false;
var hasCtrlOut = false;
for (var i = 0; i < data.product.parameters.length && !(hasCtrlIn && hasCtrlOut); i++) {
var p = data.product.parameters[i];
if (p.direction == "input")
hasCtrlIn = true;
else
hasCtrlOut = true;
}
if (hasCtrlIn)
data.tibia.lv2.ports.push({ type: "param", direction: "input", name: "Control in", id: "control_in", control: true });
if (hasCtrlOut)
data.tibia.lv2.ports.push({ type: "param", direction: "output", name: "Control out", id: "control_out", control: true });
}
api.generateFileFromTemplateFile(`data${sep}manifest.ttl.in`, `data${sep}manifest.ttl.in`, data);
api.copyFile(`src${sep}lv2.c`, `src${sep}lv2.c`);
api.generateFileFromTemplateFile(`src${sep}data.h`, `src${sep}data.h`, data);
};

View File

@ -15,16 +15,27 @@
doap:maintainer [
a foaf:Organization ;
foaf:name "{{=it.company.name}}" ;
foaf:mbox "{{=it.company.email}}" ;
foaf:mbox <mailto:{{=it.company.email}}> ;
rdfs:seeAlso {{=it.tibia.lv2.ttlURI(it.company.url)}}
] ;
lv2:minorVersion {{=/^([0-9]+)\./.exec(it.lv2.version)[1]}} ;
lv2:microVersion {{=/^[0-9]+\.([0-9]+)/.exec(it.lv2.version)[1]}} ;
{{?it.tibia.lv2.ports.find(p => p.type == "midi")}}
{{?(it.tibia.lv2.ports.find(p => p.type == "midi" && !p.optional))}}
lv2:requiredFeature urid:map ;
lv2:optionalFeature log:log ;
{{??}}
lv2:optionalFeature urid:map ;
{{?}}
lv2:optionalFeature log:log ;
lv2:optionalFeature lv2:hardRTCapable ;
{{?(it.product.state && it.product.state.dspCustom)}}
lv2:optionalFeature state:threadSafeRestore ;
{{?}}
{{?it.product.ui}}
ui:ui plugin:ui ;
{{?}}
{{?(it.product.state && it.product.state.dspCustom)}}
lv2:extensionData state:interface ;
{{?}}
lv2:port [
{{~it.tibia.lv2.ports :p:i}}
{{?p.isBypass}}
@ -33,7 +44,7 @@
lv2:designation lv2:enabled ;
lv2:name "Enabled" ;
lv2:shortName "Enabled" ;
lv2:symbol "{{=p.symbol}}" ;
lv2:symbol "enabled" ;
lv2:default 1 ;
lv2:minimum 0 ;
lv2:maximum 1 ;
@ -53,7 +64,7 @@
lv2:designation lv2:latency ;
lv2:name "Latency" ;
lv2:shortName "Latency" ;
lv2:symbol "{{=p.symbol}}" ;
lv2:symbol "{{=p.id}}" ;
lv2:portProperty lv2:connectionOptional ;
lv2:portProperty lv2:integer ;
lv2:portProperty lv2:reportsLatency ;
@ -64,7 +75,7 @@
{{?"shortName" in p}}
lv2:shortName "{{=p.shortName.substring(0, 16)}}" ;
{{?}}
lv2:symbol "{{=p.symbol}}" ;
lv2:symbol "{{=p.id}}" ;
{{?p.type == "control"}}
lv2:minimum {{=p.minimum.toExponential()}} ;
lv2:maximum {{=p.maximum.toExponential()}} ;
@ -117,3 +128,17 @@
] .
{{?}}
{{~}}
{{?it.product.ui}}
plugin:ui
a ui:@UI_TYPE@ ;
ui:binary <{{=it.product.bundleName}}@DLL_SUFFIX@> ;
{{?!it.product.ui.userResizable}}
lv2:optionalFeature ui:noUserResize ; # doesn't work as lv2:requiredFeature, don't ask me why
{{?!it.product.ui.selfResizable}}
lv2:optionalFeature ui:fixedSize ;
{{?}}
{{?}}
lv2:requiredFeature ui:idleInterface ;
lv2:extensionData ui:idleInterface .
{{?}}

View File

@ -1,7 +1,7 @@
/*
* Tibia
*
* Copyright (C) 2024 Orastron Srl unipersonale
* Copyright (C) 2024, 2025 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
@ -27,6 +27,10 @@
#define DATA_PRODUCT_CONTROL_INPUTS_N {{=it.product.parameters.filter(x => x.direction == "input").length}}
#define DATA_PRODUCT_CONTROL_OUTPUTS_N {{=it.product.parameters.filter(x => x.direction == "output").length}}
{{?it.product.buses.find(x => x.type == "midi" && !x.optional)}}
#define DATA_PRODUCT_MIDI_REQUIRED
{{?}}
#if DATA_PRODUCT_MIDI_INPUTS_N > 0
static uint32_t midi_in_index[DATA_PRODUCT_MIDI_INPUTS_N] = {
{{~it.tibia.lv2.ports.filter(x => x.type == "midi" && x.direction == "input") :p}}{{=p.busIndex}}, {{~}}
@ -64,3 +68,21 @@ static uint32_t param_out_index[DATA_PRODUCT_CONTROL_OUTPUTS_N] = {
{{~it.tibia.lv2.ports.filter(x => x.type == "control" && x.direction == "output") :p}}{{=p.paramIndex}}, {{~}}
};
#endif
{{?it.product.ui}}
#define DATA_UI
#define DATA_LV2_UI_URI "{{=it.tibia.CGetUTF8StringLiteral(it.tibia.lv2.expandURI(it.lv2.uri + '#ui'))}}"
#define DATA_UI_USER_RESIZABLE {{=it.product.ui.userResizable ? 1 : 0}}
{{?}}
{{?(it.product.ui || (it.product.state && it.product.state.dspCustom))}}
#if DATA_PRODUCT_CONTROL_INPUTS_N > 0
static uint32_t index_to_param[DATA_PRODUCT_CONTROL_INPUTS_N + DATA_PRODUCT_CONTROL_OUTPUTS_N] = {
{{~it.tibia.lv2.ports.filter(x => x.type == "control").map((e, i) => ({ i: i, pi: e.paramIndex })).sort((a, b) => a.pi - b.pi) :p}}{{=p.i + it.tibia.lv2.ports.filter(x => x.type != "control").length}}, {{~}}
};
#endif
{{?}}
{{?it.product.state && it.product.state.dspCustom}}
#define DATA_STATE_DSP_CUSTOM
{{?}}

View File

@ -1,7 +1,7 @@
/*
* Tibia
*
* Copyright (C) 2024 Orastron Srl unipersonale
* Copyright (C) 2024, 2025 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
@ -22,26 +22,78 @@
#include <stdint.h>
#include "data.h"
#include "plugin_api.h"
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-function"
#include "plugin.h"
#ifdef DATA_UI
# include "plugin_ui.h"
#endif
#pragma GCC diagnostic pop
#include "lv2/core/lv2.h"
#if DATA_PRODUCT_MIDI_INPUTS_N + DATA_PRODUCT_MIDI_OUTPUTS_N > 0
#include "lv2/core/lv2_util.h"
#include "lv2/atom/util.h"
#include "lv2/atom/atom.h"
#include "lv2/log/log.h"
#include "lv2/log/logger.h"
#include "lv2/midi/midi.h"
#include "lv2/urid/urid.h"
#include <lv2/core/lv2.h>
#include <lv2/core/lv2_util.h>
#include <lv2/log/log.h>
#include <lv2/log/logger.h>
#include <lv2/urid/urid.h>
#if (DATA_PRODUCT_MIDI_INPUTS_N + DATA_PRODUCT_MIDI_OUTPUTS_N > 0) || defined(DATA_STATE_DSP_CUSTOM)
# include <lv2/atom/atom.h>
# if DATA_PRODUCT_MIDI_INPUTS_N + DATA_PRODUCT_MIDI_OUTPUTS_N > 0
# include <lv2/atom/util.h>
# include <lv2/midi/midi.h>
# endif
#endif
#ifdef DATA_UI
# include <lv2/ui/ui.h>
#endif
#ifdef DATA_STATE_DSP_CUSTOM
# include <lv2/state/state.h>
#endif
#include <string.h>
#if defined(__i386__) || defined(__x86_64__)
#include <xmmintrin.h>
#include <pmmintrin.h>
# include <xmmintrin.h>
# include <pmmintrin.h>
#endif
#if (DATA_PRODUCT_CONTROL_INPUTS_N > 0) && defined(DATA_STATE_DSP_CUSTOM)
# include <stdatomic.h>
# if defined(_WIN32) || defined(__CYGWIN__)
# include <processthreadsapi.h>
# define yield SwitchToThread
# else
# include <sched.h>
# define yield sched_yield
# endif
#endif
#define CONTROL_INPUT_INDEX_OFFSET ( \
DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N \
+ DATA_PRODUCT_AUDIO_OUTPUT_CHANNELS_N \
+ DATA_PRODUCT_MIDI_INPUTS_N \
+ DATA_PRODUCT_MIDI_OUTPUTS_N )
#define CONTROL_OUTPUT_INDEX_OFFSET (CONTROL_INPUT_INDEX_OFFSET + DATA_PRODUCT_CONTROL_INPUTS_N)
#if DATA_PRODUCT_CONTROL_INPUTS_N > 0
static inline float clampf(float x, float m, float M) {
return x < m ? m : (x > M ? M : x);
}
static float adjust_param(size_t index, float value) {
if (param_data[index].flags & DATA_PARAM_BYPASS)
value = value > 0.f ? 0.f : 1.f;
else if (param_data[index].flags & DATA_PARAM_TOGGLED)
value = value > 0.f ? 1.f : 0.f;
else if (param_data[index].flags & DATA_PARAM_INTEGER)
value = (int32_t)(value + (value >= 0.f ? 0.5f : -0.5f));
return clampf(value, param_data[index].min, param_data[index].max);
}
#endif
typedef struct {
plugin p;
float sample_rate;
#if DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N > 0
const float * x[DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N];
#endif
@ -59,26 +111,77 @@ typedef struct {
#endif
#if DATA_PRODUCT_CONTROL_INPUTS_N > 0
float params[DATA_PRODUCT_CONTROL_INPUTS_N];
# ifdef DATA_STATE_DSP_CUSTOM
float params_sync[DATA_PRODUCT_CONTROL_INPUTS_N];
atomic_flag sync_lock_flag;
char synced;
char loaded;
# endif
#endif
void * mem;
#if DATA_PRODUCT_MIDI_INPUTS_N + DATA_PRODUCT_MIDI_OUTPUT_N > 0
LV2_URID_Map * map;
char * bundle_path;
LV2_Log_Logger logger;
LV2_URID_Map * map;
#if DATA_PRODUCT_MIDI_INPUTS_N + DATA_PRODUCT_MIDI_OUTPUTS_N > 0
LV2_URID uri_midi_MidiEvent;
#endif
#ifdef DATA_STATE_DSP_CUSTOM
LV2_URID uri_atom_Chunk;
LV2_URID uri_state_data;
plugin_state_callbacks state_cbs;
LV2_State_Store_Function state_store;
LV2_State_Handle state_handle;
#endif
} plugin_instance;
static const char * get_bundle_path_cb(void *handle) {
plugin_instance *instance = (plugin_instance *)handle;
return instance->bundle_path;
}
#ifdef DATA_STATE_DSP_CUSTOM
static void state_lock_cb(void *handle) {
plugin_instance * i = (plugin_instance *)handle;
while (atomic_flag_test_and_set(&i->sync_lock_flag))
yield();
i->synced = 0;
}
static void state_unlock_cb(void *handle) {
plugin_instance * i = (plugin_instance *)handle;
atomic_flag_clear(&i->sync_lock_flag);
}
static int state_write_cb(void *handle, const char *data, size_t length) {
plugin_instance * i = (plugin_instance *)handle;
return i->state_store(i->state_handle, i->uri_state_data, data, length, i->uri_atom_Chunk, LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE);
}
# if DATA_PRODUCT_CONTROL_INPUTS_N > 0
static void state_set_parameter_cb(void *handle, size_t index, float value) {
plugin_instance * i = (plugin_instance *)handle;
size_t idx = index_to_param[index] - CONTROL_INPUT_INDEX_OFFSET;
value = adjust_param(idx, value);
i->params_sync[idx] = value;
i->loaded = 1;
}
# endif
#endif
static LV2_Handle instantiate(const struct LV2_Descriptor * descriptor, double sample_rate, const char * bundle_path, const LV2_Feature * const * features) {
(void)descriptor;
(void)bundle_path;
plugin_instance *instance = malloc(sizeof(plugin_instance));
if (instance == NULL)
return NULL;
goto err_instance;
instance->bundle_path = strdup(bundle_path);
if (instance->bundle_path == NULL)
goto err_bundle_path;
#if DATA_PRODUCT_MIDI_INPUTS_N + DATA_PRODUCT_MIDI_OUTPUT_N > 0
// from https://lv2plug.in/book
const char* missing = lv2_features_query(features,
const char * missing = lv2_features_query(features,
LV2_LOG__log, &instance->logger.log, false,
LV2_URID__map, &instance->map, true,
NULL);
@ -86,24 +189,38 @@ static LV2_Handle instantiate(const struct LV2_Descriptor * descriptor, double s
lv2_log_logger_set_map(&instance->logger, instance->map);
if (missing) {
lv2_log_error(&instance->logger, "Missing feature <%s>\n", missing);
free(instance);
return NULL;
#ifdef DATA_PRODUCT_MIDI_REQUIRED
goto err_urid;
#endif
}
instance->uri_midi_MidiEvent = instance->map->map(instance->map->handle, LV2_MIDI__MidiEvent);
#else
(void)features;
#if DATA_PRODUCT_MIDI_INPUTS_N + DATA_PRODUCT_MIDI_OUTPUTS_N > 0
if (instance->map)
instance->uri_midi_MidiEvent = instance->map->map(instance->map->handle, LV2_MIDI__MidiEvent);
#endif
#ifdef DATA_STATE_DSP_CUSTOM
if (instance->map) {
instance->uri_atom_Chunk = instance->map->map(instance->map->handle, LV2_ATOM__Chunk);
instance->uri_state_data = instance->map->map(instance->map->handle, DATA_LV2_URI "#state_data");
}
#endif
plugin_init(&instance->p);
plugin_callbacks cbs = {
/* .handle = */ (void *)instance,
/* .format = */ "lv2",
/* .get_bindir = */ get_bundle_path_cb,
/* .get_datadir = */ get_bundle_path_cb
};
plugin_init(&instance->p, &cbs);
plugin_set_sample_rate(&instance->p, sample_rate);
instance->sample_rate = (float)sample_rate;
plugin_set_sample_rate(&instance->p, instance->sample_rate);
size_t req = plugin_mem_req(&instance->p);
if (req != 0) {
instance->mem = malloc(req);
if (instance->mem == NULL) {
plugin_fini(&instance->p);
return NULL;
lv2_log_error(&instance->logger, "Not enough memory\n");
goto err_mem;
}
plugin_mem_set(&instance->p, instance->mem);
} else
@ -131,6 +248,17 @@ static LV2_Handle instantiate(const struct LV2_Descriptor * descriptor, double s
#endif
return instance;
err_mem:
plugin_fini(&instance->p);
#ifdef DATA_PRODUCT_MIDI_REQUIRED
err_urid:
#endif
free(instance->bundle_path);
err_bundle_path:
free(instance);
err_instance:
return NULL;
}
static void connect_port(LV2_Handle instance, uint32_t port, void * data_location) {
@ -175,14 +303,19 @@ static void activate(LV2_Handle instance) {
i->params[j] = i->c[j] != NULL ? *i->c[j] : param_data[j].def;
plugin_set_parameter(&i->p, param_data[j].index, i->params[j]);
}
# ifdef DATA_STATE_DSP_CUSTOM
for (uint32_t j = 0; j < DATA_PRODUCT_CONTROL_INPUTS_N; j++)
i->params_sync[j] = i->params[j];
// why is this not correct?
// i->sync_lock_flag = ATOMIC_FLAG_INIT;
atomic_flag_clear(&i->sync_lock_flag);
i->synced = 1;
i->loaded = 0;
# endif
#endif
plugin_reset(&i->p);
}
static inline float clampf(float x, float m, float M) {
return x < m ? m : (x > M ? M : x);
}
static void run(LV2_Handle instance, uint32_t sample_count) {
plugin_instance * i = (plugin_instance *)instance;
@ -199,40 +332,65 @@ static void run(LV2_Handle instance, uint32_t sample_count) {
#endif
#if DATA_PRODUCT_CONTROL_INPUTS_N > 0
# ifdef DATA_STATE_DSP_CUSTOM
_Bool locked = !atomic_flag_test_and_set(&i->sync_lock_flag);
if (locked) {
if (!i->synced) {
if (i->loaded) {
for (uint32_t j = 0; j < DATA_PRODUCT_CONTROL_INPUTS_N; j++) {
i->params[j] = i->params_sync[j];
plugin_set_parameter(&i->p, param_data[j].index, i->params[j]);
}
} else {
for (uint32_t j = 0; j < DATA_PRODUCT_CONTROL_INPUTS_N; j++)
if (i->params[j] != i->params_sync[j]) {
i->params_sync[j] = i->params[j];
plugin_set_parameter(&i->p, param_data[j].index, i->params[j]);
}
}
}
i->synced = 1;
i->loaded = 0;
}
# endif
for (uint32_t j = 0; j < DATA_PRODUCT_CONTROL_INPUTS_N; j++) {
if (i->c[j] == NULL)
continue;
float v;
if (param_data[j].flags & DATA_PARAM_BYPASS)
v = *i->c[j] > 0.f ? 0.f : 1.f;
else if (param_data[j].flags & DATA_PARAM_TOGGLED)
v = *i->c[j] > 0.f ? 1.f : 0.f;
else if (param_data[j].flags & DATA_PARAM_INTEGER)
v = (int32_t)(*i->c[j] + (*i->c[j] >= 0.f ? 0.5f : -0.5f));
else
v = *i->c[j];
v = clampf(v, param_data[j].min, param_data[j].max);
float v = adjust_param(j, *i->c[j]);
if (v != i->params[j]) {
i->params[j] = v;
plugin_set_parameter(&i->p, param_data[j].index, v);
# ifdef DATA_STATE_DSP_CUSTOM
if (locked) {
i->params_sync[j] = i->params[j];
# endif
plugin_set_parameter(&i->p, param_data[j].index, v);
# ifdef DATA_STATE_DSP_CUSTOM
}
# endif
}
}
# ifdef DATA_STATE_DSP_CUSTOM
if (locked)
atomic_flag_clear(&i->sync_lock_flag);
# endif
#endif
// from https://lv2plug.in/book
#if DATA_PRODUCT_MIDI_INPUTS_N > 0
for (size_t j = 0; j < DATA_PRODUCT_MIDI_INPUTS_N; j++) {
if (i->x_midi[j] == NULL)
continue;
LV2_ATOM_SEQUENCE_FOREACH(i->x_midi[j], ev) {
if (ev->body.type == i->uri_midi_MidiEvent) {
const uint8_t * data = (const uint8_t *)(ev + 1);
if ((data[0] & 0xf0) != 0xf0)
plugin_midi_msg_in(&i->p, midi_in_index[j], data);
// from https://lv2plug.in/book
if (i->map)
for (size_t j = 0; j < DATA_PRODUCT_MIDI_INPUTS_N; j++) {
if (i->x_midi[j] == NULL)
continue;
LV2_ATOM_SEQUENCE_FOREACH(i->x_midi[j], ev) {
if (ev->body.type == i->uri_midi_MidiEvent) {
const uint8_t * data = (const uint8_t *)(ev + 1);
if ((data[0] & 0xf0) != 0xf0)
plugin_midi_msg_in(&i->p, midi_in_index[j], data);
}
}
}
}
#endif
#if DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N > 0
@ -263,7 +421,6 @@ static void run(LV2_Handle instance, uint32_t sample_count) {
_MM_SET_FLUSH_ZERO_MODE(flush_zero_mode);
_MM_SET_DENORMALS_ZERO_MODE(denormals_zero_mode);
#endif
}
static void cleanup(LV2_Handle instance) {
@ -271,9 +428,70 @@ static void cleanup(LV2_Handle instance) {
plugin_fini(&i->p);
if (i->mem)
free(i->mem);
free(i->bundle_path);
free(instance);
}
#ifdef DATA_STATE_DSP_CUSTOM
static LV2_State_Status state_save(LV2_Handle instance, LV2_State_Store_Function store, LV2_State_Handle handle, uint32_t flags, const LV2_Feature * const * features) {
(void)flags;
(void)features;
plugin_instance * i = (plugin_instance *)instance;
if (!i->map) {
lv2_log_error(&i->logger, "Host is trying to store state but didn't provide URID map\n");
return LV2_STATE_ERR_UNKNOWN; // evidently buggy host, we don't have an errcode for it, LV2_STATE_ERR_NO_FEATURE has a different meaning
}
i->state_store = store;
i->state_handle = handle;
plugin_state_callbacks cbs = {
/* .handle = */ (void *)i,
/* .lock = */ state_lock_cb,
/* .unlock = */ state_unlock_cb,
/* .write = */ state_write_cb,
# if DATA_PRODUCT_CONTROL_INPUTS_N > 0
/* .set_parameter = */ NULL
# endif
};
return plugin_state_save(&i->p, &cbs, i->sample_rate) == 0 ? LV2_STATE_SUCCESS : LV2_STATE_ERR_UNKNOWN;
}
static LV2_State_Status state_restore(LV2_Handle instance, LV2_State_Retrieve_Function retrieve, LV2_State_Handle handle, uint32_t flags, const LV2_Feature * const * features) {
(void)flags;
(void)features;
plugin_instance * i = (plugin_instance *)instance;
if (!i->map) {
lv2_log_error(&i->logger, "Host is trying to restore state but didn't provide URID map\n");
return LV2_STATE_ERR_UNKNOWN; // evidently buggy host, we don't have an errcode for it, LV2_STATE_ERR_NO_FEATURE has a different meaning
}
size_t length;
uint32_t type, xflags; // jalv 1.6.6 crashes using NULL as per spec, so we have these two
const char * data = retrieve(handle, i->uri_state_data, &length, &type, &xflags);
if (data == NULL) {
lv2_log_error(&i->logger, "Cannot restore state since property <%s> could not be retrieved\n", DATA_LV2_URI "#state_data");
return LV2_STATE_ERR_NO_PROPERTY;
}
plugin_state_callbacks cbs = {
/* .handle = */ (void *)i,
/* .lock = */ state_lock_cb,
/* .unlock = */ state_unlock_cb,
/* .write = */ NULL,
# if DATA_PRODUCT_CONTROL_INPUTS_N > 0
/* .set_parameter = */ state_set_parameter_cb
# endif
};
return plugin_state_load(&cbs, i->sample_rate, data, length) == 0 ? LV2_STATE_SUCCESS : LV2_STATE_ERR_UNKNOWN;
}
static const void * extension_data(const char * uri) {
static const LV2_State_Interface state = { state_save, state_restore };
if (!strcmp(uri, LV2_STATE__interface))
return &state;
return NULL;
}
#endif
static const LV2_Descriptor descriptor = {
/* .URI = */ DATA_LV2_URI,
/* .instantiate = */ instantiate,
@ -282,9 +500,181 @@ static const LV2_Descriptor descriptor = {
/* .run = */ run,
/* .deactivate = */ NULL,
/* .cleanup = */ cleanup,
#ifdef DATA_STATE_DSP_CUSTOM
/* .extension_data = */ extension_data
#else
/* .extension_data = */ NULL
#endif
};
LV2_SYMBOL_EXPORT const LV2_Descriptor * lv2_descriptor(uint32_t index) {
return index == 0 ? &descriptor : NULL;
}
#ifdef DATA_UI
typedef struct {
plugin_ui * ui;
char * bundle_path;
# if DATA_PRODUCT_CONTROL_INPUTS_N > 0
LV2UI_Write_Function write;
LV2UI_Controller controller;
char has_touch;
LV2UI_Touch touch;
# endif
} ui_instance;
static const char * ui_get_bundle_path_cb(void *handle) {
ui_instance *instance = (ui_instance *)handle;
return instance->bundle_path;
}
# if DATA_PRODUCT_CONTROL_INPUTS_N > 0
static void ui_set_parameter_begin_cb(void *handle, size_t index, float value) {
ui_instance *instance = (ui_instance *)handle;
if (instance->has_touch) {
index = index_to_param[index];
instance->touch.touch(instance->touch.handle, index, true);
value = adjust_param(index - CONTROL_INPUT_INDEX_OFFSET, value);
instance->write(instance->controller, index, sizeof(float), 0, &value);
}
}
static void ui_set_parameter_cb(void *handle, size_t index, float value) {
ui_instance *instance = (ui_instance *)handle;
index = index_to_param[index];
value = adjust_param(index - CONTROL_INPUT_INDEX_OFFSET, value);
instance->write(instance->controller, index, sizeof(float), 0, &value);
}
static void ui_set_parameter_end_cb(void *handle, size_t index, float value) {
ui_instance *instance = (ui_instance *)handle;
if (instance->has_touch) {
index = index_to_param[index];
value = adjust_param(index - CONTROL_INPUT_INDEX_OFFSET, value);
instance->write(instance->controller, index, sizeof(float), 0, &value);
instance->touch.touch(instance->touch.handle, index, false);
}
}
# endif
static LV2UI_Handle ui_instantiate(const LV2UI_Descriptor * descriptor, const char * plugin_uri, const char * bundle_path, LV2UI_Write_Function write_function, LV2UI_Controller controller, LV2UI_Widget * widget, const LV2_Feature * const * features) {
(void)descriptor;
(void)plugin_uri;
ui_instance *instance = malloc(sizeof(ui_instance));
if (instance == NULL)
goto err_instance;
instance->bundle_path = strdup(bundle_path);
if (instance->bundle_path == NULL)
goto err_bundle_path;
char has_parent = 0;
void *parent = NULL;
# if DATA_PRODUCT_CONTROL_INPUTS_N > 0
instance->has_touch = 0;
# endif
for (size_t i = 0; features[i] != NULL; i++) {
if (!strcmp(features[i]->URI, LV2_UI__parent)) {
has_parent = 1;
parent = features[i]->data;
}
# if DATA_PRODUCT_CONTROL_INPUTS_N > 0
else if (!strcmp(features[i]->URI, LV2_UI__touch)) {
instance->has_touch = 1;
instance->touch = *((LV2UI_Touch *)features[i]->data);
}
# endif
}
plugin_ui_callbacks cbs = {
/* .handle = */ (void *)instance,
/* .format = */ "lv2",
/* .get_bindir = */ ui_get_bundle_path_cb,
/* .get_datadir = */ ui_get_bundle_path_cb,
# if DATA_PRODUCT_CONTROL_INPUTS_N > 0
/* .set_parameter_begin = */ ui_set_parameter_begin_cb,
/* .set_parameter = */ ui_set_parameter_cb,
/* .set_parameter_end = */ ui_set_parameter_end_cb,
# endif
};
# if DATA_PRODUCT_CONTROL_INPUTS_N > 0
instance->write = write_function;
instance->controller = controller;
# else
(void)write_function;
(void)controller;
# endif
instance->ui = plugin_ui_create(has_parent, parent, &cbs);
if (instance->ui == NULL)
goto err_create;
*widget = instance->ui->widget;
return instance;
err_create:
free(instance->bundle_path);
err_bundle_path:
free(instance);
err_instance:
*widget = NULL;
return NULL;
}
static void ui_cleanup(LV2UI_Handle handle) {
ui_instance *instance = (ui_instance *)handle;
plugin_ui_free(instance->ui);
free(instance->bundle_path);
free(instance);
}
# if DATA_PRODUCT_CONTROL_INPUTS_N + DATA_PRODUCT_CONTROL_OUTPUTS_N > 0
static void ui_port_event(LV2UI_Handle handle, uint32_t port_index, uint32_t buffer_size, uint32_t format, const void * buffer) {
(void)buffer_size;
(void)format;
ui_instance *instance = (ui_instance *)handle;
# if DATA_PRODUCT_CONTROL_INPUTS_N > 0
if (port_index < CONTROL_OUTPUT_INDEX_OFFSET) {
size_t index = port_index - CONTROL_INPUT_INDEX_OFFSET;
plugin_ui_set_parameter(instance->ui, param_data[index].index, adjust_param(index, *((float *)buffer)));
}
# endif
# if DATA_PRODUCT_CONTROL_OUTPUTS_N > 0
# if DATA_PRODUCT_CONTROL_INPUTS_N > 0
else
# endif
plugin_ui_set_parameter(instance->ui, param_out_index[port_index - CONTROL_OUTPUT_INDEX_OFFSET], *((float *)buffer));
# endif
}
# endif
static int ui_idle(LV2UI_Handle handle) {
ui_instance *instance = (ui_instance *)handle;
plugin_ui_idle(instance->ui);
return 0;
}
static const void * ui_extension_data(const char * uri) {
static const LV2UI_Idle_Interface idle = { ui_idle };
if (!strcmp(uri, LV2_UI__idleInterface))
return &idle;
return NULL;
}
static const LV2UI_Descriptor ui_descriptor = {
/* .URI = */ DATA_LV2_UI_URI,
/* .instantiate = */ ui_instantiate,
/* .cleanup = */ ui_cleanup,
# if DATA_PRODUCT_CONTROL_INPUTS_N + DATA_PRODUCT_CONTROL_OUTPUTS_N > 0
/* .port_event = */ ui_port_event,
# else
/* .port_event = */ NULL,
# endif
/* .extension_data = */ ui_extension_data
};
LV2_SYMBOL_EXPORT const LV2UI_Descriptor * lv2ui_descriptor(uint32_t index) {
return index == 0 ? &ui_descriptor : NULL;
}
#endif

View File

@ -1,7 +1,7 @@
/*
* Tibia
*
* Copyright (C) 2024 Orastron Srl unipersonale
* Copyright (C) 2024, 2025 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
@ -34,6 +34,8 @@ module.exports = function (data, api, outputCommon, outputData) {
{ id: "pprops", uri: "http://lv2plug.in/ns/ext/port-props#" },
{ id: "rdf", uri: "http://www.w3.org/1999/02/22-rdf-syntax-ns#" },
{ id: "rdfs", uri: "http://www.w3.org/2000/01/rdf-schema#" },
{ id: "state", uri: "http://lv2plug.in/ns/extensions/state#" },
{ id: "ui", uri: "http://lv2plug.in/ns/extensions/ui#" },
{ id: "units", uri: "http://lv2plug.in/ns/extensions/units#" },
{ id: "urid", uri: "http://lv2plug.in/ns/ext/urid#" }
],
@ -66,7 +68,12 @@ module.exports = function (data, api, outputCommon, outputData) {
ports: [],
ttlURI: function (uri) {
return uri.charAt(0) == "@" ? uri.substring(1) : "<" + uri + ">";
if (uri.charAt(0) == "@") {
if (uri.indexOf("#") == -1)
return uri.substring(1);
uri = data.tibia.lv2.expandURI(uri);
}
return "<" + uri + ">";
},
expandURI: function (uri) {
@ -81,6 +88,8 @@ module.exports = function (data, api, outputCommon, outputData) {
for (var id in data.lv2.prefixes)
data.tibia.lv2.prefixes.push({ id: id, uri: data.lv2.prefixes[id] });
data.tibia.lv2.prefixes.push({ id: "plugin", uri: data.tibia.lv2.expandURI(data.lv2.uri) + "#" });
var buses = data.product.buses;
var audioPorts = [];
var midiPorts = [];
@ -88,20 +97,16 @@ module.exports = function (data, api, outputCommon, outputData) {
var b = buses[bi];
if (b.type == "audio") {
if (b.channels == "mono") {
var e = { type: "audio", direction: b.direction, name: b.name, sidechain: b.sidechain, cv: b.cv, optional: b.optional, busIndex: bi };
e.symbol = data.lv2.busSymbols[bi];
var e = { type: "audio", direction: b.direction, name: b.name, sidechain: b.sidechain, cv: b.cv, optional: b.optional, busIndex: bi, id: b.id };
audioPorts.push(e);
} else {
var e = { type: "audio", direction: b.direction, name: b.name + " Left", shortName: b.shortName + " L", sidechain: b.sidechain, cv: b.cv, busIndex: bi };
e.symbol = data.lv2.busSymbols[bi] + "_L";
var e = { type: "audio", direction: b.direction, name: b.name + " Left", shortName: b.shortName + " L", sidechain: b.sidechain, cv: b.cv, busIndex: bi, id: b.id + "_l" };
audioPorts.push(e);
var e = { type: "audio", direction: b.direction, name: b.name + " Right", shortName: b.shortName + " R", sidechain: b.sidechain, cv: b.cv, busIndex: bi };
e.symbol = data.lv2.busSymbols[bi] + "_R";
var e = { type: "audio", direction: b.direction, name: b.name + " Right", shortName: b.shortName + " R", sidechain: b.sidechain, cv: b.cv, busIndex: bi, id: b.id + "_r" };
audioPorts.push(e);
}
} else {
var e = { type: "midi", direction: b.direction, name: b.name, sidechain: b.sidechain, control: b.control, optional: b.optional, busIndex: bi };
e.symbol = data.lv2.busSymbols[bi];
var e = { type: "midi", direction: b.direction, name: b.name, sidechain: b.sidechain, control: b.control, optional: b.optional, busIndex: bi, id: b.id };
midiPorts.push(e);
}
}
@ -115,7 +120,6 @@ module.exports = function (data, api, outputCommon, outputData) {
var p = data.product.parameters[i];
var e = Object.create(p);
e.type = "control";
e.symbol = data.lv2.parameterSymbols[i];
e.paramIndex = i;
ports.push(e);
}

View File

@ -1,7 +1,7 @@
#
# Tibia
#
# Copyright (C) 2023, 2024 Orastron Srl unipersonale
# Copyright (C) 2023-2025 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
@ -18,119 +18,177 @@
# File author: Stefano D'Angelo
#
TEMPLATE := vst3
include vars.mk
COMMON_DIR ?= .
DATA_DIR ?= .
PLUGIN_DIR ?= src
API_DIR ?= $(PLUGIN_DIR)
MKINC_DIR ?= $(COMMON_DIR)
BUNDLE_DIR := $(BUNDLE_NAME).vst3
ifeq ($(OS), Windows_NT)
DLL_SUFFIX = .vst3
PLATFORM = x86_64-win
VST3DIR = $(shell echo '${COMMONPROGRAMFILES}' | sed 's:\\:/:g')/VST3
VST3DIR_USER = $(shell echo '${LOCALAPPDATA}' | sed 's:\\:/:g')/Programs/Common/VST3
CC = gcc
CXX = g++
DLL_SUFFIX := .vst3
VST3DIR := $(subst \,/,$(COMMONPROGRAMFILES))/VST3
VST3DIR_USER := $(subst \,/,$(LOCALAPPDATA))/Programs/Common/VST3
CC := gcc
CXX := g++
MACHINE := $(shell $(CC) -dumpmachine | sed 's:-.*::g')
ifeq ($(MACHINE), x86_64)
VST3_PLATFORM := x86_64-win
endif
ifeq ($(MACHINE), aarch64)
VST3_PLATFORM := arm64-win
endif
else
UNAME_S = $(shell uname -s)
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S), Darwin)
DLL_SUFFIX =
PLATFORM = MacOS
VST3DIR = /Library/Audio/Plug-Ins/VST3
VST3DIR_USER = ${HOME}/Library/Audio/Plug-Ins/VST3
CC = clang
CXX = clang++
DLL_SUFFIX :=
VST3DIR := /Library/Audio/Plug-Ins/VST3
VST3DIR_USER := $(HOME)/Library/Audio/Plug-Ins/VST3
CC := clang
CXX := clang++
VST3_PLATFORM := MacOS
else
DLL_SUFFIX = .so
PLATFORM = $(shell uname -m)-linux
VST3DIR = /usr/local/lib/vst3
VST3DIR_USER = ${HOME}/.vst3
CC = gcc
CXX = g++
DLL_SUFFIX := .so
VST3DIR := /usr/local/lib/vst3
VST3DIR_USER := $(HOME)/.vst3
CC := gcc
CXX := g++
VST3_PLATFORM := $(shell uname -m)-linux
endif
endif
DLL_DIR := Contents/$(VST3_PLATFORM)
BUILD_BIN_DIR := build/$(BUNDLE_DIR)/$(DLL_DIR)
BUILD_DATA_DIR := build/$(BUNDLE_DIR)/Contents/Resources
-include $(MKINC_DIR)/vars-pre.mk
CFLAGS := -O3 -Wall -Wpedantic -Wextra
CFLAGS_ALL := -I$(DATA_DIR)/src -I$(PLUGIN_DIR) -I$(API_DIR) -fPIC $(CFLAGS_EXTRA) $(CFLAGS)
LDFLAGS :=
LDFLAGS_ALL := -shared -lm $(LDFLAGS_EXTRA) $(LDFLAGS)
CXXFLAGS := $(CFLAGS)
CXXFLAGS_ALL := -I$(DATA_DIR)/src -I$(PLUGIN_DIR) -I$(API_DIR) -fPIC $(CXXFLAGS_EXTRA) $(CXXFLAGS)
ifeq ($(UNAME_S), Darwin)
CFLAGS_ALL := $(CFLAGS_ALL) -arch arm64 -arch x86_64
LDFLAGS_ALL := $(LDFLAGS_ALL) -arch arm64 -arch x86_64
CXXFLAGS_ALL := $(CXXFLAGS_ALL) -arch arm64 -arch x86_64
ifeq ($(HAS_UI), yes)
LDFLAGS_ALL := $(LDFLAGS_ALL) -Wl,-framework,Foundation -Wl,-framework,Cocoa -Wl,-framework,Corevideo
endif
endif
COMMON_DIR := $(or $(COMMON_DIR),.)
DATA_DIR := $(or $(DATA_DIR),.)
PLUGIN_DIR := $(or $(PLUGIN_DIR),src)
CFLAGS = -O3 -Wall -Wpedantic -Wextra
CFLAGS_ALL = -I${DATA_DIR}/src -I${PLUGIN_DIR} -fPIC ${CFLAGS} ${CFLAGS_EXTRA}
LDFLAGS =
LDFLAGS_ALL = -shared -lm ${LDFLAGS} ${LDFLAGS_EXTRA}
CXXFLAGS = ${CFLAGS}
CXXFLAGS_ALL = -I${DATA_DIR}/src -I${PLUGIN_DIR} -fPIC ${CXXFLAGS} ${CXXFLAGS_EXTRA}
ifeq ($(UNAME_S), Darwin)
CFLAGS_ALL := ${CFLAGS_ALL} -arch arm64 -arch x86_64
LDFLAGS_ALL := ${LDFLAGS_ALL} -arch arm64 -arch x86_64
CXXFLAGS_ALL := ${CXXFLAGS_ALL} -arch arm64 -arch x86_64
ifeq ($(UNAME_S), Linux)
CFLAGS_ALL := $(CFLAGS_ALL) -D_GNU_SOURCE
ifeq ($(HAS_UI), yes)
CFLAGS_ALL := $(CFLAGS_ALL) $(shell pkg-config --cflags x11)
LDFLAGS_ALL := $(LDFLAGS_ALL) $(shell pkg-config --libs x11)
CXXFLAGS_ALL := $(CXXFLAGS_ALL) $(shell pkg-config --cflags x11)
endif
endif
BUNDLE_DIR = ${BUNDLE_NAME}.vst3
DLL_FILE := $(DLL_DIR)/$(BUNDLE_NAME)$(DLL_SUFFIX)
DLL_DIR = Contents/${PLATFORM}
DLL_FILE = ${DLL_DIR}/${BUNDLE_NAME}${DLL_SUFFIX}
C_SRCS := $(COMMON_DIR)/src/vst3.c $(C_SRCS_EXTRA)
C_OBJS := $(addprefix build/obj/, $(notdir $(C_SRCS:.c=.o)))
C_SRCS = ${COMMON_DIR}/src/vst3.c ${C_SRCS_EXTRA}
C_OBJS = $(addprefix build/obj/, $(notdir $(C_SRCS:.c=.o)))
M_SRCS := $(M_SRCS_EXTRA)
M_OBJS := $(addprefix build/obj/, $(notdir $(M_SRCS:.m=.o)))
CXX_SRCS = ${CXX_SRCS_EXTRA}
CXX_OBJS = $(addprefix build/obj/, $(notdir $(CXX_SRCS:.cpp=.o)))
CXX_SRCS := $(CXX_SRCS_EXTRA)
CXX_OBJS := $(addprefix build/obj/, $(notdir $(CXX_SRCS:.cpp=.o)))
DIRS := build build/obj build/$(BUNDLE_DIR) build/$(BUNDLE_DIR)/Contents build/$(BUNDLE_DIR)/Contents/Resources build/$(BUNDLE_DIR)/$(DLL_DIR)
ALL := build/$(BUNDLE_DIR)/$(DLL_FILE)
ifeq ($(UNAME_S), Darwin)
all: build/${BUNDLE_DIR}/${DLL_FILE} build/${BUNDLE_DIR}/Contents/Info.plist
build/${BUNDLE_DIR}/Contents/Info.plist: ${DATA_DIR}/data/Info.plist | build/${BUNDLE_DIR}/Contents
cp $^ $@
else
all: build/${BUNDLE_DIR}/${DLL_FILE}
ALL := $(ALL) build/$(BUNDLE_DIR)/Contents/Info.plist build/$(BUNDLE_DIR)/Contents/PkgInfo
endif
STRIP_ALL := build/$(BUNDLE_DIR)/$(DLL_FILE)
STRIP_PHONY :=
STRIP_PREREQS := $(STRIP_ALL) $(STRIP_PHONY)
-include $(MKINC_DIR)/vars-extra.mk
all: $(ALL)
ifeq ($(CXX_OBJS),)
build/${BUNDLE_DIR}/${DLL_FILE}: ${C_OBJS} | build/${BUNDLE_DIR}/${DLL_DIR}
${CC} $^ -o $@ ${CFLAGS_ALL} ${LDFLAGS_ALL}
build/$(BUNDLE_DIR)/$(DLL_FILE): $(C_OBJS) $(M_OBJS) | build/$(BUNDLE_DIR)/$(DLL_DIR)
$(CC) $^ -o $@ $(CFLAGS_ALL) $(LDFLAGS_ALL)
else
build/${BUNDLE_DIR}/${DLL_FILE}: ${C_OBJS} ${CXX_OBJS} | build/${BUNDLE_DIR}/${DLL_DIR}
${CXX} $^ -o $@ ${CFLAGS_ALL} ${CXXFLAGS_ALL} ${LDFLAGS_ALL}
build/$(BUNDLE_DIR)/$(DLL_FILE): $(C_OBJS) $(M_OBJS) $(CXX_OBJS) | build/$(BUNDLE_DIR)/$(DLL_DIR)
$(CXX) $^ -o $@ $(CFLAGS_ALL) $(CXXFLAGS_ALL) $(LDFLAGS_ALL)
endif
build/${BUNDLE_DIR}/${DLL_DIR} build/obj:
ifeq ($(UNAME_S), Darwin)
build/$(BUNDLE_DIR)/Contents/%: $(DATA_DIR)/data/% | build/$(BUNDLE_DIR)/Contents
cp $^ $@
build/$(BUNDLE_DIR)/Contents/%: $(COMMON_DIR)/data/% | build/$(BUNDLE_DIR)/Contents
cp $^ $@
endif
$(DIRS):
mkdir -p $@
clean:
rm -fr build
ifeq ($(UNAME_S), Darwin)
strip: $(STRIP_PREREQS)
strip build/$(BUNDLE_DIR)/$(DLL_FILE)
install: all
mkdir -p -m 0755 "${VST3DIR}/${BUNDLE_DIR}/${DLL_DIR}"
install -m 0755 build/${BUNDLE_DIR}/${DLL_FILE} "${VST3DIR}/${BUNDLE_DIR}/${DLL_DIR}"
install -m 0644 build/${BUNDLE_DIR}/Contents/Info.plist "${VST3DIR}/${BUNDLE_DIR}/Contents/Info.plist"
@for d in `find build/$(BUNDLE_DIR) -type d`; do \
d=`echo $$d | sed 's:^build/::'` ; \
echo mkdir -m 0755 -p "$(VST3DIR)/$$d"; \
mkdir -m 0755 -p "$(VST3DIR)/$$d"; \
done
@for f in `find build/$(BUNDLE_DIR) -type f`; do \
m=`[ -x $$f ] && echo 0755 || echo 0644`; \
d=`echo $$f | sed 's:^build/::'` ; \
d=`dirname $$d`; \
echo install -m $$m $$f "$(VST3DIR)/$$d"; \
install -m $$m $$f "$(VST3DIR)/$$d"; \
done
install-user: all
mkdir -p -m 0755 "${VST3DIR_USER}/${BUNDLE_DIR}/${DLL_DIR}"
install -m 0755 build/${BUNDLE_DIR}/${DLL_FILE} "${VST3DIR_USER}/${BUNDLE_DIR}/${DLL_DIR}"
install -m 0644 build/${BUNDLE_DIR}/Contents/Info.plist "${VST3DIR_USER}/${BUNDLE_DIR}/Contents/Info.plist"
@for d in `find build/$(BUNDLE_DIR) -type d`; do \
d=`echo $$d | sed 's:^build/::'` ; \
echo mkdir -m 0755 -p "$(VST3DIR_USER)/$$d"; \
mkdir -m 0755 -p "$(VST3DIR_USER)/$$d"; \
done
@for f in `find build/$(BUNDLE_DIR) -type f`; do \
m=`[ -x $$f ] && echo 0755 || echo 0644`; \
d=`echo $$f | sed 's:^build/::'` ; \
d=`dirname $$d`; \
echo install -m $$m $$f "$(VST3DIR_USER)/$$d"; \
install -m $$m $$f "$(VST3DIR_USER)/$$d"; \
done
else
-include $(MKINC_DIR)/rules-extra.mk
install: all
mkdir -p -m 0755 "${VST3DIR}/${BUNDLE_DIR}/${DLL_DIR}"
install -m 0755 build/${BUNDLE_DIR}/${DLL_FILE} "${VST3DIR}/${BUNDLE_DIR}/${DLL_DIR}"
install-user: all
mkdir -p -m 0755 "${VST3DIR_USER}/${BUNDLE_DIR}/${DLL_DIR}"
install -m 0755 build/${BUNDLE_DIR}/${DLL_FILE} "${VST3DIR_USER}/${BUNDLE_DIR}/${DLL_DIR}"
endif
.PHONY: all clean install install-user
.PHONY: all clean strip $(STRIP_PHONY) install install-user
.SECONDEXPANSION:
PERCENT := %
$(C_OBJS): build/obj/%.o: $$(filter $$(PERCENT)/$$(basename $$(notdir $$@)).c,$$(C_SRCS)) | build/obj
${CC} $^ -o $@ -c ${CFLAGS_ALL}
$(CC) $^ -o $@ -c $(CFLAGS_ALL)
$(M_OBJS): build/obj/%.o: $$(filter $$(PERCENT)/$$(basename $$(notdir $$@)).m,$$(M_SRCS)) | build/obj
$(CC) $^ -o $@ -c $(CFLAGS_ALL)
$(CXX_OBJS): build/obj/%.o: $$(filter $$(PERCENT)/$$(basename $$(notdir $$@)).cpp,$$(CXX_SRCS)) | build/obj
${CXX} $^ -o $@ -c ${CXXFLAGS_ALL}
$(CXX) $^ -o $@ -c $(CXXFLAGS_ALL)
-include $(MKINC_DIR)/rules-secondexp-extra.mk

View File

@ -1,7 +1,7 @@
#
# Tibia
#
# Copyright (C) 2023, 2024 Orastron Srl unipersonale
# Copyright (C) 2023-2025 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
@ -20,13 +20,28 @@
BUNDLE_NAME := {{=it.product.bundleName}}
CFLAGS_EXTRA := {{=it.make && it.make.cflags ? it.make.cflags : ""}} {{=it.vst3_make && it.vst3_make.cflags ? it.vst3_make.cflags : ""}}
LDFLAGS_EXTRA := {{=it.make && it.make.ldflags ? it.make.ldflags : ""}} {{=it.vst3_make && it.vst3_make.ldflags ? it.vst3_make.ldflags : ""}}
CXXFLAGS_EXTRA := {{=it.make && it.make.cxxflags ? it.make.cxxflags : ""}} {{=it.vst3_make && it.vst3_make.cxxflags ? it.vst3_make.cxxflags : ""}}
{{?(it.vst3_make?.commonDir || it.make?.commonDir)}}
COMMON_DIR := {{=it.vst3_make?.commonDir ?? (it.make?.commonDir ?? "")}}
{{?}}
{{?(it.vst3_make?.dataDir || it.make?.dataDir)}}
DATA_DIR := {{=it.vst3_make?.dataDir ?? (it.make?.dataDir ?? "")}}
{{?}}
{{?(it.vst3_make?.pluginDir || it.make?.pluginDir)}}
PLUGIN_DIR := {{=it.vst3_make?.pluginDir ?? (it.make?.pluginDir ?? "")}}
{{?}}
{{?(it.vst3_make?.apiDir || it.make?.apiDir)}}
API_DIR := {{=it.vst3_make?.apiDir ?? (it.make?.apiDir ?? "")}}
{{?}}
{{?(it.vst3_make?.mkincDir || it.make?.mkincDir)}}
MKINC_DIR := {{=it.vst3_make?.mkincDir ?? (it.make?.mkincDir ?? "")}}
{{?}}
C_SRCS_EXTRA := {{=it.make && it.make.cSrcs ? it.make.cSrcs : ""}} {{=it.vst3_make && it.vst3_make.cSrcs ? it.vst3_make.cSrcs : ""}}
CXX_SRCS_EXTRA := {{=it.make && it.make.cxxSrcs ? it.make.cxxSrcs : ""}} {{=it.vst3_make && it.vst3_make.cxxSrcs ? it.vst3_make.cxxSrcs : ""}}
HAS_UI := {{=it.product.ui ? "yes" : "no"}}
HAS_PARAMETER_IN := {{=it.product.parameters.filter(x => x.direction == "input").length > 0 ? "yes" : "no"}}
COMMON_DIR := {{=it.vst3_make && it.vst3_make.commonDir ? it.vst3_make.commonDir : (it.make && it.make.commonDir ? it.make.commonDir : "")}}
DATA_DIR := {{=it.vst3_make && it.vst3_make.dataDir ? it.vst3_make.dataDir : (it.make && it.make.dataDir ? it.make.dataDir : "")}}
PLUGIN_DIR := {{=it.vst3_make && it.vst3_make.pluginDir ? it.vst3_make.pluginDir : (it.make && it.make.pluginDir ? it.make.pluginDir : "")}}
{{?it.make?.extra}}
{{=it.make.extra}}
{{?}}
{{?it.vst3_make?.extra}}
{{=it.vst3_make.extra}}
{{?}}

View File

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

View File

@ -1,7 +1,7 @@
/*
* Tibia
*
* Copyright (C) 2023, 2024 Orastron Srl unipersonale
* Copyright (C) 2023-2025 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
@ -28,8 +28,8 @@
static Steinberg_char16 dataProductNameW[64] = { {{~Array.from(it.product.name).slice(0, 63) :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 };
static Steinberg_char16 dataProductVersionW[64] = { {{~Array.from(it.product.version + "." + it.product.buildVersion).slice(0, 63) :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 };
#define DATA_VST3_SDK_VERSION "VST 3.7.9"
static Steinberg_char16 dataVST3SDKVersionW[64] = { {{~Array.from("VST 3.7.9") :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 };
#define DATA_VST3_SDK_VERSION "VST 3.7.12"
static Steinberg_char16 dataVST3SDKVersionW[64] = { {{~Array.from("VST 3.7.12") :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 };
static Steinberg_char16 dataVST3ControllerNameW[64] = { {{~Array.from(it.product.name + " Controller").slice(0, 63) :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 };
@ -123,13 +123,15 @@ static uint32_t midiInIndex[DATA_PRODUCT_BUSES_MIDI_INPUT_N] = {
#endif
#define DATA_PRODUCT_PARAMETERS_N {{=it.product.parameters.filter(x => !x.isLatency).length}}
#define DATA_PRODUCT_PARAMETERS_IN_N {{=it.product.parameters.filter(x => x.direction == "input").length}}
#define DATA_PRODUCT_PARAMETERS_OUT_N {{=it.product.parameters.filter(x => !x.isLatency && x.direction == "output").length}}
#if DATA_PRODUCT_PARAMETERS_N + DATA_PRODUCT_BUSES_MIDI_INPUT_N > 0
static struct Steinberg_Vst_ParameterInfo parameterInfo[DATA_PRODUCT_PARAMETERS_N + 3 * DATA_PRODUCT_BUSES_MIDI_INPUT_N] = {
{{~it.product.parameters.filter(x => !x.isLatency) :p:i}}
{
{{?p.isBypass}}
/* .id = */ {{=(it.tibia.vst3.sdbm("Bypass") & 0x7fffffff) >>> 0}},
/* .id = */ {{=(it.tibia.sdbm("bypass ") & 0x7fffffff) >>> 0}},
/* .title = */ { {{~Array.from("Bypass") :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .shortTitle = */ { {{~Array.from("Bypass") :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .units = */ { 0 },
@ -138,7 +140,7 @@ static struct Steinberg_Vst_ParameterInfo parameterInfo[DATA_PRODUCT_PARAMETERS_
/* .unitId = */ 0,
/* .flags = */ Steinberg_Vst_ParameterInfo_ParameterFlags_kIsBypass | Steinberg_Vst_ParameterInfo_ParameterFlags_kCanAutomate
{{??}}
/* .id = */ {{=(it.tibia.vst3.sdbm(p.name) & 0x7fffffff) >>> 0}},
/* .id = */ {{=(it.tibia.sdbm(p.id) & 0x7fffffff) >>> 0}},
/* .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.unit in it.tibia.vst3.units ? it.tibia.vst3.units[p.unit] : "") :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
@ -151,9 +153,9 @@ static struct Steinberg_Vst_ParameterInfo parameterInfo[DATA_PRODUCT_PARAMETERS_
{{~}}
{{~it.product.buses.filter(x => x.type == "midi" && x.direction == "input") :b:i}}
{
/* .id = */ {{=(it.tibia.vst3.sdbm(b.name + " Channel Pressure") & 0x7fffffff) >>> 0}},
/* .title = */ { {{~Array.from(b.name + " Channel Pressure") :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .shortTitle = */ { {{~Array.from(b.shortName + " Chan Pres") :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .id = */ {{=(it.tibia.sdbm(b.id + " channel pressure") & 0x7fffffff) >>> 0}},
/* .title = */ { {{~Array.from(b.name + " Channel Pressure").slice(0, 127) :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .shortTitle = */ { {{~Array.from(b.shortName + " Chan Pres").slice(0, 127) :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .units = */ { 0 },
/* .stepCount = */ 0,
/* .defaultNormalizedValue = */ 0.0,
@ -161,9 +163,9 @@ static struct Steinberg_Vst_ParameterInfo parameterInfo[DATA_PRODUCT_PARAMETERS_
/* .flags = */ Steinberg_Vst_ParameterInfo_ParameterFlags_kIsHidden | Steinberg_Vst_ParameterInfo_ParameterFlags_kCanAutomate
},
{
/* .id = */ {{=(it.tibia.vst3.sdbm(b.name + " Pitch Bend") & 0x7fffffff) >>> 0}},
/* .title = */ { {{~Array.from(b.name + " Pitch Bend") :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .shortTitle = */ { {{~Array.from(b.shortName + " Pitch Bend") :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .id = */ {{=(it.tibia.sdbm(b.id + " pitch bend") & 0x7fffffff) >>> 0}},
/* .title = */ { {{~Array.from(b.name + " Pitch Bend").slice(0, 127) :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .shortTitle = */ { {{~Array.from(b.shortName + " Pitch Bend").slice(0, 127) :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .units = */ { 0 },
/* .stepCount = */ 0,
/* .defaultNormalizedValue = */ 0.5,
@ -171,9 +173,9 @@ static struct Steinberg_Vst_ParameterInfo parameterInfo[DATA_PRODUCT_PARAMETERS_
/* .flags = */ Steinberg_Vst_ParameterInfo_ParameterFlags_kIsHidden | Steinberg_Vst_ParameterInfo_ParameterFlags_kCanAutomate
},
{
/* .id = */ {{=(it.tibia.vst3.sdbm(b.name + " Mod Wheel") & 0x7fffffff) >>> 0}},
/* .title = */ { {{~Array.from(b.name + " Mod Wheel") :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .shortTitle = */ { {{~Array.from(b.shortName + " Mod Wheel") :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .id = */ {{=(it.tibia.sdbm(b.id + " mod wheel") & 0x7fffffff) >>> 0}},
/* .title = */ { {{~Array.from(b.name + " Mod Wheel").slice(0, 127) :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .shortTitle = */ { {{~Array.from(b.shortName + " Mod Wheel").slice(0, 127) :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .units = */ { 0 },
/* .stepCount = */ 0,
/* .defaultNormalizedValue = */ 0.0,
@ -190,7 +192,7 @@ static struct Steinberg_Vst_ParameterInfo parameterInfo[DATA_PRODUCT_PARAMETERS_
# define DATA_PARAM_INTEGER (1<<2)
# define DATA_PARAM_MAP_LOG (1<<3)
static struct {
typedef struct {
size_t index;
double min;
double max;
@ -198,8 +200,11 @@ static struct {
uint32_t flags;
double mapK;
// scalePoints?
} parameterData[DATA_PRODUCT_PARAMETERS_N] = {
{{~it.product.parameters.filter(x => !x.isLatency) :p:i}}
} ParameterData;
# if DATA_PRODUCT_PARAMETERS_IN_N > 0
static ParameterData parameterInData[DATA_PRODUCT_PARAMETERS_IN_N] = {
{{~it.product.parameters.filter(x => x.direction == "input") :p:i}}
{
/* .index = */ {{=p.paramIndex}},
/* .min = */ {{=p.minimum.toExponential()}},
@ -210,11 +215,55 @@ static struct {
},
{{~}}
};
# endif
# if DATA_PRODUCT_PARAMETERS_OUT_N > 0
static ParameterData parameterOutData[DATA_PRODUCT_PARAMETERS_OUT_N] = {
{{~it.product.parameters.filter(x => !x.isLatency && x.direction == "output") :p:i}}
{
/* .index = */ {{=p.paramIndex}},
/* .min = */ {{=p.minimum.toExponential()}},
/* .max = */ {{=p.maximum.toExponential()}},
/* .def = */ {{=p.defaultValue.toExponential()}},
/* .flags = */ 0{{?p.toggled}} | DATA_PARAM_TOGGLED{{?}}{{?p.integer}} | DATA_PARAM_INTEGER{{?}}{{?p.map == "logarithmic"}} | DATA_PARAM_MAP_LOG{{?}},
/* .mapK = */ {{?p.map == "logarithmic"}}{{=Number(2.0 * Math.log(Math.sqrt(p.maximum * p.minimum) / Math.abs(p.minimum))).toExponential()}}{{??}}0.0{{?}}
},
{{~}}
};
static size_t parameterOutDataToInfoIndex[DATA_PRODUCT_PARAMETERS_OUT_N] = {
{{=it.product.parameters.filter(x => !x.isLatency && x.direction == "output").map(x => x.paramInfoIndex).join(", ")}}
};
# endif
static size_t parameterInfoToDataIndex[DATA_PRODUCT_PARAMETERS_N] = {
{{=it.product.parameters.filter(x => !x.isLatency).map(x => x.paramDataIndex).join(", ")}}
};
# endif
#endif
{{?it.product.parameters.find(x => x.isLatency)}}
#define DATA_PARAM_LATENCY_INDEX {{=it.product.parameters.find(x => x.isLatency).paramIndex}}
#define DATA_PARAM_LATENCY_INDEX {{=it.product.parameters.find(x => x.isLatency).paramIndex}}
{{?}}
{{?it.product.ui}}
#define DATA_UI
#define DATA_UI_USER_RESIZABLE {{=it.product.ui.userResizable ? 1 : 0}}
{{?}}
/*
* Parameter indices/ids:
*
* parameterInfo.id: hash of parameter id (+ extra sometimes), univocally identifies parameter across plugin versions (a la lv2:symbol)
* parameterGetIndexById(): returns parameterInfo array index based on id (parameterInfo.id)
* parameterInfoToDataIndex: maps parameterInfo array indices to parameter(In/Out)Data indices
* parameterOutDataToInfoIndex: maps parameterOutData indices to parameterInfo array indices
* parameter(In/Out)Data.index/p.paramIndex: Tibia parameter index, as used in plugin.h
* latency out parameter is never added to parameterInfo and parameter(In/Out)Data (specially handled)
*/
{{?it.product.state && it.product.state.dspCustom}}
#define DATA_STATE_DSP_CUSTOM
{{?}}

File diff suppressed because it is too large Load Diff

View File

@ -15,7 +15,7 @@
* 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
* File author: Stefano D'Angelo, Paolo Marrone
*/
var path = require("path");
@ -49,20 +49,27 @@ module.exports = function (data, api, outputCommon, outputData) {
"pc": "%",
"s": "s",
"semitone12TET": "semi"
},
sdbm: function (s) {
var hash = 0;
for (var i = 0; i < s.length; i++)
hash = s.charCodeAt(i) + (hash << 6) + (hash << 16) - hash;
return hash >>> 0;
}
};
for (var i = 0; i < data.product.parameters.length; i++)
var inIdx = 0;
var outIdx = 0;
for (var i = 0; i < data.product.parameters.length; i++) {
data.product.parameters[i].paramIndex = i;
if (data.product.parameters[i].isLatency)
continue;
data.product.parameters[i].paramInfoIndex = inIdx + outIdx;
if (data.product.parameters[i].direction == "input") {
data.product.parameters[i].paramDataIndex = inIdx;
inIdx++;
} else {
data.product.parameters[i].paramDataIndex = outIdx;
outIdx++;
}
}
}
api.copyFile(`data${sep}PkgInfo`, `data${sep}PkgInfo`);
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);

View File

@ -1,7 +1,7 @@
#
# Tibia
#
# Copyright (C) 2023, 2024 Orastron Srl unipersonale
# Copyright (C) 2023-2025 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
@ -18,12 +18,16 @@
# File author: Stefano D'Angelo
#
ALL += build/web/index.html build/web/cert.pem build/web/key.pem
build/web/index.html: ${DATA_DIR}/src/index.html | build/web
build/web/index.html: $(DATA_DIR)/src/index.html | build/web
cp $^ $@
build/web/key.pem: build/web/cert.pem
build/web/cert.pem: | build
yes "" | openssl req -x509 -newkey rsa:2048 -keyout build/web/key.pem -out build/web/cert.pem -days 365 -nodes 2>/dev/null
strip-web-demo: build/web/index.html
$(eval TMP := $(shell mktemp /tmp/index.XXXXXX))
html-minifier --collapse-whitespace --remove-comments --remove-redundant-attributes --remove-script-type-attributes --minify-css true --minify-js true build/web/index.html > $(TMP) || (rm $(TMP) && exit 1)
cp $(TMP) build/web/index.html || (rm $(TMP) && exit 1)
rm $(TMP)

View File

@ -24,7 +24,7 @@
<head>
<title>{{=it.product.name}}</title>
<script type="module">
import * as demo from "./{{=it.product.bundleName}}.js";
import * as demo from "./{{=it.product.bundleName}}/module.js";
window.demo = demo;
</script>
<script>
@ -212,7 +212,7 @@ window.addEventListener("load", function (e) {
module = new demo.Module();
if (!midi && hasMidiInput)
midi = await navigator.requestMIDIAccess();
await module.init(audioCtx, "{{=it.product.bundleName}}_processor.js", "{{=it.product.bundleName}}.wasm");
await module.init(audioCtx, "{{=it.product.bundleName}}/processor.js", "{{=it.product.bundleName}}/module.wasm");
node = new demo.Node(module);
node.connect(audioCtx.destination, audioOutputIndex);

View File

@ -23,5 +23,6 @@ var sep = path.sep;
module.exports = function (data, api) {
api.generateFileFromTemplateFile(`src${sep}index.html`, `src${sep}index.html`, data);
api.copyFile(`web-extra.mk`, `web-extra.mk`);
api.copyFile(`vars-extra-web-demo.mk`, `vars-extra-web-demo.mk`);
api.copyFile(`rules-extra-web-demo.mk`, `rules-extra-web-demo.mk`);
};

View File

@ -0,0 +1,25 @@
#
# Tibia
#
# Copyright (C) 2023-2025 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
#
ALL := $(ALL) build/web/index.html build/web/cert.pem build/web/key.pem
STRIP_ALL := $(STRIP_ALL) build/web/index.html
STRIP_PHONY := $(STRIP_PHONY) strip-web-demo
STRIP_PREREQS := $(STRIP_ALL) $(STRIP_PHONY)

View File

@ -1,7 +1,7 @@
#
# Tibia
#
# Copyright (C) 2023, 2024 Orastron Srl unipersonale
# Copyright (C) 2023-2025 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
@ -18,19 +18,28 @@
# File author: Stefano D'Angelo
#
TEMPLATE := web
include vars.mk
COMMON_DIR := $(or $(COMMON_DIR),.)
DATA_DIR := $(or $(DATA_DIR),.)
PLUGIN_DIR := $(or $(PLUGIN_DIR),src)
COMMON_DIR ?= .
DATA_DIR ?= .
PLUGIN_DIR ?= src
API_DIR ?= $(PLUGIN_DIR)
MKINC_DIR ?= $(COMMON_DIR)
CC = clang
CXX = clang++
BUILD_BIN_DIR := build/web
BUILD_DATA_DIR := build/web
CFLAGS = -Ofast -Wall -Wpedantic -Wextra
CFLAGS_ALL = -I${COMMON_DIR}/src -I${DATA_DIR}/src -I${PLUGIN_DIR} --target=wasm32 -flto -fvisibility=hidden ${CFLAGS} ${CFLAGS_EXTRA}
-include $(MKINC_DIR)/vars-pre.mk
LDFLAGS_ALL = \
CC := clang
CXX := clang++
CFLAGS := -Ofast -Wall -Wpedantic -Wextra
CFLAGS_ALL := -I$(COMMON_DIR)/src -I$(DATA_DIR)/src -I$(PLUGIN_DIR) -I$(API_DIR) --target=wasm32 -flto -fvisibility=hidden $(CFLAGS_EXTRA) $(CFLAGS)
LDFLAGS_ALL := \
-Wl,--allow-undefined \
-Wl,--no-entry \
-Wl,--lto-O3 \
@ -53,61 +62,75 @@ LDFLAGS_ALL = \
-Wl,--export=processor_set_parameter \
-Wl,-z,stack-size=$$((8*1024*1024)) \
-nostdlib
ifeq (${HAS_MIDI_IN}, yes)
LDFLAGS_ALL += -Wl,--export=processor_midi_msg_in
ifeq ($(HAS_MIDI_IN), yes)
LDFLAGS_ALL := $(LDFLAGS_ALL) -Wl,--export=processor_midi_msg_in
endif
LDFLAGS_ALL += ${LDFLAGS} ${LDFLAGS_EXTRA}
LDFLAGS_ALL := $(LDFLAGS_ALL) $(LDFLAGS_EXTRA) $(LDFLAGS)
CXXFLAGS = ${CFLAGS}
CXXFLAGS_ALL = -I${COMMON_DIR}/src -I${DATA_DIR}/src -I${PLUGIN_DIR} --target=wasm32 -flto -fvisibility=hidden ${CXXFLAGS} ${CXXFLAGS_EXTRA}
CXXFLAGS := $(CFLAGS)
CXXFLAGS_ALL := -I$(COMMON_DIR)/src -I$(DATA_DIR)/src -I$(PLUGIN_DIR) -I$(API_DIR) --target=wasm32 -flto -fvisibility=hidden $(CXXFLAGS_EXTRA) $(CXXFLAGS)
C_SRCS = ${COMMON_DIR}/src/processor.c ${COMMON_DIR}/src/walloc.c ${COMMON_DIR}/src/string.c
C_OBJS = $(addprefix build/obj/, $(notdir $(C_SRCS:.c=.o)))
C_SRCS := $(COMMON_DIR)/src/processor.c $(COMMON_DIR)/src/walloc.c $(COMMON_DIR)/src/string.c
C_OBJS := $(addprefix build/obj/, $(notdir $(C_SRCS:.c=.o)))
ifeq ($(CXX_SRCS_EXTRA),)
CXX_SRCS =
CXX_OBJS =
CXX_SRCS :=
CXX_OBJS :=
else
CXX_SRCS = ${COMMON_DIR}/src/new.cpp ${CXX_SRCS_EXTRA}
CXX_OBJS = $(addprefix build/obj/, $(notdir $(CXX_SRCS:.cpp=.o)))
CXX_SRCS := $(COMMON_DIR)/src/new.cpp $(CXX_SRCS_EXTRA)
CXX_OBJS := $(addprefix build/obj/, $(notdir $(CXX_SRCS:.cpp=.o)))
endif
ALL = build/web/${BUNDLE_NAME}.wasm build/web/${BUNDLE_NAME}_processor.js build/web/${BUNDLE_NAME}.js
DIRS := build build/obj build/web build/web/$(BUNDLE_NAME)
default: all
ALL := build/web/$(BUNDLE_NAME)/module.wasm build/web/$(BUNDLE_NAME)/processor.js build/web/$(BUNDLE_NAME)/module.js
-include ${COMMON_DIR}/web-extra.mk
STRIP_ALL := build/web/$(BUNDLE_NAME)/module.wasm build/web/$(BUNDLE_NAME)/processor.js build/web/$(BUNDLE_NAME)/module.js
STRIP_PHONY :=
STRIP_PREREQS := $(STRIP_ALL) $(STRIP_PHONY)
all: ${ALL}
-include $(MKINC_DIR)/vars-extra.mk
all: $(ALL)
ifeq ($(CXX_OBJS),)
build/web/${BUNDLE_NAME}.wasm: ${C_OBJS} | build/web
${CC} $^ -o $@ ${CFLAGS_ALL} ${LDFLAGS_ALL}
build/web/$(BUNDLE_NAME)/module.wasm: $(C_OBJS) | build/web/$(BUNDLE_NAME)
$(CC) $^ -o $@ $(CFLAGS_ALL) $(LDFLAGS_ALL)
else
build/web/${BUNDLE_NAME}.wasm: ${C_OBJS} ${CXX_OBJS} | build/web
${CXX} $^ -o $@ ${CFLAGS_ALL} ${CXXFLAGS_ALL} ${LDFLAGS_ALL}
build/web/$(BUNDLE_NAME)/module.wasm: $(C_OBJS) $(CXX_OBJS) | build/web/$(BUNDLE_NAME)
$(CXX) $^ -o $@ $(CFLAGS_ALL) $(CXXFLAGS_ALL) $(LDFLAGS_ALL)
endif
build/web/${BUNDLE_NAME}_processor.js: ${DATA_DIR}/src/processor.js | build/web
build/web/$(BUNDLE_NAME)/processor.js: $(DATA_DIR)/src/processor.js | build/web/$(BUNDLE_NAME)
cp $^ $@
build/web/${BUNDLE_NAME}.js: ${DATA_DIR}/src/module.js | build/web
build/web/$(BUNDLE_NAME)/module.js: $(DATA_DIR)/src/module.js | build/web/$(BUNDLE_NAME)
cp $^ $@
build/web build/obj:
$(DIRS):
mkdir -p $@
clean:
rm -fr build
.PHONY: all clean
strip: $(STRIP_PREREQS)
#already stripped
#wasm-strip build/web/$(BUNDLE_NAME)/module.wasm
uglifyjs -c pure_funcs -m reserved build/web/$(BUNDLE_NAME)/module.js -o build/web/$(BUNDLE_NAME)/module.js
uglifyjs -c pure_funcs -m reserved build/web/$(BUNDLE_NAME)/processor.js -o build/web/$(BUNDLE_NAME)/processor.js
-include $(MKINC_DIR)/rules-extra.mk
.PHONY: all clean strip $(STRIP_PHONY)
.SECONDEXPANSION:
PERCENT := %
$(C_OBJS): build/obj/%.o: $$(filter $$(PERCENT)/$$(basename $$(notdir $$@)).c,$$(C_SRCS)) | build/obj
${CC} $^ -o $@ -c ${CFLAGS_ALL}
$(CC) $^ -o $@ -c $(CFLAGS_ALL)
$(CXX_OBJS): build/obj/%.o: $$(filter $$(PERCENT)/$$(basename $$(notdir $$@)).cpp,$$(CXX_SRCS)) | build/obj
${CXX} $^ -o $@ -c ${CXXFLAGS_ALL}
$(CXX) $^ -o $@ -c $(CXXFLAGS_ALL)
-include $(MKINC_DIR)/rules-secondexp-extra.mk

View File

@ -20,15 +20,27 @@
BUNDLE_NAME := {{=it.product.bundleName}}
CFLAGS_EXTRA := {{=it.make && it.make.cflags ? it.make.cflags : ""}} {{=it.web_make && it.web_make.cflags ? it.web_make.cflags : ""}}
LDFLAGS_EXTRA := {{=it.make && it.make.ldflags ? it.make.ldflags : ""}} {{=it.web_make && it.web_make.ldflags ? it.web_make.ldflags : ""}}
CXXFLAGS_EXTRA := {{=it.make && it.make.cxxflags ? it.make.cxxflags : ""}} {{=it.web_make && it.web_make.cxxflags ? it.web_make.cxxflags : ""}}
C_SRCS_EXTRA := {{=it.make && it.make.cSrcs ? it.make.cSrcs : ""}} {{=it.web_make && it.web_make.cSrcs ? it.web_make.cSrcs : ""}}
CXX_SRCS_EXTRA := {{=it.make && it.make.cxxSrcs ? it.make.cxxSrcs : ""}} {{=it.web_make && it.web_make.cxxSrcs ? it.web_make.cxxSrcs : ""}}
COMMON_DIR := {{=it.web_make && it.web_make.commonDir ? it.web_make.commonDir : (it.make && it.make.commonDir ? it.make.commonDir : "")}}
DATA_DIR := {{=it.web_make && it.web_make.dataDir ? it.web_make.dataDir : (it.make && it.make.dataDir ? it.make.dataDir : "")}}
PLUGIN_DIR := {{=it.web_make && it.web_make.pluginDir ? it.web_make.pluginDir : (it.make && it.make.pluginDir ? it.make.pluginDir : "")}}
{{?(it.web_make?.commonDir || it.make?.commonDir)}}
COMMON_DIR := {{=it.web_make?.commonDir ?? (it.make?.commonDir ?? "")}}
{{?}}
{{?(it.web_make?.dataDir || it.make?.dataDir)}}
DATA_DIR := {{=it.web_make?.dataDir ?? (it.make?.dataDir ?? "")}}
{{?}}
{{?(it.web_make?.pluginDir || it.make?.pluginDir)}}
PLUGIN_DIR := {{=it.web_make?.pluginDir ?? (it.make?.pluginDir ?? "")}}
{{?}}
{{?(it.web_make?.apiDir || it.make?.apiDir)}}
API_DIR := {{=it.web_make?.apiDir ?? (it.make?.apiDir ?? "")}}
{{?}}
{{?(it.web_make?.mkincDir || it.make?.mkincDir)}}
MKINC_DIR := {{=it.web_make?.mkincDir ?? (it.make?.mkincDir ?? "")}}
{{?}}
HAS_MIDI_IN := {{=it.product.buses.filter(x => x.type == "midi" && x.direction == "input").length > 0 ? "yes" : "no"}}
{{?it.make?.extra}}
{{=it.make.extra}}
{{?}}
{{?it.web_make?.extra}}
{{=it.web_make.extra}}
{{?}}

View File

@ -1,7 +1,7 @@
/*
* Tibia
*
* Copyright (C) 2024 Orastron Srl unipersonale
* Copyright (C) 2024, 2025 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
@ -44,3 +44,7 @@ static size_t param_out_index[DATA_PRODUCT_PARAMETERS_OUTPUT_N] = {
{{~it.product.parameters :p:i}}{{?p.direction == "output"}}{{=i}}, {{?}}{{~}}
};
#endif
{{?it.product.state && it.product.state.dspCustom}}
#define DATA_STATE_DSP_CUSTOM
{{?}}

View File

@ -1,7 +1,7 @@
/*
* Tibia
*
* Copyright (C) 2022-2024 Orastron Srl unipersonale
* Copyright (C) 2022-2025 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
@ -22,6 +22,7 @@
#include <stdint.h>
#include "data.h"
#include "plugin_api.h"
#include "plugin.h"
#include "string.h"
@ -45,11 +46,22 @@ typedef struct {
} instance;
instance * processor_new(float sample_rate) {
#ifdef DATA_STATE_DSP_CUSTOM
(void)plugin_state_load;
(void)plugin_state_save;
#endif
instance * i = malloc(sizeof(instance));
if (i == NULL)
return NULL;
plugin_init(&i->p);
plugin_callbacks cbs = {
/* .handle = */ NULL,
/* .format = */ "web",
/* .get_bindir = */ NULL,
/* .get_datadir = */ NULL
};
plugin_init(&i->p, &cbs);
#if DATA_PRODUCT_PARAMETERS_N > 0
for (size_t j = 0; j < DATA_PRODUCT_PARAMETERS_N; j++)

View File

@ -1,21 +0,0 @@
{
"android_make": {
"cxxflags": "-I../../../miniaudio",
"keyStore": "keystore.jks",
"keyAlias": "androidkey",
"storePass": "android",
"keyPass": "android",
"sdkDir": "${HOME}/Android/Sdk",
"ndkVersion": "25.2.9519653",
"buildToolsVersion": "34.0.0",
"androidxDir": "${HOME}/Android/androidx",
"kotlinDir": "${HOME}/Android/kotlin",
"androidVersion": "34",
"androidxCoreVersion": "1.10.1",
"androidxLifecycleCommonVersion": "2.6.1",
"androidxVersionedparcelableVersion": "1.1.1",
"kotlinStdlibVersion": "1.9.0",
"kotlinxCoroutinesCoreVersion": "1.7.3",
"kotlinxCoroutinesCoreJVMVersion": "1.7.3"
}
}

View File

@ -1,5 +1,6 @@
{
"android": {
"javaPackageName": "com.example.tibia_test"
"javaPackageName": "com.example.tibia_test",
"androidVersion": "36"
}
}

View File

@ -1,6 +0,0 @@
{
"cmd_make": {
"tinywavDir": "../../../tinywav",
"midiParserDir": "../../../midi-parser"
}
}

View File

@ -1,6 +0,0 @@
{
"cmd": {
"busIds": [ "input", "output", "midi_in", "midi_out" ],
"parameterIds": [ "gain", "delay", "cutoff", "bypass", "yz1" ]
}
}

View File

@ -1,5 +0,0 @@
{
"daisy_seed_make": {
"libdaisyDir": "../../../libDaisy"
}
}

View File

@ -1,6 +1,6 @@
{
"ios_make": {
"headerSearchPaths": [ "../../../../../miniaudio" ],
"deploymentTarget": 16.6
"deploymentTarget": "16.6"
}
}

View File

@ -6,8 +6,6 @@
"uri": "@example:tibia_test",
"project": "@example:project",
"types": [ "@lv2:AmplifierPlugin" ],
"version": "1.0",
"busSymbols": [ "input", "output", "midi_in", "midi_out" ],
"parameterSymbols": [ "gain", "delay", "cutoff", "enabled", "yz1" ]
"version": "1.0"
}
}

View File

@ -1,7 +1,7 @@
/*
* Tibia
*
* Copyright (C) 2023, 2024 Orastron Srl unipersonale
* Copyright (C) 2023-2025 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
@ -18,24 +18,27 @@
* File author: Stefano D'Angelo
*/
#include <stdint.h>
typedef struct plugin {
float sample_rate;
size_t delay_line_length;
float sample_rate;
size_t delay_line_length;
float gain;
float delay;
float cutoff;
char bypass;
float gain;
float delay;
float cutoff;
char bypass;
float * delay_line;
size_t delay_line_cur;
float z1;
float cutoff_k;
float yz1;
float * delay_line;
size_t delay_line_cur;
float z1;
float cutoff_k;
float yz1;
} plugin;
static void plugin_init(plugin *instance) {
static void plugin_init(plugin *instance, const plugin_callbacks *cbs) {
(void)instance;
(void)cbs;
}
static void plugin_fini(plugin *instance) {
@ -67,17 +70,16 @@ static void plugin_reset(plugin *instance) {
static void plugin_set_parameter(plugin *instance, size_t index, float value) {
switch (index) {
case 0:
//approx instance->gain = powf(10.f, 0.05f * value);
instance->gain = ((2.6039890429412597e-4f * value + 0.032131027163547855f) * value + 1.f) / ((0.0012705124328080768f * value - 0.0666763481312185f) * value + 1.f);
case plugin_parameter_gain:
instance->gain = value;
break;
case 1:
instance->delay = 0.001f * value;
case plugin_parameter_delay:
instance->delay = value;
break;
case 2:
case plugin_parameter_cutoff:
instance->cutoff = value;
break;
case 3:
case plugin_parameter_bypass:
instance->bypass = value >= 0.5f;
break;
}
@ -93,8 +95,10 @@ static size_t calc_index(size_t cur, size_t delay, size_t len) {
}
static void plugin_process(plugin *instance, const float **inputs, float **outputs, size_t n_samples) {
//approx size_t delay = roundf(instance->sample_rate * instance->delay);
size_t delay = (size_t)(instance->sample_rate * instance->delay + 0.5f);
//approx const float gain = powf(10.f, 0.05f * instance->gain);
const float gain = ((2.6039890429412597e-4f * instance->gain + 0.032131027163547855f) * instance->gain + 1.f) / ((0.0012705124328080768f * instance->gain - 0.0666763481312185f) * instance->gain + 1.f);
//approx const size_t delay = roundf(instance->sample_rate * 0.001f * instance->delay);
const size_t delay = (size_t)(instance->sample_rate * 0.001f * instance->delay + 0.5f);
const float mA1 = instance->sample_rate / (instance->sample_rate + 6.283185307179586f * instance->cutoff * instance->cutoff_k);
for (size_t i = 0; i < n_samples; i++) {
instance->delay_line[instance->delay_line_cur] = inputs[0][i];
@ -104,7 +108,7 @@ static void plugin_process(plugin *instance, const float **inputs, float **outpu
instance->delay_line_cur = 0;
const float y = x + mA1 * (instance->z1 - x);
instance->z1 = y;
outputs[0][i] = instance->bypass ? inputs[0][i] : instance->gain * y;
outputs[0][i] = instance->bypass ? inputs[0][i] : gain * y;
instance->yz1 = outputs[0][i];
}
}
@ -115,3 +119,63 @@ static void plugin_midi_msg_in(plugin *instance, size_t index, const uint8_t * d
//approx instance->cutoff_k = powf(2.f, (1.f / 12.f) * (note - 60));
instance->cutoff_k = data[1] < 64 ? (-0.19558034980097166f * data[1] - 2.361735109225749f) / (data[1] - 75.57552349522389f) : (393.95397927344214f - 7.660826245588588f * data[1]) / (data[1] - 139.0755234952239f);
}
static void serialize_float(uint8_t *dest, float f) {
union { float f; uint32_t u; } v;
v.f = f;
dest[0] = v.u & 0xff;
dest[1] = (v.u & 0xff00) >> 8;
dest[2] = (v.u & 0xff0000) >> 16;
dest[3] = (v.u & 0xff000000) >> 24;
}
static float parse_float(const uint8_t *data) {
union { float f; uint32_t u; } v;
v.u = data[0];
v.u |= data[1] << 8;
v.u |= data[2] << 16;
v.u |= data[3] << 24;
return v.f;
}
static int plugin_state_save(plugin *instance, const plugin_state_callbacks *cbs, float last_sample_rate) {
(void)last_sample_rate;
uint8_t data[13];
cbs->lock(cbs->handle);
const float gain = instance->gain;
const float delay = instance->delay;
const float cutoff = instance->cutoff;
const char bypass = instance->bypass;
cbs->unlock(cbs->handle);
serialize_float(data, gain);
serialize_float(data + 4, delay);
serialize_float(data + 8, cutoff);
data[12] = bypass ? 1 : 0;
return cbs->write(cbs->handle, (const char *)data, 13);
}
static char x_isnan(float x) {
union { uint32_t u; float f; } v;
v.f = x;
return ((v.u & 0x7f800000) == 0x7f800000) && (v.u & 0x7fffff);
}
static int plugin_state_load(const plugin_state_callbacks *cbs, float cur_sample_rate, const char *data, size_t length) {
(void)cur_sample_rate;
if (length != 13)
return -1;
const uint8_t *d = (const uint8_t *)data;
const float gain = parse_float(d);
const float delay = parse_float(d + 4);
const float cutoff = parse_float(d + 8);
const float bypass = d[12] ? 1.f : 0.f;
if (x_isnan(gain) || x_isnan(delay) || x_isnan(cutoff))
return -1;
cbs->lock(cbs->handle);
cbs->set_parameter(cbs->handle, plugin_parameter_gain, gain);
cbs->set_parameter(cbs->handle, plugin_parameter_delay, delay);
cbs->set_parameter(cbs->handle, plugin_parameter_cutoff, cutoff);
cbs->set_parameter(cbs->handle, plugin_parameter_bypass, bypass);
cbs->unlock(cbs->handle);
return 0;
}

247
test/plugin_ui.h Normal file
View File

@ -0,0 +1,247 @@
/*
* Tibia
*
* Copyright (C) 2024, 2025 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 "vinci.h"
#include <stdio.h>
#include <string.h>
typedef struct {
void *widget;
vinci *vinci;
window *window;
int param_down;
float gain;
float delay;
float cutoff;
char bypass;
float y_z1;
plugin_ui_callbacks cbs;
} plugin_ui;
#define WIDTH 600.0
#define HEIGHT 400.0
static void plugin_ui_get_default_size(uint32_t *width, uint32_t *height) {
*width = WIDTH;
*height = HEIGHT;
}
static void draw_rect(window *w, uint32_t x, uint32_t y, uint32_t width, uint32_t height, uint32_t color) {
uint32_t *data = (uint32_t*) malloc(width * height * 4);
uint32_t p = 0;
for (uint32_t i = 0; i < height; i++)
for (uint32_t j = 0; j < width; j++, p++)
data[p] = color;
window_draw(w, (unsigned char*)data, 0, 0, width, height, x, y, width, height);
free(data);
}
static void draw_slider(plugin_ui *pui, int id, float value) {
const int w = window_get_width(pui->window);
const int h = window_get_height(pui->window);
draw_rect(pui->window, 0.1 * w, 0.15 * (id + 1) * h, 0.8 * w * value, 0.1 * h, 0x6789ab);
draw_rect(pui->window, 0.1 * w + 0.8 * w * value, 0.15 * (id + 1) * h, 0.8 * w * (1.f - value), 0.1 * h, 0x1223bc);
}
static void draw_button(plugin_ui *pui, int id, char value) {
const int w = window_get_width(pui->window);
const int h = window_get_height(pui->window);
draw_rect(pui->window, 0.4 * w, 0.15 * (id + 1) * h, 0.2 * w, 0.1 * h, value ? 0x6789ab : 0x1223bc);
}
static void on_close(window *w) {
printf("on_close %p \n", (void*)w); fflush(stdout);
}
static void on_mouse_press (window *win, int32_t x, int32_t y, uint32_t state) {
(void) state;
plugin_ui *pui = (plugin_ui*) window_get_data(win);
const int w = window_get_width(win);
const int h = window_get_height(win);
if (x >= 0.1 * w && x <= 0.9 * w && y >= 0.15 * h && y <= 0.25 * h) {
pui->param_down = 0;
pui->gain = (float)((x - (0.1 * w)) / (0.8 * w));
pui->cbs.set_parameter_begin(pui->cbs.handle, 0, -60.f + 80.f * pui->gain);
draw_slider(pui, 0, pui->gain);
} else if (x >= 0.1 * w && x <= 0.9 * w && y >= 0.3 * h && y <= 0.4 * h) {
pui->param_down = 1;
pui->delay = (float)((x - (0.1 * w)) / (0.8 * w));
pui->cbs.set_parameter_begin(pui->cbs.handle, 1, 1000.f * pui->delay);
draw_slider(pui, 1, pui->delay);
} else if (x >= 0.1 * w && x <= 0.9 * w && y >= 0.45 * h && y <= 0.55 * h) {
pui->param_down = 2;
pui->cutoff = (float)((x - (0.1 * w)) / (0.8 * w));
pui->cbs.set_parameter_begin(pui->cbs.handle, 2, (632.4555320336746f * pui->cutoff + 20.653108640674372f) / (1.0326554320337158f - pui->cutoff));
draw_slider(pui, 2, pui->cutoff);
} else if (x >= 0.4 * w && x <= 0.6 * w && y >= 0.6 * h && y <= 0.7 * h) {
pui->param_down = 4;
}
}
static void on_mouse_release (window *win, int32_t x, int32_t y, uint32_t state) {
(void) state;
plugin_ui *pui = (plugin_ui*) window_get_data(win);
const int w = window_get_width(win);
const int h = window_get_height(win);
if (pui->param_down == 4)
if (x >= 0.4 * w && x <= 0.6 * w && y >= 0.6 * h && y <= 0.7 * h) {
pui->bypass = !pui->bypass;
pui->cbs.set_parameter(pui->cbs.handle, 3, pui->bypass ? 1.f : 0.f);
draw_button(pui, 3, pui->bypass);
}
if (pui->param_down != -1) {
float v = x < 0.1 * w ? 0.f : (x > 0.9 * w ? 1.f : (float)((x - (0.1 * w)) / (0.8 * w)));
switch (pui->param_down) {
case 0:
pui->gain = v;
pui->cbs.set_parameter_end(pui->cbs.handle, 0, -60.f + 80.f * pui->gain);
draw_slider(pui, 0, pui->gain);
break;
case 1:
pui->delay = v;
pui->cbs.set_parameter_end(pui->cbs.handle, 1, 1000.f * pui->delay);
draw_slider(pui, 1, pui->delay);
break;
case 2:
pui->cutoff = v;
pui->cbs.set_parameter_end(pui->cbs.handle, 2, (632.4555320336746f * pui->cutoff + 20.653108640674372f) / (1.0326554320337158f - pui->cutoff));
draw_slider(pui, 2, pui->cutoff);
break;
}
pui->param_down = -1;
}
}
static void on_mouse_move (window *win, int32_t x, int32_t y, uint32_t state) {
(void) y;
(void) state;
plugin_ui *pui = (plugin_ui*) window_get_data(win);
const int w = window_get_width(win);
float v = x < 0.1 * w ? 0.f : (x > 0.9 * w ? 1.f : (float)((x - (0.1 * w)) / (0.8 * w)));
switch (pui->param_down) {
case 0:
pui->gain = v;
pui->cbs.set_parameter(pui->cbs.handle, 0, -60.f + 80.f * pui->gain);
draw_slider(pui, 0, pui->gain);
break;
case 1:
pui->delay = v;
pui->cbs.set_parameter(pui->cbs.handle, 1, 1000.f * pui->delay);
draw_slider(pui, 1, pui->delay);
break;
case 2:
pui->cutoff = v;
pui->cbs.set_parameter(pui->cbs.handle, 2, (632.4555320336746f * pui->cutoff + 20.653108640674372f) / (1.0326554320337158f - pui->cutoff));
draw_slider(pui, 2, pui->cutoff);
break;
}
}
static void on_window_resize (window *w, int32_t width, int32_t height) {
draw_rect(w, 0, 0, width, height, 0xff9999);
plugin_ui *pui = (plugin_ui*) window_get_data(w);
draw_slider(pui, 0, pui->gain);
draw_slider(pui, 1, pui->delay);
draw_slider(pui, 2, pui->cutoff);
draw_button(pui, 3, pui->bypass);
draw_slider(pui, 4, pui->y_z1);
}
static plugin_ui *plugin_ui_create(char has_parent, void *parent, plugin_ui_callbacks *cbs) {
plugin_ui *instance = malloc(sizeof(plugin_ui));
if (instance == NULL)
return NULL;
struct window_cbs wcbs;
memset(&wcbs, 0, sizeof(window_cbs));
wcbs.on_window_close = on_close;
wcbs.on_mouse_press = on_mouse_press;
wcbs.on_mouse_release = on_mouse_release;
wcbs.on_mouse_move = on_mouse_move;
wcbs.on_window_resize = on_window_resize;
instance->param_down = -1;
instance->vinci = vinci_new();
instance->window = window_new(instance->vinci, has_parent ? parent : NULL, WIDTH, HEIGHT, &wcbs);
instance->widget = window_get_handle(instance->window);
window_set_data(instance->window, (void*) instance);
window_show(instance->window);
// just some valid values to allow drawing
instance->gain = 0.f;
instance->delay = 0.f;
instance->cutoff = 0.f;
instance->bypass = 0;
instance->y_z1 = 0.f;
on_window_resize(instance->window, window_get_width(instance->window), window_get_height(instance->window));
instance->cbs = *cbs;
return instance;
}
static void plugin_ui_free(plugin_ui *instance) {
window_free(instance->window);
vinci_destroy(instance->vinci);
free(instance);
}
static void plugin_ui_idle(plugin_ui *instance) {
vinci_idle(instance->vinci);
}
static void plugin_ui_set_parameter(plugin_ui *instance, size_t index, float value) {
switch (index) {
case 0:
instance->gain = 0.0125f * value + 0.75f;
draw_slider(instance, 0, instance->gain);
break;
case 1:
instance->delay = 0.001f * value;
draw_slider(instance, 1, instance->delay);
break;
case 2:
// (bad) approx log unmap
instance->cutoff = (1.0326554320337176f * value - 20.65310864067435f) / (value + 632.4555320336754f);
draw_slider(instance, 2, instance->cutoff);
break;
case 3:
instance->bypass = value >= 0.5f;
draw_button(instance, 3, instance->bypass);
break;
case 4:
instance->y_z1 = 0.5f * value + 0.5f;
draw_slider(instance, 4, instance->y_z1);
break;
}
}

View File

@ -11,6 +11,7 @@
"channels": "mono",
"name": "Input",
"shortName": "Input",
"id": "input",
"sidechain": false,
"cv": false,
"optional": false
@ -21,6 +22,7 @@
"channels": "mono",
"name": "Output",
"shortName": "Output",
"id": "output",
"sidechain": false,
"cv": false,
"optional": false
@ -30,6 +32,7 @@
"direction": "input",
"name": "MIDI input",
"shortName": "MIDI input",
"id": "midi_in",
"sidechain": true,
"control": true,
"optional": true
@ -39,6 +42,7 @@
{
"name": "Gain",
"shortName": "Gain",
"id": "gain",
"direction": "input",
"isBypass": false,
"isLatency": false,
@ -60,6 +64,7 @@
{
"name": "Delay",
"shortName": "Delay",
"id": "delay",
"direction": "input",
"isBypass": false,
"isLatency": false,
@ -76,6 +81,7 @@
{
"name": "Cutoff",
"shortName": "Cutoff",
"id": "cutoff",
"direction": "input",
"isBypass": false,
"isLatency": false,
@ -92,6 +98,7 @@
{
"name": "Bypass",
"shortName": "Bypass",
"id": "bypass",
"direction": "input",
"isBypass": true,
"isLatency": false,
@ -112,6 +119,7 @@
{
"name": "yz1",
"shortName": "yz1",
"id": "yz1",
"direction": "output",
"isBypass": false,
"isLatency": false,
@ -125,6 +133,13 @@
"unit": "",
"map": "linear"
}
]
],
"ui": {
"userResizable": true,
"selfResizable": false
},
"state": {
"dspCustom": true
}
}
}

1
test/rules-extra.mk Normal file
View File

@ -0,0 +1 @@
include rules-extra-web-demo.mk

View File

@ -1,32 +1,42 @@
#!/bin/sh
dir=`dirname $0`
$dir/../tibia $dir/product.json,$dir/company.json $dir/../templates/api $dir/../out/api
$dir/../tibia $dir/product.json,$dir/company.json,$dir/vst3.json $dir/../templates/vst3 $dir/../out/vst3
$dir/../tibia $dir/product.json,$dir/company.json,$dir/vst3.json,$dir/vst3-make.json $dir/../templates/vst3-make $dir/../out/vst3
cp $dir/plugin.h $dir/../out/vst3/src
$dir/../tibia $dir/product.json,$dir/company.json,$dir/vst3.json $dir/../templates/vst3-make $dir/../out/vst3
cp $dir/plugin.h $dir/plugin_ui.h $dir/../out/vst3/src
cp $dir/vars-pre.mk $dir/../out/vst3
$dir/../tibia $dir/product.json,$dir/company.json,$dir/lv2.json $dir/../templates/lv2 $dir/../out/lv2
$dir/../tibia $dir/product.json,$dir/company.json,$dir/lv2.json $dir/../templates/lv2-make $dir/../out/lv2
cp $dir/plugin.h $dir/../out/lv2/src
cp $dir/plugin.h $dir/plugin_ui.h $dir/../out/lv2/src
cp $dir/vars-pre.mk $dir/../out/lv2
$dir/../tibia $dir/product.json,$dir/company.json $dir/../templates/web $dir/../out/web
$dir/../tibia $dir/product.json,$dir/company.json $dir/../templates/web-make $dir/../out/web
$dir/../tibia $dir/product.json,$dir/company.json $dir/../templates/web-demo $dir/../out/web
cp $dir/plugin.h $dir/../out/web/src
cp $dir/vars-pre.mk $dir/vars-extra.mk $dir/rules-extra.mk $dir/../out/web
$dir/../tibia $dir/product.json,$dir/company.json,$dir/android.json $dir/../templates/android $dir/../out/android
$dir/../tibia $dir/product.json,$dir/company.json,$dir/android.json,$dir/android-make.json $dir/../templates/android-make $dir/../out/android
$dir/../tibia $dir/product.json,$dir/company.json,$dir/android.json $dir/../templates/android-make $dir/../out/android
cp $dir/keystore.jks $dir/../out/android
cp $dir/plugin.h $dir/../out/android/src
cp $dir/vars-pre.mk $dir/../out/android
$dir/../tibia $dir/product.json,$dir/company.json,$dir/ios.json $dir/../templates/ios $dir/../out/ios
$dir/../tibia $dir/product.json,$dir/company.json,$dir/ios.json,$dir/ios-make.json $dir/../templates/ios-make $dir/../out/ios
cp $dir/plugin.h $dir/../out/ios/src
cp $dir/vars-pre.mk $dir/../out/ios
$dir/../tibia $dir/product.json,$dir/company.json,$dir/cmd.json $dir/../templates/cmd $dir/../out/cmd
$dir/../tibia $dir/product.json,$dir/company.json,$dir/cmd.json,$dir/cmd-make.json $dir/../templates/cmd-make $dir/../out/cmd
$dir/../tibia $dir/product.json,$dir/company.json $dir/../templates/cmd $dir/../out/cmd
$dir/../tibia $dir/product.json,$dir/company.json $dir/../templates/cmd-make $dir/../out/cmd
cp $dir/plugin.h $dir/../out/cmd/src
cp $dir/vars-pre.mk $dir/../out/cmd
$dir/../tibia $dir/product.json,$dir/company.json,$dir/daisy-seed.json $dir/../templates/daisy-seed $dir/../out/daisy-seed
$dir/../tibia $dir/product.json,$dir/company.json,$dir/daisy-seed.json,$dir/daisy-seed-make.json $dir/../templates/daisy-seed-make $dir/../out/daisy-seed
$dir/../tibia $dir/product.json,$dir/company.json,$dir/daisy-seed.json $dir/../templates/daisy-seed-make $dir/../out/daisy-seed
cp $dir/plugin.h $dir/../out/daisy-seed/src
cp $dir/vars-pre.mk $dir/../out/daisy-seed

1
test/vars-extra.mk Normal file
View File

@ -0,0 +1 @@
include vars-extra-web-demo.mk

66
test/vars-pre.mk Normal file
View File

@ -0,0 +1,66 @@
API_DIR := ../api
VINCI_DIR := ../../../vinci
ifeq ($(TEMPLATE), cmd)
TINYWAV_DIR := ../../../tinywav
MIDI_PARSER_DIR := ../../../midi-parser
endif
ifeq ($(TEMPLATE), lv2)
ifeq ($(OS), Windows_NT)
C_SRCS_EXTRA := $(VINCI_DIR)/vinci-win32.c
LDFLAGS_EXTRA := -mwindows
else
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S), Darwin)
M_SRCS_EXTRA := $(VINCI_DIR)/vinci-cocoa.m
LDFLAGS_EXTRA := -framework Cocoa -lobjc
else
C_SRCS_EXTRA := $(VINCI_DIR)/vinci-xcb.c
LDFLAGS_EXTRA := -lxcb
endif
endif
CFLAGS_EXTRA := $(CFLAGS_EXTRA) -I${VINCI_DIR}
endif
ifeq ($(TEMPLATE), vst3)
ifeq ($(OS), Windows_NT)
C_SRCS_EXTRA := $(VINCI_DIR)/vinci-win32.c
LDFLAGS_EXTRA := -mwindows
else
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S), Darwin)
M_SRCS_EXTRA := $(VINCI_DIR)/vinci-cocoa.m
LDFLAGS_EXTRA := -framework Cocoa -lobjc
else
C_SRCS_EXTRA := $(VINCI_DIR)/vinci-xcb.c
LDFLAGS_EXTRA := -lxcb
endif
endif
CFLAGS_EXTRA := $(CFLAGS_EXTRA) -I../../../vst3_c_api
CFLAGS_EXTRA := $(CFLAGS_EXTRA) -I${VINCI_DIR}
endif
ifeq ($(TEMPLATE), daisy-seed)
LIBDAISY_DIR := ../../../libDaisy
endif
ifeq ($(TEMPLATE), android)
CXXFLAGS_EXTRA := -I../../../miniaudio
KEY_STORE := keystore.jks
KEY_ALIAS := androidkey
STORE_PASS := android
KEY_PASS := android
SDK_DIR := $(HOME)/Android/Sdk
ANDROIDX_DIR := $(HOME)/Android/androidx
KOTLIN_DIR := $(HOME)/Android/kotlin
NDK_VERSION := 28.0.13004108
BUILD_TOOLS_VERSION := 36.0.0
ANDROIDX_CORE_VERSION := 1.16.0
ANDROIDX_LIFECYCLE_COMMON_VERSION := 2.8.7
ANDROIDX_VERSIONEDPARCELABLE_VERSION := 1.2.1
KOTLIN_STDLIB_VERSION := 2.1.20
KOTLINX_COROUTINES_CORE_VERSION := 1.10.2
KOTLINX_COROUTINES_CORE_JVM_VERSION := 1.10.2
endif

View File

@ -1,5 +0,0 @@
{
"vst3_make": {
"cflags": "-I../../../vst3_c_api"
}
}

View File

@ -6,6 +6,6 @@
"controller": {
"cid": "ecf4f431312f44fbb37b88c545ae9993"
},
"subCategory": "FX"
"subCategory": "Fx"
}
}

47
tibia
View File

@ -2,37 +2,56 @@
function usage() {
console.log("Usage:");
console.log(" tibia file1.json,file2.json,...filen.json template outDirectory");
console.log(" tibia --common template outDirectory");
console.log(" tibia --data file1.json,file2.json,...filen.json template outDirectory");
console.log(" tibia file1.json,file2.json,...filen.json template outDirectory [override1=value1] [override2=value2] ...");
console.log(" tibia --common template outDirectory [override1=value1] [override2=value2] ...");
console.log(" tibia --data file1.json,file2.json,...filen.json template outDirectory [override1=value1] [override2=value2] ...");
process.exit(1);
}
var jsonFiles, template, outputDir, outputCommon, outputData;
var jsonFiles, template, outputDir, outputCommon, outputData, i;
if (process.argv[2] == "--common") {
if (process.argv.length != 5)
if (process.argv.length < 5)
usage();
jsonFiles = [];
template = process.argv[3];
outputDir = process.argv[4];
outputCommon = true;
outputData = false;
i = 5;
} else if (process.argv[2] == "--data") {
if (process.argv.length != 6)
if (process.argv.length < 6)
usage();
jsonFiles = process.argv[3].split(",");
template = process.argv[4];
outputDir = process.argv[5];
outputCommon = false;
outputData = true;
i = 6;
} else {
if (process.argv.length != 5)
if (process.argv.length < 5)
usage();
jsonFiles = process.argv[2].split(",");
template = process.argv[3];
outputDir = process.argv[4];
outputCommon = true;
outputData = true;
i = 5;
}
var overrides = [];
for (; i < process.argv.length; i++) {
var o = process.argv[i];
var r = o.match(/^([_a-zA-Z][_a-zA-Z0-9]*(\.[_a-zA-Z][_a-zA-Z0-9]*)*)=/);
if (!r)
usage();
var p = r[1].split(".");
var v = o.substring(r[0].length);
try {
v = JSON.parse(v);
} catch (e) {
usage();
}
overrides.push({ property: p, value: v });
}
var fs = require("fs");
@ -54,6 +73,13 @@ for (var i = 0; i < jsonFiles.length; i++) {
for (var k in d)
data[k] = d[k];
}
for (var i = 0; i < overrides.length; i++) {
var p = data;
var o = overrides[i];
for (var j = 0; j < o.property.length - 1; j++)
p = p[o.property[j]];
p[o.property[o.property.length - 1]] = o.value;
}
/*
if (outputData) {
@ -145,6 +171,13 @@ data.tibia = {
}
}
return s;
},
sdbm: function (s) {
var hash = 0;
for (var i = 0; i < s.length; i++)
hash = s.charCodeAt(i) + (hash << 6) + (hash << 16) - hash;
return hash >>> 0;
}
};