source: roaraudio/roard/driver_oss.c @ 1530:570f7a39d6d0

Last change on this file since 1530:570f7a39d6d0 was 1530:570f7a39d6d0, checked in by phi, 15 years ago

added warning if device uses diffrent sample rate, added support for ROAR_VIO_CTL_GET_SSTREAMID

File size: 10.4 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
97 if ( fh == -1 )
98  return -1;
99
100#ifdef SNDCTL_DSP_CHANNELS
101 tmp = info->channels;
102
103 if ( ioctl(fh, SNDCTL_DSP_CHANNELS, &tmp) == -1 ) {
104  ROAR_ERR("driver_oss_open(*): can not set number of channels");
105  return -1;
106 }
107
108 if ( tmp != info->channels ) {
109   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);
110  return -1;
111 }
112#else
113 switch (info->channels) {
114  case  1: tmp = 0; break;
115  case  2: tmp = 1; break;
116  default: return -1;
117 }
118
119 if ( ioctl(fh, SNDCTL_DSP_STEREO, &tmp) == -1 ) {
120  ROAR_ERR("driver_oss_open(*): can not set number of channels");
121  return -1;
122 }
123#endif
124
125 switch (info->codec) {
126  case ROAR_CODEC_PCM_S_LE:
127    switch (info->bits) {
128     case  8: tmp = AFMT_S8;     break;
129     case 16: tmp = AFMT_S16_LE; break;
130//     case 24: tmp = AFMT_S24_PACKED; break;
131#ifdef AFMT_S32_LE
132     case 32: tmp = AFMT_S32_LE; break;
133#endif
134     default: return -1;
135    }
136   break;
137  case ROAR_CODEC_PCM_S_BE:
138    switch (info->bits) {
139     case  8: tmp = AFMT_S8;     break;
140     case 16: tmp = AFMT_S16_BE; break;
141//     case 24: tmp = AFMT_S24_PACKED; break;
142#ifdef AFMT_S32_BE
143     case 32: tmp = AFMT_S32_BE; break;
144#endif
145     default: return -1;
146    }
147   break;
148  case ROAR_CODEC_PCM_U_LE:
149    switch (info->bits) {
150     case  8: tmp = AFMT_U8;     break;
151     case 16: tmp = AFMT_U16_LE; break;
152     default: return -1;
153    }
154   break;
155  case ROAR_CODEC_PCM_U_BE:
156    switch (info->bits) {
157     case  8: tmp = AFMT_U8;     break;
158     case 16: tmp = AFMT_U16_BE; break;
159     default: return -1;
160    }
161  case ROAR_CODEC_ALAW:
162    tmp = AFMT_A_LAW;
163   break;
164  case ROAR_CODEC_MULAW:
165    tmp = AFMT_MU_LAW;
166   break;
167#ifdef AFMT_VORBIS
168  case ROAR_CODEC_OGG_VORBIS:
169    tmp = AFMT_VORBIS;
170   break;
171#endif
172  default:
173    return -1;
174   break;
175 }
176
177 ctmp = tmp;
178#ifdef SNDCTL_DSP_SETFMT
179 if ( ioctl(fh, SNDCTL_DSP_SETFMT, &tmp) == -1 ) {
180#else
181 if ( ioctl(fh, SNDCTL_DSP_SAMPLESIZE, &tmp) == -1 ) {
182#endif
183  ROAR_ERR("driver_oss_open(*): can not set sample format");
184  return -1;
185 }
186
187 if ( tmp != ctmp ) {
188  es = NULL;
189  switch (tmp) {
190   case AFMT_S8    : es = "bits=8,codec=pcm";       break;
191   case AFMT_U8    : es = "bits=8,codec=pcm_u_le";  break;
192   case AFMT_S16_LE: es = "bits=16,codec=pcm_s_le"; break;
193   case AFMT_S16_BE: es = "bits=16,codec=pcm_s_be"; break;
194   case AFMT_U16_LE: es = "bits=16,codec=pcm_u_le"; break;
195   case AFMT_U16_BE: es = "bits=16,codec=pcm_u_be"; break;
196#ifdef AFMT_S32_LE
197   case AFMT_S32_LE: es = "bits=32,codec=pcm_s_le"; break;
198#endif
199#ifdef AFMT_S32_BE
200   case AFMT_S32_BE: es = "bits=32,codec=pcm_s_be"; break;
201#endif
202   case AFMT_A_LAW : es = "codec=alaw";             break;
203   case AFMT_MU_LAW: es = "codec=mulaw";            break;
204#ifdef AFMT_VORBIS
205   case AFMT_VORBIS: es = "codec=ogg_vorbis";       break;
206#endif
207  }
208
209  if ( es != NULL ) {
210   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);
211  } else {
212   ROAR_ERR("driver_oss_open(*): can not set requested codec, set codec manuelly via -oO codec=somecodec");
213  }
214  return -1;
215 }
216
217 tmp = info->rate;
218
219 if ( ioctl(fh, SNDCTL_DSP_SPEED, &tmp) == -1 ) {
220  ROAR_ERR("driver_oss_open(*): can not set sample rate");
221  return -1;
222 }
223
224 if ( tmp != info->rate ) {
225  ROAR_WARN("driver_oss_config_device(*): Device does not support requested sample rate: req=%iHz, sug=%iHz",
226                    info->rate, tmp);
227
228  if ( tmp < info->rate * 0.98 || tmp > info->rate * 1.02 ) {
229   ROAR_ERR("driver_oss_open(*): sample rate out of acceptable accuracy");
230   return -1;
231  }
232 }
233
234 // latency things:
235#ifdef SNDCTL_DSP_SETFRAGMENT
236
237 // defaults
238 if ( self->blocksize < 1 )
239  self->blocksize = 2048;
240 if ( self->blocks < 1 )
241  self->blocks    =    4;
242
243 switch (self->blocksize) {
244  case 1<< 4: tmp =  4; break;
245  case 1<< 5: tmp =  5; break;
246  case 1<< 6: tmp =  6; break;
247  case 1<< 7: tmp =  7; break;
248  case 1<< 8: tmp =  8; break;
249  case 1<< 9: tmp =  9; break;
250  case 1<<10: tmp = 10; break;
251  case 1<<11: tmp = 11; break;
252  case 1<<12: tmp = 12; break;
253  case 1<<13: tmp = 13; break;
254  case 1<<14: tmp = 14; break;
255  case 1<<15: tmp = 15; break;
256  case 1<<16: tmp = 16; break;
257  default: tmp = 11;
258    ROAR_WARN("driver_oss_config_device(*): blocksize of %i byte is not a valid value. trying 2KB", self->blocksize);
259   break;
260 }
261
262 ROAR_DBG("driver_oss_config_device(*): blocksize=%i(N=%i), blocks=%i", self->blocksize, tmp, self->blocks);
263
264 tmp |= self->blocks << 16;
265 if ( ioctl(fh, SNDCTL_DSP_SETFRAGMENT, &tmp) == -1 ) {
266  ROAR_WARN("driver_oss_ctl(*): Can not set fragment size, sorry :(");
267 }
268#endif
269
270 self->need_config = 0;
271
272 return 0;
273}
274
275#define er() close(self->fh); if ( self->device ) free(self->device); free(self); return -1
276int driver_oss_open(struct roar_vio_calls * inst, char * device, struct roar_audio_info * info, int fh) {
277 struct driver_oss * self = NULL;
278
279 if ( (self = malloc(sizeof(struct driver_oss))) == NULL ) {
280  ROAR_ERR("driver_oss_open(*): Can not malloc() instance data: %s", strerror(errno));
281  return -1;
282 }
283
284 memset(self, 0, sizeof(struct driver_oss));
285 memcpy(&(self->info), info, sizeof(struct roar_audio_info));
286
287 self->ssid = -1;
288 self->fh   = fh;
289
290 if ( device != NULL )
291  self->device = strdup(device);
292
293 if ( driver_oss_init_vio(inst, self) == -1 ) {
294  ROAR_ERR("driver_oss_open(*): Can not init vio interface");
295  er();
296 }
297
298 if ( driver_oss_open_device(self) == -1 ) {
299  ROAR_ERR("driver_oss_open(*): Can not open audio device");
300  er();
301 }
302
303 ROAR_DBG("driver_oss_open(*): OSS devices opened :)");
304
305 return 0;
306}
307#undef er
308
309int driver_oss_close(DRIVER_USERDATA_T   inst) {
310 return roar_vio_close((struct roar_vio_calls *)inst);
311}
312
313int     driver_oss_close_vio(struct roar_vio_calls * vio) {
314 close(_get(vio,fh));
315
316 if ( _get(vio,device) != NULL )
317  free(_get(vio,device));
318
319 free(vio->inst);
320 return 0;
321}
322
323int     driver_oss_nonblock(struct roar_vio_calls * vio, int state) {
324 if ( roar_socket_nonblock(_get(vio,fh), state) == -1 )
325  return -1;
326
327 if ( state == ROAR_SOCKET_NONBLOCK )
328  return 0;
329
330 roar_vio_sync(vio);
331
332 return 0;
333}
334
335int driver_oss_sync(struct roar_vio_calls * vio) {
336#ifdef SNDCTL_DSP_SYNC
337 return ioctl(_get(vio,fh), SNDCTL_DSP_SYNC, NULL);
338#else
339 return 0;
340#endif
341}
342
343int driver_oss_ctl(struct roar_vio_calls * vio, int cmd, void * data) {
344 struct driver_oss * self = vio->inst;
345 int d;
346
347 ROAR_DBG("driver_oss_ctl(vio=%p, cmd=0x%.8x, data=%p) = ?", vio, cmd, data);
348
349 if ( vio == NULL )
350  return -1;
351
352 switch (cmd) {
353  case ROAR_VIO_CTL_GET_DELAY:
354#ifdef SNDCTL_DSP_GETODELAY
355    if ( ioctl(_get(vio,fh), SNDCTL_DSP_GETODELAY, &d) == -1 )
356     return -1;
357
358    ROAR_DBG("driver_oss_ctl(*): delay=%i byte", d);
359
360    *(uint_least32_t *)data = d;
361#else
362    return -1;
363#endif
364   break;
365  case ROAR_VIO_CTL_SET_DBLOCKS:
366#ifdef SNDCTL_DSP_SETFRAGMENT
367    if ( !self->need_config ) {
368     ROAR_WARN("driver_oss_ctl(*): possible late ROAR_VIO_CTL_SET_DBLOCKS, setting anyway.");
369    }
370
371    self->blocks    = *(uint_least32_t *)data;
372#else
373    return -1;
374#endif
375   break;
376  case ROAR_VIO_CTL_SET_DBLKSIZE:
377#ifdef SNDCTL_DSP_SETFRAGMENT
378    if ( !self->need_config ) {
379     ROAR_WARN("driver_oss_ctl(*): possible late ROAR_VIO_CTL_SET_DBLKSIZE, setting anyway.");
380    }
381
382    self->blocksize = *(uint_least32_t *)data;
383#else
384    return -1;
385#endif
386   break;
387  case ROAR_VIO_CTL_GET_DBLKSIZE:
388    if ( !self->blocksize )
389     return -1;
390
391    *(uint_least32_t *)data = self->blocksize;
392   break;
393  case ROAR_VIO_CTL_GET_SSTREAMID:
394    self->ssid = *(int *)data;
395   break;
396  case ROAR_VIO_CTL_SET_SSTREAM:
397    self->stream = data;
398   break;
399  default:
400   return -1;
401 }
402
403 return 0;
404}
405
406ssize_t driver_oss_write   (struct roar_vio_calls * vio, void *buf, size_t count) {
407 if ( _get(vio,need_config) ) {
408  if ( driver_oss_config_device(vio->inst) == -1 ) {
409   return -1;
410  }
411 }
412
413 return write(_get(vio,fh), buf, count);
414}
415
416#endif
417//ll
Note: See TracBrowser for help on using the repository browser.