source: roaraudio/roard/driver_oss.c @ 3358:7f9d211148e0

Last change on this file since 3358:7f9d211148e0 was 2368:4128bea7fc3e, checked in by phi, 15 years ago

set sstream if we have one at open time

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