annotate c/plosc.c @ 14:900cc9a036ca tip

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