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 ## Legal
Copyright (C) 2021-2024 Orastron Srl unipersonale. Copyright (C) 2021-2025 Orastron Srl unipersonale.
Authors: Stefano D'Angelo, Paolo Marrone. Authors: Stefano D'Angelo, Paolo Marrone.

46
notes
View File

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

View File

@ -18,104 +18,143 @@
# File author: Stefano D'Angelo # File author: Stefano D'Angelo
# #
TEMPLATE := android
include vars.mk include vars.mk
ifeq (${HAS_MIDI_IN}, yes) COMMON_DIR ?= .
MIN_API := 29 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 else
MIN_API := 26 MIN_API := 26
endif endif
CC = ${ANDROID_NDK_DIR}/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi${MIN_API}-clang NDK_DIR := $(SDK_DIR)/ndk/$(NDK_VERSION)
CXX = ${ANDROID_NDK_DIR}/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi${MIN_API}-clang++ BUILD_TOOLS_DIR := $(SDK_DIR)/build-tools/$(BUILD_TOOLS_VERSION)
JC = javac
APKSIGNER = ${BUILD_TOOLS_DIR}/apksigner ANDROID_JAR_FILE := $(SDK_DIR)/platforms/android-$(ANDROID_VERSION)/android.jar
ZIPALIGN = ${BUILD_TOOLS_DIR}/zipalign ANDROIDX_CORE_FILE := $(ANDROIDX_DIR)/core-$(ANDROIDX_CORE_VERSION).jar
AAPT = ${BUILD_TOOLS_DIR}/aapt ANDROIDX_LIFECYCLE_COMMON_FILE := $(ANDROIDX_DIR)/lifecycle-common-$(ANDROIDX_LIFECYCLE_COMMON_VERSION).jar
D8 = ${BUILD_TOOLS_DIR}/d8 ANDROIDX_VERSIONEDPARCELABLE_FILE := $(ANDROIDX_DIR)/versionedparcelable-$(ANDROIDX_VERSIONEDPARCELABLE_VERSION).jar
ADB = ${ANDROID_SDK_DIR}/platform-tools/adb 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 := \ JARS := \
${ANDROID_JAR_FILE} \ $(ANDROID_JAR_FILE) \
${ANDROIDX_CORE_FILE} \ $(ANDROIDX_CORE_FILE) \
${ANDROIDX_LIFECYCLE_COMMON_FILE} \ $(ANDROIDX_LIFECYCLE_COMMON_FILE) \
${ANDROIDX_VERSIONEDPARCELABLE_FILE} \ $(ANDROIDX_VERSIONEDPARCELABLE_FILE) \
${KOTLIN_STDLIB_FILE} \ $(KOTLIN_STDLIB_FILE) \
${KOTLINX_COROUTINES_CORE_FILE} \ $(KOTLINX_COROUTINES_CORE_FILE) \
${KOTLINX_COROUTINES_CORE_JVM_FILE} $(KOTLINX_COROUTINES_CORE_JVM_FILE)
CLASSES_PATH := $(subst .,/,$(JAVA_PACKAGE_NAME)) CLASSES_PATH := $(subst .,/,$(JAVA_PACKAGE_NAME))
CLASSES := \ CLASSES := \
MainActivity \ MainActivity \
MainActivity$$WebAppInterface MainActivity$$WebAppInterface
ifeq (${HAS_MIDI_IN}, yes) ifeq ($(HAS_MIDI_IN), yes)
CLASSES += MainActivity$$WebAppInterface$$MidiDeviceCallback MainActivity$$WebAppInterface$$1 CLASSES += MainActivity$$WebAppInterface$$MidiDeviceCallback MainActivity$$WebAppInterface$$1
endif endif
COMMON_DIR := $(or $(COMMON_DIR),.) CFLAGS := -O3 -Wall -Wpedantic -Wextra
DATA_DIR := $(or $(DATA_DIR),.) CFLAGS_ALL := -I$(DATA_DIR)/src -I$(PLUGIN_DIR) -I$(API_DIR) -fPIC $(CFLAGS_EXTRA) $(CFLAGS)
PLUGIN_DIR := $(or $(PLUGIN_DIR),src)
CFLAGS = -O3 -Wall -Wpedantic -Wextra CXXFLAGS := $(CFLAGS)
CFLAGS_ALL = -I${DATA_DIR}/src -I${PLUGIN_DIR} -fPIC ${CFLAGS} ${CFLAGS_EXTRA} CXXFLAGS_ALL := -I$(DATA_DIR)/src -I$(PLUGIN_DIR) -I$(API_DIR) -fPIC -std=c++11 $(CXXFLAGS_EXTRA) $(CXXFLAGS)
CXXFLAGS = ${CFLAGS} LDFLAGS :=
CXXFLAGS_ALL = -I${DATA_DIR}/src -I${PLUGIN_DIR} -fPIC -std=c++11 ${CXXFLAGS} ${CXXFLAGS_EXTRA} 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 LDFLAGS += -lamidi
endif endif
LDFLAGS_ALL += ${LDFLAGS} ${LDFLAGS_EXTRA} LDFLAGS_ALL += $(LDFLAGS_EXTRA) $(LDFLAGS)
JFLAGS = JFLAGS :=
JFLAGS_ALL = ${JFLAGS} ${JFLAGS_EXTRA} JFLAGS_ALL := $(JFLAGS_EXTRA) $(JFLAGS)
C_SRCS = ${C_SRCS_EXTRA} C_SRCS := $(C_SRCS_EXTRA)
C_OBJS = $(addprefix build/obj/, $(notdir $(C_SRCS:.c=.o))) C_OBJS := $(addprefix build/obj/, $(notdir $(C_SRCS:.c=.o)))
CXX_SRCS = ${COMMON_DIR}/src/jni.cpp ${CXX_SRCS_EXTRA} CXX_SRCS := $(COMMON_DIR)/src/jni.cpp $(CXX_SRCS_EXTRA)
CXX_OBJS = $(addprefix build/obj/, $(notdir $(CXX_SRCS:.cpp=.o))) 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} ALL := build/$(BUNDLE_NAME).apk
${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 -include $(MKINC_DIR)/vars-extra.mk
${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 all: $(ALL)
${AAPT} package -f -M ${DATA_DIR}/data/AndroidManifest.xml -A build/assets $(foreach jar,$(JARS),-I $(jar)) -F $@ build/apk
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 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 build/apk/my_classes.jar: $(foreach class,$(CLASSES),build/obj/$(CLASSES_PATH)/$(class).class) | build/apk
@echo ${CLASSES_PATH} @echo $(CLASSES_PATH)
${D8} $(foreach class,$(CLASSES),'build/obj/$(CLASSES_PATH)/$(class).class') --min-api ${MIN_API} --output $@ --no-desugaring $(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 build/obj/$(CLASSES_PATH)/MainActivity.class: $(DATA_DIR)/src/MainActivity.java | build/obj
${JC} ${JFLAGS_ALL} -classpath "$(subst $() $(),:,$(JARS))" -d 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 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} $(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 $^ $@ cp $^ $@
build/gen build/apk build/obj build/apk/lib/armeabi-v7a build/assets: $(DIRS):
mkdir -p $@ mkdir -p $@
clean: clean:
rm -fr build rm -fr build
install: build/${BUNDLE_NAME}.apk install: build/$(BUNDLE_NAME).apk
[ -n "`${ADB} shell pm list packages | grep ^package:${JAVA_PACKAGE_NAME}`" ] && ${ADB} uninstall ${JAVA_PACKAGE_NAME}; exit 0 [ -n "`$(ADB) shell pm list packages | grep ^package:$(JAVA_PACKAGE_NAME)`" ] && $(ADB) uninstall $(JAVA_PACKAGE_NAME); exit 0
${ADB} install $^ $(ADB) install $^
-include $(MKINC_DIR)/rules-extra.mk
.PHONY: all clean install .PHONY: all clean install
@ -124,7 +163,9 @@ install: build/${BUNDLE_NAME}.apk
PERCENT := % PERCENT := %
$(C_OBJS): build/obj/%.o: $$(filter $$(PERCENT)/$$(basename $$(notdir $$@)).c,$$(C_SRCS)) | build/obj $(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_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}} BUNDLE_NAME := {{=it.product.bundleName}}
JAVA_PACKAGE_NAME := {{=it.android.javaPackageName}} 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 : ""}} {{?(it.android_make?.commonDir || it.make?.commonDir)}}
CXXFLAGS_EXTRA := {{=it.make && it.make.cxxflags ? it.make.cxxflags : ""}} {{=it.android_make && it.android_make.cxxflags ? it.android_make.cxxflags : ""}} COMMON_DIR := {{=it.android_make?.commonDir ?? (it.make?.commonDir ?? "")}}
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 : ""}} {{?(it.android_make?.dataDir || it.make?.dataDir)}}
DATA_DIR := {{=it.android_make?.dataDir ?? (it.make?.dataDir ?? "")}}
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 : ""}} {{?(it.android_make?.pluginDir || it.make?.pluginDir)}}
PLUGIN_DIR := {{=it.android_make?.pluginDir ?? (it.make?.pluginDir ?? "")}}
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 : "")}} {{?(it.android_make?.apiDir || it.make?.apiDir)}}
PLUGIN_DIR := {{=it.android_make && it.android_make.pluginDir ? it.android_make.pluginDir : (it.make && it.make.pluginDir ? it.make.pluginDir : "")}} API_DIR := {{=it.android_make?.apiDir ?? (it.make?.apiDir ?? "")}}
{{?}}
KEY_STORE := {{=it.android_make.keyStore}} {{?(it.android_make?.mkincDir || it.make?.mkincDir)}}
KEY_ALIAS := {{=it.android_make.keyAlias}} MKINC_DIR := {{=it.android_make?.mkincDir ?? (it.make?.mkincDir ?? "")}}
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
HAS_MIDI_IN := {{=it.product.buses.filter(x => x.type == "midi" && x.direction == "input").length > 0 ? "yes" : "no"}} 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: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}}"> <application android:label="{{=it.product.name}}">
<activity android:name=".MainActivity" android:label="{{=it.product.name}}" android:exported="true"> <activity android:name=".MainActivity" android:label="{{=it.product.name}}" android:exported="true">
<intent-filter> <intent-filter>

View File

@ -1,7 +1,7 @@
/* /*
* Tibia * 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 * Tibia is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -83,3 +83,7 @@ static struct {
#endif #endif
#define JNI_FUNC(x) Java_{{=it.android.javaPackageName.replaceAll("_", "_1").replaceAll(".", "_")}}_MainActivity_##x #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 * 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 * Tibia is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -22,6 +22,7 @@
#include <stdint.h> #include <stdint.h>
#include "data.h" #include "data.h"
#include "plugin_api.h"
#include "plugin.h" #include "plugin.h"
#include <string.h> #include <string.h>
@ -188,6 +189,10 @@ JNIEXPORT jboolean JNICALL
JNI_FUNC(nativeAudioStart)(JNIEnv* env, jobject thiz) { JNI_FUNC(nativeAudioStart)(JNIEnv* env, jobject thiz) {
(void)env; (void)env;
(void)thiz; (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 + NUM_CHANNELS_OUT > 0
# if NUM_CHANNELS_IN == 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) if (ma_device_init(NULL, &deviceConfig, &device) != MA_SUCCESS)
return false; 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 #if PARAMETERS_N > 0
for (size_t i = 0; i < PARAMETERS_N; i++) { 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 # 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 # Tibia is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -18,85 +18,114 @@
# File author: Stefano D'Angelo # File author: Stefano D'Angelo
# #
TEMPLATE := cmd
include vars.mk 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) ifeq ($(OS), Windows_NT)
EXE_SUFFIX = .exe EXE_SUFFIX := .exe
else else
UNAME_S = $(shell uname -s) UNAME_S := $(shell uname -s)
EXE_SUFFIX = EXE_SUFFIX :=
PREFIX = /usr/local PREFIX := /usr/local
BINDIR = ${PREFIX}/bin BINDIR := $(PREFIX)/bin
endif endif
COMMON_DIR := $(or $(COMMON_DIR),.) TINYWAV_DIR ?= ../tinywav
DATA_DIR := $(or $(DATA_DIR),.) MIDI_PARSER_DIR ?= ../midi-parser
PLUGIN_DIR := $(or $(PLUGIN_DIR),src)
CC = gcc CC := gcc
CXX = g++ CXX := g++
CFLAGS = -O3 -Wall -Wpedantic -Wextra 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_ALL := -I$(DATA_DIR)/src -I$(PLUGIN_DIR) -I$(API_DIR) -I$(TINYWAV_DIR) -I$(MIDI_PARSER_DIR)/include $(CFLAGS_EXTRA) $(CFLAGS)
LDFLAGS = LDFLAGS :=
LDFLAGS_ALL = ${LDFLAGS} ${LDFLAGS_EXTRA} LDFLAGS_ALL := $(LDFLAGS_EXTRA) $(LDFLAGS)
CXXFLAGS = ${CFLAGS} CXXFLAGS := $(CFLAGS)
CXXFLAGS_ALL = -I${DATA_DIR}/src -I${PLUGIN_DIR} -I${TINYWAV_DIR} -I${MIDI_PARSER_DIR}/include -fPIC ${CXXFLAGS} ${CXXFLAGS_EXTRA} 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) ifeq ($(UNAME_S), Darwin)
CFLAGS_ALL := ${CFLAGS_ALL} -arch arm64 -arch x86_64 CFLAGS_ALL := $(CFLAGS_ALL) -arch arm64 -arch x86_64
LDFLAGS_ALL := ${LDFLAGS_ALL} -arch arm64 -arch x86_64 LDFLAGS_ALL := $(LDFLAGS_ALL) -arch arm64 -arch x86_64
CXXFLAGS_ALL := ${CXXFLAGS_ALL} -arch arm64 -arch x86_64 CXXFLAGS_ALL := $(CXXFLAGS_ALL) -arch arm64 -arch x86_64
endif 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) 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 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_SRCS := $(CXX_SRCS_EXTRA)
CXX_OBJS = $(addprefix build/obj/, $(notdir $(CXX_SRCS:.cpp=.o))) 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),) ifeq ($(CXX_OBJS),)
build/${PROGRAM}: ${C_OBJS} build/obj/tinywav.o | build build/$(PROGRAM): $(C_OBJS) build/obj/tinywav.o | build
${CC} $^ -o $@ ${CFLAGS_ALL} ${LDFLAGS_ALL} $(CC) $^ -o $@ $(CFLAGS_ALL) $(LDFLAGS_ALL)
else else
build/${PROGRAM}: ${C_OBJS} ${CXX_OBJS} build/obj/tinywav.o | build build/$(PROGRAM): $(C_OBJS) $(CXX_OBJS) build/obj/tinywav.o | build
${CXX} $^ -o $@ ${CFLAGS_ALL} ${CXXFLAGS_ALL} ${LDFLAGS_ALL} $(CXX) $^ -o $@ $(CFLAGS_ALL) $(CXXFLAGS_ALL) $(LDFLAGS_ALL)
endif endif
build build/obj: $(DIRS):
mkdir -p $@ mkdir -p $@
clean: clean:
rm -fr build rm -fr build
strip: $(STRIP_PREREQS)
strip build/$(PROGRAM)
ifeq ($(OS), Windows_NT) ifeq ($(OS), Windows_NT)
.PHONY: all clean .PHONY: all clean strip $(STRIP_PHONY)
else else
install: all install: all
mkdir -p -m 0755 ${BINDIR} mkdir -m 0755 -p $(BINDIR)
install -m 0755 build/${PROGRAM} ${BINDIR} install -m 0755 build/$(PROGRAM) $(BINDIR)
.PHONY: all clean install .PHONY: all clean strip $(STRIP_PHONY) install
endif endif
-include $(MKINC_DIR)/rules-extra.mk
.SECONDEXPANSION: .SECONDEXPANSION:
PERCENT := % PERCENT := %
build/obj/tinywav.o: ${TINYWAV_DIR}/tinywav.c build/obj/tinywav.o: $(TINYWAV_DIR)/tinywav.c
${CC} $^ -o $@ -c ${CFLAGS_ALL} -Wno-unused-result $(CC) $^ -o $@ -c $(CFLAGS_ALL) -Wno-unused-result
$(C_OBJS): build/obj/%.o: $$(filter $$(PERCENT)/$$(basename $$(notdir $$@)).c,$$(C_SRCS)) | build/obj $(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_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}} BUNDLE_NAME := {{=it.product.bundleName}}
CFLAGS_EXTRA := {{=it.make && it.make.cflags ? it.make.cflags : ""}} {{=it.cmd_make.cflags ? it.cmd_make.cflags : ""}} {{?(it.cmd_make?.commonDir || it.make?.commonDir)}}
CXXFLAGS_EXTRA := {{=it.make && it.make.cxxflags ? it.make.cxxflags : ""}} {{=it.cmd_make.cxxflags ? it.cmd_make.cxxflags : ""}} COMMON_DIR := {{=it.cmd_make?.commonDir ?? (it.make?.commonDir ?? "")}}
LDFLAGS_EXTRA := {{=it.make && it.make.ldflags ? it.make.ldflags : ""}} {{=it.cmd_make.ldflags ? it.cmd_make.ldflags : ""}} {{?}}
{{?(it.cmd_make?.dataDir || it.make?.dataDir)}}
C_SRCS_EXTRA := {{=it.make && it.make.cSrcs ? it.make.cSrcs : ""}} {{=it.cmd_make && it.cmd_make.cSrcs ? it.cmd_make.cSrcs : ""}} DATA_DIR := {{=it.cmd_make?.dataDir ?? (it.make?.dataDir ?? "")}}
CXX_SRCS_EXTRA := {{=it.make && it.make.cxxSrcs ? it.make.cxxSrcs : ""}} {{=it.cmd_make && it.cmd_make.cxxSrcs ? it.cmd_make.cxxSrcs : ""}} {{?}}
{{?(it.cmd_make?.pluginDir || it.make?.pluginDir)}}
COMMON_DIR := {{=it.cmd_make && it.cmd_make.commonDir ? it.cmd_make.commonDir : (it.make && it.make.commonDir ? it.make.commonDir : "")}} PLUGIN_DIR := {{=it.cmd_make?.pluginDir ?? (it.make?.pluginDir ?? "")}}
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 : "")}} {{?(it.cmd_make?.apiDir || it.make?.apiDir)}}
API_DIR := {{=it.cmd_make?.apiDir ?? (it.make?.apiDir ?? "")}}
TINYWAV_DIR := {{=it.cmd_make.tinywavDir}} {{?}}
MIDI_PARSER_DIR := {{=it.cmd_make.midiParserDir}} {{?(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"}} 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 * 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 * Tibia is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -73,7 +73,7 @@ static struct {
} param_data[PARAMETERS_N] = { } param_data[PARAMETERS_N] = {
{{~it.product.parameters :p:i}} {{~it.product.parameters :p:i}}
{ {
/* .id = */ "{{=it.cmd.parameterIds[i]}}", /* .id = */ "{{=p.id}}",
/* .out = */ {{=p.direction == "output" ? 1 : 0}}, /* .out = */ {{=p.direction == "output" ? 1 : 0}},
/* .def = */ {{=p.defaultValue.toExponential()}}f, /* .def = */ {{=p.defaultValue.toExponential()}}f,
/* .min = */ {{=p.minimum.toExponential()}}f, /* .min = */ {{=p.minimum.toExponential()}}f,
@ -83,3 +83,7 @@ static struct {
{{~}} {{~}}
}; };
#endif #endif
{{?it.product.state && it.product.state.dspCustom}}
#define STATE_DSP_CUSTOM
{{?}}

View File

@ -1,7 +1,7 @@
/* /*
* Tibia * 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 * Tibia is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -22,6 +22,7 @@
#include <stdint.h> #include <stdint.h>
#include "data.h" #include "data.h"
#include "plugin_api.h"
#include "plugin.h" #include "plugin.h"
#include <stdio.h> #include <stdio.h>
@ -161,6 +162,11 @@ float clampf(float x, float m, float M) {
} }
int main(int argc, char * argv[]) { int main(int argc, char * argv[]) {
#ifdef STATE_DSP_CUSTOM
(void)plugin_state_load;
(void)plugin_state_save;
#endif
#if PARAMETERS_N > 0 #if PARAMETERS_N > 0
for (size_t i = 0; i < PARAMETERS_N; i++) for (size_t i = 0; i < PARAMETERS_N; i++)
param_values[i] = param_data[i].def; 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]); printf(" %s: %g\n", param_data[i].id, param_values[i]);
#endif #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 #if PARAMETERS_N > 0
for (size_t i = 0; i < PARAMETERS_N; i++) for (size_t i = 0; i < PARAMETERS_N; i++)

View File

@ -18,22 +18,32 @@
# File author: Stefano D'Angelo # File author: Stefano D'Angelo
# #
TEMPLATE := daisy-seed
include vars.mk 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),.) -include $(MKINC_DIR)/vars-pre.mk
DATA_DIR := $(or $(DATA_DIR),.)
PLUGIN_DIR := $(or $(PLUGIN_DIR),src)
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} include $(SYSTEM_FILES_DIR)/Makefile
LDFLAGS += ${LDFLAGS_EXTRA}
CXXFLAGS += -I${DATA_DIR}/src -I${PLUGIN_DIR} ${CXXFLAGS_EXTRA} 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}} 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 : ""}} {{?(it.daisy_seed_make?.commonDir || it.make?.commonDir)}}
LDFLAGS_EXTRA := {{=it.make && it.make.ldflags ? it.make.ldflags : ""}} {{=it.daisy_seed_make.ldflags ? it.daisy_seed_make.ldflags : ""}} COMMON_DIR := {{=it.daisy_seed_make?.commonDir ?? (it.make?.commonDir ?? "")}}
CXXFLAGS_EXTRA := {{=it.make && it.make.cxxflags ? it.make.cxxflags : ""}} {{=it.daisy_seed_make.cxxflags ? it.daisy_seed_make.cxxflags : ""}} {{?}}
{{?(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 : ""}} {{?it.make?.extra}}
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 : ""}} {{=it.make.extra}}
{{?}}
COMMON_DIR := {{=it.daisy_seed_make && it.daisy_seed_make.commonDir ? it.daisy_seed_make.commonDir : (it.make && it.make.commonDir ? it.make.commonDir : "")}} {{?it.daisy_seed_make?.extra}}
DATA_DIR := {{=it.daisy_seed_make && it.daisy_seed_make.dataDir ? it.daisy_seed_make.dataDir : (it.make && it.make.dataDir ? it.make.dataDir : "")}} {{=it.daisy_seed_make.extra}}
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}}

View File

@ -1,7 +1,7 @@
/* /*
* Tibia * 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 * Tibia is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * 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
#endif #endif
{{?it.product.state && it.product.state.dspCustom}}
#define STATE_DSP_CUSTOM
{{?}}

View File

@ -1,7 +1,7 @@
/* /*
* Tibia * 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 * Tibia is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -22,6 +22,7 @@
#include <stdint.h> #include <stdint.h>
#include "data.h" #include "data.h"
#include "plugin_api.h"
#include "plugin.h" #include "plugin.h"
#include <string.h> #include <string.h>
@ -149,6 +150,11 @@ static void AudioCallback(
} }
int main() { int main() {
#ifdef STATE_DSP_CUSTOM
(void)plugin_state_load;
(void)plugin_state_save;
#endif
hardware.Configure(); hardware.Configure();
hardware.Init(); hardware.Init();
@ -168,7 +174,13 @@ int main() {
hardware.SetAudioBlockSize(BLOCK_SIZE); hardware.SetAudioBlockSize(BLOCK_SIZE);
float sample_rate = hardware.AudioSampleRate(); 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); plugin_set_sample_rate(&instance, sample_rate);
if (plugin_mem_req(&instance) != 0) if (plugin_mem_req(&instance) != 0)

View File

@ -18,35 +18,70 @@
# File author: Stefano D'Angelo # File author: Stefano D'Angelo
# #
TEMPLATE := ios
include vars.mk include vars.mk
COMMON_DIR := $(or $(COMMON_DIR),.) COMMON_DIR ?= .
DATA_DIR := $(or $(DATA_DIR),.) DATA_DIR ?= .
PLUGIN_DIR := $(or $(PLUGIN_DIR),src) 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 := \ SOURCES := \
${DATA_DIR}/src/data.h \ $(C_SRCS_EXTRA) \
${DATA_DIR}/src/index.html \ $(M_SRCS_EXTRA) \
${COMMON_DIR}/src/app.swift \ $(CXX_SRCS_EXTRA) \
${COMMON_DIR}/src/native.mm \ $(SRCS_EXTRA) \
${COMMON_DIR}/src/app-Bridging-Header.h \ $(DATA_DIR)/src/data.h \
${PLUGIN_DIR}/plugin.h \ $(COMMON_DIR)/src/app.swift \
${C_SRCS_EXTRA} \ $(COMMON_DIR)/src/native.mm \
${CXX_SRCS_EXTRA} \ $(COMMON_DIR)/src/app-Bridging-Header.h \
${SRCS_EXTRA} $(PLUGIN_DIR)/plugin.h \
SOURCES_OUT = $(addprefix build/gen/src/, $(notdir $(SOURCES))) $(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} DIRS := build build/gen build/gen/src build/gen/res
xcodegen generate --spec project.yml -r build/gen -p build/gen
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 $@ mkdir -p $@
clean: clean:
rm -fr build rm -fr build
-include $(MKINC_DIR)/rules-extra.mk
.PHONY: all clean .PHONY: all clean
.SECONDEXPANSION: .SECONDEXPANSION:
@ -55,3 +90,8 @@ PERCENT := %
$(SOURCES_OUT): build/gen/src/%: $$(filter $$(PERCENT)/%,$$(SOURCES)) | build/gen/src $(SOURCES_OUT): build/gen/src/%: $$(filter $$(PERCENT)/%,$$(SOURCES)) | build/gen/src
cp -R $^ $@ 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 type: application
sources: sources:
- path: src - path: src
- path: res
type: folder
{{?it.ios_make.dependencies}}
dependencies:
{{~it.ios_make.dependencies :d}}
- target: {{=d}}{{~}}
{{?}}
settings: settings:
base: base:
PRODUCT_BUNDLE_IDENTIFIER: {{=it.ios.productBundleIdentifier}} 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 SWIFT_OBJC_BRIDGING_HEADER: src/app-Bridging-Header.h
{{?it.ios_make.headerSearchPaths}} {{?it.ios_make.headerSearchPaths}}
HEADER_SEARCH_PATHS: {{~it.ios_make.headerSearchPaths :p}} HEADER_SEARCH_PATHS: {{~it.ios_make.headerSearchPaths :p}}

View File

@ -20,10 +20,25 @@
BUNDLE_NAME := {{=it.product.bundleName}} 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 : ""}} {{?(it.ios_make?.commonDir || it.make?.commonDir)}}
CXX_SRCS_EXTRA := {{=it.make && it.make.cxxSrcs ? it.make.cxxSrcs : ""}} {{=it.ios_make && it.ios_make.cxxSrcs ? it.ios_make.cxxSrcs : ""}} COMMON_DIR := {{=it.ios_make?.commonDir ?? (it.make?.commonDir ?? "")}}
SRCS_EXTRA := {{=it.ios_make && it.ios_make.srcsExtra ? it.ios_make.srcsExtra : ""}} {{?}}
{{?(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 : "")}} {{?it.make?.extra}}
DATA_DIR := {{=it.ios_make && it.ios_make.dataDir ? it.ios_make.dataDir : (it.make && it.make.dataDir ? it.make.dataDir : "")}} {{=it.make.extra}}
PLUGIN_DIR := {{=it.ios_make && it.ios_make.pluginDir ? it.ios_make.pluginDir : (it.make && it.make.pluginDir ? it.make.pluginDir : "")}} {{?}}
{{?it.ios_make?.extra}}
{{=it.ios_make.extra}}
{{?}}

View File

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

View File

@ -1,7 +1,7 @@
/* /*
* Tibia * 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 * Tibia is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -81,3 +81,7 @@ static struct {
{{~}} {{~}}
}; };
#endif #endif
{{?it.product.state && it.product.state.dspCustom}}
#define STATE_DSP_CUSTOM
{{?}}

View File

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

View File

@ -1,7 +1,7 @@
/* /*
* Tibia * 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 * Tibia is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -22,6 +22,7 @@
#include <stdint.h> #include <stdint.h>
#include "data.h" #include "data.h"
#include "plugin_api.h"
#include "plugin.h" #include "plugin.h"
#if PARAMETERS_N > 0 #if PARAMETERS_N > 0
#include <algorithm> #include <algorithm>
@ -189,6 +190,11 @@ void (^midiReceiveBlock)(const MIDIEventList *evtlist, void *srcConnRefCon) = ^(
extern "C" extern "C"
char audioStart() { 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 + NUM_CHANNELS_OUT > 0
# if NUM_CHANNELS_IN == 0 # if NUM_CHANNELS_IN == 0
deviceConfig = ma_device_config_init(ma_device_type_playback); deviceConfig = ma_device_config_init(ma_device_type_playback);
@ -251,9 +257,15 @@ char audioStart() {
} }
} }
#endif #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 #if PARAMETERS_N > 0
for (size_t i = 0; i < PARAMETERS_N; i++) { for (size_t i = 0; i < PARAMETERS_N; i++) {
if (!param_data[i].out) if (!param_data[i].out)
@ -356,7 +368,7 @@ void audioPause() {
extern "C" extern "C"
void audioResume() { void audioResume() {
// TODO: couldn't this could fail?... // TODO: could this fail?...
if (device_inited) { if (device_inited) {
ma_device_init(NULL, &deviceConfig, &device); ma_device_init(NULL, &deviceConfig, &device);
ma_device_start(&device); ma_device_start(&device);

View File

@ -1,7 +1,7 @@
# #
# Tibia # 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 # Tibia is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -18,98 +18,157 @@
# File author: Stefano D'Angelo # File author: Stefano D'Angelo
# #
TEMPLATE := lv2
include vars.mk 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) ifeq ($(OS), Windows_NT)
DLL_SUFFIX = .dll DLL_SUFFIX := .dll
LV2DIR = $(shell echo '${COMMONPROGRAMFILES}' | sed 's:\\:/:g')/LV2 UI_TYPE := WindowsUI
LV2DIR_USER = $(shell echo '${APPDATA}' | sed 's:\\:/:g')/LV2 LV2DIR := $(subst \,/,$(COMMONPROGRAMFILES))/LV2
CC = gcc LV2DIR_USER := $(subst \,/,$(APPDATA))/LV2
CXX = g++ CC := gcc
CXX := g++
else else
UNAME_S = $(shell uname -s) UNAME_S = $(shell uname -s)
ifeq ($(UNAME_S), Darwin) ifeq ($(UNAME_S), Darwin)
DLL_SUFFIX = .dylib DLL_SUFFIX := .dylib
LV2DIR = /Library/Audio/Plug-Ins/LV2 UI_TYPE := CocoaUI
LV2DIR_USER = ${HOME}/Library/Audio/Plug-Ins/LV2 LV2DIR := /Library/Audio/Plug-Ins/LV2
CC = clang LV2DIR_USER := $(HOME)/Library/Audio/Plug-Ins/LV2
CXX = clang++ CC := clang
CXX := clang++
else else
DLL_SUFFIX = .so DLL_SUFFIX := .so
PREFIX = /usr/local UI_TYPE := X11UI
LV2DIR = ${PREFIX}/lib/lv2 PREFIX := /usr/local
LV2DIR_USER = ${HOME}/.lv2 LV2DIR := $(PREFIX)/lib/lv2
CC = gcc LV2DIR_USER := $(HOME)/.lv2
CXX = g++ CC := gcc
CXX := g++
endif endif
endif endif
COMMON_DIR := $(or $(COMMON_DIR),.) CFLAGS := -O3 -Wall -Wpedantic -Wextra
DATA_DIR := $(or $(DATA_DIR),.) CFLAGS_ALL := -I$(DATA_DIR)/src -I$(PLUGIN_DIR) -I$(API_DIR) $(shell pkg-config --cflags lv2) -fPIC $(CFLAGS_EXTRA) $(CFLAGS)
PLUGIN_DIR := $(or $(PLUGIN_DIR),src)
CFLAGS = -O3 -Wall -Wpedantic -Wextra LDFLAGS :=
CFLAGS_ALL = -I${DATA_DIR}/src -I${PLUGIN_DIR} -fPIC ${CFLAGS} ${CFLAGS_EXTRA} LDFLAGS_ALL := -shared $(shell pkg-config --libs lv2) $(LDFLAGS_EXTRA) $(LDFLAGS)
LDFLAGS = CXXFLAGS := $(CFLAGS)
LDFLAGS_ALL = -shared ${LDFLAGS} ${LDFLAGS_EXTRA} CXXFLAGS_ALL := -I$(DATA_DIR)/src -I$(PLUGIN_DIR) -I$(API_DIR) -fPIC $(CXXFLAGS_EXTRA) $(CXXFLAGS)
CXXFLAGS = ${CFLAGS}
CXXFLAGS_ALL = -I${DATA_DIR}/src -I${PLUGIN_DIR} -fPIC ${CXXFLAGS} ${CXXFLAGS_EXTRA}
ifeq ($(UNAME_S), Darwin) ifeq ($(UNAME_S), Darwin)
CFLAGS_ALL := ${CFLAGS_ALL} -arch arm64 -arch x86_64 CFLAGS_ALL := $(CFLAGS_ALL) -arch arm64 -arch x86_64
LDFLAGS_ALL := ${LDFLAGS_ALL} -arch arm64 -arch x86_64 LDFLAGS_ALL := $(LDFLAGS_ALL) -arch arm64 -arch x86_64
CXXFLAGS_ALL := ${CXXFLAGS_ALL} -arch arm64 -arch x86_64 CXXFLAGS_ALL := $(CXXFLAGS_ALL) -arch arm64 -arch x86_64
endif 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} M_SRCS := $(M_SRCS_EXTRA)
C_OBJS = $(addprefix build/obj/, $(notdir $(C_SRCS:.c=.o))) M_OBJS := $(addprefix build/obj/, $(notdir $(M_SRCS:.m=.o)))
CXX_SRCS = ${CXX_SRCS_EXTRA} CXX_SRCS := $(CXX_SRCS_EXTRA)
CXX_OBJS = $(addprefix build/obj/, $(notdir $(CXX_SRCS:.cpp=.o))) 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} ALL := build/$(BUNDLE_DIR)/manifest.ttl build/$(BUNDLE_DIR)/$(DLL_FILE)
cat $^ | sed s/@DLL_SUFFIX@/${DLL_SUFFIX}/g > $@
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),) ifeq ($(CXX_OBJS),)
build/${BUNDLE_DIR}/${DLL_FILE}: ${C_OBJS} | build/${BUNDLE_DIR} build/$(BUNDLE_DIR)/$(DLL_FILE): $(C_OBJS) $(M_OBJS) | build/$(BUNDLE_DIR)
${CC} $^ -o $@ ${CFLAGS_ALL} ${LDFLAGS_ALL} $(CC) $^ -o $@ $(CFLAGS_ALL) $(LDFLAGS_ALL)
else else
build/${BUNDLE_DIR}/${DLL_FILE}: ${C_OBJS} ${CXX_OBJS} | build/${BUNDLE_DIR} build/$(BUNDLE_DIR)/$(DLL_FILE): $(C_OBJS) $(M_OBJS) $(CXX_OBJS) | build/$(BUNDLE_DIR)
${CXX} $^ -o $@ ${CFLAGS_ALL} ${CXXFLAGS_ALL} ${LDFLAGS_ALL} $(CXX) $^ -o $@ $(CFLAGS_ALL) $(CXXFLAGS_ALL) $(LDFLAGS_ALL)
endif endif
build/${BUNDLE_DIR} build/obj: $(DIRS):
mkdir -p $@ mkdir -p $@
clean: clean:
rm -fr build 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 install: all
mkdir -p -m 0755 "${LV2DIR}/${BUNDLE_DIR}" @for d in `find build/$(BUNDLE_DIR) -type d`; do \
install -m 0644 build/${BUNDLE_DIR}/manifest.ttl "${LV2DIR}/${BUNDLE_DIR}" d=`echo $$d | sed 's:^build/::'` ; \
install -m 0755 build/${BUNDLE_DIR}/${DLL_FILE} "${LV2DIR}/${BUNDLE_DIR}" 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 install-user: all
mkdir -p -m 0755 "${LV2DIR_USER}/${BUNDLE_DIR}" @for d in `find build/$(BUNDLE_DIR) -type d`; do \
install -m 0644 build/${BUNDLE_DIR}/manifest.ttl "${LV2DIR_USER}/${BUNDLE_DIR}" d=`echo $$d | sed 's:^build/::'` ; \
install -m 0755 build/${BUNDLE_DIR}/${DLL_FILE} "${LV2DIR_USER}/${BUNDLE_DIR}" 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: .SECONDEXPANSION:
PERCENT := % PERCENT := %
$(C_OBJS): build/obj/%.o: $$(filter $$(PERCENT)/$$(basename $$(notdir $$@)).c,$$(C_SRCS)) | build/obj $(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_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}} 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 : ""}} {{?(it.lv2_make?.commonDir || it.make?.commonDir)}}
LDFLAGS_EXTRA := {{=it.make && it.make.ldflags ? it.make.ldflags : ""}} {{=it.lv2_make && it.lv2_make.ldflags ? it.lv2_make.ldflags : ""}} COMMON_DIR := {{=it.lv2_make?.commonDir ?? (it.make?.commonDir ?? "")}}
CXXFLAGS_EXTRA := {{=it.make && it.make.cxxflags ? it.make.cxxflags : ""}} {{=it.lv2_make && it.lv2_make.cxxflags ? it.lv2_make.cxxflags : ""}} {{?}}
{{?(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 : ""}} {{?it.make?.extra}}
CXX_SRCS_EXTRA := {{=it.make && it.make.cxxSrcs ? it.make.cxxSrcs : ""}} {{=it.lv2_make && it.lv2_make.cxxSrcs ? it.lv2_make.cxxSrcs : ""}} {{=it.make.extra}}
{{?}}
COMMON_DIR := {{=it.lv2_make && it.lv2_make.commonDir ? it.lv2_make.commonDir : (it.make && it.make.commonDir ? it.make.commonDir : "")}} {{?it.lv2_make?.extra}}
DATA_DIR := {{=it.lv2_make && it.lv2_make.dataDir ? it.lv2_make.dataDir : (it.make && it.make.dataDir ? it.make.dataDir : "")}} {{=it.lv2_make.extra}}
PLUGIN_DIR := {{=it.lv2_make && it.lv2_make.pluginDir ? it.lv2_make.pluginDir : (it.make && it.make.pluginDir ? it.make.pluginDir : "")}} {{?}}

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 [ doap:maintainer [
a foaf:Organization ; a foaf:Organization ;
foaf:name "{{=it.company.name}}" ; 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)}} rdfs:seeAlso {{=it.tibia.lv2.ttlURI(it.company.url)}}
] ; ] ;
lv2:minorVersion {{=/^([0-9]+)\./.exec(it.lv2.version)[1]}} ; lv2:minorVersion {{=/^([0-9]+)\./.exec(it.lv2.version)[1]}} ;
lv2:microVersion {{=/^[0-9]+\.([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:requiredFeature urid:map ;
lv2:optionalFeature log:log ; {{??}}
lv2:optionalFeature urid:map ;
{{?}} {{?}}
lv2:optionalFeature log:log ;
lv2:optionalFeature lv2:hardRTCapable ; 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 [ lv2:port [
{{~it.tibia.lv2.ports :p:i}} {{~it.tibia.lv2.ports :p:i}}
{{?p.isBypass}} {{?p.isBypass}}
@ -33,7 +44,7 @@
lv2:designation lv2:enabled ; lv2:designation lv2:enabled ;
lv2:name "Enabled" ; lv2:name "Enabled" ;
lv2:shortName "Enabled" ; lv2:shortName "Enabled" ;
lv2:symbol "{{=p.symbol}}" ; lv2:symbol "enabled" ;
lv2:default 1 ; lv2:default 1 ;
lv2:minimum 0 ; lv2:minimum 0 ;
lv2:maximum 1 ; lv2:maximum 1 ;
@ -53,7 +64,7 @@
lv2:designation lv2:latency ; lv2:designation lv2:latency ;
lv2:name "Latency" ; lv2:name "Latency" ;
lv2:shortName "Latency" ; lv2:shortName "Latency" ;
lv2:symbol "{{=p.symbol}}" ; lv2:symbol "{{=p.id}}" ;
lv2:portProperty lv2:connectionOptional ; lv2:portProperty lv2:connectionOptional ;
lv2:portProperty lv2:integer ; lv2:portProperty lv2:integer ;
lv2:portProperty lv2:reportsLatency ; lv2:portProperty lv2:reportsLatency ;
@ -64,7 +75,7 @@
{{?"shortName" in p}} {{?"shortName" in p}}
lv2:shortName "{{=p.shortName.substring(0, 16)}}" ; lv2:shortName "{{=p.shortName.substring(0, 16)}}" ;
{{?}} {{?}}
lv2:symbol "{{=p.symbol}}" ; lv2:symbol "{{=p.id}}" ;
{{?p.type == "control"}} {{?p.type == "control"}}
lv2:minimum {{=p.minimum.toExponential()}} ; lv2:minimum {{=p.minimum.toExponential()}} ;
lv2:maximum {{=p.maximum.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 * 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 * Tibia is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * 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_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}} #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 #if DATA_PRODUCT_MIDI_INPUTS_N > 0
static uint32_t midi_in_index[DATA_PRODUCT_MIDI_INPUTS_N] = { 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}}, {{~}} {{~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}}, {{~}} {{~it.tibia.lv2.ports.filter(x => x.type == "control" && x.direction == "output") :p}}{{=p.paramIndex}}, {{~}}
}; };
#endif #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 * 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 * Tibia is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -22,26 +22,78 @@
#include <stdint.h> #include <stdint.h>
#include "data.h" #include "data.h"
#include "plugin_api.h"
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-function"
#include "plugin.h" #include "plugin.h"
#ifdef DATA_UI
# include "plugin_ui.h"
#endif
#pragma GCC diagnostic pop
#include "lv2/core/lv2.h" #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/core/lv2_util.h" #include <lv2/log/log.h>
#include "lv2/atom/util.h" #include <lv2/log/logger.h>
#include "lv2/atom/atom.h" #include <lv2/urid/urid.h>
#include "lv2/log/log.h" #if (DATA_PRODUCT_MIDI_INPUTS_N + DATA_PRODUCT_MIDI_OUTPUTS_N > 0) || defined(DATA_STATE_DSP_CUSTOM)
#include "lv2/log/logger.h" # include <lv2/atom/atom.h>
#include "lv2/midi/midi.h" # if DATA_PRODUCT_MIDI_INPUTS_N + DATA_PRODUCT_MIDI_OUTPUTS_N > 0
#include "lv2/urid/urid.h" # 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 #endif
#include <string.h>
#if defined(__i386__) || defined(__x86_64__) #if defined(__i386__) || defined(__x86_64__)
#include <xmmintrin.h> # include <xmmintrin.h>
#include <pmmintrin.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 #endif
typedef struct { typedef struct {
plugin p; plugin p;
float sample_rate;
#if DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N > 0 #if DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N > 0
const float * x[DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N]; const float * x[DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N];
#endif #endif
@ -59,26 +111,77 @@ typedef struct {
#endif #endif
#if DATA_PRODUCT_CONTROL_INPUTS_N > 0 #if DATA_PRODUCT_CONTROL_INPUTS_N > 0
float params[DATA_PRODUCT_CONTROL_INPUTS_N]; 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 #endif
void * mem; void * mem;
#if DATA_PRODUCT_MIDI_INPUTS_N + DATA_PRODUCT_MIDI_OUTPUT_N > 0 char * bundle_path;
LV2_URID_Map * map;
LV2_Log_Logger logger; 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; LV2_URID uri_midi_MidiEvent;
#endif #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; } 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) { static LV2_Handle instantiate(const struct LV2_Descriptor * descriptor, double sample_rate, const char * bundle_path, const LV2_Feature * const * features) {
(void)descriptor; (void)descriptor;
(void)bundle_path; (void)bundle_path;
plugin_instance *instance = malloc(sizeof(plugin_instance)); plugin_instance *instance = malloc(sizeof(plugin_instance));
if (instance == NULL) 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 // 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_LOG__log, &instance->logger.log, false,
LV2_URID__map, &instance->map, true, LV2_URID__map, &instance->map, true,
NULL); 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); lv2_log_logger_set_map(&instance->logger, instance->map);
if (missing) { if (missing) {
lv2_log_error(&instance->logger, "Missing feature <%s>\n", missing); lv2_log_error(&instance->logger, "Missing feature <%s>\n", missing);
free(instance); #ifdef DATA_PRODUCT_MIDI_REQUIRED
return NULL; goto err_urid;
#endif
} }
instance->uri_midi_MidiEvent = instance->map->map(instance->map->handle, LV2_MIDI__MidiEvent); #if DATA_PRODUCT_MIDI_INPUTS_N + DATA_PRODUCT_MIDI_OUTPUTS_N > 0
#else if (instance->map)
(void)features; 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 #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); size_t req = plugin_mem_req(&instance->p);
if (req != 0) { if (req != 0) {
instance->mem = malloc(req); instance->mem = malloc(req);
if (instance->mem == NULL) { if (instance->mem == NULL) {
plugin_fini(&instance->p); lv2_log_error(&instance->logger, "Not enough memory\n");
return NULL; goto err_mem;
} }
plugin_mem_set(&instance->p, instance->mem); plugin_mem_set(&instance->p, instance->mem);
} else } else
@ -131,6 +248,17 @@ static LV2_Handle instantiate(const struct LV2_Descriptor * descriptor, double s
#endif #endif
return instance; 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) { 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; 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]); 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 #endif
plugin_reset(&i->p); 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) { static void run(LV2_Handle instance, uint32_t sample_count) {
plugin_instance * i = (plugin_instance *)instance; plugin_instance * i = (plugin_instance *)instance;
@ -199,40 +332,65 @@ static void run(LV2_Handle instance, uint32_t sample_count) {
#endif #endif
#if DATA_PRODUCT_CONTROL_INPUTS_N > 0 #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++) { for (uint32_t j = 0; j < DATA_PRODUCT_CONTROL_INPUTS_N; j++) {
if (i->c[j] == NULL) if (i->c[j] == NULL)
continue; continue;
float v; float v = adjust_param(j, *i->c[j]);
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);
if (v != i->params[j]) { if (v != i->params[j]) {
i->params[j] = v; 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 #endif
// from https://lv2plug.in/book
#if DATA_PRODUCT_MIDI_INPUTS_N > 0 #if DATA_PRODUCT_MIDI_INPUTS_N > 0
for (size_t j = 0; j < DATA_PRODUCT_MIDI_INPUTS_N; j++) { // from https://lv2plug.in/book
if (i->x_midi[j] == NULL) if (i->map)
continue; for (size_t j = 0; j < DATA_PRODUCT_MIDI_INPUTS_N; j++) {
LV2_ATOM_SEQUENCE_FOREACH(i->x_midi[j], ev) { if (i->x_midi[j] == NULL)
if (ev->body.type == i->uri_midi_MidiEvent) { continue;
const uint8_t * data = (const uint8_t *)(ev + 1); LV2_ATOM_SEQUENCE_FOREACH(i->x_midi[j], ev) {
if ((data[0] & 0xf0) != 0xf0) if (ev->body.type == i->uri_midi_MidiEvent) {
plugin_midi_msg_in(&i->p, midi_in_index[j], data); 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 #endif
#if DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N > 0 #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_FLUSH_ZERO_MODE(flush_zero_mode);
_MM_SET_DENORMALS_ZERO_MODE(denormals_zero_mode); _MM_SET_DENORMALS_ZERO_MODE(denormals_zero_mode);
#endif #endif
} }
static void cleanup(LV2_Handle instance) { static void cleanup(LV2_Handle instance) {
@ -271,9 +428,70 @@ static void cleanup(LV2_Handle instance) {
plugin_fini(&i->p); plugin_fini(&i->p);
if (i->mem) if (i->mem)
free(i->mem); free(i->mem);
free(i->bundle_path);
free(instance); 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 = { static const LV2_Descriptor descriptor = {
/* .URI = */ DATA_LV2_URI, /* .URI = */ DATA_LV2_URI,
/* .instantiate = */ instantiate, /* .instantiate = */ instantiate,
@ -282,9 +500,181 @@ static const LV2_Descriptor descriptor = {
/* .run = */ run, /* .run = */ run,
/* .deactivate = */ NULL, /* .deactivate = */ NULL,
/* .cleanup = */ cleanup, /* .cleanup = */ cleanup,
#ifdef DATA_STATE_DSP_CUSTOM
/* .extension_data = */ extension_data
#else
/* .extension_data = */ NULL /* .extension_data = */ NULL
#endif
}; };
LV2_SYMBOL_EXPORT const LV2_Descriptor * lv2_descriptor(uint32_t index) { LV2_SYMBOL_EXPORT const LV2_Descriptor * lv2_descriptor(uint32_t index) {
return index == 0 ? &descriptor : NULL; 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 * 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 * Tibia is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * 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: "pprops", uri: "http://lv2plug.in/ns/ext/port-props#" },
{ id: "rdf", uri: "http://www.w3.org/1999/02/22-rdf-syntax-ns#" }, { 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: "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: "units", uri: "http://lv2plug.in/ns/extensions/units#" },
{ id: "urid", uri: "http://lv2plug.in/ns/ext/urid#" } { id: "urid", uri: "http://lv2plug.in/ns/ext/urid#" }
], ],
@ -66,7 +68,12 @@ module.exports = function (data, api, outputCommon, outputData) {
ports: [], ports: [],
ttlURI: function (uri) { 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) { expandURI: function (uri) {
@ -81,6 +88,8 @@ module.exports = function (data, api, outputCommon, outputData) {
for (var id in data.lv2.prefixes) 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: 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 buses = data.product.buses;
var audioPorts = []; var audioPorts = [];
var midiPorts = []; var midiPorts = [];
@ -88,20 +97,16 @@ module.exports = function (data, api, outputCommon, outputData) {
var b = buses[bi]; var b = buses[bi];
if (b.type == "audio") { if (b.type == "audio") {
if (b.channels == "mono") { 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 }; var e = { type: "audio", direction: b.direction, name: b.name, sidechain: b.sidechain, cv: b.cv, optional: b.optional, busIndex: bi, id: b.id };
e.symbol = data.lv2.busSymbols[bi];
audioPorts.push(e); audioPorts.push(e);
} else { } else {
var e = { type: "audio", direction: b.direction, name: b.name + " Left", shortName: b.shortName + " L", sidechain: b.sidechain, cv: b.cv, busIndex: bi }; 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" };
e.symbol = data.lv2.busSymbols[bi] + "_L";
audioPorts.push(e); 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 }; 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" };
e.symbol = data.lv2.busSymbols[bi] + "_R";
audioPorts.push(e); audioPorts.push(e);
} }
} else { } else {
var e = { type: "midi", direction: b.direction, name: b.name, sidechain: b.sidechain, control: b.control, optional: b.optional, busIndex: 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 };
e.symbol = data.lv2.busSymbols[bi];
midiPorts.push(e); midiPorts.push(e);
} }
} }
@ -115,7 +120,6 @@ module.exports = function (data, api, outputCommon, outputData) {
var p = data.product.parameters[i]; var p = data.product.parameters[i];
var e = Object.create(p); var e = Object.create(p);
e.type = "control"; e.type = "control";
e.symbol = data.lv2.parameterSymbols[i];
e.paramIndex = i; e.paramIndex = i;
ports.push(e); ports.push(e);
} }

View File

@ -1,7 +1,7 @@
# #
# Tibia # 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 # Tibia is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -18,119 +18,177 @@
# File author: Stefano D'Angelo # File author: Stefano D'Angelo
# #
TEMPLATE := vst3
include vars.mk 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) ifeq ($(OS), Windows_NT)
DLL_SUFFIX = .vst3 DLL_SUFFIX := .vst3
PLATFORM = x86_64-win VST3DIR := $(subst \,/,$(COMMONPROGRAMFILES))/VST3
VST3DIR = $(shell echo '${COMMONPROGRAMFILES}' | sed 's:\\:/:g')/VST3 VST3DIR_USER := $(subst \,/,$(LOCALAPPDATA))/Programs/Common/VST3
VST3DIR_USER = $(shell echo '${LOCALAPPDATA}' | sed 's:\\:/:g')/Programs/Common/VST3 CC := gcc
CC = gcc CXX := g++
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 else
UNAME_S = $(shell uname -s) UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S), Darwin) ifeq ($(UNAME_S), Darwin)
DLL_SUFFIX = DLL_SUFFIX :=
PLATFORM = MacOS VST3DIR := /Library/Audio/Plug-Ins/VST3
VST3DIR = /Library/Audio/Plug-Ins/VST3 VST3DIR_USER := $(HOME)/Library/Audio/Plug-Ins/VST3
VST3DIR_USER = ${HOME}/Library/Audio/Plug-Ins/VST3 CC := clang
CC = clang CXX := clang++
CXX = clang++ VST3_PLATFORM := MacOS
else else
DLL_SUFFIX = .so DLL_SUFFIX := .so
PLATFORM = $(shell uname -m)-linux VST3DIR := /usr/local/lib/vst3
VST3DIR = /usr/local/lib/vst3 VST3DIR_USER := $(HOME)/.vst3
VST3DIR_USER = ${HOME}/.vst3 CC := gcc
CC = gcc CXX := g++
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
endif endif
COMMON_DIR := $(or $(COMMON_DIR),.) ifeq ($(UNAME_S), Linux)
DATA_DIR := $(or $(DATA_DIR),.) CFLAGS_ALL := $(CFLAGS_ALL) -D_GNU_SOURCE
PLUGIN_DIR := $(or $(PLUGIN_DIR),src) ifeq ($(HAS_UI), yes)
CFLAGS_ALL := $(CFLAGS_ALL) $(shell pkg-config --cflags x11)
CFLAGS = -O3 -Wall -Wpedantic -Wextra LDFLAGS_ALL := $(LDFLAGS_ALL) $(shell pkg-config --libs x11)
CFLAGS_ALL = -I${DATA_DIR}/src -I${PLUGIN_DIR} -fPIC ${CFLAGS} ${CFLAGS_EXTRA} CXXFLAGS_ALL := $(CXXFLAGS_ALL) $(shell pkg-config --cflags x11)
endif
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
endif endif
BUNDLE_DIR = ${BUNDLE_NAME}.vst3 DLL_FILE := $(DLL_DIR)/$(BUNDLE_NAME)$(DLL_SUFFIX)
DLL_DIR = Contents/${PLATFORM} C_SRCS := $(COMMON_DIR)/src/vst3.c $(C_SRCS_EXTRA)
DLL_FILE = ${DLL_DIR}/${BUNDLE_NAME}${DLL_SUFFIX} C_OBJS := $(addprefix build/obj/, $(notdir $(C_SRCS:.c=.o)))
C_SRCS = ${COMMON_DIR}/src/vst3.c ${C_SRCS_EXTRA} M_SRCS := $(M_SRCS_EXTRA)
C_OBJS = $(addprefix build/obj/, $(notdir $(C_SRCS:.c=.o))) M_OBJS := $(addprefix build/obj/, $(notdir $(M_SRCS:.m=.o)))
CXX_SRCS = ${CXX_SRCS_EXTRA} CXX_SRCS := $(CXX_SRCS_EXTRA)
CXX_OBJS = $(addprefix build/obj/, $(notdir $(CXX_SRCS:.cpp=.o))) 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) ifeq ($(UNAME_S), Darwin)
all: build/${BUNDLE_DIR}/${DLL_FILE} build/${BUNDLE_DIR}/Contents/Info.plist ALL := $(ALL) build/$(BUNDLE_DIR)/Contents/Info.plist build/$(BUNDLE_DIR)/Contents/PkgInfo
build/${BUNDLE_DIR}/Contents/Info.plist: ${DATA_DIR}/data/Info.plist | build/${BUNDLE_DIR}/Contents
cp $^ $@
else
all: build/${BUNDLE_DIR}/${DLL_FILE}
endif 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),) ifeq ($(CXX_OBJS),)
build/${BUNDLE_DIR}/${DLL_FILE}: ${C_OBJS} | build/${BUNDLE_DIR}/${DLL_DIR} build/$(BUNDLE_DIR)/$(DLL_FILE): $(C_OBJS) $(M_OBJS) | build/$(BUNDLE_DIR)/$(DLL_DIR)
${CC} $^ -o $@ ${CFLAGS_ALL} ${LDFLAGS_ALL} $(CC) $^ -o $@ $(CFLAGS_ALL) $(LDFLAGS_ALL)
else else
build/${BUNDLE_DIR}/${DLL_FILE}: ${C_OBJS} ${CXX_OBJS} | build/${BUNDLE_DIR}/${DLL_DIR} build/$(BUNDLE_DIR)/$(DLL_FILE): $(C_OBJS) $(M_OBJS) $(CXX_OBJS) | build/$(BUNDLE_DIR)/$(DLL_DIR)
${CXX} $^ -o $@ ${CFLAGS_ALL} ${CXXFLAGS_ALL} ${LDFLAGS_ALL} $(CXX) $^ -o $@ $(CFLAGS_ALL) $(CXXFLAGS_ALL) $(LDFLAGS_ALL)
endif 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 $@ mkdir -p $@
clean: clean:
rm -fr build rm -fr build
ifeq ($(UNAME_S), Darwin) strip: $(STRIP_PREREQS)
strip build/$(BUNDLE_DIR)/$(DLL_FILE)
install: all install: all
mkdir -p -m 0755 "${VST3DIR}/${BUNDLE_DIR}/${DLL_DIR}" @for d in `find build/$(BUNDLE_DIR) -type d`; do \
install -m 0755 build/${BUNDLE_DIR}/${DLL_FILE} "${VST3DIR}/${BUNDLE_DIR}/${DLL_DIR}" d=`echo $$d | sed 's:^build/::'` ; \
install -m 0644 build/${BUNDLE_DIR}/Contents/Info.plist "${VST3DIR}/${BUNDLE_DIR}/Contents/Info.plist" 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 install-user: all
mkdir -p -m 0755 "${VST3DIR_USER}/${BUNDLE_DIR}/${DLL_DIR}" @for d in `find build/$(BUNDLE_DIR) -type d`; do \
install -m 0755 build/${BUNDLE_DIR}/${DLL_FILE} "${VST3DIR_USER}/${BUNDLE_DIR}/${DLL_DIR}" d=`echo $$d | sed 's:^build/::'` ; \
install -m 0644 build/${BUNDLE_DIR}/Contents/Info.plist "${VST3DIR_USER}/${BUNDLE_DIR}/Contents/Info.plist" 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 .PHONY: all clean strip $(STRIP_PHONY) install install-user
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
.SECONDEXPANSION: .SECONDEXPANSION:
PERCENT := % PERCENT := %
$(C_OBJS): build/obj/%.o: $$(filter $$(PERCENT)/$$(basename $$(notdir $$@)).c,$$(C_SRCS)) | build/obj $(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_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 # 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 # Tibia is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -20,13 +20,28 @@
BUNDLE_NAME := {{=it.product.bundleName}} 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 : ""}} {{?(it.vst3_make?.commonDir || it.make?.commonDir)}}
LDFLAGS_EXTRA := {{=it.make && it.make.ldflags ? it.make.ldflags : ""}} {{=it.vst3_make && it.vst3_make.ldflags ? it.vst3_make.ldflags : ""}} COMMON_DIR := {{=it.vst3_make?.commonDir ?? (it.make?.commonDir ?? "")}}
CXXFLAGS_EXTRA := {{=it.make && it.make.cxxflags ? it.make.cxxflags : ""}} {{=it.vst3_make && it.vst3_make.cxxflags ? it.vst3_make.cxxflags : ""}} {{?}}
{{?(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 : ""}} HAS_UI := {{=it.product.ui ? "yes" : "no"}}
CXX_SRCS_EXTRA := {{=it.make && it.make.cxxSrcs ? it.make.cxxSrcs : ""}} {{=it.vst3_make && it.vst3_make.cxxSrcs ? it.vst3_make.cxxSrcs : ""}} 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 : "")}} {{?it.make?.extra}}
DATA_DIR := {{=it.vst3_make && it.vst3_make.dataDir ? it.vst3_make.dataDir : (it.make && it.make.dataDir ? it.make.dataDir : "")}} {{=it.make.extra}}
PLUGIN_DIR := {{=it.vst3_make && it.vst3_make.pluginDir ? it.vst3_make.pluginDir : (it.make && it.make.pluginDir ? it.make.pluginDir : "")}} {{?}}
{{?it.vst3_make?.extra}}
{{=it.vst3_make.extra}}
{{?}}

View File

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

View File

@ -1,7 +1,7 @@
/* /*
* Tibia * 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 * Tibia is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * 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 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 }; 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" #define DATA_VST3_SDK_VERSION "VST 3.7.12"
static Steinberg_char16 dataVST3SDKVersionW[64] = { {{~Array.from("VST 3.7.9") :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 }; 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 }; 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 #endif
#define DATA_PRODUCT_PARAMETERS_N {{=it.product.parameters.filter(x => !x.isLatency).length}} #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 #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] = { 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}} {{~it.product.parameters.filter(x => !x.isLatency) :p:i}}
{ {
{{?p.isBypass}} {{?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 }, /* .title = */ { {{~Array.from("Bypass") :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .shortTitle = */ { {{~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 }, /* .units = */ { 0 },
@ -138,7 +140,7 @@ static struct Steinberg_Vst_ParameterInfo parameterInfo[DATA_PRODUCT_PARAMETERS_
/* .unitId = */ 0, /* .unitId = */ 0,
/* .flags = */ Steinberg_Vst_ParameterInfo_ParameterFlags_kIsBypass | Steinberg_Vst_ParameterInfo_ParameterFlags_kCanAutomate /* .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 }, /* .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 }, /* .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 }, /* .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}} {{~it.product.buses.filter(x => x.type == "midi" && x.direction == "input") :b:i}}
{ {
/* .id = */ {{=(it.tibia.vst3.sdbm(b.name + " Channel Pressure") & 0x7fffffff) >>> 0}}, /* .id = */ {{=(it.tibia.sdbm(b.id + " channel pressure") & 0x7fffffff) >>> 0}},
/* .title = */ { {{~Array.from(b.name + " Channel Pressure") :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}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") :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 }, /* .units = */ { 0 },
/* .stepCount = */ 0, /* .stepCount = */ 0,
/* .defaultNormalizedValue = */ 0.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 /* .flags = */ Steinberg_Vst_ParameterInfo_ParameterFlags_kIsHidden | Steinberg_Vst_ParameterInfo_ParameterFlags_kCanAutomate
}, },
{ {
/* .id = */ {{=(it.tibia.vst3.sdbm(b.name + " Pitch Bend") & 0x7fffffff) >>> 0}}, /* .id = */ {{=(it.tibia.sdbm(b.id + " pitch bend") & 0x7fffffff) >>> 0}},
/* .title = */ { {{~Array.from(b.name + " Pitch Bend") :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}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") :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 }, /* .units = */ { 0 },
/* .stepCount = */ 0, /* .stepCount = */ 0,
/* .defaultNormalizedValue = */ 0.5, /* .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 /* .flags = */ Steinberg_Vst_ParameterInfo_ParameterFlags_kIsHidden | Steinberg_Vst_ParameterInfo_ParameterFlags_kCanAutomate
}, },
{ {
/* .id = */ {{=(it.tibia.vst3.sdbm(b.name + " Mod Wheel") & 0x7fffffff) >>> 0}}, /* .id = */ {{=(it.tibia.sdbm(b.id + " mod wheel") & 0x7fffffff) >>> 0}},
/* .title = */ { {{~Array.from(b.name + " Mod Wheel") :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}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") :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 }, /* .units = */ { 0 },
/* .stepCount = */ 0, /* .stepCount = */ 0,
/* .defaultNormalizedValue = */ 0.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_INTEGER (1<<2)
# define DATA_PARAM_MAP_LOG (1<<3) # define DATA_PARAM_MAP_LOG (1<<3)
static struct { typedef struct {
size_t index; size_t index;
double min; double min;
double max; double max;
@ -198,8 +200,11 @@ static struct {
uint32_t flags; uint32_t flags;
double mapK; double mapK;
// scalePoints? // scalePoints?
} parameterData[DATA_PRODUCT_PARAMETERS_N] = { } ParameterData;
{{~it.product.parameters.filter(x => !x.isLatency) :p:i}}
# 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}}, /* .index = */ {{=p.paramIndex}},
/* .min = */ {{=p.minimum.toExponential()}}, /* .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
#endif #endif
{{?it.product.parameters.find(x => x.isLatency)}} {{?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 * You should have received a copy of the GNU General Public License
* along with Tibia. If not, see <http://www.gnu.org/licenses/>. * 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"); var path = require("path");
@ -49,20 +49,27 @@ module.exports = function (data, api, outputCommon, outputData) {
"pc": "%", "pc": "%",
"s": "s", "s": "s",
"semitone12TET": "semi" "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; 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.generateFileFromTemplateFile(`data${sep}Info.plist`, `data${sep}Info.plist`, data);
api.copyFile(`src${sep}vst3.c`, `src${sep}vst3.c`); api.copyFile(`src${sep}vst3.c`, `src${sep}vst3.c`);
api.generateFileFromTemplateFile(`src${sep}data.h`, `src${sep}data.h`, data); api.generateFileFromTemplateFile(`src${sep}data.h`, `src${sep}data.h`, data);

View File

@ -1,7 +1,7 @@
# #
# Tibia # 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 # Tibia is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -18,12 +18,16 @@
# File author: Stefano D'Angelo # 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 $^ $@ cp $^ $@
build/web/key.pem: build/web/cert.pem build/web/key.pem: build/web/cert.pem
build/web/cert.pem: | build 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 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> <head>
<title>{{=it.product.name}}</title> <title>{{=it.product.name}}</title>
<script type="module"> <script type="module">
import * as demo from "./{{=it.product.bundleName}}.js"; import * as demo from "./{{=it.product.bundleName}}/module.js";
window.demo = demo; window.demo = demo;
</script> </script>
<script> <script>
@ -212,7 +212,7 @@ window.addEventListener("load", function (e) {
module = new demo.Module(); module = new demo.Module();
if (!midi && hasMidiInput) if (!midi && hasMidiInput)
midi = await navigator.requestMIDIAccess(); 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 = new demo.Node(module);
node.connect(audioCtx.destination, audioOutputIndex); node.connect(audioCtx.destination, audioOutputIndex);

View File

@ -23,5 +23,6 @@ var sep = path.sep;
module.exports = function (data, api) { module.exports = function (data, api) {
api.generateFileFromTemplateFile(`src${sep}index.html`, `src${sep}index.html`, data); 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 # 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 # Tibia is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -18,19 +18,28 @@
# File author: Stefano D'Angelo # File author: Stefano D'Angelo
# #
TEMPLATE := web
include vars.mk include vars.mk
COMMON_DIR := $(or $(COMMON_DIR),.) COMMON_DIR ?= .
DATA_DIR := $(or $(DATA_DIR),.) DATA_DIR ?= .
PLUGIN_DIR := $(or $(PLUGIN_DIR),src) PLUGIN_DIR ?= src
API_DIR ?= $(PLUGIN_DIR)
MKINC_DIR ?= $(COMMON_DIR)
CC = clang BUILD_BIN_DIR := build/web
CXX = clang++ BUILD_DATA_DIR := build/web
CFLAGS = -Ofast -Wall -Wpedantic -Wextra -include $(MKINC_DIR)/vars-pre.mk
CFLAGS_ALL = -I${COMMON_DIR}/src -I${DATA_DIR}/src -I${PLUGIN_DIR} --target=wasm32 -flto -fvisibility=hidden ${CFLAGS} ${CFLAGS_EXTRA}
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,--allow-undefined \
-Wl,--no-entry \ -Wl,--no-entry \
-Wl,--lto-O3 \ -Wl,--lto-O3 \
@ -53,61 +62,75 @@ LDFLAGS_ALL = \
-Wl,--export=processor_set_parameter \ -Wl,--export=processor_set_parameter \
-Wl,-z,stack-size=$$((8*1024*1024)) \ -Wl,-z,stack-size=$$((8*1024*1024)) \
-nostdlib -nostdlib
ifeq (${HAS_MIDI_IN}, yes) ifeq ($(HAS_MIDI_IN), yes)
LDFLAGS_ALL += -Wl,--export=processor_midi_msg_in LDFLAGS_ALL := $(LDFLAGS_ALL) -Wl,--export=processor_midi_msg_in
endif endif
LDFLAGS_ALL += ${LDFLAGS} ${LDFLAGS_EXTRA} LDFLAGS_ALL := $(LDFLAGS_ALL) $(LDFLAGS_EXTRA) $(LDFLAGS)
CXXFLAGS = ${CFLAGS} CXXFLAGS := $(CFLAGS)
CXXFLAGS_ALL = -I${COMMON_DIR}/src -I${DATA_DIR}/src -I${PLUGIN_DIR} --target=wasm32 -flto -fvisibility=hidden ${CXXFLAGS} ${CXXFLAGS_EXTRA} 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_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_OBJS := $(addprefix build/obj/, $(notdir $(C_SRCS:.c=.o)))
ifeq ($(CXX_SRCS_EXTRA),) ifeq ($(CXX_SRCS_EXTRA),)
CXX_SRCS = CXX_SRCS :=
CXX_OBJS = CXX_OBJS :=
else else
CXX_SRCS = ${COMMON_DIR}/src/new.cpp ${CXX_SRCS_EXTRA} CXX_SRCS := $(COMMON_DIR)/src/new.cpp $(CXX_SRCS_EXTRA)
CXX_OBJS = $(addprefix build/obj/, $(notdir $(CXX_SRCS:.cpp=.o))) CXX_OBJS := $(addprefix build/obj/, $(notdir $(CXX_SRCS:.cpp=.o)))
endif 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),) ifeq ($(CXX_OBJS),)
build/web/${BUNDLE_NAME}.wasm: ${C_OBJS} | build/web build/web/$(BUNDLE_NAME)/module.wasm: $(C_OBJS) | build/web/$(BUNDLE_NAME)
${CC} $^ -o $@ ${CFLAGS_ALL} ${LDFLAGS_ALL} $(CC) $^ -o $@ $(CFLAGS_ALL) $(LDFLAGS_ALL)
else else
build/web/${BUNDLE_NAME}.wasm: ${C_OBJS} ${CXX_OBJS} | build/web build/web/$(BUNDLE_NAME)/module.wasm: $(C_OBJS) $(CXX_OBJS) | build/web/$(BUNDLE_NAME)
${CXX} $^ -o $@ ${CFLAGS_ALL} ${CXXFLAGS_ALL} ${LDFLAGS_ALL} $(CXX) $^ -o $@ $(CFLAGS_ALL) $(CXXFLAGS_ALL) $(LDFLAGS_ALL)
endif 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 $^ $@ 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 $^ $@ cp $^ $@
build/web build/obj: $(DIRS):
mkdir -p $@ mkdir -p $@
clean: clean:
rm -fr build 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: .SECONDEXPANSION:
PERCENT := % PERCENT := %
$(C_OBJS): build/obj/%.o: $$(filter $$(PERCENT)/$$(basename $$(notdir $$@)).c,$$(C_SRCS)) | build/obj $(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_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}} 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 : ""}} {{?(it.web_make?.commonDir || it.make?.commonDir)}}
LDFLAGS_EXTRA := {{=it.make && it.make.ldflags ? it.make.ldflags : ""}} {{=it.web_make && it.web_make.ldflags ? it.web_make.ldflags : ""}} COMMON_DIR := {{=it.web_make?.commonDir ?? (it.make?.commonDir ?? "")}}
CXXFLAGS_EXTRA := {{=it.make && it.make.cxxflags ? it.make.cxxflags : ""}} {{=it.web_make && it.web_make.cxxflags ? it.web_make.cxxflags : ""}} {{?}}
{{?(it.web_make?.dataDir || it.make?.dataDir)}}
C_SRCS_EXTRA := {{=it.make && it.make.cSrcs ? it.make.cSrcs : ""}} {{=it.web_make && it.web_make.cSrcs ? it.web_make.cSrcs : ""}} DATA_DIR := {{=it.web_make?.dataDir ?? (it.make?.dataDir ?? "")}}
CXX_SRCS_EXTRA := {{=it.make && it.make.cxxSrcs ? it.make.cxxSrcs : ""}} {{=it.web_make && it.web_make.cxxSrcs ? it.web_make.cxxSrcs : ""}} {{?}}
{{?(it.web_make?.pluginDir || it.make?.pluginDir)}}
COMMON_DIR := {{=it.web_make && it.web_make.commonDir ? it.web_make.commonDir : (it.make && it.make.commonDir ? it.make.commonDir : "")}} PLUGIN_DIR := {{=it.web_make?.pluginDir ?? (it.make?.pluginDir ?? "")}}
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?.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"}} 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 * 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 * Tibia is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * 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}}, {{?}}{{~}} {{~it.product.parameters :p:i}}{{?p.direction == "output"}}{{=i}}, {{?}}{{~}}
}; };
#endif #endif
{{?it.product.state && it.product.state.dspCustom}}
#define DATA_STATE_DSP_CUSTOM
{{?}}

View File

@ -1,7 +1,7 @@
/* /*
* Tibia * 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 * Tibia is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -22,6 +22,7 @@
#include <stdint.h> #include <stdint.h>
#include "data.h" #include "data.h"
#include "plugin_api.h"
#include "plugin.h" #include "plugin.h"
#include "string.h" #include "string.h"
@ -45,11 +46,22 @@ typedef struct {
} instance; } instance;
instance * processor_new(float sample_rate) { 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)); instance * i = malloc(sizeof(instance));
if (i == NULL) if (i == NULL)
return 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 #if DATA_PRODUCT_PARAMETERS_N > 0
for (size_t j = 0; j < DATA_PRODUCT_PARAMETERS_N; j++) 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": { "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": { "ios_make": {
"headerSearchPaths": [ "../../../../../miniaudio" ], "headerSearchPaths": [ "../../../../../miniaudio" ],
"deploymentTarget": 16.6 "deploymentTarget": "16.6"
} }
} }

View File

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

View File

@ -1,7 +1,7 @@
/* /*
* Tibia * 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 * Tibia is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -18,24 +18,27 @@
* File author: Stefano D'Angelo * File author: Stefano D'Angelo
*/ */
#include <stdint.h>
typedef struct plugin { typedef struct plugin {
float sample_rate; float sample_rate;
size_t delay_line_length; size_t delay_line_length;
float gain; float gain;
float delay; float delay;
float cutoff; float cutoff;
char bypass; char bypass;
float * delay_line; float * delay_line;
size_t delay_line_cur; size_t delay_line_cur;
float z1; float z1;
float cutoff_k; float cutoff_k;
float yz1; float yz1;
} plugin; } plugin;
static void plugin_init(plugin *instance) { static void plugin_init(plugin *instance, const plugin_callbacks *cbs) {
(void)instance; (void)instance;
(void)cbs;
} }
static void plugin_fini(plugin *instance) { 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) { static void plugin_set_parameter(plugin *instance, size_t index, float value) {
switch (index) { switch (index) {
case 0: case plugin_parameter_gain:
//approx instance->gain = powf(10.f, 0.05f * value); instance->gain = value;
instance->gain = ((2.6039890429412597e-4f * value + 0.032131027163547855f) * value + 1.f) / ((0.0012705124328080768f * value - 0.0666763481312185f) * value + 1.f);
break; break;
case 1: case plugin_parameter_delay:
instance->delay = 0.001f * value; instance->delay = value;
break; break;
case 2: case plugin_parameter_cutoff:
instance->cutoff = value; instance->cutoff = value;
break; break;
case 3: case plugin_parameter_bypass:
instance->bypass = value >= 0.5f; instance->bypass = value >= 0.5f;
break; 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) { 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); //approx const float gain = powf(10.f, 0.05f * instance->gain);
size_t delay = (size_t)(instance->sample_rate * instance->delay + 0.5f); 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); 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++) { for (size_t i = 0; i < n_samples; i++) {
instance->delay_line[instance->delay_line_cur] = inputs[0][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; instance->delay_line_cur = 0;
const float y = x + mA1 * (instance->z1 - x); const float y = x + mA1 * (instance->z1 - x);
instance->z1 = y; 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]; 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)); //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); 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", "channels": "mono",
"name": "Input", "name": "Input",
"shortName": "Input", "shortName": "Input",
"id": "input",
"sidechain": false, "sidechain": false,
"cv": false, "cv": false,
"optional": false "optional": false
@ -21,6 +22,7 @@
"channels": "mono", "channels": "mono",
"name": "Output", "name": "Output",
"shortName": "Output", "shortName": "Output",
"id": "output",
"sidechain": false, "sidechain": false,
"cv": false, "cv": false,
"optional": false "optional": false
@ -30,6 +32,7 @@
"direction": "input", "direction": "input",
"name": "MIDI input", "name": "MIDI input",
"shortName": "MIDI input", "shortName": "MIDI input",
"id": "midi_in",
"sidechain": true, "sidechain": true,
"control": true, "control": true,
"optional": true "optional": true
@ -39,6 +42,7 @@
{ {
"name": "Gain", "name": "Gain",
"shortName": "Gain", "shortName": "Gain",
"id": "gain",
"direction": "input", "direction": "input",
"isBypass": false, "isBypass": false,
"isLatency": false, "isLatency": false,
@ -60,6 +64,7 @@
{ {
"name": "Delay", "name": "Delay",
"shortName": "Delay", "shortName": "Delay",
"id": "delay",
"direction": "input", "direction": "input",
"isBypass": false, "isBypass": false,
"isLatency": false, "isLatency": false,
@ -76,6 +81,7 @@
{ {
"name": "Cutoff", "name": "Cutoff",
"shortName": "Cutoff", "shortName": "Cutoff",
"id": "cutoff",
"direction": "input", "direction": "input",
"isBypass": false, "isBypass": false,
"isLatency": false, "isLatency": false,
@ -92,6 +98,7 @@
{ {
"name": "Bypass", "name": "Bypass",
"shortName": "Bypass", "shortName": "Bypass",
"id": "bypass",
"direction": "input", "direction": "input",
"isBypass": true, "isBypass": true,
"isLatency": false, "isLatency": false,
@ -112,6 +119,7 @@
{ {
"name": "yz1", "name": "yz1",
"shortName": "yz1", "shortName": "yz1",
"id": "yz1",
"direction": "output", "direction": "output",
"isBypass": false, "isBypass": false,
"isLatency": false, "isLatency": false,
@ -125,6 +133,13 @@
"unit": "", "unit": "",
"map": "linear" "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 #!/bin/sh
dir=`dirname $0` 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/../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 $dir/../tibia $dir/product.json,$dir/company.json,$dir/vst3.json $dir/../templates/vst3-make $dir/../out/vst3
cp $dir/plugin.h $dir/../out/vst3/src 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 $dir/../out/lv2
$dir/../tibia $dir/product.json,$dir/company.json,$dir/lv2.json $dir/../templates/lv2-make $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 $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-make $dir/../out/web
$dir/../tibia $dir/product.json,$dir/company.json $dir/../templates/web-demo $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/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/../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/keystore.jks $dir/../out/android
cp $dir/plugin.h $dir/../out/android/src 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/../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 $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/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/../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-make $dir/../out/cmd
cp $dir/plugin.h $dir/../out/cmd/src 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/../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/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": { "controller": {
"cid": "ecf4f431312f44fbb37b88c545ae9993" "cid": "ecf4f431312f44fbb37b88c545ae9993"
}, },
"subCategory": "FX" "subCategory": "Fx"
} }
} }

47
tibia
View File

@ -2,37 +2,56 @@
function usage() { function usage() {
console.log("Usage:"); console.log("Usage:");
console.log(" tibia 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"); console.log(" tibia --common template outDirectory [override1=value1] [override2=value2] ...");
console.log(" tibia --data file1.json,file2.json,...filen.json template outDirectory"); console.log(" tibia --data file1.json,file2.json,...filen.json template outDirectory [override1=value1] [override2=value2] ...");
process.exit(1); process.exit(1);
} }
var jsonFiles, template, outputDir, outputCommon, outputData; var jsonFiles, template, outputDir, outputCommon, outputData, i;
if (process.argv[2] == "--common") { if (process.argv[2] == "--common") {
if (process.argv.length != 5) if (process.argv.length < 5)
usage(); usage();
jsonFiles = []; jsonFiles = [];
template = process.argv[3]; template = process.argv[3];
outputDir = process.argv[4]; outputDir = process.argv[4];
outputCommon = true; outputCommon = true;
outputData = false; outputData = false;
i = 5;
} else if (process.argv[2] == "--data") { } else if (process.argv[2] == "--data") {
if (process.argv.length != 6) if (process.argv.length < 6)
usage(); usage();
jsonFiles = process.argv[3].split(","); jsonFiles = process.argv[3].split(",");
template = process.argv[4]; template = process.argv[4];
outputDir = process.argv[5]; outputDir = process.argv[5];
outputCommon = false; outputCommon = false;
outputData = true; outputData = true;
i = 6;
} else { } else {
if (process.argv.length != 5) if (process.argv.length < 5)
usage(); usage();
jsonFiles = process.argv[2].split(","); jsonFiles = process.argv[2].split(",");
template = process.argv[3]; template = process.argv[3];
outputDir = process.argv[4]; outputDir = process.argv[4];
outputCommon = true; outputCommon = true;
outputData = 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"); var fs = require("fs");
@ -54,6 +73,13 @@ for (var i = 0; i < jsonFiles.length; i++) {
for (var k in d) for (var k in d)
data[k] = d[k]; 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) { if (outputData) {
@ -145,6 +171,13 @@ data.tibia = {
} }
} }
return s; 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;
} }
}; };