source: roaraudio/libroar/vs.c @ 4719:a10929cd0665

Last change on this file since 4719:a10929cd0665 was 4719:a10929cd0665, checked in by phi, 13 years ago

some basic latency control

File size: 31.1 KB
Line 
1//vs.c:
2
3/*
4 *      Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2010-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
38#if defined(ROAR_HAVE_GETSOCKOPT) && defined(ROAR_HAVE_SETSOCKOPT)
39#define _HAVE_SOCKOPT
40#endif
41
42#define FLAG_NONE      0x0000
43#define FLAG_STREAM    0x0001
44#define FLAG_NONBLOCK  0x0002
45#define FLAG_BUFFERED  0x0004
46#define FLAG_CLOSEFILE 0x0008
47#define FLAG_DIR_IN    0x1000
48#define FLAG_DIR_OUT   0x2000
49
50#define _initerr()  do { errno = 0; roar_err_clear(); } while(0)
51#define _seterr(x)  do { if ( error != NULL ) *error = (x); ROAR_DBG("roar_vs_*(*): *error=%s(%i)", roar_vs_strerr((x)), (x)); } while(0)
52#define _seterrre() do { _seterr(roar_errno); } while(0)
53#define _seterrse() do { roar_err_from_errno(); _seterr(roar_errno); } while(0)
54#define _ckvss(ret) do { if ( vss == NULL ) { _seterr(ROAR_ERROR_INVAL); return (ret); } } while(0)
55
56struct roar_vs {
57 int flags;
58 struct roar_connection con_store;
59 struct roar_connection * con;
60 struct roar_stream       stream;
61 struct roar_vio_calls    vio;
62 struct roar_audio_info   info;
63 size_t                   readc, writec;
64 int                      mixerid;
65 int                      first_primid;
66 struct roar_buffer     * readbuffer, * writebuffer;
67 struct roar_vio_calls    file_store;
68 struct roar_vio_calls  * file;
69 struct roar_buffer     * readring, * writering;
70#ifdef _HAVE_SOCKOPT
71 struct {
72  float target;
73  float window;
74  float minlag;
75  float p;
76 } latc;
77#endif
78};
79
80static int _roar_vs_find_first_prim(roar_vs_t * vss);
81
82const char * roar_vs_strerr(int error) {
83 const struct {
84  int err;
85  const char * msg;
86 } msgs[] = {
87  {ROAR_ERROR_NONE,        "No error"},
88  {ROAR_ERROR_PERM,        "Operation not permitted"},
89  {ROAR_ERROR_NOENT,       "No such object, file or directory"},
90  {ROAR_ERROR_BADMSG,      "Bad message"},
91  {ROAR_ERROR_BUSY,        "Device or resource busy"},
92  {ROAR_ERROR_CONNREFUSED, "Connection refused"},
93  {ROAR_ERROR_NOSYS,       "Function not implemented"},
94  {ROAR_ERROR_NOTSUP,      "Operation not supported"},
95  {ROAR_ERROR_PIPE,        "Broken pipe"},
96  {ROAR_ERROR_PROTO,       "Protocol error"},
97  {ROAR_ERROR_RANGE,       "Result too large or parameter out of range"},
98  {ROAR_ERROR_MSGSIZE,     "Message too long"},
99  {ROAR_ERROR_NOMEM,       "Not enough space"},
100  {ROAR_ERROR_INVAL,       "Invalid argument"},
101  {-1, NULL}
102 };
103 int i;
104
105 for (i = 0; msgs[i].msg != NULL; i++)
106  if ( msgs[i].err == error )
107   return msgs[i].msg;
108
109 return "(unknown)";
110}
111
112static roar_vs_t * roar_vs_init(int * error) {
113 roar_vs_t * vss = roar_mm_malloc(sizeof(roar_vs_t));
114
115 if ( vss == NULL ) {
116  _seterrse();
117  return NULL;
118 }
119
120 memset(vss, 0, sizeof(roar_vs_t));
121
122 vss->mixerid      = -1;
123 vss->first_primid = -1;
124
125#ifdef _HAVE_SOCKOPT
126 vss->latc.target = -2.; // must be less than latc.minlag!
127 vss->latc.window = -1.;
128 vss->latc.minlag = -1.;
129 vss->latc.p      =  0.005;
130#endif
131
132 return vss;
133}
134
135roar_vs_t * roar_vs_new_from_con(struct roar_connection * con, int * error) {
136 roar_vs_t * vss = roar_vs_init(error);
137
138 if ( vss == NULL )
139  return NULL;
140
141 vss->con = con;
142
143 return vss;
144}
145
146roar_vs_t * roar_vs_new(const char * server, const char * name, int * error) {
147 roar_vs_t * vss = roar_vs_init(error);
148 int ret;
149
150 if ( vss == NULL )
151  return NULL;
152
153 vss->con = &(vss->con_store);
154
155 _initerr();
156
157 ret = roar_simple_connect(vss->con, (char*)server, (char*)name);
158
159 if ( ret == -1 ) {
160  roar_vs_close(vss, ROAR_VS_TRUE, NULL);
161  _seterrre();
162  return NULL;
163 }
164
165 return vss;
166}
167
168int roar_vs_stream(roar_vs_t * vss, const struct roar_audio_info * info, int dir, int * error) {
169 struct roar_stream_info sinfo;
170 int ret;
171
172 _ckvss(-1);
173
174 if ( vss->flags & FLAG_STREAM ) {
175  _seterr(ROAR_ERROR_INVAL);
176  return -1;
177 }
178
179 _initerr();
180
181 if ( info != &(vss->info) )
182  memcpy(&(vss->info), info, sizeof(struct roar_audio_info));
183
184 ret = roar_vio_simple_new_stream_obj(&(vss->vio), vss->con, &(vss->stream),
185                                      info->rate, info->channels, info->bits, info->codec,
186                                      dir
187                                     );
188
189 if ( ret == -1 ) {
190  _seterrre();
191  return -1;
192 }
193
194 if ( roar_stream_get_info(vss->con, &(vss->stream), &sinfo) != -1 ) {
195  // TODO: fix this:
196  // as we currently do not support to select mixer we just check if we hit the
197  // right one.
198  if ( vss->mixerid != -1 && vss->mixerid != sinfo.mixer ) {
199   _seterr(ROAR_ERROR_INVAL); // TODO: should we maybe use a diffrent value?
200   roar_vio_close(&(vss->vio));
201   return -1;
202  }
203
204  vss->mixerid = sinfo.mixer;
205  _roar_vs_find_first_prim(vss);
206 }
207
208 vss->flags |= FLAG_STREAM;
209
210 switch (dir) {
211  case ROAR_DIR_PLAY: vss->flags |= FLAG_DIR_OUT; break;
212 }
213
214 return 0;
215}
216
217roar_vs_t * roar_vs_new_simple(const char * server, const char * name, int rate, int channels, int codec, int bits, int dir, int * error) {
218 roar_vs_t * vss = roar_vs_new(server, name, error);
219 int ret;
220
221 if (vss == NULL)
222  return NULL;
223
224 memset(&(vss->info), 0, sizeof(vss->info));
225
226 vss->info.rate     = rate;
227 vss->info.channels = channels;
228 vss->info.codec    = codec;
229 vss->info.bits     = bits;
230
231 ret = roar_vs_stream(vss, &(vss->info), dir, error);
232
233 if (ret == -1) {
234  roar_vs_close(vss, ROAR_VS_TRUE, NULL);
235  return NULL;
236 }
237
238 return vss;
239}
240
241int roar_vs_file(roar_vs_t * vss, struct roar_vio_calls * vio, int closefile, int * error) {
242 _ckvss(-1);
243
244 if ( vio == NULL || (closefile != ROAR_VS_TRUE && closefile != ROAR_VS_FALSE)) {
245  _seterr(ROAR_ERROR_INVAL);
246  return -1;
247 }
248
249 if ( vss->file != NULL ) {
250  _seterr(ROAR_ERROR_INVAL);
251  return -1;
252 }
253
254 vss->file = vio;
255 if ( closefile == ROAR_VS_TRUE )
256  vss->flags |= FLAG_CLOSEFILE;
257
258 return 0;
259}
260
261int roar_vs_file_simple(roar_vs_t * vss, char * filename, int * error) {
262 struct roar_vio_defaults def;
263 struct roar_vio_calls * file;
264 char buf[64];
265 ssize_t ret;
266 int dir = O_RDONLY;
267 int codec = -1;
268 const char * content_type;
269
270 _ckvss(-1);
271
272 if ( vss->file != NULL ) {
273  _seterr(ROAR_ERROR_INVAL);
274  return -1;
275 }
276
277 if ( vss->flags & FLAG_STREAM ) {
278  switch (vss->flags & (FLAG_DIR_IN|FLAG_DIR_OUT)) {
279   case FLAG_DIR_IN:  dir = O_WRONLY; break;
280   case FLAG_DIR_OUT: dir = O_RDONLY; break;
281   case FLAG_DIR_IN|FLAG_DIR_OUT: dir = O_RDWR; break;
282   default:
283     _seterr(ROAR_ERROR_INVAL);
284     return -1;
285  }
286 }
287
288 file = &(vss->file_store);
289
290 _initerr();
291
292 if ( roar_vio_dstr_init_defaults(&def, ROAR_VIO_DEF_TYPE_NONE, dir, 0644) == -1 ) {
293  _seterrre();
294  return -1;
295 }
296
297 if ( roar_vio_open_dstr(file, filename, &def, 1) == -1 ) {
298  _seterrre();
299  return -1;
300 }
301
302 if ( !(vss->flags & FLAG_STREAM) ) {
303  if ( roar_vio_ctl(file, ROAR_VIO_CTL_GET_MIMETYPE, &content_type) != -1 ) {
304   codec = roar_mime2codec(content_type);
305  }
306
307  if ( codec == -1 ) {
308   ret = roar_vio_read(file, buf, sizeof(buf));
309
310   codec = roar_file_codecdetect(buf, ret);
311
312   if ( codec == -1 ) {
313    roar_vio_close(file);
314    _seterr(ROAR_ERROR_INVAL); // Other value?
315    return -1;
316   }
317
318   if ( roar_vio_lseek(file, 0, SEEK_SET) != 0 ) {
319    roar_vio_close(file);
320   _seterrre();
321    return -1;
322   }
323  }
324
325  memset(&(vss->info), 0, sizeof(vss->info));
326
327  vss->info.rate     = ROAR_RATE_DEFAULT;
328  vss->info.channels = ROAR_CHANNELS_DEFAULT;
329  vss->info.codec    = codec;
330  vss->info.bits     = ROAR_BITS_DEFAULT;
331
332  ret = roar_vs_stream(vss, &(vss->info), ROAR_DIR_PLAY, error);
333
334  if ( ret == -1 ) {
335   roar_vio_close(file);
336   return -1;
337  }
338 }
339
340 if ( roar_vs_file(vss, file, ROAR_VS_TRUE, error) == -1 ) {
341  roar_vio_close(file);
342  return -1;
343 }
344
345 return 0;
346}
347
348roar_vs_t * roar_vs_new_from_file(const char * server, const char * name, char * filename, int * error) {
349 roar_vs_t * vss = roar_vs_new(server, name, error);
350
351 if ( vss == NULL )
352  return NULL;
353
354 if ( roar_vs_file_simple(vss, filename, error) != 0 ) {
355  roar_vs_close(vss, ROAR_VS_TRUE, NULL);
356  return NULL;
357 }
358
359 return vss;
360}
361
362int roar_vs_buffer(roar_vs_t * vss, size_t buffer, int * error) {
363 _ckvss(-1);
364
365 if ( vss->flags & FLAG_BUFFERED )
366  return -1;
367
368 if ( roar_buffer_ring_new(&(vss->readring), buffer, 0) == -1 ) {
369  _seterrre();
370  return -1;
371 }
372
373 if ( roar_buffer_ring_new(&(vss->writering), buffer, 0) == -1 ) {
374  _seterrre();
375  roar_buffer_free(vss->readring);
376  vss->readring = NULL;
377  return -1;
378 }
379
380 vss->flags |= FLAG_BUFFERED;
381
382 return 0;
383}
384
385int roar_vs_close(roar_vs_t * vss, int killit, int * error) {
386 if ( killit != ROAR_VS_TRUE && killit != ROAR_VS_FALSE ) {
387  _seterr(ROAR_ERROR_INVAL);
388  return -1;
389 }
390
391 _ckvss(-1);
392
393 if ( vss->readbuffer != NULL )
394  roar_buffer_free(vss->readbuffer);
395 if ( vss->writebuffer != NULL )
396  roar_buffer_free(vss->writebuffer);
397
398 if ( vss->readring != NULL )
399  roar_buffer_free(vss->readring);
400 if ( vss->writering != NULL )
401  roar_buffer_free(vss->writering);
402
403 if ( vss->file != NULL && vss->flags & FLAG_CLOSEFILE )
404  roar_vio_close(vss->file);
405
406 if ( vss->flags & FLAG_STREAM ) {
407  if ( killit == ROAR_VS_TRUE ) {
408   roar_kick(vss->con, ROAR_OT_STREAM, roar_stream_get_id(&(vss->stream)));
409  }
410
411  roar_vio_close(&(vss->vio));
412 }
413
414 if ( vss->con == &(vss->con_store) ) {
415  roar_disconnect(vss->con);
416 }
417
418 roar_mm_free(vss);
419 return 0;
420}
421
422static ssize_t roar_vs_write_direct(roar_vs_t * vss, const void * buf, size_t len, int * error) {
423 ssize_t ret = roar_vio_write(&(vss->vio), (void*)buf, len);
424
425 if ( ret == -1 ) {
426#ifdef EAGAIN
427  if ( errno == EAGAIN )
428   return 0;
429#endif
430
431#ifdef EWOULDBLOCK
432  if ( errno == EWOULDBLOCK )
433   return 0;
434#endif
435
436  _seterrre();
437 } else {
438  if ( !(vss->flags & FLAG_BUFFERED) ) {
439   //printf("A: vss->writec=%zu, ret=%zi\n", vss->writec, ret);
440   vss->writec += ret;
441  }
442 }
443
444 //printf("B: vss->writec=%zu, ret=%zi\n", vss->writec, ret);
445 return ret;
446}
447
448ssize_t roar_vs_write(roar_vs_t * vss, const void * buf, size_t len, int * error) {
449 ssize_t ret;
450 size_t writelen;
451
452 _ckvss(-1);
453
454 if ( !(vss->flags & FLAG_STREAM) ) {
455  _seterr(ROAR_ERROR_INVAL);
456  return -1;
457 }
458
459 _initerr();
460
461 if ( vss->flags & FLAG_BUFFERED ) {
462  writelen = len;
463
464  if ( roar_buffer_ring_write(vss->writering, (void*)buf, &writelen) == -1 ) {
465   _seterrre();
466   return -1;
467  }
468
469  ret = writelen;
470  vss->writec += ret;
471 } else {
472  ret = roar_vs_write_direct(vss, buf, len, error);
473 }
474
475 return ret;
476}
477
478ssize_t roar_vs_read (roar_vs_t * vss,       void * buf, size_t len, int * error) {
479 ssize_t ret;
480
481 _ckvss(-1);
482
483 if ( !(vss->flags & FLAG_STREAM) ) {
484  _seterr(ROAR_ERROR_INVAL);
485  return -1;
486 }
487
488 _initerr();
489
490 ret = roar_vio_read(&(vss->vio), buf, len);
491
492 if ( ret == -1 ) {
493  _seterrre();
494 } else {
495  vss->readc += ret;
496 }
497
498 return ret;
499}
500
501int     roar_vs_sync (roar_vs_t * vss, int wait, int * error) {
502 struct roar_event waits, triggered;
503
504 _ckvss(-1);
505
506 if ( !(vss->flags & FLAG_STREAM) ) {
507  _seterr(ROAR_ERROR_INVAL);
508  return -1;
509 }
510
511 if ( wait != ROAR_VS_NOWAIT && wait != ROAR_VS_WAIT ) {
512  _seterr(ROAR_ERROR_INVAL);
513  return -1;
514 }
515
516 _initerr();
517
518 if ( roar_vio_sync(&(vss->vio)) == -1 ) {
519  _seterrre();
520  return -1;
521 }
522
523 if ( wait == ROAR_VS_WAIT ) {
524  memset(&waits, 0, sizeof(waits));
525  waits.event       = ROAR_OE_STREAM_XRUN;
526  waits.emitter     = -1;
527  waits.target      = roar_stream_get_id(&(vss->stream));
528  waits.target_type = ROAR_OT_STREAM;
529
530  if ( roar_wait(vss->con, &triggered, &waits, 1) == -1 ) {
531   _seterrre();
532   return -1;
533  }
534 }
535
536 return 0;
537}
538
539int     roar_vs_blocking (roar_vs_t * vss, int val, int * error) {
540 int old = -1;
541
542 _ckvss(-1);
543
544  if ( !(vss->flags & FLAG_STREAM) ) {
545  _seterr(ROAR_ERROR_INVAL);
546  return -1;
547 }
548
549 old = vss->flags & FLAG_NONBLOCK ? ROAR_VS_FALSE : ROAR_VS_TRUE;
550
551 _initerr();
552
553 switch (val) {
554  case ROAR_VS_TRUE:
555    if ( roar_vio_nonblock(&(vss->vio), ROAR_SOCKET_BLOCK) == -1 ) {
556     _seterrre();
557     return -1;
558    }
559    vss->flags |= FLAG_NONBLOCK;
560    vss->flags -= FLAG_NONBLOCK;
561    return old;
562   break;
563  case ROAR_VS_FALSE:
564    if ( roar_vio_nonblock(&(vss->vio), ROAR_SOCKET_NONBLOCK) == -1 ) {
565     _seterrre();
566     return -1;
567    }
568    vss->flags |= FLAG_NONBLOCK;
569    return old;
570   break;
571  case ROAR_VS_TOGGLE:
572    if ( old == ROAR_VS_TRUE ) {
573     return roar_vs_blocking(vss, ROAR_VS_FALSE, error);
574    } else {
575     return roar_vs_blocking(vss, ROAR_VS_TRUE, error);
576    }
577   break;
578  case ROAR_VS_ASK:
579    return old;
580   break;
581 }
582
583 _seterr(ROAR_ERROR_INVAL);
584 return -1;
585}
586
587static int _roar_vs_find_first_prim(roar_vs_t * vss) {
588 struct roar_stream stream;
589 struct roar_stream_info info;
590 int id[ROAR_STREAMS_MAX];
591 int num;
592 int i;
593
594 if ( vss->first_primid != -1 )
595  return vss->first_primid;
596
597 if ( vss->mixerid == -1 )
598  return -1;
599
600 if ( (num = roar_list_streams(vss->con, id, ROAR_STREAMS_MAX)) == -1 ) {
601  return -1;
602 }
603
604 for (i = 0; i < num; i++) {
605  if ( roar_get_stream(vss->con, &stream, id[i]) == -1 )
606   continue;
607
608  if ( stream.dir != ROAR_DIR_OUTPUT )
609   continue;
610
611  if ( roar_stream_get_info(vss->con, &stream, &info) == -1 )
612   continue;
613
614  if ( info.mixer == vss->mixerid ) {
615   vss->first_primid = id[i];
616   return id[i];
617  }
618 }
619
620 return -1;
621}
622
623ssize_t roar_vs_position(roar_vs_t * vss, int backend, int * error) {
624 struct roar_stream stream;
625 struct roar_stream      out_stream;
626 struct roar_stream_info out_info;
627 size_t offset = 0;
628
629 _ckvss(-1);
630
631 if ( !(vss->flags & FLAG_STREAM) ) {
632  _seterr(ROAR_ERROR_INVAL);
633  return -1;
634 }
635
636 _initerr();
637
638 if ( roar_get_stream(vss->con, &stream, roar_stream_get_id(&(vss->stream))) == -1 ) {
639  _seterrre();
640  return -1;
641 }
642
643 if ( backend == ROAR_VS_BACKEND_DEFAULT ) {
644  backend = ROAR_VS_BACKEND_FIRST;
645 }
646
647 switch (backend) {
648  case ROAR_VS_BACKEND_NONE:
649    return stream.pos;
650   break;
651  case ROAR_VS_BACKEND_FIRST:
652    if ( vss->first_primid == -1 ) {
653     _seterr(ROAR_ERROR_UNKNOWN);
654     return -1;
655    }
656
657    backend = vss->first_primid;
658   break;
659  default:
660    if ( backend < 0 ) {
661     _seterr(ROAR_ERROR_INVAL);
662     return -1;
663    }
664   break;
665 }
666
667
668 if ( backend >= 0 ) {
669  roar_stream_new_by_id(&out_stream, backend);
670
671  if ( roar_stream_get_info(vss->con, &out_stream, &out_info) == -1 ) {
672   _seterrre();
673   return -1;
674  }
675
676  offset  = out_info.delay * vss->info.rate;
677  offset /= 1000000;
678 }
679
680 return stream.pos + offset;
681}
682
683#ifdef _HAVE_SOCKOPT
684static void roar_vs_latency_managed(roar_vs_t * vss, roar_mus_t lat) {
685 struct roar_vio_sysio_sockopt sockopt;
686 float tmp = ((float)lat/1000.0) - vss->latc.target;
687 int val;
688
689 tmp *= vss->latc.p;
690
691 sockopt.level   = SOL_SOCKET;
692 sockopt.optname = SO_SNDBUF;
693 sockopt.optval  = &val;
694 sockopt.optlen  = sizeof(val);
695
696 roar_vio_ctl(&(vss->vio), ROAR_VIO_CTL_GET_SYSIO_SOCKOPT, &sockopt);
697
698 val /= 2;
699
700 tmp = 1.0 - tmp;
701
702 val = (float)val*tmp;
703
704 sockopt.optlen  = sizeof(val);
705
706 roar_vio_ctl(&(vss->vio), ROAR_VIO_CTL_SET_SYSIO_SOCKOPT, &sockopt);
707}
708#endif
709
710roar_mus_t roar_vs_latency(roar_vs_t * vss, int backend, int * error) {
711 ssize_t pos  = roar_vs_position(vss, backend, error);
712 ssize_t bps;  // byte per sample
713 size_t  lioc; // local IO (byte) counter
714 size_t  lpos; // local possition
715 signed long long int lag;
716
717// printf("pos=%zi\n", pos);
718
719 _initerr();
720
721 _ckvss(-1);
722
723 if (pos == -1) {
724  _seterrre();
725  return 0;
726 }
727
728 if ( !(vss->flags & FLAG_STREAM) ) {
729  _seterr(ROAR_ERROR_INVAL);
730  return 0;
731 }
732
733 if ( vss->writec == 0 ) {
734  lioc = vss->readc;
735 } else {
736  //printf("writec=%zu\n", vss->writec);
737  lioc = vss->writec;
738 }
739
740 bps = roar_info2samplesize(&(vss->info));
741
742 if ( bps == -1 ) {
743  _seterrre();
744  return 0;
745 }
746
747 lpos = (lioc*8) / bps;
748
749 //printf("pos=%zi, lpos=%zi, bps=%zi, diff[lpos-pos]=%zi\n", pos, lpos, bps, (lpos - pos));
750
751 lag = (signed long long int)lpos - (signed long long int)pos;
752 lag /= vss->info.channels;
753
754 // we now have the lag in frames
755 // return value are mus
756 // so we need to multiply with 1s/mus and
757 // multiply by 1/rate
758
759 lag *= 1000000; // 1s/mus
760 lag /= vss->info.rate;
761
762 if ( lag == 0 ) {
763  _seterr(ROAR_ERROR_NONE);
764 }
765
766#ifdef _HAVE_SOCKOPT
767 if (vss->latc.target > vss->latc.minlag) {
768  roar_vs_latency_managed(vss, lag);
769 }
770#endif
771
772 return lag;
773}
774
775static int roar_vs_flag(roar_vs_t * vss, int flag, int val, int * error) {
776 struct roar_stream_info info;
777 int old = -1;
778
779 _ckvss(-1);
780
781 if ( !(vss->flags & FLAG_STREAM) ) {
782  _seterr(ROAR_ERROR_INVAL);
783  return -1;
784 }
785
786 if ( val != ROAR_VS_ASK )
787  old = roar_vs_flag(vss, flag, ROAR_VS_ASK, error);
788
789 _initerr();
790
791 switch (val) {
792  case ROAR_VS_TRUE:
793  case ROAR_VS_FALSE:
794    if ( roar_stream_set_flags(vss->con, &(vss->stream), flag,
795                               val == ROAR_VS_TRUE ? ROAR_SET_FLAG : ROAR_RESET_FLAG) == -1 ) {
796     _seterrre();
797     return -1;
798    }
799    return old;
800   break;
801  case ROAR_VS_TOGGLE:
802    return roar_vs_flag(vss, flag, old == ROAR_VS_TRUE ? ROAR_VS_FALSE : ROAR_VS_TRUE, error);
803   break;
804  case ROAR_VS_ASK:
805    if ( roar_stream_get_info(vss->con, &(vss->stream), &info) == -1 ) {
806     _seterrre();
807     return -1;
808    }
809    return info.flags & flag ? ROAR_VS_TRUE : ROAR_VS_FALSE;
810   break;
811 }
812
813 _seterr(ROAR_ERROR_INVAL);
814 return -1;
815}
816
817int     roar_vs_pause(roar_vs_t * vss, int val, int * error) {
818 return roar_vs_flag(vss, ROAR_FLAG_PAUSE, val, error);
819}
820
821int     roar_vs_mute (roar_vs_t * vss, int val, int * error) {
822 return roar_vs_flag(vss, ROAR_FLAG_MUTE, val, error);
823}
824
825static int roar_vs_volume (roar_vs_t * vss, float * c, size_t channels, int * error) {
826 struct roar_mixer_settings mixer;
827 size_t i;
828 register float s;
829 int oldchannels;
830 int handled;
831
832 _ckvss(-1);
833
834 if ( !(vss->flags & FLAG_STREAM) ) {
835  _seterr(ROAR_ERROR_INVAL);
836  return -1;
837 }
838
839 if ( channels > ROAR_MAX_CHANNELS ) {
840  _seterr(ROAR_ERROR_INVAL);
841  return -1;
842 }
843
844 _initerr();
845
846 if ( roar_get_vol(vss->con, roar_stream_get_id(&(vss->stream)), &mixer, &oldchannels) == -1 ) {
847  _seterrre();
848  return -1;
849 }
850
851 for (i = 0; i < channels; i++) {
852  s = c[i] * 65535.0;
853  if ( s > 66190.0 || s < -655.0 ) {
854   _seterr(ROAR_ERROR_RANGE);
855   return -1;
856  } else if ( s > 65535.0 ) {
857   s = 65535.0;
858  } else if ( s <     0.0 ) {
859   s = 0.0;
860  }
861  mixer.mixer[i] = s;
862 }
863
864 mixer.scale = 65535;
865
866 if ( channels != oldchannels ) {
867  handled = 0;
868  switch (oldchannels) {
869   case 1:
870     if ( channels == 2 ) {
871      mixer.mixer[0] = (mixer.mixer[0] + mixer.mixer[1]) / 2;
872      handled = 1;
873     }
874    break;
875   case 2:
876     if ( channels == 1 ) {
877      mixer.mixer[1] = mixer.mixer[0];
878      handled = 1;
879     }
880    break;
881   case 4:
882     if ( channels == 1 ) {
883      mixer.mixer[1] = mixer.mixer[0];
884      mixer.mixer[2] = mixer.mixer[0];
885      mixer.mixer[3] = mixer.mixer[0];
886      handled = 1;
887     } else if ( channels == 2 ) {
888      mixer.mixer[2] = mixer.mixer[0];
889      mixer.mixer[3] = mixer.mixer[1];
890      handled = 1;
891     }
892    break;
893  }
894  if ( handled ) {
895   channels = oldchannels;
896  } else {
897   _seterr(ROAR_ERROR_INVAL);
898   return -1;
899  }
900 }
901
902 if ( roar_set_vol(vss->con, roar_stream_get_id(&(vss->stream)), &mixer, channels) == -1 ) {
903  _seterrre();
904  return -1;
905 }
906
907 return 0;
908}
909
910int     roar_vs_volume_mono   (roar_vs_t * vss, float c, int * error) {
911 return roar_vs_volume(vss, &c, 1, error);
912}
913
914int     roar_vs_volume_stereo (roar_vs_t * vss, float l, float r, int * error) {
915 float c[2] = {l, r};
916 return roar_vs_volume(vss, c, 2, error);
917}
918
919int     roar_vs_volume_get    (roar_vs_t * vss, float * l, float * r, int * error) {
920 struct roar_mixer_settings mixer;
921 int channels;
922
923 if ( vss == NULL || l == NULL || r == NULL ) {
924  _seterr(ROAR_ERROR_INVAL);
925  return -1;
926 }
927
928 if ( !(vss->flags & FLAG_STREAM) ) {
929  _seterr(ROAR_ERROR_INVAL);
930  return -1;
931 }
932
933 _initerr();
934
935 if ( roar_get_vol(vss->con, roar_stream_get_id(&(vss->stream)), &mixer, &channels) == -1 ) {
936  _seterrre();
937  return -1;
938 }
939
940 if ( channels == 1 )
941  mixer.mixer[1] = mixer.mixer[0];
942
943 *l = mixer.mixer[0] / (float)mixer.scale;
944 *r = mixer.mixer[1] / (float)mixer.scale;
945
946 return 0;
947}
948
949int     roar_vs_meta          (roar_vs_t * vss, struct roar_keyval * kv, size_t len, int * error) {
950 struct roar_meta meta;
951 size_t i;
952 int type;
953 int ret = 0;
954
955 _ckvss(-1);
956
957 if ( !(vss->flags & FLAG_STREAM) ) {
958  _seterr(ROAR_ERROR_INVAL);
959  return -1;
960 }
961
962 meta.type   = ROAR_META_TYPE_NONE;
963 meta.key[0] = 0;
964 meta.value  = NULL;
965
966 _initerr();
967
968 if ( roar_stream_meta_set(vss->con, &(vss->stream), ROAR_META_MODE_CLEAR, &meta) == -1 ) {
969  _seterrre();
970  ret = -1;
971 }
972
973 for (i = 0; i < len; i++) {
974  type = roar_meta_inttype(kv[i].key);
975  meta.type  = type;
976  meta.value = kv[i].value;
977
978  if ( roar_stream_meta_set(vss->con, &(vss->stream), ROAR_META_MODE_ADD, &meta) == -1 ) {
979   _seterrre();
980   ret = -1;
981  }
982 }
983
984 meta.type   = ROAR_META_TYPE_NONE;
985 meta.key[0] = 0;
986 meta.value  = NULL;
987 if ( roar_stream_meta_set(vss->con, &(vss->stream), ROAR_META_MODE_FINALIZE, &meta) == -1 ) {
988  _seterrre();
989  ret = -1;
990 }
991
992 return ret;
993}
994
995int     roar_vs_role          (roar_vs_t * vss, int role, int * error) {
996 _ckvss(-1);
997
998 if ( !(vss->flags & FLAG_STREAM) ) {
999  _seterr(ROAR_ERROR_INVAL);
1000  return -1;
1001 }
1002
1003 _initerr();
1004
1005 if ( roar_stream_set_role(vss->con, &(vss->stream), role) == -1 ) {
1006  _seterrre();
1007  return -1;
1008 }
1009
1010 return 0;
1011}
1012
1013
1014int     roar_vs_iterate       (roar_vs_t * vss, int wait, int * error) {
1015 struct roar_vio_select vios[3];
1016 struct roar_vio_selecttv rtv = {.sec = 0, .nsec = 1};
1017 size_t len = 0;
1018 ssize_t i;
1019 ssize_t ret;
1020 int can_read = 0, can_write = 0;
1021 int can_flush_stream = 0, can_flush_file = 0;
1022 int is_eof = 0;
1023 void * data;
1024 size_t tmp;
1025
1026 // TODO: fix error handling below.
1027
1028 _ckvss(-1);
1029
1030 if ( wait != ROAR_VS_WAIT && wait != ROAR_VS_NOWAIT ) {
1031  _seterr(ROAR_ERROR_INVAL);
1032  return -1;
1033 }
1034
1035 ROAR_VIO_SELECT_SETVIO(&(vios[len]), &(vss->vio), ((vss->flags & FLAG_DIR_IN  ? ROAR_VIO_SELECT_READ  : 0) |
1036                                                    (vss->flags & FLAG_DIR_OUT ? ROAR_VIO_SELECT_WRITE : 0)));
1037 vios[len].ud.vp = &(vss->vio);
1038 len++;
1039
1040 ROAR_VIO_SELECT_SETVIO(&(vios[len]), roar_get_connection_vio2(vss->con), ROAR_VIO_SELECT_READ);
1041 vios[len].ud.vp = vss->con;
1042 len++;
1043
1044
1045// TODO: FIXME: need to do two select()s so we can sleep more efficently and test for both directions.
1046// for the moment we disable file direction and hope it will not block anyway...
1047/*
1048 if ( vss->file != NULL ) {
1049  ROAR_VIO_SELECT_SETVIO(&(vios[len]), vss->file, ((vss->flags & FLAG_DIR_IN  ? ROAR_VIO_SELECT_WRITE : 0) |
1050                                                   (vss->flags & FLAG_DIR_OUT ? ROAR_VIO_SELECT_READ  : 0)));
1051  vios[len].ud.vp = vss->file;
1052  len++;
1053 }
1054*/
1055
1056 ret = roar_vio_select(vios, len, (wait == ROAR_VS_NOWAIT ? &rtv : NULL), NULL);
1057
1058// part 2 of above hack:
1059// emulate read handle.
1060  if ( vss->file != NULL ) {
1061   vios[len].ud.vp   = vss->file;
1062   vios[len].eventsa = ROAR_VIO_SELECT_WRITE|ROAR_VIO_SELECT_READ;
1063   len++;
1064  }
1065
1066 // no error here nor EOF.
1067 if ( ret == 0 )
1068  return 1;
1069
1070 for (i = 0; i < len; i++) {
1071  if ( !vios[i].eventsa )
1072   continue;
1073
1074  if ( vios[i].ud.vp == &(vss->vio) ) {
1075   if ( vios[i].eventsa & ROAR_VIO_SELECT_READ )
1076    can_read++;
1077
1078   if ( vios[i].eventsa & ROAR_VIO_SELECT_WRITE ) {
1079    can_write++;
1080    can_flush_stream = 1;
1081   }
1082  } else if ( vios[i].ud.vp == vss->con ) {
1083   roar_sync(vss->con);
1084  } else if ( vss->file != NULL && vios[i].ud.vp == vss->file ) {
1085   if ( vios[i].eventsa & ROAR_VIO_SELECT_READ )
1086    can_write++;
1087
1088   if ( vios[i].eventsa & ROAR_VIO_SELECT_WRITE ) {
1089    can_read++;
1090    can_flush_file = 1;
1091   }
1092  }
1093 }
1094
1095 if ( vss->flags & FLAG_BUFFERED ) {
1096  if ( roar_buffer_ring_avail(vss->readring, NULL, &tmp) != -1 )
1097   if ( tmp > 0 )
1098    can_read++;
1099
1100// no check here to just let the read return zero and we do EOF handling.
1101/*
1102  if ( roar_buffer_ring_avail(vss->writering, &tmp, NULL) != -1 )
1103   if ( tmp > 0 )
1104*/
1105    can_write++;
1106 }
1107
1108 ROAR_DBG("roar_vs_iterate(vss=%p, wait=%i, error=%p): can_read=%i, can_write=%i", vss, wait, error, can_read, can_write);
1109
1110 // TODO: FIXME: Need to correct error handling here!
1111
1112 if ( can_flush_stream && vss->writebuffer != NULL ) {
1113  if ( roar_buffer_get_data(vss->writebuffer, &data) == -1 )
1114   return -1;
1115
1116  if ( roar_buffer_get_len(vss->writebuffer, &len) == -1 )
1117   return -1;
1118
1119  ret = roar_vs_write_direct(vss, data, len, error);
1120
1121  if ( ret == -1 ) {
1122   return -1;
1123  } else if ( ret == len ) {
1124   roar_buffer_free(vss->writebuffer);
1125   vss->writebuffer = NULL;
1126  } else {
1127   if ( roar_buffer_set_offset(vss->writebuffer, ret) == -1 )
1128    return -1;
1129  }
1130 }
1131
1132 if ( can_flush_file && vss->readbuffer != NULL ) {
1133  if ( roar_buffer_get_data(vss->readbuffer, &data) == -1 )
1134   return -1;
1135
1136  if ( roar_buffer_get_len(vss->readbuffer, &len) == -1 )
1137   return -1;
1138
1139  ret = roar_vio_write(vss->file, data, len);
1140
1141  if ( ret == -1 ) {
1142   return -1;
1143  } else if ( ret == len ) {
1144   roar_buffer_free(vss->readbuffer);
1145   vss->readbuffer = NULL;
1146  } else {
1147   if ( roar_buffer_set_offset(vss->readbuffer, ret) == -1 )
1148    return -1;
1149  }
1150 }
1151
1152#define _READ_SIZE 1024
1153
1154 if ( can_read == 2 && vss->readbuffer == NULL ) {
1155  if ( roar_buffer_new_data(&(vss->readbuffer), (len = _READ_SIZE), &data) == -1 )
1156   return -1;
1157
1158  ret = roar_vs_read(vss, data, len, error);
1159
1160  if ( ret == -1 ) {
1161   roar_buffer_free(vss->readbuffer);
1162   vss->readbuffer = NULL;
1163   return -1;
1164  } else if ( ret == 0 ) {
1165   is_eof = 1;
1166   roar_buffer_free(vss->readbuffer);
1167   vss->readbuffer = NULL;
1168  } else {
1169   len = ret;
1170   if ( roar_buffer_set_len(vss->readbuffer, len) == -1 )
1171    return -1;
1172
1173   if ( vss->flags & FLAG_BUFFERED ) {
1174    tmp = len;
1175    if ( roar_buffer_ring_write(vss->readring, data, &tmp) == -1 ) {
1176     ret = -1;
1177    } else {
1178     ret = tmp;
1179    }
1180   } else {
1181    ret = roar_vio_write(vss->file, data, len);
1182   }
1183
1184   if ( ret == -1 ) {
1185    return -1;
1186   } else if ( ret == len ) {
1187    roar_buffer_free(vss->readbuffer);
1188    vss->readbuffer = NULL;
1189   } else {
1190    if ( roar_buffer_set_offset(vss->readbuffer, ret) == -1 )
1191     return -1;
1192   }
1193  }
1194 }
1195
1196 if ( can_write == 2 && vss->writebuffer == NULL ) {
1197  if ( roar_buffer_new_data(&(vss->writebuffer), (len = _READ_SIZE), &data) == -1 )
1198   return -1;
1199
1200  if ( vss->flags & FLAG_BUFFERED ) {
1201   tmp = len;
1202   if ( roar_buffer_ring_read(vss->writering, data, &tmp) == -1 ) {
1203    ret = -1;
1204   } else {
1205    ret = tmp;
1206   }
1207  } else {
1208   ret = roar_vio_read(vss->file, data, len);
1209  }
1210
1211  ROAR_DBG("roar_vs_iterate(vss=%p, wait=%i, error=%p): ret=%lli", vss, wait, error, (long long int)ret);
1212
1213  if ( ret == -1 ) {
1214   ROAR_DBG("roar_vs_iterate(vss=%p, wait=%i, error=%p) = ?", vss, wait, error);
1215
1216   roar_buffer_free(vss->writebuffer);
1217   vss->writebuffer = NULL;
1218   return -1;
1219  } else if ( ret == 0 ) {
1220   ROAR_DBG("roar_vs_iterate(vss=%p, wait=%i, error=%p) = ?", vss, wait, error);
1221
1222   is_eof = 1;
1223   roar_buffer_free(vss->writebuffer);
1224   vss->writebuffer = NULL;
1225  } else {
1226   ROAR_DBG("roar_vs_iterate(vss=%p, wait=%i, error=%p) = ?", vss, wait, error);
1227
1228   if ( len != ret ) {
1229    len = ret;
1230    if ( roar_buffer_set_len(vss->writebuffer, len) == -1 )
1231     return -1;
1232   }
1233
1234   ROAR_DBG("roar_vs_iterate(vss=%p, wait=%i, error=%p) = ?", vss, wait, error);
1235
1236   ret = roar_vs_write_direct(vss, data, len, error);
1237
1238   if ( ret == -1 ) {
1239    return -1;
1240   } else if ( ret == len ) {
1241    roar_buffer_free(vss->writebuffer);
1242    vss->writebuffer = NULL;
1243   } else {
1244    if ( roar_buffer_set_offset(vss->writebuffer, ret) == -1 )
1245     return -1;
1246   }
1247  }
1248 }
1249
1250 return is_eof ? 0 : 2;
1251}
1252
1253int     roar_vs_run           (roar_vs_t * vss, int * error) {
1254 int ret;
1255
1256 _ckvss(-1);
1257
1258 while ((ret = roar_vs_iterate(vss, ROAR_VS_WAIT, error)) > 0);
1259
1260 ROAR_DBG("roar_vs_run(vss=%p, error=%p): ret=%i", vss, error, ret);
1261
1262 if ( ret == 0 ) {
1263  // flush buffers:
1264  roar_vs_iterate(vss, ROAR_VS_WAIT, error);
1265 }
1266
1267 if ( roar_vs_sync(vss, ROAR_VS_WAIT, error) == -1 )
1268  return -1;
1269
1270 return ret;
1271}
1272
1273ssize_t roar_vs_get_avail_read(roar_vs_t * vss, int * error) {
1274 size_t len;
1275
1276 _ckvss(-1);
1277
1278 if ( !(vss->flags & FLAG_BUFFERED) ) {
1279  _seterr(ROAR_ERROR_INVAL);
1280  return -1;
1281 }
1282
1283 if ( roar_buffer_ring_avail(vss->readring, &len, NULL) == -1 ) {
1284  _seterrre();
1285  return -1;
1286 }
1287
1288 return len;
1289}
1290
1291ssize_t roar_vs_get_avail_write(roar_vs_t * vss, int * error) {
1292 size_t len;
1293
1294 _ckvss(-1);
1295
1296 if ( !(vss->flags & FLAG_BUFFERED) ) {
1297  _seterr(ROAR_ERROR_INVAL);
1298  return -1;
1299 }
1300
1301 if ( roar_buffer_ring_avail(vss->writering, NULL, &len) == -1 ) {
1302  _seterrre();
1303  return -1;
1304 }
1305
1306 return len;
1307}
1308
1309int     roar_vs_reset_buffer(roar_vs_t * vss, int writering, int readring, int * error) {
1310 _ckvss(-1);
1311
1312 if ( !(vss->flags & FLAG_BUFFERED) ) {
1313  _seterr(ROAR_ERROR_INVAL);
1314  return -1;
1315 }
1316
1317 if ( writering != ROAR_VS_TRUE || writering != ROAR_VS_FALSE ||
1318      readring  != ROAR_VS_TRUE || readring  != ROAR_VS_FALSE ) {
1319  _seterr(ROAR_ERROR_INVAL);
1320  return -1;
1321 }
1322
1323 if ( writering ) {
1324  if ( roar_buffer_ring_reset(vss->writering) == -1 ) {
1325   _seterrre();
1326   return -1;
1327  }
1328 }
1329
1330 if ( readring ) {
1331  if ( roar_buffer_ring_reset(vss->readring) == -1 ) {
1332   _seterrre();
1333   return -1;
1334  }
1335 }
1336
1337 return 0;
1338}
1339
1340int     roar_vs_ctl           (roar_vs_t * vss, roar_vs_ctlcmd cmd, void * argp, int * error) {
1341 _ckvss(-1);
1342
1343 switch (cmd) {
1344  case ROAR_VS_CMD_NOOP:
1345   break;
1346  case ROAR_VS_CMD_SET_MIXER:
1347    vss->mixerid = *(int*)argp;
1348   break;
1349  case ROAR_VS_CMD_GET_MIXER:
1350    *(int*)argp = vss->mixerid;
1351   break;
1352  case ROAR_VS_CMD_SET_FIRST_PRIM:
1353    vss->first_primid = *(int*)argp;
1354   break;
1355  case ROAR_VS_CMD_GET_FIRST_PRIM:
1356    *(int*)argp = vss->first_primid;
1357   break;
1358#ifdef _HAVE_SOCKOPT
1359  case ROAR_VS_CMD_SET_LATC_P:
1360    vss->latc.p = *(float*)argp;
1361   break;
1362  case ROAR_VS_CMD_GET_LATC_P:
1363    *(float*)argp = vss->latc.p;
1364   break;
1365  case ROAR_VS_CMD_SET_LATC_TARGET:
1366    vss->latc.target = *(float*)argp;
1367   break;
1368  case ROAR_VS_CMD_GET_LATC_TARGET:
1369    *(float*)argp = vss->latc.target;
1370   break;
1371  case ROAR_VS_CMD_SET_LATC_WINDOW:
1372    vss->latc.window = *(float*)argp;
1373   break;
1374  case ROAR_VS_CMD_GET_LATC_WINDOW:
1375    *(float*)argp = vss->latc.window;
1376   break;
1377  case ROAR_VS_CMD_SET_LATC_MINLAG:
1378    vss->latc.minlag = *(float*)argp;
1379   break;
1380  case ROAR_VS_CMD_GET_LATC_MINLAG:
1381    *(float*)argp = vss->latc.minlag;
1382   break;
1383#endif
1384// use ifndef here so warnings of unhandled enum values will be shown in DEBUG mode.
1385#ifndef DEBUG
1386  default:
1387    return -1;
1388   break;
1389#endif
1390 }
1391
1392 return 0;
1393}
1394
1395struct roar_connection * roar_vs_connection_obj(roar_vs_t * vss, int * error) {
1396 _ckvss(NULL);
1397
1398 return vss->con;
1399}
1400
1401struct roar_stream     * roar_vs_stream_obj    (roar_vs_t * vss, int * error) {
1402 _ckvss(NULL);
1403
1404 if ( !(vss->flags & FLAG_STREAM) ) {
1405  _seterr(ROAR_ERROR_INVAL);
1406  return NULL;
1407 }
1408
1409 return &(vss->stream);
1410}
1411
1412struct roar_vio_calls  * roar_vs_vio_obj       (roar_vs_t * vss, int * error) {
1413 _ckvss(NULL);
1414
1415 if ( !(vss->flags & FLAG_STREAM) ) {
1416  _seterr(ROAR_ERROR_INVAL);
1417  return NULL;
1418 }
1419
1420 return &(vss->vio);
1421}
1422
1423//ll
Note: See TracBrowser for help on using the repository browser.