]>
Commit | Line | Data |
---|---|---|
27503323 FB |
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 | */ | |
67b915a5 FB |
24 | #include "vl.h" |
25 | ||
83fb7adf | 26 | #if !defined(_WIN32) && !defined(__APPLE__) |
d329a6fb | 27 | #include <ctype.h> |
27503323 FB |
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> | |
d329a6fb | 36 | #include <sys/mman.h> |
27503323 FB |
37 | #include <sys/types.h> |
38 | #include <sys/ioctl.h> | |
39 | #include <sys/soundcard.h> | |
40 | ||
27503323 FB |
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 | ||
d329a6fb FB |
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; | |
27503323 FB |
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 | ||
d329a6fb | 159 | static void do_open () |
27503323 | 160 | { |
d329a6fb FB |
161 | int mmmmssss; |
162 | audio_buf_info abinfo; | |
163 | int fmt, freq, nchannels; | |
27503323 | 164 | |
d329a6fb FB |
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; | |
27503323 FB |
171 | } |
172 | ||
d329a6fb FB |
173 | if (-1 != oss.fd) |
174 | close (oss.fd); | |
27503323 | 175 | |
d329a6fb FB |
176 | oss.fd = open ("/dev/dsp", O_RDWR | O_NONBLOCK); |
177 | if (-1 == oss.fd) { | |
178 | ERRFail ("can not open /dev/dsp"); | |
179 | } | |
27503323 | 180 | |
d329a6fb FB |
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 | } | |
27503323 | 206 | |
d329a6fb | 207 | IOCTL ((oss.fd, SNDCTL_DSP_GETOSPACE, &abinfo)); |
27503323 | 208 | |
d329a6fb FB |
209 | oss.nfrags = abinfo.fragstotal; |
210 | oss.fragsize = abinfo.fragsize; | |
211 | oss.bufsize = oss.nfrags * oss.fragsize; | |
212 | oss.old_optr = 0; | |
27503323 | 213 | |
d329a6fb FB |
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 | } | |
27503323 | 238 | else { |
d329a6fb FB |
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)); | |
27503323 FB |
264 | } |
265 | } | |
266 | ||
d329a6fb FB |
267 | static void maybe_open (int req_freq, int req_nchannels, |
268 | audfmt_e req_fmt, int force_open) | |
27503323 | 269 | { |
d329a6fb | 270 | int oss_fmt, bits16; |
27503323 | 271 | |
d329a6fb | 272 | switch (req_fmt) { |
27503323 FB |
273 | case AUD_FMT_U8: |
274 | bits16 = 0; | |
d329a6fb FB |
275 | oss_fmt = AFMT_U8; |
276 | oss.copy_fn = copy_no_conversion; | |
27503323 FB |
277 | break; |
278 | ||
279 | case AUD_FMT_S8: | |
280 | Fail ("can not play 8bit signed"); | |
281 | ||
282 | case AUD_FMT_S16: | |
283 | bits16 = 1; | |
d329a6fb FB |
284 | oss_fmt = AFMT_S16_LE; |
285 | oss.copy_fn = copy_no_conversion; | |
27503323 FB |
286 | break; |
287 | ||
288 | case AUD_FMT_U16: | |
289 | bits16 = 1; | |
d329a6fb FB |
290 | oss_fmt = AFMT_S16_LE; |
291 | oss.copy_fn = copy_u16_to_s16; | |
27503323 FB |
292 | break; |
293 | ||
294 | default: | |
295 | abort (); | |
296 | } | |
297 | ||
d329a6fb FB |
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 (); | |
27503323 | 309 | } |
d329a6fb | 310 | } |
27503323 | 311 | |
d329a6fb FB |
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 | } | |
27503323 | 316 | |
d329a6fb FB |
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); | |
27503323 FB |
320 | } |
321 | ||
322 | int AUD_write (void *in_buf, int size) | |
323 | { | |
324 | int to_copy, temp; | |
325 | uint8_t *in, *out; | |
326 | ||
d329a6fb | 327 | to_copy = MIN (oss.bufsize - oss.live, size); |
27503323 FB |
328 | |
329 | temp = to_copy; | |
330 | ||
331 | in = in_buf; | |
d329a6fb | 332 | out = oss.buf; |
27503323 FB |
333 | |
334 | while (temp) { | |
335 | int copy; | |
336 | ||
d329a6fb FB |
337 | copy = MIN (temp, oss.bufsize - oss.wpos); |
338 | oss.copy_fn (out + oss.wpos, in, copy); | |
27503323 | 339 | |
d329a6fb FB |
340 | oss.wpos += copy; |
341 | if (oss.wpos == oss.bufsize) { | |
342 | oss.wpos = 0; | |
27503323 FB |
343 | } |
344 | ||
345 | temp -= copy; | |
346 | in += copy; | |
d329a6fb | 347 | oss.live += copy; |
27503323 FB |
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 | ||
d329a6fb | 359 | if (0 == oss.live) |
27503323 FB |
360 | return; |
361 | ||
d329a6fb FB |
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); | |
27503323 FB |
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; | |
d329a6fb | 396 | bytes = MIN (oss.live, bytes); |
27503323 FB |
397 | #if 0 |
398 | bytes = (bytes / fragsize) * fragsize; | |
399 | #endif | |
400 | ||
401 | while (bytes) { | |
402 | int left, play, written; | |
403 | ||
d329a6fb | 404 | left = oss.bufsize - oss.rpos; |
27503323 | 405 | play = MIN (left, bytes); |
d329a6fb | 406 | written = write (oss.fd, (void *) ((uint32_t) oss.buf + oss.rpos), play); |
27503323 FB |
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; | |
d329a6fb FB |
418 | oss.live -= play; |
419 | oss.rpos += play; | |
27503323 FB |
420 | bytes -= play; |
421 | ||
d329a6fb FB |
422 | if (oss.rpos == oss.bufsize) { |
423 | oss.rpos = 0; | |
27503323 FB |
424 | } |
425 | } | |
426 | } | |
427 | ||
428 | static int get_dsp_bytes (void) | |
429 | { | |
430 | int res; | |
431 | struct count_info info; | |
432 | ||
d329a6fb | 433 | res = ioctl (oss.fd, SNDCTL_DSP_GETOPTR, &info); |
27503323 FB |
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 | ||
d329a6fb | 447 | void AUD_adjust_estimate (int leftover) |
27503323 | 448 | { |
d329a6fb | 449 | oss.leftover = leftover; |
27503323 FB |
450 | } |
451 | ||
452 | int AUD_get_free (void) | |
453 | { | |
454 | int free, elapsed; | |
455 | ||
d329a6fb | 456 | free = oss.bufsize - oss.live; |
27503323 FB |
457 | |
458 | if (0 == free) | |
459 | return 0; | |
460 | ||
461 | elapsed = free; | |
d329a6fb | 462 | switch (est) { |
27503323 FB |
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 | { | |
27503323 FB |
483 | uint64_t ticks, delta; |
484 | uint64_t ua_elapsed; | |
485 | uint64_t al_elapsed; | |
486 | ||
8a7ddc38 | 487 | ticks = qemu_get_clock(rt_clock); |
d329a6fb FB |
488 | delta = ticks - oss.old_ticks; |
489 | oss.old_ticks = ticks; | |
27503323 | 490 | |
d329a6fb | 491 | ua_elapsed = (delta * oss.bytes_per_second) / 1000; |
27503323 FB |
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 | ||
d329a6fb | 501 | elapsed += oss.leftover; |
27503323 FB |
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 | { | |
d329a6fb | 516 | return oss.live; |
27503323 FB |
517 | } |
518 | ||
519 | int AUD_get_buffer_size (void) | |
520 | { | |
d329a6fb FB |
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; | |
27503323 FB |
539 | } |
540 | ||
541 | void AUD_init (void) | |
542 | { | |
543 | int fsp; | |
27503323 FB |
544 | |
545 | DEREF (pab); | |
546 | ||
d329a6fb FB |
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; | |
27503323 FB |
552 | if (0 != (fsp & (fsp - 1))) { |
553 | Fail ("fragment size %d is not power of 2", fsp); | |
554 | } | |
555 | ||
d329a6fb | 556 | conf.fragsize = lsbindex (fsp); |
27503323 | 557 | } |
67b915a5 FB |
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 |