iPlug2 - C++ Audio Plug-in Framework
Loading...
Searching...
No Matches
IPlugOSC_internal.cpp
1#include "IPlugOSC.h"
2
3using namespace iplug;
4
5#ifdef OS_WIN
6#define XSleep Sleep
7#else
8void XSleep(int ms) { usleep(ms?ms*1000:100); }
9#endif
10
11OSCDevice::OSCDevice(const char* dest, int maxpacket, int sendsleep, sockaddr_in* listen_addr)
12{
13 mHasOutput = dest != nullptr;
14 mHasInput = listen_addr != nullptr;
15
16 memset(&mSendAddress, 0, sizeof(mSendAddress));
17 mMaxMacketSize = maxpacket > 0 ? maxpacket : 1024;
18 mSendSleep = sendsleep >= 0 ? sendsleep : 10;
19 mSendSocket = socket(AF_INET, SOCK_DGRAM, 0);
20
21 if (mSendSocket == INVALID_SOCKET)
22 {
23 //TODO:
24 }
25 else if (listen_addr)
26 {
27 mReceiveAddress = *listen_addr;
28 int on = 1;
29 setsockopt(mSendSocket, SOL_SOCKET, SO_BROADCAST, (char*)&on, sizeof(on));
30 if (!bind(mSendSocket, (struct sockaddr*) & mReceiveAddress, sizeof(struct sockaddr)))
31 {
32 SET_SOCK_BLOCK(mSendSocket, false);
33 }
34 else
35 {
36 closesocket(mSendSocket);
37 mSendSocket = INVALID_SOCKET;
38 }
39 }
40 else
41 {
42 mDestination.Set(dest);
43
44 WDL_String tmp(dest);
45 int sendport = 0;
46 char* p = strstr(tmp.Get(), ":");
47 if (p)
48 {
49 *p++ = 0;
50 sendport = atoi(p);
51 }
52 if (!sendport) sendport = 8000;
53
54 mSendAddress.sin_family = AF_INET;
55 mSendAddress.sin_addr.s_addr = inet_addr(tmp.Get());
56 mSendAddress.sin_port = htons(sendport);
57
58 int on = 1;
59 setsockopt(mSendSocket, SOL_SOCKET, SO_BROADCAST, (char*)&on, sizeof(on));
60 SET_SOCK_BLOCK(mSendSocket, false);
61 }
62}
63
64OSCDevice::~OSCDevice()
65{
66 if (mSendSocket != INVALID_SOCKET)
67 {
68 shutdown(mSendSocket, SHUT_RDWR);
69 closesocket(mSendSocket);
70 mSendSocket = INVALID_SOCKET;
71 }
72}
73
74void OSCDevice::RunInput()
75{
76 if (mSendSocket == INVALID_SOCKET)
77 return;
78
79 struct sockaddr* p = mDestination.GetLength() ? nullptr : (struct sockaddr*) & mSendAddress;
80
81 for (;;)
82 {
83 char buf[16384];
84 buf[0] = 0;
85 socklen_t plen = (socklen_t)sizeof(mSendAddress);
86 const int len = (int)recvfrom(mSendSocket, buf, sizeof(buf), 0, p, p ? &plen : nullptr);
87
88 if (len < 1)
89 break;
90
91 OnMessage(1, (const unsigned char*)buf, len);
92 }
93}
94
95void OSCDevice::RunOutput()
96{
97 static char hdr[16] = { '#', 'b', 'u', 'n', 'd', 'l', 'e', 0, 0, 0, 0, 0, 1, 0, 0, 0 };
98
99 // send mSendQueue as UDP blocks
100 if (mSendQueue.Available() <= 16)
101 {
102 if (mSendQueue.Available() > 0) mSendQueue.Clear();
103 return;
104 }
105 // mSendQueue should begin with a 16 byte pad, then messages in OSC
106
107 char* packetstart = (char*)mSendQueue.Get();
108 int packetlen = 16;
109 bool hasbundle = false;
110 mSendQueue.Advance(16); // skip bundle for now, but keep it around
111
112 SET_SOCK_BLOCK(mSendSocket, true);
113
114 while (mSendQueue.Available() >= sizeof(int))
115 {
116 int len = *(int*)mSendQueue.Get(); // not advancing
117 OSC_MAKEINTMEM4BE((char*)&len);
118
119 if (len < 1 || len > MAX_OSC_MSG_LEN || len > mSendQueue.Available()) break;
120
121 if (packetlen > 16 && packetlen + sizeof(int) + len > mMaxMacketSize)
122 {
123 // packet is full
124 if (!hasbundle)
125 {
126 packetstart += 20;
127 packetlen -= 20;
128 }
129 else
130 {
131 memcpy(packetstart, hdr, 16);
132 }
133
134 sendto(mSendSocket, packetstart, packetlen, 0, (struct sockaddr*) & mSendAddress, sizeof(mSendAddress));
135 if (mSendSleep > 0)
136 XSleep(mSendSleep);
137
138 packetstart = (char*)mSendQueue.Get() - 16; // safe since we padded the queue start
139 packetlen = 16;
140 hasbundle = false;
141 }
142
143 if (packetlen > 16) hasbundle = true;
144 mSendQueue.Advance(sizeof(int) + len);
145 packetlen += sizeof(int) + len;
146 }
147
148 if (packetlen > 16)
149 {
150 if (!hasbundle)
151 {
152 packetstart += 20;
153 packetlen -= 20;
154 }
155 else
156 {
157 memcpy(packetstart, hdr, 16);
158 }
159 sendto(mSendSocket, packetstart, packetlen, 0, (struct sockaddr*) & mSendAddress, sizeof(mSendAddress));
160 if (mSendSleep > 0)
161 XSleep(mSendSleep);
162 }
163 SET_SOCK_BLOCK(mSendSocket, false);
164
165 mSendQueue.Clear();
166}
167
168void OSCDevice::AddInstance(void(*callback)(void* d1, int dev_idx, int msglen, void* msg), void* d1, int dev_idx)
169{
170 const rec r = { callback, d1, dev_idx };
171 mInstances.Add(r);
172}
173
174void OSCDevice::OnMessage(char type, const unsigned char* msg, int len)
175{
176 const int n = mInstances.GetSize();
177 const rec* r = mInstances.Get();
178 for (int x = 0; x < n; x++)
179 if (r[x].callback) r[x].callback(r[x].data1, r[x].dev_idx, len, (void*)msg);
180}
181
182void OSCDevice::SendOSC(const char* src, int len)
183{
184 if (!mSendQueue.GetSize())
185 mSendQueue.Add(nullptr, 16);
186
187 int tlen = len;
188 OSC_MAKEINTMEM4BE(&tlen);
189 mSendQueue.Add(&tlen, sizeof(tlen));
190 mSendQueue.Add(src, len);
191}
192
193//static
194void OSCInterface::MessageCallback(void* d1, int dev_idx, int len, void* msg)
195{
196 OSCInterface* _this = (OSCInterface*)d1;
197
198 if (_this && msg)
199 {
200 if (_this->mIncomingEvents.GetSize() < 65536 * 8)
201 {
202 const int this_sz = ((sizeof(incomingEvent) + (len - 3)) + 7) & ~7;
203
204 _this->mIncomingEvents_mutex.Enter();
205 const int oldsz = _this->mIncomingEvents.GetSize();
206 _this->mIncomingEvents.Resize(oldsz + this_sz, false);
207
208 if (_this->mIncomingEvents.GetSize() == oldsz + this_sz)
209 {
210 incomingEvent* item = (incomingEvent*)((char*)_this->mIncomingEvents.Get() + oldsz);
211 item->dev_ptr = _this->mDevices.Get(dev_idx);
212 item->sz = len;
213 memcpy(item->msg, msg, len);
214 }
215 _this->mIncomingEvents_mutex.Leave();
216 }
217 }
218}
219
220void OSCInterface::OnTimer(Timer& timer)
221{
222 // Process OUR devices only (instance-owned, not global)
223 const int nDevices = mDevices.GetSize();
224
225 for (auto i = 0; i < nDevices; i++)
226 {
227 auto* pDev = mDevices.Get(i);
228 if (pDev->mHasInput)
229 pDev->RunInput();
230 }
231
232 if (mIncomingEvents.GetSize())
233 {
234 WDL_HeapBuf tmp; // local, not static (thread-safe)
235
236 mIncomingEvents_mutex.Enter();
237 tmp.CopyFrom(&mIncomingEvents, false);
238 mIncomingEvents.Resize(0, false);
239 mIncomingEvents_mutex.Leave();
240
241 int pos = 0;
242 const int endpos = tmp.GetSize();
243 while (pos < endpos + 1 - sizeof(incomingEvent))
244 {
245 incomingEvent* evt = (incomingEvent*)((char*)tmp.Get() + pos);
246
247 const int this_sz = ((sizeof(incomingEvent) + (evt->sz - 3)) + 7) & ~7;
248
249 if (pos + this_sz > endpos) break;
250 pos += this_sz;
251
252 int rd_pos = 0;
253 int rd_sz = evt->sz;
254 if (evt->sz > 20 && !strcmp((char*)evt->msg, "#bundle"))
255 {
256 rd_sz = *(int*)(evt->msg + 16);
257 OSC_MAKEINTMEM4BE(&rd_sz);
258 rd_pos += 20;
259 }
260
261 while (rd_pos + rd_sz <= evt->sz && rd_sz >= 0)
262 {
263 OscMessageRead rmsg((char*)evt->msg + rd_pos, rd_sz);
264
265 const char* mstr = rmsg.GetMessage();
266 if (mstr && *mstr)
267 OnOSCMessage(rmsg);
268
269 rd_pos += rd_sz + 4;
270 if (rd_pos >= evt->sz) break;
271
272 rd_sz = *(int*)(evt->msg + rd_pos - 4);
273 OSC_MAKEINTMEM4BE(&rd_sz);
274 }
275 }
276 }
277
278 for (auto i = 0; i < nDevices; i++)
279 {
280 auto* pDev = mDevices.Get(i);
281 if (pDev->mHasOutput)
282 pDev->RunOutput(); // send queued messages
283 }
284}
285
286OSCInterface::OSCInterface(OSCLogFunc logFunc)
287: mLogFunc(logFunc)
288{
289 JNL::open_socketlib();
290
291 // Each instance gets its own timer (not shared)
292 mTimer = std::unique_ptr<Timer>(Timer::Create(std::bind(&OSCInterface::OnTimer, this, std::placeholders::_1), OSC_TIMER_RATE));
293}
294
295OSCInterface::~OSCInterface()
296{
297 // Stop timer first to prevent callbacks during destruction.
298 // Timer::Stop() synchronously invalidates the timer on all platforms,
299 // and both the timer callback and destructor run on the main thread,
300 // so there's no race condition.
301 mTimer = nullptr;
302
303 // Clean up our own devices (instance-owned)
304 mDevices.Empty(true);
305}
306
308{
309 if (device)
310 {
311 int idx = mDevices.Find(device);
312 if (idx >= 0)
313 mDevices.Delete(idx, true); // true = free the device
314 }
315}
316
317OSCDevice* OSCInterface::CreateReceiver(WDL_String& log, int port)
318{
319 const char buf[] = "127.0.0.1";
320
321 struct sockaddr_in addr;
322 addr.sin_addr.s_addr = INADDR_ANY;
323 addr.sin_family = AF_INET;
324 if (buf[0] && buf[0] != '*') addr.sin_addr.s_addr = inet_addr(buf);
325 if (addr.sin_addr.s_addr == INADDR_NONE) addr.sin_addr.s_addr = INADDR_ANY;
326 addr.sin_port = htons(port);
327
328 // Search OUR devices for reuse (instance-owned, not global)
329 bool isReuse = false;
330 OSCDevice* r = nullptr;
331 for (int x = 0; x < mDevices.GetSize(); x++)
332 {
333 OSCDevice* dev = mDevices.Get(x);
334 if (dev && dev->mHasInput)
335 {
336 if (dev->mReceiveAddress.sin_port == addr.sin_port && dev->mReceiveAddress.sin_addr.s_addr == addr.sin_addr.s_addr)
337 {
338 r = dev;
339 isReuse = true;
340
341 log.AppendFormatted(1024, "Attached to already-opened listener '%s:%i'\r\n", buf, port);
342
343 break;
344 }
345 }
346 }
347
348 if (!r)
349 {
350 std::unique_ptr<OSCDevice> device(new OSCDevice(nullptr, 0, -1, &addr));
351
352 if (device->mSendSocket == INVALID_SOCKET)
353 {
354 log.AppendFormatted(1024, "Error listening for '%s:%i'\r\n", buf, port);
355 }
356 else
357 {
358 r = device.release();
359 log.AppendFormatted(1024, "Listening for OSC on '%s:%i'\r\n", buf, port);
360 }
361 }
362
363 if (r)
364 {
365 r->AddInstance(MessageCallback, this, mDevices.GetSize());
366
367 if (!isReuse)
368 mDevices.Add(r); // Add to OUR device list only
369 }
370
371 return r;
372}
373
374OSCDevice* OSCInterface::CreateSender(WDL_String& log, const char* ip, int port)
375{
376 WDL_String destStr;
377 destStr.SetFormatted(256, "%s:%i", ip, port);
378
379 // Search OUR devices for reuse (instance-owned, not global)
380 OSCDevice* r = nullptr;
381 bool isReuse = false;
382 for (auto x = 0; x < mDevices.GetSize(); x++)
383 {
384 OSCDevice* d = mDevices.Get(x);
385 if (d && d->mHasOutput)
386 {
387 if (!strcmp(d->mDestination.Get(), destStr.Get()))
388 {
389 isReuse = true;
390 r = d; // reuse!
391 break;
392 }
393 }
394 }
395
396 if (!r)
397 {
398 isReuse = false;
399 std::unique_ptr<OSCDevice> device(new OSCDevice(destStr.Get(), 0, -1, nullptr));
400 if (device->mSendSocket == INVALID_SOCKET)
401 {
402 log.AppendFormatted(1024, "Warning: failed creating destination for output '%s'\n", destStr.Get());
403 }
404 else
405 {
406 r = device.release();
407 }
408 }
409
410 if (r)
411 {
412 log.AppendFormatted(1024, "Set destination: '%s'\n", destStr.Get());
413
414 r->AddInstance(MessageCallback, this, mDevices.GetSize());
415
416 if (!isReuse)
417 mDevices.Add(r); // Add to OUR device list only
418 }
419
420 return r;
421}
void RemoveDevice(OSCDevice *device)
Remove a device from the device list and delete it.
Base class for timer.
Definition: IPlugTimer.h:40