annotate c/plosc.c @ 0:bbd2b1abfb32

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