Compare commits

..

102 Commits

Author SHA1 Message Date
9310b08670 necessary cleaning of single comment 2025-07-25 08:20:00 +02:00
Stefano D'Angelo
59838dda25 made lv2 template and test plugin c++ friendly 2025-07-18 17:33:23 +02:00
1e5d2b49da add extra fields to product.json for external templates 2025-07-18 11:39:44 +02:00
69cff1ebab vst3 typo bug fix 2025-07-08 15:15:23 +02:00
Stefano D'Angelo
50118f66a4 fix ios issues 2025-06-08 08:57:35 +02:00
Stefano D'Angelo
6695731d09 update copyright headers 2025-06-05 08:22:52 +02:00
Stefano D'Angelo
9a86ef2de8 use shell pipefail in makefiles 2025-06-05 08:21:03 +02:00
bb9d68c633 extended file copy/generation api to allow chmod 2025-06-04 11:33:36 +02:00
Stefano D'Angelo
e7e65a6dc6 test buildVersion 0 2025-06-02 19:15:46 +02:00
Stefano D'Angelo
a350588115 update vst3 sdk version 2025-06-02 17:58:58 +02:00
Stefano D'Angelo
245ae36cf4 fix copyright headers 2025-06-01 22:29:21 +02:00
41c0b3e704 fix vst3 factoryCreateInstance 2025-05-28 12:01:34 +02:00
Stefano D'Angelo
b8469d8b86 exit code 1 from shell loops on error in Makefiles 2025-05-22 16:29:15 +02:00
Stefano D'Angelo
f0fd47bc27 using -fvisibility=hidden everywhere 2025-05-22 16:07:48 +02:00
e3a39dbb6e introduced $(PHONY) and fix strip on macos 2025-05-22 15:41:49 +02:00
31a0e8925e add cast to make c++ happy 2025-05-14 18:09:36 +02:00
Stefano D'Angelo
f507660841 bundle name tibia_test -> tibia-test 2025-05-14 11:27:09 +02:00
Stefano D'Angelo
063f7122b9 lv2 ui uris are now custom-defined 2025-05-13 16:21:32 +02:00
Stefano D'Angelo
d78a375c95 removed lv2-next 2025-05-13 16:16:48 +02:00
Stefano D'Angelo
5b46c45f4a added .DELETE_ON_ERROR and ensured proper cleanup on error 2025-05-12 07:44:28 +02:00
Stefano D'Angelo
db77378776 api: define templateDir and drop getAllFiles 2025-05-11 17:36:37 +02:00
Stefano D'Angelo
931cdd922a update notes 2025-05-11 10:29:24 +02:00
Stefano D'Angelo
a905009db0 makefile vars added to notes 2025-05-11 10:22:45 +02:00
Stefano D'Angelo
3448832862 update copyright notice in web-demo 2025-05-09 15:32:44 +02:00
Stefano D'Angelo
8eaf255762 let javascript module only export buses and parameters 2025-05-09 15:32:10 +02:00
fb767d9bbf fix vst3 bindir datadir on windows 2025-04-21 09:49:12 +02:00
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
69 changed files with 2738 additions and 1738 deletions

View File

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

28
notes
View File

@ -84,7 +84,7 @@ product {
web: AudioWorkletNode.{numberOfInputs,numberOfOutputs,outputChannelCount} - lots of implications
cmd: lots of places
android: lots of places
ios: data.h, index.html
ios: lots of places
type:
"audio" or "midi", required
VST3: BusInfo mediaType, ParameterInfo (channel pressure, pitch bend params) - lots of implications
@ -92,7 +92,7 @@ product {
web: AudioWorkletNode.{numberOfInputs,numberOfOutputs,outputChannelCount} - lots of implications
cmd: lots of places
android: lots of places
ios: data.h, index.html
ios: lots of places
channels:
"mono" or "stereo", audio type only, required
VST3: BusInfo channelCount, plugin get/set bus arrangements
@ -100,7 +100,7 @@ product {
web: AudioWorkletNode.outputChannelCount - lots of implications
cmd: lots of places
android: lots of places
ios: data.h
ios: lots of places
sidechain:
bus is not part of main audio path (sidechain)? boolean, default false
VST3: BusInfo busType
@ -108,7 +108,6 @@ product {
web: web-demo choice of audio I/O buses
cmd: choice of audio I/O buses
android: choice of audio I/O buses
ios: choice of audio I/O buses
cv:
bus is control voltage audio-rate? boolean, audio type only, default false
VST3: BusInfo flags
@ -168,7 +167,7 @@ product {
web: AudioWorkletProcessor.parameterDescriptors, web-demo <range> readonly/input listener - lots of implications
cmd: lots of places
android: lots of places
ios: data.h, index.html
ios: lots of places
isBypass:
parameter is bypass/enabled? boolean - lots of implications, default false
VST3: ParameterInfo, controller get/set parameter/state
@ -183,16 +182,8 @@ product {
LV2: manifest.ttl lv2:port, TBD round output value
web: not (yet) used
cmd: not (yet) used
ios: not used
android: not (yet) used
isCpumeter:
parameter is output cpu meter? boolean. It must be an output parameter. It is handled within the wrappers, and not by the plugin user code
VST3: data.h, vst3.c
LV2: data.h, lv2.c
web: processor.js
cmd: TODO
android: data.h jni.cpp
ios: data.h native.mm
ios: not (yet) used
defaultValue:
default value, number, mapped, required for non-bypass
VST3: ParameterInfo defaultNormalizedValue, controller initialize
@ -277,3 +268,12 @@ product {
}
]
}
makefile dirs:
- make always executed from Makefile directory
- COMMON_DIR: static common sources (e.g., src/lv2.c)
- DATA_DIR: generated sources/data (e.g., src/data.h, data/manifest.ttl.in)
- PLUGIN_DIR: plugin sources (e.g., plugin.h)
- API_DIR: generated Tibia-related API sources (e.g., plugin_api.h)
- MKINC_DIR: Makefile includes (e.g., vars-pre.mk)
- build: build files

View File

@ -1,7 +1,7 @@
#
# Tibia
#
# Copyright (C) 2023, 2024 Orastron Srl unipersonale
# Copyright (C) 2023-2025 Orastron Srl unipersonale
#
# Tibia is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@ -18,23 +18,60 @@
# File author: Stefano D'Angelo
#
SHELL := bash -o pipefail
TEMPLATE := android
include vars.mk
COMMON_DIR ?= .
DATA_DIR ?= .
PLUGIN_DIR ?= src
API_DIR ?= $(PLUGIN_DIR)
MKINC_DIR ?= $(COMMON_DIR)
BUILD_BIN_DIR := build/apk/lib/armeabi-v7a
BUILD_DATA_DIR := build/assets/index.html
include $(MKINC_DIR)/vars-pre.mk
ifeq ($(HAS_MIDI_IN), yes)
MIN_API := 29
MIN_API := 29
else
MIN_API := 26
MIN_API := 26
endif
CC := $(ANDROID_NDK_DIR)/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi$(MIN_API)-clang
CXX := $(ANDROID_NDK_DIR)/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi$(MIN_API)-clang++
NDK_DIR := $(SDK_DIR)/ndk/$(NDK_VERSION)
BUILD_TOOLS_DIR := $(SDK_DIR)/build-tools/$(BUILD_TOOLS_VERSION)
ANDROID_JAR_FILE := $(SDK_DIR)/platforms/android-$(ANDROID_VERSION)/android.jar
ANDROIDX_CORE_FILE := $(ANDROIDX_DIR)/core-$(ANDROIDX_CORE_VERSION).jar
ANDROIDX_LIFECYCLE_COMMON_FILE := $(ANDROIDX_DIR)/lifecycle-common-$(ANDROIDX_LIFECYCLE_COMMON_VERSION).jar
ANDROIDX_VERSIONEDPARCELABLE_FILE := $(ANDROIDX_DIR)/versionedparcelable-$(ANDROIDX_VERSIONEDPARCELABLE_VERSION).jar
KOTLIN_STDLIB_FILE := $(KOTLIN_DIR)/kotlin-stdlib-$(KOTLIN_STDLIB_VERSION).jar
KOTLINX_COROUTINES_CORE_FILE := $(KOTLIN_DIR)/kotlinx-coroutines-core-$(KOTLINX_COROUTINES_CORE_VERSION).jar
KOTLINX_COROUTINES_CORE_JVM_FILE := $(KOTLIN_DIR)/kotlinx-coroutines-core-jvm-$(KOTLINX_COROUTINES_CORE_JVM_VERSION).jar
ifeq ($(OS), Windows_NT)
NDK_BIN_DIR ?= $(NDK_DIR)/toolchains/llvm/prebuilt/windows-x86_64/bin/
else
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S), Darwin)
NDK_BIN_DIR ?= $(NDK_DIR)/toolchains/llvm/prebuilt/darwin-x86_64/bin/
else
NDK_BIN_DIR ?= $(NDK_DIR)/toolchains/llvm/prebuilt/linux-x86_64/bin/
endif
endif
CC := $(NDK_BIN_DIR)/armv7a-linux-androideabi$(MIN_API)-clang
CXX := $(NDK_BIN_DIR)/armv7a-linux-androideabi$(MIN_API)-clang++
JC := javac
APKSIGNER := $(BUILD_TOOLS_DIR)/apksigner
ZIPALIGN := $(BUILD_TOOLS_DIR)/zipalign
AAPT := $(BUILD_TOOLS_DIR)/aapt
D8 := $(BUILD_TOOLS_DIR)/d8
ADB := $(ANDROID_SDK_DIR)/platform-tools/adb
ADB := $(SDK_DIR)/platform-tools/adb
JARS := \
$(ANDROID_JAR_FILE) \
@ -54,25 +91,21 @@ ifeq ($(HAS_MIDI_IN), yes)
CLASSES += MainActivity$$WebAppInterface$$MidiDeviceCallback MainActivity$$WebAppInterface$$1
endif
COMMON_DIR := $(or $(COMMON_DIR),.)
DATA_DIR := $(or $(DATA_DIR),.)
PLUGIN_DIR := $(or $(PLUGIN_DIR),src)
CFLAGS := -O3 -Wall -Wpedantic -Wextra
CFLAGS_ALL := -I$(DATA_DIR)/src -I$(PLUGIN_DIR) -fPIC $(CFLAGS) $(CFLAGS_EXTRA)
CFLAGS_ALL := -I$(DATA_DIR)/src -I$(PLUGIN_DIR) -I$(API_DIR) -fPIC -fvisibility=hidden $(CFLAGS_EXTRA) $(CFLAGS)
CXXFLAGS := $(CFLAGS)
CXXFLAGS_ALL := -I$(DATA_DIR)/src -I$(PLUGIN_DIR) -fPIC -std=c++11 $(CXXFLAGS) $(CXXFLAGS_EXTRA)
CXXFLAGS_ALL := -I$(DATA_DIR)/src -I$(PLUGIN_DIR) -I$(API_DIR) -fPIC -fvisibility=hidden -std=c++11 $(CXXFLAGS_EXTRA) $(CXXFLAGS)
LDFLAGS :=
LDFLAGS_ALL := -shared -static-libstdc++ -landroid
ifeq ($(HAS_MIDI_IN), yes)
LDFLAGS += -lamidi
endif
LDFLAGS_ALL += $(LDFLAGS) $(LDFLAGS_EXTRA)
LDFLAGS_ALL += $(LDFLAGS_EXTRA) $(LDFLAGS)
JFLAGS :=
JFLAGS_ALL := $(JFLAGS) $(JFLAGS_EXTRA)
JFLAGS_ALL := $(JFLAGS_EXTRA) $(JFLAGS)
C_SRCS := $(C_SRCS_EXTRA)
C_OBJS := $(addprefix build/obj/, $(notdir $(C_SRCS:.c=.o)))
@ -80,9 +113,13 @@ C_OBJS := $(addprefix build/obj/, $(notdir $(C_SRCS:.c=.o)))
CXX_SRCS := $(COMMON_DIR)/src/jni.cpp $(CXX_SRCS_EXTRA)
CXX_OBJS := $(addprefix build/obj/, $(notdir $(CXX_SRCS:.cpp=.o)))
DIRS := build build/gen build/apk build/obj build/apk/lib build/apk/lib/armeabi-v7a build/assets
ALL := build/$(BUNDLE_NAME).apk
-include $(COMMON_DIR)/vars-extra.mk
PHONY := all clean install
-include $(MKINC_DIR)/vars-extra.mk
all: $(ALL)
@ -111,7 +148,7 @@ build/apk/lib/armeabi-v7a/lib$(BUNDLE_NAME).so: $(C_OBJS) $(CXX_OBJS) | build/ap
build/assets/index.html: $(DATA_DIR)/src/index.html | build/assets
cp $^ $@
build/gen build/apk build/obj build/apk/lib/armeabi-v7a build/assets:
$(DIRS):
mkdir -p $@
clean:
@ -121,9 +158,9 @@ install: build/$(BUNDLE_NAME).apk
[ -n "`$(ADB) shell pm list packages | grep ^package:$(JAVA_PACKAGE_NAME)`" ] && $(ADB) uninstall $(JAVA_PACKAGE_NAME); exit 0
$(ADB) install $^
-include $(COMMON_DIR)/rules-extra.mk
-include $(MKINC_DIR)/rules-extra.mk
.PHONY: all clean install
.PHONY: $(PHONY)
.SECONDEXPANSION:
@ -135,4 +172,6 @@ $(C_OBJS): build/obj/%.o: $$(filter $$(PERCENT)/$$(basename $$(notdir $$@)).c,$$
$(CXX_OBJS): build/obj/%.o: $$(filter $$(PERCENT)/$$(basename $$(notdir $$@)).cpp,$$(CXX_SRCS)) | build/obj
$(CXX) $^ -o $@ -c $(CXXFLAGS_ALL)
-include $(COMMON_DIR)/rules-secondexp-extra.mk
-include $(MKINC_DIR)/rules-secondexp-extra.mk
.DELETE_ON_ERROR:

View File

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

View File

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

View File

@ -1,7 +1,7 @@
/*
* Tibia
*
* Copyright (C) 2024 Orastron Srl unipersonale
* Copyright (C) 2024, 2025 Orastron Srl unipersonale
*
* Tibia is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -18,37 +18,37 @@
* File author: Stefano D'Angelo
*/
#define NUM_AUDIO_BUSES_IN {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "input").length}}
#define NUM_AUDIO_BUSES_OUT {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "output").length}}
#define NUM_AUDIO_BUSES_IN {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "input").length}}
#define NUM_AUDIO_BUSES_OUT {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "output").length}}
#define AUDIO_BUS_IN {{=it.product.buses.findIndex(x => x.type == "audio" && x.direction == "input" && !x.cv && !x.sidechain && !x.optional)}}
#define AUDIO_BUS_OUT {{=it.product.buses.findIndex(x => x.type == "audio" && x.direction == "output" && !x.cv && !x.sidechain && !x.optional)}}
#define AUDIO_BUS_IN {{=it.product.buses.findIndex(x => x.type == "audio" && x.direction == "input" && !x.cv && !x.sidechain && !x.optional)}}
#define AUDIO_BUS_OUT {{=it.product.buses.findIndex(x => x.type == "audio" && x.direction == "output" && !x.cv && !x.sidechain && !x.optional)}}
#define NUM_CHANNELS_IN {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "input" && !x.cv && !x.sidechain && !x.optional).length > 0 ? (it.product.buses.filter(x => x.type == "audio" && x.direction == "input" && !x.cv && !x.sidechain && !x.optional)[0].channels == "mono" ? 1 : 2) : 0}}
#define NUM_CHANNELS_OUT {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "output" && !x.cv && !x.sidechain && !x.optional).length > 0 ? (it.product.buses.filter(x => x.type == "audio" && x.direction == "output" && !x.cv && !x.sidechain && !x.optional)[0].channels == "mono" ? 1 : 2) : 0}}
#define NUM_NON_OPT_CHANNELS_IN {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "input" && !x.optional).reduce((a, x) => a + (x.channels == "mono" ? 1 : 2), 0)}}
#define NUM_NON_OPT_CHANNELS_OUT {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "output" && !x.optional).reduce((a, x) => a + (x.channels == "mono" ? 1 : 2), 0)}}
#define NUM_ALL_CHANNELS_IN {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "input") .reduce((a, x) => a + (x.channels == "mono" ? 1 : 2), 0)}}
#define NUM_ALL_CHANNELS_OUT {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "output").reduce((a, x) => a + (x.channels == "mono" ? 1 : 2), 0)}}
#define NUM_CHANNELS_IN {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "input" && !x.cv && !x.sidechain && !x.optional).length > 0 ? (it.product.buses.filter(x => x.type == "audio" && x.direction == "input" && !x.cv && !x.sidechain && !x.optional)[0].channels == "mono" ? 1 : 2) : 0}}
#define NUM_CHANNELS_OUT {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "output" && !x.cv && !x.sidechain && !x.optional).length > 0 ? (it.product.buses.filter(x => x.type == "audio" && x.direction == "output" && !x.cv && !x.sidechain && !x.optional)[0].channels == "mono" ? 1 : 2) : 0}}
#define NUM_NON_OPT_CHANNELS_IN {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "input" && !x.optional).reduce((a, x) => a + (x.channels == "mono" ? 1 : 2), 0)}}
#define NUM_NON_OPT_CHANNELS_OUT {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "output" && !x.optional).reduce((a, x) => a + (x.channels == "mono" ? 1 : 2), 0)}}
#define NUM_ALL_CHANNELS_IN {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "input").reduce((a, x) => a + (x.channels == "mono" ? 1 : 2), 0)}}
#define NUM_ALL_CHANNELS_OUT {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "output").reduce((a, x) => a + (x.channels == "mono" ? 1 : 2), 0)}}
#define NUM_MIDI_INPUTS {{=it.product.buses.filter(x => x.type == "midi" && x.direction == "input").length}}
#define NUM_MIDI_INPUTS {{=it.product.buses.filter(x => x.type == "midi" && x.direction == "input").length}}
#define MIDI_BUS_IN {{=it.product.buses.findIndex(x => x.type == "midi" && x.direction == "input")}}
#define MIDI_BUS_IN {{=it.product.buses.findIndex(x => x.type == "midi" && x.direction == "input")}}
#if NUM_AUDIO_BUSES_IN + NUM_AUDIO_BUSES_OUT > 0
static struct {
size_t index;
char out;
char optional;
char channels;
size_t index;
char out;
char optional;
char channels;
} audio_bus_data[NUM_AUDIO_BUSES_IN + NUM_AUDIO_BUSES_OUT] = {
{{~it.product.buses :b:i}}
{{?b.type == "audio"}}
{
/* .index = */ {{=i}},
/* .out = */ {{=b.direction == "output" ? 1 : 0}},
/* .optional = */ {{=b.optional ? 1 : 0}},
/* .channels = */ {{=b.channels == "mono" ? 1 : 2}}
/* .index = */ {{=i}},
/* .out = */ {{=b.direction == "output" ? 1 : 0}},
/* .optional = */ {{=b.optional ? 1 : 0}},
/* .channels = */ {{=b.channels == "mono" ? 1 : 2}}
},
{{?}}
{{~}}
@ -72,19 +72,18 @@ static struct {
} param_data[PARAMETERS_N] = {
{{~it.product.parameters :p}}
{
/* .out = */ {{=p.direction == "output" ? 1 : 0}},
/* .def = */ {{=p.defaultValue.toExponential()}},
/* .min = */ {{=p.minimum.toExponential()}}f,
/* .max = */ {{=p.maximum.toExponential()}}f,
/* .flags = */ {{?p.isBypass}}PARAM_BYPASS{{??}}0{{?p.toggled}} | PARAM_TOGGLED{{?}}{{?p.integer}} | PARAM_INTEGER{{?}}{{?}}
/* .out = */ {{=p.direction == "output" ? 1 : 0}},
/* .def = */ {{=p.defaultValue.toExponential()}},
/* .min = */ {{=p.minimum.toExponential()}}f,
/* .max = */ {{=p.maximum.toExponential()}}f,
/* .flags = */ {{?p.isBypass}}PARAM_BYPASS{{??}}0{{?p.toggled}} | PARAM_TOGGLED{{?}}{{?p.integer}} | PARAM_INTEGER{{?}}{{?}}
},
{{~}}
};
{{?it.product.parameters.find(x => x.direction == "output" && x.isCpumeter)}}
# define PARAM_OUT_CPU_INDEX {{=it.product.parameters.indexOf(it.product.parameters.find(x => x.direction == "output" && x.isCpumeter))}}
{{?}}
#endif
#define JNI_FUNC(x) Java_{{=it.android.javaPackageName.replaceAll("_", "_1").replaceAll(".", "_")}}_MainActivity_##x
{{?it.product.state && it.product.state.dspCustom}}
#define STATE_DSP_CUSTOM
{{?}}

View File

@ -1,7 +1,7 @@
/*
* Tibia
*
* Copyright (C) 2023, 2024 Orastron Srl unipersonale
* Copyright (C) 2023-2025 Orastron Srl unipersonale
*
* Tibia is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -46,11 +46,8 @@
#if NUM_MIDI_INPUTS > 0
# include <vector>
# include <amidi/AMidi.h>
#endif
#ifdef PARAM_OUT_CPU_INDEX
# include "fatica.h"
# include <amidi/AMidi.h>
#endif
#if defined(__i386__) || defined(__x86_64__)
@ -58,59 +55,51 @@
#include <pmmintrin.h>
#endif
static ma_device device;
static plugin instance;
static void *mem;
static ma_device device;
static plugin instance;
static void * mem;
#if NUM_NON_OPT_CHANNELS_IN > NUM_CHANNELS_IN
float zero[BLOCK_SIZE];
float zero[BLOCK_SIZE];
#endif
#if NUM_CHANNELS_IN > 0
float x_buf[NUM_CHANNELS_IN * BLOCK_SIZE];
float *x_in[NUM_CHANNELS_IN];
float x_buf[NUM_CHANNELS_IN * BLOCK_SIZE];
float * x_in[NUM_CHANNELS_IN];
#endif
#if NUM_ALL_CHANNELS_IN > 0
const float *x[NUM_ALL_CHANNELS_IN];
const float * x[NUM_ALL_CHANNELS_IN];
#else
const float **x;
const float ** x;
#endif
#if NUM_NON_OPT_CHANNELS_OUT > 0
float y_buf[NUM_NON_OPT_CHANNELS_OUT * BLOCK_SIZE];
float y_buf[NUM_NON_OPT_CHANNELS_OUT * BLOCK_SIZE];
#endif
#if NUM_CHANNELS_OUT > 0
float *y_out[NUM_CHANNELS_OUT];
float * y_out[NUM_CHANNELS_OUT];
#endif
#if NUM_ALL_CHANNELS_OUT > 0
float *y[NUM_ALL_CHANNELS_OUT];
float * y[NUM_ALL_CHANNELS_OUT];
#else
float **y;
float ** y;
#endif
#if PARAMETERS_N > 0
std::mutex mutex;
float param_values[PARAMETERS_N];
float param_values_prev[PARAMETERS_N];
std::mutex mutex;
float param_values[PARAMETERS_N];
float param_values_prev[PARAMETERS_N];
#endif
#if NUM_MIDI_INPUTS > 0
struct PortData {
AMidiDevice *device;
int portNumber;
AMidiOutputPort *port;
AMidiDevice *device;
int portNumber;
AMidiOutputPort *port;
};
std::vector<PortData> midiPorts;
# define MIDI_BUFFER_SIZE 1024
uint8_t midiBuffer[MIDI_BUFFER_SIZE];
#endif
#ifdef PARAM_OUT_CPU_INDEX
float cpu_meter = 0.f;
float sample_rate = 1.f;
uint8_t midiBuffer[MIDI_BUFFER_SIZE];
#endif
static void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) {
(void)pDevice;
#ifdef PARAM_OUT_CPU_INDEX
const unsigned long long processTimeStart = fatica_time_process();
#endif
#if defined(__aarch64__)
uint64_t fpcr;
__asm__ __volatile__ ("mrs %0, fpcr" : "=r"(fpcr));
@ -127,15 +116,8 @@ static void data_callback(ma_device* pDevice, void* pOutput, const void* pInput,
if (mutex.try_lock()) {
# if PARAMETERS_N > 0
for (size_t i = 0; i < PARAMETERS_N; i++) {
if (param_data[i].out) {
# ifdef PARAM_OUT_CPU_INDEX
if (i == PARAM_OUT_CPU_INDEX) {
param_values_prev[i] = param_values[i] = cpu_meter;
continue;
}
# endif
if (param_data[i].out)
param_values_prev[i] = param_values[i] = plugin_get_parameter(&instance, i);
}
else if (param_values_prev[i] != param_values[i]) {
plugin_set_parameter(&instance, i, param_values[i]);
param_values_prev[i] = param_values[i];
@ -200,11 +182,6 @@ static void data_callback(ma_device* pDevice, void* pOutput, const void* pInput,
_MM_SET_FLUSH_ZERO_MODE(flush_zero_mode);
_MM_SET_DENORMALS_ZERO_MODE(denormals_zero_mode);
#endif
#ifdef PARAM_OUT_CPU_INDEX
const unsigned long long processTimeEnd = fatica_time_process();
fatica_cpu_meter(&cpu_meter, processTimeStart, processTimeEnd, frameCount, sample_rate);
#endif
}
extern "C"
@ -212,6 +189,10 @@ JNIEXPORT jboolean JNICALL
JNI_FUNC(nativeAudioStart)(JNIEnv* env, jobject thiz) {
(void)env;
(void)thiz;
#ifdef STATE_DSP_CUSTOM
(void)plugin_state_load;
(void)plugin_state_save;
#endif
#if NUM_CHANNELS_IN + NUM_CHANNELS_OUT > 0
# if NUM_CHANNELS_IN == 0
@ -225,35 +206,35 @@ JNI_FUNC(nativeAudioStart)(JNIEnv* env, jobject thiz) {
ma_device_config deviceConfig = ma_device_config_init(ma_device_type_playback);
#endif
deviceConfig.periodSizeInFrames = BLOCK_SIZE;
deviceConfig.periods = 1;
deviceConfig.performanceProfile = ma_performance_profile_low_latency;
deviceConfig.noPreSilencedOutputBuffer = 1;
deviceConfig.noClip = 0;
deviceConfig.noDisableDenormals = 0;
deviceConfig.noFixedSizedCallback = 1;
deviceConfig.dataCallback = data_callback;
deviceConfig.capture.pDeviceID = NULL;
deviceConfig.capture.format = ma_format_f32;
deviceConfig.capture.channels = NUM_CHANNELS_IN;
deviceConfig.capture.shareMode = ma_share_mode_shared;
deviceConfig.playback.pDeviceID = NULL;
deviceConfig.playback.format = ma_format_f32;
deviceConfig.periodSizeInFrames = BLOCK_SIZE;
deviceConfig.periods = 1;
deviceConfig.performanceProfile = ma_performance_profile_low_latency;
deviceConfig.noPreSilencedOutputBuffer = 1;
deviceConfig.noClip = 0;
deviceConfig.noDisableDenormals = 0;
deviceConfig.noFixedSizedCallback = 1;
deviceConfig.dataCallback = data_callback;
deviceConfig.capture.pDeviceID = NULL;
deviceConfig.capture.format = ma_format_f32;
deviceConfig.capture.channels = NUM_CHANNELS_IN;
deviceConfig.capture.shareMode = ma_share_mode_shared;
deviceConfig.playback.pDeviceID = NULL;
deviceConfig.playback.format = ma_format_f32;
#if NUM_CHANNELS_IN + NUM_CHANNELS_OUT > 0
deviceConfig.playback.channels = NUM_CHANNELS_OUT;
deviceConfig.playback.channels = NUM_CHANNELS_OUT;
#else
deviceConfig.playback.channels = 1; // Fake & muted
deviceConfig.playback.channels = 1; // Fake & muted
#endif
deviceConfig.playback.shareMode = ma_share_mode_shared;
deviceConfig.playback.shareMode = ma_share_mode_shared;
if (ma_device_init(NULL, &deviceConfig, &device) != MA_SUCCESS)
return false;
plugin_callbacks cbs = {
/* .handle = */ NULL,
/* .format = */ "android",
/* .get_bindir = */ NULL,
/* .get_datadir = */ NULL
/* .handle = */ NULL,
/* .format = */ "android",
/* .get_bindir = */ NULL,
/* .get_datadir = */ NULL
};
plugin_init(&instance, &cbs);
@ -266,10 +247,6 @@ JNI_FUNC(nativeAudioStart)(JNIEnv* env, jobject thiz) {
#endif
plugin_set_sample_rate(&instance, (float)device.sampleRate);
#ifdef PARAM_OUT_CPU_INDEX
sample_rate = (float)device.sampleRate;
#endif
size_t req = plugin_mem_req(&instance);
if (req != 0) {
mem = malloc(req);

View File

@ -27,5 +27,4 @@ module.exports = function (data, api) {
api.copyFile(`src${sep}jni.cpp`, `src${sep}jni.cpp`);
api.generateFileFromTemplateFile(`src${sep}MainActivity.java`, `src${sep}MainActivity.java`, data);
api.generateFileFromTemplateFile(`src${sep}index.html`, `src${sep}index.html`, data);
api.copyFile(`..${sep}common${sep}fatica.h`, `src${sep}fatica.h`);
};

View File

@ -1,7 +1,7 @@
/*
* Tibia
*
* Copyright (C) 2024 Orastron Srl unipersonale
* Copyright (C) 2024, 2025 Orastron Srl unipersonale
*
* Tibia is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -21,25 +21,35 @@
#ifndef PLUGIN_API_H
#define PLUGIN_API_H
#include <stddef.h>
typedef struct {
void *handle;
const char *format;
const char * (*get_bindir) (void *handle);
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;
const char *format;
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;
{{?}}
const char * (*get_bindir) (void *handle);
const char * (*get_datadir) (void *handle);
void (*set_parameter_begin)(void *handle, size_t index);
void (*set_parameter) (void *handle, size_t index, float value);
void (*set_parameter_end) (void *handle, size_t index);
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}}
@ -47,8 +57,7 @@ enum {
{{~it.product.parameters :p}}
plugin_parameter_{{=p.id}},
{{~}}
plugin_parameter_count
plugin_parameter__count
};
{{?}}

View File

@ -22,5 +22,5 @@ var path = require("path");
var sep = path.sep;
module.exports = function (data, api) {
api.generateFileFromTemplateFile(`src${sep}plugin_api.h`, `src${sep}plugin_api.h`, data);
api.generateFileFromTemplateFile(`src${sep}plugin_api.h`, `plugin_api.h`, data);
};

View File

@ -1,7 +1,7 @@
#
# Tibia
#
# Copyright (C) 2024 Orastron Srl unipersonale
# Copyright (C) 2024, 2025 Orastron Srl unipersonale
#
# Tibia is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@ -18,8 +18,23 @@
# File author: Stefano D'Angelo
#
SHELL := bash -o pipefail
TEMPLATE := cmd
include vars.mk
COMMON_DIR ?= .
DATA_DIR ?= .
PLUGIN_DIR ?= src
API_DIR ?= $(PLUGIN_DIR)
MKINC_DIR ?= $(COMMON_DIR)
BUILD_BIN_DIR := build
BUILD_DATA_DIR := build
-include $(MKINC_DIR)/vars-pre.mk
ifeq ($(OS), Windows_NT)
EXE_SUFFIX := .exe
else
@ -29,26 +44,25 @@ else
BINDIR := $(PREFIX)/bin
endif
COMMON_DIR := $(or $(COMMON_DIR),.)
DATA_DIR := $(or $(DATA_DIR),.)
PLUGIN_DIR := $(or $(PLUGIN_DIR),src)
TINYWAV_DIR ?= ../tinywav
MIDI_PARSER_DIR ?= ../midi-parser
CC := gcc
CXX := g++
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 -fvisibility=hidden $(CFLAGS_EXTRA) $(CFLAGS)
LDFLAGS :=
LDFLAGS_ALL := $(LDFLAGS) $(LDFLAGS_EXTRA)
LDFLAGS_ALL := $(LDFLAGS_EXTRA) $(LDFLAGS)
CXXFLAGS := $(CFLAGS)
CXXFLAGS_ALL := -I$(DATA_DIR)/src -I$(PLUGIN_DIR) -I$(TINYWAV_DIR) -I$(MIDI_PARSER_DIR)/include -fPIC $(CXXFLAGS) $(CXXFLAGS_EXTRA)
CXXFLAGS_ALL := -I$(DATA_DIR)/src -I$(PLUGIN_DIR) -I$(API_DIR) -I$(TINYWAV_DIR) -I$(MIDI_PARSER_DIR)/include $(CXXFLAGS_EXTRA) $(CXXFLAGS)
ifeq ($(UNAME_S), Darwin)
CFLAGS_ALL := $(CFLAGS_ALL) -arch arm64 -arch x86_64
LDFLAGS_ALL := $(LDFLAGS_ALL) -arch arm64 -arch x86_64
CXXFLAGS_ALL := $(CXXFLAGS_ALL) -arch arm64 -arch x86_64
CFLAGS_ALL := $(CFLAGS_ALL) -arch arm64 -arch x86_64
LDFLAGS_ALL := $(LDFLAGS_ALL) -arch arm64 -arch x86_64
CXXFLAGS_ALL := $(CXXFLAGS_ALL) -arch arm64 -arch x86_64
endif
PROGRAM := $(BUNDLE_NAME)$(EXE_SUFFIX)
@ -62,9 +76,19 @@ C_OBJS := $(addprefix build/obj/, $(notdir $(C_SRCS:.c=.o)))
CXX_SRCS := $(CXX_SRCS_EXTRA)
CXX_OBJS := $(addprefix build/obj/, $(notdir $(CXX_SRCS:.cpp=.o)))
DIRS := build build/obj
ALL := build/$(PROGRAM)
-include $(COMMON_DIR)/vars-extra.mk
STRIP_ALL := build/$(PROGRAM)
STRIP_PREREQS := $(STRIP_ALL)
PHONY := all clean strip
ifneq ($(OS), Windows_NT)
PHONY := $(PHONY) install
endif
-include $(MKINC_DIR)/vars-extra.mk
all: $(ALL)
@ -76,23 +100,24 @@ build/$(PROGRAM): $(C_OBJS) $(CXX_OBJS) build/obj/tinywav.o | build
$(CXX) $^ -o $@ $(CFLAGS_ALL) $(CXXFLAGS_ALL) $(LDFLAGS_ALL)
endif
build build/obj:
$(DIRS):
mkdir -p $@
clean:
rm -fr build
ifeq ($(OS), Windows_NT)
.PHONY: all clean
else
strip: $(STRIP_PREREQS)
strip build/$(PROGRAM)
ifneq ($(OS), Windows_NT)
install: all
mkdir -m 0755 -p $(BINDIR)
install -m 0755 build/$(PROGRAM) $(BINDIR)
.PHONY: all clean install
endif
-include $(COMMON_DIR)/rules-extra.mk
.PHONY: $(PHONY)
-include $(MKINC_DIR)/rules-extra.mk
.SECONDEXPANSION:
@ -107,4 +132,6 @@ $(C_OBJS): build/obj/%.o: $$(filter $$(PERCENT)/$$(basename $$(notdir $$@)).c,$$
$(CXX_OBJS): build/obj/%.o: $$(filter $$(PERCENT)/$$(basename $$(notdir $$@)).cpp,$$(CXX_SRCS)) | build/obj
$(CXX) $^ -o $@ -c $(CXXFLAGS_ALL)
-include $(COMMON_DIR)/rules-secondexp-extra.mk
-include $(MKINC_DIR)/rules-secondexp-extra.mk
.DELETE_ON_ERROR:

View File

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

View File

@ -1,7 +1,7 @@
/*
* Tibia
*
* Copyright (C) 2024 Orastron Srl unipersonale
* Copyright (C) 2024, 2025 Orastron Srl unipersonale
*
* Tibia is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -83,3 +83,7 @@ static struct {
{{~}}
};
#endif
{{?it.product.state && it.product.state.dspCustom}}
#define STATE_DSP_CUSTOM
{{?}}

View File

@ -1,7 +1,7 @@
/*
* Tibia
*
* Copyright (C) 2024 Orastron Srl unipersonale
* Copyright (C) 2024, 2025 Orastron Srl unipersonale
*
* Tibia is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -162,6 +162,11 @@ float clampf(float x, float m, float M) {
}
int main(int argc, char * argv[]) {
#ifdef STATE_DSP_CUSTOM
(void)plugin_state_load;
(void)plugin_state_save;
#endif
#if PARAMETERS_N > 0
for (size_t i = 0; i < PARAMETERS_N; i++)
param_values[i] = param_data[i].def;

View File

@ -1,75 +0,0 @@
#ifndef FATICA_H
#define FATICA_H
// API
// unit = 100-nanosecond starting from somewhen
unsigned long long fatica_time_process(void);
void fatica_cpu_meter(float *value, unsigned long long start, unsigned long long end, uint32_t sample_count, float sample_rate);
// Implementation
#if defined(_WIN32) || defined(__CYGWIN__)
# include <windows.h>
static ULONGLONG filetime_to_ull(const FILETIME* ft) {
return (((ULONGLONG)ft->dwHighDateTime) << 32) + ft->dwLowDateTime;
}
unsigned long long fatica_time_process(void) {
FILETIME creationTime, exitTime, kernelTime, userTime;
const DWORD threadId = GetCurrentThreadId();
const HANDLE hThread = OpenThread(THREAD_QUERY_INFORMATION, FALSE, threadId);
if (!GetThreadTimes(hThread, &creationTime, &exitTime, &kernelTime, &userTime))
return 0ull;
CloseHandle(hThread);
return (unsigned long long) (filetime_to_ull(&kernelTime) + filetime_to_ull(&userTime));
}
#elif defined(__linux__)
# ifndef _XOPEN_SOURCE
# if __STDC_VERSION__ >= 199901L
# define _XOPEN_SOURCE 600
# else
# define _XOPEN_SOURCE 500
# endif
# endif
# include <time.h>
# include <unistd.h>
unsigned long long fatica_time_process(void) {
struct timespec ts;
if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts) != 0)
return 0ull;
return (unsigned long long) (ts.tv_sec * 1e7 + ts.tv_nsec / 1e2);
}
#elif defined(__APPLE__)
# include <unistd.h>
# include <mach/mach.h>
# include <mach/thread_act.h>
unsigned long long fatica_time_process(void) {
thread_t thread = mach_thread_self();
thread_basic_info_data_t info;
mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT;
if (thread_info(thread, THREAD_BASIC_INFO, (thread_info_t) &info, &count) != KERN_SUCCESS)
return 0ull;
return (info.user_time.seconds + info.system_time.seconds) * 1000
+ (info.user_time.microseconds + info.system_time.microseconds) / 1000;
}
#else
# error "System not supported"
#endif
void fatica_cpu_meter(float *value, unsigned long long start, unsigned long long end, uint32_t sample_count, float sample_rate) {
const unsigned long long processTime100n = end - start;
const double processTimeMs = ((double) processTime100n) * 1.0e-4;
*value = *value * 0.99f + ((float) (processTimeMs * sample_count / (sample_rate * 1000))) * 0.01f;
}
#endif // FATICA_H

View File

@ -1,7 +1,7 @@
#
# Tibia
#
# Copyright (C) 2023, 2024 Orastron Srl unipersonale
# Copyright (C) 2023-2025 Orastron Srl unipersonale
#
# Tibia is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@ -18,13 +18,23 @@
# File author: Stefano D'Angelo
#
SHELL := bash -o pipefail
TEMPLATE := daisy-seed
include vars.mk
TARGET := $(BUNDLE_NAME)
COMMON_DIR ?= .
DATA_DIR ?= .
PLUGIN_DIR ?= src
API_DIR ?= $(PLUGIN_DIR)
MKINC_DIR ?= $(COMMON_DIR)
COMMON_DIR := $(or $(COMMON_DIR),.)
DATA_DIR := $(or $(DATA_DIR),.)
PLUGIN_DIR := $(or $(PLUGIN_DIR),src)
-include $(MKINC_DIR)/vars-pre.mk
LIBDAISY_DIR ?= ../libDaisy
TARGET := $(BUNDLE_NAME)
CPP_SOURCES := $(COMMON_DIR)/src/main.cpp $(CXX_SRCS_EXTRA)
@ -34,8 +44,8 @@ include $(SYSTEM_FILES_DIR)/Makefile
C_SOURCES += $(C_SRCS_EXTRA)
CFLAGS += -I$(DATA_DIR)/src -I$(PLUGIN_DIR) $(CFLAGS_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) $(CXXFLAGS_EXTRA)
CXXFLAGS += -I$(DATA_DIR)/src -I$(PLUGIN_DIR) -I$(API_DIR) $(CXXFLAGS_EXTRA)
-include $(COMMON_DIR)/extra.mk

View File

@ -18,17 +18,27 @@
# File author: Stefano D'Angelo
#
BUNDLE_NAME := {{=it.product.bundleName}}
BUNDLE_NAME := {{=it.product.bundleName}}
CFLAGS_EXTRA := {{=it.make?.cflags ?? ""}} {{=it.daisy_seed_make?.cflags ?? ""}}
CXXFLAGS_EXTRA := {{=it.make?.cxxflags ?? ""}} {{=it.daisy_seed_make?.cxxflags ?? ""}}
LDFLAGS_EXTRA := {{=it.make?.ldflags ?? ""}} {{=it.daisy_seed_make?.ldflags ?? ""}}
{{?(it.daisy_seed_make?.commonDir || it.make?.commonDir)}}
COMMON_DIR := {{=it.daisy_seed_make?.commonDir ?? (it.make?.commonDir ?? "")}}
{{?}}
{{?(it.daisy_seed_make?.dataDir || it.make?.dataDir)}}
DATA_DIR := {{=it.daisy_seed_make?.dataDir ?? (it.make?.dataDir ?? "")}}
{{?}}
{{?(it.daisy_seed_make?.pluginDir || it.make?.pluginDir)}}
PLUGIN_DIR := {{=it.daisy_seed_make?.pluginDir ?? (it.make?.pluginDir ?? "")}}
{{?}}
{{?(it.daisy_seed_make?.apiDir || it.make?.apiDir)}}
API_DIR := {{=it.daisy_seed_make?.apiDir ?? (it.make?.apiDir ?? "")}}
{{?}}
{{?(it.daisy_seed_make?.mkincDir || it.make?.mkincDir)}}
MKINC_DIR := {{=it.daisy_seed_make?.mkincDir ?? (it.make?.mkincDir ?? "")}}
{{?}}
C_SRCS_EXTRA := {{=it.make?.cSrcs ?? ""}} {{=it.daisy_seed_make?.cSrcs ?? ""}}
CXX_SRCS_EXTRA := {{=it.make?.cxxSrcs ?? ""}} {{=it.daisy_seed_make?.cxxSrcs ?? ""}}
COMMON_DIR := {{=it.daisy_seed_make?.commonDir ?? (it.make?.commonDir ?? "")}}
DATA_DIR := {{=it.daisy_seed_make?.dataDir ?? (it.make?.dataDir ?? "")}}
PLUGIN_DIR := {{=it.daisy_seed_make?.pluginDir ?? (it.make?.pluginDir ?? "")}}
LIBDAISY_DIR := {{=it.daisy_seed_make.libdaisyDir}}
{{?it.make?.extra}}
{{=it.make.extra}}
{{?}}
{{?it.daisy_seed_make?.extra}}
{{=it.daisy_seed_make.extra}}
{{?}}

View File

@ -1,7 +1,7 @@
/*
* Tibia
*
* Copyright (C) 2024 Orastron Srl unipersonale
* Copyright (C) 2024, 2025 Orastron Srl unipersonale
*
* Tibia is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -98,3 +98,7 @@ static int midi_cc_maps[NUM_PARAMETERS] = { {{~it.daisy_seed.midiCCMaps :p}}{{=p
# endif
#endif
{{?it.product.state && it.product.state.dspCustom}}
#define STATE_DSP_CUSTOM
{{?}}

View File

@ -1,7 +1,7 @@
/*
* Tibia
*
* Copyright (C) 2023, 2024 Orastron Srl unipersonale
* Copyright (C) 2023-2025 Orastron Srl unipersonale
*
* Tibia is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -150,6 +150,11 @@ static void AudioCallback(
}
int main() {
#ifdef STATE_DSP_CUSTOM
(void)plugin_state_load;
(void)plugin_state_save;
#endif
hardware.Configure();
hardware.Init();

View File

@ -1,7 +1,7 @@
#
# Tibia
#
# Copyright (C) 2024 Orastron Srl unipersonale
# Copyright (C) 2024, 2025 Orastron Srl unipersonale
#
# Tibia is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@ -18,41 +18,71 @@
# File author: Stefano D'Angelo
#
SHELL := bash -o pipefail
TEMPLATE := ios
include vars.mk
COMMON_DIR := $(or $(COMMON_DIR),.)
DATA_DIR := $(or $(DATA_DIR),.)
PLUGIN_DIR := $(or $(PLUGIN_DIR),src)
COMMON_DIR ?= .
DATA_DIR ?= .
PLUGIN_DIR ?= src
API_DIR ?= $(PLUGIN_DIR)
MKINC_DIR ?= $(COMMON_DIR)
BUILD_BIN_DIR := build/gen/res
BUILD_DATA_DIR := build/gen/res
-include $(MKINC_DIR)/vars-pre.mk
CFLAGS := -O3 -Wall -Wpedantic -Wextra
CFLAGS_ALL := -I$(DATA_DIR)/src -I$(PLUGIN_DIR) -I$(API_DIR) -fvisibility=hidden $(CFLAGS_EXTRA) $(CFLAGS)
CXXFLAGS := $(CFLAGS)
CXXFLAGS_ALL := -I$(DATA_DIR)/src -I$(PLUGIN_DIR) -I$(API_DIR) -fvisibility=hidden $(CXXFLAGS_EXTRA) $(CXXFLAGS)
LDFLAGS :=
LDFLAGS_ALL := $(LDFLAGS_EXTRA) $(LDFLAGS)
SOURCES := \
$(C_SRCS_EXTRA) \
$(M_SRCS_EXTRA) \
$(CXX_SRCS_EXTRA) \
$(SRCS_EXTRA) \
$(DATA_DIR)/src/data.h \
$(DATA_DIR)/src/index.html \
$(COMMON_DIR)/src/app.swift \
$(COMMON_DIR)/src/native.mm \
$(COMMON_DIR)/src/app-Bridging-Header.h \
$(PLUGIN_DIR)/plugin.h \
$(PLUGIN_DIR)/fatica.h \
$(C_SRCS_EXTRA) \
$(CXX_SRCS_EXTRA) \
$(SRCS_EXTRA)
$(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)))
-include $(COMMON_DIR)/vars-extra.mk
DIRS := build build/gen build/gen/src build/gen/res
ALL := build/gen/$(BUNDLE_NAME).xcodeproj
-include $(MKINC_DIR)/vars-extra.mk
all: $(ALL)
build/gen/$(BUNDLE_NAME).xcodeproj: $(SOURCES_OUT)
xcodegen generate --spec project.yml -r build/gen -p build/gen
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/gen/src:
build/project_out.yml: project.yml build/
sed -e 's|@CFLAGS_ALL@|$(CFLAGS_ALL)|g' \
-e 's|@CXXFLAGS_ALL@|$(CXXFLAGS_ALL)|g' \
-e 's|@LDFLAGS_ALL@|$(LDFLAGS_ALL)|g' $< > $@
$(DIRS):
mkdir -p $@
clean:
rm -fr build
-include $(COMMON_DIR)/rules-extra.mk
-include $(MKINC_DIR)/rules-extra.mk
.PHONY: all clean
@ -63,4 +93,9 @@ PERCENT := %
$(SOURCES_OUT): build/gen/src/%: $$(filter $$(PERCENT)/%,$$(SOURCES)) | build/gen/src
cp -R $^ $@
-include $(COMMON_DIR)/rules-secondexp-extra.mk
$(RESOURCES_OUT): build/gen/res/%: $$(filter $$(PERCENT)/%,$$(RESOURCES)) | build/gen/res
cp -R $^ $@
-include $(MKINC_DIR)/rules-secondexp-extra.mk
.DELETE_ON_ERROR:

View File

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

View File

@ -18,12 +18,27 @@
# File author: Stefano D'Angelo
#
BUNDLE_NAME := {{=it.product.bundleName}}
BUNDLE_NAME := {{=it.product.bundleName}}
C_SRCS_EXTRA := {{=it.make?.cSrcs ?? ""}} {{=it.ios_make?.cSrcs ?? ""}}
CXX_SRCS_EXTRA := {{=it.make?.cxxSrcs ?? ""}} {{=it.ios_make?.cxxSrcs ?? ""}}
SRCS_EXTRA := {{=it.ios_make?.srcsExtra ?? ""}}
{{?(it.ios_make?.commonDir || it.make?.commonDir)}}
COMMON_DIR := {{=it.ios_make?.commonDir ?? (it.make?.commonDir ?? "")}}
{{?}}
{{?(it.ios_make?.dataDir || it.make?.dataDir)}}
DATA_DIR := {{=it.ios_make?.dataDir ?? (it.make?.dataDir ?? "")}}
{{?}}
{{?(it.ios_make?.pluginDir || it.make?.pluginDir)}}
PLUGIN_DIR := {{=it.ios_make?.pluginDir ?? (it.make?.pluginDir ?? "")}}
{{?}}
{{?(it.ios_make?.apiDir || it.make?.apiDir)}}
API_DIR := {{=it.ios_make?.apiDir ?? (it.make?.apiDir ?? "")}}
{{?}}
{{?(it.ios_make?.mkincDir || it.make?.mkincDir)}}
MKINC_DIR := {{=it.ios_make?.mkincDir ?? (it.make?.mkincDir ?? "")}}
{{?}}
COMMON_DIR := {{=it.ios_make?.commonDir ?? (it.make?.commonDir ?? "")}}
DATA_DIR := {{=it.ios_make?.dataDir ?? (it.make?.dataDir ?? "")}}
PLUGIN_DIR := {{=it.ios_make?.pluginDir ?? (it.make?.pluginDir ?? "")}}
{{?it.make?.extra}}
{{=it.make.extra}}
{{?}}
{{?it.ios_make?.extra}}
{{=it.ios_make.extra}}
{{?}}

View File

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

View File

@ -1,7 +1,7 @@
/*
* Tibia
*
* Copyright (C) 2024 Orastron Srl unipersonale
* Copyright (C) 2024, 2025 Orastron Srl unipersonale
*
* Tibia is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -18,70 +18,70 @@
* File author: Stefano D'Angelo
*/
#define NUM_AUDIO_BUSES_IN {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "input").length}}
#define NUM_AUDIO_BUSES_OUT {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "output").length}}
#define NUM_AUDIO_BUSES_IN {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "input").length}}
#define NUM_AUDIO_BUSES_OUT {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "output").length}}
#define AUDIO_BUS_IN {{=it.product.buses.findIndex(x => x.type == "audio" && x.direction == "input" && !x.cv && !x.sidechain && !x.optional)}}
#define AUDIO_BUS_OUT {{=it.product.buses.findIndex(x => x.type == "audio" && x.direction == "output" && !x.cv && !x.sidechain && !x.optional)}}
#define AUDIO_BUS_IN {{=it.product.buses.findIndex(x => x.type == "audio" && x.direction == "input" && !x.cv && !x.sidechain && !x.optional)}}
#define AUDIO_BUS_OUT {{=it.product.buses.findIndex(x => x.type == "audio" && x.direction == "output" && !x.cv && !x.sidechain && !x.optional)}}
#define NUM_CHANNELS_IN {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "input" && !x.cv && !x.sidechain && !x.optional).length > 0 ? (it.product.buses.filter(x => x.type == "audio" && x.direction == "input" && !x.cv && !x.sidechain && !x.optional)[0].channels == "mono" ? 1 : 2) : 0}}
#define NUM_CHANNELS_OUT {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "output" && !x.cv && !x.sidechain && !x.optional).length > 0 ? (it.product.buses.filter(x => x.type == "audio" && x.direction == "output" && !x.cv && !x.sidechain && !x.optional)[0].channels == "mono" ? 1 : 2) : 0}}
#define NUM_NON_OPT_CHANNELS_IN {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "input" && !x.optional).reduce((a, x) => a + (x.channels == "mono" ? 1 : 2), 0)}}
#define NUM_NON_OPT_CHANNELS_OUT {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "output" && !x.optional).reduce((a, x) => a + (x.channels == "mono" ? 1 : 2), 0)}}
#define NUM_ALL_CHANNELS_IN {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "input").reduce((a, x) => a + (x.channels == "mono" ? 1 : 2), 0)}}
#define NUM_ALL_CHANNELS_OUT {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "output").reduce((a, x) => a + (x.channels == "mono" ? 1 : 2), 0)}}
#define NUM_CHANNELS_IN {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "input" && !x.cv && !x.sidechain && !x.optional).length > 0 ? (it.product.buses.filter(x => x.type == "audio" && x.direction == "input" && !x.cv && !x.sidechain && !x.optional)[0].channels == "mono" ? 1 : 2) : 0}}
#define NUM_CHANNELS_OUT {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "output" && !x.cv && !x.sidechain && !x.optional).length > 0 ? (it.product.buses.filter(x => x.type == "audio" && x.direction == "output" && !x.cv && !x.sidechain && !x.optional)[0].channels == "mono" ? 1 : 2) : 0}}
#define NUM_NON_OPT_CHANNELS_IN {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "input" && !x.optional).reduce((a, x) => a + (x.channels == "mono" ? 1 : 2), 0)}}
#define NUM_NON_OPT_CHANNELS_OUT {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "output" && !x.optional).reduce((a, x) => a + (x.channels == "mono" ? 1 : 2), 0)}}
#define NUM_ALL_CHANNELS_IN {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "input").reduce((a, x) => a + (x.channels == "mono" ? 1 : 2), 0)}}
#define NUM_ALL_CHANNELS_OUT {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "output").reduce((a, x) => a + (x.channels == "mono" ? 1 : 2), 0)}}
#define NUM_MIDI_INPUTS {{=it.product.buses.filter(x => x.type == "midi" && x.direction == "input").length}}
#define NUM_MIDI_INPUTS {{=it.product.buses.filter(x => x.type == "midi" && x.direction == "input").length}}
#define MIDI_BUS_IN {{=it.product.buses.findIndex(x => x.type == "midi" && x.direction == "input")}}
#define MIDI_BUS_IN {{=it.product.buses.findIndex(x => x.type == "midi" && x.direction == "input")}}
#if NUM_AUDIO_BUSES_IN + NUM_AUDIO_BUSES_OUT > 0
static struct {
size_t index;
char out;
char optional;
char channels;
size_t index;
char out;
char optional;
char channels;
} audio_bus_data[NUM_AUDIO_BUSES_IN + NUM_AUDIO_BUSES_OUT] = {
{{~it.product.buses :b:i}}
{{?b.type == "audio"}}
{
/* .index = */ {{=i}},
/* .out = */ {{=b.direction == "output" ? 1 : 0}},
/* .optional = */ {{=b.optional ? 1 : 0}},
/* .channels = */ {{=b.channels == "mono" ? 1 : 2}}
/* .index = */ {{=i}},
/* .out = */ {{=b.direction == "output" ? 1 : 0}},
/* .optional = */ {{=b.optional ? 1 : 0}},
/* .channels = */ {{=b.channels == "mono" ? 1 : 2}}
},
{{?}}
{{~}}
};
#endif
#define PARAMETERS_N {{=it.product.parameters.length}}
#define PARAMETERS_N {{=it.product.parameters.length}}
#if PARAMETERS_N > 0
# define PARAM_BYPASS 1
# define PARAM_TOGGLED (1<<1)
# define PARAM_INTEGER (1<<2)
# define PARAM_BYPASS 1
# define PARAM_TOGGLED (1<<1)
# define PARAM_INTEGER (1<<2)
static struct {
char out;
float def;
float min;
float max;
uint32_t flags;
char out;
float def;
float min;
float max;
uint32_t flags;
} param_data[PARAMETERS_N] = {
{{~it.product.parameters :p}}
{
/* .out = */ {{=p.direction == "output" ? 1 : 0}},
/* .def = */ {{=p.defaultValue.toExponential()}},
/* .min = */ {{=p.minimum.toExponential()}}f,
/* .max = */ {{=p.maximum.toExponential()}}f,
/* .flags = */ {{?p.isBypass}}PARAM_BYPASS{{??p.isLatency}}PARAM_INTEGER{{??}}0{{?p.toggled}} | PARAM_TOGGLED{{?}}{{?p.integer}} | PARAM_INTEGER{{?}}{{?}}
/* .out = */ {{=p.direction == "output" ? 1 : 0}},
/* .def = */ {{=p.defaultValue.toExponential()}},
/* .min = */ {{=p.minimum.toExponential()}}f,
/* .max = */ {{=p.maximum.toExponential()}}f,
/* .flags = */ {{?p.isBypass}}PARAM_BYPASS{{??p.isLatency}}PARAM_INTEGER{{??}}0{{?p.toggled}} | PARAM_TOGGLED{{?}}{{?p.integer}} | PARAM_INTEGER{{?}}{{?}}
},
{{~}}
};
{{?it.product.parameters.find(x => x.direction == "output" && x.isCpumeter)}}
# define PARAM_OUT_CPU_INDEX {{=it.product.parameters.indexOf(it.product.parameters.find(x => x.direction == "output" && x.isCpumeter))}}
{{?}}
#endif
{{?it.product.state && it.product.state.dspCustom}}
#define STATE_DSP_CUSTOM
{{?}}

View File

@ -21,15 +21,14 @@
-->
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
<title>{{=it.product.name}}</title>
<script type="text/javascript">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
<title>{{=it.product.name}}</title>
<script type="text/javascript">
function request(data) {
return window.webkit.messageHandlers.listener.postMessage(data);
return window.webkit.messageHandlers.messageHandler.postMessage(data);
}
function needAudioPermission() {
return request({ name: "needAudioPermission" });
}
@ -41,15 +40,15 @@ function requestAudioPermission() {
function audioStart() {
return request({ name: "audioStart" });
}
function audioStop() {
return request({ name: "audioStop" });
}
function setParameter(index, value) {
return request({ name: "setParameter", index: index, value: value });
}
function getParameter(index) {
return request({ name: "getParameter", index: index });
}
@ -76,126 +75,125 @@ var outParamInterval;
window.onload = async function () {
topButtonElem = document.getElementById("topButton");
var paramsElem = document.getElementById("params");
var paramsElem = document.getElementById("params");
for (var i = 0; i < data.buses.length; i++) {
if (data.buses[i].type == "audio" && data.buses[i].direction == "input") {
hasAudioPermission = !await needAudioPermission();
break;
}
}
for (var i = 0; i < data.buses.length; i++)
if (data.buses[i].type == "audio" && data.buses[i].direction == "input") {
hasAudioPermission = !await needAudioPermission();
break;
}
topButtonElem.value = hasAudioPermission ? "START" : "INIT";
topButtonElem.addEventListener("click", async function () {
if (hasAudioPermission) {
if (audioStarted) {
clearInterval(outParamInterval);
await audioStop();
topButtonElem.value = hasAudioPermission ? "START" : "INIT";
topButtonElem.addEventListener("click", async function () {
if (hasAudioPermission) {
if (audioStarted) {
clearInterval(outParamInterval);
await audioStop();
paramsElem.innerHTML = "";
paramsElem.innerHTML = "";
topButtonElem.value = "START";
audioStarted = false;
} else {
if (await audioStart()) {
for (var i = 0; i < data.parameters.length; i++) {
var div = document.createElement("div");
topButtonElem.value = "START";
audioStarted = false;
} else {
if (await audioStart()) {
for (var i = 0; i < data.parameters.length; i++) {
var div = document.createElement("div");
var label = document.createElement("label");
label.setAttribute("for", "p" + i);
label.innerText = data.parameters[i].name;
var label = document.createElement("label");
label.setAttribute("for", "p" + i);
label.innerText = data.parameters[i].name;
var range = document.createElement("input");
range.classList.add("range");
range.setAttribute("type", "range");
range.setAttribute("id", "p" + i);
range.setAttribute("name", "p" + i);
if (data.parameters[i].isBypass || data.parameters[i].toggled) {
range.setAttribute("min", 0);
range.setAttribute("max", 1);
range.setAttribute("step", 1);
} else {
range.setAttribute("min", 0);
range.setAttribute("max", 1);
range.setAttribute("step", data.parameters[i].integer ? 1 / (data.parameters[i].maximum - data.parameters[i].minimum) : "any");
}
range.value = unmap(i, data.parameters[i].defaultValue);
if (data.parameters[i].direction == "output")
range.setAttribute("readonly", "true");
else {
let index = i;
range.addEventListener("input",
async function (ev) {
await setParameter(index, map(index, parseFloat(ev.target.value)));
});
}
var range = document.createElement("input");
range.classList.add("range");
range.setAttribute("type", "range");
range.setAttribute("id", "p" + i);
range.setAttribute("name", "p" + i);
if (data.parameters[i].isBypass || data.parameters[i].toggled) {
range.setAttribute("min", 0);
range.setAttribute("max", 1);
range.setAttribute("step", 1);
} else {
range.setAttribute("min", 0);
range.setAttribute("max", 1);
range.setAttribute("step", data.parameters[i].integer ? 1 / (data.parameters[i].maximum - data.parameters[i].minimum) : "any");
}
range.value = unmap(i, data.parameters[i].defaultValue);
if (data.parameters[i].direction == "output")
range.setAttribute("readonly", "true");
else {
let index = i;
range.addEventListener("input",
async function (ev) {
await setParameter(index, map(index, parseFloat(ev.target.value)));
});
}
div.appendChild(label);
div.appendChild(document.createElement("br"));
div.appendChild(range);
paramsElem.appendChild(div);
}
div.appendChild(label);
div.appendChild(document.createElement("br"));
div.appendChild(range);
paramsElem.appendChild(div);
}
outParamInterval = setInterval(
async function () {
for (var i = 0; i < data.parameters.length; i++)
if (data.parameters[i].direction == "output") {
document.getElementById("p" + i).value = unmap(i, await getParameter(i));
}
}, 50);
topButtonElem.value = "STOP";
audioStarted = true;
} else
alert("Could not start audio");
}
} else {
await requestAudioPermission();
var interval = setInterval(
async function () {
if (!await needAudioPermission()) {
gotAudioPermission();
clearInterval(interval);
}
}, 50);
}
});
outParamInterval = setInterval(
async function () {
for (var i = 0; i < data.parameters.length; i++)
if (data.parameters[i].direction == "output") {
document.getElementById("p" + i).value = unmap(i, await getParameter(i));
}
}, 50);
topButtonElem.value = "STOP";
audioStarted = true;
} else
alert("Could not start audio");
}
} else {
await requestAudioPermission();
var interval = setInterval(
async function () {
if (!await needAudioPermission()) {
gotAudioPermission();
clearInterval(interval);
}
}, 50);
}
});
};
function gotAudioPermission() {
hasAudioPermission = true;
topButtonElem.value = "START";
}
</script>
<style>
* {
margin: 0;
padding: 0;
user-select: none;
}
</script>
<style>
* {
margin: 0;
padding: 0;
user-select: none;
}
body {
margin: 1em;
}
body {
margin: 1em;
}
#topButton {
width: 100%;
border: 0;
background-color: #04aa6d;
color: white;
padding: 0.5em;
text-align: center;
margin-bottom: 1em;
padding: 1em;
}
#topButton {
width: 100%;
border: 0;
background-color: #04aa6d;
color: white;
padding: 0.5em;
text-align: center;
margin-bottom: 1em;
padding: 1em;
}
.range {
width: 90%;
}
</style>
</head>
<body>
<input id="topButton" type="button">
<div id="params"></div>
</body>
.range {
width: 90%;
}
</style>
</head>
<body>
<input id="topButton" type="button">
<div id="params"></div>
</body>
</html>

View File

@ -1,7 +1,7 @@
/*
* Tibia
*
* Copyright (C) 2023, 2024 Orastron Srl unipersonale
* Copyright (C) 2023-2025 Orastron Srl unipersonale
*
* Tibia is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -25,74 +25,63 @@
#include "plugin_api.h"
#include "plugin.h"
#if PARAMETERS_N > 0
# include <algorithm>
#include <algorithm>
#endif
#if PARAMETERS_N + NUM_MIDI_INPUTS > 0
# include <mutex>
#include <mutex>
#endif
#include <vector>
#define MINIAUDIO_IMPLEMENTATION
#define MA_NO_RUNTIME_LINKING
#include "miniaudio.h"
#define BLOCK_SIZE 32
#ifdef PARAM_OUT_CPU_INDEX
# include "fatica.h"
#endif
static ma_device device;
static ma_device_config deviceConfig;
char device_inited = 0;
static plugin instance;
static void *mem;
static ma_device device;
static ma_device_config deviceConfig;
char device_inited = 0;
static plugin instance;
static void * mem;
#if NUM_NON_OPT_CHANNELS_IN > NUM_CHANNELS_IN
float zero[BLOCK_SIZE];
float zero[BLOCK_SIZE];
#endif
#if NUM_CHANNELS_IN > 0
float x_buf[NUM_CHANNELS_IN * BLOCK_SIZE];
float *x_in[NUM_CHANNELS_IN];
float x_buf[NUM_CHANNELS_IN * BLOCK_SIZE];
float * x_in[NUM_CHANNELS_IN];
#endif
#if NUM_ALL_CHANNELS_IN > 0
const float *x[NUM_ALL_CHANNELS_IN];
const float * x[NUM_ALL_CHANNELS_IN];
#else
const float **x;
const float ** x;
#endif
#if NUM_NON_OPT_CHANNELS_OUT > 0
float y_buf[NUM_NON_OPT_CHANNELS_OUT * BLOCK_SIZE];
float y_buf[NUM_NON_OPT_CHANNELS_OUT * BLOCK_SIZE];
#endif
#if NUM_CHANNELS_OUT > 0
float *y_out[NUM_CHANNELS_OUT];
float * y_out[NUM_CHANNELS_OUT];
#endif
#if NUM_ALL_CHANNELS_OUT > 0
float *y[NUM_ALL_CHANNELS_OUT];
float * y[NUM_ALL_CHANNELS_OUT];
#else
float **y;
float ** y;
#endif
#if PARAMETERS_N > 0
std::mutex mutex;
float param_values[PARAMETERS_N];
float param_values_prev[PARAMETERS_N];
std::mutex mutex;
float param_values[PARAMETERS_N];
float param_values_prev[PARAMETERS_N];
#endif
#if NUM_MIDI_INPUTS > 0
CFStringRef midiClientName = NULL;
MIDIClientRef midiClient = NULL;
CFStringRef midiInputName = NULL;
MIDIPortRef midiPort = NULL;
CFStringRef midiClientName = NULL;
MIDIClientRef midiClient = NULL;
CFStringRef midiInputName = NULL;
MIDIPortRef midiPort = NULL;
#define MIDIBUFFERLEN 1023
uint8_t midiBuffer[MIDIBUFFERLEN];
int midiBuffer_i = 0;
#endif
#ifdef PARAM_OUT_CPU_INDEX
float cpu_meter = 0.f;
float sample_rate = 1.f;
uint8_t midiBuffer[MIDIBUFFERLEN];
int midiBuffer_i = 0;
#endif
static void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) {
(void)pDevice;
#ifdef PARAM_OUT_CPU_INDEX
const unsigned long long processTimeStart = fatica_time_process();
#endif
#if defined(__aarch64__)
uint64_t fpcr;
__asm__ __volatile__ ("mrs %0, fpcr" : "=r"(fpcr));
@ -103,15 +92,8 @@ static void data_callback(ma_device* pDevice, void* pOutput, const void* pInput,
if (mutex.try_lock()) {
# if PARAMETERS_N > 0
for (size_t i = 0; i < PARAMETERS_N; i++) {
if (param_data[i].out) {
# ifdef PARAM_OUT_CPU_INDEX
if (i == PARAM_OUT_CPU_INDEX) {
param_values_prev[i] = param_values[i] = cpu_meter;
continue;
}
# endif
if (param_data[i].out)
param_values_prev[i] = param_values[i] = plugin_get_parameter(&instance, i);
}
else if (param_values_prev[i] != param_values[i]) {
plugin_set_parameter(&instance, i, param_values[i]);
param_values_prev[i] = param_values[i];
@ -170,11 +152,6 @@ static void data_callback(ma_device* pDevice, void* pOutput, const void* pInput,
#if defined(__aarch64__)
__asm__ __volatile__ ("msr fpcr, %0" : : "r"(fpcr));
#endif
#ifdef PARAM_OUT_CPU_INDEX
const unsigned long long processTimeEnd = fatica_time_process();
fatica_cpu_meter(&cpu_meter, processTimeStart, processTimeEnd, frameCount, sample_rate);
#endif
}
#if (NUM_MIDI_INPUTS > 0)
@ -187,6 +164,7 @@ void (^midiNotifyBlock)(const MIDINotification *message) = ^(const MIDINotificat
};
void (^midiReceiveBlock)(const MIDIEventList *evtlist, void *srcConnRefCon) = ^(const MIDIEventList *evtlist, void *srcConnRefCon) {
(void)srcConnRefCon;
const MIDIEventPacket *p = evtlist->packet;
for (UInt32 i = 0; i < evtlist->numPackets; i++) {
for (UInt32 j = 0; j < p->wordCount; j++) {
@ -199,7 +177,7 @@ void (^midiReceiveBlock)(const MIDIEventList *evtlist, void *srcConnRefCon) = ^(
continue;
mutex.lock();
if (midiBuffer_i < MIDIBUFFERLEN - 3) {
midiBuffer[midiBuffer_i ] = t[2];
midiBuffer[midiBuffer_i ] = t[2];
midiBuffer[midiBuffer_i + 1] = t[1];
midiBuffer[midiBuffer_i + 2] = t[0];
midiBuffer_i += 3;
@ -213,6 +191,11 @@ void (^midiReceiveBlock)(const MIDIEventList *evtlist, void *srcConnRefCon) = ^(
extern "C"
char audioStart() {
#ifdef STATE_DSP_CUSTOM
(void)plugin_state_load;
(void)plugin_state_save;
#endif
#if NUM_CHANNELS_IN + NUM_CHANNELS_OUT > 0
# if NUM_CHANNELS_IN == 0
deviceConfig = ma_device_config_init(ma_device_type_playback);
@ -225,26 +208,26 @@ char audioStart() {
deviceConfig = ma_device_config_init(ma_device_type_playback);
#endif
deviceConfig.periodSizeInFrames = BLOCK_SIZE;
deviceConfig.periods = 1;
deviceConfig.performanceProfile = ma_performance_profile_low_latency;
deviceConfig.noPreSilencedOutputBuffer = 1;
deviceConfig.noClip = 0;
deviceConfig.noDisableDenormals = 0;
deviceConfig.noFixedSizedCallback = 1;
deviceConfig.dataCallback = data_callback;
deviceConfig.capture.pDeviceID = NULL;
deviceConfig.capture.format = ma_format_f32;
deviceConfig.capture.channels = NUM_CHANNELS_IN;
deviceConfig.capture.shareMode = ma_share_mode_shared;
deviceConfig.playback.pDeviceID = NULL;
deviceConfig.playback.format = ma_format_f32;
deviceConfig.periodSizeInFrames = BLOCK_SIZE;
deviceConfig.periods = 1;
deviceConfig.performanceProfile = ma_performance_profile_low_latency;
deviceConfig.noPreSilencedOutputBuffer = 1;
deviceConfig.noClip = 0;
deviceConfig.noDisableDenormals = 0;
deviceConfig.noFixedSizedCallback = 1;
deviceConfig.dataCallback = data_callback;
deviceConfig.capture.pDeviceID = NULL;
deviceConfig.capture.format = ma_format_f32;
deviceConfig.capture.channels = NUM_CHANNELS_IN;
deviceConfig.capture.shareMode = ma_share_mode_shared;
deviceConfig.playback.pDeviceID = NULL;
deviceConfig.playback.format = ma_format_f32;
#if NUM_CHANNELS_IN + NUM_CHANNELS_OUT > 0
deviceConfig.playback.channels = NUM_CHANNELS_OUT;
deviceConfig.playback.channels = NUM_CHANNELS_OUT;
#else
deviceConfig.playback.channels = 1; // Fake & muted
deviceConfig.playback.channels = 1; // Fake & muted
#endif
deviceConfig.playback.shareMode = ma_share_mode_shared;
deviceConfig.playback.shareMode = ma_share_mode_shared;
if (ma_device_init(NULL, &deviceConfig, &device) != MA_SUCCESS)
return false;
@ -277,10 +260,10 @@ char audioStart() {
#endif
plugin_callbacks cbs = {
/* .handle = */ NULL,
/* .format = */ "ios",
/* .get_bindir = */ NULL,
/* .get_datadir = */ NULL
/* .handle = */ NULL,
/* .format = */ "ios",
/* .get_bindir = */ NULL,
/* .get_datadir = */ NULL
};
plugin_init(&instance, &cbs);
@ -293,9 +276,6 @@ char audioStart() {
#endif
plugin_set_sample_rate(&instance, (float)device.sampleRate);
#ifdef PARAM_OUT_CPU_INDEX
sample_rate = (float)device.sampleRate;
#endif
size_t req = plugin_mem_req(&instance);
if (req != 0) {

View File

@ -26,6 +26,5 @@ module.exports = function (data, api) {
api.generateFileFromTemplateFile(`src${sep}index.html`, `src${sep}index.html`, data);
api.copyFile(`src${sep}native.mm`, `src${sep}native.mm`);
api.copyFile(`src${sep}app-Bridging-Header.h`, `src${sep}app-Bridging-Header.h`);
api.copyFile(`src${sep}app.swift`, `src${sep}app.swift`, data);
api.copyFile(`..${sep}common${sep}fatica.h`, `src${sep}fatica.h`);
api.copyFile(`src${sep}app.swift`, `src${sep}app.swift`);
};

View File

@ -1,7 +1,7 @@
#
# Tibia
#
# Copyright (C) 2024 Orastron Srl unipersonale
# Copyright (C) 2024, 2025 Orastron Srl unipersonale
#
# Tibia is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@ -18,13 +18,30 @@
# File author: Stefano D'Angelo
#
SHELL := bash -o pipefail
TEMPLATE := lv2
include vars.mk
COMMON_DIR ?= .
DATA_DIR ?= .
PLUGIN_DIR ?= src
API_DIR ?= $(PLUGIN_DIR)
MKINC_DIR ?= $(COMMON_DIR)
BUNDLE_DIR := $(BUNDLE_NAME).lv2
BUILD_BIN_DIR := build/$(BUNDLE_DIR)
BUILD_DATA_DIR := build/$(BUNDLE_DIR)
-include $(MKINC_DIR)/vars-pre.mk
ifeq ($(OS), Windows_NT)
DLL_SUFFIX := .dll
UI_TYPE := WindowsUI
LV2DIR := $(shell echo '$(COMMONPROGRAMFILES)' | sed 's:\\:/:g')/LV2
LV2DIR_USER := $(shell echo '$(APPDATA)' | sed 's:\\:/:g')/LV2
LV2DIR := $(subst \,/,$(COMMONPROGRAMFILES))/LV2
LV2DIR_USER := $(subst \,/,$(APPDATA))/LV2
CC := gcc
CXX := g++
else
@ -47,30 +64,21 @@ else
endif
endif
COMMON_DIR := $(or $(COMMON_DIR),.)
DATA_DIR := $(or $(DATA_DIR),.)
PLUGIN_DIR := $(or $(PLUGIN_DIR),src)
CFLAGS := -O3 -Wall -Wpedantic -Wextra
CFLAGS_ALL := -I$(DATA_DIR)/src -I$(PLUGIN_DIR) $(shell pkg-config --cflags lv2) -fPIC $(CFLAGS) $(CFLAGS_EXTRA)
CFLAGS_ALL := -I$(DATA_DIR)/src -I$(PLUGIN_DIR) -I$(API_DIR) $(shell pkg-config --cflags lv2) -fPIC -fvisibility=hidden $(CFLAGS_EXTRA) $(CFLAGS)
LDFLAGS :=
LDFLAGS_ALL := -shared $(shell pkg-config --libs lv2) $(LDFLAGS) $(LDFLAGS_EXTRA)
LDFLAGS_ALL := -shared $(shell pkg-config --libs lv2) $(LDFLAGS_EXTRA) $(LDFLAGS)
CXXFLAGS := $(CFLAGS)
CXXFLAGS_ALL := -I$(DATA_DIR)/src -I$(PLUGIN_DIR) -fPIC $(CXXFLAGS) $(CXXFLAGS_EXTRA)
CXXFLAGS_ALL := -I$(DATA_DIR)/src -I$(PLUGIN_DIR) -I$(API_DIR) $(shell pkg-config --cflags lv2) -fPIC -fvisibility=hidden $(CXXFLAGS_EXTRA) $(CXXFLAGS)
ifeq ($(UNAME_S), Darwin)
CFLAGS_ALL := $(CFLAGS_ALL) -arch arm64 -arch x86_64
LDFLAGS_ALL := $(LDFLAGS_ALL) -arch arm64 -arch x86_64
CXXFLAGS_ALL := $(CXXFLAGS_ALL) -arch arm64 -arch x86_64
CFLAGS_ALL := $(CFLAGS_ALL) -arch arm64 -arch x86_64
LDFLAGS_ALL := $(LDFLAGS_ALL) -arch arm64 -arch x86_64
CXXFLAGS_ALL := $(CXXFLAGS_ALL) -arch arm64 -arch x86_64
endif
BUNDLE_DIR := $(BUNDLE_NAME).lv2
BUNDLE_DATA_PATH := build/$(BUNDLE_DIR)
BUNDLE_BIN_PATH := build/$(BUNDLE_DIR)
DLL_FILE := $(BUNDLE_NAME)$(DLL_SUFFIX)
C_SRCS := $(COMMON_DIR)/src/lv2.c $(C_SRCS_EXTRA)
@ -82,9 +90,16 @@ M_OBJS := $(addprefix build/obj/, $(notdir $(M_SRCS:.m=.o)))
CXX_SRCS := $(CXX_SRCS_EXTRA)
CXX_OBJS := $(addprefix build/obj/, $(notdir $(CXX_SRCS:.cpp=.o)))
DIRS := build build/$(BUNDLE_DIR) build/obj
ALL := build/$(BUNDLE_DIR)/manifest.ttl build/$(BUNDLE_DIR)/$(DLL_FILE)
-include $(COMMON_DIR)/vars-extra.mk
STRIP_ALL := build/$(BUNDLE_DIR)/$(DLL_FILE) build/$(BUNDLE_DIR)/manifest.ttl
STRIP_PREREQS := $(STRIP_ALL)
.PHONY: all clean strip install install-user
-include $(MKINC_DIR)/vars-extra.mk
all: $(ALL)
@ -99,43 +114,55 @@ build/$(BUNDLE_DIR)/$(DLL_FILE): $(C_OBJS) $(M_OBJS) $(CXX_OBJS) | build/$(BUNDL
$(CXX) $^ -o $@ $(CFLAGS_ALL) $(CXXFLAGS_ALL) $(LDFLAGS_ALL)
endif
build/$(BUNDLE_DIR) build/obj:
$(DIRS):
mkdir -p $@
clean:
rm -fr build
strip: $(STRIP_PREREQS)
strip -Sx 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
$(eval TMP := $(shell mktemp /tmp/index.XXXXXX))
sed '/^[[:space:]]*$$/d' build/$(BUNDLE_DIR)/manifest.ttl > $(TMP) || (rm $(TMP) && exit 1)
cp $(TMP) build/$(BUNDLE_DIR)/manifest.ttl || (rm $(TMP) && exit 1)
rm $(TMP)
install: all
@for d in `find build/$(BUNDLE_DIR) -type d`; do \
d=`echo $$d | sed 's:^build/::'` ; \
echo mkdir -m 0755 -p "$(LV2DIR)/$$d"; \
mkdir -m 0755 -p "$(LV2DIR)/$$d"; \
mkdir -m 0755 -p "$(LV2DIR)/$$d" || exit 1; \
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"; \
install -m $$m $$f "$(LV2DIR)/$$d" || exit 1; \
done
install-user: all
@for d in `find build/$(BUNDLE_DIR) -type d`; do \
d=`echo $$d | sed 's:^build/::'` ; \
echo mkdir -m 0755 -p "$(LV2DIR_USER)/$$d"; \
mkdir -m 0755 -p "$(LV2DIR_USER)/$$d"; \
mkdir -m 0755 -p "$(LV2DIR_USER)/$$d" || exit 1; \
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"; \
install -m $$m $$f "$(LV2DIR_USER)/$$d" || exit 1; \
done
-include $(COMMON_DIR)/rules-extra.mk
-include $(MKINC_DIR)/rules-extra.mk
.PHONY: all clean install install-user
.PHONY: $(PHONY)
.SECONDEXPANSION:
@ -150,4 +177,6 @@ $(M_OBJS): build/obj/%.o: $$(filter $$(PERCENT)/$$(basename $$(notdir $$@)).m,$$
$(CXX_OBJS): build/obj/%.o: $$(filter $$(PERCENT)/$$(basename $$(notdir $$@)).cpp,$$(CXX_SRCS)) | build/obj
$(CXX) $^ -o $@ -c $(CXXFLAGS_ALL)
-include $(COMMON_DIR)/rules-secondexp-extra.mk
-include $(MKINC_DIR)/rules-secondexp-extra.mk
.DELETE_ON_ERROR:

View File

@ -18,17 +18,27 @@
# File author: Stefano D'Angelo
#
BUNDLE_NAME := {{=it.product.bundleName}}
BUNDLE_NAME := {{=it.product.bundleName}}
COMMON_DIR := {{=it.lv2_make?.commonDir ?? (it.make?.commonDir ?? "")}}
DATA_DIR := {{=it.lv2_make?.dataDir ?? (it.make?.dataDir ?? "")}}
PLUGIN_DIR := {{=it.lv2_make?.pluginDir ?? (it.make?.pluginDir ?? "")}}
{{?(it.lv2_make?.commonDir || it.make?.commonDir)}}
COMMON_DIR := {{=it.lv2_make?.commonDir ?? (it.make?.commonDir ?? "")}}
{{?}}
{{?(it.lv2_make?.dataDir || it.make?.dataDir)}}
DATA_DIR := {{=it.lv2_make?.dataDir ?? (it.make?.dataDir ?? "")}}
{{?}}
{{?(it.lv2_make?.pluginDir || it.make?.pluginDir)}}
PLUGIN_DIR := {{=it.lv2_make?.pluginDir ?? (it.make?.pluginDir ?? "")}}
{{?}}
{{?(it.lv2_make?.apiDir || it.make?.apiDir)}}
API_DIR := {{=it.lv2_make?.apiDir ?? (it.make?.apiDir ?? "")}}
{{?}}
{{?(it.lv2_make?.mkincDir || it.make?.mkincDir)}}
MKINC_DIR := {{=it.lv2_make?.mkincDir ?? (it.make?.mkincDir ?? "")}}
{{?}}
C_SRCS_EXTRA := {{=it.make?.cSrcs ?? ""}} {{=it.lv2_make?.cSrcs ?? ""}}
M_SRCS_EXTRA := {{=it.make?.mSrcs ?? ""}} {{=it.lv2_make?.mSrcs ?? ""}}
CXX_SRCS_EXTRA := {{=it.make?.cxxSrcs ?? ""}} {{=it.lv2_make?.cxxSrcs ?? ""}}
CFLAGS_EXTRA := {{=it.make?.cflags ?? ""}} {{=it.lv2_make?.cflags ?? ""}}
CXXFLAGS_EXTRA := {{=it.make?.cxxflags ?? ""}} {{=it.lv2_make?.cxxflags ?? ""}}
LDFLAGS_EXTRA := {{=it.make?.ldflags ?? ""}} {{=it.lv2_make?.ldflags ?? ""}}
{{?it.make?.extra}}
{{=it.make.extra}}
{{?}}
{{?it.lv2_make?.extra}}
{{=it.lv2_make.extra}}
{{?}}

View File

@ -15,18 +15,26 @@
doap:maintainer [
a foaf:Organization ;
foaf:name "{{=it.company.name}}" ;
foaf:mbox "{{=it.company.email}}" ;
foaf:mbox <mailto:{{=it.company.email}}> ;
rdfs:seeAlso {{=it.tibia.lv2.ttlURI(it.company.url)}}
] ;
lv2:minorVersion {{=/^([0-9]+)\./.exec(it.lv2.version)[1]}} ;
lv2:microVersion {{=/^[0-9]+\.([0-9]+)/.exec(it.lv2.version)[1]}} ;
{{?it.tibia.lv2.ports.find(p => p.type == "midi")}}
{{?(it.tibia.lv2.ports.find(p => p.type == "midi" && !p.optional))}}
lv2:requiredFeature urid:map ;
lv2:optionalFeature log:log ;
{{??}}
lv2:optionalFeature urid:map ;
{{?}}
lv2:optionalFeature log:log ;
lv2:optionalFeature lv2:hardRTCapable ;
{{?it.lv2.ui}}
{{?(it.product.state && it.product.state.dspCustom)}}
lv2:optionalFeature state:threadSafeRestore ;
{{?}}
{{?it.product.ui}}
ui:ui {{=it.tibia.lv2.ttlURI(it.lv2.ui.uri)}} ;
{{?}}
{{?(it.product.state && it.product.state.dspCustom)}}
lv2:extensionData state:interface ;
{{?}}
lv2:port [
{{~it.tibia.lv2.ports :p:i}}
@ -121,7 +129,7 @@
{{?}}
{{~}}
{{?it.lv2.ui}}
{{?it.product.ui}}
{{=it.tibia.lv2.ttlURI(it.lv2.ui.uri)}}
a ui:@UI_TYPE@ ;
ui:binary <{{=it.product.bundleName}}@DLL_SUFFIX@> ;

View File

@ -1,7 +1,7 @@
/*
* Tibia
*
* Copyright (C) 2024 Orastron Srl unipersonale
* Copyright (C) 2024, 2025 Orastron Srl unipersonale
*
* Tibia is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -27,6 +27,10 @@
#define DATA_PRODUCT_CONTROL_INPUTS_N {{=it.product.parameters.filter(x => x.direction == "input").length}}
#define DATA_PRODUCT_CONTROL_OUTPUTS_N {{=it.product.parameters.filter(x => x.direction == "output").length}}
{{?it.product.buses.find(x => x.type == "midi" && !x.optional)}}
#define DATA_PRODUCT_MIDI_REQUIRED
{{?}}
#if DATA_PRODUCT_MIDI_INPUTS_N > 0
static uint32_t midi_in_index[DATA_PRODUCT_MIDI_INPUTS_N] = {
{{~it.tibia.lv2.ports.filter(x => x.type == "midi" && x.direction == "input") :p}}{{=p.busIndex}}, {{~}}
@ -63,19 +67,22 @@ static struct {
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.product.parameters.find(x => x.direction == "output" && x.isCpumeter)}}
# define PARAM_OUT_CPU_INDEX {{=it.product.parameters.indexOf(it.product.parameters.find(x => x.direction == "output" && x.isCpumeter))}}
{{?}}
#endif
{{?it.lv2.ui}}
{{?it.product.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}}
{{?}}
{{?(it.product.ui || (it.product.state && it.product.state.dspCustom))}}
#if DATA_PRODUCT_CONTROL_INPUTS_N > 0
static uint32_t index_to_param[DATA_PRODUCT_CONTROL_INPUTS_N + DATA_PRODUCT_CONTROL_OUTPUTS_N] = {
{{~it.tibia.lv2.ports.filter(x => x.type == "control").map((e, i) => ({ i: i, pi: e.paramIndex })).sort((a, b) => a.pi - b.pi) :p}}{{=p.i + it.tibia.lv2.ports.filter(x => x.type != "control").length}}, {{~}}
};
#endif
{{?}}
{{?it.product.state && it.product.state.dspCustom}}
#define DATA_STATE_DSP_CUSTOM
{{?}}

View File

@ -1,7 +1,7 @@
/*
* Tibia
*
* Copyright (C) 2024 Orastron Srl unipersonale
* Copyright (C) 2024, 2025 Orastron Srl unipersonale
*
* Tibia is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -31,18 +31,23 @@
#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"
#include <lv2/core/lv2.h>
#include <lv2/core/lv2_util.h>
#include <lv2/log/log.h>
#include <lv2/log/logger.h>
#include <lv2/urid/urid.h>
#if (DATA_PRODUCT_MIDI_INPUTS_N + DATA_PRODUCT_MIDI_OUTPUTS_N > 0) || defined(DATA_STATE_DSP_CUSTOM)
# include <lv2/atom/atom.h>
# if DATA_PRODUCT_MIDI_INPUTS_N + DATA_PRODUCT_MIDI_OUTPUTS_N > 0
# include <lv2/atom/util.h>
# include <lv2/midi/midi.h>
# endif
#endif
#ifdef DATA_UI
# include "lv2/ui/ui.h"
# include <lv2/ui/ui.h>
#endif
#ifdef DATA_STATE_DSP_CUSTOM
# include <lv2/state/state.h>
#endif
#include <string.h>
@ -52,10 +57,29 @@
# include <pmmintrin.h>
#endif
#ifdef PARAM_OUT_CPU_INDEX
# include "fatica.h"
#if (DATA_PRODUCT_CONTROL_INPUTS_N > 0) && defined(DATA_STATE_DSP_CUSTOM)
# ifdef __cplusplus
# include <atomic>
# else
# include <stdatomic.h>
# endif
# 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);
}
@ -69,37 +93,52 @@ static float adjust_param(size_t index, float value) {
value = (int32_t)(value + (value >= 0.f ? 0.5f : -0.5f));
return clampf(value, param_data[index].min, param_data[index].max);
}
#endif
typedef struct {
plugin p;
plugin p;
float sample_rate;
#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
#if DATA_PRODUCT_AUDIO_OUTPUT_CHANNELS_N > 0
float *y[DATA_PRODUCT_AUDIO_OUTPUT_CHANNELS_N];
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];
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];
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];
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];
float params[DATA_PRODUCT_CONTROL_INPUTS_N];
# ifdef DATA_STATE_DSP_CUSTOM
float params_sync[DATA_PRODUCT_CONTROL_INPUTS_N];
# ifdef __cplusplus
std::atomic_flag sync_lock_flag;
# else
atomic_flag sync_lock_flag;
# endif
char synced;
char loaded;
# endif
#endif
void *mem;
char *bundle_path;
LV2_Log_Logger logger;
LV2_URID_Map *map;
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;
LV2_URID uri_midi_MidiEvent;
#endif
#ifdef PARAM_OUT_CPU_INDEX
float cpu_meter;
float sample_rate;
#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;
@ -108,11 +147,53 @@ static const char * get_bundle_path_cb(void *handle) {
return instance->bundle_path;
}
#ifdef DATA_STATE_DSP_CUSTOM
static void state_lock_cb(void *handle) {
plugin_instance * i = (plugin_instance *)handle;
# ifdef __cplusplus
while (i->sync_lock_flag.test_and_set())
# else
while (atomic_flag_test_and_set(&i->sync_lock_flag))
# endif
yield();
i->synced = 0;
}
static void state_unlock_cb(void *handle) {
plugin_instance * i = (plugin_instance *)handle;
# ifdef __cplusplus
i->sync_lock_flag.clear();
# else
atomic_flag_clear(&i->sync_lock_flag);
# endif
}
static int state_write_cb(void *handle, const char *data, size_t length) {
plugin_instance * i = (plugin_instance *)handle;
return i->state_store(i->state_handle, i->uri_state_data, data, length, i->uri_atom_Chunk, LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE);
}
# if DATA_PRODUCT_CONTROL_INPUTS_N > 0
static void state_set_parameter_cb(void *handle, size_t index, float value) {
plugin_instance * i = (plugin_instance *)handle;
size_t idx = index_to_param[index] - CONTROL_INPUT_INDEX_OFFSET;
value = adjust_param(idx, value);
i->params_sync[idx] = value;
i->loaded = 1;
}
# endif
#endif
static LV2_Handle instantiate(const struct LV2_Descriptor * descriptor, double sample_rate, const char * bundle_path, const LV2_Feature * const * features) {
(void)descriptor;
(void)bundle_path;
plugin_instance *instance = malloc(sizeof(plugin_instance));
// make C++ compilers happy
const char * missing;
plugin_callbacks cbs;
size_t req;
plugin_instance *instance = (plugin_instance *)malloc(sizeof(plugin_instance));
if (instance == NULL)
goto err_instance;
@ -121,31 +202,39 @@ static LV2_Handle instantiate(const struct LV2_Descriptor * descriptor, double s
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,
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);
#ifdef DATA_PRODUCT_MIDI_REQUIRED
goto err_urid;
#endif
}
#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);
if (instance->map)
instance->uri_midi_MidiEvent = instance->map->map(instance->map->handle, LV2_MIDI__MidiEvent);
#endif
#ifdef DATA_STATE_DSP_CUSTOM
if (instance->map) {
instance->uri_atom_Chunk = instance->map->map(instance->map->handle, LV2_ATOM__Chunk);
instance->uri_state_data = instance->map->map(instance->map->handle, DATA_LV2_URI "#state_data");
}
#endif
plugin_callbacks cbs = {
/* .handle = */ (void *)instance,
/* .format = */ "lv2",
/* .get_bindir = */ get_bundle_path_cb,
/* .get_datadir = */ get_bundle_path_cb
};
cbs.handle = (void *)instance;
cbs.format = "lv2";
cbs.get_bindir = get_bundle_path_cb;
cbs.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);
instance->sample_rate = (float)sample_rate;
plugin_set_sample_rate(&instance->p, instance->sample_rate);
req = plugin_mem_req(&instance->p);
if (req != 0) {
instance->mem = malloc(req);
if (instance->mem == NULL) {
@ -176,16 +265,14 @@ static LV2_Handle instantiate(const struct LV2_Descriptor * descriptor, double s
for (uint32_t i = 0; i < DATA_PRODUCT_CONTROL_INPUTS_N + DATA_PRODUCT_CONTROL_OUTPUTS_N; i++)
instance->c[i] = NULL;
#endif
#ifdef PARAM_OUT_CPU_INDEX
instance->cpu_meter = 0.f;
instance->sample_rate = sample_rate;
#endif
return instance;
err_mem:
plugin_fini(&instance->p);
#ifdef DATA_PRODUCT_MIDI_REQUIRED
err_urid:
#endif
free(instance->bundle_path);
err_bundle_path:
free(instance);
@ -197,34 +284,34 @@ static void connect_port(LV2_Handle instance, uint32_t port, void * data_locatio
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;
i->x[port] = (const float *)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;
i->y[port] = (float *)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;
i->x_midi[port] = (const LV2_Atom_Sequence *)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;
i->y_midi[port] = (LV2_Atom_Sequence *)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;
i->c[port] = (float *)data_location;
#endif
}
@ -235,6 +322,19 @@ static void activate(LV2_Handle instance) {
i->params[j] = i->c[j] != NULL ? *i->c[j] : param_data[j].def;
plugin_set_parameter(&i->p, param_data[j].index, i->params[j]);
}
# ifdef DATA_STATE_DSP_CUSTOM
for (uint32_t j = 0; j < DATA_PRODUCT_CONTROL_INPUTS_N; j++)
i->params_sync[j] = i->params[j];
# ifdef __cplusplus
i->sync_lock_flag.clear();
# else
// why is this not correct?
// i->sync_lock_flag = ATOMIC_FLAG_INIT;
atomic_flag_clear(&i->sync_lock_flag);
# endif
i->synced = 1;
i->loaded = 0;
# endif
#endif
plugin_reset(&i->p);
}
@ -242,10 +342,6 @@ static void activate(LV2_Handle instance) {
static void run(LV2_Handle instance, uint32_t sample_count) {
plugin_instance * i = (plugin_instance *)instance;
#ifdef PARAM_OUT_CPU_INDEX
const unsigned long long processTimeStart = fatica_time_process();
#endif
#if defined(__aarch64__)
uint64_t fpcr;
__asm__ __volatile__ ("mrs %0, fpcr" : "=r"(fpcr));
@ -259,30 +355,73 @@ static void run(LV2_Handle instance, uint32_t sample_count) {
#endif
#if DATA_PRODUCT_CONTROL_INPUTS_N > 0
# ifdef DATA_STATE_DSP_CUSTOM
# ifdef __cplusplus
_Bool locked = !i->sync_lock_flag.test_and_set();
# else
_Bool locked = !atomic_flag_test_and_set(&i->sync_lock_flag);
# endif
if (locked) {
if (!i->synced) {
if (i->loaded) {
for (uint32_t j = 0; j < DATA_PRODUCT_CONTROL_INPUTS_N; j++) {
i->params[j] = i->params_sync[j];
plugin_set_parameter(&i->p, param_data[j].index, i->params[j]);
}
} else {
for (uint32_t j = 0; j < DATA_PRODUCT_CONTROL_INPUTS_N; j++)
if (i->params[j] != i->params_sync[j]) {
i->params_sync[j] = i->params[j];
plugin_set_parameter(&i->p, param_data[j].index, i->params[j]);
}
}
}
i->synced = 1;
i->loaded = 0;
}
# endif
for (uint32_t j = 0; j < DATA_PRODUCT_CONTROL_INPUTS_N; j++) {
if (i->c[j] == NULL)
continue;
float v = adjust_param(j, *i->c[j]);
if (v != i->params[j]) {
i->params[j] = v;
plugin_set_parameter(&i->p, param_data[j].index, v);
# ifdef DATA_STATE_DSP_CUSTOM
if (locked) {
i->params_sync[j] = i->params[j];
# endif
plugin_set_parameter(&i->p, param_data[j].index, v);
# ifdef DATA_STATE_DSP_CUSTOM
}
# endif
}
}
# ifdef DATA_STATE_DSP_CUSTOM
if (locked)
# ifdef __cplusplus
i->sync_lock_flag.clear();
# else
atomic_flag_clear(&i->sync_lock_flag);
# endif
# endif
#endif
// from https://lv2plug.in/book
#if DATA_PRODUCT_MIDI_INPUTS_N > 0
for (size_t j = 0; j < DATA_PRODUCT_MIDI_INPUTS_N; j++) {
if (i->x_midi[j] == NULL)
continue;
LV2_ATOM_SEQUENCE_FOREACH(i->x_midi[j], ev) {
if (ev->body.type == i->uri_midi_MidiEvent) {
const uint8_t * data = (const uint8_t *)(ev + 1);
if ((data[0] & 0xf0) != 0xf0)
plugin_midi_msg_in(&i->p, midi_in_index[j], data);
// from https://lv2plug.in/book
if (i->map)
for (size_t j = 0; j < DATA_PRODUCT_MIDI_INPUTS_N; j++) {
if (i->x_midi[j] == NULL)
continue;
LV2_ATOM_SEQUENCE_FOREACH(i->x_midi[j], ev) {
if (ev->body.type == i->uri_midi_MidiEvent) {
const uint8_t * data = (const uint8_t *)(ev + 1);
if ((data[0] & 0xf0) != 0xf0)
plugin_midi_msg_in(&i->p, midi_in_index[j], data);
}
}
}
}
#endif
#if DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N > 0
@ -300,15 +439,8 @@ static void run(LV2_Handle instance, uint32_t 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) {
# ifdef PARAM_OUT_CPU_INDEX
if (k == PARAM_OUT_CPU_INDEX) {
*i->c[k] = i->cpu_meter;
continue;
}
# endif
if (i->c[k] != NULL)
*i->c[k] = plugin_get_parameter(&i->p, k);
}
}
#else
(void)plugin_get_parameter;
@ -320,12 +452,6 @@ static void run(LV2_Handle instance, uint32_t sample_count) {
_MM_SET_FLUSH_ZERO_MODE(flush_zero_mode);
_MM_SET_DENORMALS_ZERO_MODE(denormals_zero_mode);
#endif
#ifdef PARAM_OUT_CPU_INDEX
const unsigned long long processTimeEnd = fatica_time_process();
fatica_cpu_meter(&i->cpu_meter, processTimeStart, processTimeEnd, sample_count, i->sample_rate);
#endif
}
static void cleanup(LV2_Handle instance) {
@ -337,15 +463,79 @@ static void cleanup(LV2_Handle 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 = (const char *)retrieve(handle, i->uri_state_data, &length, &type, &xflags);
if (data == NULL) {
lv2_log_error(&i->logger, "Cannot restore state since property <%s> could not be retrieved\n", DATA_LV2_URI "#state_data");
return LV2_STATE_ERR_NO_PROPERTY;
}
plugin_state_callbacks cbs = {
/* .handle = */ (void *)i,
/* .lock = */ state_lock_cb,
/* .unlock = */ state_unlock_cb,
/* .write = */ NULL,
# if DATA_PRODUCT_CONTROL_INPUTS_N > 0
/* .set_parameter = */ state_set_parameter_cb
# endif
};
return plugin_state_load(&cbs, i->sample_rate, data, length) == 0 ? LV2_STATE_SUCCESS : LV2_STATE_ERR_UNKNOWN;
}
static const void * extension_data(const char * uri) {
static const LV2_State_Interface state = { state_save, state_restore };
if (!strcmp(uri, LV2_STATE__interface))
return &state;
return NULL;
}
#endif
static const LV2_Descriptor descriptor = {
/* .URI = */ DATA_LV2_URI,
/* .instantiate = */ instantiate,
/* .connect_port = */ connect_port,
/* .activate = */ activate,
/* .run = */ run,
/* .deactivate = */ NULL,
/* .cleanup = */ cleanup,
/* .extension_data = */ NULL
/* .URI = */ DATA_LV2_URI,
/* .instantiate = */ instantiate,
/* .connect_port = */ connect_port,
/* .activate = */ activate,
/* .run = */ run,
/* .deactivate = */ NULL,
/* .cleanup = */ cleanup,
#ifdef DATA_STATE_DSP_CUSTOM
/* .extension_data = */ extension_data
#else
/* .extension_data = */ NULL
#endif
};
LV2_SYMBOL_EXPORT const LV2_Descriptor * lv2_descriptor(uint32_t index) {
@ -354,34 +544,29 @@ LV2_SYMBOL_EXPORT const LV2_Descriptor * lv2_descriptor(uint32_t index) {
#ifdef DATA_UI
typedef struct {
plugin_ui *ui;
char *bundle_path;
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;
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) {
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);
}
}
@ -392,10 +577,12 @@ static void ui_set_parameter_cb(void *handle, size_t index, float value) {
instance->write(instance->controller, index, sizeof(float), 0, &value);
}
static void ui_set_parameter_end_cb(void *handle, size_t index) {
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);
}
}
@ -405,7 +592,12 @@ static LV2UI_Handle ui_instantiate(const LV2UI_Descriptor * descriptor, const ch
(void)descriptor;
(void)plugin_uri;
ui_instance *instance = malloc(sizeof(ui_instance));
// make C++ compilers happy
char has_parent;
void *parent;
plugin_ui_callbacks cbs;
ui_instance *instance = (ui_instance *)malloc(sizeof(ui_instance));
if (instance == NULL)
goto err_instance;
@ -413,35 +605,33 @@ static LV2UI_Handle ui_instantiate(const LV2UI_Descriptor * descriptor, const ch
if (instance->bundle_path == NULL)
goto err_bundle_path;
char has_parent = 0;
void *parent = NULL;
has_parent = 0;
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 (!strcmp(features[i]->URI, LV2_UI__touch)) {
# 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,
cbs.handle = (void *)instance;
cbs.format = "lv2";
cbs.get_bindir = ui_get_bundle_path_cb;
cbs.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
cbs.set_parameter_begin = ui_set_parameter_begin_cb;
cbs.set_parameter = ui_set_parameter_cb;
cbs.set_parameter_end = ui_set_parameter_end_cb;
# endif
};
# if DATA_PRODUCT_CONTROL_INPUTS_N > 0
instance->write = write_function;
instance->controller = controller;
@ -478,11 +668,18 @@ static void ui_port_event(LV2UI_Handle handle, uint32_t port_index, uint32_t buf
(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)));
} else
}
# 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
@ -500,15 +697,15 @@ static const void * ui_extension_data(const char * uri) {
}
static const LV2UI_Descriptor ui_descriptor = {
/* .URI = */ DATA_LV2_UI_URI,
/* .instantiate = */ ui_instantiate,
/* .cleanup = */ ui_cleanup,
/* .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,
/* .port_event = */ ui_port_event,
# else
/* .port_event = */ NULL,
/* .port_event = */ NULL,
# endif
/* .extension_data = */ ui_extension_data
/* .extension_data = */ ui_extension_data
};
LV2_SYMBOL_EXPORT const LV2UI_Descriptor * lv2ui_descriptor(uint32_t index) {

View File

@ -1,7 +1,7 @@
/*
* Tibia
*
* Copyright (C) 2024 Orastron Srl unipersonale
* Copyright (C) 2024, 2025 Orastron Srl unipersonale
*
* Tibia is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -34,6 +34,7 @@ module.exports = function (data, api, outputCommon, outputData) {
{ id: "pprops", uri: "http://lv2plug.in/ns/ext/port-props#" },
{ id: "rdf", uri: "http://www.w3.org/1999/02/22-rdf-syntax-ns#" },
{ id: "rdfs", uri: "http://www.w3.org/2000/01/rdf-schema#" },
{ id: "state", uri: "http://lv2plug.in/ns/extensions/state#" },
{ id: "ui", uri: "http://lv2plug.in/ns/extensions/ui#" },
{ id: "units", uri: "http://lv2plug.in/ns/extensions/units#" },
{ id: "urid", uri: "http://lv2plug.in/ns/ext/urid#" }
@ -67,7 +68,12 @@ module.exports = function (data, api, outputCommon, outputData) {
ports: [],
ttlURI: function (uri) {
return uri.charAt(0) == "@" ? uri.substring(1) : "<" + uri + ">";
if (uri.charAt(0) == "@") {
if (uri.indexOf("#") == -1)
return uri.substring(1);
uri = data.tibia.lv2.expandURI(uri);
}
return "<" + uri + ">";
},
expandURI: function (uri) {
@ -122,5 +128,4 @@ module.exports = function (data, api, outputCommon, outputData) {
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);
api.copyFile(`..${sep}common${sep}fatica.h`, `src${sep}fatica.h`);
};

View File

@ -1,7 +1,7 @@
#
# Tibia
#
# Copyright (C) 2023, 2024 Orastron Srl unipersonale
# Copyright (C) 2023-2025 Orastron Srl unipersonale
#
# Tibia is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@ -18,74 +18,85 @@
# File author: Stefano D'Angelo
#
TEMPLATE := vst3
include vars.mk
COMMON_DIR ?= .
DATA_DIR ?= .
PLUGIN_DIR ?= src
API_DIR ?= $(PLUGIN_DIR)
MKINC_DIR ?= $(COMMON_DIR)
BUNDLE_DIR := $(BUNDLE_NAME).vst3
ifeq ($(OS), Windows_NT)
DLL_SUFFIX := .vst3
PLATFORM := x86_64-win
VST3DIR := $(shell echo '$(COMMONPROGRAMFILES)' | sed 's:\\:/:g')/VST3
VST3DIR_USER := $(shell echo '$(LOCALAPPDATA)' | sed 's:\\:/:g')/Programs/Common/VST3
VST3DIR := $(subst \,/,$(COMMONPROGRAMFILES))/VST3
VST3DIR_USER := $(subst \,/,$(LOCALAPPDATA))/Programs/Common/VST3
CC := gcc
CXX := g++
MACHINE := $(shell $(CC) -dumpmachine | sed 's:-.*::g')
ifeq ($(MACHINE), x86_64)
VST3_PLATFORM := x86_64-win
endif
ifeq ($(MACHINE), aarch64)
VST3_PLATFORM := arm64-win
endif
else
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S), Darwin)
DLL_SUFFIX :=
PLATFORM := MacOS
VST3DIR := /Library/Audio/Plug-Ins/VST3
VST3DIR_USER := $(HOME)/Library/Audio/Plug-Ins/VST3
CC := clang
CXX := clang++
VST3_PLATFORM := MacOS
else
DLL_SUFFIX := .so
PLATFORM := $(shell uname -m)-linux
VST3DIR := /usr/local/lib/vst3
VST3DIR_USER := $(HOME)/.vst3
CC := gcc
CXX := g++
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 -fvisibility=hidden $(CFLAGS_EXTRA) $(CFLAGS)
LDFLAGS :=
LDFLAGS_ALL := -shared -lm $(LDFLAGS_EXTRA) $(LDFLAGS)
CXXFLAGS := $(CFLAGS)
CXXFLAGS_ALL := -I$(DATA_DIR)/src -I$(PLUGIN_DIR) -I$(API_DIR) -fPIC $(CXXFLAGS_EXTRA) $(CXXFLAGS)
ifeq ($(UNAME_S), Darwin)
CFLAGS_ALL := $(CFLAGS_ALL) -arch arm64 -arch x86_64
LDFLAGS_ALL := $(LDFLAGS_ALL) -arch arm64 -arch x86_64
CXXFLAGS_ALL := $(CXXFLAGS_ALL) -arch arm64 -arch x86_64
ifeq ($(HAS_UI), yes)
LDFLAGS_ALL := $(LDFLAGS_ALL) -Wl,-framework,Foundation -Wl,-framework,Cocoa -Wl,-framework,Corevideo
endif
endif
COMMON_DIR := $(or $(COMMON_DIR),.)
DATA_DIR := $(or $(DATA_DIR),.)
PLUGIN_DIR := $(or $(PLUGIN_DIR),src)
CFLAGS := -O3 -Wall -Wpedantic -Wextra
CFLAGS_ALL := -I$(DATA_DIR)/src -I$(PLUGIN_DIR) -fPIC $(CFLAGS) $(CFLAGS_EXTRA)
LDFLAGS :=
LDFLAGS_ALL := -shared -lm $(LDFLAGS) $(LDFLAGS_EXTRA)
CXXFLAGS := $(CFLAGS)
CXXFLAGS_ALL := -I$(DATA_DIR)/src -I$(PLUGIN_DIR) -fPIC $(CXXFLAGS) $(CXXFLAGS_EXTRA)
ifeq ($(UNAME_S), Darwin)
CFLAGS_ALL := $(CFLAGS_ALL) -arch arm64 -arch x86_64
LDFLAGS_ALL := $(LDFLAGS_ALL) -arch arm64 -arch x86_64
CXXFLAGS_ALL := $(CXXFLAGS_ALL) -arch arm64 -arch x86_64
ifeq ($(HAS_UI), yes)
LDFLAGS_ALL := $(LDFLAGS_ALL) -Wl,-framework,Foundation -Wl,-framework,Cocoa -Wl,-framework,Corevideo
endif
endif
ifeq ($(UNAME_S), Linux)
CFLAGS_ALL := $(CFLAGS_ALL) -D_GNU_SOURCE
LDFLAGS_ALL := $(LDFLAGS_ALL) -ldl
ifeq ($(HAS_UI), yes)
CFLAGS_ALL := $(CFLAGS_ALL) $(shell pkg-config --cflags x11)
LDFLAGS_ALL := $(LDFLAGS_ALL) $(shell pkg-config --libs x11)
CXXFLAGS_ALL := $(CXXFLAGS_ALL) $(shell pkg-config --cflags x11)
endif
CFLAGS_ALL := $(CFLAGS_ALL) -D_GNU_SOURCE
ifeq ($(HAS_UI), yes)
CFLAGS_ALL := $(CFLAGS_ALL) $(shell pkg-config --cflags x11)
LDFLAGS_ALL := $(LDFLAGS_ALL) $(shell pkg-config --libs x11)
CXXFLAGS_ALL := $(CXXFLAGS_ALL) $(shell pkg-config --cflags x11)
endif
endif
BUNDLE_DIR := $(BUNDLE_NAME).vst3
DLL_DIR := Contents/$(PLATFORM)
DLL_FILE := $(DLL_DIR)/$(BUNDLE_NAME)$(DLL_SUFFIX)
BUNDLE_DATA_PATH := build/$(BUNDLE_DIR)/Contents/Resources
BUNDLE_BIN_PATH := build/$(BUNDLE_DIR)/$(DLL_DIR)
C_SRCS := $(COMMON_DIR)/src/vst3.c $(C_SRCS_EXTRA)
C_OBJS := $(addprefix build/obj/, $(notdir $(C_SRCS:.c=.o)))
@ -95,13 +106,19 @@ M_OBJS := $(addprefix build/obj/, $(notdir $(M_SRCS:.m=.o)))
CXX_SRCS := $(CXX_SRCS_EXTRA)
CXX_OBJS := $(addprefix build/obj/, $(notdir $(CXX_SRCS:.cpp=.o)))
ALL := build/$(BUNDLE_DIR)/$(DLL_FILE)
DIRS := build build/obj build/$(BUNDLE_DIR) build/$(BUNDLE_DIR)/Contents build/$(BUNDLE_DIR)/Contents/Resources build/$(BUNDLE_DIR)/$(DLL_DIR)
ALL := build/$(BUNDLE_DIR)/$(DLL_FILE)
ifeq ($(UNAME_S), Darwin)
ALL := $(ALL) build/$(BUNDLE_DIR)/Contents/Info.plist build/$(BUNDLE_DIR)/Contents/PkgInfo
ALL := $(ALL) build/$(BUNDLE_DIR)/Contents/Info.plist build/$(BUNDLE_DIR)/Contents/PkgInfo
endif
-include $(COMMON_DIR)/vars-extra.mk
STRIP_ALL := build/$(BUNDLE_DIR)/$(DLL_FILE)
STRIP_PREREQS := $(STRIP_ALL)
PHONY := all clean strip install install-user
-include $(MKINC_DIR)/vars-extra.mk
all: $(ALL)
@ -121,46 +138,46 @@ build/$(BUNDLE_DIR)/Contents/%: $(COMMON_DIR)/data/% | build/$(BUNDLE_DIR)/Conte
cp $^ $@
endif
build/$(BUNDLE_DIR)/Contents build/$(BUNDLE_DIR)/$(DLL_DIR) build/obj:
mkdir -p $@
${BUNDLE_DATA_PATH}:
$(DIRS):
mkdir -p $@
clean:
rm -fr build
strip: $(STRIP_PREREQS)
strip -Sx build/$(BUNDLE_DIR)/$(DLL_FILE)
install: all
@for d in `find build/$(BUNDLE_DIR) -type d`; do \
d=`echo $$d | sed 's:^build/::'` ; \
echo mkdir -m 0755 -p "$(VST3DIR)/$$d"; \
mkdir -m 0755 -p "$(VST3DIR)/$$d"; \
mkdir -m 0755 -p "$(VST3DIR)/$$d" || exit 1; \
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"; \
install -m $$m $$f "$(VST3DIR)/$$d" || exit 1; \
done
install-user: all
@for d in `find build/$(BUNDLE_DIR) -type d`; do \
d=`echo $$d | sed 's:^build/::'` ; \
echo mkdir -m 0755 -p "$(VST3DIR_USER)/$$d"; \
mkdir -m 0755 -p "$(VST3DIR_USER)/$$d"; \
mkdir -m 0755 -p "$(VST3DIR_USER)/$$d" || exit 1; \
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"; \
install -m $$m $$f "$(VST3DIR_USER)/$$d" || exit 1; \
done
-include $(COMMON_DIR)/rules-extra.mk
-include $(MKINC_DIR)/rules-extra.mk
.PHONY: all clean install install-user
.PHONY: $(PHONY)
.SECONDEXPANSION:
@ -175,4 +192,6 @@ $(M_OBJS): build/obj/%.o: $$(filter $$(PERCENT)/$$(basename $$(notdir $$@)).m,$$
$(CXX_OBJS): build/obj/%.o: $$(filter $$(PERCENT)/$$(basename $$(notdir $$@)).cpp,$$(CXX_SRCS)) | build/obj
$(CXX) $^ -o $@ -c $(CXXFLAGS_ALL)
-include $(COMMON_DIR)/rules-secondexp-extra.mk
-include $(MKINC_DIR)/rules-secondexp-extra.mk
.DELETE_ON_ERROR:

View File

@ -1,7 +1,7 @@
#
# Tibia
#
# Copyright (C) 2023, 2024 Orastron Srl unipersonale
# Copyright (C) 2023-2025 Orastron Srl unipersonale
#
# Tibia is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@ -18,19 +18,30 @@
# File author: Stefano D'Angelo
#
BUNDLE_NAME := {{=it.product.bundleName}}
BUNDLE_NAME := {{=it.product.bundleName}}
COMMON_DIR := {{=it.vst3_make?.commonDir ?? (it.make?.commonDir ?? "")}}
DATA_DIR := {{=it.vst3_make?.dataDir ?? (it.make?.dataDir ?? "")}}
PLUGIN_DIR := {{=it.vst3_make?.pluginDir ?? (it.make?.pluginDir ?? "")}}
{{?(it.vst3_make?.commonDir || it.make?.commonDir)}}
COMMON_DIR := {{=it.vst3_make?.commonDir ?? (it.make?.commonDir ?? "")}}
{{?}}
{{?(it.vst3_make?.dataDir || it.make?.dataDir)}}
DATA_DIR := {{=it.vst3_make?.dataDir ?? (it.make?.dataDir ?? "")}}
{{?}}
{{?(it.vst3_make?.pluginDir || it.make?.pluginDir)}}
PLUGIN_DIR := {{=it.vst3_make?.pluginDir ?? (it.make?.pluginDir ?? "")}}
{{?}}
{{?(it.vst3_make?.apiDir || it.make?.apiDir)}}
API_DIR := {{=it.vst3_make?.apiDir ?? (it.make?.apiDir ?? "")}}
{{?}}
{{?(it.vst3_make?.mkincDir || it.make?.mkincDir)}}
MKINC_DIR := {{=it.vst3_make?.mkincDir ?? (it.make?.mkincDir ?? "")}}
{{?}}
C_SRCS_EXTRA := {{=it.make?.cSrcs ?? ""}} {{=it.vst3_make?.cSrcs ?? ""}}
M_SRCS_EXTRA := {{=it.make?.mSrcs ?? ""}} {{=it.vst3_make?.mSrcs ?? ""}}
CXX_SRCS_EXTRA := {{=it.make?.cxxSrcs ?? ""}} {{=it.vst3_make?.cxxSrcs ?? ""}}
HAS_UI := {{=it.product.ui ? "yes" : "no"}}
HAS_PARAMETER_IN := {{=it.product.parameters.filter(x => x.direction == "input").length > 0 ? "yes" : "no"}}
CFLAGS_EXTRA := {{=it.make?.cflags ?? ""}} {{=it.vst3_make?.cflags ?? ""}}
CXXFLAGS_EXTRA := {{=it.make?.cxxflags ?? ""}} {{=it.vst3_make?.cxxflags ?? ""}}
LDFLAGS_EXTRA := {{=it.make?.ldflags ?? ""}} {{=it.vst3_make?.ldflags ?? ""}}
HAS_UI := {{=it.product.ui ? "yes" : "no"}}
{{?it.make?.extra}}
{{=it.make.extra}}
{{?}}
{{?it.vst3_make?.extra}}
{{=it.vst3_make.extra}}
{{?}}

View File

@ -1,7 +1,7 @@
/*
* Tibia
*
* Copyright (C) 2023, 2024 Orastron Srl unipersonale
* Copyright (C) 2023-2025 Orastron Srl unipersonale
*
* Tibia is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -18,54 +18,54 @@
* File author: Stefano D'Angelo
*/
#define DATA_COMPANY_NAME "{{=it.tibia.CGetUTF8StringLiteral(it.company.name, 63)}}"
#define DATA_COMPANY_URL "{{=it.tibia.CGetUTF8StringLiteral(it.company.url, 255)}}"
#define DATA_COMPANY_EMAIL "{{=it.tibia.CGetUTF8StringLiteral(it.company.email, 127)}}"
#define DATA_COMPANY_NAME "{{=it.tibia.CGetUTF8StringLiteral(it.company.name, 63)}}"
#define DATA_COMPANY_URL "{{=it.tibia.CGetUTF8StringLiteral(it.company.url, 255)}}"
#define DATA_COMPANY_EMAIL "{{=it.tibia.CGetUTF8StringLiteral(it.company.email, 127)}}"
#define DATA_PRODUCT_NAME "{{=it.tibia.CGetUTF8StringLiteral(it.product.name, 63)}}"
#define DATA_PRODUCT_VERSION "{{=(it.product.version + '.' + it.product.buildVersion).substring(0, 63)}}"
#define DATA_PRODUCT_NAME "{{=it.tibia.CGetUTF8StringLiteral(it.product.name, 63)}}"
#define DATA_PRODUCT_VERSION "{{=(it.product.version + '.' + it.product.buildVersion).substring(0, 63)}}"
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 dataProductNameW[64] = { {{~Array.from(it.product.name).slice(0, 63) :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 };
static Steinberg_char16 dataProductVersionW[64] = { {{~Array.from(it.product.version + "." + it.product.buildVersion).slice(0, 63) :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 };
#define DATA_VST3_SDK_VERSION "VST 3.7.9"
static Steinberg_char16 dataVST3SDKVersionW[64] = { {{~Array.from("VST 3.7.9") :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 };
#define DATA_VST3_SDK_VERSION "VST 3.7.13"
static Steinberg_char16 dataVST3SDKVersionW[64] = { {{~Array.from("VST 3.7.13") :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 };
#define DATA_VST3_PLUGIN_CID_1 0x{{=it.vst3.plugin.cid.substring(0, 8)}}
#define DATA_VST3_PLUGIN_CID_2 0x{{=it.vst3.plugin.cid.substring(8, 16)}}
#define DATA_VST3_PLUGIN_CID_3 0x{{=it.vst3.plugin.cid.substring(16, 24)}}
#define DATA_VST3_PLUGIN_CID_4 0x{{=it.vst3.plugin.cid.substring(24, 32)}}
#define DATA_VST3_PLUGIN_CID_1 0x{{=it.vst3.plugin.cid.substring(0, 8)}}
#define DATA_VST3_PLUGIN_CID_2 0x{{=it.vst3.plugin.cid.substring(8, 16)}}
#define DATA_VST3_PLUGIN_CID_3 0x{{=it.vst3.plugin.cid.substring(16, 24)}}
#define DATA_VST3_PLUGIN_CID_4 0x{{=it.vst3.plugin.cid.substring(24, 32)}}
#define DATA_VST3_CONTROLLER_CID_1 0x{{=it.vst3.controller.cid.substring(0, 8)}}
#define DATA_VST3_CONTROLLER_CID_2 0x{{=it.vst3.controller.cid.substring(8, 16)}}
#define DATA_VST3_CONTROLLER_CID_3 0x{{=it.vst3.controller.cid.substring(16, 24)}}
#define DATA_VST3_CONTROLLER_CID_4 0x{{=it.vst3.controller.cid.substring(24, 32)}}
#define DATA_VST3_CONTROLLER_CID_1 0x{{=it.vst3.controller.cid.substring(0, 8)}}
#define DATA_VST3_CONTROLLER_CID_2 0x{{=it.vst3.controller.cid.substring(8, 16)}}
#define DATA_VST3_CONTROLLER_CID_3 0x{{=it.vst3.controller.cid.substring(16, 24)}}
#define DATA_VST3_CONTROLLER_CID_4 0x{{=it.vst3.controller.cid.substring(24, 32)}}
static const Steinberg_TUID dataPluginCID = SMTG_INLINE_UID(DATA_VST3_PLUGIN_CID_1, DATA_VST3_PLUGIN_CID_2, DATA_VST3_PLUGIN_CID_3, DATA_VST3_PLUGIN_CID_4);
static const Steinberg_TUID dataPluginCID = SMTG_INLINE_UID(DATA_VST3_PLUGIN_CID_1, DATA_VST3_PLUGIN_CID_2, DATA_VST3_PLUGIN_CID_3, DATA_VST3_PLUGIN_CID_4);
static const Steinberg_TUID dataControllerCID = SMTG_INLINE_UID(DATA_VST3_CONTROLLER_CID_1, DATA_VST3_CONTROLLER_CID_2, DATA_VST3_CONTROLLER_CID_3, DATA_VST3_CONTROLLER_CID_4);
#define DATA_VST3_SUBCATEGORY "{{=it.vst3.subCategory}}"
#define DATA_VST3_SUBCATEGORY "{{=it.vst3.subCategory}}"
#define DATA_PRODUCT_BUSES_AUDIO_INPUT_N {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "input").length}}
#define DATA_PRODUCT_BUSES_AUDIO_OUTPUT_N {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "output").length}}
#define DATA_PRODUCT_BUSES_MIDI_INPUT_N {{=it.product.buses.filter(x => x.type == "midi" && x.direction == "input").length}}
#define DATA_PRODUCT_BUSES_MIDI_OUTPUT_N {{=it.product.buses.filter(x => x.type == "midi" && x.direction == "output").length}}
#define DATA_PRODUCT_BUSES_AUDIO_INPUT_N {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "input").length}}
#define DATA_PRODUCT_BUSES_AUDIO_OUTPUT_N {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "output").length}}
#define DATA_PRODUCT_BUSES_MIDI_INPUT_N {{=it.product.buses.filter(x => x.type == "midi" && x.direction == "input").length}}
#define DATA_PRODUCT_BUSES_MIDI_OUTPUT_N {{=it.product.buses.filter(x => x.type == "midi" && x.direction == "output").length}}
#define DATA_PRODUCT_CHANNELS_AUDIO_INPUT_N {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "input").reduce((a, x) => a + (x.channels == "mono" ? 1 : 2), 0)}}
#define DATA_PRODUCT_CHANNELS_AUDIO_OUTPUT_N {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "output").reduce((a, x) => a + (x.channels == "mono" ? 1 : 2), 0)}}
#define DATA_PRODUCT_CHANNELS_AUDIO_INPUT_N {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "input").reduce((a, x) => a + (x.channels == "mono" ? 1 : 2), 0)}}
#define DATA_PRODUCT_CHANNELS_AUDIO_OUTPUT_N {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "output").reduce((a, x) => a + (x.channels == "mono" ? 1 : 2), 0)}}
#if DATA_PRODUCT_BUSES_AUDIO_INPUT_N > 0
static struct Steinberg_Vst_BusInfo busInfoAudioInput[DATA_PRODUCT_BUSES_AUDIO_INPUT_N] = {
{{~it.product.buses.filter(x => x.type == "audio" && x.direction == "input") :b}}
{
/* .mediaType = */ Steinberg_Vst_MediaTypes_kAudio,
/* .direction = */ Steinberg_Vst_BusDirections_kInput,
/* .channelCount = */ {{=b.channels == "mono" ? 1 : 2}},
/* .name = */ { {{~Array.from(b.name) :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .busType = */ {{?b.sidechain}}Steinberg_Vst_BusTypes_kAux{{??}}Steinberg_Vst_BusTypes_kMain{{?}},
/* .flags = */ 0{{?b.cv}} | Steinberg_Vst_BusInfo_BusFlags_kIsControlVoltage{{?}}{{?!b.optional}} | Steinberg_Vst_BusInfo_BusFlags_kDefaultActive{{?}}
/* .mediaType = */ Steinberg_Vst_MediaTypes_kAudio,
/* .direction = */ Steinberg_Vst_BusDirections_kInput,
/* .channelCount = */ {{=b.channels == "mono" ? 1 : 2}},
/* .name = */ { {{~Array.from(b.name) :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .busType = */ {{?b.sidechain}}Steinberg_Vst_BusTypes_kAux{{??}}Steinberg_Vst_BusTypes_kMain{{?}},
/* .flags = */ 0{{?b.cv}} | Steinberg_Vst_BusInfo_BusFlags_kIsControlVoltage{{?}}{{?!b.optional}} | Steinberg_Vst_BusInfo_BusFlags_kDefaultActive{{?}}
},
{{~}}
};
@ -75,12 +75,12 @@ static struct Steinberg_Vst_BusInfo busInfoAudioInput[DATA_PRODUCT_BUSES_AUDIO_I
static struct Steinberg_Vst_BusInfo busInfoAudioOutput[DATA_PRODUCT_BUSES_AUDIO_OUTPUT_N] = {
{{~it.product.buses.filter(x => x.type == "audio" && x.direction == "output") :b}}
{
/* .mediaType = */ Steinberg_Vst_MediaTypes_kAudio,
/* .direction = */ Steinberg_Vst_BusDirections_kOutput,
/* .channelCount = */ {{=b.channels == "mono" ? 1 : 2}},
/* .name = */ { {{~Array.from(b.name) :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .busType = */ {{?b.sidechain}}Steinberg_Vst_BusTypes_kAux{{??}}Steinberg_Vst_BusTypes_kMain{{?}},
/* .flags = */ 0{{?b.cv}} | Steinberg_Vst_BusInfo_BusFlags_kIsControlVoltage{{?}}{{?!b.optional}} | Steinberg_Vst_BusInfo_BusFlags_kDefaultActive{{?}}
/* .mediaType = */ Steinberg_Vst_MediaTypes_kAudio,
/* .direction = */ Steinberg_Vst_BusDirections_kOutput,
/* .channelCount = */ {{=b.channels == "mono" ? 1 : 2}},
/* .name = */ { {{~Array.from(b.name) :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .busType = */ {{?b.sidechain}}Steinberg_Vst_BusTypes_kAux{{??}}Steinberg_Vst_BusTypes_kMain{{?}},
/* .flags = */ 0{{?b.cv}} | Steinberg_Vst_BusInfo_BusFlags_kIsControlVoltage{{?}}{{?!b.optional}} | Steinberg_Vst_BusInfo_BusFlags_kDefaultActive{{?}}
},
{{~}}
};
@ -90,12 +90,12 @@ static struct Steinberg_Vst_BusInfo busInfoAudioOutput[DATA_PRODUCT_BUSES_AUDIO_
static struct Steinberg_Vst_BusInfo busInfoMidiInput[DATA_PRODUCT_BUSES_MIDI_INPUT_N] = {
{{~it.product.buses.filter(x => x.type == "midi" && x.direction == "input") :b}}
{
/* .mediaType = */ Steinberg_Vst_MediaTypes_kEvent,
/* .direction = */ Steinberg_Vst_BusDirections_kInput,
/* .channelCount = */ 16,
/* .name = */ { {{~Array.from(b.name) :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .busType = */ {{?b.sidechain}}Steinberg_Vst_BusTypes_kAux{{??}}Steinberg_Vst_BusTypes_kMain{{?}},
/* .flags = */ 0{{?!b.optional}} | Steinberg_Vst_BusInfo_BusFlags_kDefaultActive{{?}}
/* .mediaType = */ Steinberg_Vst_MediaTypes_kEvent,
/* .direction = */ Steinberg_Vst_BusDirections_kInput,
/* .channelCount = */ 16,
/* .name = */ { {{~Array.from(b.name) :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .busType = */ {{?b.sidechain}}Steinberg_Vst_BusTypes_kAux{{??}}Steinberg_Vst_BusTypes_kMain{{?}},
/* .flags = */ 0{{?!b.optional}} | Steinberg_Vst_BusInfo_BusFlags_kDefaultActive{{?}}
},
{{~}}
};
@ -105,12 +105,12 @@ static struct Steinberg_Vst_BusInfo busInfoMidiInput[DATA_PRODUCT_BUSES_MIDI_INP
static struct Steinberg_Vst_BusInfo busInfoMidiOutput[DATA_PRODUCT_BUSES_MIDI_OUTPUT_N] = {
{{~it.product.buses.filter(x => x.type == "midi" && x.direction == "output") :b}}
{
/* .mediaType = */ Steinberg_Vst_MediaTypes_kEvent,
/* .direction = */ Steinberg_Vst_BusDirections_kOutput,
/* .channelCount = */ 16,
/* .name = */ { {{~Array.from(b.name) :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .busType = */ {{?b.sidechain}}Steinberg_Vst_BusTypes_kAux{{??}}Steinberg_Vst_BusTypes_kMain{{?}},
/* .flags = */ 0{{?!b.optional}} | Steinberg_Vst_BusInfo_BusFlags_kDefaultActive{{?}}
/* .mediaType = */ Steinberg_Vst_MediaTypes_kEvent,
/* .direction = */ Steinberg_Vst_BusDirections_kOutput,
/* .channelCount = */ 16,
/* .name = */ { {{~Array.from(b.name) :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .busType = */ {{?b.sidechain}}Steinberg_Vst_BusTypes_kAux{{??}}Steinberg_Vst_BusTypes_kMain{{?}},
/* .flags = */ 0{{?!b.optional}} | Steinberg_Vst_BusInfo_BusFlags_kDefaultActive{{?}}
},
{{~}}
};
@ -122,108 +122,148 @@ static uint32_t midiInIndex[DATA_PRODUCT_BUSES_MIDI_INPUT_N] = {
};
#endif
#define DATA_PRODUCT_PARAMETERS_N {{=it.product.parameters.filter(x => !x.isLatency).length}}
#define DATA_PRODUCT_PARAMETERS_N {{=it.product.parameters.filter(x => !x.isLatency).length}}
#define DATA_PRODUCT_PARAMETERS_IN_N {{=it.product.parameters.filter(x => x.direction == "input").length}}
#define DATA_PRODUCT_PARAMETERS_OUT_N {{=it.product.parameters.filter(x => !x.isLatency && x.direction == "output").length}}
#if DATA_PRODUCT_PARAMETERS_N + DATA_PRODUCT_BUSES_MIDI_INPUT_N > 0
static struct Steinberg_Vst_ParameterInfo parameterInfo[DATA_PRODUCT_PARAMETERS_N + 3 * DATA_PRODUCT_BUSES_MIDI_INPUT_N] = {
{{~it.product.parameters.filter(x => !x.isLatency) :p:i}}
{
{{?p.isBypass}}
/* .id = */ {{=(it.tibia.vst3.sdbm("Bypass") & 0x7fffffff) >>> 0}},
/* .title = */ { {{~Array.from("Bypass") :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .shortTitle = */ { {{~Array.from("Bypass") :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .units = */ { 0 },
/* .stepCount = */ 1,
/* .defaultNormalizedValue = */ 0.0,
/* .unitId = */ 0,
/* .flags = */ Steinberg_Vst_ParameterInfo_ParameterFlags_kIsBypass | Steinberg_Vst_ParameterInfo_ParameterFlags_kCanAutomate
/* .id = */ {{=(it.tibia.sdbm("bypass ") & 0x7fffffff) >>> 0}},
/* .title = */ { {{~Array.from("Bypass") :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .shortTitle = */ { {{~Array.from("Bypass") :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .units = */ { 0 },
/* .stepCount = */ 1,
/* .defaultNormalizedValue = */ 0.0,
/* .unitId = */ 0,
/* .flags = */ Steinberg_Vst_ParameterInfo_ParameterFlags_kIsBypass | Steinberg_Vst_ParameterInfo_ParameterFlags_kCanAutomate
{{??}}
/* .id = */ {{=(it.tibia.vst3.sdbm(p.name) & 0x7fffffff) >>> 0}},
/* .title = */ { {{~Array.from(p.name) :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .shortTitle = */ { {{~Array.from(p.shortName) :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .units = */ { {{~Array.from(p.unit in it.tibia.vst3.units ? it.tibia.vst3.units[p.unit] : "") :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .stepCount = */ {{=p.toggled ? 1 : (p.list && p.scalePoints.length > 1 ? p.scalePoints.length - 1 : (p.integer ? p.maximum - p.minimum : 0))}},
/* .defaultNormalizedValue = */ {{?p.map == "logarithmic"}}{{=Number(Math.log(p.defaultValue / p.minimum) / (2 * Math.log(Math.sqrt(p.minimum * p.maximum) / Math.abs(p.minimum)))).toExponential()}}{{??}}{{=Number((p.defaultValue - p.minimum) / (p.maximum - p.minimum)).toExponential()}}{{?}},
/* .unitId = */ 0,
/* .flags = */ {{?p.direction == "input"}}Steinberg_Vst_ParameterInfo_ParameterFlags_kCanAutomate{{??}}Steinberg_Vst_ParameterInfo_ParameterFlags_kIsReadOnly{{?}}
/* .id = */ {{=(it.tibia.sdbm(p.id) & 0x7fffffff) >>> 0}},
/* .title = */ { {{~Array.from(p.name) :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .shortTitle = */ { {{~Array.from(p.shortName) :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .units = */ { {{~Array.from(p.unit in it.tibia.vst3.units ? it.tibia.vst3.units[p.unit] : "") :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .stepCount = */ {{=p.toggled ? 1 : (p.list && p.scalePoints.length > 1 ? p.scalePoints.length - 1 : (p.integer ? p.maximum - p.minimum : 0))}},
/* .defaultNormalizedValue = */ {{?p.map == "logarithmic"}}{{=Number(Math.log(p.defaultValue / p.minimum) / (2 * Math.log(Math.sqrt(p.minimum * p.maximum) / Math.abs(p.minimum)))).toExponential()}}{{??}}{{=Number((p.defaultValue - p.minimum) / (p.maximum - p.minimum)).toExponential()}}{{?}},
/* .unitId = */ 0,
/* .flags = */ {{?p.direction == "input"}}Steinberg_Vst_ParameterInfo_ParameterFlags_kCanAutomate{{??}}Steinberg_Vst_ParameterInfo_ParameterFlags_kIsReadOnly{{?}}
{{?}}
},
{{~}}
{{~it.product.buses.filter(x => x.type == "midi" && x.direction == "input") :b:i}}
{
/* .id = */ {{=(it.tibia.vst3.sdbm(b.name + " Channel Pressure") & 0x7fffffff) >>> 0}},
/* .title = */ { {{~Array.from(b.name + " Channel Pressure") :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .shortTitle = */ { {{~Array.from(b.shortName + " Chan Pres") :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .units = */ { 0 },
/* .stepCount = */ 0,
/* .defaultNormalizedValue = */ 0.0,
/* .unitId = */ 0,
/* .flags = */ Steinberg_Vst_ParameterInfo_ParameterFlags_kIsHidden | Steinberg_Vst_ParameterInfo_ParameterFlags_kCanAutomate
/* .id = */ {{=(it.tibia.sdbm(b.id + " channel pressure") & 0x7fffffff) >>> 0}},
/* .title = */ { {{~Array.from(b.name + " Channel Pressure").slice(0, 127) :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .shortTitle = */ { {{~Array.from(b.shortName + " Chan Pres").slice(0, 127) :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .units = */ { 0 },
/* .stepCount = */ 0,
/* .defaultNormalizedValue = */ 0.0,
/* .unitId = */ 0,
/* .flags = */ Steinberg_Vst_ParameterInfo_ParameterFlags_kIsHidden | Steinberg_Vst_ParameterInfo_ParameterFlags_kCanAutomate
},
{
/* .id = */ {{=(it.tibia.vst3.sdbm(b.name + " Pitch Bend") & 0x7fffffff) >>> 0}},
/* .title = */ { {{~Array.from(b.name + " Pitch Bend") :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .shortTitle = */ { {{~Array.from(b.shortName + " Pitch Bend") :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .units = */ { 0 },
/* .stepCount = */ 0,
/* .defaultNormalizedValue = */ 0.5,
/* .unitId = */ 0,
/* .flags = */ Steinberg_Vst_ParameterInfo_ParameterFlags_kIsHidden | Steinberg_Vst_ParameterInfo_ParameterFlags_kCanAutomate
/* .id = */ {{=(it.tibia.sdbm(b.id + " pitch bend") & 0x7fffffff) >>> 0}},
/* .title = */ { {{~Array.from(b.name + " Pitch Bend").slice(0, 127) :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .shortTitle = */ { {{~Array.from(b.shortName + " Pitch Bend").slice(0, 127) :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .units = */ { 0 },
/* .stepCount = */ 0,
/* .defaultNormalizedValue = */ 0.5,
/* .unitId = */ 0,
/* .flags = */ Steinberg_Vst_ParameterInfo_ParameterFlags_kIsHidden | Steinberg_Vst_ParameterInfo_ParameterFlags_kCanAutomate
},
{
/* .id = */ {{=(it.tibia.vst3.sdbm(b.name + " Mod Wheel") & 0x7fffffff) >>> 0}},
/* .title = */ { {{~Array.from(b.name + " Mod Wheel") :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .shortTitle = */ { {{~Array.from(b.shortName + " Mod Wheel") :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .units = */ { 0 },
/* .stepCount = */ 0,
/* .defaultNormalizedValue = */ 0.0,
/* .unitId = */ 0,
/* .flags = */ Steinberg_Vst_ParameterInfo_ParameterFlags_kIsHidden | Steinberg_Vst_ParameterInfo_ParameterFlags_kCanAutomate
/* .id = */ {{=(it.tibia.sdbm(b.id + " mod wheel") & 0x7fffffff) >>> 0}},
/* .title = */ { {{~Array.from(b.name + " Mod Wheel").slice(0, 127) :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .shortTitle = */ { {{~Array.from(b.shortName + " Mod Wheel").slice(0, 127) :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },
/* .units = */ { 0 },
/* .stepCount = */ 0,
/* .defaultNormalizedValue = */ 0.0,
/* .unitId = */ 0,
/* .flags = */ Steinberg_Vst_ParameterInfo_ParameterFlags_kIsHidden | Steinberg_Vst_ParameterInfo_ParameterFlags_kCanAutomate
},
{{~}}
};
# if DATA_PRODUCT_PARAMETERS_N > 0
# define DATA_PARAM_BYPASS 1
# define DATA_PARAM_TOGGLED (1<<1)
# define DATA_PARAM_INTEGER (1<<2)
# define DATA_PARAM_MAP_LOG (1<<3)
# define DATA_PARAM_BYPASS 1
# define DATA_PARAM_TOGGLED (1<<1)
# define DATA_PARAM_INTEGER (1<<2)
# define DATA_PARAM_MAP_LOG (1<<3)
static struct {
size_t index;
double min;
double max;
double def;
uint32_t flags;
double mapK;
typedef struct {
size_t index;
double min;
double max;
double def;
uint32_t flags;
double mapK;
// scalePoints?
} parameterData[DATA_PRODUCT_PARAMETERS_N] = {
{{~it.product.parameters.filter(x => !x.isLatency) :p:i}}
} ParameterData;
# if DATA_PRODUCT_PARAMETERS_IN_N > 0
static ParameterData parameterInData[DATA_PRODUCT_PARAMETERS_IN_N] = {
{{~it.product.parameters.filter(x => x.direction == "input") :p:i}}
{
/* .index = */ {{=p.paramIndex}},
/* .min = */ {{=p.minimum.toExponential()}},
/* .max = */ {{=p.maximum.toExponential()}},
/* .def = */ {{=p.defaultValue.toExponential()}},
/* .flags = */ {{?p.isBypass}}DATA_PARAM_BYPASS{{??}}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{{?}}
/* .index = */ {{=p.paramIndex}},
/* .min = */ {{=p.minimum.toExponential()}},
/* .max = */ {{=p.maximum.toExponential()}},
/* .def = */ {{=p.defaultValue.toExponential()}},
/* .flags = */ {{?p.isBypass}}DATA_PARAM_BYPASS{{??}}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{{?}}
},
{{~}}
};
# 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{{?}}
},
{{~}}
};
{{?it.product.parameters.find(x => x.direction == "output" && x.isCpumeter)}}
# define PARAM_OUT_CPU_INDEX {{=it.product.parameters.indexOf(it.product.parameters.find(x => x.direction == "output" && x.isCpumeter))}}
{{?}}
static size_t parameterOutDataToInfoIndex[DATA_PRODUCT_PARAMETERS_OUT_N] = {
{{=it.product.parameters.filter(x => !x.isLatency && x.direction == "output").map(x => x.paramInfoIndex).join(", ")}}
};
# endif
static size_t parameterInfoToDataIndex[DATA_PRODUCT_PARAMETERS_N] = {
{{=it.product.parameters.filter(x => !x.isLatency).map(x => x.paramDataIndex).join(", ")}}
};
# endif
#endif
{{?it.product.parameters.find(x => x.isLatency)}}
#define DATA_PARAM_LATENCY_INDEX {{=it.product.parameters.find(x => x.isLatency).paramIndex}}
#define DATA_PARAM_LATENCY_INDEX {{=it.product.parameters.find(x => x.isLatency).paramIndex}}
{{?}}
{{?it.product.ui}}
#define DATA_UI
#define DATA_UI_USER_RESIZABLE {{=it.product.ui.userResizable ? 1 : 0}}
#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

@ -49,23 +49,28 @@ module.exports = function (data, api, outputCommon, outputData) {
"pc": "%",
"s": "s",
"semitone12TET": "semi"
},
sdbm: function (s) {
var hash = 0;
for (var i = 0; i < s.length; i++)
hash = s.charCodeAt(i) + (hash << 6) + (hash << 16) - hash;
return hash >>> 0;
}
};
for (var i = 0; i < data.product.parameters.length; i++)
var inIdx = 0;
var outIdx = 0;
for (var i = 0; i < data.product.parameters.length; i++) {
data.product.parameters[i].paramIndex = i;
if (data.product.parameters[i].isLatency)
continue;
data.product.parameters[i].paramInfoIndex = inIdx + outIdx;
if (data.product.parameters[i].direction == "input") {
data.product.parameters[i].paramDataIndex = inIdx;
inIdx++;
} else {
data.product.parameters[i].paramDataIndex = outIdx;
outIdx++;
}
}
}
api.copyFile(`data${sep}PkgInfo`, `data${sep}PkgInfo`);
api.generateFileFromTemplateFile(`data${sep}Info.plist`, `data${sep}Info.plist`, data);
api.copyFile(`src${sep}vst3.c`, `src${sep}vst3.c`);
api.generateFileFromTemplateFile(`src${sep}data.h`, `src${sep}data.h`, data);
api.copyFile(`..${sep}common${sep}fatica.h`, `src${sep}fatica.h`);
};

View File

@ -0,0 +1,33 @@
#
# 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
#
build/web/index.html: $(DATA_DIR)/src/index.html | build/web
cp $^ $@
build/web/key.pem: build/web/cert.pem
build/web/cert.pem: | build
openssl req -x509 -newkey rsa:2048 -keyout build/web/key.pem -out build/web/cert.pem -days 365 -nodes -subj "/C=US/ST=Denial/L=Springfield/O=Dis/CN=www.example.com" 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

@ -3,7 +3,7 @@
Tibia
Copyright (C) 2022-2024 Orastron Srl unipersonale
Copyright (C) 2022-2025 Orastron Srl unipersonale
Tibia is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -75,12 +75,12 @@ var Player = {
};
function map(index, value) {
var p = demo.Module.data.product.parameters[index];
var p = demo.Module.parameters[index];
return p.map == "logarithmic" ? p.minimum * Math.exp((2.0 * Math.log(Math.sqrt(p.maximum * p.minimum) / Math.abs(p.minimum))) * value) : p.minimum + (p.maximum - p.minimum) * value;
}
function unmap(index, value) {
var p = demo.Module.data.product.parameters[index];
var p = demo.Module.parameters[index];
return p.map == "logarithmic" ? Math.log(value / p.minimum) / (2.0 * Math.log(Math.sqrt(p.maximum * p.minimum) / Math.abs(p.minimum))) : (value - p.minimum) / (p.maximum - p.minimum);
}
@ -112,7 +112,7 @@ var units = {
};
function displayValue(elem, index, value) {
var param = demo.Module.data.product.parameters[index];
var param = demo.Module.parameters[index];
var unit = param.unit;
if (param.integer)
value = Math.round(value);
@ -132,9 +132,9 @@ window.addEventListener("load", function (e) {
var playPause = document.getElementById("playPause");
var controls = document.getElementById("controls");
audioInputIndex = demo.Module.data.product.buses.filter(x => x.type == "audio" && x.direction == "input").findIndex(x => !x.cv && !x.sidechain);
audioOutputIndex = demo.Module.data.product.buses.filter(x => x.type == "audio" && x.direction == "output").findIndex(x => !x.cv && !x.sidechain);
hasMidiInput = demo.Module.data.product.buses.filter(x => x.type == "midi" && x.direction == "input").length > 0;
audioInputIndex = demo.Module.buses.filter(x => x.type == "audio" && x.direction == "input").findIndex(x => !x.cv && !x.sidechain);
audioOutputIndex = demo.Module.buses.filter(x => x.type == "audio" && x.direction == "output").findIndex(x => !x.cv && !x.sidechain);
hasMidiInput = demo.Module.buses.filter(x => x.type == "midi" && x.direction == "input").length > 0;
if (hasMidiInput && !navigator.requestMIDIAccess)
alert("Your browser doesn't support the Web MIDI API");
@ -145,7 +145,7 @@ window.addEventListener("load", function (e) {
file.value = "";
playPause.disabled = true;
var parameters = demo.Module.data.product.parameters;
var parameters = demo.Module.parameters;
for (var i = 0; i < parameters.length; i++) {
var div = document.createElement("div");
@ -241,8 +241,8 @@ window.addEventListener("load", function (e) {
if ((e.data[0] & 0xf0) == 0xf0)
return;
var msg = { type: "midi", data: e.data };
for (var i = 0; i < demo.Module.data.product.buses.length; i++) {
var b = demo.Module.data.product.buses[i];
for (var i = 0; i < demo.Module.buses.length; i++) {
var b = demo.Module.buses[i];
if (b.type != "midi" || b.direction != "input")
continue;
msg.index = i;

View File

@ -23,6 +23,6 @@ var sep = path.sep;
module.exports = function (data, api) {
api.generateFileFromTemplateFile(`src${sep}index.html`, `src${sep}index.html`, data);
api.copyFile(`vars-extra.mk`, `vars-extra.mk`);
api.copyFile(`rules-extra.mk`, `rules-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

@ -1,7 +1,7 @@
#
# Tibia
#
# Copyright (C) 2023, 2024 Orastron Srl unipersonale
# Copyright (C) 2023-2025 Orastron Srl unipersonale
#
# Tibia is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@ -18,10 +18,8 @@
# File author: Stefano D'Angelo
#
build/web/index.html: ${DATA_DIR}/src/index.html | build/web
cp $^ $@
ALL := $(ALL) build/web/index.html build/web/cert.pem build/web/key.pem
build/web/key.pem: build/web/cert.pem
build/web/cert.pem: | build
yes "" | openssl req -x509 -newkey rsa:2048 -keyout build/web/key.pem -out build/web/cert.pem -days 365 -nodes 2>/dev/null
STRIP_ALL := $(STRIP_ALL) build/web/index.html
STRIP_PREREQS := $(STRIP_PREREQS) strip-web-demo
PHONY := $(PHONY) strip-web-demo

View File

@ -1,7 +1,7 @@
#
# Tibia
#
# Copyright (C) 2023, 2024 Orastron Srl unipersonale
# Copyright (C) 2023-2025 Orastron Srl unipersonale
#
# Tibia is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@ -18,17 +18,28 @@
# File author: Stefano D'Angelo
#
SHELL := bash -o pipefail
TEMPLATE := web
include vars.mk
COMMON_DIR := $(or $(COMMON_DIR),.)
DATA_DIR := $(or $(DATA_DIR),.)
PLUGIN_DIR := $(or $(PLUGIN_DIR),src)
COMMON_DIR ?= .
DATA_DIR ?= .
PLUGIN_DIR ?= src
API_DIR ?= $(PLUGIN_DIR)
MKINC_DIR ?= $(COMMON_DIR)
BUILD_BIN_DIR := build/web
BUILD_DATA_DIR := build/web
-include $(MKINC_DIR)/vars-pre.mk
CC := clang
CXX := clang++
CFLAGS := -Ofast -Wall -Wpedantic -Wextra
CFLAGS_ALL := -I$(COMMON_DIR)/src -I$(DATA_DIR)/src -I$(PLUGIN_DIR) --target=wasm32 -flto -fvisibility=hidden $(CFLAGS) $(CFLAGS_EXTRA)
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 \
@ -56,28 +67,32 @@ LDFLAGS_ALL := \
ifeq ($(HAS_MIDI_IN), yes)
LDFLAGS_ALL := $(LDFLAGS_ALL) -Wl,--export=processor_midi_msg_in
endif
LDFLAGS_ALL := $(LDFLAGS_ALL) $(LDFLAGS) $(LDFLAGS_EXTRA)
LDFLAGS_ALL := $(LDFLAGS_ALL) $(LDFLAGS_EXTRA) $(LDFLAGS)
CXXFLAGS := $(CFLAGS)
CXXFLAGS_ALL := -I$(COMMON_DIR)/src -I$(DATA_DIR)/src -I$(PLUGIN_DIR) --target=wasm32 -flto -fvisibility=hidden $(CXXFLAGS) $(CXXFLAGS_EXTRA)
CXXFLAGS_ALL := -I$(COMMON_DIR)/src -I$(DATA_DIR)/src -I$(PLUGIN_DIR) -I$(API_DIR) --target=wasm32 -flto -fvisibility=hidden $(CXXFLAGS_EXTRA) $(CXXFLAGS)
C_SRCS := $(COMMON_DIR)/src/processor.c $(COMMON_DIR)/src/walloc.c $(COMMON_DIR)/src/string.c
C_OBJS := $(addprefix build/obj/, $(notdir $(C_SRCS:.c=.o)))
ifeq ($(CXX_SRCS_EXTRA),)
CXX_SRCS :=
CXX_OBJS :=
CXX_SRCS :=
CXX_OBJS :=
else
CXX_SRCS := $(COMMON_DIR)/src/new.cpp $(CXX_SRCS_EXTRA)
CXX_OBJS := $(addprefix build/obj/, $(notdir $(CXX_SRCS:.cpp=.o)))
CXX_SRCS := $(COMMON_DIR)/src/new.cpp $(CXX_SRCS_EXTRA)
CXX_OBJS := $(addprefix build/obj/, $(notdir $(CXX_SRCS:.cpp=.o)))
endif
BUNDLE_DATA_PATH := build/web
BUNDLE_BIN_PATH := build/web
DIRS := build build/obj build/web build/web/$(BUNDLE_NAME)
ALL := build/web/$(BUNDLE_NAME)/module.wasm build/web/$(BUNDLE_NAME)/processor.js build/web/$(BUNDLE_NAME)/module.js
-include $(COMMON_DIR)/vars-extra.mk
STRIP_ALL := build/web/$(BUNDLE_NAME)/module.wasm build/web/$(BUNDLE_NAME)/processor.js build/web/$(BUNDLE_NAME)/module.js
STRIP_PREREQS := $(STRIP_ALL)
PHONY := all clean strip
-include $(MKINC_DIR)/vars-extra.mk
all: $(ALL)
@ -89,21 +104,27 @@ build/web/$(BUNDLE_NAME)/module.wasm: $(C_OBJS) $(CXX_OBJS) | build/web/$(BUNDLE
$(CXX) $^ -o $@ $(CFLAGS_ALL) $(CXXFLAGS_ALL) $(LDFLAGS_ALL)
endif
build/web/$(BUNDLE_NAME)/processor.js: $(DATA_DIR)/res/processor.js | build/web/$(BUNDLE_NAME)
build/web/$(BUNDLE_NAME)/processor.js: $(DATA_DIR)/src/processor.js | build/web/$(BUNDLE_NAME)
cp $^ $@
build/web/$(BUNDLE_NAME)/module.js: $(DATA_DIR)/res/module.js | build/web/$(BUNDLE_NAME)
build/web/$(BUNDLE_NAME)/module.js: $(DATA_DIR)/src/module.js | build/web/$(BUNDLE_NAME)
cp $^ $@
build/obj build/web build/web/$(BUNDLE_NAME):
$(DIRS):
mkdir -p $@
clean:
rm -fr build
-include $(COMMON_DIR)/rules-extra.mk
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
.PHONY: all clean
-include $(MKINC_DIR)/rules-extra.mk
.PHONY: $(PHONY)
.SECONDEXPANSION:
@ -115,4 +136,6 @@ $(C_OBJS): build/obj/%.o: $$(filter $$(PERCENT)/$$(basename $$(notdir $$@)).c,$$
$(CXX_OBJS): build/obj/%.o: $$(filter $$(PERCENT)/$$(basename $$(notdir $$@)).cpp,$$(CXX_SRCS)) | build/obj
$(CXX) $^ -o $@ -c $(CXXFLAGS_ALL)
-include $(COMMON_DIR)/rules-secondexp-extra.mk
-include $(MKINC_DIR)/rules-secondexp-extra.mk
.DELETE_ON_ERROR:

View File

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

View File

@ -1,7 +1,7 @@
/*
* Tibia
*
* Copyright (C) 2024 Orastron Srl unipersonale
* Copyright (C) 2024, 2025 Orastron Srl unipersonale
*
* Tibia is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -18,17 +18,17 @@
* File author: Stefano D'Angelo
*/
#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_PARAMETERS_N {{=it.product.parameters.length}}
#define DATA_PRODUCT_PARAMETERS_OUTPUT_N {{=it.product.parameters.filter(x => x.direction == "output").length}}
#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_PARAMETERS_N {{=it.product.parameters.length}}
#define DATA_PRODUCT_PARAMETERS_OUTPUT_N {{=it.product.parameters.filter(x => x.direction == "output").length}}
#if DATA_PRODUCT_PARAMETERS_N > 0
static struct {
char out;
float def;
char out;
float def;
} param_data[DATA_PRODUCT_PARAMETERS_N] = {
{{~it.product.parameters :p}}
{
@ -44,3 +44,7 @@ static size_t param_out_index[DATA_PRODUCT_PARAMETERS_OUTPUT_N] = {
{{~it.product.parameters :p:i}}{{?p.direction == "output"}}{{=i}}, {{?}}{{~}}
};
#endif
{{?it.product.state && it.product.state.dspCustom}}
#define DATA_STATE_DSP_CUSTOM
{{?}}

View File

@ -1,7 +1,7 @@
/*
* Tibia
*
* Copyright (C) 2022-2024 Orastron Srl unipersonale
* Copyright (C) 2022-2025 Orastron Srl unipersonale
*
* Tibia is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -18,13 +18,12 @@
* File author: Stefano D'Angelo
*/
const data = {
company: {{=JSON.stringify(it.company, null, 2)}},
product: {{=JSON.stringify(it.product, null, 2)}}
};
const buses = {{=JSON.stringify(it.product.buses, null, 2)}};
const parameters = {{=JSON.stringify(it.product.parameters, null, 2)}};
export class Module {
static get data() { return data; }
static get buses() { return buses; }
static get parameters() { return parameters; }
async init(context, processorJsPath, wasmPath) {
var wasmBytes = await fetch(wasmPath)
@ -45,11 +44,11 @@ export class Module {
export class Node extends AudioWorkletNode {
constructor(module) {
super(module.context, data.product.bundleName,
super(module.context, "{{=it.product.bundleName}}",
{
numberOfInputs: data.product.buses.filter(b => b.type == "audio" && b.direction == "input").length,
numberOfOutputs: data.product.buses.filter(b => b.type == "audio" && b.direction == "output").length,
outputChannelCount: data.product.buses.filter(b => b.type == "audio" && b.direction == "output").map(b => b.channels == "mono" ? 1 : 2),
numberOfInputs: buses.filter(b => b.type == "audio" && b.direction == "input").length,
numberOfOutputs: buses.filter(b => b.type == "audio" && b.direction == "output").length,
outputChannelCount: buses.filter(b => b.type == "audio" && b.direction == "output").map(b => b.channels == "mono" ? 1 : 2),
processorOptions: { wasmBytes: module.wasmBytes }
});
}

View File

@ -1,7 +1,7 @@
/*
* Tibia
*
* Copyright (C) 2022-2024 Orastron Srl unipersonale
* Copyright (C) 2022-2025 Orastron Srl unipersonale
*
* Tibia is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -29,32 +29,37 @@
#include "walloc.h"
typedef struct {
plugin p;
void *mem;
plugin p;
void * mem;
#if DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N > 0
float x_buf[DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N * 128];
const float *x[DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N];
float zero_buf[128];
float x_buf[DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N * 128];
const float * x[DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N];
float zero_buf[128];
#endif
#if DATA_PRODUCT_AUDIO_OUTPUT_CHANNELS_N > 0
float y_buf[DATA_PRODUCT_AUDIO_OUTPUT_CHANNELS_N * 128];
float *y[DATA_PRODUCT_AUDIO_OUTPUT_CHANNELS_N];
float y_buf[DATA_PRODUCT_AUDIO_OUTPUT_CHANNELS_N * 128];
float * y[DATA_PRODUCT_AUDIO_OUTPUT_CHANNELS_N];
#endif
#if DATA_PRODUCT_PARAMETERS_OUTPUT_N > 0
float out_params[DATA_PRODUCT_PARAMETERS_OUTPUT_N];
float out_params[DATA_PRODUCT_PARAMETERS_OUTPUT_N];
#endif
} instance;
instance * processor_new(float sample_rate) {
#ifdef DATA_STATE_DSP_CUSTOM
(void)plugin_state_load;
(void)plugin_state_save;
#endif
instance * i = malloc(sizeof(instance));
if (i == NULL)
return NULL;
plugin_callbacks cbs = {
/* .handle = */ NULL,
/* .format = */ "web",
/* .get_bindir = */ NULL,
/* .get_datadir = */ NULL
/* .handle = */ NULL,
/* .format = */ "web",
/* .get_bindir = */ NULL,
/* .get_datadir = */ NULL
};
plugin_init(&i->p, &cbs);

View File

@ -18,19 +18,14 @@
* File author: Stefano D'Angelo
*/
const buses = {{=JSON.stringify(it.product.buses, null, 2)}};
const parameters = {{=JSON.stringify(it.product.parameters, null, 2)}};
var buses = {{=JSON.stringify(it.product.buses, null, 2)}};
var parameters = {{=JSON.stringify(it.product.parameters, null, 2)}};
const busesIn = buses.filter(x => x.type == "audio" && x.direction == "input");
const busesOut = buses.filter(x => x.type == "audio" && x.direction == "output");
var busesIn = buses.filter(x => x.type == "audio" && x.direction == "input");
var busesOut = buses.filter(x => x.type == "audio" && x.direction == "output");
const nChansIn = busesIn.reduce((a, x) => a + (x.channels == "mono" ? 1 : 2), 0);
const nChansOut = busesOut.reduce((a, x) => a + (x.channels == "mono" ? 1 : 2), 0);
let cpu_meter = 0.0;
// performance is not available, and there's no better option than Date atm
const now = Date.now;
var nChansIn = busesIn.reduce((a, x) => a + (x.channels == "mono" ? 1 : 2), 0);
var nChansOut = busesOut.reduce((a, x) => a + (x.channels == "mono" ? 1 : 2), 0);
class Processor extends AudioWorkletProcessor {
constructor(options) {
@ -76,8 +71,6 @@ class Processor extends AudioWorkletProcessor {
}
process(inputs, outputs, params) {
const processTimeStart = now();
for (var i = 0; i < this.parametersIn.length; i++) {
var index = this.parametersIn[i].index;
var parameter = parameters[index];
@ -148,11 +141,7 @@ class Processor extends AudioWorkletProcessor {
for (var i = 0; i < this.parametersOut.length; i++) {
var index = this.parametersOut[i].index;
var value;
if (parameters[index].isCpumeter)
value = cpu_meter;
else
value = this.parametersOutValues[i];
var value = this.parametersOutValues[i];
if (value != this.parametersOut[i].value) {
this.paramOutChangeMsg.index = index;
this.paramOutChangeMsg.value = value;
@ -160,9 +149,6 @@ class Processor extends AudioWorkletProcessor {
this.parametersOut[i].value = value;
}
}
const processTimeEnd = now();
const processTimeMs = processTimeEnd - processTimeStart;
cpu_meter = cpu_meter * 0.99 + (processTimeMs * 0.128 / sampleRate) * 0.01; // TODO: something better than 0.99?
return true; // because Chrome sucks: https://bugs.chromium.org/p/chromium/issues/detail?id=921354
}

View File

@ -28,7 +28,7 @@ extern unsigned char __heap_base;
typedef struct _header {
struct _header *next;
struct _header *prev;
char free;
char free;
} header;
static char inited = 0;

View File

@ -29,6 +29,6 @@ module.exports = function (data, api) {
api.copyFile(`src${sep}new.cpp`, `src${sep}new.cpp`);
api.copyFile(`src${sep}processor.c`, `src${sep}processor.c`);
api.generateFileFromTemplateFile(`src${sep}data.h`, `src${sep}data.h`, data);
api.generateFileFromTemplateFile(`src${sep}processor.js`, `res${sep}processor.js`, data);
api.generateFileFromTemplateFile(`src${sep}module.js`, `res${sep}module.js`, data);
api.generateFileFromTemplateFile(`src${sep}processor.js`, `src${sep}processor.js`, data);
api.generateFileFromTemplateFile(`src${sep}module.js`, `src${sep}module.js`, data);
};

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" : "27.0.11902837",
"buildToolsVersion" : "35.0.0",
"androidxDir" : "${HOME}/Android/androidx",
"kotlinDir" : "${HOME}/Android/kotlin",
"androidVersion" : "35",
"androidxCoreVersion" : "1.13.1",
"androidxLifecycleCommonVersion" : "2.8.3",
"androidxVersionedparcelableVersion" : "1.2.0",
"kotlinStdlibVersion" : "2.0.0",
"kotlinxCoroutinesCoreVersion" : "1.8.0",
"kotlinxCoroutinesCoreJVMVersion" : "1.8.1"
}
}

View File

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

View File

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

View File

@ -1,8 +1,7 @@
{
"company": {
"name": "Example company",
"url": "https://www.example.com/",
"email": "info@example.com",
"domain": "com.example"
"name": "Example company",
"url": "https://www.example.com/",
"email": "info@example.com"
}
}

View File

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

View File

@ -1,5 +1,5 @@
{
"daisy_seed": {
"parameterPins" : [ 15, 16, 17, 18, 22 ]
"parameterPins": [ 15, 16, 17, 18, 22 ]
}
}

View File

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

View File

@ -1,5 +1,5 @@
{
"ios": {
"productBundleIdentifier" : "com.example.tibia-test"
"productBundleIdentifier": "com.example.tibia-test"
}
}

View File

@ -1,6 +0,0 @@
{
"lv2_make": {
"cflags" : "$(shell pkg-config --cflags pugl-cairo-0 pugl-0 cairo)",
"ldflags" : "$(shell pkg-config --libs pugl-cairo-0 pugl-0 cairo) -Wl,-rpath,$(shell pkg-config --variable=libdir pugl-cairo-0),-rpath,$(shell pkg-config --variable=libdir pugl-0),-rpath,$(shell pkg-config --variable=libdir cairo)"
}
}

View File

@ -8,7 +8,7 @@
"types": [ "@lv2:AmplifierPlugin" ],
"version": "1.0",
"ui": {
"uri": "@example:tibia_test_ui"
"uri": "@example:tibia_test#ui"
}
}
}

View File

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

View File

@ -1,7 +1,7 @@
/*
* Tibia
*
* Copyright (C) 2024 Orastron Srl unipersonale
* Copyright (C) 2024, 2025 Orastron Srl unipersonale
*
* Tibia is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -18,289 +18,230 @@
* File author: Stefano D'Angelo, Paolo Marrone
*/
#include <pugl/pugl.h>
#include <pugl/cairo.h>
#include <cairo.h>
#include "vinci.h"
#include <stdio.h>
#include <string.h>
typedef struct {
void * widget;
PuglWorld * world;
PuglView * view;
void *widget;
double fw;
double fh;
double x;
double y;
double w;
double h;
vinci *ui;
window *w;
int param_down;
float gain;
float delay;
float cutoff;
char bypass;
float y_z1;
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
#define RATIO (WIDTH / HEIGHT)
#define INV_RATIO (HEIGHT / WIDTH)
static void plugin_ui_get_default_size(uint32_t *width, uint32_t *height) {
*width = WIDTH;
*height = HEIGHT;
}
static void plugin_ui_update_geometry(plugin_ui *instance) {
PuglRect frame = puglGetFrame(instance->view);
instance->fw = frame.width;
instance->fh = frame.height;
if (frame.width == 0 || frame.height == 0)
return;
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);
}
if (instance->fw / instance->fh > RATIO) {
instance->w = RATIO * instance->fh;
instance->h = instance->fh;
instance->x = 0.5 * (instance->fw - instance->w);
instance->y = 0.0;
} else {
instance->w = instance->fw;
instance->h = INV_RATIO * instance->fw;
instance->x = 0.0;
instance->y = 0.5 * (instance->fh - instance->h);
static void draw_slider(plugin_ui *pui, int id, float value) {
const int w = window_get_width(pui->w);
const int h = window_get_height(pui->w);
draw_rect(pui->w, 0.1 * w, 0.15 * (id + 1) * h, 0.8 * w * value, 0.1 * h, 0x6789ab);
draw_rect(pui->w, 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->w);
const int h = window_get_height(pui->w);
draw_rect(pui->w, 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 plugin_ui_draw(plugin_ui *instance) {
cairo_t *cr = (cairo_t *)puglGetContext(instance->view);
double x = instance->x;
double y = instance->y;
double w = instance->w;
double h = instance->h;
static void on_mouse_release (window *win, int32_t x, int32_t y, uint32_t state) {
(void) state;
cairo_set_line_width(cr, 0.005 * h);
plugin_ui *pui = (plugin_ui*) window_get_data(win);
const int w = window_get_width(win);
const int h = window_get_height(win);
cairo_set_source_rgb(cr, 0, 0, 0);
cairo_paint(cr);
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);
}
cairo_set_source_rgb(cr, 0.2, 0.2, 0.2);
cairo_rectangle(cr, x, y, w, h);
cairo_fill(cr);
cairo_set_source_rgb(cr, 0.1, 0.1, 0.1);
cairo_rectangle(cr, x + 0.1 * w, y + 0.15 * h, 0.8 * w, 0.1 * h);
cairo_fill(cr);
cairo_set_source_rgb(cr, 0.5, 0.5, 0.5);
cairo_rectangle(cr, x + 0.1 * w, y + 0.15 * h, 0.8 * w * instance->gain, 0.1 * h);
cairo_fill(cr);
cairo_set_source_rgb(cr, 0.7, 0.7, 0.7);
cairo_rectangle(cr, x + 0.1 * w, y + 0.15 * h, 0.8 * w, 0.1 * h);
cairo_stroke(cr);
cairo_set_source_rgb(cr, 0.1, 0.1, 0.1);
cairo_rectangle(cr, x + 0.1 * w, y + 0.3 * h, 0.8 * w, 0.1 * h);
cairo_fill(cr);
cairo_set_source_rgb(cr, 0.5, 0.5, 0.5);
cairo_rectangle(cr, x + 0.1 * w, y + 0.3 * h, 0.8 * w * instance->delay, 0.1 * h);
cairo_fill(cr);
cairo_set_source_rgb(cr, 0.7, 0.7, 0.7);
cairo_rectangle(cr, x + 0.1 * w, y + 0.3 * h, 0.8 * w, 0.1 * h);
cairo_stroke(cr);
cairo_set_source_rgb(cr, 0.1, 0.1, 0.1);
cairo_rectangle(cr, x + 0.1 * w, y + 0.45 * h, 0.8 * w, 0.1 * h);
cairo_fill(cr);
cairo_set_source_rgb(cr, 0.5, 0.5, 0.5);
cairo_rectangle(cr, x + 0.1 * w, y + 0.45 * h, 0.8 * w * instance->cutoff, 0.1 * h);
cairo_fill(cr);
cairo_set_source_rgb(cr, 0.7, 0.7, 0.7);
cairo_rectangle(cr, x + 0.1 * w, y + 0.45 * h, 0.8 * w, 0.1 * h);
cairo_stroke(cr);
if (instance->bypass)
cairo_set_source_rgb(cr, 1.0, 0, 0);
else
cairo_set_source_rgb(cr, 0.7, 0.7, 0.7);
cairo_rectangle(cr, x + 0.4 * w, y + 0.6 * h, 0.2 * w, 0.1 * h);
cairo_fill(cr);
cairo_set_source_rgb(cr, 0.1, 0.1, 0.1);
cairo_rectangle(cr, x + 0.1 * w, y + 0.75 * h, 0.8 * w, 0.1 * h);
cairo_fill(cr);
cairo_set_source_rgb(cr, 0.5, 0.5, 0.5);
cairo_rectangle(cr, x + 0.1 * w, y + 0.75 * h, 0.8 * w * instance->y_z1, 0.1 * h);
cairo_fill(cr);
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 PuglStatus plugin_ui_on_event(PuglView *view, const PuglEvent *event) {
switch (event->type) {
case PUGL_CONFIGURE:
{
plugin_ui *instance = (plugin_ui *)puglGetHandle(view);
plugin_ui_update_geometry(instance);
puglPostRedisplay(instance->view);
}
break;
case PUGL_BUTTON_PRESS:
{
plugin_ui *instance = (plugin_ui *)puglGetHandle(view);
const PuglButtonEvent *ev = (const PuglButtonEvent *)event;
double x = instance->x;
double y = instance->y;
double w = instance->w;
double h = instance->h;
static void on_mouse_move (window *win, int32_t x, int32_t y, uint32_t state) {
(void) y;
(void) state;
if (ev->x >= x + 0.1 * w && ev->x <= x + 0.9 * w
&& ev->y >= y + 0.15 * h && ev->y <= y + 0.25 * h) {
instance->param_down = 0;
instance->gain = (float)((ev->x - (x + 0.1 * w)) / (0.8 * w));
instance->cbs.set_parameter_begin(instance->cbs.handle, 0);
instance->cbs.set_parameter(instance->cbs.handle, 0, -60.f + 80.f * instance->gain);
puglPostRedisplay(instance->view);
} else if (ev->x >= x + 0.1 * w && ev->x <= x + 0.9 * w
&& ev->y >= y + 0.3 * h && ev->y <= y + 0.4 * h) {
instance->param_down = 1;
instance->delay = (float)((ev->x - (x + 0.1 * w)) / (0.8 * w));
instance->cbs.set_parameter_begin(instance->cbs.handle, 1);
instance->cbs.set_parameter(instance->cbs.handle, 1, 1000.f * instance->delay);
puglPostRedisplay(instance->view);
} else if (ev->x >= x + 0.1 * w && ev->x <= x + 0.9 * w
&& ev->y >= y + 0.45 * h && ev->y <= y + 0.55 * h) {
instance->param_down = 2;
instance->cutoff = (float)((ev->x - (x + 0.1 * w)) / (0.8 * w));
instance->cbs.set_parameter_begin(instance->cbs.handle, 2);
instance->cbs.set_parameter(instance->cbs.handle, 2, (632.4555320336746f * instance->cutoff + 20.653108640674372f) / (1.0326554320337158f - instance->cutoff));
puglPostRedisplay(instance->view);
} else if (ev->x >= x + 0.4 * w && ev->x <= x + 0.6 * w
&& ev->y >= y + 0.6 * h && ev->y <= y + 0.7 * h) {
instance->param_down = 3;
}
}
break;
case PUGL_MOTION:
{
plugin_ui *instance = (plugin_ui *)puglGetHandle(view);
const PuglMotionEvent *ev = (const PuglMotionEvent *)event;
double x = instance->x;
double w = instance->w;
float v = ev->x < x + 0.1 * w ? 0.f : (ev->x > x + 0.9 * w ? 1.f : (float)((ev->x - (x + 0.1 * w)) / (0.8 * w)));
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 (instance->param_down) {
case 0:
instance->gain = v;
instance->cbs.set_parameter(instance->cbs.handle, 0, -60.f + 80.f * instance->gain);
puglPostRedisplay(instance->view);
break;
case 1:
instance->delay = v;
instance->cbs.set_parameter(instance->cbs.handle, 1, 1000.f * instance->delay);
puglPostRedisplay(instance->view);
break;
case 2:
instance->cutoff = v;
instance->cbs.set_parameter(instance->cbs.handle, 2, (632.4555320336746f * instance->cutoff + 20.653108640674372f) / (1.0326554320337158f - instance->cutoff));
puglPostRedisplay(instance->view);
break;
}
}
break;
case PUGL_BUTTON_RELEASE:
{
plugin_ui *instance = (plugin_ui *)puglGetHandle(view);
const PuglButtonEvent *ev = (const PuglButtonEvent *)event;
double x = instance->x;
double y = instance->y;
double w = instance->w;
double h = instance->h;
if (instance->param_down == 3)
if (ev->x >= x + 0.4 * w && ev->x <= x + 0.6 * w
&& ev->y >= y + 0.6 * h && ev->y <= y + 0.7 * h) {
instance->bypass = !instance->bypass;
instance->cbs.set_parameter(instance->cbs.handle, 3, instance->bypass ? 1.f : 0.f);
puglPostRedisplay(instance->view);
}
if (instance->param_down != -1) {
instance->cbs.set_parameter_end(instance->cbs.handle, instance->param_down);
instance->param_down = -1;
}
}
break;
case PUGL_EXPOSE:
{
plugin_ui *instance = (plugin_ui *)puglGetHandle(view);
plugin_ui_update_geometry(instance); // I didn't expect this was needed here for X11 to work decently when resizing
plugin_ui_draw(instance);
}
break;
default:
break;
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;
}
return PUGL_SUCCESS;
}
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));
plugin_ui *instance = (plugin_ui *) 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->world = puglNewWorld(PUGL_MODULE, 0);
instance->view = puglNewView(instance->world);
puglSetSizeHint(instance->view, PUGL_DEFAULT_SIZE, WIDTH, HEIGHT);
puglSetViewHint(instance->view, PUGL_RESIZABLE, PUGL_TRUE);
puglSetHandle(instance->view, instance);
puglSetBackend(instance->view, puglCairoBackend());
PuglRect frame = { 0, 0, WIDTH, HEIGHT };
puglSetFrame(instance->view, frame);
puglSetEventFunc(instance->view, plugin_ui_on_event);
if (has_parent)
puglSetParentWindow(instance->view, (PuglNativeView)parent);
if (puglRealize(instance->view)) {
puglFreeView(instance->view);
puglFreeWorld(instance->world);
return NULL;
}
instance->widget = (void *)puglGetNativeView(instance->view);
instance->ui = vinci_new();
instance->w = window_new(instance->ui, has_parent ? parent : NULL, WIDTH, HEIGHT, &wcbs);
instance->widget = window_get_handle(instance->w);
window_set_data(instance->w, (void*) instance);
window_show(instance->w);
// 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->w, window_get_width(instance->w), window_get_height(instance->w));
instance->cbs = *cbs;
puglSetFrame(instance->view, frame); // Intentionally duplicated because of ardour/lv2/mac strange event order call
puglShow(instance->view, PUGL_SHOW_RAISE); // Cocoa calls events at this so it's better this happens late
return instance;
}
static void plugin_ui_free(plugin_ui *instance) {
puglFreeView(instance->view);
puglFreeWorld(instance->world);
window_free(instance->w);
vinci_destroy(instance->ui);
free(instance);
}
static void plugin_ui_idle(plugin_ui *instance) {
puglUpdate(instance->world, 0);
vinci_idle(instance->ui);
}
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;
}
puglPostRedisplay(instance->view);
}

View File

@ -2,8 +2,10 @@
"product": {
"name": "Tibia test product",
"version": "1.0.0",
"buildVersion": "1",
"bundleName": "tibia_test",
"buildVersion": 0,
"description": "A toy plugin to test Tibia",
"copyright": "Copyright © 2021-2025 Orastron Srl unipersonale",
"bundleName": "tibia-test",
"buses": [
{
"type": "audio",
@ -132,29 +134,15 @@
"list": false,
"unit": "",
"map": "linear"
},
{
"name": "cpu",
"shortName": "cpu",
"id": "cpu",
"direction": "output",
"isBypass": false,
"isLatency": false,
"isCpumeter": true,
"defaultValue": 0.0,
"minimum": 0.0,
"maximum": 1.0,
"toggled": false,
"optional": false,
"integer": false,
"list": false,
"unit": "",
"map": "linear"
}
],
"ui": {
"userResizable": true,
"selfResizable": false
"selfResizable": false,
"highResolution": true
},
"state": {
"dspCustom": true
}
}
}

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

@ -0,0 +1,21 @@
#
# 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 rules-extra-web-demo.mk

View File

@ -1,39 +1,62 @@
#!/bin/sh
#
# 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
#
dir=`dirname $0`
$dir/../tibia $dir/product.json,$dir/company.json $dir/../templates/common $dir/../out/vst3
$dir/../tibia $dir/product.json,$dir/company.json $dir/../templates/api $dir/../out/api
$dir/../tibia $dir/product.json,$dir/company.json,$dir/vst3.json $dir/../templates/vst3 $dir/../out/vst3
$dir/../tibia $dir/product.json,$dir/company.json,$dir/vst3.json,$dir/vst3-make.json $dir/../templates/vst3-make $dir/../out/vst3
$dir/../tibia $dir/product.json,$dir/company.json,$dir/vst3.json $dir/../templates/vst3-make $dir/../out/vst3
cp $dir/plugin.h $dir/plugin_ui.h $dir/../out/vst3/src
cp $dir/vars-pre.mk $dir/../out/vst3
$dir/../tibia $dir/product.json,$dir/company.json $dir/../templates/common $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/lv2-make.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/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/common $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-demo $dir/../out/web
cp $dir/plugin.h $dir/../out/web/src
cp $dir/vars-pre.mk $dir/vars-extra.mk $dir/rules-extra.mk $dir/../out/web
$dir/../tibia $dir/product.json,$dir/company.json $dir/../templates/common $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/plugin.h $dir/../out/android/src
cp $dir/vars-pre.mk $dir/../out/android
$dir/../tibia $dir/product.json,$dir/company.json $dir/../templates/common $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
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/../templates/common $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-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/vars-pre.mk $dir/../out/cmd
$dir/../tibia $dir/product.json,$dir/company.json $dir/../templates/common $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/vars-pre.mk $dir/../out/daisy-seed

View File

@ -1,7 +1,7 @@
#
# Tibia
#
# Copyright (C) 2023, 2024 Orastron Srl unipersonale
# 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
@ -18,4 +18,4 @@
# File author: Stefano D'Angelo
#
ALL := $(ALL) build/web/index.html build/web/cert.pem build/web/key.pem
include vars-extra-web-demo.mk

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

@ -0,0 +1,86 @@
#
# 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
#
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,6 +0,0 @@
{
"vst3_make": {
"cflags" : "-I../../../vst3_c_api $(shell pkg-config --cflags pugl-cairo-0)",
"ldflags" : "$(shell pkg-config --libs pugl-cairo-0 pugl-0 cairo) -Wl,-rpath,$(shell pkg-config --variable=libdir pugl-cairo-0),-rpath,$(shell pkg-config --variable=libdir pugl-0),-rpath,$(shell pkg-config --variable=libdir cairo)"
}
}

102
tibia
View File

@ -1,38 +1,77 @@
#!/usr/bin/env node
/*
* 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
*/
function usage() {
console.log("Usage:");
console.log(" tibia file1.json,file2.json,...filen.json template outDirectory");
console.log(" tibia --common template outDirectory");
console.log(" tibia --data file1.json,file2.json,...filen.json template outDirectory");
console.log(" tibia file1.json,file2.json,...filen.json template outDirectory [override1=value1] [override2=value2] ...");
console.log(" tibia --common template outDirectory [override1=value1] [override2=value2] ...");
console.log(" tibia --data file1.json,file2.json,...filen.json template outDirectory [override1=value1] [override2=value2] ...");
process.exit(1);
}
var jsonFiles, template, outputDir, outputCommon, outputData;
var jsonFiles, template, outputDir, outputCommon, outputData, i;
if (process.argv[2] == "--common") {
if (process.argv.length != 5)
if (process.argv.length < 5)
usage();
jsonFiles = [];
template = process.argv[3];
outputDir = process.argv[4];
outputCommon = true;
outputData = false;
i = 5;
} else if (process.argv[2] == "--data") {
if (process.argv.length != 6)
if (process.argv.length < 6)
usage();
jsonFiles = process.argv[3].split(",");
template = process.argv[4];
outputDir = process.argv[5];
outputCommon = false;
outputData = true;
i = 6;
} else {
if (process.argv.length != 5)
if (process.argv.length < 5)
usage();
jsonFiles = process.argv[2].split(",");
template = process.argv[3];
outputDir = process.argv[4];
outputCommon = true;
outputData = true;
i = 5;
}
var overrides = [];
for (; i < process.argv.length; i++) {
var o = process.argv[i];
var r = o.match(/^([_a-zA-Z][_a-zA-Z0-9]*(\.[_a-zA-Z][_a-zA-Z0-9]*)*)=/);
if (!r)
usage();
var p = r[1].split(".");
var v = o.substring(r[0].length);
try {
v = JSON.parse(v);
} catch (e) {
usage();
}
overrides.push({ property: p, value: v });
}
var fs = require("fs");
@ -54,6 +93,13 @@ for (var i = 0; i < jsonFiles.length; i++) {
for (var k in d)
data[k] = d[k];
}
for (var i = 0; i < overrides.length; i++) {
var p = data;
var o = overrides[i];
for (var j = 0; j < o.property.length - 1; j++)
p = p[o.property[j]];
p[o.property[o.property.length - 1]] = o.value;
}
/*
if (outputData) {
@ -69,39 +115,30 @@ var doT = require("dot");
doT.templateSettings.strip = false;
var api = {
// https://coderrocketfuel.com/article/recursively-list-all-the-files-in-a-directory-using-node-js
getAllFiles: function (dirPath, arrayOfFiles, relDir) {
var files = fs.readdirSync(dirPath);
var arrayOfFiles = arrayOfFiles || [];
var relDir = relDir || "";
files.forEach(function(file) {
if (fs.statSync(dirPath + path.sep + file).isDirectory())
arrayOfFiles = api.getAllFiles(dirPath + path.sep + file, arrayOfFiles, relDir + file + path.sep);
else
arrayOfFiles.push(relDir + file);
});
return arrayOfFiles
},
generateFileFromTemplateFile: function (templateFile, outFile, data) {
generateFileFromTemplateFile: function (templateFile, outFile, data, mode) {
if (!outputData)
return;
var dir = outputDir + path.sep + path.dirname(outFile);
fs.mkdirSync(dir, { recursive: true });
var t = doT.template(fs.readFileSync(template + path.sep + templateFile, { encoding: "utf-8" }));
fs.writeFileSync(outputDir + path.sep + outFile, t(data), { encoding: "utf-8" });
var outputFile = outputDir + path.sep + outFile;
fs.writeFileSync(outputFile, t(data), { encoding: "utf-8" });
if (mode !== undefined)
fs.chmodSync(outputFile, mode);
},
copyFile: function (inFile, outFile) {
copyFile: function (inFile, outFile, mode) {
if (!outputCommon)
return;
var dir = outputDir + path.sep + path.dirname(outFile);
fs.mkdirSync(dir, { recursive: true });
fs.copyFileSync(template + path.sep + inFile, outputDir + path.sep + outFile);
}
var outputFile = outputDir + path.sep + outFile;
fs.copyFileSync(template + path.sep + inFile, outputFile);
if (mode !== undefined)
fs.chmodSync(outputFile, mode);
},
templateDir: template
};
data.tibia = {
@ -145,6 +182,13 @@ data.tibia = {
}
}
return s;
},
sdbm: function (s) {
var hash = 0;
for (var i = 0; i < s.length; i++)
hash = s.charCodeAt(i) + (hash << 6) + (hash << 16) - hash;
return hash >>> 0;
}
};