source: roaraudio/roard/driver_oss.c @ 1544:9629323e9999

Last change on this file since 1544:9629323e9999 was 1544:9629323e9999, checked in by phi, 15 years ago

moved parts that should be in the header from the c code file into the header file

File size: 13.2 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) {
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 return 0;
361}
362#undef er
363
364int     driver_oss_reopen_device(struct driver_oss * self) {
365#ifdef SNDCTL_DSP_SYNC
366 ioctl(self->fh, SNDCTL_DSP_SYNC, NULL);
367#endif
368
369 close(self->fh);
370
371 if ( driver_oss_open_device(self) == -1 )
372  return -1;
373
374 self->need_config = 1;
375
376 return 0;
377}
378
379int driver_oss_close(DRIVER_USERDATA_T   inst) {
380 return roar_vio_close((struct roar_vio_calls *)inst);
381}
382
383int     driver_oss_close_vio(struct roar_vio_calls * vio) {
384 close(_get(vio,fh));
385
386 if ( _get(vio,device) != NULL )
387  free(_get(vio,device));
388
389 free(vio->inst);
390 return 0;
391}
392
393int     driver_oss_nonblock(struct roar_vio_calls * vio, int state) {
394 if ( roar_socket_nonblock(_get(vio,fh), state) == -1 )
395  return -1;
396
397 if ( state == ROAR_SOCKET_NONBLOCK )
398  return 0;
399
400 roar_vio_sync(vio);
401
402 return 0;
403}
404
405int driver_oss_sync(struct roar_vio_calls * vio) {
406#ifdef SNDCTL_DSP_SYNC
407 return ioctl(_get(vio,fh), SNDCTL_DSP_SYNC, NULL);
408#else
409 return 0;
410#endif
411}
412
413int driver_oss_ctl(struct roar_vio_calls * vio, int cmd, void * data) {
414 struct driver_oss * self = vio->inst;
415#ifdef SNDCTL_DSP_GETODELAY
416 int d;
417#endif
418
419 ROAR_DBG("driver_oss_ctl(vio=%p, cmd=0x%.8x, data=%p) = ?", vio, cmd, data);
420
421 if ( vio == NULL )
422  return -1;
423
424 switch (cmd) {
425  case ROAR_VIO_CTL_GET_DELAY:
426#ifdef SNDCTL_DSP_GETODELAY
427    if ( ioctl(_get(vio,fh), SNDCTL_DSP_GETODELAY, &d) == -1 )
428     return -1;
429
430    ROAR_DBG("driver_oss_ctl(*): delay=%i byte", d);
431
432    *(uint_least32_t *)data = d;
433#else
434    return -1;
435#endif
436   break;
437  case ROAR_VIO_CTL_SET_DBLOCKS:
438#ifdef SNDCTL_DSP_SETFRAGMENT
439    if ( !self->need_config ) {
440     ROAR_WARN("driver_oss_ctl(*): possible late ROAR_VIO_CTL_SET_DBLOCKS, setting anyway.");
441    }
442
443    self->blocks    = *(uint_least32_t *)data;
444#else
445    return -1;
446#endif
447   break;
448  case ROAR_VIO_CTL_SET_DBLKSIZE:
449#ifdef SNDCTL_DSP_SETFRAGMENT
450    if ( !self->need_config ) {
451     ROAR_WARN("driver_oss_ctl(*): possible late ROAR_VIO_CTL_SET_DBLKSIZE, setting anyway.");
452    }
453
454    self->blocksize = *(uint_least32_t *)data;
455#else
456    return -1;
457#endif
458   break;
459  case ROAR_VIO_CTL_GET_DBLKSIZE:
460    if ( !self->blocksize )
461     return -1;
462
463    *(uint_least32_t *)data = self->blocksize;
464   break;
465  case ROAR_VIO_CTL_SET_SSTREAMID:
466    self->ssid = *(int *)data;
467   break;
468  case ROAR_VIO_CTL_SET_SSTREAM:
469    self->stream = data;
470   break;
471  case ROAR_VIO_CTL_GET_AUINFO:
472    memcpy(data, &(self->info), sizeof(struct roar_audio_info));
473   break;
474  case ROAR_VIO_CTL_SET_AUINFO:
475    memcpy(&(self->info), data, sizeof(struct roar_audio_info));
476    return driver_oss_reopen_device(self);
477   break;
478  default:
479   return -1;
480 }
481
482 return 0;
483}
484
485ssize_t driver_oss_write   (struct roar_vio_calls * vio, void *buf, size_t count) {
486 if ( _get(vio,fh) == -1 )
487  return -1;
488
489 if ( _get(vio,need_config) ) {
490  if ( driver_oss_config_device(vio->inst) == -1 ) {
491   return -1;
492  }
493 }
494
495 return write(_get(vio,fh), buf, count);
496}
497
498#endif
499//ll
Note: See TracBrowser for help on using the repository browser.