source: roaraudio/libroar/vs.c @ 4260:73d7bdb1a9c5

Last change on this file since 4260:73d7bdb1a9c5 was 4260:73d7bdb1a9c5, checked in by phi, 14 years ago

better NULL checking

File size: 16.0 KB
Line 
1//vs.c:
2
3/*
4 *      Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2010
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#define FLAG_NONE     0x0000
39#define FLAG_STREAM   0x0001
40#define FLAG_NONBLOCK 0x0002
41#define FLAG_BUFFERED 0x0004
42
43#define _initerr()  do { errno = 0; roar_err_clear(); } while(0)
44#define _seterr(x)  do { if ( error != NULL ) *error = (x); } while(0)
45#define _seterrre() do { _seterr(roar_errno); } while(0)
46#define _seterrse() do { roar_err_from_errno(); _seterr(roar_errno); } while(0)
47#define _ckvss(ret) do { if ( vss == NULL ) { _seterr(ROAR_ERROR_INVAL); return (ret); } } while(0)
48
49struct roar_vs {
50 int flags;
51 struct roar_connection con_store;
52 struct roar_connection * con;
53 struct roar_stream       stream;
54 struct roar_vio_calls    vio;
55 struct roar_audio_info   info;
56 size_t                   readc, writec;
57 int                      mixerid;
58 int                      first_primid;
59};
60
61static int _roar_vs_find_first_prim(roar_vs_t * vss);
62
63const char * roar_vs_strerr(int error) {
64 const struct {
65  int err;
66  const char * msg;
67 } msgs[] = {
68  {ROAR_ERROR_NONE,        "No error"},
69  {ROAR_ERROR_PERM,        "Operation not permitted"},
70  {ROAR_ERROR_NOENT,       "No such object, file or directory"},
71  {ROAR_ERROR_BADMSG,      "Bad message"},
72  {ROAR_ERROR_BUSY,        "Device or resource busy"},
73  {ROAR_ERROR_CONNREFUSED, "Connection refused"},
74  {ROAR_ERROR_NOSYS,       "Function not implemented"},
75  {ROAR_ERROR_NOTSUP,      "Operation not supported"},
76  {ROAR_ERROR_PIPE,        "Broken pipe"},
77  {ROAR_ERROR_PROTO,       "Protocol error"},
78  {ROAR_ERROR_RANGE,       "Result too large or parameter out of range"},
79  {ROAR_ERROR_MSGSIZE,     "Message too long"},
80  {ROAR_ERROR_NOMEM,       "Not enough space"},
81  {ROAR_ERROR_INVAL,       "Invalid argument"},
82  {-1, NULL}
83 };
84 int i;
85
86 for (i = 0; msgs[i].msg != NULL; i++)
87  if ( msgs[i].err == error )
88   return msgs[i].msg;
89
90 return "(unknown)";
91}
92
93static roar_vs_t * roar_vs_init(int * error) {
94 roar_vs_t * vss = roar_mm_malloc(sizeof(roar_vs_t));
95
96 if ( vss == NULL ) {
97  _seterrse();
98  return NULL;
99 }
100
101 memset(vss, 0, sizeof(roar_vs_t));
102
103 vss->mixerid      = -1;
104 vss->first_primid = -1;
105
106 return vss;
107}
108
109roar_vs_t * roar_vs_new_from_con(struct roar_connection * con, int * error) {
110 roar_vs_t * vss = roar_vs_init(error);
111
112 if ( vss == NULL )
113  return NULL;
114
115 vss->con = con;
116
117 return vss;
118}
119
120roar_vs_t * roar_vs_new(const char * server, const char * name, int * error) {
121 roar_vs_t * vss = roar_vs_init(error);
122 int ret;
123
124 if ( vss == NULL )
125  return NULL;
126
127 vss->con = &(vss->con_store);
128
129 _initerr();
130
131 ret = roar_simple_connect(vss->con, (char*)server, (char*)name);
132
133 if ( ret == -1 ) {
134  roar_vs_close(vss, ROAR_VS_TRUE, NULL);
135  _seterrre();
136  return NULL;
137 }
138
139 return vss;
140}
141
142int roar_vs_stream(roar_vs_t * vss, const struct roar_audio_info * info, int dir, int * error) {
143 struct roar_stream_info sinfo;
144 int ret;
145
146 _ckvss(-1);
147
148 if ( vss->flags & FLAG_STREAM ) {
149  _seterr(ROAR_ERROR_INVAL);
150  return -1;
151 }
152
153 _initerr();
154
155 if ( info != &(vss->info) )
156  memcpy(&(vss->info), info, sizeof(struct roar_audio_info));
157
158 ret = roar_vio_simple_new_stream_obj(&(vss->vio), vss->con, &(vss->stream),
159                                      info->rate, info->channels, info->bits, info->codec,
160                                      dir
161                                     );
162
163 if ( ret == -1 ) {
164  _seterrre();
165  return -1;
166 }
167
168 if ( roar_stream_get_info(vss->con, &(vss->stream), &sinfo) != -1 ) {
169  vss->mixerid = sinfo.mixer;
170  _roar_vs_find_first_prim(vss);
171 }
172
173 vss->flags |= FLAG_STREAM;
174
175 return 0;
176}
177
178roar_vs_t * roar_vs_new_simple(const char * server, const char * name, int rate, int channels, int codec, int bits, int dir, int * error) {
179 roar_vs_t * vss = roar_vs_new(server, name, error);
180 int ret;
181
182 if (vss == NULL)
183  return NULL;
184
185 memset(&(vss->info), 0, sizeof(vss->info));
186
187 vss->info.rate     = rate;
188 vss->info.channels = channels;
189 vss->info.codec    = codec;
190 vss->info.bits     = bits;
191
192 ret = roar_vs_stream(vss, &(vss->info), dir, error);
193
194 if (ret == -1) {
195  roar_vs_close(vss, ROAR_VS_TRUE, NULL);
196  return NULL;
197 }
198
199 return vss;
200}
201
202int roar_vs_close(roar_vs_t * vss, int killit, int * error) {
203 if ( killit != ROAR_VS_TRUE && killit != ROAR_VS_FALSE ) {
204  _seterr(ROAR_ERROR_UNKNOWN);
205  return -1;
206 }
207
208 _ckvss(-1);
209
210 if ( vss->flags & FLAG_STREAM ) {
211  if ( killit == ROAR_VS_TRUE ) {
212   roar_kick(vss->con, ROAR_OT_STREAM, roar_stream_get_id(&(vss->stream)));
213  }
214
215  roar_vio_close(&(vss->vio));
216 }
217
218 if ( vss->con == &(vss->con_store) ) {
219  roar_disconnect(vss->con);
220 }
221
222 roar_mm_free(vss);
223 return 0;
224}
225
226ssize_t roar_vs_write(roar_vs_t * vss, const void * buf, size_t len, int * error) {
227 ssize_t ret;
228
229 _ckvss(-1);
230
231 if ( !(vss->flags & FLAG_STREAM) ) {
232  _seterr(ROAR_ERROR_INVAL);
233  return -1;
234 }
235
236 _initerr();
237
238 ret = roar_vio_write(&(vss->vio), (void*)buf, len);
239
240 if ( ret == -1 ) {
241#ifdef EAGAIN
242  if ( errno == EAGAIN )
243   return 0;
244#endif
245
246#ifdef EWOULDBLOCK
247  if ( errno == EWOULDBLOCK )
248   return 0;
249#endif
250
251  _seterrre();
252 } else {
253  vss->writec += ret;
254 }
255
256 return ret;
257}
258
259ssize_t roar_vs_read (roar_vs_t * vss,       void * buf, size_t len, int * error) {
260 ssize_t ret;
261
262 _ckvss(-1);
263
264 if ( !(vss->flags & FLAG_STREAM) ) {
265  _seterr(ROAR_ERROR_INVAL);
266  return -1;
267 }
268
269 _initerr();
270
271 ret = roar_vio_read(&(vss->vio), buf, len);
272
273 if ( ret == -1 ) {
274  _seterrre();
275 } else {
276  vss->readc += ret;
277 }
278
279 return ret;
280}
281
282int     roar_vs_sync (roar_vs_t * vss, int wait, int * error) {
283 _ckvss(-1);
284
285 if ( !(vss->flags & FLAG_STREAM) ) {
286  _seterr(ROAR_ERROR_INVAL);
287  return -1;
288 }
289
290 if ( wait != ROAR_VS_NOWAIT ) {
291  _seterr(ROAR_ERROR_INVAL);
292  return -1;
293 }
294
295 _initerr();
296
297 if ( roar_vio_sync(&(vss->vio)) == -1 ) {
298  _seterrre();
299  return -1;
300 }
301
302 return 0;
303}
304
305int     roar_vs_blocking (roar_vs_t * vss, int val, int * error) {
306 int old = -1;
307
308 _ckvss(-1);
309
310  if ( !(vss->flags & FLAG_STREAM) ) {
311  _seterr(ROAR_ERROR_INVAL);
312  return -1;
313 }
314
315 old = vss->flags & FLAG_NONBLOCK ? ROAR_VS_FALSE : ROAR_VS_TRUE;
316
317 _initerr();
318
319 switch (val) {
320  case ROAR_VS_TRUE:
321    if ( roar_vio_nonblock(&(vss->vio), ROAR_SOCKET_BLOCK) == -1 ) {
322     _seterrre();
323     return -1;
324    }
325    vss->flags |= FLAG_NONBLOCK;
326    vss->flags -= FLAG_NONBLOCK;
327    return old;
328   break;
329  case ROAR_VS_FALSE:
330    if ( roar_vio_nonblock(&(vss->vio), ROAR_SOCKET_NONBLOCK) == -1 ) {
331     _seterrre();
332     return -1;
333    }
334    vss->flags |= FLAG_NONBLOCK;
335    return old;
336   break;
337  case ROAR_VS_TOGGLE:
338    if ( old == ROAR_VS_TRUE ) {
339     return roar_vs_blocking(vss, ROAR_VS_FALSE, error);
340    } else {
341     return roar_vs_blocking(vss, ROAR_VS_TRUE, error);
342    }
343   break;
344  case ROAR_VS_ASK:
345    return old;
346   break;
347 }
348
349 _seterr(ROAR_ERROR_INVAL);
350 return -1;
351}
352
353static int _roar_vs_find_first_prim(roar_vs_t * vss) {
354 struct roar_stream stream;
355 struct roar_stream_info info;
356 int id[ROAR_STREAMS_MAX];
357 int num;
358 int i;
359
360 if ( vss->first_primid != -1 )
361  return vss->first_primid;
362
363 if ( vss->mixerid == -1 )
364  return -1;
365
366 if ( (num = roar_list_streams(vss->con, id, ROAR_STREAMS_MAX)) == -1 ) {
367  return -1;
368 }
369
370 for (i = 0; i < num; i++) {
371  if ( roar_get_stream(vss->con, &stream, id[i]) == -1 )
372   continue;
373
374  if ( stream.dir != ROAR_DIR_OUTPUT )
375   continue;
376
377  if ( roar_stream_get_info(vss->con, &stream, &info) == -1 )
378   continue;
379
380  if ( info.mixer == vss->mixerid ) {
381   vss->first_primid = id[i];
382   return id[i];
383  }
384 }
385
386 return -1;
387}
388
389ssize_t roar_vs_position(roar_vs_t * vss, int backend, int * error) {
390 struct roar_stream stream;
391 struct roar_stream      out_stream;
392 struct roar_stream_info out_info;
393 size_t offset;
394
395 _ckvss(-1);
396
397 if ( !(vss->flags & FLAG_STREAM) ) {
398  _seterr(ROAR_ERROR_INVAL);
399  return -1;
400 }
401
402 _initerr();
403
404 if ( roar_get_stream(vss->con, &stream, roar_stream_get_id(&(vss->stream))) == -1 ) {
405  _seterrre();
406  return -1;
407 }
408
409 switch (backend) {
410  case ROAR_VS_BACKEND_NONE:
411    return stream.pos;
412   break;
413  case ROAR_VS_BACKEND_FIRST:
414   // _roar_vs_find_first_prim(vss);
415    if ( vss->first_primid == -1 ) {
416     _seterr(ROAR_ERROR_UNKNOWN);
417     return -1;
418    }
419
420    roar_stream_new_by_id(&out_stream, vss->first_primid);
421
422    if ( roar_stream_get_info(vss->con, &out_stream, &out_info) == -1 ) {
423     _seterrre();
424     return -1;
425    }
426
427    offset  = out_info.delay * vss->info.rate;
428    offset /= 1000000;
429
430    return stream.pos + offset;
431   break;
432  default:
433    _seterr(ROAR_ERROR_NOTSUP);
434    return -1;
435   break;
436 }
437
438 _seterr(ROAR_ERROR_NOSYS);
439 return -1;
440}
441
442roar_mus_t roar_vs_latency(roar_vs_t * vss, int backend, int * error) {
443 ssize_t pos  = roar_vs_position(vss, backend, error);
444 ssize_t bps;  // byte per sample
445 size_t  lioc; // local IO (byte) counter
446 size_t  lpos; // local possition
447 roar_mus_t  lag;
448
449 _initerr();
450
451 _ckvss(-1);
452
453 if (pos == -1) {
454  _seterrre();
455  return 0;
456 }
457
458 if ( !(vss->flags & FLAG_STREAM) ) {
459  _seterr(ROAR_ERROR_INVAL);
460  return 0;
461 }
462
463 if ( vss->writec == 0 ) {
464  lioc = vss->readc;
465 } else {
466  lioc = vss->writec;
467 }
468
469 bps = roar_info2samplesize(&(vss->info));
470
471 if ( bps == -1 ) {
472  _seterrre();
473  return 0;
474 }
475
476 lpos = lioc / bps;
477
478 lag = (roar_mus_t)lpos - (roar_mus_t)pos;
479
480 // we now have the lag in frames
481 // return value are ms
482 // so we need to multiply with 1s/ms and
483 // multiply by 1/rate
484
485 lag *= 1000000; // 1s/ms
486 lag /= vss->info.rate;
487
488 if ( lag == 0 ) {
489  _seterr(ROAR_ERROR_NONE);
490 }
491
492 return lag;
493}
494
495static int roar_vs_flag(roar_vs_t * vss, int flag, int val, int * error) {
496 struct roar_stream_info info;
497 int old = -1;
498
499 _ckvss(-1);
500
501 if ( !(vss->flags & FLAG_STREAM) ) {
502  _seterr(ROAR_ERROR_INVAL);
503  return -1;
504 }
505
506 if ( val != ROAR_VS_ASK )
507  old = roar_vs_flag(vss, flag, ROAR_VS_ASK, error);
508
509 _initerr();
510
511 switch (val) {
512  case ROAR_VS_TRUE:
513  case ROAR_VS_FALSE:
514    if ( roar_stream_set_flags(vss->con, &(vss->stream), flag,
515                               val == ROAR_VS_TRUE ? ROAR_SET_FLAG : ROAR_RESET_FLAG) == -1 ) {
516     _seterrre();
517     return -1;
518    }
519    return old;
520   break;
521  case ROAR_VS_TOGGLE:
522    return roar_vs_flag(vss, flag, old == ROAR_VS_TRUE ? ROAR_VS_FALSE : ROAR_VS_TRUE, error);
523   break;
524  case ROAR_VS_ASK:
525    if ( roar_stream_get_info(vss->con, &(vss->stream), &info) == -1 ) {
526     _seterrre();
527     return -1;
528    }
529    return info.flags & flag ? ROAR_VS_TRUE : ROAR_VS_FALSE;
530   break;
531 }
532
533 _seterr(ROAR_ERROR_INVAL);
534 return -1;
535}
536
537int     roar_vs_pause(roar_vs_t * vss, int val, int * error) {
538 return roar_vs_flag(vss, ROAR_FLAG_PAUSE, val, error);
539}
540
541int     roar_vs_mute (roar_vs_t * vss, int val, int * error) {
542 return roar_vs_flag(vss, ROAR_FLAG_MUTE, val, error);
543}
544
545static int roar_vs_volume (roar_vs_t * vss, float * c, size_t channels, int * error) {
546 struct roar_mixer_settings mixer;
547 size_t i;
548 register float s;
549 int oldchannels;
550 int handled;
551
552 _ckvss(-1);
553
554 if ( !(vss->flags & FLAG_STREAM) ) {
555  _seterr(ROAR_ERROR_INVAL);
556  return -1;
557 }
558
559 if ( channels > ROAR_MAX_CHANNELS ) {
560  _seterr(ROAR_ERROR_INVAL);
561  return -1;
562 }
563
564 _initerr();
565
566 if ( roar_get_vol(vss->con, roar_stream_get_id(&(vss->stream)), &mixer, &oldchannels) == -1 ) {
567  _seterrre();
568  return -1;
569 }
570
571 for (i = 0; i < channels; i++) {
572  s = c[i] * 65535.0;
573  if ( s > 66190.0 || s < -655.0 ) {
574   _seterr(ROAR_ERROR_RANGE);
575   return -1;
576  } else if ( s > 65535.0 ) {
577   s = 65535.0;
578  } else if ( s <     0.0 ) {
579   s = 0.0;
580  }
581  mixer.mixer[i] = s;
582 }
583
584 mixer.scale = 65535;
585
586 if ( channels != oldchannels ) {
587  handled = 0;
588  switch (oldchannels) {
589   case 1:
590     if ( channels == 2 ) {
591      mixer.mixer[0] = (mixer.mixer[0] + mixer.mixer[1]) / 2;
592      handled = 1;
593     }
594    break;
595   case 2:
596     if ( channels == 1 ) {
597      mixer.mixer[1] = mixer.mixer[0];
598      handled = 1;
599     }
600    break;
601   case 4:
602     if ( channels == 1 ) {
603      mixer.mixer[1] = mixer.mixer[0];
604      mixer.mixer[2] = mixer.mixer[0];
605      mixer.mixer[3] = mixer.mixer[0];
606      handled = 1;
607     } else if ( channels == 2 ) {
608      mixer.mixer[2] = mixer.mixer[0];
609      mixer.mixer[3] = mixer.mixer[1];
610      handled = 1;
611     }
612    break;
613  }
614  if ( handled ) {
615   channels = oldchannels;
616  } else {
617   _seterr(ROAR_ERROR_INVAL);
618   return -1;
619  }
620 }
621
622 if ( roar_set_vol(vss->con, roar_stream_get_id(&(vss->stream)), &mixer, channels) == -1 ) {
623  _seterrre();
624  return -1;
625 }
626
627 return 0;
628}
629
630int     roar_vs_volume_mono   (roar_vs_t * vss, float c, int * error) {
631 return roar_vs_volume(vss, &c, 1, error);
632}
633
634int     roar_vs_volume_stereo (roar_vs_t * vss, float l, float r, int * error) {
635 float c[2] = {l, r};
636 return roar_vs_volume(vss, c, 2, error);
637}
638
639int     roar_vs_volume_get    (roar_vs_t * vss, float * l, float * r, int * error) {
640 struct roar_mixer_settings mixer;
641 int channels;
642
643 if ( vss == NULL || l == NULL || r == NULL ) {
644  _seterr(ROAR_ERROR_INVAL);
645  return -1;
646 }
647
648 if ( !(vss->flags & FLAG_STREAM) ) {
649  _seterr(ROAR_ERROR_INVAL);
650  return -1;
651 }
652
653 _initerr();
654
655 if ( roar_get_vol(vss->con, roar_stream_get_id(&(vss->stream)), &mixer, &channels) == -1 ) {
656  _seterrre();
657  return -1;
658 }
659
660 if ( channels == 1 )
661  mixer.mixer[1] = mixer.mixer[0];
662
663 *l = mixer.mixer[0] / (float)mixer.scale;
664 *r = mixer.mixer[1] / (float)mixer.scale;
665
666 return 0;
667}
668
669int     roar_vs_meta          (roar_vs_t * vss, struct roar_keyval * kv, size_t len, int * error) {
670 struct roar_meta meta;
671 size_t i;
672 int type;
673 int ret = 0;
674
675 _ckvss(-1);
676
677 if ( !(vss->flags & FLAG_STREAM) ) {
678  _seterr(ROAR_ERROR_INVAL);
679  return -1;
680 }
681
682 meta.type   = ROAR_META_TYPE_NONE;
683 meta.key[0] = 0;
684 meta.value  = NULL;
685
686 _initerr();
687
688 if ( roar_stream_meta_set(vss->con, &(vss->stream), ROAR_META_MODE_CLEAR, &meta) == -1 ) {
689  _seterrre();
690  ret = -1;
691 }
692
693 for (i = 0; i < len; i++) {
694  type = roar_meta_inttype(kv[i].key);
695  meta.type  = type;
696  meta.value = kv[i].value;
697
698  if ( roar_stream_meta_set(vss->con, &(vss->stream), ROAR_META_MODE_ADD, &meta) == -1 ) {
699   _seterrre();
700   ret = -1;
701  }
702 }
703
704 meta.type   = ROAR_META_TYPE_NONE;
705 meta.key[0] = 0;
706 meta.value  = NULL;
707 if ( roar_stream_meta_set(vss->con, &(vss->stream), ROAR_META_MODE_FINALIZE, &meta) == -1 ) {
708  _seterrre();
709  ret = -1;
710 }
711
712 return ret;
713}
714
715int     roar_vs_role          (roar_vs_t * vss, int role, int * error) {
716 _ckvss(-1);
717
718 if ( !(vss->flags & FLAG_STREAM) ) {
719  _seterr(ROAR_ERROR_INVAL);
720  return -1;
721 }
722
723 _initerr();
724
725 if ( roar_stream_set_role(vss->con, &(vss->stream), role) == -1 ) {
726  _seterrre();
727  return -1;
728 }
729
730 return 0;
731}
732
733struct roar_connection * roar_vs_connection_obj(roar_vs_t * vss, int * error) {
734 _ckvss(NULL);
735
736 return vss->con;
737}
738
739struct roar_stream     * roar_vs_stream_obj    (roar_vs_t * vss, int * error) {
740 _ckvss(NULL);
741
742 if ( !(vss->flags & FLAG_STREAM) ) {
743  _seterr(ROAR_ERROR_INVAL);
744  return NULL;
745 }
746
747 return &(vss->stream);
748}
749
750struct roar_vio_calls  * roar_vs_vio_obj       (roar_vs_t * vss, int * error) {
751 _ckvss(NULL);
752
753 if ( !(vss->flags & FLAG_STREAM) ) {
754  _seterr(ROAR_ERROR_INVAL);
755  return NULL;
756 }
757
758 return &(vss->vio);
759}
760
761//ll
Note: See TracBrowser for help on using the repository browser.