iPlug2 - C++ Audio Plug-in Framework
Loading...
Searching...
No Matches
IPlugWasmUI.cpp
1/*
2 ==============================================================================
3
4 This file is part of the iPlug 2 library. Copyright (C) the iPlug 2 developers.
5
6 See LICENSE.txt for more info.
7
8 ==============================================================================
9*/
10
11#include "IPlugWasmUI.h"
12
13#include <memory>
14
15#include <emscripten.h>
16#include <emscripten/bind.h>
17
18using namespace iplug;
19using namespace emscripten;
20
21IPlugWasmUI::IPlugWasmUI(const InstanceInfo& info, const Config& config)
22: IPlugAPIBase(config, kAPIWEB)
23{
24 mControllerName.SetFormatted(32, "%s_Hybrid", GetPluginName());
25}
26
27void IPlugWasmUI::SendParameterValueFromUI(int paramIdx, double value)
28{
29 // Send parameter value to DSP via controller
30 EM_ASM({
31 if (typeof window.iPlugController !== 'undefined') {
32 window.iPlugController.setParam($0, $1);
33 } else {
34 console.warn('IPlugWasmUI: controller not ready');
35 }
36 }, paramIdx, value);
37
38 // Call super class to trigger OnParamChangeUI()
39 IPlugAPIBase::SendParameterValueFromUI(paramIdx, value);
40}
41
42void IPlugWasmUI::SendMidiMsgFromUI(const IMidiMsg& msg)
43{
44 EM_ASM({
45 if (typeof window.iPlugController !== 'undefined') {
46 window.iPlugController.sendMidi($0, $1, $2);
47 } else {
48 console.warn('IPlugWasmUI: controller not ready');
49 }
50 }, msg.mStatus, msg.mData1, msg.mData2);
51}
52
53void IPlugWasmUI::SendSysexMsgFromUI(const ISysEx& msg)
54{
55 EM_ASM({
56 if (typeof window.iPlugController !== 'undefined') {
57 var data = new Uint8Array($1);
58 data.set(HEAPU8.subarray($0, $0 + $1));
59 window.iPlugController.sendSysex(data);
60 } else {
61 console.warn('IPlugWasmUI: controller not ready');
62 }
63 }, reinterpret_cast<intptr_t>(msg.mData), msg.mSize);
64}
65
66void IPlugWasmUI::SendArbitraryMsgFromUI(int msgTag, int ctrlTag, int dataSize, const void* pData)
67{
68 if (dataSize > 0 && pData)
69 {
70 EM_ASM({
71 if (typeof window.iPlugController !== 'undefined') {
72 var data = new Uint8Array($2);
73 data.set(HEAPU8.subarray($3, $3 + $2));
74 window.iPlugController.sendArbitraryMsg($0, $1, data);
75 } else {
76 console.warn('IPlugWasmUI: controller not ready');
77 }
78 }, msgTag, ctrlTag, dataSize, reinterpret_cast<intptr_t>(pData));
79 }
80 else
81 {
82 EM_ASM({
83 if (typeof window.iPlugController !== 'undefined') {
84 window.iPlugController.sendArbitraryMsg($0, $1, null);
85 }
86 }, msgTag, ctrlTag);
87 }
88}
89
90void IPlugWasmUI::SendDSPIdleTick()
91{
92 EM_ASM({
93 if (typeof window.iPlugController !== 'undefined') {
94 window.iPlugController.sendIdleTick();
95 }
96 });
97}
98
99// Global instance for Emscripten bindings
100extern std::unique_ptr<iplug::IPlugWasmUI> gPlug;
101
102// Parent-window dims that arrived before `gPlug` was constructed.
103// `iplug_fsready()` runs asynchronously after IDBFS syncs — so on the
104// first page load the JS bundle's initial ResizeObserver callback can
105// fire before `gPlug` exists. Without buffering, that resize is lost
106// and the canvas stays pinned at the plugin's default dimensions until
107// the parent element is resized again. Replayed by
108// `IPlugWasmUI_ApplyPendingParentWindowResize()`.
109static int gPendingParentWindowW = 0;
110static int gPendingParentWindowH = 0;
111
112extern "C" EMSCRIPTEN_KEEPALIVE void IPlugWasmUI_ApplyPendingParentWindowResize()
113{
114 if (gPlug && gPendingParentWindowW > 0 && gPendingParentWindowH > 0)
115 {
116 gPlug->OnParentWindowResize(gPendingParentWindowW, gPendingParentWindowH);
117 gPendingParentWindowW = 0;
118 gPendingParentWindowH = 0;
119 }
120}
121
122// Callback functions called by JavaScript controller when DSP sends messages
123static void _SendParameterValueFromDelegate(int paramIdx, double normalizedValue)
124{
125 gPlug->SendParameterValueFromDelegate(paramIdx, normalizedValue, true);
126}
127
128static void _SendControlValueFromDelegate(int ctrlTag, double normalizedValue)
129{
130 gPlug->SendControlValueFromDelegate(ctrlTag, normalizedValue);
131}
132
133static void _SendControlMsgFromDelegate(int ctrlTag, int msgTag, int dataSize, uintptr_t pData)
134{
135 const uint8_t* pDataPtr = reinterpret_cast<uint8_t*>(pData);
136 gPlug->SendControlMsgFromDelegate(ctrlTag, msgTag, dataSize, pDataPtr);
137}
138
139static void _SendArbitraryMsgFromDelegate(int msgTag, int dataSize, uintptr_t pData)
140{
141 const uint8_t* pDataPtr = reinterpret_cast<uint8_t*>(pData);
142 gPlug->SendArbitraryMsgFromDelegate(msgTag, dataSize, pDataPtr);
143}
144
145static void _SendMidiMsgFromDelegate(int status, int data1, int data2)
146{
147 IMidiMsg msg{0, (uint8_t)status, (uint8_t)data1, (uint8_t)data2};
148 gPlug->SendMidiMsgFromDelegate(msg);
149}
150
151static void _SendSysexMsgFromDelegate(int dataSize, uintptr_t pData)
152{
153 const uint8_t* pDataPtr = reinterpret_cast<uint8_t*>(pData);
154 ISysEx msg(0, pDataPtr, dataSize);
155 gPlug->SendSysexMsgFromDelegate(msg);
156}
157
158static void _StartIdleTimer()
159{
160 gPlug->CreateTimer();
161}
162
163static void _OnParentWindowResize(int width, int height)
164{
165 if (gPlug)
166 {
167 gPlug->OnParentWindowResize(width, height);
168 }
169 else
170 {
171 // gPlug not constructed yet — stash the dims so
172 // `iplug_fsready()` can replay them once the UI is open.
173 gPendingParentWindowW = width;
174 gPendingParentWindowH = height;
175 }
176}
177
178EMSCRIPTEN_BINDINGS(IPlugWasmUI) {
179 function("SPVFD", &_SendParameterValueFromDelegate);
180 function("SCVFD", &_SendControlValueFromDelegate);
181 function("SCMFD", &_SendControlMsgFromDelegate);
182 function("SAMFD", &_SendArbitraryMsgFromDelegate);
183 function("SMMFD", &_SendMidiMsgFromDelegate);
184 function("SSMFD", &_SendSysexMsgFromDelegate);
185 function("StartIdleTimer", &_StartIdleTimer);
186 function("OnParentWindowResize", &_OnParentWindowResize);
187}
The base class of an IPlug plug-in, which interacts with the different plug-in APIs.
Definition: IPlugAPIBase.h:43
Hybrid UI class - IGraphics side for split DSP/UI builds.
Definition: IPlugWasmUI.h:32
Encapsulates a MIDI message and provides helper functions.
Definition: IPlugMidi.h:31
A struct for dealing with SysEx messages.
Definition: IPlugMidi.h:539