source: roaraudio/roard/driver_oss.c @ 5055:db7a96528119

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

support auto reconf bits=32->16 in case not supported (See: #48)

File size: 15.9 KB
Line 
1//driver_oss.c:
2
3/*
4 *      Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2008-2011
5 *
6 *  This file is part of roard 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 *  RoarAudio 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 */
25
26#include "roard.h"
27#if defined(ROAR_HAVE_OSS_BSD) || defined(ROAR_HAVE_OSS)
28
29
30#define _get(vio,obj) (((struct driver_oss*)((vio)->inst))->obj)
31
32int driver_oss_init_vio(struct roar_vio_calls * vio, struct driver_oss * inst) {
33 if ( vio == NULL )
34  return -1;
35
36 memset(vio, 0, sizeof(struct roar_vio_calls));
37
38 vio->write    = driver_oss_write;
39 vio->read     = driver_oss_read;
40 vio->nonblock = driver_oss_nonblock;
41 vio->sync     = driver_oss_sync;
42 vio->ctl      = driver_oss_ctl;
43 vio->close    = driver_oss_close_vio;
44
45 vio->inst     = (void*) inst;
46
47 return 0;
48}
49
50int driver_oss_open_device(struct driver_oss * self) {
51 int    flags  = 0;
52 int    rw     = 0;
53 int    fh     = self->fh;
54 char * device = self->device;
55
56 if ( fh != -1 )
57  return 0;
58
59#ifdef ROAR_DEFAULT_OSS_DEV
60 if ( device == NULL )
61  device = ROAR_DEFAULT_OSS_DEV;
62#endif
63
64 if ( device == NULL ) {
65  ROAR_ERR("driver_oss_open_device(*): no default device found, you need to specify one manuelly");
66  return -1;
67 }
68
69 if ( self->ssid != -1 ) {
70  rw = streams_get_flag(self->ssid, ROAR_FLAG_RECSOURCE);
71 }
72
73 if ( rw ) {
74  flags |= O_RDWR;
75 } else {
76  flags |= O_WRONLY;
77 }
78
79 if ( (fh = open(device, flags, 0644)) == -1 ) {
80  ROAR_ERR("driver_oss_open_device(*): Can not open OSS device: %s: %s", device, strerror(errno));
81  return -1;
82 }
83
84 self->fh          = fh;
85 self->need_config = 1;
86
87 return 0;
88}
89
90int driver_oss_config_device(struct driver_oss * self) {
91 int                      fh   =   self->fh;
92 struct roar_audio_info * info = &(self->info);
93 int tmp, ctmp;
94 char * es;
95 int autoconfig         = 0;
96 int need_update_server = 0;
97
98 if ( fh == -1 )
99  return -1;
100
101 if ( self->ssid != -1 ) {
102  autoconfig = streams_get_flag(self->ssid, ROAR_FLAG_AUTOCONF);
103 }
104
105 ROAR_DBG("driver_oss_config_device(self=%p): ssid=%i, autoconfig=%i", self, self->ssid, autoconfig);
106
107#ifdef SNDCTL_DSP_CHANNELS
108 tmp = info->channels;
109
110 if ( ioctl(fh, SNDCTL_DSP_CHANNELS, &tmp) == -1 ) {
111  ROAR_ERR("driver_oss_config_device(*): can not set number of channels");
112  return -1;
113 }
114
115 if ( tmp != info->channels ) {
116  if ( autoconfig ) {
117   need_update_server = 1;
118   self->info.channels = tmp;
119  } else {
120   ROAR_ERR("driver_oss_config_device(*): can not set requested numer of channels, OSS suggested %i channels, to use this restart with -oO channels=%i or set codec manuelly via -oO channels=num", tmp, tmp);
121   return -1;
122  }
123 }
124#else
125 switch (info->channels) {
126  case  1: tmp = 0; break;
127  case  2: tmp = 1; break;
128  default: return -1;
129 }
130
131 if ( ioctl(fh, SNDCTL_DSP_STEREO, &tmp) == -1 ) {
132  ROAR_ERR("driver_oss_config_device(*): can not set number of channels");
133  return -1;
134 }
135#endif
136
137 if ( autoconfig ) {
138  if ( info->bits > 16 ) {
139   info->bits = 32;
140   need_update_server = 1;
141  }
142
143  if ( info->bits == 32 ) {
144   switch (info->codec) {
145#ifndef AFMT_S32_LE
146    case ROAR_CODEC_PCM_S_LE:
147#endif
148#ifndef AFMT_S32_BE
149    case ROAR_CODEC_PCM_S_BE:
150#endif
151    case ROAR_CODEC_PCM_U_LE:
152    case ROAR_CODEC_PCM_U_BE:
153      info->bits = 16;
154      need_update_server = 1;
155     break;
156   }
157  }
158 }
159
160 switch (info->codec) {
161  case ROAR_CODEC_PCM_S_LE:
162    switch (info->bits) {
163     case  8: tmp = AFMT_S8;     break;
164     case 16: tmp = AFMT_S16_LE; break;
165//     case 24: tmp = AFMT_S24_PACKED; break;
166#ifdef AFMT_S32_LE
167     case 32: tmp = AFMT_S32_LE; break;
168#endif
169     default: return -1;
170    }
171   break;
172  case ROAR_CODEC_PCM_S_BE:
173    switch (info->bits) {
174     case  8: tmp = AFMT_S8;     break;
175     case 16: tmp = AFMT_S16_BE; break;
176//     case 24: tmp = AFMT_S24_PACKED; break;
177#ifdef AFMT_S32_BE
178     case 32: tmp = AFMT_S32_BE; break;
179#endif
180     default: return -1;
181    }
182   break;
183  case ROAR_CODEC_PCM_U_LE:
184    switch (info->bits) {
185     case  8: tmp = AFMT_U8;     break;
186     case 16: tmp = AFMT_U16_LE; break;
187     default: return -1;
188    }
189   break;
190  case ROAR_CODEC_PCM_U_BE:
191    switch (info->bits) {
192     case  8: tmp = AFMT_U8;     break;
193     case 16: tmp = AFMT_U16_BE; break;
194     default: return -1;
195    }
196  case ROAR_CODEC_ALAW:
197    tmp = AFMT_A_LAW;
198   break;
199  case ROAR_CODEC_MULAW:
200    tmp = AFMT_MU_LAW;
201   break;
202#ifdef AFMT_VORBIS
203  case ROAR_CODEC_OGG_VORBIS:
204    tmp = AFMT_VORBIS;
205   break;
206#endif
207  default:
208    return -1;
209   break;
210 }
211
212 ctmp = tmp;
213#ifdef SNDCTL_DSP_SETFMT
214 if ( ioctl(fh, SNDCTL_DSP_SETFMT, &tmp) == -1 ) {
215#else
216 if ( ioctl(fh, SNDCTL_DSP_SAMPLESIZE, &tmp) == -1 ) {
217#endif
218  ROAR_ERR("driver_oss_config_device(*): can not set sample format");
219  return -1;
220 }
221
222 if ( tmp != ctmp ) {
223  if ( autoconfig ) {
224   need_update_server = 1;
225   switch (tmp) {
226    case AFMT_S8    : self->info.bits =  8; self->info.codec = ROAR_CODEC_PCM;      break;
227    case AFMT_U8    : self->info.bits =  8; self->info.codec = ROAR_CODEC_PCM_U_LE; break;
228    case AFMT_S16_LE: self->info.bits = 16; self->info.codec = ROAR_CODEC_PCM_S_LE; break;
229    case AFMT_S16_BE: self->info.bits = 16; self->info.codec = ROAR_CODEC_PCM_S_BE; break;
230    case AFMT_U16_LE: self->info.bits = 16; self->info.codec = ROAR_CODEC_PCM_U_LE; break;
231    case AFMT_U16_BE: self->info.bits = 16; self->info.codec = ROAR_CODEC_PCM_U_BE; break;
232#ifdef AFMT_S32_LE
233    case AFMT_S32_LE: self->info.bits = 32; self->info.codec = ROAR_CODEC_PCM_S_LE; break;
234#endif
235#ifdef AFMT_S32_BE
236    case AFMT_S32_BE: self->info.bits = 32; self->info.codec = ROAR_CODEC_PCM_S_BE; break;
237#endif
238/*
239    case AFMT_A_LAW : self->info.bits =  8; self->info.codec = ROAR_CODEC_ALAW;     break;
240    case AFMT_MU_LAW: self->info.bits =  8; self->info.codec = ROAR_CODEC_MULAW;    break;
241#ifdef AFMT_VORBIS
242    case AFMT_VORBIS: self->info.codec = ROAR_CODEC_OGG_VORBIS;                     break;
243#endif
244*/
245    case AFMT_A_LAW:
246    case AFMT_MU_LAW:
247#ifdef AFMT_VORBIS
248    case AFMT_VORBIS:
249#endif
250      ROAR_WARN("driver_oss_config_device(*): Auto config failed: OSS Codec %i needs a codecfilter!", tmp);
251      ROAR_ERR("driver_oss_config_device(*): can not set requested codec, set codec manuelly via -oO codec=somecodec");
252      return -1;
253     break;
254    default:
255      ROAR_WARN("driver_oss_config_device(*): Auto config failed: unknown OSS Codec %i", tmp);
256      ROAR_ERR("driver_oss_config_device(*): can not set requested codec, set codec manuelly via -oO codec=somecodec");
257      return -1;
258     break;
259   }
260  } else {
261   es = NULL;
262   switch (tmp) {
263    case AFMT_S8    : es = "bits=8,codec=pcm";       break;
264    case AFMT_U8    : es = "bits=8,codec=pcm_u_le";  break;
265    case AFMT_S16_LE: es = "bits=16,codec=pcm_s_le"; break;
266    case AFMT_S16_BE: es = "bits=16,codec=pcm_s_be"; break;
267    case AFMT_U16_LE: es = "bits=16,codec=pcm_u_le"; break;
268    case AFMT_U16_BE: es = "bits=16,codec=pcm_u_be"; break;
269#ifdef AFMT_S32_LE
270    case AFMT_S32_LE: es = "bits=32,codec=pcm_s_le"; break;
271#endif
272#ifdef AFMT_S32_BE
273    case AFMT_S32_BE: es = "bits=32,codec=pcm_s_be"; break;
274#endif
275    case AFMT_A_LAW : es = "codec=alaw";             break;
276    case AFMT_MU_LAW: es = "codec=mulaw";            break;
277#ifdef AFMT_VORBIS
278    case AFMT_VORBIS: es = "codec=ogg_vorbis";       break;
279#endif
280   }
281
282   if ( es != NULL ) {
283    ROAR_ERR("driver_oss_config_device(*): can not set requested codec, OSS retruned another codec than requested, to use this restart with -oO %s or set codec manuelly via -oO codec=somecodec", es);
284   } else {
285    ROAR_ERR("driver_oss_config_device(*): can not set requested codec, set codec manuelly via -oO codec=somecodec");
286   }
287   return -1;
288  }
289 }
290
291 tmp = info->rate;
292
293 if ( ioctl(fh, SNDCTL_DSP_SPEED, &tmp) == -1 ) {
294  ROAR_ERR("driver_oss_config_device(*): can not set sample rate");
295  return -1;
296 }
297
298 if ( tmp != info->rate ) {
299  if ( autoconfig ) {
300   need_update_server = 1;
301   self->info.rate = tmp;
302  } else {
303   ROAR_WARN("driver_oss_config_device(*): Device does not support requested sample rate: req=%iHz, sug=%iHz",
304                     info->rate, tmp);
305
306   if ( tmp < info->rate * 0.98 || tmp > info->rate * 1.02 ) {
307    ROAR_ERR("driver_oss_config_device(*): sample rate out of acceptable accuracy");
308    return -1;
309   }
310  }
311 }
312
313 // latency things:
314#ifdef SNDCTL_DSP_SETFRAGMENT
315
316 // defaults
317 if ( self->blocksize < 1 )
318  self->blocksize = 2048;
319 if ( self->blocks < 1 )
320  self->blocks    =    4;
321
322 switch (self->blocksize) {
323  case 1<< 4: tmp =  4; break;
324  case 1<< 5: tmp =  5; break;
325  case 1<< 6: tmp =  6; break;
326  case 1<< 7: tmp =  7; break;
327  case 1<< 8: tmp =  8; break;
328  case 1<< 9: tmp =  9; break;
329  case 1<<10: tmp = 10; break;
330  case 1<<11: tmp = 11; break;
331  case 1<<12: tmp = 12; break;
332  case 1<<13: tmp = 13; break;
333  case 1<<14: tmp = 14; break;
334  case 1<<15: tmp = 15; break;
335  case 1<<16: tmp = 16; break;
336  default: tmp = 11;
337    ROAR_WARN("driver_oss_config_device(*): blocksize of %i byte is not a valid value. trying 2KB", self->blocksize);
338   break;
339 }
340
341 ROAR_DBG("driver_oss_config_device(*): blocksize=%i(N=%i), blocks=%i", self->blocksize, tmp, self->blocks);
342
343 tmp |= self->blocks << 16;
344 if ( ioctl(fh, SNDCTL_DSP_SETFRAGMENT, &tmp) == -1 ) {
345  ROAR_WARN("driver_oss_ctl(*): Can not set fragment size, sorry :(");
346 }
347#endif
348
349 if ( need_update_server ) {
350  if ( self->stream == NULL ) {
351   streams_get(self->ssid, &(self->stream));
352  }
353
354  if ( self->stream == NULL ) {
355   ROAR_ERR("driver_oss_config_device(*): Auto config failed: can not set new values for stream: no stream object known");
356   return -1;
357  }
358
359  memcpy(&(ROAR_STREAM(self->stream)->info), info, sizeof(struct roar_audio_info));
360 }
361
362 ROAR_DBG("driver_oss_config_device(*): self->ssid=%i, fh=%i", self->ssid, fh);
363 streams_set_fh(self->ssid, -2);
364 self->need_config = 0;
365
366 ROAR_DBG("driver_oss_config_device(*) = 0");
367 return 0;
368}
369
370#define er() close(self->fh); if ( self->device ) roar_mm_free(self->device); roar_mm_free(self); return -1
371int driver_oss_open(struct roar_vio_calls * inst, char * device, struct roar_audio_info * info, int fh, struct roar_stream_server * sstream) {
372 struct driver_oss * self = NULL;
373
374 if ( (self = roar_mm_malloc(sizeof(struct driver_oss))) == NULL ) {
375  ROAR_ERR("driver_oss_open(*): Can not roar_mm_malloc() instance data: %s", strerror(errno));
376  return -1;
377 }
378
379 memset(self, 0, sizeof(struct driver_oss));
380 memcpy(&(self->info), info, sizeof(struct roar_audio_info));
381
382 self->ssid = -1;
383 self->fh   = fh;
384
385 if ( sstream != NULL )
386  self->ssid = ROAR_STREAM(sstream)->id;
387
388 if ( fh != -1 ) {
389  self->fh_savemode = 1;
390 } else {
391  self->fh_savemode = 0;
392 }
393
394 if ( device != NULL )
395  self->device = roar_mm_strdup(device);
396
397 if ( driver_oss_init_vio(inst, self) == -1 ) {
398  ROAR_ERR("driver_oss_open(*): Can not init vio interface");
399  er();
400 }
401
402 if ( driver_oss_open_device(self) == -1 ) {
403  ROAR_ERR("driver_oss_open(*): Can not open audio device");
404  er();
405 }
406
407 ROAR_DBG("driver_oss_open(*): OSS devices opened :)");
408
409 if ( sstream != NULL )
410  driver_oss_ctl(inst, ROAR_VIO_CTL_SET_SSTREAM, sstream);
411
412 return 0;
413}
414#undef er
415
416int     driver_oss_reopen_device(struct driver_oss * self) {
417
418 // we need to reject in fh save mode.
419 if ( self->fh_savemode )
420  return -1;
421
422#ifdef SNDCTL_DSP_SYNC
423 ioctl(self->fh, SNDCTL_DSP_SYNC, NULL);
424#endif
425
426 close(self->fh);
427
428 if ( driver_oss_open_device(self) == -1 )
429  return -1;
430
431 self->need_config = 1;
432
433 return 0;
434}
435
436int driver_oss_close(DRIVER_USERDATA_T   inst) {
437 return roar_vio_close((struct roar_vio_calls *)inst);
438}
439
440int     driver_oss_close_vio(struct roar_vio_calls * vio) {
441 close(_get(vio,fh));
442
443 if ( _get(vio,device) != NULL )
444  roar_mm_free(_get(vio,device));
445
446 roar_mm_free(vio->inst);
447 return 0;
448}
449
450int     driver_oss_nonblock(struct roar_vio_calls * vio, int state) {
451 ROAR_DBG("driver_oss_nonblock(vio=%p, state=%i) = ?", vio, state);
452
453 if ( roar_socket_nonblock(_get(vio,fh), state) == -1 ) {
454  ROAR_DBG("driver_oss_nonblock(vio=%p, state=%i) = -1", vio, state);
455  return -1;
456 }
457
458 if ( state == ROAR_SOCKET_NONBLOCK ) {
459  ROAR_DBG("driver_oss_nonblock(vio=%p, state=%i) = 0", vio, state);
460  return 0;
461 }
462
463 roar_vio_sync(vio);
464
465 ROAR_DBG("driver_oss_nonblock(vio=%p, state=%i) = 0", vio, state);
466
467 return 0;
468}
469
470int driver_oss_sync(struct roar_vio_calls * vio) {
471#ifdef SNDCTL_DSP_SYNC
472 return ioctl(_get(vio,fh), SNDCTL_DSP_SYNC, NULL);
473#else
474 return 0;
475#endif
476}
477
478int driver_oss_ctl(struct roar_vio_calls * vio, int cmd, void * data) {
479 struct driver_oss * self = vio->inst;
480#ifdef SNDCTL_DSP_GETODELAY
481 int d;
482#endif
483
484 ROAR_DBG("driver_oss_ctl(vio=%p, cmd=0x%.8x, data=%p) = ?", vio, cmd, data);
485
486 if ( vio == NULL )
487  return -1;
488
489 switch (cmd) {
490  case ROAR_VIO_CTL_GET_DELAY:
491#ifdef SNDCTL_DSP_GETODELAY
492    if ( ioctl(_get(vio,fh), SNDCTL_DSP_GETODELAY, &d) == -1 )
493     return -1;
494
495    ROAR_DBG("driver_oss_ctl(*): delay=%i byte", d);
496
497    *(uint_least32_t *)data = d;
498#else
499    return -1;
500#endif
501   break;
502  case ROAR_VIO_CTL_SET_DBLOCKS:
503#ifdef SNDCTL_DSP_SETFRAGMENT
504    if ( !self->need_config ) {
505     ROAR_WARN("driver_oss_ctl(*): possible late ROAR_VIO_CTL_SET_DBLOCKS, setting anyway.");
506    }
507
508    self->blocks    = *(uint_least32_t *)data;
509#else
510    return -1;
511#endif
512   break;
513  case ROAR_VIO_CTL_SET_DBLKSIZE:
514#ifdef SNDCTL_DSP_SETFRAGMENT
515    if ( !self->need_config ) {
516     ROAR_WARN("driver_oss_ctl(*): possible late ROAR_VIO_CTL_SET_DBLKSIZE, setting anyway.");
517    }
518
519    self->blocksize = *(uint_least32_t *)data;
520#else
521    return -1;
522#endif
523   break;
524  case ROAR_VIO_CTL_GET_DBLKSIZE:
525    if ( !self->blocksize )
526     return -1;
527
528    *(uint_least32_t *)data = self->blocksize;
529   break;
530  case ROAR_VIO_CTL_SET_SSTREAMID:
531    self->ssid = *(int *)data;
532   break;
533  case ROAR_VIO_CTL_SET_SSTREAM:
534    self->stream = data;
535   break;
536  case ROAR_VIO_CTL_GET_AUINFO:
537    memcpy(data, &(self->info), sizeof(struct roar_audio_info));
538   break;
539  case ROAR_VIO_CTL_SET_AUINFO:
540    memcpy(&(self->info), data, sizeof(struct roar_audio_info));
541    return driver_oss_reopen_device(self);
542   break;
543#ifdef SNDCTL_DSP_SETPLAYVOL
544  case ROAR_VIO_CTL_SET_VOLUME:
545    switch (self->info.channels) {
546     case 1:
547       d  = ROAR_MIXER(data)->mixer[0] * 100 / ROAR_MIXER(data)->scale;
548       d |= d << 8;
549      break;
550     case 2:
551       d  =  ROAR_MIXER(data)->mixer[0] * 100 / ROAR_MIXER(data)->scale;
552       d |= (ROAR_MIXER(data)->mixer[0] * 100 / ROAR_MIXER(data)->scale) << 8;
553      break;
554     default:
555      return -1;
556    }
557    return ioctl(_get(vio,fh), SNDCTL_DSP_SETPLAYVOL, &d);
558   break;
559#endif
560  default:
561   return -1;
562 }
563
564 return 0;
565}
566
567ssize_t driver_oss_write   (struct roar_vio_calls * vio, void *buf, size_t count) {
568 ROAR_DBG("driver_oss_write(vio=%p, buf=%p, count=%llu) = ?", vio, buf, (long long unsigned int)count);
569
570 if ( _get(vio,fh) == -1 )
571  return -1;
572
573 if ( _get(vio,need_config) ) {
574  if ( driver_oss_config_device(vio->inst) == -1 ) {
575   return -1;
576  }
577 }
578
579 return write(_get(vio,fh), buf, count);
580}
581
582ssize_t driver_oss_read     (struct roar_vio_calls * vio, void *buf, size_t count) {
583 ROAR_DBG("driver_oss_read(vio=%p, buf=%p, count=%llu) = ?", vio, buf, (long long unsigned int)count);
584
585 if ( _get(vio,fh) == -1 )
586  return -1;
587
588 if ( _get(vio,need_config) ) {
589  if ( driver_oss_config_device(vio->inst) == -1 ) {
590   ROAR_DBG("driver_oss_read(vio=%p, buf=%p, count=%llu) = -1", vio, buf, (long long unsigned int)count);
591   return -1;
592  }
593 }
594
595 ROAR_DBG("driver_oss_read(vio=%p, buf=%p, count=%llu) = ?", vio, buf, (long long unsigned int)count);
596
597 return read(_get(vio,fh), buf, count);
598}
599
600#endif
601//ll
Note: See TracBrowser for help on using the repository browser.