@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.
- package/CMakeLists.txt +194 -0
- package/CMakePresets.json +115 -0
- package/LICENSE +21 -0
- package/NOTICE.md +17 -0
- package/README.md +40 -2
- package/cmake/Vst3HostDependencies.cmake +179 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.mjs +88 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/index.d.mts +586 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +1 -0
- package/dist/prepare-ayjTVhfk.mjs +1149 -0
- package/dist/prepare-ayjTVhfk.mjs.map +1 -0
- package/native/src/Bundle.cpp +253 -0
- package/native/src/Bundle.h +32 -0
- package/native/src/EditorArea.h +92 -0
- package/native/src/EditorAreaMac.mm +52 -0
- package/native/src/EditorAreaWindows.cpp +66 -0
- package/native/src/EditorAreaX11.cpp +512 -0
- package/native/src/EditorWindow.h +72 -0
- package/native/src/EditorWindowMac.mm +134 -0
- package/native/src/EditorWindowWindows.cpp +148 -0
- package/native/src/EventLoop.h +50 -0
- package/native/src/EventLoopLinux.cpp +204 -0
- package/native/src/EventLoopMac.mm +146 -0
- package/native/src/EventLoopWindows.cpp +132 -0
- package/native/src/HostContext.cpp +217 -0
- package/native/src/HostContext.h +116 -0
- package/native/src/HostInterface.cpp +1026 -0
- package/native/src/HostInterface.h +138 -0
- package/native/src/HostWindow.cpp +901 -0
- package/native/src/HostWindow.h +187 -0
- package/native/src/Ipc.cpp +158 -0
- package/native/src/Ipc.h +91 -0
- package/native/src/Json.cpp +426 -0
- package/native/src/Json.h +85 -0
- package/native/src/LiveAudio.cpp +416 -0
- package/native/src/LiveAudio.h +121 -0
- package/native/src/Main.cpp +178 -0
- package/native/src/Paths.h +20 -0
- package/native/src/Plugin.cpp +1865 -0
- package/native/src/Plugin.h +321 -0
- package/native/src/Session.cpp +656 -0
- package/native/src/Session.h +82 -0
- package/native/src/Settings.cpp +93 -0
- package/native/src/Settings.h +23 -0
- package/native/src/Version.h.in +11 -0
- package/native/src/X11Library.cpp +127 -0
- package/native/src/X11Library.h +83 -0
- package/native/tools/Embed.cpp +49 -0
- package/package.json +58 -3
- package/protocol.json +7 -0
- package/targets.json +33 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
// soundor-vst3-host: a VST3 host controlled over stdin/stdout (README,
|
|
2
|
+
// "Protocol"). Usage:
|
|
3
|
+
//
|
|
4
|
+
// soundor-vst3-host [--ui] [--max-payload <bytes>] run, speaking the protocol
|
|
5
|
+
// soundor-vst3-host --version print its identity as JSON
|
|
6
|
+
//
|
|
7
|
+
// Protocol frames use the process's original stdin and stdout; plugins that
|
|
8
|
+
// write to stdout (fd 1) write to stderr instead, which carries diagnostics.
|
|
9
|
+
#include "EventLoop.h"
|
|
10
|
+
#include "Ipc.h"
|
|
11
|
+
#include "Session.h"
|
|
12
|
+
#include "Version.h"
|
|
13
|
+
|
|
14
|
+
#include <cstdio>
|
|
15
|
+
#include <cstdlib>
|
|
16
|
+
#include <cstring>
|
|
17
|
+
#include <memory>
|
|
18
|
+
#include <string>
|
|
19
|
+
#include <thread>
|
|
20
|
+
|
|
21
|
+
#if defined(_WIN32)
|
|
22
|
+
#ifndef NOMINMAX
|
|
23
|
+
#define NOMINMAX
|
|
24
|
+
#endif
|
|
25
|
+
#include <fcntl.h>
|
|
26
|
+
#include <io.h>
|
|
27
|
+
#include <ole2.h>
|
|
28
|
+
#include <windows.h>
|
|
29
|
+
#else
|
|
30
|
+
#include <fcntl.h>
|
|
31
|
+
#include <unistd.h>
|
|
32
|
+
#endif
|
|
33
|
+
#if defined(__linux__)
|
|
34
|
+
#include <sys/prctl.h>
|
|
35
|
+
|
|
36
|
+
#include <csignal>
|
|
37
|
+
#endif
|
|
38
|
+
|
|
39
|
+
namespace
|
|
40
|
+
{
|
|
41
|
+
using namespace soundor::vst3host;
|
|
42
|
+
|
|
43
|
+
struct Channels
|
|
44
|
+
{
|
|
45
|
+
NativeHandle in = 0;
|
|
46
|
+
NativeHandle out = 0;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// Moves the protocol's streams to private handles, and points the
|
|
50
|
+
// standard ones somewhere harmless: stdout to stderr, stdin to nothing.
|
|
51
|
+
Channels takeStdio()
|
|
52
|
+
{
|
|
53
|
+
Channels channels;
|
|
54
|
+
#if defined(_WIN32)
|
|
55
|
+
const HANDLE process = GetCurrentProcess();
|
|
56
|
+
HANDLE in = nullptr;
|
|
57
|
+
HANDLE out = nullptr;
|
|
58
|
+
DuplicateHandle(process, GetStdHandle(STD_INPUT_HANDLE), process, &in, 0, FALSE, DUPLICATE_SAME_ACCESS);
|
|
59
|
+
DuplicateHandle(process, GetStdHandle(STD_OUTPUT_HANDLE), process, &out, 0, FALSE, DUPLICATE_SAME_ACCESS);
|
|
60
|
+
channels.in = reinterpret_cast<NativeHandle>(in);
|
|
61
|
+
channels.out = reinterpret_cast<NativeHandle>(out);
|
|
62
|
+
std::fflush(stdout);
|
|
63
|
+
_dup2(_fileno(stderr), _fileno(stdout));
|
|
64
|
+
SetStdHandle(STD_OUTPUT_HANDLE, GetStdHandle(STD_ERROR_HANDLE));
|
|
65
|
+
const int nul = _open("NUL", _O_RDONLY);
|
|
66
|
+
if (nul >= 0)
|
|
67
|
+
{
|
|
68
|
+
_dup2(nul, _fileno(stdin));
|
|
69
|
+
_close(nul);
|
|
70
|
+
}
|
|
71
|
+
SetStdHandle(STD_INPUT_HANDLE, reinterpret_cast<HANDLE>(_get_osfhandle(_fileno(stdin))));
|
|
72
|
+
#else
|
|
73
|
+
channels.in = fcntl(0, F_DUPFD_CLOEXEC, 3);
|
|
74
|
+
channels.out = fcntl(1, F_DUPFD_CLOEXEC, 3);
|
|
75
|
+
std::fflush(stdout);
|
|
76
|
+
dup2(2, 1);
|
|
77
|
+
const int null = open("/dev/null", O_RDONLY | O_CLOEXEC);
|
|
78
|
+
if (null >= 0)
|
|
79
|
+
{
|
|
80
|
+
dup2(null, 0);
|
|
81
|
+
close(null);
|
|
82
|
+
}
|
|
83
|
+
#endif
|
|
84
|
+
return channels;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
int printVersion()
|
|
88
|
+
{
|
|
89
|
+
std::printf("{\"name\":\"@soundor/vst3-host\",\"version\":\"%s\",\"protocol\":%d,\"sdkVersion\":\"%s\"}\n",
|
|
90
|
+
hostVersion, protocolVersion, sdkVersion);
|
|
91
|
+
return 0;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
int usage(const char* problem)
|
|
95
|
+
{
|
|
96
|
+
std::fprintf(stderr,
|
|
97
|
+
"soundor-vst3-host: %s\n"
|
|
98
|
+
"usage: soundor-vst3-host [--ui] [--max-payload <bytes>] | --version\n"
|
|
99
|
+
"It speaks a framed protocol on stdin/stdout; launch it with @soundor/vst3-host's launchHost().\n",
|
|
100
|
+
problem);
|
|
101
|
+
return 64;
|
|
102
|
+
}
|
|
103
|
+
} // namespace
|
|
104
|
+
|
|
105
|
+
int main(int argc, char** argv)
|
|
106
|
+
{
|
|
107
|
+
bool ui = false;
|
|
108
|
+
unsigned long long maxPayload = defaultMaxPayload;
|
|
109
|
+
for (int i = 1; i < argc; ++i)
|
|
110
|
+
{
|
|
111
|
+
const std::string arg = argv[i];
|
|
112
|
+
if (arg == "--version")
|
|
113
|
+
return printVersion();
|
|
114
|
+
if (arg == "--ui")
|
|
115
|
+
ui = true;
|
|
116
|
+
else if (arg == "--headless")
|
|
117
|
+
ui = false;
|
|
118
|
+
else if (arg == "--max-payload" && i + 1 < argc)
|
|
119
|
+
{
|
|
120
|
+
char* end = nullptr;
|
|
121
|
+
maxPayload = std::strtoull(argv[++i], &end, 10);
|
|
122
|
+
if (end == nullptr || *end != '\0' || maxPayload == 0 || maxPayload > 0xFFFFFFFFULL)
|
|
123
|
+
return usage("--max-payload takes a byte count up to 4294967295");
|
|
124
|
+
}
|
|
125
|
+
else
|
|
126
|
+
return usage(("unknown argument " + arg).c_str());
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
#if defined(__linux__)
|
|
130
|
+
// Never outlive the client, even if it is killed without closing stdin.
|
|
131
|
+
prctl(PR_SET_PDEATHSIG, SIGKILL);
|
|
132
|
+
#endif
|
|
133
|
+
#if defined(_WIN32)
|
|
134
|
+
if (ui)
|
|
135
|
+
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
|
|
136
|
+
// Plugins expect a single-threaded COM apartment on their UI thread.
|
|
137
|
+
OleInitialize(nullptr);
|
|
138
|
+
#endif
|
|
139
|
+
|
|
140
|
+
const Channels channels = takeStdio();
|
|
141
|
+
auto loop = EventLoop::create(ui);
|
|
142
|
+
FrameWriter writer(channels.out);
|
|
143
|
+
auto session = std::make_unique<Session>(*loop, writer, ui);
|
|
144
|
+
session->hello(static_cast<size_t>(maxPayload));
|
|
145
|
+
|
|
146
|
+
// Requests are read on their own thread and handled on the main thread.
|
|
147
|
+
std::thread reader(
|
|
148
|
+
[&loop, &session, in = channels.in, maxPayload]
|
|
149
|
+
{
|
|
150
|
+
FrameReader frames(in, static_cast<size_t>(maxHeader), static_cast<size_t>(maxPayload));
|
|
151
|
+
while (true)
|
|
152
|
+
{
|
|
153
|
+
auto frame = std::make_shared<Frame>();
|
|
154
|
+
std::string error;
|
|
155
|
+
switch (frames.read(*frame, error))
|
|
156
|
+
{
|
|
157
|
+
case FrameReader::Status::Frame:
|
|
158
|
+
loop->post([&session, frame] { session->onFrame(std::move(*frame)); });
|
|
159
|
+
continue;
|
|
160
|
+
case FrameReader::Status::End:
|
|
161
|
+
loop->post([&session] { session->onInputClosed(); });
|
|
162
|
+
return;
|
|
163
|
+
case FrameReader::Status::Error:
|
|
164
|
+
loop->post([&session, error] { session->onProtocolError(error); });
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
reader.detach();
|
|
170
|
+
|
|
171
|
+
loop->run();
|
|
172
|
+
const int code = session->exitCode();
|
|
173
|
+
session.reset();
|
|
174
|
+
std::fflush(stderr);
|
|
175
|
+
// The reader may still be blocked on stdin: leave without running static
|
|
176
|
+
// destructors under it.
|
|
177
|
+
std::_Exit(code);
|
|
178
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// UTF-8 strings to and from std::filesystem paths (the protocol's paths are
|
|
2
|
+
// UTF-8; Windows paths are UTF-16).
|
|
3
|
+
#pragma once
|
|
4
|
+
|
|
5
|
+
#include <filesystem>
|
|
6
|
+
#include <string>
|
|
7
|
+
|
|
8
|
+
namespace soundor::vst3host
|
|
9
|
+
{
|
|
10
|
+
inline std::filesystem::path toPath(const std::string& utf8)
|
|
11
|
+
{
|
|
12
|
+
return { std::u8string(utf8.begin(), utf8.end()) };
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
inline std::string fromPath(const std::filesystem::path& path)
|
|
16
|
+
{
|
|
17
|
+
const std::u8string utf8 = path.u8string();
|
|
18
|
+
return { utf8.begin(), utf8.end() };
|
|
19
|
+
}
|
|
20
|
+
} // namespace soundor::vst3host
|