root / oss.c @ dc5d0b3d
History | View | Annotate | Download (13.4 kB)
1 |
/*
|
---|---|
2 |
* QEMU OSS Audio output driver
|
3 |
*
|
4 |
* Copyright (c) 2003 Vassili Karpov (malc)
|
5 |
*
|
6 |
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
7 |
* of this software and associated documentation files (the "Software"), to deal
|
8 |
* in the Software without restriction, including without limitation the rights
|
9 |
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10 |
* copies of the Software, and to permit persons to whom the Software is
|
11 |
* furnished to do so, subject to the following conditions:
|
12 |
*
|
13 |
* The above copyright notice and this permission notice shall be included in
|
14 |
* all copies or substantial portions of the Software.
|
15 |
*
|
16 |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17 |
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18 |
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
19 |
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20 |
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21 |
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
22 |
* THE SOFTWARE.
|
23 |
*/
|
24 |
#include "vl.h" |
25 |
|
26 |
#ifndef _WIN32
|
27 |
#include <ctype.h> |
28 |
#include <fcntl.h> |
29 |
#include <errno.h> |
30 |
#include <stdio.h> |
31 |
#include <unistd.h> |
32 |
#include <string.h> |
33 |
#include <stdlib.h> |
34 |
#include <limits.h> |
35 |
#include <inttypes.h> |
36 |
#include <sys/mman.h> |
37 |
#include <sys/types.h> |
38 |
#include <sys/ioctl.h> |
39 |
#include <sys/soundcard.h> |
40 |
|
41 |
|
42 |
/* http://www.df.lth.se/~john_e/gems/gem002d.html */
|
43 |
/* http://www.multi-platforms.com/Tips/PopCount.htm */
|
44 |
static inline uint32_t popcount (uint32_t u) |
45 |
{ |
46 |
u = ((u&0x55555555) + ((u>>1)&0x55555555)); |
47 |
u = ((u&0x33333333) + ((u>>2)&0x33333333)); |
48 |
u = ((u&0x0f0f0f0f) + ((u>>4)&0x0f0f0f0f)); |
49 |
u = ((u&0x00ff00ff) + ((u>>8)&0x00ff00ff)); |
50 |
u = ( u&0x0000ffff) + (u>>16); |
51 |
return u;
|
52 |
} |
53 |
|
54 |
static inline uint32_t lsbindex (uint32_t u) |
55 |
{ |
56 |
return popcount ((u&-u)-1); |
57 |
} |
58 |
|
59 |
#define MIN(a, b) ((a)>(b)?(b):(a))
|
60 |
#define MAX(a, b) ((a)<(b)?(b):(a))
|
61 |
|
62 |
#define DEREF(x) (void)x |
63 |
#define log(...) fprintf (stderr, "oss: " __VA_ARGS__) |
64 |
#define ERRFail(...) do { \ |
65 |
int _errno = errno; \
|
66 |
fprintf (stderr, "oss: " __VA_ARGS__); \
|
67 |
fprintf (stderr, "system error: %s\n", strerror (_errno)); \
|
68 |
abort (); \ |
69 |
} while (0) |
70 |
#define Fail(...) do { \ |
71 |
fprintf (stderr, "oss: " __VA_ARGS__); \
|
72 |
fprintf (stderr, "\n"); \
|
73 |
abort (); \ |
74 |
} while (0) |
75 |
|
76 |
#ifdef DEBUG_OSS
|
77 |
#define lwarn(...) fprintf (stderr, "oss: " __VA_ARGS__) |
78 |
#define linfo(...) fprintf (stderr, "oss: " __VA_ARGS__) |
79 |
#define ldebug(...) fprintf (stderr, "oss: " __VA_ARGS__) |
80 |
#else
|
81 |
#define lwarn(...)
|
82 |
#define linfo(...)
|
83 |
#define ldebug(...)
|
84 |
#endif
|
85 |
|
86 |
|
87 |
#define IOCTL(args) do { \ |
88 |
int ret = ioctl args; \
|
89 |
if (-1 == ret) { \ |
90 |
ERRFail (#args); \
|
91 |
} \ |
92 |
ldebug ("ioctl " #args " = %d\n", ret); \ |
93 |
} while (0) |
94 |
|
95 |
static struct { |
96 |
int fd;
|
97 |
int freq;
|
98 |
int bits16;
|
99 |
int nchannels;
|
100 |
int rpos;
|
101 |
int wpos;
|
102 |
int live;
|
103 |
int oss_fmt;
|
104 |
int bytes_per_second;
|
105 |
int is_mapped;
|
106 |
void *buf;
|
107 |
int bufsize;
|
108 |
int nfrags;
|
109 |
int fragsize;
|
110 |
int old_optr;
|
111 |
int leftover;
|
112 |
uint64_t old_ticks; |
113 |
void (*copy_fn)(void *, void *, int); |
114 |
} oss = { .fd = -1 };
|
115 |
|
116 |
static struct { |
117 |
int try_mmap;
|
118 |
int nfrags;
|
119 |
int fragsize;
|
120 |
} conf = { |
121 |
.try_mmap = 0,
|
122 |
.nfrags = 4,
|
123 |
.fragsize = 4096
|
124 |
}; |
125 |
|
126 |
static enum {DONT, DSP, TID} est = DONT; |
127 |
|
128 |
static void copy_no_conversion (void *dst, void *src, int size) |
129 |
{ |
130 |
memcpy (dst, src, size); |
131 |
} |
132 |
|
133 |
static void copy_u16_to_s16 (void *dst, void *src, int size) |
134 |
{ |
135 |
int i;
|
136 |
uint16_t *out, *in; |
137 |
|
138 |
out = dst; |
139 |
in = src; |
140 |
|
141 |
for (i = 0; i < size / 2; i++) { |
142 |
out[i] = in[i] + 0x8000;
|
143 |
} |
144 |
} |
145 |
|
146 |
static void pab (struct audio_buf_info *abinfo) |
147 |
{ |
148 |
DEREF (abinfo); |
149 |
|
150 |
ldebug ("fragments %d, fragstotal %d, fragsize %d, bytes %d\n"
|
151 |
"rpos %d, wpos %d, live %d\n",
|
152 |
abinfo->fragments, |
153 |
abinfo->fragstotal, |
154 |
abinfo->fragsize, |
155 |
abinfo->bytes, |
156 |
rpos, wpos, live); |
157 |
} |
158 |
|
159 |
static void do_open () |
160 |
{ |
161 |
int mmmmssss;
|
162 |
audio_buf_info abinfo; |
163 |
int fmt, freq, nchannels;
|
164 |
|
165 |
if (oss.buf) {
|
166 |
if (-1 == munmap (oss.buf, oss.bufsize)) { |
167 |
ERRFail ("failed to unmap audio buffer %p %d",
|
168 |
oss.buf, oss.bufsize); |
169 |
} |
170 |
oss.buf = NULL;
|
171 |
} |
172 |
|
173 |
if (-1 != oss.fd) |
174 |
close (oss.fd); |
175 |
|
176 |
oss.fd = open ("/dev/dsp", O_RDWR | O_NONBLOCK);
|
177 |
if (-1 == oss.fd) { |
178 |
ERRFail ("can not open /dev/dsp");
|
179 |
} |
180 |
|
181 |
fmt = oss.oss_fmt; |
182 |
freq = oss.freq; |
183 |
nchannels = oss.nchannels; |
184 |
|
185 |
IOCTL ((oss.fd, SNDCTL_DSP_RESET, 1));
|
186 |
IOCTL ((oss.fd, SNDCTL_DSP_SAMPLESIZE, &fmt)); |
187 |
IOCTL ((oss.fd, SNDCTL_DSP_CHANNELS, &nchannels)); |
188 |
IOCTL ((oss.fd, SNDCTL_DSP_SPEED, &freq)); |
189 |
IOCTL ((oss.fd, SNDCTL_DSP_NONBLOCK)); |
190 |
|
191 |
mmmmssss = (conf.nfrags << 16) | conf.fragsize;
|
192 |
IOCTL ((oss.fd, SNDCTL_DSP_SETFRAGMENT, &mmmmssss)); |
193 |
|
194 |
if ((oss.oss_fmt != fmt)
|
195 |
|| (oss.nchannels != nchannels) |
196 |
|| (oss.freq != freq)) { |
197 |
Fail ("failed to set audio parameters\n"
|
198 |
"parameter | requested value | obtained value\n"
|
199 |
"format | %10d | %10d\n"
|
200 |
"channels | %10d | %10d\n"
|
201 |
"frequency | %10d | %10d\n",
|
202 |
oss.oss_fmt, fmt, |
203 |
oss.nchannels, nchannels, |
204 |
oss.freq, freq); |
205 |
} |
206 |
|
207 |
IOCTL ((oss.fd, SNDCTL_DSP_GETOSPACE, &abinfo)); |
208 |
|
209 |
oss.nfrags = abinfo.fragstotal; |
210 |
oss.fragsize = abinfo.fragsize; |
211 |
oss.bufsize = oss.nfrags * oss.fragsize; |
212 |
oss.old_optr = 0;
|
213 |
|
214 |
oss.bytes_per_second = (freq << (nchannels >> 1)) << oss.bits16;
|
215 |
|
216 |
linfo ("bytes per second %d\n", oss.bytes_per_second);
|
217 |
|
218 |
linfo ("fragments %d, fragstotal %d, fragsize %d, bytes %d, bufsize %d\n",
|
219 |
abinfo.fragments, |
220 |
abinfo.fragstotal, |
221 |
abinfo.fragsize, |
222 |
abinfo.bytes, |
223 |
oss.bufsize); |
224 |
|
225 |
oss.buf = MAP_FAILED; |
226 |
oss.is_mapped = 0;
|
227 |
|
228 |
if (conf.try_mmap) {
|
229 |
oss.buf = mmap (NULL, oss.bufsize, PROT_WRITE, MAP_SHARED, oss.fd, 0); |
230 |
if (MAP_FAILED == oss.buf) {
|
231 |
int err;
|
232 |
|
233 |
err = errno; |
234 |
log ("failed to mmap audio, size %d, fd %d\n"
|
235 |
"syserr: %s\n",
|
236 |
oss.bufsize, oss.fd, strerror (err)); |
237 |
} |
238 |
else {
|
239 |
est = TID; |
240 |
oss.is_mapped = 1;
|
241 |
} |
242 |
} |
243 |
|
244 |
if (MAP_FAILED == oss.buf) {
|
245 |
est = TID; |
246 |
oss.buf = mmap (NULL, oss.bufsize, PROT_READ | PROT_WRITE,
|
247 |
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); |
248 |
if (MAP_FAILED == oss.buf) {
|
249 |
ERRFail ("mmap audio buf, size %d", oss.bufsize);
|
250 |
} |
251 |
} |
252 |
|
253 |
oss.rpos = 0;
|
254 |
oss.wpos = 0;
|
255 |
oss.live = 0;
|
256 |
|
257 |
if (oss.is_mapped) {
|
258 |
int trig;
|
259 |
|
260 |
trig = 0;
|
261 |
IOCTL ((oss.fd, SNDCTL_DSP_SETTRIGGER, &trig)); |
262 |
trig = PCM_ENABLE_OUTPUT; |
263 |
IOCTL ((oss.fd, SNDCTL_DSP_SETTRIGGER, &trig)); |
264 |
} |
265 |
} |
266 |
|
267 |
static void maybe_open (int req_freq, int req_nchannels, |
268 |
audfmt_e req_fmt, int force_open)
|
269 |
{ |
270 |
int oss_fmt, bits16;
|
271 |
|
272 |
switch (req_fmt) {
|
273 |
case AUD_FMT_U8:
|
274 |
bits16 = 0;
|
275 |
oss_fmt = AFMT_U8; |
276 |
oss.copy_fn = copy_no_conversion; |
277 |
break;
|
278 |
|
279 |
case AUD_FMT_S8:
|
280 |
Fail ("can not play 8bit signed");
|
281 |
|
282 |
case AUD_FMT_S16:
|
283 |
bits16 = 1;
|
284 |
oss_fmt = AFMT_S16_LE; |
285 |
oss.copy_fn = copy_no_conversion; |
286 |
break;
|
287 |
|
288 |
case AUD_FMT_U16:
|
289 |
bits16 = 1;
|
290 |
oss_fmt = AFMT_S16_LE; |
291 |
oss.copy_fn = copy_u16_to_s16; |
292 |
break;
|
293 |
|
294 |
default:
|
295 |
abort (); |
296 |
} |
297 |
|
298 |
if (force_open
|
299 |
|| (-1 == oss.fd)
|
300 |
|| (oss_fmt != oss.oss_fmt) |
301 |
|| (req_nchannels != oss.nchannels) |
302 |
|| (req_freq != oss.freq) |
303 |
|| (bits16 != oss.bits16)) { |
304 |
oss.oss_fmt = oss_fmt; |
305 |
oss.nchannels = req_nchannels; |
306 |
oss.freq = req_freq; |
307 |
oss.bits16 = bits16; |
308 |
do_open (); |
309 |
} |
310 |
} |
311 |
|
312 |
void AUD_reset (int req_freq, int req_nchannels, audfmt_e req_fmt) |
313 |
{ |
314 |
maybe_open (req_freq, req_nchannels, req_fmt, 0);
|
315 |
} |
316 |
|
317 |
void AUD_open (int req_freq, int req_nchannels, audfmt_e req_fmt) |
318 |
{ |
319 |
maybe_open (req_freq, req_nchannels, req_fmt, 1);
|
320 |
} |
321 |
|
322 |
int AUD_write (void *in_buf, int size) |
323 |
{ |
324 |
int to_copy, temp;
|
325 |
uint8_t *in, *out; |
326 |
|
327 |
to_copy = MIN (oss.bufsize - oss.live, size); |
328 |
|
329 |
temp = to_copy; |
330 |
|
331 |
in = in_buf; |
332 |
out = oss.buf; |
333 |
|
334 |
while (temp) {
|
335 |
int copy;
|
336 |
|
337 |
copy = MIN (temp, oss.bufsize - oss.wpos); |
338 |
oss.copy_fn (out + oss.wpos, in, copy); |
339 |
|
340 |
oss.wpos += copy; |
341 |
if (oss.wpos == oss.bufsize) {
|
342 |
oss.wpos = 0;
|
343 |
} |
344 |
|
345 |
temp -= copy; |
346 |
in += copy; |
347 |
oss.live += copy; |
348 |
} |
349 |
|
350 |
return to_copy;
|
351 |
} |
352 |
|
353 |
void AUD_run (void) |
354 |
{ |
355 |
int res;
|
356 |
int bytes;
|
357 |
struct audio_buf_info abinfo;
|
358 |
|
359 |
if (0 == oss.live) |
360 |
return;
|
361 |
|
362 |
if (oss.is_mapped) {
|
363 |
count_info info; |
364 |
|
365 |
res = ioctl (oss.fd, SNDCTL_DSP_GETOPTR, &info); |
366 |
if (-1 == res) { |
367 |
int err;
|
368 |
|
369 |
err = errno; |
370 |
lwarn ("SNDCTL_DSP_GETOPTR failed with %s\n", strerror (err));
|
371 |
return;
|
372 |
} |
373 |
|
374 |
if (info.ptr > oss.old_optr) {
|
375 |
bytes = info.ptr - oss.old_optr; |
376 |
} |
377 |
else {
|
378 |
bytes = oss.bufsize + info.ptr - oss.old_optr; |
379 |
} |
380 |
|
381 |
oss.old_optr = info.ptr; |
382 |
oss.live -= bytes; |
383 |
return;
|
384 |
} |
385 |
|
386 |
res = ioctl (oss.fd, SNDCTL_DSP_GETOSPACE, &abinfo); |
387 |
|
388 |
if (-1 == res) { |
389 |
int err;
|
390 |
|
391 |
err = errno; |
392 |
lwarn ("SNDCTL_DSP_GETOSPACE failed with %s\n", strerror (err));
|
393 |
} |
394 |
|
395 |
bytes = abinfo.bytes; |
396 |
bytes = MIN (oss.live, bytes); |
397 |
#if 0
|
398 |
bytes = (bytes / fragsize) * fragsize;
|
399 |
#endif
|
400 |
|
401 |
while (bytes) {
|
402 |
int left, play, written;
|
403 |
|
404 |
left = oss.bufsize - oss.rpos; |
405 |
play = MIN (left, bytes); |
406 |
written = write (oss.fd, (void *) ((uint32_t) oss.buf + oss.rpos), play);
|
407 |
|
408 |
if (-1 == written) { |
409 |
if (EAGAIN == errno || EINTR == errno) {
|
410 |
return;
|
411 |
} |
412 |
else {
|
413 |
ERRFail ("write audio");
|
414 |
} |
415 |
} |
416 |
|
417 |
play = written; |
418 |
oss.live -= play; |
419 |
oss.rpos += play; |
420 |
bytes -= play; |
421 |
|
422 |
if (oss.rpos == oss.bufsize) {
|
423 |
oss.rpos = 0;
|
424 |
} |
425 |
} |
426 |
} |
427 |
|
428 |
static int get_dsp_bytes (void) |
429 |
{ |
430 |
int res;
|
431 |
struct count_info info;
|
432 |
|
433 |
res = ioctl (oss.fd, SNDCTL_DSP_GETOPTR, &info); |
434 |
if (-1 == res) { |
435 |
int err;
|
436 |
|
437 |
err = errno; |
438 |
lwarn ("SNDCTL_DSP_GETOPTR failed with %s\n", strerror (err));
|
439 |
return -1; |
440 |
} |
441 |
else {
|
442 |
ldebug ("bytes %d\n", info.bytes);
|
443 |
return info.bytes;
|
444 |
} |
445 |
} |
446 |
|
447 |
void AUD_adjust_estimate (int leftover) |
448 |
{ |
449 |
oss.leftover = leftover; |
450 |
} |
451 |
|
452 |
int AUD_get_free (void) |
453 |
{ |
454 |
int free, elapsed;
|
455 |
|
456 |
free = oss.bufsize - oss.live; |
457 |
|
458 |
if (0 == free) |
459 |
return 0; |
460 |
|
461 |
elapsed = free; |
462 |
switch (est) {
|
463 |
case DONT:
|
464 |
break;
|
465 |
|
466 |
case DSP:
|
467 |
{ |
468 |
static int old_bytes; |
469 |
int bytes;
|
470 |
|
471 |
bytes = get_dsp_bytes (); |
472 |
if (bytes <= 0) |
473 |
return free;
|
474 |
|
475 |
elapsed = bytes - old_bytes; |
476 |
old_bytes = bytes; |
477 |
ldebug ("dsp elapsed %d bytes\n", elapsed);
|
478 |
break;
|
479 |
} |
480 |
|
481 |
case TID:
|
482 |
{ |
483 |
uint64_t ticks, delta; |
484 |
uint64_t ua_elapsed; |
485 |
uint64_t al_elapsed; |
486 |
|
487 |
ticks = qemu_get_clock(rt_clock); |
488 |
delta = ticks - oss.old_ticks; |
489 |
oss.old_ticks = ticks; |
490 |
|
491 |
ua_elapsed = (delta * oss.bytes_per_second) / 1000;
|
492 |
al_elapsed = ua_elapsed & ~3ULL;
|
493 |
|
494 |
ldebug ("tid elapsed %llu bytes\n", ua_elapsed);
|
495 |
|
496 |
if (al_elapsed > (uint64_t) INT_MAX)
|
497 |
elapsed = INT_MAX; |
498 |
else
|
499 |
elapsed = al_elapsed; |
500 |
|
501 |
elapsed += oss.leftover; |
502 |
} |
503 |
} |
504 |
|
505 |
if (elapsed > free) {
|
506 |
lwarn ("audio can not keep up elapsed %d free %d\n", elapsed, free);
|
507 |
return free;
|
508 |
} |
509 |
else {
|
510 |
return elapsed;
|
511 |
} |
512 |
} |
513 |
|
514 |
int AUD_get_live (void) |
515 |
{ |
516 |
return oss.live;
|
517 |
} |
518 |
|
519 |
int AUD_get_buffer_size (void) |
520 |
{ |
521 |
return oss.bufsize;
|
522 |
} |
523 |
|
524 |
#define QC_OSS_FRAGSIZE "QEMU_OSS_FRAGSIZE" |
525 |
#define QC_OSS_NFRAGS "QEMU_OSS_NFRAGS" |
526 |
#define QC_OSS_MMAP "QEMU_OSS_MMAP" |
527 |
|
528 |
static int get_conf_val (const char *key, int defval) |
529 |
{ |
530 |
int val = defval;
|
531 |
char *strval;
|
532 |
|
533 |
strval = getenv (key); |
534 |
if (strval) {
|
535 |
val = atoi (strval); |
536 |
} |
537 |
|
538 |
return val;
|
539 |
} |
540 |
|
541 |
void AUD_init (void) |
542 |
{ |
543 |
int fsp;
|
544 |
|
545 |
DEREF (pab); |
546 |
|
547 |
conf.fragsize = get_conf_val (QC_OSS_FRAGSIZE, conf.fragsize); |
548 |
conf.nfrags = get_conf_val (QC_OSS_NFRAGS, conf.nfrags); |
549 |
conf.try_mmap = get_conf_val (QC_OSS_MMAP, conf.try_mmap); |
550 |
|
551 |
fsp = conf.fragsize; |
552 |
if (0 != (fsp & (fsp - 1))) { |
553 |
Fail ("fragment size %d is not power of 2", fsp);
|
554 |
} |
555 |
|
556 |
conf.fragsize = lsbindex (fsp); |
557 |
} |
558 |
|
559 |
#else
|
560 |
|
561 |
void AUD_run (void) |
562 |
{ |
563 |
} |
564 |
|
565 |
int AUD_write (void *in_buf, int size) |
566 |
{ |
567 |
return 0; |
568 |
} |
569 |
|
570 |
void AUD_reset (int rfreq, int rnchannels, audfmt_e rfmt) |
571 |
{ |
572 |
} |
573 |
|
574 |
void AUD_adjust_estimate (int _leftover) |
575 |
{ |
576 |
} |
577 |
|
578 |
int AUD_get_free (void) |
579 |
{ |
580 |
return 0; |
581 |
} |
582 |
|
583 |
int AUD_get_live (void) |
584 |
{ |
585 |
return 0; |
586 |
} |
587 |
|
588 |
int AUD_get_buffer_size (void) |
589 |
{ |
590 |
return 0; |
591 |
} |
592 |
|
593 |
void AUD_init (void) |
594 |
{ |
595 |
} |
596 |
|
597 |
#endif
|