Mercurial > hg > plosc
comparison c/plosc.c @ 0:bbd2b1abfb32
Initial check in.
author | samer |
---|---|
date | Wed, 11 Jan 2012 15:30:21 +0000 |
parents | |
children | 6c1ba6bf9f96 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:bbd2b1abfb32 |
---|---|
1 /* | |
2 * Copyright (C) 2009 Samer Abdallah | |
3 * | |
4 * This program is free software; you can redistribute it and/or modify | |
5 * it under the terms of the GNU General Public License as published by | |
6 * the Free Software Foundation; either version 2 of the License, or | |
7 * (at your option) any later version. | |
8 * | |
9 * This program is distributed in the hope that it will be useful, | |
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 * GNU General Public License for more details. | |
13 * | |
14 */ | |
15 | |
16 #include <SWI-Stream.h> | |
17 #include <SWI-Prolog.h> | |
18 | |
19 #include <stdio.h> | |
20 #include <string.h> | |
21 #include <math.h> | |
22 #include <lo/lo.h> | |
23 | |
24 // --------------------------------------------------------------------------- | |
25 | |
26 // Reimplementation of lo_server_thread to all calls to | |
27 // Prolog from the server thread. | |
28 | |
29 typedef struct _my_server_thread { | |
30 lo_server s; | |
31 pthread_t thread; | |
32 volatile int active; | |
33 volatile int done; | |
34 } *my_server_thread; | |
35 | |
36 int my_server_thread_start(my_server_thread st); | |
37 int my_server_thread_stop(my_server_thread st); | |
38 int my_server_thread_run(my_server_thread st, int timeout); | |
39 void my_server_thread_free(my_server_thread st); | |
40 my_server_thread my_server_thread_new(const char *port, lo_err_handler err_h); | |
41 | |
42 // --------------------------------------------------------------------------- | |
43 | |
44 // BLOB to hold a lo_address | |
45 static PL_blob_t addr_blob; | |
46 | |
47 // BLOB to hold server thread | |
48 static PL_blob_t server_blob; | |
49 | |
50 static predicate_t call3, call5; | |
51 static atom_t osc_immed; | |
52 static functor_t osc_ts_2; | |
53 static functor_t int_1, float_1, double_1, string_1; | |
54 | |
55 install_t install(); | |
56 | |
57 foreign_t mk_address( term_t host, term_t port, term_t addr); | |
58 foreign_t is_address( term_t addr); | |
59 foreign_t send_osc_now( term_t addr, term_t msg, term_t args); | |
60 foreign_t send_osc_at( term_t addr, term_t msg, term_t args, term_t time); | |
61 foreign_t send_osc_from_at( term_t serv, term_t addr, term_t msg, term_t args, term_t time); | |
62 foreign_t send_timestamped( term_t addr, term_t msg, term_t args, term_t sec, term_t frac); | |
63 foreign_t now( term_t sec, term_t frac); | |
64 foreign_t time_to_ts( term_t time, term_t sec, term_t frac); | |
65 foreign_t time_from_ts( term_t time, term_t sec, term_t frac); | |
66 | |
67 // OSC server predicates | |
68 foreign_t mk_server( term_t port, term_t server); | |
69 foreign_t start_server( term_t server); | |
70 foreign_t stop_server( term_t server); | |
71 foreign_t del_handler( term_t server, term_t msg, term_t types); | |
72 foreign_t add_handler( term_t server, term_t msg, term_t types, term_t handler); | |
73 foreign_t add_handler_x( term_t server, term_t msg, term_t types, term_t handler); | |
74 foreign_t run_server( term_t server); | |
75 | |
76 | |
77 // BLOB functions | |
78 int addr_release(atom_t a) { | |
79 PL_blob_t *type; | |
80 size_t len; | |
81 void *p=PL_blob_data(a,&len,&type); | |
82 if (p) lo_address_free(*(lo_address *)p); | |
83 return TRUE; | |
84 } | |
85 | |
86 int addr_write(IOSTREAM *s, atom_t a, int flags) { | |
87 PL_blob_t *type; | |
88 size_t len; | |
89 lo_address *p=(lo_address *)PL_blob_data(a,&len,&type); | |
90 if (p) { | |
91 const char *host = lo_address_get_hostname(*p); | |
92 const char *port = lo_address_get_port(*p); | |
93 if (host!=NULL && port!=NULL) { | |
94 Sfprintf(s,"osc_address<%s:%s>",host,port); | |
95 } else { | |
96 Sfprintf(s,"osc_address<invalid>"); | |
97 } | |
98 } | |
99 return TRUE; | |
100 } | |
101 | |
102 int server_release(atom_t a) { | |
103 PL_blob_t *type; | |
104 size_t len; | |
105 void *p=PL_blob_data(a,&len,&type); | |
106 if (p) my_server_thread_free(*(my_server_thread *)p); | |
107 return TRUE; | |
108 } | |
109 | |
110 int server_write(IOSTREAM *s, atom_t a, int flags) { | |
111 PL_blob_t *type; | |
112 size_t len; | |
113 my_server_thread *p=(my_server_thread *)PL_blob_data(a,&len,&type); | |
114 if (p) { | |
115 char *url=lo_server_get_url((*p)->s); | |
116 Sfprintf(s,"osc_server<%s>",url); | |
117 free(url); | |
118 } | |
119 return TRUE; | |
120 } | |
121 | |
122 install_t install() { | |
123 PL_register_foreign("osc_now", 2, (void *)now, 0); | |
124 PL_register_foreign("time_to_ts", 3, (void *)time_to_ts, 0); | |
125 PL_register_foreign("time_from_ts", 3, (void *)time_from_ts, 0); | |
126 PL_register_foreign("osc_mk_address", 3, (void *)mk_address, 0); | |
127 PL_register_foreign("osc_is_address", 1, (void *)is_address, 0); | |
128 PL_register_foreign("osc_send_now", 3, (void *)send_osc_now, 0); | |
129 PL_register_foreign("osc_send_at", 4, (void *)send_osc_at, 0); | |
130 PL_register_foreign("osc_send_from_at", 5, (void *)send_osc_from_at, 0); | |
131 PL_register_foreign("osc_mk_server", 2, (void *)mk_server, 0); | |
132 PL_register_foreign("osc_start_server", 1, (void *)start_server, 0); | |
133 PL_register_foreign("osc_stop_server", 1, (void *)stop_server, 0); | |
134 PL_register_foreign("osc_run_server", 1, (void *)run_server, 0); | |
135 PL_register_foreign("osc_del_method", 3, (void *)del_handler, 0); | |
136 PL_register_foreign("osc_add_method", 4, (void *)add_handler, 0); | |
137 PL_register_foreign("osc_add_method_x", 4, (void *)add_handler_x, 0); | |
138 | |
139 addr_blob.magic = PL_BLOB_MAGIC; | |
140 addr_blob.flags = PL_BLOB_UNIQUE; | |
141 addr_blob.name = "osc_address"; | |
142 addr_blob.acquire = 0; | |
143 addr_blob.release = addr_release; | |
144 addr_blob.write = addr_write; | |
145 addr_blob.compare = 0; | |
146 | |
147 server_blob.magic = PL_BLOB_MAGIC; | |
148 server_blob.flags = PL_BLOB_UNIQUE; | |
149 server_blob.name = "osc_server"; | |
150 server_blob.acquire = 0; | |
151 server_blob.release = server_release; | |
152 server_blob.write = server_write; | |
153 server_blob.compare = 0; | |
154 | |
155 call3 = PL_predicate("call",3,"user"); | |
156 call5 = PL_predicate("call",5,"user"); | |
157 osc_immed = PL_new_atom("osc_immed"); | |
158 osc_ts_2 = PL_new_functor(PL_new_atom("osc_ts"),2); | |
159 int_1 = PL_new_functor(PL_new_atom("int"),1); | |
160 float_1 = PL_new_functor(PL_new_atom("float"),1); | |
161 double_1 = PL_new_functor(PL_new_atom("double"),1); | |
162 string_1 = PL_new_functor(PL_new_atom("string"),1); | |
163 } | |
164 | |
165 // throws a Prolog exception to signal type error | |
166 static int type_error(term_t actual, const char *expected) | |
167 { | |
168 term_t ex = PL_new_term_ref(); | |
169 int rc; | |
170 | |
171 rc = PL_unify_term(ex, PL_FUNCTOR_CHARS, "error", 2, | |
172 PL_FUNCTOR_CHARS, "type_error", 2, | |
173 PL_CHARS, expected, | |
174 PL_TERM, actual, | |
175 PL_VARIABLE); | |
176 | |
177 return PL_raise_exception(ex); | |
178 } | |
179 | |
180 static int osc_error(int errno, const char *errmsg, const char *msg) | |
181 { | |
182 term_t ex = PL_new_term_ref(); | |
183 int rc; | |
184 | |
185 rc=PL_unify_term(ex, PL_FUNCTOR_CHARS, "error", 1, | |
186 PL_FUNCTOR_CHARS, "osc_error", 3, | |
187 PL_INTEGER, errno, | |
188 PL_CHARS, errmsg, | |
189 PL_CHARS, msg==NULL ? "none" : msg); | |
190 | |
191 return PL_raise_exception(ex); | |
192 } | |
193 | |
194 static int arg_error(const char *type, term_t arg) | |
195 { | |
196 term_t ex = PL_new_term_ref(); | |
197 int rc; | |
198 | |
199 rc=PL_unify_term(ex, PL_FUNCTOR_CHARS, "error", 1, | |
200 PL_FUNCTOR_CHARS, "arg_error", 2, | |
201 PL_CHARS, type, | |
202 PL_TERM, arg); | |
203 | |
204 return PL_raise_exception(ex); | |
205 } | |
206 | |
207 // put lo_address into a Prolog BLOB | |
208 static int unify_addr(term_t addr,lo_address a) { | |
209 return PL_unify_blob(addr, &a, sizeof(lo_address), &addr_blob); | |
210 } | |
211 | |
212 // get lo_address from BLOB | |
213 static int get_addr(term_t addr, lo_address *a) | |
214 { | |
215 PL_blob_t *type; | |
216 size_t len; | |
217 lo_address *a1; | |
218 | |
219 PL_get_blob(addr, (void **)&a1, &len, &type); | |
220 if (type != &addr_blob) { | |
221 return type_error(addr, "osc_address"); | |
222 } else { | |
223 *a=*a1; | |
224 return TRUE; | |
225 } | |
226 } | |
227 | |
228 // put lo_address into a Prolog BLOB | |
229 static int unify_server(term_t server,my_server_thread s) { | |
230 return PL_unify_blob(server, &s, sizeof(my_server_thread), &server_blob); | |
231 } | |
232 | |
233 // get my_server_thread from BLOB | |
234 static int get_server(term_t server, my_server_thread *a) | |
235 { | |
236 PL_blob_t *type; | |
237 size_t len; | |
238 my_server_thread *a1; | |
239 | |
240 PL_get_blob(server, (void **)&a1, &len, &type); | |
241 if (type != &server_blob) { | |
242 return type_error(server, "osc_server"); | |
243 } else { | |
244 *a=*a1; | |
245 return TRUE; | |
246 } | |
247 } | |
248 | |
249 // get Prolog (Unix) time value and convert to OSC timestamp | |
250 static int get_prolog_time(term_t time, lo_timetag *ts) { | |
251 double t, ft; | |
252 int ok = PL_get_float(time, &t); | |
253 | |
254 ft=floor(t); | |
255 ts->sec = ((uint32_t)ft)+2208988800u; | |
256 ts->frac = (uint32_t)(4294967296.0*(t-ft)); | |
257 return ok; | |
258 } | |
259 | |
260 static int get_timetag(term_t sec, term_t frac, lo_timetag *ts) { | |
261 int64_t s, f; | |
262 int ok = PL_get_int64(sec, &s) && PL_get_int64(frac, &f); | |
263 ts->sec = s; | |
264 ts->frac = f; | |
265 return ok; | |
266 } | |
267 | |
268 | |
269 static int get_msg(term_t msg, char **m) { | |
270 int rc=PL_get_chars(msg, m, CVT_ATOM | CVT_STRING); | |
271 if (rc && strcmp(*m,"any")==0) *m=NULL; | |
272 return rc; | |
273 } | |
274 | |
275 // parse a list of Prolog terms and add arguments to an OSC message | |
276 static int add_msg_args(lo_message msg, term_t list) | |
277 { | |
278 term_t head=PL_new_term_ref(); | |
279 | |
280 // copy term ref so as not to modify original | |
281 list=PL_copy_term_ref(list); | |
282 | |
283 while (PL_get_list(list,head,list)) { | |
284 atom_t name; | |
285 int rc, arity; | |
286 const char *type; | |
287 | |
288 if (!PL_get_name_arity(head,&name,&arity)) return type_error(head,"term"); | |
289 type=PL_atom_chars(name); | |
290 switch (arity) { | |
291 case 1: { | |
292 term_t a1=PL_new_term_ref(); | |
293 rc=PL_get_arg(1,head,a1); // !!!! check return value | |
294 | |
295 if (!strcmp(type,"int")) { | |
296 int x; | |
297 if (!PL_get_integer(a1,&x)) return type_error(a1,"integer"); | |
298 lo_message_add_int32(msg,x); | |
299 } else if (!strcmp(type,"double")) { | |
300 double x; | |
301 if (!PL_get_float(a1,&x)) return type_error(a1,"float"); | |
302 lo_message_add_double(msg,x); | |
303 } else if (!strcmp(type,"string")) { | |
304 char *x; | |
305 if (!PL_get_chars(a1,&x,CVT_ATOM|CVT_STRING)) return type_error(a1,"string"); | |
306 lo_message_add_string(msg,x); | |
307 } else if (!strcmp(type,"symbol")) { | |
308 char *x; | |
309 if (!PL_get_chars(a1,&x,CVT_ATOM)) return type_error(a1,"atom"); | |
310 lo_message_add_symbol(msg,x); | |
311 } else if (!strcmp(type,"float")) { | |
312 double x; | |
313 if (!PL_get_float(a1,&x)) return type_error(a1,"float"); | |
314 lo_message_add_float(msg,(float)x); | |
315 } else { | |
316 return arg_error(type,head); | |
317 } | |
318 | |
319 break; | |
320 } | |
321 case 0: { | |
322 if (!strcmp(type,"true")) lo_message_add_true(msg); | |
323 else if (!strcmp(type,"false")) lo_message_add_false(msg); | |
324 else if (!strcmp(type,"nil")) lo_message_add_nil(msg); | |
325 else if (!strcmp(type,"inf")) lo_message_add_infinitum(msg); | |
326 break; | |
327 } | |
328 } | |
329 } | |
330 if (!PL_get_nil(list)) return type_error(list,"nil"); | |
331 return TRUE; | |
332 } | |
333 | |
334 static int send_msg_timestamped(lo_address a, lo_timetag *ts, char *path, term_t args) | |
335 { | |
336 lo_message msg=lo_message_new(); | |
337 lo_bundle bun=lo_bundle_new(*ts); | |
338 | |
339 if (add_msg_args(msg,args)) { | |
340 int ret; | |
341 | |
342 lo_bundle_add_message(bun,path,msg); | |
343 ret = lo_send_bundle(a,bun); | |
344 lo_message_free(msg); | |
345 lo_bundle_free(bun); | |
346 if (ret==-1) { | |
347 return osc_error(lo_address_errno(a),lo_address_errstr(a),path); | |
348 } else { | |
349 return TRUE; | |
350 } | |
351 } else return FALSE; | |
352 } | |
353 | |
354 static int send_msg_timestamped_from(lo_address a, lo_server s, lo_timetag *ts, char *path, term_t args) | |
355 { | |
356 lo_message msg=lo_message_new(); | |
357 lo_bundle bun=lo_bundle_new(*ts); | |
358 | |
359 if (add_msg_args(msg,args)) { | |
360 int ret; | |
361 | |
362 lo_bundle_add_message(bun,path,msg); | |
363 ret = lo_send_bundle_from(a,s,bun); | |
364 lo_message_free(msg); | |
365 lo_bundle_free(bun); | |
366 if (ret==-1) { | |
367 return osc_error(lo_address_errno(a),lo_address_errstr(a),path); | |
368 } else { | |
369 return TRUE; | |
370 } | |
371 } else return FALSE; | |
372 } | |
373 | |
374 static int send_msg(lo_address a, char *path, term_t args) | |
375 { | |
376 lo_message msg=lo_message_new(); | |
377 | |
378 if (add_msg_args(msg,args)) { | |
379 if (lo_send_message(a,path,msg)==-1) { | |
380 lo_message_free(msg); | |
381 return osc_error(lo_address_errno(a),lo_address_errstr(a),path); | |
382 } else { | |
383 lo_message_free(msg); | |
384 return TRUE; | |
385 } | |
386 } else return FALSE; | |
387 } | |
388 | |
389 foreign_t mk_address(term_t host, term_t port, term_t addr) { | |
390 char *h, *p; | |
391 | |
392 if (PL_get_chars(host, &h, CVT_ATOM | CVT_STRING)) { | |
393 if (PL_get_chars(port, &p, CVT_INTEGER)) { | |
394 lo_address a = lo_address_new(h,p); | |
395 return unify_addr(addr,a); | |
396 } else { | |
397 return type_error(port,"integer"); | |
398 } | |
399 } else { | |
400 return type_error(host,"atom"); | |
401 } | |
402 } | |
403 | |
404 foreign_t now(term_t sec, term_t frac) { | |
405 lo_timetag ts; | |
406 int64_t s, f; | |
407 | |
408 lo_timetag_now(&ts); | |
409 s=ts.sec; f=ts.frac; | |
410 return PL_unify_int64(sec,s) && PL_unify_int64(frac,f); | |
411 } | |
412 | |
413 foreign_t time_to_ts(term_t time, term_t sec, term_t frac) { | |
414 lo_timetag ts; | |
415 | |
416 return get_prolog_time(time,&ts) && | |
417 PL_unify_int64(sec,ts.sec) && | |
418 PL_unify_int64(frac,ts.frac); | |
419 } | |
420 | |
421 foreign_t time_from_ts(term_t time, term_t sec, term_t frac) { | |
422 lo_timetag ts; | |
423 | |
424 return get_timetag(sec,frac,&ts) && | |
425 PL_unify_float(time, (double)(ts.sec-2208988800u) + ts.frac/4294967296.0); | |
426 } | |
427 | |
428 | |
429 | |
430 // set current random state structure to values in Prolog term | |
431 foreign_t is_address(term_t addr) { | |
432 PL_blob_t *type; | |
433 return PL_is_blob(addr,&type) && type==&addr_blob; | |
434 } | |
435 | |
436 foreign_t send_osc_from_at(term_t serv, term_t addr, term_t msg, term_t args, term_t time) { | |
437 my_server_thread s; | |
438 lo_address a; | |
439 lo_timetag ts; | |
440 char *m; | |
441 | |
442 return get_addr(addr,&a) && | |
443 get_server(serv,&s) && | |
444 get_prolog_time(time,&ts) && | |
445 get_msg(msg, &m) && | |
446 send_msg_timestamped_from(a,s->s,&ts,m,args); | |
447 } | |
448 | |
449 foreign_t send_osc_at(term_t addr, term_t msg, term_t args, term_t time) { | |
450 lo_address a; | |
451 lo_timetag ts; | |
452 char *m; | |
453 | |
454 return get_addr(addr,&a) && | |
455 get_prolog_time(time,&ts) && | |
456 get_msg(msg, &m) && | |
457 send_msg_timestamped(a,&ts,m,args); | |
458 } | |
459 | |
460 foreign_t send_timestamped(term_t addr, term_t msg, term_t args, term_t secs, term_t frac) { | |
461 lo_address a; | |
462 lo_timetag ts; | |
463 char *m; | |
464 | |
465 return get_addr(addr,&a) && | |
466 get_timetag(secs,frac,&ts) && | |
467 get_msg(msg, &m) && | |
468 send_msg_timestamped(a,&ts,m,args); | |
469 } | |
470 | |
471 | |
472 | |
473 foreign_t send_osc_now(term_t addr, term_t msg, term_t args) { | |
474 lo_address a; | |
475 char *m; | |
476 | |
477 return get_addr(addr,&a) && | |
478 get_msg(msg, &m) && | |
479 send_msg(a,m,args); | |
480 } | |
481 | |
482 | |
483 | |
484 /* | |
485 * Server Bits | |
486 */ | |
487 | |
488 static void prolog_thread_func(void *data); | |
489 | |
490 // parse a list of type terms and encode as a NULL terminated | |
491 // string where each character encodes the type of one argument. | |
492 static int get_types_list(term_t list, char *typespec, int len) | |
493 { | |
494 term_t head=PL_new_term_ref(); | |
495 int count=0; | |
496 | |
497 // copy term ref so as not to modify original | |
498 list=PL_copy_term_ref(list); | |
499 | |
500 while (PL_get_list(list,head,list) && count<len) { | |
501 atom_t name; | |
502 int arity; | |
503 const char *type; | |
504 | |
505 if (!PL_get_name_arity(head,&name,&arity)) return type_error(head,"term"); | |
506 type=PL_atom_chars(name); | |
507 switch (arity) { | |
508 case 1: { | |
509 if (!strcmp(type,"int")) { | |
510 typespec[count++]='i'; | |
511 } else if (!strcmp(type,"double")) { | |
512 typespec[count++]='d'; | |
513 } else if (!strcmp(type,"string")) { | |
514 typespec[count++]='s'; | |
515 } else if (!strcmp(type,"symbol")) { | |
516 typespec[count++]='S'; | |
517 } else if (!strcmp(type,"float")) { | |
518 typespec[count++]='f'; | |
519 } | |
520 break; | |
521 } | |
522 case 0: { | |
523 if (!strcmp(type,"true")) typespec[count++]='T'; | |
524 else if (!strcmp(type,"false")) typespec[count++]='F'; | |
525 else if (!strcmp(type,"nil")) typespec[count++]='N'; | |
526 else if (!strcmp(type,"inf")) typespec[count++]='I'; | |
527 break; | |
528 } | |
529 } | |
530 } | |
531 typespec[count]=0; | |
532 if (!PL_get_nil(list)) return type_error(list,"nil"); | |
533 return TRUE; | |
534 } | |
535 | |
536 // parse a term representing argument types - types can be a list | |
537 // as accepted by get_types_list() above or the atom 'any' | |
538 static int get_types(term_t types, char *buffer, int len, char **typespec) | |
539 { | |
540 if (PL_is_list(types)) { | |
541 *typespec=buffer; | |
542 return get_types_list(types,buffer,len); | |
543 } else if (PL_is_atom(types)) { | |
544 char *a; | |
545 if (PL_get_atom_chars(types,&a) && strcmp(a,"any")==0) { | |
546 *typespec=NULL; return TRUE; | |
547 } else return type_error(types,"list or 'any'"); | |
548 } else return type_error(types,"list or 'any'"); | |
549 } | |
550 | |
551 // handler server error | |
552 static void server_error(int num, const char *msg, const char *path) { | |
553 osc_error(num,msg,path); | |
554 } | |
555 | |
556 // handle the /plosc/stop message for the synchronous server loop | |
557 // in run_stoppable_server() and hence osc_run_server/1 | |
558 static int stop_handler(const char *path, const char *types, lo_arg **argv, | |
559 int argc, lo_message msg, void *user_data) | |
560 { | |
561 my_server_thread s=(my_server_thread)user_data; | |
562 s->active=0; | |
563 return 1; | |
564 } | |
565 | |
566 // get message arguments and unify given term with list of arg terms | |
567 static int unify_msg_args(term_t list, const char *types, lo_arg **argv, int argc) | |
568 { | |
569 int i, rc=0; | |
570 for (i=0; i<argc; i++) { | |
571 term_t head=PL_new_term_ref(); | |
572 term_t tail=PL_new_term_ref(); | |
573 if (!PL_unify_list(list,head,tail)) PL_fail; | |
574 switch (types[i]) { | |
575 case 'i': rc=PL_unify_term(head,PL_FUNCTOR, int_1, PL_INT,argv[i]->i); break; | |
576 case 'f': rc=PL_unify_term(head,PL_FUNCTOR, float_1, PL_FLOAT,(double)argv[i]->f); break; | |
577 case 'd': rc=PL_unify_term(head,PL_FUNCTOR, double_1, PL_DOUBLE,argv[i]->d); break; | |
578 case 's': rc=PL_unify_term(head,PL_FUNCTOR, string_1, PL_CHARS,&argv[i]->s); break; | |
579 case 'h': rc=PL_unify_term(head,PL_FUNCTOR_CHARS,"int64",1,PL_INT64,argv[i]->h); break; | |
580 case 'c': rc=PL_unify_term(head,PL_FUNCTOR_CHARS,"char",1,PL_INT,(int)argv[i]->c); break; | |
581 case 'S': rc=PL_unify_term(head,PL_FUNCTOR_CHARS,"symbol",1,PL_CHARS,&argv[i]->S); break; | |
582 case 'T': rc=PL_unify_term(head,PL_FUNCTOR_CHARS,"true",0); break; | |
583 case 'F': rc=PL_unify_term(head,PL_FUNCTOR_CHARS,"false",0); break; | |
584 case 'N': rc=PL_unify_term(head,PL_FUNCTOR_CHARS,"nil",0); break; | |
585 case 'I': rc=PL_unify_term(head,PL_FUNCTOR_CHARS,"inf",0); break; | |
586 case 'b': rc=PL_unify_term(head,PL_FUNCTOR_CHARS,"blob",0); break; | |
587 case 't': rc=PL_unify_term(head,PL_FUNCTOR_CHARS,"timetag",2, | |
588 PL_INT64,(int64_t)argv[i]->t.sec, | |
589 PL_INT64,(int64_t)argv[i]->t.frac); | |
590 break; | |
591 case 'm': rc=PL_unify_term(head,PL_FUNCTOR_CHARS,"midi",4, | |
592 PL_INT,(int)argv[i]->m[0], PL_INT,(int)argv[i]->m[1], | |
593 PL_INT,(int)argv[i]->m[2], PL_INT,(int)argv[i]->m[3]); | |
594 break; | |
595 } | |
596 if (!rc) PL_fail; | |
597 list=tail; | |
598 } | |
599 return PL_unify_nil(list); | |
600 } | |
601 | |
602 // handle OSC message by calling the associated Prolog goal | |
603 static int prolog_handler(const char *path, const char *types, lo_arg **argv, | |
604 int argc, lo_message msg, void *user_data) | |
605 { | |
606 term_t goal = PL_new_term_ref(); | |
607 term_t term0 = PL_new_term_refs(3); | |
608 | |
609 | |
610 PL_recorded((record_t)user_data,goal); // retrieve the goal term | |
611 PL_put_term(term0,goal); // term_t goal encoded in user_data | |
612 PL_put_atom_chars(term0+1,path); | |
613 | |
614 return !( unify_msg_args(PL_copy_term_ref(term0+2),types,argv,argc) | |
615 && PL_call_predicate(NULL,PL_Q_NORMAL,call3,term0)); | |
616 } | |
617 | |
618 static int prolog_handler_x(const char *path, const char *types, lo_arg **argv, | |
619 int argc, lo_message msg, void *user_data) | |
620 { | |
621 term_t goal = PL_new_term_ref(); | |
622 term_t term0 = PL_new_term_refs(5); | |
623 int rc=1; | |
624 | |
625 lo_timetag ts = lo_message_get_timestamp(msg); | |
626 lo_address sender = lo_message_get_source(msg); | |
627 // printf("osc tt: %u s + %u micros\n",ts.sec,ts.frac); | |
628 | |
629 PL_recorded((record_t)user_data,goal); // retrieve the goal term | |
630 PL_put_term(term0,goal); // term_t goal encoded in user_data | |
631 PL_put_atom_chars(term0+3,path); | |
632 | |
633 if (ts.sec==0u) PL_put_atom(term0+2,osc_immed); | |
634 else { | |
635 rc=PL_unify_term( term0+2, PL_FUNCTOR, osc_ts_2, | |
636 PL_INT64, (int64_t)ts.sec, | |
637 PL_INT64, (int64_t)ts.frac); | |
638 } | |
639 // PL_put_float(term0+2, (double)(ts.sec-2208988800u) + ts.frac/4294967296.0); | |
640 | |
641 return !( rc | |
642 && unify_addr(term0+1,sender) | |
643 && unify_msg_args(PL_copy_term_ref(term0+4),types,argv,argc) | |
644 && PL_call_predicate(NULL,PL_Q_NORMAL,call5,term0)); | |
645 } | |
646 | |
647 /* | |
648 static int generic_handler(const char *path, const char *types, lo_arg **argv, | |
649 int argc, lo_message msg, void *user_data) | |
650 { | |
651 int i; | |
652 | |
653 printf("path: <%s>\n", path); | |
654 for (i=0; i<argc; i++) { | |
655 printf("arg %d '%c' ", i, types[i]); | |
656 lo_arg_pp(types[i], argv[i]); | |
657 printf("\n"); | |
658 } | |
659 printf("\n"); | |
660 fflush(stdout); | |
661 return 1; | |
662 } | |
663 | |
664 static int verbose_prolog_handler(const char *path, const char *types, lo_arg **argv, | |
665 int argc, lo_message msg, void *user_data) | |
666 { | |
667 generic_handler(path,types,argv,argc,msg,user_data); | |
668 prolog_handler(path,types,argv,argc,msg,user_data); | |
669 return 1; | |
670 } | |
671 */ | |
672 | |
673 // run OSC server in this thread but with an extra message handler | |
674 // to allow the /plosc/stop message to terminate the loop. | |
675 static int run_stoppable_server(my_server_thread s, int timeout) | |
676 { | |
677 lo_server_add_method(s->s, "/plosc/stop", NULL, stop_handler, (void *)s); | |
678 my_server_thread_run(s,timeout); | |
679 lo_server_del_method(s->s,"/plosc/stop",NULL); | |
680 return TRUE; | |
681 } | |
682 | |
683 foreign_t mk_server(term_t port, term_t server) | |
684 { | |
685 char *p; | |
686 | |
687 if (PL_get_chars(port, &p, CVT_INTEGER)) { | |
688 my_server_thread s = my_server_thread_new(p, server_error); | |
689 if (s) return unify_server(server,s); | |
690 else return FALSE; | |
691 } else { | |
692 return type_error(port,"integer"); | |
693 } | |
694 } | |
695 | |
696 foreign_t add_handler_x(term_t server, term_t msg, term_t types, term_t goal) | |
697 { | |
698 my_server_thread s; | |
699 lo_method method; | |
700 char *pattern, *typespec; | |
701 char buffer[256]; // !! space for up to 255 arguments | |
702 int rc; | |
703 | |
704 rc = get_server(server,&s) | |
705 && get_msg(msg,&pattern) | |
706 && get_types(types,buffer,256,&typespec); | |
707 | |
708 if (rc) { | |
709 record_t goal_record=PL_record(goal); | |
710 method = lo_server_add_method(s->s, pattern, typespec, prolog_handler_x, (void *)goal_record); | |
711 } | |
712 return rc; | |
713 } | |
714 | |
715 foreign_t add_handler(term_t server, term_t msg, term_t types, term_t goal) | |
716 { | |
717 my_server_thread s; | |
718 lo_method method; | |
719 char *pattern, *typespec; | |
720 char buffer[256]; // !! space for up to 255 arguments | |
721 int rc; | |
722 | |
723 rc = get_server(server,&s) | |
724 && get_msg(msg,&pattern) | |
725 && get_types(types,buffer,256,&typespec); | |
726 | |
727 if (rc) { | |
728 record_t goal_record=PL_record(goal); | |
729 method = lo_server_add_method(s->s, pattern, typespec, prolog_handler, (void *)goal_record); | |
730 } | |
731 return rc; | |
732 } | |
733 | |
734 foreign_t del_handler(term_t server, term_t msg, term_t types) | |
735 { | |
736 my_server_thread s; | |
737 char *pattern, *typespec; | |
738 char buffer[256]; // !! space for up to 255 arguments | |
739 int rc; | |
740 | |
741 rc = get_server(server,&s) | |
742 && get_msg(msg,&pattern) | |
743 && get_types(types,buffer,256,&typespec); | |
744 | |
745 if (rc) lo_server_del_method(s->s,pattern,typespec); | |
746 return rc; | |
747 } | |
748 | |
749 foreign_t start_server( term_t server) | |
750 { | |
751 my_server_thread s; | |
752 return get_server(server,&s) && (my_server_thread_start(s)==0); | |
753 } | |
754 | |
755 foreign_t stop_server( term_t server) | |
756 { | |
757 my_server_thread s; | |
758 return get_server(server,&s) && (my_server_thread_stop(s)==0); | |
759 } | |
760 | |
761 foreign_t run_server( term_t server) | |
762 { | |
763 my_server_thread s; | |
764 printf("running OSC server synchronously...\n"); | |
765 return get_server(server,&s) && run_stoppable_server(s,10); | |
766 } | |
767 | |
768 | |
769 // ------------------------------------------------------------------------- | |
770 // my_server_thread implementation | |
771 | |
772 my_server_thread my_server_thread_new(const char *port, lo_err_handler err_h) | |
773 { | |
774 my_server_thread st = malloc(sizeof(struct _my_server_thread)); | |
775 st->s = lo_server_new(port, err_h); | |
776 st->active = 0; | |
777 st->done = 0; | |
778 | |
779 if (!st->s) { | |
780 free(st); | |
781 return NULL; | |
782 } | |
783 return st; | |
784 } | |
785 | |
786 void my_server_thread_free(my_server_thread st) | |
787 { | |
788 if (st) { | |
789 if (st->active) { | |
790 my_server_thread_stop(st); | |
791 } | |
792 lo_server_free(st->s); | |
793 } | |
794 free(st); | |
795 } | |
796 | |
797 int my_server_thread_stop(my_server_thread st) | |
798 { | |
799 int result; | |
800 | |
801 if (st->active) { | |
802 st->active = 0; // Signal thread to stop | |
803 | |
804 result = pthread_join( st->thread, NULL ); | |
805 if (result) { | |
806 fprintf(stderr,"Failed to stop thread: pthread_join(), %s",strerror(result)); | |
807 return -result; | |
808 } | |
809 } | |
810 | |
811 return 0; | |
812 } | |
813 | |
814 | |
815 int my_server_thread_start(my_server_thread st) | |
816 { | |
817 int result; | |
818 | |
819 if (!st->active) { | |
820 st->active = 1; | |
821 st->done = 0; | |
822 | |
823 // Create the server thread | |
824 result = pthread_create(&(st->thread), NULL, (void *)&prolog_thread_func, st); | |
825 if (result) { | |
826 fprintf(stderr, "Failed to create thread: pthread_create(), %s", | |
827 strerror(result)); | |
828 return -result; | |
829 } | |
830 | |
831 } | |
832 return 0; | |
833 } | |
834 | |
835 int my_server_thread_run(my_server_thread st, int timeout) | |
836 { | |
837 st->active = 1; | |
838 st->done = 0; | |
839 while (st->active) { | |
840 lo_server_recv_noblock(st->s, timeout); | |
841 } | |
842 st->done = 1; | |
843 return 0; | |
844 } | |
845 | |
846 // code for the asynchronous server loop | |
847 // we must create and attach a new Prolog engine to enable | |
848 // calls to Prolog from this thread. | |
849 static void prolog_thread_func(void *data) | |
850 { | |
851 my_server_thread st = (my_server_thread)data; | |
852 | |
853 printf("OSC server started.\n"); | |
854 PL_thread_attach_engine(NULL); | |
855 my_server_thread_run(st,50); | |
856 PL_thread_destroy_engine(); | |
857 printf("OSC server stopped.\n"); | |
858 pthread_exit(NULL); | |
859 } | |
860 |