source: roaraudio/libroar/stream.c @ 5229:d7e314825b8a

Last change on this file since 5229:d7e314825b8a was 5229:d7e314825b8a, checked in by phi, 12 years ago

corrected coding style

File size: 31.7 KB
Line 
1//stream.c:
2
3/*
4 *      Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2008-2011
5 *
6 *  This file is part of libroar a part of RoarAudio,
7 *  a cross-platform sound system for both, home and professional use.
8 *  See README for details.
9 *
10 *  This file is free software; you can redistribute it and/or modify
11 *  it under the terms of the GNU General Public License version 3
12 *  as published by the Free Software Foundation.
13 *
14 *  libroar is distributed in the hope that it will be useful,
15 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 *  GNU General Public License for more details.
18 *
19 *  You should have received a copy of the GNU General Public License
20 *  along with this software; see the file COPYING.  If not, write to
21 *  the Free Software Foundation, 51 Franklin Street, Fifth Floor,
22 *  Boston, MA 02110-1301, USA.
23 *
24 *  NOTE for everyone want's to change something and send patches:
25 *  read README and HACKING! There a addition information on
26 *  the license of this document you need to read before you send
27 *  any patches.
28 *
29 *  NOTE for uses of non-GPL (LGPL,...) software using libesd, libartsc
30 *  or libpulse*:
31 *  The libs libroaresd, libroararts and libroarpulse link this lib
32 *  and are therefore GPL. Because of this it may be illigal to use
33 *  them with any software that uses libesd, libartsc or libpulse*.
34 */
35
36#include "libroar.h"
37
38int roar_stream_connect  (struct roar_connection * con, struct roar_stream * s, int dir) {
39 return roar_stream_connect2(con, s, dir, -1);
40}
41
42int roar_stream_connect2 (struct roar_connection * con, struct roar_stream * s, int dir, int mixer) {
43 struct roar_libroar_config * config = roar_libroar_get_config();
44 struct roar_stream  ms;
45 struct roar_message m;
46
47 s->dir = dir;
48
49 memset(&m,  0, sizeof(m));
50 memcpy(&ms, s, sizeof(ms));
51
52 m.cmd     = ROAR_CMD_NEW_STREAM;
53 m.stream  = mixer;
54 m.pos     = 0;
55
56 if ( config != NULL ) {
57  if ( config->info.rate )
58   ms.info.rate = config->info.rate;
59  if ( config->info.bits )
60   ms.info.bits = config->info.bits;
61  if ( config->info.channels )
62   ms.info.channels = config->info.channels;
63  if ( config->info.codec )
64   ms.info.codec = config->info.codec;
65 }
66
67 roar_stream_s2m(&ms, &m);
68
69 if ( roar_req3(con, &m, NULL) != 0 )
70  return -1;
71
72 if ( m.cmd == ROAR_CMD_OK ) {
73  s->id = m.stream;
74
75  ROAR_DBG("roar_stream_connect(*) = 0");
76  return 0;
77 }
78
79 ROAR_ERR("roar_stream_connect(*): Connecting new stream faild!");
80 ROAR_DBG("roar_stream_connect(*) = -1");
81 return -1;
82}
83
84int roar_stream_new (struct roar_stream * s, unsigned int rate,
85                     unsigned int channels, unsigned int bits, unsigned int codec) {
86
87 if ( s == NULL ) {
88  roar_err_set(ROAR_ERROR_FAULT);
89  return -1;
90 }
91
92 s->fh         = -1;
93 s->id         = -1;
94 s->pos        =  0;
95 s->pos_rel_id = -1;
96
97 s->dir        = ROAR_DIR_DEFAULT;
98
99/*
100 s->datalen    = 0;
101 s->offset     = 0;
102
103 s->database   = NULL;
104 s->dataoff    = NULL;
105*/
106
107 s->info.rate     = rate;
108 s->info.channels = channels;
109 s->info.bits     = bits;
110 s->info.codec    = codec;
111
112 if ( bits > ROAR_BITS_MAX )
113  return -1;
114
115 return 0;
116}
117
118int roar_stream_set_rel_id(struct roar_stream * s, int id) {
119 if ( s == NULL ) {
120  roar_err_set(ROAR_ERROR_FAULT);
121  return -1;
122 }
123
124 s->pos_rel_id = id;
125
126 return 0;
127}
128
129int roar_stream_get_rel_id(struct roar_stream * s) {
130 if ( s == NULL ) {
131  roar_err_set(ROAR_ERROR_FAULT);
132  return -1;
133 }
134
135 return s->pos_rel_id;
136}
137
138int roar_stream_new_by_id(struct roar_stream * s, int id) {
139 if ( s == NULL ) {
140  roar_err_set(ROAR_ERROR_FAULT);
141  return -1;
142 }
143
144 if ( roar_stream_new_empty(s) == -1 )
145  return -1;
146
147 return roar_stream_set_id(s, id);
148}
149
150int roar_stream_new_empty(struct roar_stream * s) {
151 if ( s == NULL ) {
152  roar_err_set(ROAR_ERROR_FAULT);
153  return -1;
154 }
155
156 return roar_stream_new(s, 0, 0, 0, 0);
157}
158
159int roar_stream_set_id (struct roar_stream * s, int id) {
160 if ( s == NULL ) {
161  roar_err_set(ROAR_ERROR_FAULT);
162  return -1;
163 }
164
165 s->id = id;
166
167 return 0;
168}
169
170int roar_stream_get_id (struct roar_stream * s) {
171 if ( s == NULL ) {
172  roar_err_set(ROAR_ERROR_FAULT);
173  return -1;
174 }
175
176 return s->id;
177}
178
179int roar_stream_set_fh (struct roar_stream * s, int fh) {
180 if ( s == NULL ) {
181  roar_err_set(ROAR_ERROR_FAULT);
182  return -1;
183 }
184
185 s->fh = fh;
186
187 return 0;
188}
189
190int roar_stream_get_fh (struct roar_stream * s) {
191 if ( s == NULL ) {
192  roar_err_set(ROAR_ERROR_FAULT);
193  return -1;
194 }
195
196 return s->fh;
197}
198
199int roar_stream_set_dir (struct roar_stream * s, int dir) {
200 if ( s == NULL ) {
201  roar_err_set(ROAR_ERROR_FAULT);
202  return -1;
203 }
204
205 s->dir = dir;
206
207 return 0;
208}
209
210int roar_stream_get_dir (struct roar_stream * s) {
211 if ( s == NULL ) {
212  roar_err_set(ROAR_ERROR_FAULT);
213  return -1;
214 }
215
216 return s->dir;
217}
218
219
220int roar_stream_exec    (struct roar_connection * con, struct roar_stream * s) {
221 struct roar_message m;
222
223 memset(&m,  0, sizeof(m));
224
225 m.cmd     = ROAR_CMD_EXEC_STREAM;
226 m.stream  = s->id;
227 m.datalen = 0;
228 m.pos     = 0;
229
230 if ( roar_req(con, &m, NULL) == -1 )
231  return -1;
232
233 if ( m.cmd == ROAR_CMD_OK )
234  return 0;
235 return -1;
236}
237
238int roar_stream_connect_to (struct roar_connection * con, struct roar_stream * s, int type, char * host, int port) {
239 struct roar_message m;
240
241 if ( roar_stream_connect_to_ask(con, s, type, host, port) == -1 )
242  return -1;
243
244 if ( roar_recv_message(con, &m, NULL) == -1 )
245  return -1;
246
247 if ( m.cmd == ROAR_CMD_OK )
248  return 0;
249 return -1;
250}
251
252int roar_stream_connect_to_ask (struct roar_connection * con, struct roar_stream * s, int type, char * host, int port) {
253 struct roar_message m;
254 int len = 0;
255
256 if ( host == NULL ) {
257  roar_err_set(ROAR_ERROR_FAULT);
258  return -1;
259 }
260
261 ROAR_DBG("roar_stream_connect_to_ask(*): Ask the server to connect to: %s:%i", host, port);
262
263 memset(&m,  0, sizeof(m));
264
265 m.cmd     = ROAR_CMD_CON_STREAM;
266 m.stream  = s->id;
267 m.pos     = 0;
268
269 m.data[0] = 0;
270 m.data[1] = type;
271 ((uint16_t*)&(m.data))[1] = ROAR_HOST2NET16(port);
272
273 len = strlen(host);
274
275 if ( len > 76 ) {
276  roar_err_set(ROAR_ERROR_NAMETOOLONG);
277  return -1;
278 }
279
280 strncpy(&(m.data[4]), host, len);
281
282 m.datalen = len + 4;
283
284 if ( roar_send_message(con, &m, NULL) == -1 )
285  return -1;
286
287 return 0;
288}
289
290int roar_stream_passfh  (struct roar_connection * con, struct roar_stream * s, int fh) {
291 struct roar_message m;
292 int confh;
293
294 if ( con == NULL || s == NULL ) {
295  roar_err_set(ROAR_ERROR_FAULT);
296  return -1;
297 }
298
299 if ( fh < 0 ) {
300  roar_err_set(ROAR_ERROR_INVAL);
301  return -1;
302 }
303
304 memset(&m,  0, sizeof(m));
305
306 m.cmd     = ROAR_CMD_PASSFH;
307 m.stream  = s->id;
308 m.pos     = 0;
309 m.datalen = 0;
310
311 ROAR_DBG("roar_stream_passfh(con=%p{...}, s={.id=%i,...}, fh=%i) = ?", con, s->id, fh);
312
313 roar_libroar_nowarn();
314 if ( (confh = roar_get_connection_fh(con)) == -1 ) {
315  roar_libroar_warn();
316  return -1;
317 }
318 roar_libroar_warn();
319
320 if ( roar_send_message(con, &m, NULL) == -1 ) {
321  ROAR_DBG("roar_stream_passfh(con=%p{...}, s={.id=%i,...}, fh=%i) = -1 // can not send message", con, s->id, fh);
322  return -1;
323 }
324
325 ROAR_DBG("roar_stream_passfh(*): msg send");
326
327 if ( roar_socket_send_fh(confh, fh, NULL, 0) == -1 )
328  return -1;
329
330 ROAR_DBG("roar_stream_passfh(*): fh send");
331
332 if ( roar_recv_message(con, &m, NULL) == -1 )
333  return -1;
334
335 ROAR_DBG("roar_stream_passfh(*): mes recved");
336
337 if ( m.cmd == ROAR_CMD_OK )
338  return 0;
339
340 return -1;
341}
342
343int roar_stream_attach_simple (struct roar_connection * con, struct roar_stream * s, int client) {
344 struct roar_message m;
345 uint16_t * info = (uint16_t *) m.data;
346 int i;
347
348 if ( con == NULL || s == NULL ) {
349  roar_err_set(ROAR_ERROR_FAULT);
350  return -1;
351 }
352
353 if ( client < 0 ) {
354  roar_err_set(ROAR_ERROR_INVAL);
355  return -1;
356 }
357
358 memset(&m,  0, sizeof(m));
359
360 m.cmd     = ROAR_CMD_ATTACH;
361 m.stream  = s->id;
362 m.pos     = 0;
363 m.datalen = 6;
364
365 info[0] = 0;
366 info[1] = ROAR_ATTACH_SIMPLE;
367 info[2] = client;
368
369 for (i = 0; i < m.datalen/2; i++) {
370  info[i] = ROAR_HOST2NET16(info[i]);
371 }
372
373 if ( roar_req(con, &m, NULL) == -1 )
374  return -1;
375
376 if ( m.cmd != ROAR_CMD_OK )
377  return -1;
378
379 return 0;
380}
381
382int roar_stream_add_data (struct roar_connection * con, struct roar_stream * s, char * data, size_t len) {
383 struct roar_message m;
384
385 if ( con == NULL || s == NULL || data == NULL ) {
386  roar_err_set(ROAR_ERROR_FAULT);
387  return -1;
388 }
389
390 memset(&m,  0, sizeof(m));
391
392 m.cmd     = ROAR_CMD_ADD_DATA;
393 m.stream  = s->id;
394 m.pos     = 0;
395 m.datalen = len;
396
397// if ( roar_req(con, &m, (void**)&data) == -1 )
398//  return -1;
399 if ( roar_send_message(con, &m, data) != 0 )
400  return -1;
401
402 if ( roar_recv_message(con, &m, NULL) == -1 )
403  return -1;
404
405 if ( m.cmd == ROAR_CMD_OK )
406  return 0;
407 return -1;
408}
409
410int roar_stream_send_data (struct roar_connection * con, struct roar_stream * s, char * data, size_t len) {
411
412 roar_debug_warn_obsolete("roar_stream_send_data", "roar_vio_write", NULL);
413
414 if ( s == NULL ) {
415  roar_err_set(ROAR_ERROR_FAULT);
416  return -1;
417 }
418
419 if ( s->fh == -1 ) {
420  if ( con == NULL ) {
421   roar_err_set(ROAR_ERROR_NOTSUP);
422   return -1;
423  }
424
425  if ( roar_stream_add_data(con, s, data, len) == -1 )
426   return -1;
427
428  return len;
429 }
430
431#ifdef ROAR_HAVE_IO_POSIX
432 return ROAR_NETWORK_WRITE(s->fh, data, len);
433#endif
434
435 roar_err_set(ROAR_ERROR_NOTSUP);
436 return -1;
437}
438
439int roar_stream_get_info (struct roar_connection * con, struct roar_stream * s, struct roar_stream_info * info) {
440 struct roar_message m;
441 uint16_t * data = (uint16_t *) m.data;
442 int i;
443
444 if ( con == NULL || s == NULL || info == NULL ) {
445  roar_err_set(ROAR_ERROR_FAULT);
446  return -1;
447 }
448
449 memset(&m,  0, sizeof(m));
450
451 m.cmd     = ROAR_CMD_GET_STREAM_PARA;
452 m.stream  = s->id;
453 m.datalen = 4;
454 m.pos     = 0;
455
456 data[0] = 0; // Version and reserved
457 data[1] = ROAR_STREAM_PARA_INFO; // stream
458
459 for (i = 0; i < m.datalen/2; i++) {
460  data[i] = ROAR_HOST2NET16(data[i]);
461 }
462
463 if ( roar_req(con, &m, NULL) == -1 )
464  return -1;
465
466 if ( m.cmd != ROAR_CMD_OK )
467  return -1;
468
469 for (i = 0; i < m.datalen/2; i++) {
470  data[i] = ROAR_NET2HOST16(data[i]);
471 }
472
473 if ( m.datalen < 7*2 ) {
474  roar_err_set(ROAR_ERROR_MSGSIZE);
475  return -1;
476 }
477
478 if ( data[0] != 0 || data[1] != 1 ) {
479  roar_err_set(ROAR_ERROR_NSVERSION);
480  return -1;
481 }
482
483 memset(info, 0, sizeof(struct roar_stream_info));
484 info->mixer = -1;
485 info->role  = ROAR_ROLE_UNKNOWN;
486
487 info->block_size     = data[2];
488 info->pre_underruns  = data[3];
489 info->post_underruns = data[4];
490 info->codec          = data[5];
491 info->flags          = data[6];
492 info->delay          = data[7]*1000;
493
494 if ( m.datalen < 9*2 ) {
495  info->state         = ROAR_STREAMSTATE_UNKNOWN;
496  return 0;
497 } else {
498  info->state         = data[8];
499 }
500
501 if ( m.datalen < 10*2 ) {
502  return 0;
503 } else {
504  info->flags        |= ((uint32_t)data[9]) << 16;
505 }
506
507 if ( m.datalen < 11*2 ) {
508  return 0;
509 } else {
510  info->mixer         = data[10];
511 }
512
513 if ( m.datalen < 12*2 ) {
514  return 0;
515 } else {
516  info->role          = data[11];
517 }
518
519 return 0;
520}
521
522int roar_stream_get_name (struct roar_connection * con, struct roar_stream * s, char * name, size_t len) {
523 struct roar_message m;
524 uint16_t * data = (uint16_t *) m.data;
525
526 if ( con == NULL || s == NULL || name == NULL || len == 0 ) {
527  roar_err_set(ROAR_ERROR_FAULT);
528  return -1;
529 }
530
531 name[0] = 0; // just in case...
532
533 memset(&m,  0, sizeof(m));
534
535 m.cmd     = ROAR_CMD_GET_STREAM_PARA;
536 m.stream  = s->id;
537 m.datalen = 4;
538 m.pos     = 0;
539
540 data[0] = 0; // Version and reserved
541 data[1] = ROAR_STREAM_PARA_NAME; // stream
542
543 data[0] = ROAR_HOST2NET16(data[0]);
544 data[1] = ROAR_HOST2NET16(data[1]);
545
546 ROAR_DBG("roar_stream_get_name(*) = ?");
547
548 if ( roar_req(con, &m, NULL) == -1 )
549  return -1;
550
551 ROAR_DBG("roar_stream_get_name(*) = ?");
552
553 if ( m.cmd != ROAR_CMD_OK )
554  return -1;
555
556 ROAR_DBG("roar_stream_get_name(*) = ?");
557
558 if ( m.datalen < 4 ) {
559  roar_err_set(ROAR_ERROR_MSGSIZE);
560  return -1;
561 }
562
563 data[0] = ROAR_NET2HOST16(data[0]);
564 data[1] = ROAR_NET2HOST16(data[1]);
565
566 ROAR_DBG("roar_stream_get_name(*) = ?");
567
568 if ( data[0] != 0 ) {
569  roar_err_set(ROAR_ERROR_NSVERSION);
570  return -1;
571 }
572
573 if ( data[1] != ROAR_STREAM_PARA_NAME ) {
574  roar_err_set(ROAR_ERROR_TYPEMM);
575  return -1;
576 }
577
578 m.datalen -= 4;
579
580 len--;
581
582 if ( len > m.datalen )
583  len = m.datalen;
584
585 strncpy(name, ((char*)m.data)+4, len);
586 name[len] = 0;
587
588 ROAR_DBG("roar_stream_get_name(*) = 0");
589
590 return 0;
591}
592
593int roar_stream_get_chanmap (struct roar_connection * con, struct roar_stream * s, char * map, size_t * len) {
594 struct roar_message m;
595 uint16_t * data = (uint16_t *) m.data;
596
597 ROAR_DBG("roar_stream_get_chanmap(con=%p, s=%p, map=%p, len=%p) = ?", con, s, map, len);
598
599 if ( con == NULL || s == NULL || map == NULL || len == NULL ) {
600  roar_err_set(ROAR_ERROR_FAULT);
601  return -1;
602 }
603
604 if ( *len == 0 ) {
605  roar_err_set(ROAR_ERROR_INVAL);
606  return -1;
607 }
608
609 memset(&m, 0, sizeof(m));
610
611 m.cmd     = ROAR_CMD_GET_STREAM_PARA;
612 m.stream  = s->id;
613 m.datalen = 2*2;
614
615 data[0] = 0; // Version and reserved
616 data[1] = ROAR_STREAM_PARA_CHANMAP;
617
618 data[0] = ROAR_HOST2NET16(data[0]);
619 data[1] = ROAR_HOST2NET16(data[1]);
620
621 if ( roar_req(con, &m, NULL) == -1 )
622  return -1;
623
624 ROAR_DBG("roar_stream_get_chanmap(con=%p, s=%p{.id=%i}, map=%p, len=%p) = ?", con, s, s->id, map, len);
625
626 if ( m.cmd != ROAR_CMD_OK )
627  return -1;
628
629 ROAR_DBG("roar_stream_get_chanmap(con=%p, s=%p{.id=%i}, map=%p, len=%p) = ?", con, s, s->id, map, len);
630
631 if ( m.datalen < 4 ) {
632  roar_err_set(ROAR_ERROR_MSGSIZE);
633  return -1;
634 }
635
636 data[0] = ROAR_NET2HOST16(data[0]);
637 data[1] = ROAR_NET2HOST16(data[1]);
638
639 ROAR_DBG("roar_stream_get_chanmap(con=%p, s=%p{.id=%i}, map=%p, len=%p) = ?", con, s, s->id, map, len);
640
641 if ( data[0] != 0 ) {
642  roar_err_set(ROAR_ERROR_NSVERSION);
643  return -1;
644 }
645
646 if ( data[1] != ROAR_STREAM_PARA_CHANMAP ) {
647  roar_err_set(ROAR_ERROR_TYPEMM);
648  return -1;
649 }
650
651 ROAR_DBG("roar_stream_get_chanmap(con=%p, s=%p{.id=%i}, map=%p, len=%p) = ?", con, s, s->id, map, len);
652
653 m.datalen -= 4;
654
655 if ( m.datalen > *len ) {
656  roar_err_set(ROAR_ERROR_NOMEM);
657  return -1;
658 }
659
660 ROAR_DBG("roar_stream_get_chanmap(con=%p, s=%p{.id=%i}, map=%p, len=%p) = ?", con, s, s->id, map, len);
661
662 memcpy(map, &(m.data[4]), m.datalen);
663
664 *len = m.datalen;
665
666 ROAR_DBG("roar_stream_get_chanmap(con=%p, s=%p{.id=%i}, map=%p, len=%p) = 0", con, s, s->id, map, len);
667 return 0;
668}
669
670int roar_stream_set_chanmap (struct roar_connection * con, struct roar_stream * s, char * map, size_t   len) {
671 struct roar_message m;
672 uint16_t * data = (uint16_t *) m.data;
673
674 if ( con == NULL || s == NULL || map == NULL ) {
675  roar_err_set(ROAR_ERROR_FAULT);
676  return -1;
677 }
678
679 if ( len == 0 ) {
680  roar_err_set(ROAR_ERROR_INVAL);
681  return 0;
682 }
683
684 memset(&m, 0, sizeof(m));
685
686 m.cmd     = ROAR_CMD_SET_STREAM_PARA;
687 m.stream  = s->id;
688 m.datalen = 2*2 + len;
689
690 if ( m.datalen > sizeof(m.data) )
691  return -1;
692
693 data[0] = 0; // Version and reserved
694 data[1] = ROAR_STREAM_PARA_CHANMAP;
695
696 data[0] = ROAR_HOST2NET16(data[0]);
697 data[1] = ROAR_HOST2NET16(data[1]);
698
699 memcpy(&(m.data[4]), map, len);
700
701 if ( roar_req(con, &m, NULL) == -1 )
702  return -1;
703
704 if ( m.cmd != ROAR_CMD_OK )
705  return -1;
706
707 return 0;
708}
709
710
711int roar_stream_set_flags (struct roar_connection * con, struct roar_stream * s, int flags, int reset) {
712 return roar_stream_set_flags2(con, s, flags, reset == ROAR_RESET_FLAG ? ROAR_RESET_FLAG : ROAR_SET_FLAG);
713}
714
715int roar_stream_set_flags2 (struct roar_connection * con, struct roar_stream * s, uint32_t flags, int action) {
716 struct roar_message m;
717 uint16_t * data = (uint16_t *) m.data;
718 int i;
719
720 if ( con == NULL || s == NULL ) {
721  roar_err_set(ROAR_ERROR_FAULT);
722  return -1;
723 }
724
725 memset(&m,  0, sizeof(m));
726
727 m.cmd     = ROAR_CMD_SET_STREAM_PARA;
728 m.stream  = s->id;
729 m.pos     = 0;
730
731 if ( flags & 0xFFFF0000 ) {
732  m.datalen = 2*5;
733 } else {
734  m.datalen = 2*4;
735 }
736
737 data[0] = 0; // Version and reserved
738 data[1] = ROAR_STREAM_PARA_FLAGS; // flags
739 data[2] = action;
740 data[3] = flags & 0x0000FFFF;
741
742 if ( flags & 0xFFFF0000 ) {
743  data[4] = (flags & 0xFFFF0000) >> 16;
744 }
745
746 for (i = 0; i < m.datalen/2; i++) {
747  data[i] = ROAR_HOST2NET16(data[i]);
748 }
749
750 if ( roar_req(con, &m, NULL) == -1 )
751  return -1;
752
753 if ( m.cmd != ROAR_CMD_OK )
754  return -1;
755
756 return 0;
757}
758
759int roar_stream_set_role  (struct roar_connection * con, struct roar_stream * s, int role) {
760 struct roar_message m;
761 uint16_t * data = (uint16_t *) m.data;
762 int i;
763
764 if ( con == NULL || s == NULL ) {
765  roar_err_set(ROAR_ERROR_FAULT);
766  return -1;
767 }
768
769 memset(&m,  0, sizeof(m));
770
771 m.cmd     = ROAR_CMD_SET_STREAM_PARA;
772 m.stream  = s->id;
773 m.datalen = 6;
774 m.pos     = 0;
775
776 data[0] = 0; // Version and reserved
777 data[1] = ROAR_STREAM_PARA_ROLE; // flags
778 data[2] = role;
779
780 for (i = 0; i < m.datalen/2; i++) {
781  data[i] = ROAR_HOST2NET16(data[i]);
782 }
783
784 if ( roar_req(con, &m, NULL) == -1 )
785  return -1;
786
787 if ( m.cmd != ROAR_CMD_OK )
788  return -1;
789
790 return 0;
791}
792
793#define _ROAR_STREAM_MESSAGE_LEN ((5+1)*4)
794
795int roar_stream_s2m     (struct roar_stream * s, struct roar_message * m) {
796 uint32_t * data;
797 int i;
798
799 if ( s == NULL || m == NULL ) {
800  roar_err_set(ROAR_ERROR_FAULT);
801  return -1;
802 }
803
804 m->datalen = _ROAR_STREAM_MESSAGE_LEN;
805 data = (uint32_t*) m->data;
806
807 data[0] = s->dir;
808 data[1] = s->pos_rel_id;
809 data[2] = s->info.rate;
810 data[3] = s->info.bits;
811 data[4] = s->info.channels;
812 data[5] = s->info.codec;
813
814 for (i = 0; i < _ROAR_STREAM_MESSAGE_LEN/4; i++)
815  data[i] = ROAR_HOST2NET32(data[i]);
816
817 ROAR_DBG("roar_stream_s2m(*): s->info:");
818 roar_debug_audio_info_print(&(s->info));
819
820 m->pos = s->pos;
821
822 return 0;
823}
824int roar_stream_m2s     (struct roar_stream * s, struct roar_message * m) {
825 uint32_t * data;
826 int i;
827
828 if ( s == NULL || m == NULL ) {
829  roar_err_set(ROAR_ERROR_FAULT);
830  return -1;
831 }
832
833 if ( m->datalen != _ROAR_STREAM_MESSAGE_LEN ) {
834  roar_err_set(ROAR_ERROR_MSGSIZE);
835  return -1;
836 }
837
838 s->pos = m->pos;
839
840 data = (uint32_t*) m->data;
841
842 for (i = 0; i < _ROAR_STREAM_MESSAGE_LEN/4; i++)
843  data[i] = ROAR_NET2HOST32(data[i]);
844
845 s->id            = m->stream;
846 s->dir           = data[0];
847 s->pos_rel_id    = data[1];
848 s->info.rate     = data[2];
849 s->info.bits     = data[3];
850 s->info.channels = data[4];
851 s->info.codec    = data[5];
852
853 ROAR_DBG("roar_stream_m2s(*): s->info:");
854 roar_debug_audio_info_print(&(s->info));
855
856 return 0;
857}
858
859// stream direction funcs:
860/*
861#define roar_dir2str(x)   ((x) == ROAR_DIR_PLAY   ? "play"   : (x) == ROAR_DIR_MONITOR ? "monitor" : \
862                           (x) == ROAR_DIR_FILTER ? "filter" : (x) == ROAR_DIR_RECORD  ? "record"  : \
863                           (x) == ROAR_DIR_OUTPUT ? "output" : (x) == ROAR_DIR_BIDIR   ? "bidir"   : \
864                           (x) == ROAR_DIR_MIXING ? "mixing" : \
865                           "unknown")
866*/
867
868static struct {
869 int    dir;
870 const char * name;
871} _libroar_dir[] = {
872 {ROAR_DIR_PLAY,        "play"       },
873 {ROAR_DIR_RECORD,      "record"     },
874 {ROAR_DIR_MONITOR,     "monitor"    },
875 {ROAR_DIR_FILTER,      "filter"     },
876 {ROAR_DIR_OUTPUT,      "output"     },
877 {ROAR_DIR_MIXING,      "mixing"     },
878 {ROAR_DIR_META,        "meta"       },
879 {ROAR_DIR_BIDIR,       "bidir"      },
880 {ROAR_DIR_THRU,        "thru"       },
881 {ROAR_DIR_BRIDGE,      "bridge"     },
882 {ROAR_DIR_MIDI_IN,     "midi_in"    },
883 {ROAR_DIR_MIDI_OUT,    "midi_out"   },
884 {ROAR_DIR_LIGHT_IN,    "light_in"   },
885 {ROAR_DIR_LIGHT_OUT,   "light_out"  },
886 {ROAR_DIR_RAW_IN,      "raw_in"     },
887 {ROAR_DIR_RAW_OUT,     "raw_out"    },
888 {ROAR_DIR_COMPLEX_IN,  "complex_in" },
889 {ROAR_DIR_COMPLEX_OUT, "complex_out"},
890 {ROAR_DIR_RDTCS_IN,    "rdtcs_in"   },
891 {ROAR_DIR_RDTCS_OUT,   "rdtcs_out"  },
892 {-1,                   "unknown"    }
893};
894
895const char * roar_dir2str (const int dir) {
896 int i;
897
898 for (i = 0; _libroar_dir[i].dir != -1; i++)
899  if ( _libroar_dir[i].dir == dir )
900   return _libroar_dir[i].name;
901
902 return _libroar_dir[i].name;
903}
904
905int roar_str2dir (const char * name) {
906 int i;
907
908 for (i = 0; _libroar_dir[i].dir != -1; i++)
909  if ( !strcmp(_libroar_dir[i].name, name) )
910   return _libroar_dir[i].dir;
911
912 return _libroar_dir[i].dir;
913}
914
915// codec funcs:
916
917/*
918#define roar_codec2str(x) ((x) == ROAR_CODEC_PCM_S_LE  ? "pcm_s_le"  : (x) == ROAR_CODEC_PCM_S_BE  ? "pcm_s_be"  : \
919                           (x) == ROAR_CODEC_PCM_S_PDP ? "pcm_s_pdp" : (x) == ROAR_CODEC_MIDI_FILE ? "midi_file" : \
920                           "unknown" )
921*/
922
923static struct {
924 int    codec;
925 const char * name;
926 const char * mime;
927} _libroar_codec[] = {
928 // PCM:
929 {ROAR_CODEC_PCM_S_LE,    "pcm_s_le",    NULL},
930 {ROAR_CODEC_PCM_S_BE,    "pcm_s_be",    NULL},
931 {ROAR_CODEC_PCM_S_PDP,   "pcm_s_pdp",   NULL},
932 {ROAR_CODEC_PCM_U_LE,    "pcm_u_le",    NULL},
933 {ROAR_CODEC_PCM_U_BE,    "pcm_u_be",    NULL},
934 {ROAR_CODEC_PCM_U_PDP,   "pcm_u_pdp",   NULL},
935 {ROAR_CODEC_DEFAULT,     "default",     NULL}, // alias
936 {ROAR_CODEC_DEFAULT,     "pcm",         NULL}, // alias
937 {ROAR_CODEC_DEFAULT,     "raw",         NULL}, // alias
938 {ROAR_CODEC_PCM_S,       "pcm_s",       NULL}, // alias
939 {ROAR_CODEC_PCM_U,       "pcm_u",       NULL}, // alias
940
941 // MIDI:
942 {ROAR_CODEC_MIDI_FILE,   "midi_file",   NULL},
943 {ROAR_CODEC_MIDI,        "midi",        NULL},
944 {ROAR_CODEC_ROARMIDI,    "roarmidi",    NULL},
945
946 // XIPH:
947 {ROAR_CODEC_OGG_VORBIS,  "ogg_vorbis",  "application/ogg"},
948 {ROAR_CODEC_OGG_VORBIS,  "vorbis",      "application/ogg"}, // alias
949 {ROAR_CODEC_FLAC,        "flac",        "audio/x-flac"},
950 {ROAR_CODEC_OGG_SPEEX,   "ogg_speex",   "audio/ogg; codecs=speex"},
951 {ROAR_CODEC_OGG_SPEEX,   "speex",       "audio/ogg; codecs=speex"}, // alias
952 {ROAR_CODEC_OGG_FLAC,    "ogg_flac",    "audio/ogg; codecs=flac"},
953 {ROAR_CODEC_OGG_GENERAL, "ogg_general", "application/ogg"},
954 {ROAR_CODEC_OGG_CELT,    "ogg_celt",    "audio/ogg; codecs=celt"},
955 {ROAR_CODEC_OGG,         "ogg",         "application/ogg"},
956 {ROAR_CODEC_OGG_OPUS,    "ogg_opus",    NULL},
957 {ROAR_CODEC_ROAR_OPUS,   "roar_opus",   NULL},
958 {ROAR_CODEC_ROAR_CELT,   "roar_celt",   NULL},
959 {ROAR_CODEC_ROAR_SPEEX,  "roar_speex",  NULL},
960
961 // RAUM:
962 {ROAR_CODEC_RAUM,        "raum",        NULL},
963 {ROAR_CODEC_RAUM_VORBIS, "raum_vorbis", NULL},
964 {ROAR_CODEC_RAUM_FLAC,   "raum_flac",   NULL},
965
966 // RIFF/WAVE like:
967 {ROAR_CODEC_RIFF_WAVE,   "riff_wave",   "audio/x-wav"},
968 {ROAR_CODEC_RIFF_WAVE,   "wave",        "audio/x-wav"}, // alias
969 {ROAR_CODEC_RIFF_WAVE,   "wav",         "audio/x-wav"}, // alias
970 {ROAR_CODEC_RIFX,        "rifx",        NULL},
971 {ROAR_CODEC_AU,          "au",          "audio/basic"},
972 {ROAR_CODEC_AIFF,        "aiff",        "audio/aiff"},
973
974 //Log codecs:
975 {ROAR_CODEC_ALAW,        "alaw",        NULL},
976 {ROAR_CODEC_AUTLAW_LE,   "autlaw_le",   NULL},
977 {ROAR_CODEC_AUTLAW_BE,   "autlaw_be",   NULL},
978 {ROAR_CODEC_AUTLAW,      "autlaw",      NULL}, // alias
979 {ROAR_CODEC_MULAW,       "mulaw",       NULL},
980 {ROAR_CODEC_MULAW,       "ulaw",        NULL}, // alias
981 {ROAR_CODEC_MUUTLAW_LE,  "muutlaw_le",  NULL},
982 {ROAR_CODEC_MUUTLAW_BE,  "muutlaw_be",  NULL},
983 {ROAR_CODEC_MUUTLAW,     "muutlaw",     NULL}, // alias
984
985 //GSM:
986 {ROAR_CODEC_GSM,         "gsm",         NULL},
987 {ROAR_CODEC_GSM49,       "gsm49",       NULL},
988
989 //SPC-700 Bit Rate Reduction of
990 //Super Nintendo Entertainment System (SNES)
991 {ROAR_CODEC_BRR,         "brr",         NULL},
992
993 // Meta Codecs:
994 {ROAR_CODEC_META_VCLT,     "meta_vclt",     NULL},
995 {ROAR_CODEC_META_RALT,     "meta_ralt",     NULL},
996 {ROAR_CODEC_META_RALB,     "meta_ralb",     NULL},
997 {ROAR_CODEC_META_RALB_LE,  "meta_ralb_le",  NULL},
998 {ROAR_CODEC_META_RALB_BE,  "meta_ralb_be",  NULL},
999 {ROAR_CODEC_META_RALB_PDP, "meta_ralb_pdp", NULL},
1000
1001 // light control:
1002 {ROAR_CODEC_DMX512,      "dmx512",      NULL},
1003 {ROAR_CODEC_ROARDMX,     "roardmx",     NULL},
1004
1005 // Radio Data and Transmitter Control System:
1006 {ROAR_CODEC_RDS,         "rds",         NULL},
1007
1008 // User specific:
1009 {ROAR_CODEC_USER0,       "user0",       NULL},
1010 {ROAR_CODEC_USER1,       "user1",       NULL},
1011 {ROAR_CODEC_USER2,       "user2",       NULL},
1012 {ROAR_CODEC_USER3,       "user3",       NULL},
1013 {ROAR_CODEC_USER4,       "user4",       NULL},
1014 {ROAR_CODEC_USER5,       "user5",       NULL},
1015 {ROAR_CODEC_USER6,       "user6",       NULL},
1016 {ROAR_CODEC_USER7,       "user7",       NULL},
1017 {ROAR_CODEC_USER8,       "user8",       NULL},
1018 {ROAR_CODEC_USER9,       "user9",       NULL},
1019 {ROAR_CODEC_USER10,      "user10",      NULL},
1020 {ROAR_CODEC_USER11,      "user11",      NULL},
1021 {ROAR_CODEC_USER12,      "user12",      NULL},
1022 {ROAR_CODEC_USER13,      "user13",      NULL},
1023 {ROAR_CODEC_USER14,      "user14",      NULL},
1024 {ROAR_CODEC_USER15,      "user15",      NULL},
1025 {-1, NULL, NULL}
1026};
1027
1028int roar_str2codec(const char * codec) {
1029 int i;
1030 int guess;
1031
1032 if ( codec == NULL || *codec == 0 )
1033  return ROAR_CODEC_DEFAULT;
1034
1035 if ( (guess = atoi(codec)) > 0 )
1036  return guess;
1037
1038 for (i = 0; _libroar_codec[i].codec != -1; i++)
1039  if ( strcasecmp(_libroar_codec[i].name, codec) == 0 )
1040   return _libroar_codec[i].codec;
1041
1042 roar_err_set(ROAR_ERROR_NOENT);
1043 return -1;
1044}
1045
1046
1047const char * roar_codec2str (const int codec) {
1048 int i;
1049
1050 for (i = 0; _libroar_codec[i].codec != -1; i++)
1051  if ( _libroar_codec[i].codec == codec )
1052   return _libroar_codec[i].name;
1053
1054 return "unknown";
1055}
1056
1057int    roar_mime2codec (const char * mime) {
1058 int i;
1059
1060 if ( mime == NULL ) {
1061  roar_err_set(ROAR_ERROR_FAULT);
1062  return -1;
1063 }
1064
1065 if ( *mime == 0 ) {
1066  roar_err_set(ROAR_ERROR_INVAL);
1067  return -1;
1068 }
1069
1070 for (i = 0; _libroar_codec[i].codec != -1; i++)
1071  if ( _libroar_codec[i].mime != NULL )
1072   if ( strcasecmp(_libroar_codec[i].mime, mime) == 0 )
1073    return _libroar_codec[i].codec;
1074
1075 roar_err_set(ROAR_ERROR_NOENT);
1076 return -1;
1077}
1078
1079const char * roar_codec2mime (const int    codec) {
1080 int i;
1081
1082 for (i = 0; _libroar_codec[i].codec != -1; i++)
1083  if ( _libroar_codec[i].codec == codec )
1084   return _libroar_codec[i].mime;
1085
1086 roar_err_set(ROAR_ERROR_NOENT);
1087 return NULL;
1088}
1089
1090int32_t roar_str2rate(const char * rate) {
1091 struct roar_audio_info info;
1092 int ret;
1093
1094 if ( roar_profile2info(&info, rate) != -1 ) {
1095  return info.rate;
1096 }
1097
1098 ret = atoi(rate);
1099
1100 if ( ret == 0 && rate[0] != '0' ) {
1101  roar_err_set(ROAR_ERROR_NOENT);
1102  return -1;
1103 }
1104
1105 return ret;
1106}
1107
1108int32_t roar_str2bits(const char * bits) {
1109 struct roar_audio_info info;
1110 int ret;
1111
1112 if ( roar_profile2info(&info, bits) != -1 ) {
1113  return info.bits;
1114 }
1115
1116 ret = atoi(bits);
1117
1118 if ( ret == 0 && bits[0] != '0' ) {
1119  roar_err_set(ROAR_ERROR_NOENT);
1120  return -1;
1121 }
1122
1123 return ret;
1124}
1125
1126int32_t roar_str2channels(const char * channels) {
1127 struct roar_audio_info info;
1128 int ret;
1129
1130 if ( !strcasecmp(channels, "mono") ) {
1131  return 1;
1132 } else if ( !strcasecmp(channels, "stereo") ) {
1133  return 2;
1134 }
1135
1136 if ( roar_profile2info(&info, channels) != -1 ) {
1137  return info.channels;
1138 }
1139
1140 ret = atoi(channels);
1141
1142 if ( ret == 0 && channels[0] != '0' ) {
1143  roar_err_set(ROAR_ERROR_NOENT);
1144  return -1;
1145 }
1146
1147 return ret;
1148}
1149
1150
1151const char * roar_streamstate2str(int streamstate) {
1152 switch (streamstate) {
1153  case ROAR_STREAMSTATE_UNUSED:  return "unused";  break;
1154  case ROAR_STREAMSTATE_INITING: return "initing"; break;
1155  case ROAR_STREAMSTATE_NEW:     return "new";     break;
1156  case ROAR_STREAMSTATE_OLD:     return "old";     break;
1157  case ROAR_STREAMSTATE_CLOSING: return "closing"; break;
1158 }
1159
1160 return "unknown";
1161}
1162
1163static struct {
1164 int    role;
1165 const char * name;
1166} _libroar_role[] = {
1167 {ROAR_ROLE_UNKNOWN,          "unknown"         },
1168 {ROAR_ROLE_NONE,             "none"            },
1169 {ROAR_ROLE_MUSIC,            "music"           },
1170 {ROAR_ROLE_VIDEO,            "video"           },
1171 {ROAR_ROLE_GAME,             "game"            },
1172 {ROAR_ROLE_EVENT,            "event"           },
1173 {ROAR_ROLE_BEEP,             "beep"            },
1174 {ROAR_ROLE_PHONE,            "phone"           },
1175 {ROAR_ROLE_BACKGROUND_MUSIC, "background music"},
1176 {ROAR_ROLE_BACKGROUND_MUSIC, "background_music"}, // alias
1177 {ROAR_ROLE_VOICE,            "voice"           },
1178 {ROAR_ROLE_INSTRUMENT,       "instrument"      },
1179 {ROAR_ROLE_RHYTHM,           "rhythm"          },
1180 {ROAR_ROLE_CLICK,            "click",          },
1181 {ROAR_ROLE_MIXED,            "mixed",          },
1182 {-1, NULL}
1183};
1184
1185int    roar_str2role  (const char * role) {
1186 int i;
1187
1188 roar_err_clear();
1189
1190 for (i = 0; _libroar_role[i].name != NULL; i++)
1191  if ( !strcasecmp(_libroar_role[i].name, role) )
1192   return _libroar_role[i].role;
1193
1194 roar_err_set(ROAR_ERROR_NOENT);
1195 return ROAR_ROLE_UNKNOWN;
1196}
1197
1198const char * roar_role2str  (const int    role) {
1199 int i;
1200
1201 for (i = 0; _libroar_role[i].name != NULL; i++)
1202  if ( _libroar_role[i].role == role )
1203   return _libroar_role[i].name;
1204
1205 return "unknown";
1206}
1207
1208ssize_t roar_info2samplesize (struct roar_audio_info * info) {
1209 if ( info == NULL ) {
1210  roar_err_set(ROAR_ERROR_FAULT);
1211  return -1;
1212 }
1213
1214 switch (info->codec) {
1215  case ROAR_CODEC_PCM_S_LE:
1216  case ROAR_CODEC_PCM_S_BE:
1217  case ROAR_CODEC_PCM_S_PDP:
1218  case ROAR_CODEC_PCM_U_LE:
1219  case ROAR_CODEC_PCM_U_BE:
1220  case ROAR_CODEC_PCM_U_PDP:
1221    return info->bits;
1222   break;
1223  case ROAR_CODEC_ALAW:
1224  case ROAR_CODEC_MULAW:
1225    return 8;
1226   break;
1227  case ROAR_CODEC_DMX512:
1228    return 8;
1229   break;
1230  case ROAR_CODEC_RDS:
1231    return 26;
1232   break;
1233  default:
1234    roar_err_set(ROAR_ERROR_INVAL);
1235    return -1;
1236   break;
1237 }
1238}
1239
1240ssize_t roar_info2framesize  (struct roar_audio_info * info) {
1241 ssize_t ret = roar_info2samplesize(info);
1242
1243 if ( ret == -1 )
1244  return -1;
1245
1246 ret *= info->channels;
1247
1248 return ret;
1249}
1250
1251ssize_t roar_info2bitspersec(struct roar_audio_info * info) {
1252 ssize_t ret = roar_info2samplesize(info);
1253
1254 if ( ret == -1 )
1255  return -1;
1256
1257 ret *= info->channels * info->rate;
1258
1259 return ret;
1260}
1261
1262static struct {
1263 const char * name;
1264 struct roar_audio_info info;
1265} _libroar_aiprofiles[] = {
1266 {"default",   {.rate     = ROAR_RATE_DEFAULT,
1267                .bits     = ROAR_BITS_DEFAULT,
1268                .channels = ROAR_CHANNELS_DEFAULT,
1269                .codec    = ROAR_CODEC_DEFAULT}},
1270 {"default-server",
1271               {.rate     = ROAR_RATE_DEFAULT,
1272                .bits     = ROAR_ROARD_BITS,
1273                .channels = ROAR_CHANNELS_DEFAULT,
1274                .codec    = ROAR_CODEC_DEFAULT}},
1275 {"wav",       {.rate     = ROAR_RATE_DEFAULT,
1276                .bits     = ROAR_BITS_DEFAULT,
1277                .channels = ROAR_CHANNELS_DEFAULT,
1278                .codec    = ROAR_CODEC_RIFF_WAVE}},
1279 {"au",        {.rate     = ROAR_RATE_DEFAULT,
1280                .bits     = ROAR_BITS_DEFAULT,
1281                .channels = ROAR_CHANNELS_DEFAULT,
1282                .codec    = ROAR_CODEC_AU}},
1283 {"cd",        {.rate =  44100, .bits = 16, .channels =  2, .codec = ROAR_CODEC_DEFAULT}},
1284 {"cdr",       {.rate =  44100, .bits = 16, .channels =  2, .codec = ROAR_CODEC_PCM_S_BE}},
1285 {"dat",       {.rate =  48000, .bits = 16, .channels =  2, .codec = ROAR_CODEC_PCM_S_LE}},
1286 {"isdn-eu",   {.rate =   8000, .bits =  8, .channels =  1, .codec = ROAR_CODEC_ALAW}},
1287 {"isdn-na",   {.rate =   8000, .bits =  8, .channels =  1, .codec = ROAR_CODEC_MULAW}},
1288 {"speex-nb",  {.rate =   8000, .bits = 16, .channels =  2, .codec = ROAR_CODEC_ROAR_SPEEX}},
1289 {"speex-wb",  {.rate =  16000, .bits = 16, .channels =  2, .codec = ROAR_CODEC_ROAR_SPEEX}},
1290 {"speex-uwb", {.rate =  32000, .bits = 16, .channels =  2, .codec = ROAR_CODEC_ROAR_SPEEX}},
1291 {"opus",      {.rate =  48000, .bits = 16, .channels =  2, .codec = ROAR_CODEC_ROAR_OPUS}},
1292 {"gsm",       {.rate =   8000, .bits = 16, .channels =  1, .codec = ROAR_CODEC_GSM}},
1293 {"brr",       {.rate =   8000, .bits = 32, .channels =  1, .codec = ROAR_CODEC_BRR}},
1294 {"brr6k",     {.rate =   6000, .bits = 32, .channels =  1, .codec = ROAR_CODEC_BRR}},
1295 {"rds",       {.rate =      0, .bits =  0, .channels =  0, .codec = ROAR_CODEC_RDS}},
1296 {"midi",      {.rate =      0, .bits =  8, .channels = 16, .codec = ROAR_CODEC_MIDI}},
1297 {"dmx512",    {.rate =      0, .bits =  8, .channels =  0, .codec = ROAR_CODEC_DMX512}},
1298 {NULL,        {.rate =      0, .bits =  0, .channels =  0, .codec = 0}}
1299};
1300
1301int     roar_profile2info    (struct roar_audio_info * info, const char * profile) {
1302 int i;
1303
1304 if ( info == NULL || profile == NULL ) {
1305  roar_err_set(ROAR_ERROR_FAULT);
1306  return -1;
1307 }
1308
1309 for (i = 0; _libroar_aiprofiles[i].name != NULL; i++) {
1310  if ( !strcasecmp(_libroar_aiprofiles[i].name, profile) ) {
1311   memcpy(info, &(_libroar_aiprofiles[i].info), sizeof(struct roar_audio_info));
1312   return 0;
1313  }
1314 }
1315
1316 roar_err_set(ROAR_ERROR_NOENT);
1317 return -1;
1318}
1319
1320ssize_t   roar_profiles_list   (const char ** list, size_t len, size_t offset) {
1321 size_t i;
1322 int idx = 0;
1323
1324 if ( list == NULL ) {
1325  roar_err_set(ROAR_ERROR_FAULT);
1326  return -1;
1327 }
1328
1329 if ( len == 0 )
1330  return 0;
1331
1332 if ( offset >= (sizeof(_libroar_aiprofiles)/sizeof(*_libroar_aiprofiles)) )
1333  return 0;
1334
1335 for (i = offset; _libroar_aiprofiles[i].name != NULL; i++) {
1336  list[idx++] = _libroar_aiprofiles[i].name;
1337 }
1338
1339 return idx;
1340}
1341
1342//ll
Note: See TracBrowser for help on using the repository browser.