source: roaraudio/roard/driver_oss.c @ 1532:ce5df2df033d

Last change on this file since 1532:ce5df2df033d was 1532:ce5df2df033d, checked in by phi, 15 years ago

got auto config working, need cleanup

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