source: roaraudio/libroaross/libroaross.c @ 3150:f35a31dddc8d

Last change on this file since 3150:f35a31dddc8d was 3150:f35a31dddc8d, checked in by phi, 14 years ago

wrote most of the mixer stuff

File size: 17.5 KB
Line 
1//libroaross.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, 675 Mass Ave, Cambridge, MA 02139, USA.
22 *
23 *  NOTE for everyone want's to change something and send patches:
24 *  read README and HACKING! There a addition information on
25 *  the license of this document you need to read before you send
26 *  any patches.
27 *
28 *  NOTE for uses of non-GPL (LGPL,...) software using libesd, libartsc
29 *  or libpulse*:
30 *  The libs libroaresd, libroararts and libroarpulse link this lib
31 *  and are therefore GPL. Because of this it may be illigal to use
32 *  them with any software that uses libesd, libartsc or libpulse*.
33 */
34
35#include "roaraudio.h"
36
37#if defined(ROAR_HAVE_OSS_BSD) || defined(ROAR_HAVE_OSS)
38#if defined(__OpenBSD__) || defined(__NetBSD__)
39#include <soundcard.h>
40#else
41#include <sys/soundcard.h>
42#endif
43#include <sys/ioctl.h>
44
45#ifdef ROAR_HAVE_H_SYS_TYPES
46#include <sys/types.h>
47#endif
48
49#ifdef ROAR_HAVE_H_FCNTL
50#include <fcntl.h>
51#endif
52
53#ifdef ROAR_HAVE_H_UNISTD
54#include <unistd.h>
55#endif
56
57#include <sys/stat.h>
58#include <dlfcn.h>
59
60#if defined(RTLD_NEXT)
61#define REAL_LIBC RTLD_NEXT
62#else
63#define REAL_LIBC ((void *) -1L)
64#endif
65
66#define _MAX_POINTER  8
67
68// handle type:
69#define HT_NONE       0
70#define HT_STREAM     1
71#define HT_MIXER      2
72
73struct session {
74 int refc;
75 struct roar_connection con;
76};
77
78static struct session _session = {.refc = 0};
79
80struct handle {
81 int refc; // refrence counter
82 struct session * session;
83 int type;
84 struct roar_stream    stream;
85 struct roar_vio_calls stream_vio;
86 int                   stream_dir;
87 int                   stream_opened;
88};
89
90static struct {
91 int     (*open)(const char *pathname, int flags, mode_t mode);
92 int     (*close)(int fd);
93 ssize_t (*write)(int fd, const void *buf, size_t count);
94 ssize_t (*read)(int fd, void *buf, size_t count);
95 int     (*ioctl)(int d, int request, ...);
96} _os;
97
98static struct {
99 struct {
100  int volume;
101  int pcm;
102  int line;
103  int line1;
104  int line2;
105  int line3;
106  int digital1;
107  int digital2;
108  int digital3;
109 } sid;
110} _mix_settings = {
111                   .sid = {
112                           .volume   = -1,
113                           .pcm      = -1,
114                           .line     =  0,
115                           .line1    =  1,
116                           .line2    =  2,
117                           .line3    =  3,
118                           .digital1 =  1,
119                           .digital2 =  2,
120                           .digital3 =  3
121                          }
122                  };
123
124static struct pointer {
125 int fh;
126 struct handle * handle;
127} _ptr[_MAX_POINTER];
128
129static void _init_os (void) {
130 memset(&_os, 0, sizeof(_os));
131
132 _os.open  = dlsym(REAL_LIBC, "open");
133 _os.close = dlsym(REAL_LIBC, "close");
134 _os.write = dlsym(REAL_LIBC, "write");
135 _os.read  = dlsym(REAL_LIBC, "read");
136 _os.ioctl = dlsym(REAL_LIBC, "ioctl");
137}
138
139static void _init_ptr (void) {
140 int i;
141
142 for (i = 0; i < _MAX_POINTER; i++) {
143  _ptr[i].fh = -1;
144 }
145}
146
147static void _init (void) {
148 static int inited = 0;
149
150 if ( !inited ) {
151  _init_os();
152  _init_ptr();
153  inited++;
154 }
155}
156
157static int _open_dummy (void) {
158 int p[2];
159
160 if ( pipe(p) == -1 )
161  return -1;
162
163 close(p[1]);
164
165 return p[0];
166}
167
168static struct session * _open_session (char * server, char * name) {
169 if ( _session.refc == 0 ) {
170
171  if ( name == NULL )
172   name = "libroaross client";
173
174  if ( roar_simple_connect(&(_session.con), server, name) == -1 )
175   return NULL;
176 }
177
178 _session.refc++;
179 return &_session;
180}
181
182static void _close_session(struct session * session) {
183 if ( session == NULL )
184  return;
185
186 session->refc--;
187
188 ROAR_DBG("_close_session(session=%p): session->refc=%i", session, session->refc);
189
190 if ( session->refc == 0 ) {
191  roar_disconnect(&(session->con));
192 }
193}
194
195static struct handle * _open_handle(struct session * session) {
196 struct handle * handle;
197
198 if ( (handle = roar_mm_malloc(sizeof(struct handle))) == NULL )
199  return NULL;
200
201 memset(handle, 0, sizeof(struct handle));
202
203 handle->refc = 1;
204 handle->session = session;
205 session->refc++; // TODO: better warp this
206 handle->type = HT_NONE;
207 handle->stream_dir = ROAR_DIR_PLAY;
208 roar_stream_new(&(handle->stream), ROAR_RATE_DEFAULT, ROAR_CHANNELS_DEFAULT, ROAR_BITS_DEFAULT, ROAR_CODEC_DEFAULT);
209
210 return handle;
211}
212
213static void _close_handle(struct handle * handle) {
214 if (handle == NULL)
215  return;
216
217 handle->refc--;
218
219 ROAR_DBG("_close_handle(handle=%p): handle->refc=%i", handle, handle->refc);
220
221 if ( handle->refc == 0 ) {
222  if ( handle->stream_opened )
223   roar_vio_close(&(handle->stream_vio));
224
225  handle->session->refc--;
226
227  _close_session(handle->session);
228
229  roar_mm_free(handle);
230 }
231}
232
233static struct pointer * _get_pointer_by_fh (int fh) {
234 int i;
235
236 for (i = 0; i < _MAX_POINTER; i++) {
237  if ( _ptr[i].fh == fh )
238   return &(_ptr[i]);
239 }
240
241 return NULL;
242}
243
244static struct pointer * _open_pointer(struct handle * handle) {
245 struct pointer * ret = _get_pointer_by_fh(-1);
246
247 if ( ret == NULL )
248  return NULL;
249
250 if ( (ret->fh = _open_dummy()) == -1 )
251  return NULL;
252
253 ret->handle = handle;
254
255 return ret;
256}
257
258static void _close_pointer(struct pointer * pointer) {
259 if ( pointer == NULL )
260  return;
261
262 _os.close(pointer->fh);
263
264 pointer->fh = -1;
265
266 _close_handle(pointer->handle);
267}
268
269// -------------------------------------
270// central open function:
271// -------------------------------------
272
273static int _open_file (const char *pathname, int flags) {
274 struct session * session;
275 struct handle  * handle;
276 struct pointer * pointer;
277 struct {
278  char * prefix;
279  int type;
280 } * ptr = NULL, p[] = {
281  {"/dev/dsp",   HT_STREAM},
282  {"/dev/mixer", HT_MIXER},
283  {NULL, HT_NONE},
284 };
285 int i;
286
287 for (i = 0; p[i].prefix != NULL; i++) {
288  if ( !strcmp(pathname, p[i].prefix) ) {
289   ptr = &(p[i]);
290  }
291 }
292
293 if ( ptr == NULL )
294  return -2;
295
296 if ( (session = _open_session(NULL, NULL)) == NULL ) {
297  return -1;
298 }
299
300 if ( (handle = _open_handle(session)) == NULL ) {
301  _close_session(session);
302  return -1;
303 }
304
305 handle->type = ptr->type;
306
307 switch (flags & (O_RDONLY|O_WRONLY|O_RDWR)) {
308  case O_RDONLY:
309    handle->stream_dir = ROAR_DIR_MONITOR;
310   break;
311  case O_WRONLY:
312    handle->stream_dir = ROAR_DIR_PLAY;
313   break;
314  case O_RDWR:
315    handle->stream_dir = ROAR_DIR_BIDIR;
316   break;
317 }
318
319 if ( (pointer = _open_pointer(handle)) == NULL ) {
320  _close_handle(handle);
321  return -1;
322 }
323
324 return pointer->fh;
325}
326
327// -------------------------------------
328// open function for streams:
329// -------------------------------------
330
331static int _open_stream (struct handle * handle) {
332  // FIXME: this should be re-written much more cleanly:
333
334 if ( handle == NULL )
335  return -1;
336
337 if ( roar_vio_simple_new_stream_obj(&(handle->stream_vio),
338                                     &(handle->session->con), &(handle->stream),
339                                     handle->stream.info.rate,
340                                     handle->stream.info.channels,
341                                     handle->stream.info.bits,
342                                     handle->stream.info.codec,
343                                     handle->stream_dir
344                                    ) == -1 )
345  return -1;
346
347 handle->stream_opened++;
348
349 _mix_settings.sid.pcm = roar_stream_get_id(&(handle->stream));
350
351 return 0;
352}
353
354// -------------------------------------
355// function to parse format:
356// -------------------------------------
357
358static int _ioctl_stream_format (struct handle * handle, int format) {
359 struct roar_audio_info * info = &(handle->stream.info);
360
361 switch (format) {
362  case AFMT_S8:
363    info->bits  = 8;
364    info->codec = ROAR_CODEC_PCM_S_LE;
365   break;
366  case AFMT_U8:
367    info->bits  = 8;
368    info->codec = ROAR_CODEC_PCM_U_LE;
369   break;
370  case AFMT_S16_BE:
371    info->bits  = 16;
372    info->codec = ROAR_CODEC_PCM_S_BE;
373   break;
374  case AFMT_S16_LE:
375    info->bits  = 16;
376    info->codec = ROAR_CODEC_PCM_S_LE;
377   break;
378  case AFMT_U16_BE:
379    info->bits  = 16;
380    info->codec = ROAR_CODEC_PCM_U_BE;
381   break;
382  case AFMT_U16_LE:
383    info->bits  = 16;
384    info->codec = ROAR_CODEC_PCM_U_LE;
385   break;
386#ifdef AFMT_S32_BE
387  case AFMT_S32_BE:
388    info->bits  = 32;
389    info->codec = ROAR_CODEC_PCM_S_BE;
390   break;
391#endif
392#ifdef AFMT_S32_LE
393  case AFMT_S32_LE:
394    info->bits  = 32;
395    info->codec = ROAR_CODEC_PCM_S_LE;
396   break;
397#endif
398  case AFMT_A_LAW:
399    info->bits  = 8;
400    info->codec = ROAR_CODEC_ALAW;
401   break;
402  case AFMT_MU_LAW:
403    info->bits  = 8;
404    info->codec = ROAR_CODEC_MULAW;
405   break;
406#ifdef AFMT_VORBIS
407  case AFMT_VORBIS:
408    info->codec = ROAR_CODEC_OGG_VORBIS;
409   break;
410#endif
411  default:
412    errno = ENOSYS;
413    return -1;
414   break;
415 }
416
417 return 0;
418}
419
420static inline int _ioctl_stream_format_list (void) {
421 int format = 0;
422
423 format |= AFMT_S8;
424 format |= AFMT_U8;
425
426 format |= AFMT_S16_BE;
427 format |= AFMT_S16_LE;
428
429 format |= AFMT_U16_BE;
430 format |= AFMT_U16_LE;
431
432#ifdef AFMT_S32_BE
433 format |= AFMT_S32_BE;
434#endif
435#ifdef AFMT_S32_LE
436 format |= AFMT_S32_LE;
437#endif
438
439 format |= AFMT_A_LAW;
440 format |= AFMT_MU_LAW;
441
442#ifdef AFMT_VORBIS
443 format |= AFMT_VORBIS;
444#endif
445
446 return format;
447}
448
449// -------------------------------------
450// mixer ioctls:
451// -------------------------------------
452
453static int _ioctl_mixer (struct handle * handle, long unsigned int req, int * ip) {
454 int channels;
455 struct roar_mixer_settings mixer;
456 char * name = NULL;
457 int o_w    = 0;
458 int o_sid  = -1;
459
460 switch (req) {
461#if 0
462  case SNDCTL_MIX_DESCRIPTION: name = "SNDCTL_MIX_DESCRIPTION"; break;
463  case SNDCTL_MIX_ENUMINFO:    name = "SNDCTL_MIX_ENUMINFO";    break;
464  case SNDCTL_MIX_EXTINFO:     name = "SNDCTL_MIX_EXTINFO";     break;
465  case SNDCTL_MIX_NREXT:       name = "SNDCTL_MIX_NREXT";       break;
466  case SNDCTL_MIX_NRMIX:       name = "SNDCTL_MIX_NRMIX";       break;
467  case SNDCTL_MIX_READ:        name = "SNDCTL_MIX_READ";        break;
468  case SNDCTL_MIX_WRITE:       name = "SNDCTL_MIX_WRITE";       break;
469#endif
470  case SOUND_MIXER_READ_DEVMASK: name = "SOUND_MIXER_READ_DEVMASK"; break;
471  case SOUND_MIXER_READ_PCM:     name = "SOUND_MIXER_READ_PCM";     break;
472  case SOUND_MIXER_WRITE_PCM:    name = "SOUND_MIXER_WRITE_PCM";    break;
473 }
474 if ( name != NULL ) {
475  ROAR_DBG("_ioctl_mixer(handle=%p, req=%lu, ip=%p): unspported mixer command %s", handle, req, ip, name);
476  ROAR_DBG("_ioctl_mixer(handle=%p, req=%lu, ip=%p) = -1 // errno = ENOSYS", handle, req, ip);
477  errno = ENOSYS;
478  return -1;
479 }
480
481 switch (req) {
482  case SOUND_MIXER_READ_VOLUME:    o_w = 0; o_sid = _mix_settings.sid.volume;   break;
483  case SOUND_MIXER_READ_PCM:       o_w = 0; o_sid = _mix_settings.sid.pcm;      break;
484  case SOUND_MIXER_READ_LINE:      o_w = 0; o_sid = _mix_settings.sid.line;     break;
485  case SOUND_MIXER_READ_LINE1:     o_w = 0; o_sid = _mix_settings.sid.line1;    break;
486  case SOUND_MIXER_READ_LINE2:     o_w = 0; o_sid = _mix_settings.sid.line2;    break;
487  case SOUND_MIXER_READ_LINE3:     o_w = 0; o_sid = _mix_settings.sid.line3;    break;
488#if 0
489  case SOUND_MIXER_READ_DIGITAL1:  o_w = 0; o_sid = _mix_settings.sid.digital1; break;
490  case SOUND_MIXER_READ_DIGITAL2:  o_w = 0; o_sid = _mix_settings.sid.digital2; break;
491  case SOUND_MIXER_READ_DIGITAL3:  o_w = 0; o_sid = _mix_settings.sid.digital3; break;
492#endif
493  case SOUND_MIXER_WRITE_VOLUME:   o_w = 1; o_sid = _mix_settings.sid.volume;   break;
494  case SOUND_MIXER_WRITE_PCM:      o_w = 1; o_sid = _mix_settings.sid.pcm;      break;
495  case SOUND_MIXER_WRITE_LINE:     o_w = 1; o_sid = _mix_settings.sid.line;     break;
496  case SOUND_MIXER_WRITE_LINE1:    o_w = 1; o_sid = _mix_settings.sid.line1;    break;
497  case SOUND_MIXER_WRITE_LINE2:    o_w = 1; o_sid = _mix_settings.sid.line2;    break;
498  case SOUND_MIXER_WRITE_LINE3:    o_w = 1; o_sid = _mix_settings.sid.line3;    break;
499#if 0
500  case SOUND_MIXER_WRITE_DIGITAL1: o_w = 1; o_sid = _mix_settings.sid.digital1; break;
501  case SOUND_MIXER_WRITE_DIGITAL2: o_w = 1; o_sid = _mix_settings.sid.digital2; break;
502  case SOUND_MIXER_WRITE_DIGITAL3: o_w = 1; o_sid = _mix_settings.sid.digital3; break;
503#endif
504 }
505 if ( o_sid != -1 ) {
506  // set/get volume
507  if ( o_w ) {
508   mixer.scale    = 65535;
509   mixer.mixer[0] = ( *ip       & 0xFF)*65535/50;
510   mixer.mixer[1] = ((*ip >> 8) & 0xFF)*65535/50;
511   if ( roar_set_vol(&(handle->session->con), o_sid, &mixer, 2) == -1 ) {
512    errno = ENOSYS;
513    return -1;
514   }
515   return 0;
516  } else {
517   if ( roar_get_vol(&(handle->session->con), o_sid, &mixer, &channels) == -1 ) {
518    errno = ENOSYS;
519    return -1;
520   }
521   *ip = ((50*mixer.mixer[0])/mixer.scale) | (((50*mixer.mixer[0])/mixer.scale)<<8);
522   return 0;
523  }
524 }
525
526 switch (req) {
527  case SOUND_MIXER_READ_DEVMASK:
528    *ip = 0;
529
530    if ( _mix_settings.sid.volume != -1 )
531     *ip |= SOUND_MASK_VOLUME;
532    if ( _mix_settings.sid.pcm != -1 )
533     *ip |= SOUND_MASK_PCM;
534    if ( _mix_settings.sid.line != -1 )
535     *ip |= SOUND_MASK_LINE;
536    if ( _mix_settings.sid.line1 != -1 )
537     *ip |= SOUND_MASK_LINE1;
538    if ( _mix_settings.sid.line2 != -1 )
539     *ip |= SOUND_MASK_LINE2;
540    if ( _mix_settings.sid.line3 != -1 )
541     *ip |= SOUND_MASK_LINE3;
542    if ( _mix_settings.sid.digital1 != -1 )
543     *ip |= SOUND_MASK_DIGITAL1;
544    if ( _mix_settings.sid.digital2 != -1 )
545     *ip |= SOUND_MASK_DIGITAL2;
546    if ( _mix_settings.sid.digital3 != -1 )
547     *ip |= SOUND_MASK_DIGITAL3;
548
549    return 0;
550   break;
551 }
552
553 ROAR_DBG("_ioctl_mixer(handle=%p, req=%lu, ip=%p): unknown mixer CTL", handle, req, ip);
554 ROAR_DBG("_ioctl_mixer(handle=%p, req=%lu, ip=%p) = -1 // errno = ENOSYS", handle, req, ip);
555 errno = ENOSYS;
556 return -1;
557}
558
559// -------------------------------------
560// emulated functions follow:
561// -------------------------------------
562
563int     open(const char *pathname, int flags, ...) {
564 int     ret;
565 mode_t  mode = 0;
566 va_list args;
567
568 _init();
569
570 ret = _open_file(pathname, flags);
571
572 switch (ret) {
573  case -2:       // continue as normal, use _op.open()
574   break;
575  case -1:       // pass error to caller
576    return -1;
577   break;
578  default:       // return successfully opened pointer to caller
579    return ret;
580   break;
581 }
582
583 if (flags & O_CREAT) {
584  va_start(args, flags);
585  mode = va_arg(args, mode_t);
586  va_end(args);
587 }
588
589 return _os.open(pathname, flags, mode);
590}
591
592int     close(int fd) {
593 struct pointer * pointer;
594 _init();
595
596 if ( (pointer = _get_pointer_by_fh(fd)) != NULL ) {
597  _close_pointer(pointer);
598  return 0;
599 }
600
601 return _os.close(fd);
602}
603
604ssize_t write(int fd, const void *buf, size_t count) {
605 struct pointer * pointer;
606
607 _init();
608
609 if ( (pointer = _get_pointer_by_fh(fd)) != NULL ) {
610  if ( pointer->handle->type == HT_STREAM ) {
611   if ( pointer->handle->stream_opened == 0 ) {
612    if ( _open_stream(pointer->handle) == -1 ) {
613     errno = EIO;
614     return -1;
615    }
616   }
617   return roar_vio_write(&(pointer->handle->stream_vio), (char*)buf, count);
618  } else {
619   errno = EINVAL;
620   return -1;
621  }
622 }
623
624 return _os.write(fd, buf, count);
625}
626
627ssize_t read(int fd, void *buf, size_t count) {
628 struct pointer * pointer;
629
630 _init();
631
632 if ( (pointer = _get_pointer_by_fh(fd)) != NULL ) {
633  if ( pointer->handle->type == HT_STREAM ) {
634   if ( pointer->handle->stream_opened == 0 ) {
635    if ( _open_stream(pointer->handle) == -1 ) {
636     errno = EIO;
637     return -1;
638    }
639   }
640   return roar_vio_read(&(pointer->handle->stream_vio), buf, count);
641  } else {
642   errno = EINVAL;
643   return -1;
644  }
645 }
646
647 return _os.read(fd, buf, count);
648}
649
650extern int ioctl (int __fd, unsigned long int __request, ...) {
651 struct pointer * pointer;
652 struct handle  * handle;
653 va_list args;
654 void *argp;
655 int * ip = NULL;
656
657 _init();
658
659// ROAR_DBG("ioctl(__fd=%i, __request=%lu) = ?", __fd, (long unsigned int) __request);
660
661 va_start (args, __request);
662 argp = va_arg (args, void *);
663 va_end (args);
664
665// ROAR_DBG("ioctl(__fd=%i, __request=%lu): argp=%p", __fd, (long unsigned int) __request, argp);
666
667 if ( (pointer = _get_pointer_by_fh(__fd)) != NULL ) {
668  ip = argp;
669//  ROAR_DBG("ioctl(__fd=%i, __request=%lu): ip=%p", __fd, (long unsigned int) __request, ip);
670  switch ((handle = pointer->handle)->type) {
671   case HT_STREAM:
672     switch (__request) {
673      case SNDCTL_DSP_RESET:
674      case SNDCTL_DSP_POST:
675       break;
676      case SNDCTL_DSP_SPEED:
677        handle->stream.info.rate = *ip;
678        return 0;
679       break;
680      case SNDCTL_DSP_CHANNELS:
681        handle->stream.info.channels = *ip;
682        return 0;
683       break;
684      case SNDCTL_DSP_SETFMT:
685        return _ioctl_stream_format(handle, *ip);
686       break;
687      case SNDCTL_DSP_GETFMTS:
688//        ROAR_DBG("ioctl(__fd=%i, __request=%lu): ip=%p", __fd, (long unsigned int) __request, ip);
689        *ip = _ioctl_stream_format_list();
690        return 0;
691       break;
692      default:
693        ROAR_DBG("ioctl(__fd=%i, __request=%lu) = -1 // errno = ENOSYS", __fd, (long unsigned int) __request);
694        errno = ENOSYS;
695        return -1;
696     }
697    break;
698   case HT_MIXER:
699     return _ioctl_mixer(handle, __request, ip);
700    break;
701   default:
702     ROAR_DBG("ioctl(__fd=%i, __request=%lu): unknown handle type: no ioctl()s supported", __fd, (long unsigned int) __request);
703     ROAR_DBG("ioctl(__fd=%i, __request=%lu) = -1 // errno = ENOSYS", __fd, (long unsigned int) __request);
704     errno = EINVAL;
705     return -1;
706    break;
707  }
708 }
709
710 return _os.ioctl(__fd, __request, argp);
711}
712
713#endif
714
715//ll
Note: See TracBrowser for help on using the repository browser.