~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Linux Cross Reference
JACK/libjack/port.c


** Warning: Cannot open xref database.

1 /* 2 Copyright (C) 2001-2003 Paul Davis 3 4 This program is free software; you can redistribute it and/or modify 5 it under the terms of the GNU Lesser General Public License as published by 6 the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. 13 14 You should have received a copy of the GNU Lesser General Public License 15 along with this program; if not, write to the Free Software 16 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 18 $Id: port.c,v 1.13 2003/10/31 20:52:24 joq Exp $ 19 */ 20 21 #include <string.h> 22 #include <stdio.h> 23 #include <math.h> 24 25 #include <config.h> 26 27 #include <jack/jack.h> 28 #include <jack/types.h> 29 #include <jack/internal.h> 30 #include <jack/engine.h> 31 #include <jack/pool.h> 32 #include <jack/port.h> 33 #include <jack/jslist.h> 34 35 #include "local.h" 36 37 static void jack_audio_port_mixdown (jack_port_t *port, 38 jack_nframes_t nframes); 39 40 /* These function pointers are local to each address space. For 41 * internal clients they reside within jackd; for external clients in 42 * the application process. */ 43 jack_port_functions_t jack_builtin_audio_functions = { 44 .mixdown = jack_audio_port_mixdown, 45 }; 46 47 jack_port_functions_t jack_builtin_NULL_functions = { 48 .mixdown = NULL, 49 }; 50 51 /* Only the Audio port type is currently built in. */ 52 jack_port_type_info_t jack_builtin_port_types[] = { 53 { .type_name = JACK_DEFAULT_AUDIO_TYPE, 54 .buffer_scale_factor = 1, 55 }, 56 { .type_name = "", } 57 }; 58 59 jack_port_t * 60 jack_port_new (const jack_client_t *client, jack_port_id_t port_id, 61 jack_control_t *control) 62 { 63 jack_port_shared_t *shared = &control->ports[port_id]; 64 jack_port_type_id_t ptid = shared->ptype_id; 65 jack_port_t *port = (jack_port_t *) malloc (sizeof (jack_port_t)); 66 67 port->mix_buffer = NULL; 68 port->client_segment_base = NULL; 69 port->shared = shared; 70 port->type_info = &client->engine->port_types[ptid]; 71 pthread_mutex_init (&port->connection_lock, NULL); 72 port->connections = 0; 73 port->tied = NULL; 74 75 if (client->control->id == port->shared->client_id) { 76 77 /* It's our port, so initialize the pointers to port 78 * functions within this address space. These builtin 79 * definitions can be overridden by the client. 80 */ 81 82 if (ptid == JACK_AUDIO_PORT_TYPE) { 83 84 port->fptr = jack_builtin_audio_functions; 85 port->shared->has_mixdown = TRUE; 86 87 } else { /* no other builtin functions */ 88 89 port->fptr = jack_builtin_NULL_functions; 90 port->shared->has_mixdown = FALSE; 91 } 92 } 93 94 /* set up a base address so that port->offset can be used to 95 compute the correct location. we don't store the location 96 directly, because port->client_segment_base and/or 97 port->offset can change if the buffer size or port counts 98 are changed. 99 */ 100 101 port->client_segment_base = (void **) &client->port_segment[ptid].attached_at; 102 103 return port; 104 } 105 106 jack_port_t * 107 jack_port_register (jack_client_t *client, 108 const char *port_name, 109 const char *port_type, 110 unsigned long flags, 111 unsigned long buffer_size) 112 { 113 jack_request_t req; 114 jack_port_t *port = 0; 115 int length ; 116 117 req.type = RegisterPort; 118 119 length = strlen ( (const char *) client->control->name ) 120 + 1 + strlen ( port_name ) ; 121 if ( length >= sizeof (req.x.port_info.name) ) { 122 jack_error ("\"%s:%s\" is too long to be used as a JACK port name.\n" 123 "Please use %lu characters or less.", 124 client->control->name , 125 port_name , 126 sizeof (req.x.port_info.name) - 1); 127 return NULL ; 128 } 129 130 strcpy ((char *) req.x.port_info.name, 131 (const char *) client->control->name); 132 strcat ((char *) req.x.port_info.name, ":"); 133 strcat ((char *) req.x.port_info.name, port_name); 134 135 snprintf (req.x.port_info.type, sizeof (req.x.port_info.type), 136 "%s", port_type); 137 req.x.port_info.flags = flags; 138 req.x.port_info.buffer_size = buffer_size; 139 req.x.port_info.client_id = client->control->id; 140 141 if (jack_client_deliver_request (client, &req)) { 142 return NULL; 143 } 144 145 if ((port = jack_port_new (client, req.x.port_info.port_id, 146 client->engine)) == NULL) { 147 return NULL; 148 } 149 150 client->ports = jack_slist_prepend (client->ports, port); 151 152 return port; 153 } 154 155 int 156 jack_port_unregister (jack_client_t *client, jack_port_t *port) 157 { 158 jack_request_t req; 159 160 req.type = UnRegisterPort; 161 req.x.port_info.port_id = port->shared->id; 162 req.x.port_info.client_id = client->control->id; 163 164 return jack_client_deliver_request (client, &req); 165 } 166 167 int 168 jack_port_lock (jack_client_t *client, jack_port_t *port) 169 { 170 if (port) { 171 port->shared->locked = 1; 172 return 0; 173 } 174 return -1; 175 } 176 177 int 178 jack_port_unlock (jack_client_t *client, jack_port_t *port) 179 { 180 if (port) { 181 port->shared->locked = 0; 182 return 0; 183 } 184 return -1; 185 } 186 187 /* LOCAL (in-client) connection querying only */ 188 189 int 190 jack_port_connected (const jack_port_t *port) 191 { 192 return jack_slist_length (port->connections); 193 } 194 195 int 196 jack_port_connected_to (const jack_port_t *port, const char *portname) 197 { 198 JSList *node; 199 int ret = FALSE; 200 201 /* XXX this really requires a cross-process lock 202 so that ports/connections cannot go away 203 while we are checking for them. that's hard, 204 and has a non-trivial performance impact 205 for jackd. 206 */ 207 208 pthread_mutex_lock (&((jack_port_t *) port)->connection_lock); 209 210 for (node = port->connections; node; node = jack_slist_next (node)) { 211 jack_port_t *other_port = (jack_port_t *) node->data; 212 213 if (strcmp (other_port->shared->name, portname) == 0) { 214 ret = TRUE; 215 break; 216 } 217 } 218 219 pthread_mutex_unlock (&((jack_port_t *) port)->connection_lock); 220 return ret; 221 } 222 223 const char ** 224 jack_port_get_connections (const jack_port_t *port) 225 { 226 const char **ret = NULL; 227 JSList *node; 228 unsigned int n; 229 230 /* XXX this really requires a cross-process lock 231 so that ports/connections cannot go away 232 while we are checking for them. that's hard, 233 and has a non-trivial performance impact 234 for jackd. 235 */ 236 237 pthread_mutex_lock (&((jack_port_t *) port)->connection_lock); 238 239 if (port->connections != NULL) { 240 241 ret = (const char **) 242 malloc (sizeof (char *) 243 * (jack_slist_length (port->connections) + 1)); 244 for (n = 0, node = port->connections; node; 245 node = jack_slist_next (node), ++n) { 246 ret[n] = ((jack_port_t *) node->data)->shared->name; 247 } 248 ret[n] = NULL; 249 } 250 251 pthread_mutex_unlock (&((jack_port_t *) port)->connection_lock); 252 return ret; 253 } 254 255 /* SERVER-SIDE (all) connection querying */ 256 257 const char ** 258 jack_port_get_all_connections (const jack_client_t *client, 259 const jack_port_t *port) 260 { 261 const char **ret; 262 jack_request_t req; 263 unsigned int i; 264 265 if (port == NULL) { 266 return NULL; 267 } 268 269 req.type = GetPortConnections; 270 271 req.x.port_info.name[0] = '\0'; 272 req.x.port_info.type[0] = '\0'; 273 req.x.port_info.flags = 0; 274 req.x.port_info.buffer_size = 0; 275 req.x.port_info.client_id = 0; 276 req.x.port_info.port_id = port->shared->id; 277 278 jack_client_deliver_request (client, &req); 279 280 if (req.status != 0 || req.x.port_connections.nports == 0) { 281 return NULL; 282 } 283 284 if (client->request_fd < 0) { 285 /* internal client */ 286 return req.x.port_connections.ports; 287 } 288 289 ret = (const char **) 290 malloc (sizeof (char *) * (req.x.port_connections.nports + 1)); 291 292 for (i = 0; i < req.x.port_connections.nports; ++i ) { 293 jack_port_id_t port_id; 294 295 if (read (client->request_fd, &port_id, sizeof (port_id)) 296 != sizeof (port_id)) { 297 jack_error ("cannot read port id from server"); 298 return 0; 299 } 300 301 ret[i] = jack_port_by_id (client, port_id)->shared->name; 302 } 303 304 ret[i] = NULL; 305 306 return ret; 307 } 308 309 jack_port_t * 310 jack_port_by_id (const jack_client_t *client, jack_port_id_t id) 311 { 312 JSList *node; 313 314 for (node = client->ports; node; node = jack_slist_next (node)) { 315 if (((jack_port_t *) node->data)->shared->id == id) { 316 return (jack_port_t *) node->data; 317 } 318 } 319 320 if (id >= client->engine->port_max) 321 return NULL; 322 323 if (client->engine->ports[id].in_use) 324 return jack_port_new (client, id, client->engine); 325 326 return NULL; 327 } 328 329 jack_port_t * 330 jack_port_by_name (jack_client_t *client, const char *port_name) 331 { 332 unsigned long i, limit; 333 jack_port_shared_t *port; 334 335 limit = client->engine->port_max; 336 port = &client->engine->ports[0]; 337 338 for (i = 0; i < limit; i++) { 339 if (port[i].in_use && strcmp (port[i].name, port_name) == 0) { 340 return jack_port_new (client, port[i].id, 341 client->engine); 342 } 343 } 344 345 return NULL; 346 } 347 348 jack_nframes_t 349 jack_port_get_latency (jack_port_t *port) 350 { 351 return port->shared->latency; 352 } 353 354 jack_nframes_t 355 jack_port_get_total_latency (jack_client_t *client, jack_port_t *port) 356 { 357 return port->shared->total_latency; 358 } 359 360 void 361 jack_port_set_latency (jack_port_t *port, jack_nframes_t nframes) 362 { 363 port->shared->latency = nframes; 364 } 365 366 void * 367 jack_port_get_buffer (jack_port_t *port, jack_nframes_t nframes) 368 { 369 JSList *node, *next; 370 371 /* Output port. The buffer was assigned by the engine 372 when the port was registered. 373 */ 374 if (port->shared->flags & JackPortIsOutput) { 375 if (port->tied) { 376 return jack_port_get_buffer (port->tied, nframes); 377 } 378 379 return jack_output_port_buffer (port); 380 } 381 382 /* Input port. Since this can only be called from the 383 process() callback, and since no connections can be 384 made/broken during this phase (enforced by the jack 385 server), there is no need to take the connection lock here 386 */ 387 if ((node = port->connections) == NULL) { 388 389 /* no connections; return a zero-filled buffer */ 390 return jack_zero_filled_buffer; 391 } 392 393 if ((next = jack_slist_next (node)) == NULL) { 394 395 /* one connection: use zero-copy mode - just pass 396 the buffer of the connected (output) port. 397 */ 398 return jack_port_get_buffer (((jack_port_t *) node->data), 399 nframes); 400 } 401 402 /* Multiple connections. Use a local buffer and mix the 403 incoming data into that buffer. We have already 404 established the existence of a mixdown function during the 405 connection process. 406 */ 407 if (port->mix_buffer == NULL) { 408 port->mix_buffer = 409 jack_pool_alloc ( 410 port->type_info->buffer_scale_factor 411 * sizeof (jack_default_audio_sample_t) 412 * nframes); 413 } 414 port->fptr.mixdown (port, nframes); 415 return (jack_default_audio_sample_t *) port->mix_buffer; 416 } 417 418 int 419 jack_port_tie (jack_port_t *src, jack_port_t *dst) 420 421 { 422 if (dst->shared->client_id != src->shared->client_id) { 423 jack_error ("cannot tie ports not owned by the same client"); 424 return -1; 425 } 426 427 if (dst->shared->flags & JackPortIsOutput) { 428 jack_error ("cannot tie an input port"); 429 return -1; 430 } 431 432 dst->tied = src; 433 return 0; 434 } 435 436 int 437 jack_port_untie (jack_port_t *port) 438 439 { 440 if (port->tied == NULL) { 441 jack_error ("port \"%s\" is not tied", port->shared->name); 442 return -1; 443 } 444 port->tied = NULL; 445 return 0; 446 } 447 448 int 449 jack_port_request_monitor (jack_port_t *port, int onoff) 450 451 { 452 if (onoff) { 453 port->shared->monitor_requests++; 454 } else if (port->shared->monitor_requests) { 455 port->shared->monitor_requests--; 456 } 457 458 if ((port->shared->flags & JackPortIsOutput) == 0) { 459 460 JSList *node; 461 462 /* this port is for input, so recurse over each of the 463 connected ports. 464 */ 465 466 pthread_mutex_lock (&port->connection_lock); 467 for (node = port->connections; node; 468 node = jack_slist_next (node)) { 469 470 /* drop the lock because if there is a feedback loop, 471 we will deadlock. XXX much worse things will 472 happen if there is a feedback loop !!! 473 */ 474 475 pthread_mutex_unlock (&port->connection_lock); 476 jack_port_request_monitor ((jack_port_t *) node->data, 477 onoff); 478 pthread_mutex_lock (&port->connection_lock); 479 } 480 pthread_mutex_unlock (&port->connection_lock); 481 } 482 483 return 0; 484 } 485 486 int 487 jack_port_request_monitor_by_name (jack_client_t *client, 488 const char *port_name, int onoff) 489 490 { 491 jack_port_t *port; 492 unsigned long i, limit; 493 jack_port_shared_t *ports; 494 495 limit = client->engine->port_max; 496 ports = &client->engine->ports[0]; 497 498 for (i = 0; i < limit; i++) { 499 if (ports[i].in_use && 500 strcmp (ports[i].name, port_name) == 0) { 501 port = jack_port_new (client, ports[i].id, 502 client->engine); 503 return jack_port_request_monitor (port, onoff); 504 free (port); 505 return 0; 506 } 507 } 508 509 return -1; 510 } 511 512 int 513 jack_ensure_port_monitor_input (jack_port_t *port, int yn) 514 { 515 if (yn) { 516 if (port->shared->monitor_requests == 0) { 517 port->shared->monitor_requests++; 518 } 519 } else { 520 if (port->shared->monitor_requests > 0) { 521 port->shared->monitor_requests = 0; 522 } 523 } 524 525 return 0; 526 } 527 528 int 529 jack_port_monitoring_input (jack_port_t *port) 530 { 531 return port->shared->monitor_requests > 0; 532 } 533 534 const char * 535 jack_port_name (const jack_port_t *port) 536 { 537 return port->shared->name; 538 } 539 540 const char * 541 jack_port_short_name (const jack_port_t *port) 542 { 543 /* we know there is always a colon, because we put 544 it there ... 545 */ 546 547 return strchr (port->shared->name, ':') + 1; 548 } 549 550 int 551 jack_port_is_mine (const jack_client_t *client, const jack_port_t *port) 552 { 553 return port->shared->client_id == client->control->id; 554 } 555 556 int 557 jack_port_flags (const jack_port_t *port) 558 { 559 return port->shared->flags; 560 } 561 562 const char * 563 jack_port_type (const jack_port_t *port) 564 { 565 return port->type_info->type_name; 566 } 567 568 int 569 jack_port_set_name (jack_port_t *port, const char *new_name) 570 { 571 char *colon; 572 int len; 573 574 colon = strchr (port->shared->name, ':'); 575 len = sizeof (port->shared->name) - 576 ((int) (colon - port->shared->name)) - 2; 577 snprintf (colon+1, len, "%s", new_name); 578 579 return 0; 580 } 581 582 /* AUDIO PORT SUPPORT */ 583 584 static inline float f_max(float x, float a) 585 { 586 x -= a; 587 x += fabs (x); 588 x *= 0.5; 589 x += a; 590 591 return (x); 592 } 593 594 static void 595 jack_audio_port_mixdown (jack_port_t *port, jack_nframes_t nframes) 596 { 597 JSList *node; 598 jack_port_t *input; 599 jack_nframes_t n; 600 jack_default_audio_sample_t *buffer; 601 jack_default_audio_sample_t *dst, *src; 602 603 /* by the time we've called this, we've already established 604 the existence of more than one connection to this input 605 port and allocated a mix_buffer. 606 */ 607 608 /* no need to take connection lock, since this is called 609 from the process() callback, and the jack server 610 ensures that no changes to connections happen 611 during this time. 612 */ 613 614 node = port->connections; 615 input = (jack_port_t *) node->data; 616 buffer = port->mix_buffer; 617 618 memcpy (buffer, jack_output_port_buffer (input), 619 sizeof (jack_default_audio_sample_t) * nframes); 620 621 for (node = jack_slist_next (node); node; 622 node = jack_slist_next (node)) { 623 624 input = (jack_port_t *) node->data; 625 626 n = nframes; 627 dst = buffer; 628 src = jack_output_port_buffer (input); 629 630 while (n--) { 631 *dst++ += *src++; 632 } 633 } 634 } 635

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.