iPlug2 - C++ Audio Plug-in Framework
IPlugWeb.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 "IPlugWeb.h"
12 
13 #include <memory>
14 
15 #include <emscripten.h>
16 #include <emscripten/bind.h>
17 
18 using namespace iplug;
19 using namespace emscripten;
20 
21 static const int kNumMsgHeaderBytes = 6;
22 static const int kNumSPVFUIBytes = 18;
23 static const int kNumSMMFUIBytes = 9;
24 static const int kNumSSMFUIBytes = 10; // + data size
25 static const int kNumSAMFUIBytes = 18; // + data size
26 
27 IPlugWeb::IPlugWeb(const InstanceInfo& info, const Config& config)
28 : IPlugAPIBase(config, kAPIWEB)
29 {
30  mSPVFUIBuf.Resize(kNumSPVFUIBytes); memcpy(mSPVFUIBuf.GetData(), "SPVFUI", kNumMsgHeaderBytes);
31  mSMMFUIBuf.Resize(kNumSMMFUIBytes); memcpy(mSMMFUIBuf.GetData(), "SMMFUI", kNumMsgHeaderBytes);
32  mSSMFUIBuf.Resize(kNumSSMFUIBytes); memcpy(mSSMFUIBuf.GetData(), "SSMFUI", kNumMsgHeaderBytes);
33  mSAMFUIBuf.Resize(kNumSAMFUIBytes); memcpy(mSAMFUIBuf.GetData(), "SAMFUI", kNumMsgHeaderBytes);
34 
35  mWAMCtrlrJSObjectName.SetFormatted(32, "%s_WAM", GetPluginName());
36 }
37 
38 void IPlugWeb::SendParameterValueFromUI(int paramIdx, double value)
39 {
40 #if WEBSOCKET_CLIENT
41  int pos = kNumMsgHeaderBytes;
42  *((int*)(mSPVFUIBuf.GetData() + pos)) = paramIdx; pos += sizeof(int);
43  *((double*)(mSPVFUIBuf.GetData() + pos)) = value; pos += sizeof(double);
44 
45  EM_ASM({
46  var jsbuff = Module.HEAPU8.subarray($0, $0 + $1);
47  ws.send(jsbuff);
48  }, (int) mSPVFUIBuf.GetData(), kNumSPVFUIBytes);
49 
50 #else
51  val::global(mWAMCtrlrJSObjectName.Get()).call<void>("setParam", paramIdx, value);
52 #endif
53  IPlugAPIBase::SendParameterValueFromUI(paramIdx, value); // call super class in order to make sure OnParamChangeUI() gets triggered
54 };
55 
56 void IPlugWeb::SendMidiMsgFromUI(const IMidiMsg& msg)
57 {
58 #if WEBSOCKET_CLIENT
59  int pos = kNumMsgHeaderBytes;
60  mSMMFUIBuf.GetData()[pos] = msg.mStatus; pos++;
61  mSMMFUIBuf.GetData()[pos] = msg.mData1; pos++;
62  mSMMFUIBuf.GetData()[pos] = msg.mData2; pos++;
63 
64  EM_ASM({
65  var jsbuff = Module.HEAPU8.subarray($0, $0 + $1);
66  ws.send(jsbuff);
67  }, (int) mSMMFUIBuf.GetData(), kNumSMMFUIBytes);
68 
69 #else
70  WDL_String dataStr;
71  dataStr.SetFormatted(16, "%i:%i:%i", msg.mStatus, msg.mData1, msg.mData2);
72  val::global(mWAMCtrlrJSObjectName.Get()).call<void>("sendMessage", std::string("SMMFUI"), std::string(dataStr.Get()));
73 #endif
74 }
75 
76 void IPlugWeb::SendSysexMsgFromUI(const ISysEx& msg)
77 {
78  DBGMSG("TODO: SendSysexMsgFromUI");
79 
80 // EM_ASM({
81 // window[Module.UTF8ToString($0)]["midiOut"].send(0x90, 0x45, 0x7f);
82 // }, mWAMCtrlrJSObjectName.Get());
83 // val::global(mWAMCtrlrJSObjectName.Get())["midiOut"].call<void>("send", {0x90, 0x45, 0x7f} );
84 
85 // #if WEBSOCKET_CLIENT
86 // mSSMFUIBuf.Resize(kNumSSMFUIBytes + msg.mSize);
87 // int pos = kNumMsgHeaderBytes;
88 //
89 // *((int*)(mSSMFUIBuf.GetData() + pos)) = msg.mSize; pos += sizeof(int);
90 // memcpy(mSSMFUIBuf.GetData() + pos, msg.mData, msg.mSize);
91 //
92 // EM_ASM({
93 // var jsbuff = Module.HEAPU8.subarray($0, $0 + $1);
94 // ws.send(jsbuff);
95 // }, (int) mSSMFUIBuf.GetData(), mSSMFUIBuf.Size());
96 // #else
97 // EM_ASM({
98 // window[Module.UTF8ToString($0)].sendMessage('SSMFUI', $1, Module.HEAPU8.slice($1, $1 + $2).buffer);
99 // }, mWAMCtrlrJSObjectName.Get(), (int) msg.mData, msg.mSize);
100 // #endif
101 }
102 
103 void IPlugWeb::SendArbitraryMsgFromUI(int msgTag, int ctrlTag, int dataSize, const void* pData)
104 {
105  mSAMFUIBuf.Resize(kNumSAMFUIBytes + dataSize);
106  int pos = kNumMsgHeaderBytes;
107 
108  *((int*)(mSAMFUIBuf.GetData() + pos)) = msgTag; pos += sizeof(int);
109  *((int*)(mSAMFUIBuf.GetData() + pos)) = ctrlTag; pos += sizeof(int);
110  *((int*)(mSAMFUIBuf.GetData() + pos)) = dataSize; pos += sizeof(int);
111 
112  memcpy(mSAMFUIBuf.GetData() + pos, pData, dataSize);
113 
114 #if WEBSOCKET_CLIENT
115  EM_ASM({
116  var jsbuff = Module.HEAPU8.subarray($0, $0 + $1);
117  ws.send(jsbuff);
118  }, (int) mSAMFUIBuf.GetData(), mSAMFUIBuf.Size());
119 #else
120  EM_ASM({
121  if(typeof window[Module.UTF8ToString($0)] === 'undefined' ) {
122  console.log("warning - SAMFUI called before controller exists");
123  }
124  else {
125  window[Module.UTF8ToString($0)].sendMessage('SAMFUI', "", Module.HEAPU8.slice($1, $1 + $2).buffer);
126  }
127  }, mWAMCtrlrJSObjectName.Get(), (int) mSAMFUIBuf.GetData() + kNumMsgHeaderBytes, mSAMFUIBuf.Size() - kNumMsgHeaderBytes); // Non websocket doesn't need "SAMFUI" bytes at beginning
128 #endif
129 }
130 
131 void IPlugWeb::SendDSPIdleTick()
132 {
133  EM_ASM({
134  if(typeof window[Module.UTF8ToString($0)] === 'undefined' ) {
135  console.log("warning - SendDSPIdleTick called before controller exists");
136  }
137  else {
138  window[Module.UTF8ToString($0)].sendMessage("TICK", "", 0.);
139  }
140  }, mWAMCtrlrJSObjectName.Get());
141 }
142 
143 extern std::unique_ptr<IPlugWeb> gPlug;
144 
145 // could probably do this without these extra functions
146 // https://kripken.github.io/emscripten-site/docs/porting/connecting_cpp_and_javascript/embind.html#deriving-from-c-classes-in-javascript
147 static void _SendArbitraryMsgFromDelegate(int msgTag, int dataSize, uintptr_t pData)
148 {
149  const uint8_t* pDataPtr = reinterpret_cast<uint8_t*>(pData); // embind doesn't allow us to pass raw pointers
150  gPlug->SendArbitraryMsgFromDelegate(msgTag, dataSize, pDataPtr);
151 }
152 
153 static void _SendControlMsgFromDelegate(int ctrlTag, int msgTag, int dataSize, uintptr_t pData)
154 {
155  const uint8_t* pDataPtr = reinterpret_cast<uint8_t*>(pData); // embind doesn't allow us to pass raw pointers
156  gPlug->SendControlMsgFromDelegate(ctrlTag, msgTag, dataSize, pDataPtr);
157 }
158 
159 static void _SendControlValueFromDelegate(int ctrlTag, double normalizedValue)
160 {
161  gPlug->SendControlValueFromDelegate(ctrlTag, normalizedValue);
162 }
163 
164 static void _SendParameterValueFromDelegate(int paramIdx, double normalizedValue)
165 {
166  gPlug->SendParameterValueFromDelegate(paramIdx, normalizedValue, true);
167 }
168 
169 static void _SendMidiMsgFromDelegate(int status, int data1, int data2)
170 {
171  IMidiMsg msg {0, (uint8_t) status, (uint8_t) data1, (uint8_t) data2};
172  gPlug->SendMidiMsgFromDelegate(msg);
173 }
174 
175 static void _SendSysexMsgFromDelegate(int dataSize, uintptr_t pData)
176 {
177  const uint8_t* pDataPtr = reinterpret_cast<uint8_t*>(pData); // embind doesn't allow us to pass raw pointers
178  ISysEx msg(0, pDataPtr, dataSize);
179  gPlug->SendSysexMsgFromDelegate(msg);
180 }
181 
182 static void _StartIdleTimer()
183 {
184  gPlug->CreateTimer();
185 }
186 
187 EMSCRIPTEN_BINDINGS(IPlugWeb) {
188  function("SPVFD", &_SendParameterValueFromDelegate);
189  function("SAMFD", &_SendArbitraryMsgFromDelegate);
190  function("SCMFD", &_SendControlMsgFromDelegate);
191  function("SCVFD", &_SendControlValueFromDelegate);
192  function("SMMFD", &_SendMidiMsgFromDelegate);
193  function("SSMFD", &_SendSysexMsgFromDelegate);
194  function("StartIdleTimer", &_StartIdleTimer);
195 }
The base class of an IPlug plug-in, which interacts with the different plug-in APIs.
Definition: IPlugAPIBase.h:42
Encapsulates a MIDI message and provides helper functions.
Definition: IPlugMidi.h:30
const char * GetPluginName() const
This is used for the UI "editor" - controller side of a WAM or remote editors that communicate with d...
Definition: IPlugWeb.h:25
A struct for dealing with SysEx messages.
Definition: IPlugMidi.h:538