@soundor/vst3-host 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/CMakeLists.txt +194 -0
  2. package/CMakePresets.json +115 -0
  3. package/LICENSE +21 -0
  4. package/NOTICE.md +17 -0
  5. package/README.md +40 -2
  6. package/cmake/Vst3HostDependencies.cmake +179 -0
  7. package/dist/cli.d.mts +1 -0
  8. package/dist/cli.mjs +88 -0
  9. package/dist/cli.mjs.map +1 -0
  10. package/dist/index.d.mts +586 -0
  11. package/dist/index.d.mts.map +1 -0
  12. package/dist/index.mjs +1 -0
  13. package/dist/prepare-ayjTVhfk.mjs +1149 -0
  14. package/dist/prepare-ayjTVhfk.mjs.map +1 -0
  15. package/native/src/Bundle.cpp +253 -0
  16. package/native/src/Bundle.h +32 -0
  17. package/native/src/EditorArea.h +92 -0
  18. package/native/src/EditorAreaMac.mm +52 -0
  19. package/native/src/EditorAreaWindows.cpp +66 -0
  20. package/native/src/EditorAreaX11.cpp +512 -0
  21. package/native/src/EditorWindow.h +72 -0
  22. package/native/src/EditorWindowMac.mm +134 -0
  23. package/native/src/EditorWindowWindows.cpp +148 -0
  24. package/native/src/EventLoop.h +50 -0
  25. package/native/src/EventLoopLinux.cpp +204 -0
  26. package/native/src/EventLoopMac.mm +146 -0
  27. package/native/src/EventLoopWindows.cpp +132 -0
  28. package/native/src/HostContext.cpp +217 -0
  29. package/native/src/HostContext.h +116 -0
  30. package/native/src/HostInterface.cpp +1026 -0
  31. package/native/src/HostInterface.h +138 -0
  32. package/native/src/HostWindow.cpp +901 -0
  33. package/native/src/HostWindow.h +187 -0
  34. package/native/src/Ipc.cpp +158 -0
  35. package/native/src/Ipc.h +91 -0
  36. package/native/src/Json.cpp +426 -0
  37. package/native/src/Json.h +85 -0
  38. package/native/src/LiveAudio.cpp +416 -0
  39. package/native/src/LiveAudio.h +121 -0
  40. package/native/src/Main.cpp +178 -0
  41. package/native/src/Paths.h +20 -0
  42. package/native/src/Plugin.cpp +1865 -0
  43. package/native/src/Plugin.h +321 -0
  44. package/native/src/Session.cpp +656 -0
  45. package/native/src/Session.h +82 -0
  46. package/native/src/Settings.cpp +93 -0
  47. package/native/src/Settings.h +23 -0
  48. package/native/src/Version.h.in +11 -0
  49. package/native/src/X11Library.cpp +127 -0
  50. package/native/src/X11Library.h +83 -0
  51. package/native/tools/Embed.cpp +49 -0
  52. package/package.json +58 -3
  53. package/protocol.json +7 -0
  54. package/targets.json +33 -0
package/CMakeLists.txt ADDED
@@ -0,0 +1,194 @@
1
+ # soundor-vst3-host: the native executable of @soundor/vst3-host.
2
+ #
3
+ # cmake --workflow --preset dev Debug, tests, -Werror → build/dev
4
+ # cmake --workflow --preset release the shipped configuration → build/release
5
+ #
6
+ # The version comes from package.json and the protocol from protocol.json,
7
+ # so the executable and the Node client agree on both. Nothing here depends
8
+ # on JUCE, the Soundor engine or any other workspace package.
9
+ cmake_minimum_required(VERSION 3.25)
10
+
11
+ file(READ "${CMAKE_CURRENT_SOURCE_DIR}/package.json" soundor_package)
12
+ string(JSON SOUNDOR_VST3_HOST_VERSION GET "${soundor_package}" version)
13
+ file(READ "${CMAKE_CURRENT_SOURCE_DIR}/protocol.json" soundor_protocol)
14
+ string(JSON SOUNDOR_VST3_HOST_PROTOCOL GET "${soundor_protocol}" version)
15
+ string(JSON SOUNDOR_VST3_HOST_MAX_HEADER GET "${soundor_protocol}" maxHeaderBytes)
16
+ string(JSON SOUNDOR_VST3_HOST_MAX_PAYLOAD GET "${soundor_protocol}" defaultMaxPayloadBytes)
17
+ string(REGEX MATCH "^[0-9]+\\.[0-9]+\\.[0-9]+" soundor_numeric_version "${SOUNDOR_VST3_HOST_VERSION}")
18
+
19
+ # The oldest macOS the host runs on, in any build. CMake wants it before project().
20
+ if(APPLE AND NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET AND NOT DEFINED ENV{MACOSX_DEPLOYMENT_TARGET})
21
+ set(CMAKE_OSX_DEPLOYMENT_TARGET 12.0 CACHE STRING "The oldest macOS the host runs on")
22
+ endif()
23
+
24
+ project(SoundorVst3Host VERSION ${soundor_numeric_version} LANGUAGES C CXX)
25
+ if(APPLE)
26
+ enable_language(OBJCXX)
27
+ endif()
28
+
29
+ option(SOUNDOR_VST3_HOST_BUILD_TESTS "Build the self-test and the fixture plugins" OFF)
30
+ option(SOUNDOR_VST3_HOST_WARNINGS_AS_ERRORS "Treat warnings in the host's own sources as errors" OFF)
31
+ option(SOUNDOR_VST3_HOST_SANITIZERS "Build with AddressSanitizer and UndefinedBehaviorSanitizer" OFF)
32
+ option(SOUNDOR_VST3_HOST_CLANG_TIDY "Check the host's own sources with clang-tidy" OFF)
33
+
34
+ set(CMAKE_CXX_STANDARD 20)
35
+ set(CMAKE_CXX_STANDARD_REQUIRED ON)
36
+ set(CMAKE_CXX_EXTENSIONS OFF)
37
+ set(CMAKE_OBJCXX_STANDARD 20)
38
+ set(CMAKE_CXX_VISIBILITY_PRESET hidden)
39
+ set(CMAKE_OBJCXX_VISIBILITY_PRESET hidden)
40
+ set(CMAKE_VISIBILITY_INLINES_HIDDEN ON)
41
+ set(CMAKE_POSITION_INDEPENDENT_CODE ON)
42
+ # A self-contained executable: no Visual C++ redistributable needed.
43
+ set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
44
+ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
45
+
46
+ find_package(Threads REQUIRED)
47
+ include(cmake/Vst3HostDependencies.cmake)
48
+
49
+ if(SOUNDOR_VST3_HOST_SANITIZERS AND NOT MSVC)
50
+ add_compile_options(-fsanitize=address,undefined -fno-omit-frame-pointer -fno-sanitize-recover=undefined)
51
+ add_link_options(-fsanitize=address,undefined)
52
+ endif()
53
+
54
+ # ── The SDK's hosting helpers ───────────────────────────────────────────────
55
+
56
+ set(sdk "${SOUNDOR_VST3_SDK_DIR}")
57
+ add_library(soundor_vst3_sdk_hosting STATIC
58
+ "${sdk}/pluginterfaces/base/conststringtable.cpp"
59
+ "${sdk}/pluginterfaces/base/coreiids.cpp"
60
+ "${sdk}/pluginterfaces/base/funknown.cpp"
61
+ "${sdk}/pluginterfaces/base/ustring.cpp"
62
+ "${sdk}/public.sdk/source/common/commoniids.cpp"
63
+ "${sdk}/public.sdk/source/common/commonstringconvert.cpp"
64
+ "${sdk}/public.sdk/source/common/memorystream.cpp"
65
+ "${sdk}/public.sdk/source/vst/hosting/connectionproxy.cpp"
66
+ "${sdk}/public.sdk/source/vst/hosting/eventlist.cpp"
67
+ "${sdk}/public.sdk/source/vst/hosting/hostclasses.cpp"
68
+ "${sdk}/public.sdk/source/vst/hosting/module.cpp"
69
+ "${sdk}/public.sdk/source/vst/hosting/parameterchanges.cpp"
70
+ "${sdk}/public.sdk/source/vst/hosting/pluginterfacesupport.cpp"
71
+ "${sdk}/public.sdk/source/vst/utility/stringconvert.cpp"
72
+ "${sdk}/public.sdk/source/vst/vstinitiids.cpp")
73
+ if(WIN32)
74
+ target_sources(soundor_vst3_sdk_hosting PRIVATE
75
+ "${sdk}/public.sdk/source/vst/hosting/module_win32.cpp"
76
+ "${sdk}/public.sdk/source/common/threadchecker_win32.cpp")
77
+ target_link_libraries(soundor_vst3_sdk_hosting PUBLIC ole32 shell32)
78
+ # Written for C++17, where u8string() returns std::string.
79
+ set_source_files_properties("${sdk}/public.sdk/source/vst/hosting/module_win32.cpp"
80
+ PROPERTIES COMPILE_OPTIONS "$<$<CXX_COMPILER_ID:MSVC>:/Zc:char8_t->")
81
+ target_compile_definitions(soundor_vst3_sdk_hosting PUBLIC UNICODE _UNICODE NOMINMAX)
82
+ elseif(APPLE)
83
+ target_sources(soundor_vst3_sdk_hosting PRIVATE
84
+ "${sdk}/public.sdk/source/vst/hosting/module_mac.mm"
85
+ "${sdk}/public.sdk/source/common/threadchecker_mac.mm")
86
+ target_link_libraries(soundor_vst3_sdk_hosting PUBLIC "-framework CoreFoundation" "-framework Foundation")
87
+ # The SDK's Objective-C++ needs ARC; the host's own (EditorWindowMac.mm,
88
+ # EventLoopMac.mm) manages references itself.
89
+ set_source_files_properties("${sdk}/public.sdk/source/vst/hosting/module_mac.mm"
90
+ PROPERTIES COMPILE_OPTIONS -fobjc-arc)
91
+ else()
92
+ target_sources(soundor_vst3_sdk_hosting PRIVATE
93
+ "${sdk}/public.sdk/source/vst/hosting/module_linux.cpp"
94
+ "${sdk}/public.sdk/source/common/threadchecker_linux.cpp")
95
+ target_link_libraries(soundor_vst3_sdk_hosting PUBLIC ${CMAKE_DL_LIBS})
96
+ endif()
97
+ target_include_directories(soundor_vst3_sdk_hosting SYSTEM PUBLIC "${sdk}")
98
+ target_compile_definitions(soundor_vst3_sdk_hosting PUBLIC $<IF:$<CONFIG:Debug>,DEVELOPMENT=1,RELEASE=1>)
99
+ target_link_libraries(soundor_vst3_sdk_hosting PUBLIC Threads::Threads)
100
+ # Upstream code: its warnings are not ours to fix.
101
+ if(MSVC)
102
+ target_compile_options(soundor_vst3_sdk_hosting PRIVATE /W0)
103
+ else()
104
+ target_compile_options(soundor_vst3_sdk_hosting PRIVATE -w)
105
+ endif()
106
+
107
+ # ── Warnings for the host's own sources ─────────────────────────────────────
108
+
109
+ function(soundor_vst3_host_warnings target)
110
+ if(MSVC)
111
+ # Warnings are errors with Clang and GCC (the dev, release and tidy
112
+ # presets); MSVC's /W4 reports, but does not fail, the build.
113
+ target_compile_options(${target} PRIVATE /W4 /permissive- /utf-8)
114
+ else()
115
+ target_compile_options(${target} PRIVATE -Wall -Wextra -Wno-unused-parameter)
116
+ if(SOUNDOR_VST3_HOST_WARNINGS_AS_ERRORS)
117
+ target_compile_options(${target} PRIVATE -Werror)
118
+ endif()
119
+ endif()
120
+ if(SOUNDOR_VST3_HOST_CLANG_TIDY)
121
+ find_program(SOUNDOR_CLANG_TIDY_EXE NAMES clang-tidy-18 clang-tidy REQUIRED)
122
+ set_target_properties(${target} PROPERTIES
123
+ CXX_CLANG_TIDY "${SOUNDOR_CLANG_TIDY_EXE};--warnings-as-errors=*")
124
+ endif()
125
+ endfunction()
126
+
127
+ # Every configuration's outputs in one place (Visual Studio adds a
128
+ # per-configuration directory otherwise).
129
+ function(soundor_vst3_host_output target kind directory)
130
+ set_target_properties(${target} PROPERTIES ${kind}_OUTPUT_DIRECTORY "${directory}")
131
+ foreach(config IN ITEMS Debug Release RelWithDebInfo MinSizeRel)
132
+ string(TOUPPER ${config} upper)
133
+ set_target_properties(${target} PROPERTIES ${kind}_OUTPUT_DIRECTORY_${upper} "${directory}")
134
+ endforeach()
135
+ endfunction()
136
+
137
+ # ── The executable ──────────────────────────────────────────────────────────
138
+
139
+ configure_file(native/src/Version.h.in "${CMAKE_BINARY_DIR}/generated/Version.h" @ONLY)
140
+
141
+ add_executable(soundor-vst3-host
142
+ native/src/Bundle.cpp
143
+ native/src/HostContext.cpp
144
+ native/src/HostInterface.cpp
145
+ native/src/HostWindow.cpp
146
+ native/src/Ipc.cpp
147
+ native/src/Json.cpp
148
+ native/src/LiveAudio.cpp
149
+ native/src/Main.cpp
150
+ native/src/Plugin.cpp
151
+ native/src/Session.cpp
152
+ native/src/Settings.cpp
153
+ "${SOUNDOR_VST3_HOST_FONTS_SOURCE}")
154
+ if(WIN32)
155
+ target_sources(soundor-vst3-host PRIVATE native/src/EventLoopWindows.cpp native/src/EditorWindowWindows.cpp
156
+ native/src/EditorAreaWindows.cpp)
157
+ target_link_libraries(soundor-vst3-host PRIVATE user32 gdi32)
158
+ elseif(APPLE)
159
+ target_sources(soundor-vst3-host PRIVATE native/src/EventLoopMac.mm native/src/EditorWindowMac.mm
160
+ native/src/EditorAreaMac.mm)
161
+ target_link_libraries(soundor-vst3-host PRIVATE "-framework Cocoa")
162
+ else()
163
+ # Xlib's headers only: the library is loaded in UI mode (X11Library.cpp).
164
+ find_path(SOUNDOR_X11_INCLUDE_DIR X11/Xlib.h REQUIRED)
165
+ target_include_directories(soundor-vst3-host SYSTEM PRIVATE "${SOUNDOR_X11_INCLUDE_DIR}")
166
+ target_sources(soundor-vst3-host PRIVATE native/src/EventLoopLinux.cpp native/src/EditorAreaX11.cpp
167
+ native/src/X11Library.cpp)
168
+ # The C++ runtime inside the executable, in every build: it runs on
169
+ # distributions with an older libstdc++ than the build machine's. A failed
170
+ # check is not kept, so installing the library is enough.
171
+ include(CheckCXXSourceCompiles)
172
+ set(CMAKE_REQUIRED_LINK_OPTIONS -static-libstdc++ -static-libgcc)
173
+ check_cxx_source_compiles("#include <string>
174
+ int main(int argc, char** argv) { return static_cast<int>(std::string(argv[0]).size()) + argc; }" SOUNDOR_VST3_HOST_STATIC_CXX_RUNTIME)
175
+ unset(CMAKE_REQUIRED_LINK_OPTIONS)
176
+ if(NOT SOUNDOR_VST3_HOST_STATIC_CXX_RUNTIME)
177
+ unset(SOUNDOR_VST3_HOST_STATIC_CXX_RUNTIME CACHE)
178
+ message(FATAL_ERROR "Linux builds link the C++ runtime statically, and libstdc++.a wasn't found.\n"
179
+ "Fedora, RHEL: sudo dnf install libstdc++-static\n"
180
+ "Elsewhere: the package with your compiler's static C++ library.")
181
+ endif()
182
+ target_link_options(soundor-vst3-host PRIVATE -static-libstdc++ -static-libgcc)
183
+ endif()
184
+ target_include_directories(soundor-vst3-host PRIVATE native/src "${CMAKE_BINARY_DIR}/generated")
185
+ target_link_libraries(soundor-vst3-host PRIVATE soundor_vst3_sdk_hosting soundor_imgui SDL3::SDL3-static)
186
+ # A large generated array: nothing to lint.
187
+ set_source_files_properties("${SOUNDOR_VST3_HOST_FONTS_SOURCE}" PROPERTIES SKIP_LINTING ON)
188
+ soundor_vst3_host_warnings(soundor-vst3-host)
189
+ soundor_vst3_host_output(soundor-vst3-host RUNTIME "${CMAKE_BINARY_DIR}/bin")
190
+
191
+ if(SOUNDOR_VST3_HOST_BUILD_TESTS)
192
+ enable_testing()
193
+ add_subdirectory(native/tests)
194
+ endif()
@@ -0,0 +1,115 @@
1
+ {
2
+ "version": 6,
3
+ "cmakeMinimumRequired": {
4
+ "major": 3,
5
+ "minor": 25,
6
+ "patch": 0
7
+ },
8
+ "configurePresets": [
9
+ {
10
+ "name": "dev",
11
+ "displayName": "Debug build with the self-test and fixture plugins",
12
+ "binaryDir": "${sourceDir}/build/dev",
13
+ "cacheVariables": {
14
+ "CMAKE_BUILD_TYPE": "Debug",
15
+ "SOUNDOR_VST3_HOST_BUILD_TESTS": "ON",
16
+ "SOUNDOR_VST3_HOST_WARNINGS_AS_ERRORS": "ON"
17
+ }
18
+ },
19
+ {
20
+ "name": "release",
21
+ "displayName": "The shipped configuration, with the fixtures for its tests",
22
+ "binaryDir": "${sourceDir}/build/release",
23
+ "cacheVariables": {
24
+ "CMAKE_BUILD_TYPE": "Release",
25
+ "SOUNDOR_VST3_HOST_BUILD_TESTS": "ON",
26
+ "SOUNDOR_VST3_HOST_WARNINGS_AS_ERRORS": "ON"
27
+ }
28
+ },
29
+ {
30
+ "name": "sanitize",
31
+ "inherits": "dev",
32
+ "displayName": "Debug build with ASan + UBSan",
33
+ "binaryDir": "${sourceDir}/build/sanitize",
34
+ "cacheVariables": {
35
+ "SOUNDOR_VST3_HOST_SANITIZERS": "ON"
36
+ }
37
+ },
38
+ {
39
+ "name": "tidy",
40
+ "inherits": "dev",
41
+ "displayName": "Debug build checked by clang-tidy",
42
+ "binaryDir": "${sourceDir}/build/tidy",
43
+ "cacheVariables": {
44
+ "SOUNDOR_VST3_HOST_CLANG_TIDY": "ON"
45
+ }
46
+ }
47
+ ],
48
+ "buildPresets": [
49
+ { "name": "dev", "configurePreset": "dev", "configuration": "Debug" },
50
+ {
51
+ "name": "release",
52
+ "configurePreset": "release",
53
+ "configuration": "Release"
54
+ },
55
+ {
56
+ "name": "sanitize",
57
+ "configurePreset": "sanitize",
58
+ "configuration": "Debug"
59
+ },
60
+ { "name": "tidy", "configurePreset": "tidy", "configuration": "Debug" }
61
+ ],
62
+ "testPresets": [
63
+ {
64
+ "name": "dev",
65
+ "configurePreset": "dev",
66
+ "configuration": "Debug",
67
+ "output": { "outputOnFailure": true }
68
+ },
69
+ {
70
+ "name": "release",
71
+ "configurePreset": "release",
72
+ "configuration": "Release",
73
+ "output": { "outputOnFailure": true }
74
+ },
75
+ {
76
+ "name": "sanitize",
77
+ "configurePreset": "sanitize",
78
+ "configuration": "Debug",
79
+ "output": { "outputOnFailure": true }
80
+ }
81
+ ],
82
+ "workflowPresets": [
83
+ {
84
+ "name": "dev",
85
+ "steps": [
86
+ { "type": "configure", "name": "dev" },
87
+ { "type": "build", "name": "dev" },
88
+ { "type": "test", "name": "dev" }
89
+ ]
90
+ },
91
+ {
92
+ "name": "release",
93
+ "steps": [
94
+ { "type": "configure", "name": "release" },
95
+ { "type": "build", "name": "release" },
96
+ { "type": "test", "name": "release" }
97
+ ]
98
+ },
99
+ {
100
+ "name": "sanitize",
101
+ "steps": [
102
+ { "type": "configure", "name": "sanitize" },
103
+ { "type": "build", "name": "sanitize" },
104
+ { "type": "test", "name": "sanitize" }
105
+ ]
106
+ },
107
+ {
108
+ "name": "tidy",
109
+ "steps": [
110
+ { "type": "configure", "name": "tidy" },
111
+ { "type": "build", "name": "tidy" }
112
+ ]
113
+ }
114
+ ]
115
+ }
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Dmitrii Balakin and Egor Smirnov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/NOTICE.md ADDED
@@ -0,0 +1,17 @@
1
+ # Notices
2
+
3
+ `@soundor/vst3-host` is MIT-licensed (LICENSE). Its executable is built with
4
+ the following third-party code, under its own license:
5
+
6
+ | Component | Version | License | Notice |
7
+ | -------------------------------------------------------------------------------------------------- | -------------- | ------- | ------------------------------------------------------- |
8
+ | [VST3 SDK](https://github.com/steinbergmedia/vst3sdk) (pluginterfaces, public.sdk hosting helpers) | 3.8.1 build 84 | MIT | `licenses/vst3sdk/LICENSE.txt` in each platform package |
9
+
10
+ | [SDL](https://github.com/libsdl-org/SDL) | 3.4.18 | Zlib | `licenses/sdl/LICENSE.txt` in each platform package |
11
+ | [Dear ImGui](https://github.com/ocornut/imgui) | 1.92.9b | MIT | `licenses/imgui/LICENSE.txt` in each platform package |
12
+ | [Inter](https://github.com/rsms/inter) (font) | 4.1 | OFL-1.1 | `licenses/inter/LICENSE.txt` in each platform package |
13
+
14
+ VST is a registered trademark of Steinberg Media Technologies GmbH.
15
+
16
+ The test fixtures (`native/tests/fixtures`) additionally use the SDK's `base`
17
+ library (MIT); they are never shipped as binaries.
package/README.md CHANGED
@@ -1,3 +1,41 @@
1
- # Temporary Holding Version
1
+ # @soundor/vst3-host
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ A standalone VST3 host for tests and development: a native executable driven
4
+ from Node.js. It loads a `.vst3` bundle by path, processes audio offline
5
+ (parameters, MIDI, transport, state), and opens the plugin's editor in a
6
+ native window when asked. Any VST3 plugin, made with Soundor or not.
7
+
8
+ ```sh
9
+ pnpm add -D @soundor/vst3-host
10
+ ```
11
+
12
+ ```ts
13
+ import { launchHost } from '@soundor/vst3-host';
14
+
15
+ const host = await launchHost({
16
+ plugin: 'build/MyPlugin.vst3',
17
+ sampleRate: 48_000,
18
+ blockSize: 512,
19
+ });
20
+ try {
21
+ const [first] = await host.getParameters();
22
+ await host.setParameter(first!.id, 0.5);
23
+ const input = new Float32Array(48_000);
24
+ const { outputs } = await host.process({ inputs: [[input, input]] });
25
+ } finally {
26
+ await host.close();
27
+ }
28
+ ```
29
+
30
+ The executable comes in a platform package installed with this one, at the
31
+ same version: Linux x64 (glibc), macOS (arm64 and x64) and Windows x64.
32
+ Installing compiles and downloads nothing else.
33
+
34
+ Documentation: [user guide](https://soundor.dev/docs/testing/vst3-host) and
35
+ [internals](https://soundor.dev/docs/contribute/vst3-host) (building from
36
+ source, prebuilt selection, releases).
37
+
38
+ ## License
39
+
40
+ MIT (LICENSE). The executable includes the VST3 SDK, under its own MIT license
41
+ (NOTICE.md). VST is a registered trademark of Steinberg Media Technologies GmbH.
@@ -0,0 +1,179 @@
1
+ # The host's third-party dependencies.
2
+ #
3
+ # Each is fetched at configure time from an exact upstream commit or release,
4
+ # verified by SHA-256, and compiled into a private static target. The archives
5
+ # are kept in the user cache (SOUNDOR_CACHE, else ~/.cache/soundor,
6
+ # ~/Library/Caches/Soundor or %LOCALAPPDATA%\Soundor\cache), so a new build
7
+ # directory does not download them again.
8
+ #
9
+ # | Dependency | Version | License | Used for |
10
+ # | ---------------------------- | --------------- | -------- | --------------------------------- |
11
+ # | VST3 SDK pluginterfaces | 3.8.1 build 84 | MIT | VST3 interfaces (host, fixtures) |
12
+ # | VST3 SDK public.sdk | 3.8.1 build 84 | MIT | hosting helpers (host), plugin base classes (fixtures) |
13
+ # | VST3 SDK base | 3.8.1 build 84 | MIT | plugin base classes (fixtures only) |
14
+ # | SDL | 3.4.18 | zlib | the host window, live audio, file dialogs |
15
+ # | Dear ImGui | 1.92.9b | MIT | the host interface |
16
+ # | Inter | 4.1 | OFL-1.1 | the host interface's font |
17
+ #
18
+ # SDL is the one built by its own CMake project (as a static library, with only
19
+ # the subsystems the host uses): its platform configuration is not worth
20
+ # reproducing. ImGui and Inter are compiled and embedded by the host's CMake.
21
+ #
22
+ # The tag v3.8.1_build_84 of each repository: VST3 SDK 3.8 is the first
23
+ # release under the MIT license. Offline: -DFETCHCONTENT_SOURCE_DIR_SOUNDOR_VST3_<PART>=<checkout>.
24
+
25
+ include_guard(GLOBAL)
26
+ include(FetchContent)
27
+
28
+ set(SOUNDOR_VST3_SDK_VERSION "3.8.1")
29
+ set(SOUNDOR_VST3_PLUGINTERFACES_REVISION "4f547e8e102b47de4a8b8aaf343c73b700786372")
30
+ set(SOUNDOR_VST3_PLUGINTERFACES_SHA256 "ee70777eebb8450fc04fae2a6dd452f723d12bfb8222af443b8c1f8a9259089a")
31
+ set(SOUNDOR_VST3_PUBLIC_SDK_REVISION "586dc5e6c8012c3e4b01c79389375cbe96bdb1da")
32
+ set(SOUNDOR_VST3_PUBLIC_SDK_SHA256 "71319491b771068b8e2c5374c22830a339677e9d54c4cd33af4a0aa210e4c838")
33
+ set(SOUNDOR_VST3_BASE_REVISION "fcf9da0bd27a16f7f03773a3a39822f28f5c8477")
34
+ set(SOUNDOR_VST3_BASE_SHA256 "3753d45a79b5e324c2843d74a18c8f1117fe0b1118fa9312658aecf6360ec603")
35
+
36
+ function(soundor_vst3_host_cache_root out)
37
+ if(DEFINED ENV{SOUNDOR_CACHE} AND NOT "$ENV{SOUNDOR_CACHE}" STREQUAL "")
38
+ set(root "$ENV{SOUNDOR_CACHE}")
39
+ elseif(WIN32)
40
+ set(root "$ENV{LOCALAPPDATA}/Soundor/cache")
41
+ elseif(APPLE)
42
+ set(root "$ENV{HOME}/Library/Caches/Soundor")
43
+ elseif(DEFINED ENV{XDG_CACHE_HOME} AND NOT "$ENV{XDG_CACHE_HOME}" STREQUAL "")
44
+ set(root "$ENV{XDG_CACHE_HOME}/soundor")
45
+ else()
46
+ set(root "$ENV{HOME}/.cache/soundor")
47
+ endif()
48
+ file(TO_CMAKE_PATH "${root}" root)
49
+ set(${out} "${root}" PARENT_SCOPE)
50
+ endfunction()
51
+
52
+ # The SDK's sources include each other as "pluginterfaces/...",
53
+ # "public.sdk/source/..." and "base/source/...": the parts are laid out
54
+ # side by side under one root, as the SDK's umbrella repository does.
55
+ set(SOUNDOR_VST3_SDK_DIR "${CMAKE_BINARY_DIR}/_deps/vst3sdk")
56
+ soundor_vst3_host_cache_root(soundor_cache)
57
+ set(soundor_downloads "${soundor_cache}/vst3-host/downloads")
58
+
59
+ function(soundor_vst3_sdk_part name repository directory revision sha256)
60
+ FetchContent_Declare(
61
+ ${name}
62
+ URL "https://github.com/steinbergmedia/${repository}/archive/${revision}.tar.gz"
63
+ URL_HASH "SHA256=${sha256}"
64
+ DOWNLOAD_NAME "${repository}-${revision}.tar.gz"
65
+ DOWNLOAD_DIR "${soundor_downloads}"
66
+ DOWNLOAD_EXTRACT_TIMESTAMP TRUE
67
+ SOURCE_DIR "${SOUNDOR_VST3_SDK_DIR}/${directory}"
68
+ # Populate only: the host defines its own SDK targets.
69
+ SOURCE_SUBDIR "soundor-populate-only")
70
+ FetchContent_MakeAvailable(${name})
71
+ endfunction()
72
+
73
+ soundor_vst3_sdk_part(soundor_vst3_pluginterfaces vst3_pluginterfaces pluginterfaces
74
+ ${SOUNDOR_VST3_PLUGINTERFACES_REVISION} ${SOUNDOR_VST3_PLUGINTERFACES_SHA256})
75
+ soundor_vst3_sdk_part(soundor_vst3_public_sdk vst3_public_sdk public.sdk
76
+ ${SOUNDOR_VST3_PUBLIC_SDK_REVISION} ${SOUNDOR_VST3_PUBLIC_SDK_SHA256})
77
+ if(SOUNDOR_VST3_HOST_BUILD_TESTS)
78
+ soundor_vst3_sdk_part(soundor_vst3_base vst3_base base
79
+ ${SOUNDOR_VST3_BASE_REVISION} ${SOUNDOR_VST3_BASE_SHA256})
80
+ endif()
81
+
82
+ # The notice every distributed host executable carries.
83
+ configure_file("${SOUNDOR_VST3_SDK_DIR}/pluginterfaces/LICENSE.txt"
84
+ "${CMAKE_BINARY_DIR}/licenses/vst3sdk/LICENSE.txt" COPYONLY)
85
+
86
+ # ── The host interface: SDL, Dear ImGui, Inter ──────────────────────────────
87
+
88
+ set(SOUNDOR_SDL_VERSION "3.4.18")
89
+ set(SOUNDOR_SDL_SHA256 "9c75cf16330322c217dedd2e0609f1124f1b54b8633e763467b4684d0f4334a3")
90
+ set(SOUNDOR_IMGUI_VERSION "1.92.9b")
91
+ set(SOUNDOR_IMGUI_SHA256 "21d8a0a565e85dce943e375db00812c2f3f0ab21f3f0f7964e364a63422d7f99")
92
+ set(SOUNDOR_INTER_VERSION "4.1")
93
+ set(SOUNDOR_INTER_SHA256 "9883fdd4a49d4fb66bd8177ba6625ef9a64aa45899767dde3d36aa425756b11e")
94
+
95
+ function(soundor_vst3_host_archive name url sha256 download_name out)
96
+ FetchContent_Declare(
97
+ ${name}
98
+ URL "${url}"
99
+ URL_HASH "SHA256=${sha256}"
100
+ DOWNLOAD_NAME "${download_name}"
101
+ DOWNLOAD_DIR "${soundor_downloads}"
102
+ DOWNLOAD_EXTRACT_TIMESTAMP TRUE
103
+ # Populate only: SDL is configured below; the other targets are ours.
104
+ SOURCE_SUBDIR "soundor-populate-only")
105
+ FetchContent_MakeAvailable(${name})
106
+ set(${out} "${${name}_SOURCE_DIR}" PARENT_SCOPE)
107
+ endfunction()
108
+
109
+ soundor_vst3_host_archive(soundor_sdl
110
+ "https://github.com/libsdl-org/SDL/releases/download/release-${SOUNDOR_SDL_VERSION}/SDL3-${SOUNDOR_SDL_VERSION}.tar.gz"
111
+ ${SOUNDOR_SDL_SHA256} "SDL3-${SOUNDOR_SDL_VERSION}.tar.gz" SOUNDOR_SDL_DIR)
112
+ soundor_vst3_host_archive(soundor_imgui
113
+ "https://github.com/ocornut/imgui/archive/refs/tags/v${SOUNDOR_IMGUI_VERSION}.tar.gz"
114
+ ${SOUNDOR_IMGUI_SHA256} "imgui-${SOUNDOR_IMGUI_VERSION}.tar.gz" SOUNDOR_IMGUI_DIR)
115
+ soundor_vst3_host_archive(soundor_inter
116
+ "https://github.com/rsms/inter/releases/download/v${SOUNDOR_INTER_VERSION}/Inter-${SOUNDOR_INTER_VERSION}.zip"
117
+ ${SOUNDOR_INTER_SHA256} "Inter-${SOUNDOR_INTER_VERSION}.zip" SOUNDOR_INTER_DIR)
118
+
119
+ # SDL: a static library with the subsystems the host interface uses (video,
120
+ # rendering, events, audio, dialogs). On Linux, X11 only (VST3 editors are
121
+ # X11 windows), and every system library is loaded at run time, so headless
122
+ # mode needs none of them.
123
+ function(soundor_vst3_host_sdl)
124
+ set(BUILD_SHARED_LIBS OFF)
125
+ set(CMAKE_C_CLANG_TIDY "")
126
+ set(CMAKE_CXX_CLANG_TIDY "")
127
+ foreach(option IN ITEMS SDL_STATIC SDL_AUDIO SDL_VIDEO SDL_RENDER SDL_DEPS_SHARED SDL_DISABLE_INSTALL
128
+ SDL_X11_XSYNC)
129
+ set(${option} ON CACHE BOOL "" FORCE)
130
+ endforeach()
131
+ foreach(option IN ITEMS SDL_SHARED SDL_TEST_LIBRARY SDL_TESTS SDL_EXAMPLES SDL_INSTALL SDL_GPU SDL_CAMERA
132
+ SDL_HAPTIC SDL_HIDAPI SDL_POWER SDL_SENSOR SDL_TRAY SDL_VULKAN SDL_RENDER_VULKAN
133
+ SDL_RENDER_GPU SDL_WAYLAND SDL_KMSDRM SDL_OFFSCREEN SDL_DUMMYCAMERA SDL_LIBUDEV
134
+ SDL_IBUS SDL_JACK SDL_SNDIO SDL_OSS SDL_DISKAUDIO SDL_OPENVR SDL_RPI SDL_ROCKCHIP
135
+ SDL_VIVANTE SDL_LIBURING SDL_FRIBIDI SDL_LIBTHAI SDL_CLANG_TIDY SDL_RPATH
136
+ SDL_X11_XSCRNSAVER SDL_X11_XSHAPE SDL_X11_XDBE SDL_X11_XTEST)
137
+ set(${option} OFF CACHE BOOL "" FORCE)
138
+ endforeach()
139
+ add_subdirectory("${SOUNDOR_SDL_DIR}" "${CMAKE_BINARY_DIR}/_deps/sdl-build" EXCLUDE_FROM_ALL SYSTEM)
140
+ endfunction()
141
+ soundor_vst3_host_sdl()
142
+
143
+ add_library(soundor_imgui STATIC
144
+ "${SOUNDOR_IMGUI_DIR}/imgui.cpp"
145
+ "${SOUNDOR_IMGUI_DIR}/imgui_draw.cpp"
146
+ "${SOUNDOR_IMGUI_DIR}/imgui_tables.cpp"
147
+ "${SOUNDOR_IMGUI_DIR}/imgui_widgets.cpp"
148
+ "${SOUNDOR_IMGUI_DIR}/backends/imgui_impl_sdl3.cpp"
149
+ "${SOUNDOR_IMGUI_DIR}/backends/imgui_impl_sdlrenderer3.cpp")
150
+ target_include_directories(soundor_imgui SYSTEM PUBLIC "${SOUNDOR_IMGUI_DIR}" "${SOUNDOR_IMGUI_DIR}/backends")
151
+ # No demo window, no default font file, no shell or clipboard helpers the
152
+ # host does not use.
153
+ target_compile_definitions(soundor_imgui PUBLIC IMGUI_DISABLE_DEMO_WINDOWS IMGUI_DISABLE_DEBUG_TOOLS
154
+ IMGUI_DISABLE_OBSOLETE_FUNCTIONS)
155
+ target_link_libraries(soundor_imgui PUBLIC SDL3::SDL3-static)
156
+ if(MSVC)
157
+ target_compile_options(soundor_imgui PRIVATE /W0)
158
+ else()
159
+ target_compile_options(soundor_imgui PRIVATE -w)
160
+ endif()
161
+
162
+ # Inter's regular and semibold faces, embedded as byte arrays by a small
163
+ # build-time tool (CMake's string functions are too slow for fonts).
164
+ add_executable(soundor_vst3_host_embed "${CMAKE_CURRENT_LIST_DIR}/../native/tools/Embed.cpp")
165
+ set(SOUNDOR_VST3_HOST_FONTS_SOURCE "${CMAKE_BINARY_DIR}/generated/HostFonts.cpp")
166
+ add_custom_command(
167
+ OUTPUT "${SOUNDOR_VST3_HOST_FONTS_SOURCE}"
168
+ COMMAND soundor_vst3_host_embed "${SOUNDOR_VST3_HOST_FONTS_SOURCE}" soundor::vst3host::fonts
169
+ regular "${SOUNDOR_INTER_DIR}/extras/ttf/Inter-Regular.ttf"
170
+ semibold "${SOUNDOR_INTER_DIR}/extras/ttf/Inter-SemiBold.ttf"
171
+ DEPENDS soundor_vst3_host_embed
172
+ "${SOUNDOR_INTER_DIR}/extras/ttf/Inter-Regular.ttf"
173
+ "${SOUNDOR_INTER_DIR}/extras/ttf/Inter-SemiBold.ttf"
174
+ COMMENT "Embedding the host interface's fonts"
175
+ VERBATIM)
176
+
177
+ configure_file("${SOUNDOR_SDL_DIR}/LICENSE.txt" "${CMAKE_BINARY_DIR}/licenses/sdl/LICENSE.txt" COPYONLY)
178
+ configure_file("${SOUNDOR_IMGUI_DIR}/LICENSE.txt" "${CMAKE_BINARY_DIR}/licenses/imgui/LICENSE.txt" COPYONLY)
179
+ configure_file("${SOUNDOR_INTER_DIR}/LICENSE.txt" "${CMAKE_BINARY_DIR}/licenses/inter/LICENSE.txt" COPYONLY)
package/dist/cli.d.mts ADDED
@@ -0,0 +1 @@
1
+ export {}
package/dist/cli.mjs ADDED
@@ -0,0 +1,88 @@
1
+ #!/usr/bin/env node
2
+ import { a as nativeInputs, i as resolveHost, l as Vst3HostError, r as launchHost, t as prepareHost } from "./prepare-ayjTVhfk.mjs";
3
+ import { parseArgs } from "node:util";
4
+ // src/cli.ts
5
+ const { positionals, values } = parseArgs({
6
+ allowPositionals: true,
7
+ options: {
8
+ mode: { type: "string" },
9
+ artifact: { type: "string" },
10
+ commit: { type: "string" },
11
+ class: { type: "string" },
12
+ bare: { type: "boolean" },
13
+ interface: { type: "boolean" },
14
+ "sample-rate": { type: "string" },
15
+ "block-size": { type: "string" }
16
+ }
17
+ });
18
+ const USAGE = `usage: soundor-vst3-host <command>
19
+ open <plugin.vst3> [--class <name>] [--bare | --interface] [--sample-rate <hz>] [--block-size <frames>]
20
+ prepare [--mode prebuilt|published|source] [--artifact <dir>] [--commit <sha>]
21
+ resolve
22
+ inputs`;
23
+ /** Opens a plugin's editor and waits until its window is closed. */
24
+ async function open() {
25
+ const plugin = positionals[1];
26
+ if (plugin === void 0) throw new Error("open needs a .vst3 bundle");
27
+ if (values.bare && values.interface) throw new Error("--bare and --interface are opposites");
28
+ const number = (name) => {
29
+ const text = values[name];
30
+ if (text === void 0) return void 0;
31
+ const value = Number(text);
32
+ if (!Number.isFinite(value)) throw new Error(`--${name} is a number`);
33
+ return value;
34
+ };
35
+ const host = await launchHost({
36
+ plugin,
37
+ className: values.class,
38
+ editor: true,
39
+ presentation: values.bare ? "editor" : values.interface ? "host" : null,
40
+ sampleRate: number("sample-rate"),
41
+ blockSize: number("block-size"),
42
+ requestTimeout: 2147483647,
43
+ onLog: (line) => console.error(line)
44
+ });
45
+ const closed = new Promise((resolve) => {
46
+ host.once("editorClosed", () => resolve());
47
+ host.once("exit", () => resolve());
48
+ process.once("SIGINT", () => resolve());
49
+ process.once("SIGTERM", () => resolve());
50
+ });
51
+ console.error(`${host.info.name}: close its window (or press Ctrl+C) to quit`);
52
+ await closed;
53
+ await host.close();
54
+ }
55
+ try {
56
+ switch (positionals[0]) {
57
+ case "open":
58
+ await open();
59
+ break;
60
+ case "prepare": {
61
+ const mode = values.mode ?? "prebuilt";
62
+ if (mode !== "prebuilt" && mode !== "published" && mode !== "source") throw new Error(`--mode is "prebuilt", "published" or "source", not "${mode}"`);
63
+ await prepareHost({
64
+ mode,
65
+ artifact: values.artifact,
66
+ commit: values.commit
67
+ });
68
+ break;
69
+ }
70
+ case "resolve": {
71
+ const resolved = resolveHost();
72
+ console.log(`${resolved.executable}\t(${resolved.source}: ${resolved.reason})`);
73
+ break;
74
+ }
75
+ case "inputs":
76
+ console.log(JSON.stringify(nativeInputs(), null, 2));
77
+ break;
78
+ default:
79
+ console.error(USAGE);
80
+ process.exitCode = 64;
81
+ }
82
+ } catch (error) {
83
+ console.error(error instanceof Vst3HostError ? `error (${error.code}): ${error.message}` : `error: ${error.message}`);
84
+ process.exitCode = 1;
85
+ }
86
+ export {};
87
+
88
+ //# sourceMappingURL=cli.mjs.map