forked from libpd/libpd
-
Notifications
You must be signed in to change notification settings - Fork 1
/
LibPDNativeMessaging.cs
465 lines (373 loc) · 15 KB
/
LibPDNativeMessaging.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
/*
*
* For information on usage and redistribution, and for a DISCLAIMER OF ALL
* WARRANTIES, see the file, "LICENSE.txt," in this distribution.
*
*
* Created by SharpDevelop.
* User: Tebjan Halm
* Date: 11.04.2012
* Time: 07:01
*
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Threading;
using System.Runtime.CompilerServices;
namespace LibPDBinding
{
#region delegates
public delegate void LibPDPrint(string text);
public delegate void LibPDBang(string recv);
public delegate void LibPDFloat(string recv, float x);
public delegate void LibPDSymbol(string recv, string sym);
public delegate void LibPDList(string recv, object[] args);
public delegate void LibPDMessage(string recv, string msg, object[] args);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate void LibPDPrintHook([In] [MarshalAs(UnmanagedType.LPStr)] string text);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate void LibPDBangHook([In] [MarshalAs(UnmanagedType.LPStr)] string recv);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate void LibPDFloatHook([In] [MarshalAs(UnmanagedType.LPStr)] string recv, float x);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate void LibPDSymbolHook([In] [MarshalAs(UnmanagedType.LPStr)] string recv, [In] [MarshalAs(UnmanagedType.LPStr)] string sym);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate void LibPDListHook([In] [MarshalAs(UnmanagedType.LPStr)] string recv, int argc, IntPtr argv);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate void LibPDMessageHook([In] [MarshalAs(UnmanagedType.LPStr)] string recv, [In] [MarshalAs(UnmanagedType.LPStr)] string msg, int argc, IntPtr argv);
#endregion delegates
//the receiver part of libpd
public static partial class LibPD
{
#region Events
//private delegate field, holds the function pointer
private static LibPDPrintHook PrintHook;
private static LibPDBangHook BangHook;
private static LibPDFloatHook FloatHook;
private static LibPDSymbolHook SymbolHook;
private static LibPDListHook ListHook;
private static LibPDMessageHook MessageHook;
//import hook set method
[DllImport("libpdcsharp.dll", EntryPoint="libpd_set_printhook")]
private static extern void set_printhook(LibPDPrintHook hook);
[DllImport("libpdcsharp.dll", EntryPoint="libpd_set_banghook")]
private static extern void set_banghook(LibPDBangHook hook) ;
[DllImport("libpdcsharp.dll", EntryPoint="libpd_set_floathook")]
private static extern void set_floathook(LibPDFloatHook hook) ;
[DllImport("libpdcsharp.dll", EntryPoint="libpd_set_symbolhook")]
private static extern void set_symbolhook(LibPDSymbolHook hook) ;
[DllImport("libpdcsharp.dll", EntryPoint="libpd_set_listhook")]
private static extern void set_listhook(LibPDListHook hook) ;
[DllImport("libpdcsharp.dll", EntryPoint="libpd_set_messagehook")]
private static extern void set_messagehook(LibPDMessageHook hook) ;
private static void SetupHooks()
{
//create the delegate with the method to call
PrintHook = new LibPDPrintHook(RaisePrintEvent);
set_printhook(PrintHook);
BangHook = new LibPDBangHook(RaiseBangEvent);
set_banghook(BangHook);
FloatHook = new LibPDFloatHook(RaiseFloatEvent);
set_floathook(FloatHook);
SymbolHook = new LibPDSymbolHook(RaiseSymbolEvent);
set_symbolhook(SymbolHook);
ListHook = new LibPDListHook(RaiseListEvent);
set_listhook(ListHook);
MessageHook = new LibPDMessageHook(RaiseMessageEvent);
set_messagehook(MessageHook);
}
/// <summary>
/// Subscribe to this event in order to get PDs print messages.
/// Note: Events may be raised by several threads, such as the GUI thread and
/// the audio thread. If a subscriber method calls operations that must be executed
/// in a particular thread, then the subscriber method is responsible for posting
/// those calls to the appropriate synchronization context.
/// </summary>
public static event LibPDPrint Print = delegate{};
/// <summary>
/// Subscribe to this event in order to get PDs bang messages.
/// Note: Events may be raised by several threads, such as the GUI thread and
/// the audio thread. If a subscriber method calls operations that must be executed
/// in a particular thread, then the subscriber method is responsible for posting
/// those calls to the appropriate synchronization context.
/// </summary>
public static event LibPDBang Bang = delegate{};
/// <summary>
/// Subscribe to this event in order to get PDs float messages.
/// Note: Events may be raised by several threads, such as the GUI thread and
/// the audio thread. If a subscriber method calls operations that must be executed
/// in a particular thread, then the subscriber method is responsible for posting
/// those calls to the appropriate synchronization context.
/// </summary>
public static event LibPDFloat Float = delegate{};
/// <summary>
/// Subscribe to this event in order to get PDs symbol messages.
/// Note: Events may be raised by several threads, such as the GUI thread and
/// the audio thread. If a subscriber method calls operations that must be executed
/// in a particular thread, then the subscriber method is responsible for posting
/// those calls to the appropriate synchronization context.
/// </summary>
public static event LibPDSymbol Symbol = delegate{};
/// <summary>
/// Subscribe to this event in order to get PDs list messages. Currently only
/// float and symbol types are supported. Other types in the list such as pointers will be null.
/// Note: Events may be raised by several threads, such as the GUI thread and
/// the audio thread. If a subscriber method calls operations that must be executed
/// in a particular thread, then the subscriber method is responsible for posting
/// those calls to the appropriate synchronization context.
/// </summary>
public static event LibPDList List = delegate{};
/// <summary>
/// Subscribe to this event in order to get PDs typed messages. Currently only
/// float and symbol types are supported. Other types in the list such as pointers will be null.
/// Note: Events may be raised by several threads, such as the GUI thread and
/// the audio thread. If a subscriber method calls operations that must be executed
/// in a particular thread, then the subscriber method is responsible for posting
/// those calls to the appropriate synchronization context.
/// </summary>
public static event LibPDMessage Message = delegate{};
private static void RaisePrintEvent(string e)
{
Print(e);
}
private static void RaiseBangEvent(string recv)
{
Bang(recv);
}
private static void RaiseFloatEvent(string recv, float e)
{
Float(recv, e);
}
private static void RaiseSymbolEvent(string recv, string e)
{
Symbol(recv, e);
}
[DllImport("libpdcsharp.dll", EntryPoint="libpd_atom_is_float")]
private static extern int atom_is_float(IntPtr a) ;
[DllImport("libpdcsharp.dll", EntryPoint="libpd_atom_is_symbol")]
private static extern int atom_is_symbol(IntPtr a) ;
[DllImport("libpdcsharp.dll", EntryPoint="libpd_atom_get_float")]
private static extern float atom_get_float(IntPtr a) ;
[DllImport("libpdcsharp.dll", EntryPoint="libpd_atom_get_symbol")]
private static extern IntPtr atom_get_symbol(IntPtr a);
[DllImport("libpdcsharp.dll", EntryPoint="libpd_next_atom")]
private static extern IntPtr next_atom(IntPtr a);
private static void RaiseListEvent(string recv, int argc, IntPtr argv)
{
var args = new object[argc];
ConvertList(args, argc, argv);
List(recv, args);
}
private static void RaiseMessageEvent(string recv, string msg, int argc, IntPtr argv)
{
var args = new object[argc];
ConvertList(args, argc, argv);
Message(recv, msg, args);
}
private static void ConvertList(object[] args, int argc, IntPtr argv)
{
for (int i = 0; i < argc; i++)
{
if(i!=0) argv = next_atom(argv);
if(atom_is_float(argv) != 0)
{
args[i] = atom_get_float(argv);
}
else if(atom_is_symbol(argv) != 0)
{
args[i] = Marshal.PtrToStringAnsi(atom_get_symbol(argv));
}
}
}
#endregion Events
#region Message sending
/// <summary>
/// If set to true each sent message will be written to Debug output
/// </summary>
public static bool WriteMessageToDebug
{
[MethodImpl(MethodImplOptions.Synchronized)]
get
{
return SWriteMessageToDebug;
}
[MethodImpl(MethodImplOptions.Synchronized)]
set
{
SWriteMessageToDebug = value;
}
}
private static bool SWriteMessageToDebug;
//binding-----------------------------------
//store bindings
private static Dictionary<string, IntPtr> Bindings = new Dictionary<string, IntPtr>();
[DllImport("libpdcsharp.dll", EntryPoint="libpd_bind")]
private static extern IntPtr bind([In] [MarshalAs(UnmanagedType.LPStr)] string sym) ;
/// <summary>
/// subscribes to pd messages sent to the given symbol
/// </summary>
/// <param name="symbol"> </param>
/// <returns> true on success </returns>
[MethodImpl(MethodImplOptions.Synchronized)]
public static bool Subscribe(string sym)
{
if(String.IsNullOrEmpty(sym)) return false;
if(Bindings.ContainsKey(sym)) return true;
var ptr = bind(sym);
if(ptr == IntPtr.Zero) return false;
Bindings[sym] = ptr;
return true;
}
[DllImport("libpdcsharp.dll", EntryPoint="libpd_unbind")]
private static extern void unbind(IntPtr p) ;
/// <summary>
/// unsubscribes from pd messages sent to the given symbol; will do nothing
/// if there is no subscription to this symbol
/// </summary>
/// <param name="sym"> </param>
/// <returns>true if unsubscribed</returns>
[MethodImpl(MethodImplOptions.Synchronized)]
public static bool Unsubscribe(string sym)
{
if(String.IsNullOrEmpty(sym) || !Bindings.ContainsKey(sym)) return false;
unbind(Bindings[sym]);
return Bindings.Remove(sym);
}
//sending-----------------------------------------------------------
[DllImport("libpdcsharp.dll", EntryPoint="libpd_bang")]
private static extern int send_bang([In] [MarshalAs(UnmanagedType.LPStr)] string recv) ;
/// <summary>
/// sends a bang to the object associated with the given symbol
/// </summary>
/// <param name="recv">
/// symbol associated with receiver </param>
/// <returns> error code, 0 on success </returns>
[MethodImpl(MethodImplOptions.Synchronized)]
public static int SendBang(string recv)
{
return send_bang(recv);
}
[DllImport("libpdcsharp.dll", EntryPoint="libpd_float")]
private static extern int send_float([In] [MarshalAs(UnmanagedType.LPStr)] string recv, float x) ;
/// <summary>
/// sends a float to the object associated with the given symbol
/// </summary>
/// <param name="recv">
/// symbol associated with receiver </param>
/// <param name="x"> </param>
/// <returns> error code, 0 on success </returns>
[MethodImpl(MethodImplOptions.Synchronized)]
public static int SendFloat(string recv, float x)
{
return send_float(recv, x);
}
[DllImport("libpdcsharp.dll", EntryPoint="libpd_symbol")]
private static extern int send_symbol([In] [MarshalAs(UnmanagedType.LPStr)] string recv, [In] [MarshalAs(UnmanagedType.LPStr)] string sym) ;
/// <summary>
/// sends a symbol to the object associated with the given symbol
/// </summary>
/// <param name="recv">
/// symbol associated with receiver </param>
/// <param name="sym"> </param>
/// <returns> error code, 0 on success </returns>
[MethodImpl(MethodImplOptions.Synchronized)]
public static int SendSymbol(string recv, string sym)
{
return send_symbol(recv, sym);
}
[DllImport("libpdcsharp.dll", EntryPoint="libpd_start_message")]
private static extern int start_message(int max_length) ;
[DllImport("libpdcsharp.dll", EntryPoint="libpd_finish_message")]
private static extern int finish_message([In] [MarshalAs(UnmanagedType.LPStr)] string recv, [In] [MarshalAs(UnmanagedType.LPStr)] string msg) ;
/// <summary>
/// Sends a message to an object in pd
/// </summary>
/// <param name="receiver"> </param>
/// <param name="message"> </param>
/// <param name="args"> list of arguments of type Integer, Float, or String </param>
/// <returns> error code, 0 on success </returns>
[MethodImpl(MethodImplOptions.Synchronized)]
public static int SendMessage(string receiver, string message, params object[] args)
{
var s = "";
int err = ProcessArgs(args, ref s);
var ret = (err == 0) ? finish_message(receiver, message) : err;
if(SWriteMessageToDebug)
{
s = String.Format("Message: {0} {1}", receiver, message) + s;
s += " Start: " + err;
s += " End: " + ret;
Debug.WriteLine(s);
}
return ret;
}
[DllImport("libpdcsharp.dll", EntryPoint="libpd_finish_list")]
private static extern int finish_list([In] [MarshalAs(UnmanagedType.LPStr)] string recv) ;
/// <summary>
/// Sends a list to an object in pd
/// </summary>
/// <param name="receiver"> </param>
/// <param name="args"> list of arguments of type Integer, Float, or String </param>
/// <returns> error code, 0 on success </returns>
[MethodImpl(MethodImplOptions.Synchronized)]
public static int SendList(string receiver, params object[] args)
{
string s = "";
int err = ProcessArgs(args, ref s);
var ret = (err == 0) ? finish_list(receiver) : err;
if(SWriteMessageToDebug)
{
s = String.Format("List: {0}", receiver) + s;
s += " Start: " + err;
s += " End: " + ret;
Debug.WriteLine(s);
}
return ret;
}
[DllImport("libpdcsharp.dll", EntryPoint="libpd_add_float")]
private static extern void add_float(float x) ;
[DllImport("libpdcsharp.dll", EntryPoint="libpd_add_symbol")]
private static extern void add_symbol([In] [MarshalAs(UnmanagedType.LPStr)] string sym) ;
//parse args helper with debug string
private static int ProcessArgs(object[] args, ref string debug)
{
if (start_message(args.Length) != 0)
{
return -100;
}
foreach (object arg in args)
{
if (arg is int?)
{
add_float((int)((int?) arg));
if(SWriteMessageToDebug) debug += " i:" + arg.ToString();
}
else if (arg is float?)
{
add_float((float)((float?) arg));
if(SWriteMessageToDebug) debug += " f:" + arg.ToString();
}
else if (arg is double?)
{
add_float((float)((double?) arg));
if(SWriteMessageToDebug) debug += " d:" + arg.ToString();
}
else if (arg is string)
{
add_symbol((string) arg);
if(SWriteMessageToDebug) debug += " s:" + arg.ToString();
}
else
{
if(SWriteMessageToDebug) debug += " illegal argument: " + arg.ToString();
return -101; // illegal argument
}
}
return 0;
}
#endregion Message sending
}
}