1 #include "config.h"
2
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <unistd.h>
6 #include <string.h>
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <pthread.h>
10 #include <sys/ioctl.h>
11 #ifdef HAVE_SOUNDCARD_H
12 # include <soundcard.h>
13 #endif
14 #ifdef HAVE_SYS_SOUNDCARD_H
15 # include <sys/soundcard.h>
16 #endif
17
18 #include "grab-ng.h"
19
20 /* -------------------------------------------------------------------- */
21
22 extern int debug;
23 static const char *names[] = SOUND_DEVICE_NAMES;
24
25 static int mixer_read_attr(struct ng_attribute *attr);
26 static void mixer_write_attr(struct ng_attribute *attr, int val);
27
28 struct mixer_handle {
29 int mix;
30 int volctl;
31 int volume;
32 int muted;
33 };
34
35 static struct ng_attribute mixer_attrs[] = {
36 {
37 id: ATTR_ID_MUTE,
38 name: "mute",
39 type: ATTR_TYPE_BOOL,
40 read: mixer_read_attr,
41 write: mixer_write_attr,
42 },{
43 id: ATTR_ID_VOLUME,
44 name: "volume",
45 type: ATTR_TYPE_INTEGER,
46 min: 0,
47 max: 100,
48 read: mixer_read_attr,
49 write: mixer_write_attr,
50 },{
51 /* end of list */
52 }
53 };
54
55 static void
56 mixer_close(void *handle)
57 {
58 struct mixer_handle *h = handle;
59
60 if (-1 != h->mix)
61 close(h->mix);
62 free(h);
63 }
64
65 static void*
66 mixer_open(char *device)
67 {
68 struct mixer_handle *h;
69
70 h = malloc(sizeof(*h));
71 memset(h,0,sizeof(*h));
72 h->mix = -1;
73 h->volctl = -1;
74
75 if (-1 == (h->mix = open(device,O_RDONLY))) {
76 fprintf(stderr,"open %s: %s\n",device,strerror(errno));
77 goto err;
78 }
79 fcntl(h->mix,F_SETFD,FD_CLOEXEC);
80 return h;
81
82 err:
83 mixer_close(h);
84 return NULL;
85 }
86
87 static struct ng_attribute*
88 mixer_volctl(void *handle, char *channel)
89 {
90 struct mixer_handle *h = handle;
91 struct ng_attribute *attrs;
92 int i, devmask;
93
94 if (-1 == ioctl(h->mix,MIXER_READ(SOUND_MIXER_DEVMASK),&devmask)) {
95 fprintf(stderr,"oss mixer read devmask: %s",strerror(errno));
96 return NULL;
97 }
98 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
99 if ((1<<i) & devmask && strcasecmp(names[i],channel) == 0) {
100 if (-1 == ioctl(h->mix,MIXER_READ(i),&h->volume)) {
101 fprintf(stderr,"oss mixer read volume: %s",strerror(errno));
102 return NULL;
103 } else {
104 h->volctl = i;
105 }
106 }
107 }
108
109 if (-1 == h->volctl) {
110 fprintf(stderr,"oss mixer: '%s' not found, available:", channel);
111 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
112 if ((1<<i) & devmask)
113 fprintf(stderr," '%s'",names[i]);
114 fprintf(stderr,"\n");
115 return NULL;
116 }
117
118 attrs = malloc(sizeof(mixer_attrs));
119 memcpy(attrs,mixer_attrs,sizeof(mixer_attrs));
120 for (i = 0; attrs[i].name != NULL; i++)
121 attrs[i].handle = h;
122
123 return attrs;
124 }
125
126 static int
127 mixer_read_attr(struct ng_attribute *attr)
128 {
129 struct mixer_handle *h = attr->handle;
130 int vol;
131
132 switch (attr->id) {
133 case ATTR_ID_VOLUME:
134 if (-1 == ioctl(h->mix,MIXER_READ(h->volctl),&h->volume))
135 perror("oss mixer read volume");
136 vol = h->volume & 0x7f;
137 return vol;
138 case ATTR_ID_MUTE:
139 return h->muted;
140 default:
141 return -1;
142 }
143 }
144
145 static void
146 mixer_write_attr(struct ng_attribute *attr, int val)
147 {
148 struct mixer_handle *h = attr->handle;
149
150 switch (attr->id) {
151 case ATTR_ID_VOLUME:
152 val &= 0x7f;
153 h->volume = val | (val << 8);
154 if (-1 == ioctl(h->mix,MIXER_WRITE(h->volctl),&h->volume))
155 perror("oss mixer write volume");
156 h->muted = 0;
157 break;
158 case ATTR_ID_MUTE:
159 h->muted = val;
160 if (h->muted) {
161 int zero = 0;
162 if (-1 == ioctl(h->mix,MIXER_READ(h->volctl),&h->volume))
163 perror("oss mixer read volume");
164 if (-1 == ioctl(h->mix,MIXER_WRITE(h->volctl),&zero))
165 perror("oss mixer write volume");
166 } else {
167 if (-1 == ioctl(h->mix,MIXER_WRITE(h->volctl),&h->volume))
168 perror("oss mixer write volume");
169 }
170 break;
171 }
172 }
173
174 static struct ng_devinfo* mixer_probe(void)
175 {
176 struct ng_devinfo *info = NULL;
177 int i,n,fd;
178 #ifdef SOUND_MIXER_INFO
179 mixer_info minfo;
180 #endif
181
182 n = 0;
183 for (i = 0; NULL != ng_dev.mixer_scan[i]; i++) {
184 fd = open(ng_dev.mixer_scan[i],O_RDONLY);
185 if (-1 == fd)
186 continue;
187 info = realloc(info,sizeof(*info) * (n+2));
188 memset(info+n,0,sizeof(*info)*2);
189 strcpy(info[n].device,ng_dev.mixer_scan[i]);
190 #ifdef SOUND_MIXER_INFO
191 ioctl(fd,SOUND_MIXER_INFO,&minfo);
192 strcpy(info[n].name,minfo.name);
193 #else
194 strcpy(info[n].name,ng_dev.mixer_scan[i]);
195 #endif
196 close(fd);
197 n++;
198 }
199 return info;
200 }
201
202 static struct ng_devinfo*
203 mixer_channels(char *device)
204 {
205 struct ng_devinfo *info = NULL;
206 static char *names[] = SOUND_DEVICE_NAMES;
207 static char *labels[] = SOUND_DEVICE_LABELS;
208 int fd,i,n,devmask;
209
210 if (-1 == (fd = open(device,O_RDONLY))) {
211 fprintf(stderr,"open %s: %s\n",device,strerror(errno));
212 return NULL;
213 }
214 n = 0;
215 ioctl(fd,MIXER_READ(SOUND_MIXER_DEVMASK),&devmask);
216 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
217 if (!((1<<i) & devmask))
218 continue;
219 info = realloc(info,sizeof(*info) * (n+2));
220 memset(info+n,0,sizeof(*info)*2);
221 strcpy(info[n].device,names[i]);
222 strcpy(info[n].name,labels[i]);
223 n++;
224 }
225 close(fd);
226 return info;
227 }
228
229 struct ng_mix_driver oss_mixer = {
230 name: "oss",
231 probe: mixer_probe,
232 channels: mixer_channels,
233 open: mixer_open,
234 volctl: mixer_volctl,
235 close: mixer_close,
236 };
237
238 /* ------------------------------------------------------------------- */
239
240 struct oss_handle {
241 int fd;
242
243 /* oss */
244 struct ng_audio_fmt ifmt;
245 unsigned int afmt,channels,rate;
246 unsigned int blocksize;
247
248 /* me */
249 struct ng_audio_fmt ofmt;
250 int byteswap;
251 int bytes;
252 int bytes_per_sec;
253 };
254
255 static const unsigned int afmt_to_oss[AUDIO_FMT_COUNT] = {
256 0,
257 AFMT_U8,
258 AFMT_U8,
259 AFMT_S16_LE,
260 AFMT_S16_LE,
261 AFMT_S16_BE,
262 AFMT_S16_BE
263 };
264
265 static int
266 oss_setformat(struct oss_handle *h, struct ng_audio_fmt *fmt)
267 {
268 int frag;
269
270 if (0 == afmt_to_oss[fmt->fmtid])
271 return -1;
272
273 h->afmt = afmt_to_oss[fmt->fmtid];
274 h->channels = ng_afmt_to_channels[fmt->fmtid];
275 frag = 0x7fff000c; /* 4k */
276
277 /* format */
278 ioctl(h->fd, SNDCTL_DSP_SETFMT, &h->afmt);
279 if (h->afmt != afmt_to_oss[fmt->fmtid]) {
280 if (ng_debug)
281 fprintf(stderr,"oss: SNDCTL_DSP_SETFMT(%d): %s\n",
282 afmt_to_oss[fmt->fmtid],strerror(errno));
283 goto err;
284 }
285
286 /* channels */
287 ioctl(h->fd, SNDCTL_DSP_CHANNELS, &h->channels);
288 if (h->channels != ng_afmt_to_channels[fmt->fmtid]) {
289 if (ng_debug)
290 fprintf(stderr,"oss: SNDCTL_DSP_CHANNELS(%d): %s\n",
291 ng_afmt_to_channels[fmt->fmtid],strerror(errno));
292 goto err;
293 }
294
295 /* sample rate */
296 h->rate = fmt->rate;
297 ioctl(h->fd, SNDCTL_DSP_SPEED, &h->rate);
298 ioctl(h->fd, SNDCTL_DSP_SETFRAGMENT, &frag);
299 if (h->rate != fmt->rate) {
300 fprintf(stderr, "oss: warning: got sample rate %d (asked for %d)\n",
301 h->rate,fmt->rate);
302 if (h->rate < fmt->rate * 1001 / 1000 &&
303 h->rate > fmt->rate * 999 / 1000) {
304 /* ignore very small differences ... */
305 h->rate = fmt->rate;
306 }
307 }
308
309 if (-1 == ioctl(h->fd, SNDCTL_DSP_GETBLKSIZE, &h->blocksize)) {
310 if (ng_debug)
311 perror("SNDCTL_DSP_GETBLKSIZE");
312 goto err;
313 }
314 if (0 == h->blocksize)
315 /* dmasound bug compatibility */
316 h->blocksize = 4096;
317
318 if (ng_debug)
319 fprintf(stderr,"oss: bs=%d rate=%d channels=%d bits=%d (%s)\n",
320 h->blocksize,h->rate,
321 ng_afmt_to_channels[fmt->fmtid],
322 ng_afmt_to_bits[fmt->fmtid],
323 ng_afmt_to_desc[fmt->fmtid]);
324 return 0;
325
326 err:
327 if (ng_debug)
328 fprintf(stderr,"oss: sound format not supported [%s]\n",
329 ng_afmt_to_desc[fmt->fmtid]);
330 return -1;
331 }
332
333 static void*
334 oss_open(char *device, struct ng_audio_fmt *fmt, int record)
335 {
336 struct oss_handle *h;
337 struct ng_audio_fmt ifmt;
338
339 h = malloc(sizeof(*h));
340 if (NULL == h)
341 return NULL;
342 memset(h,0,sizeof(*h));
343
344 if (-1 == (h->fd = open(device ? device : ng_dev.dsp,
345 record ? O_RDONLY : O_WRONLY | O_NONBLOCK))) {
346 fprintf(stderr,"oss: open %s: %s\n",
347 device ? device : ng_dev.dsp,
348 strerror(errno));
349 goto err;
350 }
351 fcntl(h->fd,F_SETFD,FD_CLOEXEC);
352
353 if (0 == oss_setformat(h,fmt)) {
354 /* fine, native format works */
355 fmt->rate = h->rate;
356 h->ifmt = *fmt;
357 h->ofmt = *fmt;
358 h->bytes_per_sec = ng_afmt_to_bits[h->ifmt.fmtid] *
359 ng_afmt_to_channels[h->ifmt.fmtid] * h->ifmt.rate / 8;
360 return h;
361 }
362
363 /* try byteswapping */
364 ifmt = *fmt;
365 switch (fmt->fmtid) {
366 case AUDIO_S16_LE_MONO: ifmt.fmtid = AUDIO_S16_BE_MONO; break;
367 case AUDIO_S16_LE_STEREO: ifmt.fmtid = AUDIO_S16_BE_STEREO; break;
368 case AUDIO_S16_BE_MONO: ifmt.fmtid = AUDIO_S16_LE_MONO; break;
369 case AUDIO_S16_BE_STEREO: ifmt.fmtid = AUDIO_S16_LE_STEREO; break;
370 }
371 if (0 == oss_setformat(h,&ifmt)) {
372 if (ng_debug)
373 fprintf(stderr,"oss: byteswapping pcm data\n");
374 h->byteswap = 1;
375 ifmt.rate = h->rate;
376 fmt->rate = h->rate;
377 h->ifmt = ifmt;
378 h->ofmt = *fmt;
379 h->bytes_per_sec = ng_afmt_to_bits[h->ifmt.fmtid] *
380 ng_afmt_to_channels[h->ifmt.fmtid] * h->ifmt.rate / 8;
381 return h;
382 }
383
384 fprintf(stderr,"oss: can't use format %s\n",
385 ng_afmt_to_desc[fmt->fmtid]);
386
387 err:
388 fmt->rate = 0;
389 fmt->fmtid = AUDIO_NONE;
390 if (h->fd)
391 close(h->fd);
392 free(h);
393 return NULL;
394 }
395
396 static int
397 oss_startrec(void *handle)
398 {
399 struct oss_handle *h = handle;
400 int trigger;
401
402 if (ng_debug)
403 fprintf(stderr,"oss: startrec\n");
404 trigger = 0;
405 ioctl(h->fd,SNDCTL_DSP_SETTRIGGER,&trigger);
406
407 #if 1
408 /*
409 * Try to clear the sound driver buffers. IMHO this shouldn't
410 * be needed, but looks like it is with some drivers ...
411 */
412 {
413 int oflags,flags,rc;
414 unsigned char buf[4096];
415
416 oflags = fcntl(h->fd,F_GETFL);
417 flags = oflags | O_NONBLOCK;
418 fcntl(h->fd,F_SETFL,flags);
419 for (;;) {
420 rc = read(h->fd,buf,sizeof(buf));
421 if (ng_debug)
422 fprintf(stderr,"oss: clearbuf rc=%d errno=%s\n",rc,strerror(errno));
423 if (rc != sizeof(buf))
424 break;
425 }
426 fcntl(h->fd,F_SETFL,oflags);
427 }
428 #endif
429
430 trigger = PCM_ENABLE_INPUT;
431 ioctl(h->fd,SNDCTL_DSP_SETTRIGGER,&trigger);
432 return 0;
433 }
434
435 static void
436 oss_bufread(int fd,char *buffer,int blocksize)
437 {
438 int rc,count=0;
439
440 /* why FreeBSD returns chunks smaller than blocksize? */
441 for (;;) {
442 rc = read(fd,buffer+count,blocksize-count);
443 if (rc < 0) {
444 if (EINTR == errno)
445 continue;
446 perror("oss: read");
447 exit(1);
448 }
449 count += rc;
450 if (count == blocksize)
451 return;
452 }
453 fprintf(stderr,"#");
454 }
455
456 static void
457 oss_bufswap(void *ptr, int size)
458 {
459 unsigned short *buf = ptr;
460 int i;
461
462 size = size >> 1;
463 for (i = 0; i < size; i++)
464 buf[i] = ((buf[i] >> 8) & 0xff) | ((buf[i] << 8) & 0xff00);
465 }
466
467 static struct ng_audio_buf*
468 oss_read(void *handle, int64_t stopby)
469 {
470 struct oss_handle *h = handle;
471 struct ng_audio_buf* buf;
472 int bytes;
473
474 if (stopby) {
475 bytes = stopby * h->bytes_per_sec / 1000000000 - h->bytes;
476 if (ng_debug)
477 fprintf(stderr,"oss: left: %d bytes (%.3fs)\n",
478 bytes,(float)bytes/h->bytes_per_sec);
479 if (bytes <= 0)
480 return NULL;
481 bytes = (bytes + 3) & ~3;
482 if (bytes > (int)h->blocksize)
483 bytes = h->blocksize;
484 } else {
485 bytes = h->blocksize;
486 }
487 buf = ng_malloc_audio_buf(&h->ofmt,bytes);
488 oss_bufread(h->fd,buf->data,bytes);
489 if (h->byteswap)
490 oss_bufswap(buf->data,bytes);
491 h->bytes += bytes;
492 buf->info.ts = (long long)h->bytes * 1000000000 / h->bytes_per_sec;
493 return buf;
494 }
495
496 static struct ng_audio_buf*
497 oss_write(void *handle, struct ng_audio_buf *buf)
498 {
499 struct oss_handle *h = handle;
500 int rc;
501
502 if (0 == buf->written && h->byteswap)
503 oss_bufswap(buf->data,buf->size);
504 rc = write(h->fd, buf->data+buf->written, buf->size-buf->written);
505 switch (rc) {
506 case -1:
507 perror("write dsp");
508 free(buf);
509 buf = NULL;
510 case 0:
511 fprintf(stderr,"write dsp: Huh? no data written?\n");
512 free(buf);
513 buf = NULL;
514 default:
515 buf->written += rc;
516 if (buf->written == buf->size) {
517 free(buf);
518 buf = NULL;
519 }
520 }
521 return buf;
522 }
523
524 static int64_t
525 oss_latency(void *handle)
526 {
527 struct oss_handle *h = handle;
528 audio_buf_info info;
529 int bytes,samples;
530 long long latency;
531
532 if (-1 == ioctl(h->fd, SNDCTL_DSP_GETOSPACE, &info))
533 return 0;
534 bytes = info.fragsize * info.fragstotal;
535 samples = bytes * 8 / ng_afmt_to_bits[h->ifmt.fmtid] / h->channels;
536 latency = (long long)samples * 1000000000 / h->rate;
537 if (ng_debug)
538 fprintf(stderr,"oss: bytes: %d / samples: %d => latency: %lld\n",
539 bytes,samples,latency);
540 return latency;
541 }
542
543 static int
544 oss_fd(void *handle)
545 {
546 struct oss_handle *h = handle;
547 return h->fd;
548 }
549
550 static void
551 oss_close(void *handle)
552 {
553 struct oss_handle *h = handle;
554
555 close(h->fd);
556 free(h);
557 }
558
559 /* ------------------------------------------------------------------- */
560
561 static struct ng_dsp_driver oss_dsp = {
562 name: "oss",
563 open: oss_open,
564 close: oss_close,
565 fd: oss_fd,
566 startrec: oss_startrec,
567 read: oss_read,
568 write: oss_write,
569 latency: oss_latency,
570 };
571
572 extern void ng_plugin_init(void);
573 void ng_plugin_init(void)
574 {
575 ng_dsp_driver_register(NG_PLUGIN_MAGIC,__FILE__,&oss_dsp);
576 ng_mix_driver_register(NG_PLUGIN_MAGIC,__FILE__,&oss_mixer);
577 }
578
|
This page was automatically generated by the
LXR engine.
|