]>
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 | */ | |
24 | #include <fcntl.h> | |
25 | #include <errno.h> | |
26 | #include <stdio.h> | |
27 | #include <unistd.h> | |
28 | #include <string.h> | |
29 | #include <stdlib.h> | |
30 | #include <limits.h> | |
31 | #include <inttypes.h> | |
32 | #include <sys/types.h> | |
33 | #include <sys/ioctl.h> | |
34 | #include <sys/soundcard.h> | |
35 | ||
36 | #include "vl.h" | |
37 | ||
38 | /* http://www.df.lth.se/~john_e/gems/gem002d.html */ | |
39 | /* http://www.multi-platforms.com/Tips/PopCount.htm */ | |
40 | static inline uint32_t popcount (uint32_t u) | |
41 | { | |
42 | u = ((u&0x55555555) + ((u>>1)&0x55555555)); | |
43 | u = ((u&0x33333333) + ((u>>2)&0x33333333)); | |
44 | u = ((u&0x0f0f0f0f) + ((u>>4)&0x0f0f0f0f)); | |
45 | u = ((u&0x00ff00ff) + ((u>>8)&0x00ff00ff)); | |
46 | u = ( u&0x0000ffff) + (u>>16); | |
47 | return u; | |
48 | } | |
49 | ||
50 | static inline uint32_t lsbindex (uint32_t u) | |
51 | { | |
52 | return popcount ((u&-u)-1); | |
53 | } | |
54 | ||
55 | #define MIN(a, b) ((a)>(b)?(b):(a)) | |
56 | #define MAX(a, b) ((a)<(b)?(b):(a)) | |
57 | ||
58 | #define DEREF(x) (void)x | |
59 | #define log(...) fprintf (stderr, "oss: " __VA_ARGS__) | |
60 | #define ERRFail(...) do { \ | |
61 | int _errno = errno; \ | |
62 | fprintf (stderr, "oss: " __VA_ARGS__); \ | |
63 | fprintf (stderr, "system error: %s\n", strerror (_errno)); \ | |
64 | abort (); \ | |
65 | } while (0) | |
66 | #define Fail(...) do { \ | |
67 | fprintf (stderr, "oss: " __VA_ARGS__); \ | |
68 | fprintf (stderr, "\n"); \ | |
69 | abort (); \ | |
70 | } while (0) | |
71 | ||
72 | #ifdef DEBUG_OSS | |
73 | #define lwarn(...) fprintf (stderr, "oss: " __VA_ARGS__) | |
74 | #define linfo(...) fprintf (stderr, "oss: " __VA_ARGS__) | |
75 | #define ldebug(...) fprintf (stderr, "oss: " __VA_ARGS__) | |
76 | #else | |
77 | #define lwarn(...) | |
78 | #define linfo(...) | |
79 | #define ldebug(...) | |
80 | #endif | |
81 | ||
82 | ||
83 | #define IOCTL(args) do { \ | |
84 | int ret = ioctl args; \ | |
85 | if (-1 == ret) { \ | |
86 | ERRFail (#args); \ | |
87 | } \ | |
88 | ldebug ("ioctl " #args " = %d\n", ret); \ | |
89 | } while (0) | |
90 | ||
91 | static int audio_fd = -1; | |
92 | static int freq; | |
93 | static int conf_nfrags = 4; | |
94 | static int conf_fragsize; | |
95 | static int nfrags; | |
96 | static int fragsize; | |
97 | static int bufsize; | |
98 | static int nchannels; | |
99 | static int fmt; | |
100 | static int rpos; | |
101 | static int wpos; | |
102 | static int atom; | |
103 | static int live; | |
104 | static int leftover; | |
105 | static int bytes_per_second; | |
106 | static void *buf; | |
107 | static enum {DONT, DSP, TID} estimate = TID; | |
108 | ||
109 | static void (*copy_fn)(void *, void *, int); | |
110 | ||
111 | static void copy_no_conversion (void *dst, void *src, int size) | |
112 | { | |
113 | memcpy (dst, src, size); | |
114 | } | |
115 | ||
116 | static void copy_u16_to_s16 (void *dst, void *src, int size) | |
117 | { | |
118 | int i; | |
119 | uint16_t *out, *in; | |
120 | ||
121 | out = dst; | |
122 | in = src; | |
123 | ||
124 | for (i = 0; i < size / 2; i++) { | |
125 | out[i] = in[i] + 0x8000; | |
126 | } | |
127 | } | |
128 | ||
129 | static void pab (struct audio_buf_info *abinfo) | |
130 | { | |
131 | DEREF (abinfo); | |
132 | ||
133 | ldebug ("fragments %d, fragstotal %d, fragsize %d, bytes %d\n" | |
134 | "rpos %d, wpos %d, live %d\n", | |
135 | abinfo->fragments, | |
136 | abinfo->fragstotal, | |
137 | abinfo->fragsize, | |
138 | abinfo->bytes, | |
139 | rpos, wpos, live); | |
140 | } | |
141 | ||
142 | void AUD_reset (int rfreq, int rnchannels, audfmt_e rfmt) | |
143 | { | |
144 | int fmt_; | |
145 | int bits16; | |
146 | ||
147 | if (-1 == audio_fd) { | |
148 | AUD_open (rfreq, rnchannels, rfmt); | |
149 | return; | |
150 | } | |
151 | ||
152 | switch (rfmt) { | |
153 | case AUD_FMT_U8: | |
154 | bits16 = 0; | |
155 | fmt_ = AFMT_U8; | |
156 | copy_fn = copy_no_conversion; | |
157 | atom = 1; | |
158 | break; | |
159 | ||
160 | case AUD_FMT_S8: | |
161 | Fail ("can not play 8bit signed"); | |
162 | ||
163 | case AUD_FMT_S16: | |
164 | bits16 = 1; | |
165 | fmt_ = AFMT_S16_LE; | |
166 | copy_fn = copy_no_conversion; | |
167 | atom = 2; | |
168 | break; | |
169 | ||
170 | case AUD_FMT_U16: | |
171 | bits16 = 1; | |
172 | fmt_ = AFMT_S16_LE; | |
173 | copy_fn = copy_u16_to_s16; | |
174 | atom = 2; | |
175 | break; | |
176 | ||
177 | default: | |
178 | abort (); | |
179 | } | |
180 | ||
181 | if ((fmt_ == fmt) && (bits16 + 1 == nchannels) && (rfreq == freq)) | |
182 | return; | |
183 | else { | |
184 | AUD_open (rfreq, rnchannels, rfmt); | |
185 | } | |
186 | } | |
187 | ||
188 | void AUD_open (int rfreq, int rnchannels, audfmt_e rfmt) | |
189 | { | |
190 | int fmt_; | |
191 | int mmmmssss; | |
192 | struct audio_buf_info abinfo; | |
193 | int _fmt; | |
194 | int _freq; | |
195 | int _nchannels; | |
196 | int bits16; | |
197 | ||
198 | bits16 = 0; | |
199 | ||
200 | switch (rfmt) { | |
201 | case AUD_FMT_U8: | |
202 | bits16 = 0; | |
203 | fmt_ = AFMT_U8; | |
204 | copy_fn = copy_no_conversion; | |
205 | atom = 1; | |
206 | break; | |
207 | ||
208 | case AUD_FMT_S8: | |
209 | Fail ("can not play 8bit signed"); | |
210 | ||
211 | case AUD_FMT_S16: | |
212 | bits16 = 1; | |
213 | fmt_ = AFMT_S16_LE; | |
214 | copy_fn = copy_no_conversion; | |
215 | atom = 2; | |
216 | break; | |
217 | ||
218 | case AUD_FMT_U16: | |
219 | bits16 = 1; | |
220 | fmt_ = AFMT_S16_LE; | |
221 | copy_fn = copy_u16_to_s16; | |
222 | atom = 2; | |
223 | break; | |
224 | ||
225 | default: | |
226 | abort (); | |
227 | } | |
228 | ||
229 | if (buf) { | |
230 | free (buf); | |
231 | buf = 0; | |
232 | } | |
233 | ||
234 | if (-1 != audio_fd) | |
235 | close (audio_fd); | |
236 | ||
237 | audio_fd = open ("/dev/dsp", O_WRONLY | O_NONBLOCK); | |
238 | if (-1 == audio_fd) { | |
239 | ERRFail ("can not open /dev/dsp"); | |
240 | } | |
241 | ||
242 | _fmt = fmt_; | |
243 | _freq = rfreq; | |
244 | _nchannels = rnchannels; | |
245 | ||
246 | IOCTL ((audio_fd, SNDCTL_DSP_RESET, 1)); | |
247 | IOCTL ((audio_fd, SNDCTL_DSP_SAMPLESIZE, &_fmt)); | |
248 | IOCTL ((audio_fd, SNDCTL_DSP_CHANNELS, &_nchannels)); | |
249 | IOCTL ((audio_fd, SNDCTL_DSP_SPEED, &_freq)); | |
250 | IOCTL ((audio_fd, SNDCTL_DSP_NONBLOCK)); | |
251 | ||
252 | /* from oss.pdf: | |
253 | ||
254 | The argument to this call is an integer encoded as 0xMMMMSSSS (in | |
255 | hex). The 16 least significant bits determine the fragment | |
256 | size. The size is 2^SSSS. For examp le SSSS=0008 gives fragment | |
257 | size of 256 bytes (2^8). The minimum is 16 bytes (SSSS=4) and the | |
258 | maximum is total_buffer_size/2. Some devices or processor | |
259 | architectures may require larger fragments - in this case the | |
260 | requested fragment size is automatically increased. | |
261 | ||
262 | So ahem... 4096 = 2^12, and grand total 0x0004000c | |
263 | */ | |
264 | ||
265 | mmmmssss = (conf_nfrags << 16) | conf_fragsize; | |
266 | IOCTL ((audio_fd, SNDCTL_DSP_SETFRAGMENT, &mmmmssss)); | |
267 | ||
268 | linfo ("_fmt = %d, fmt = %d\n" | |
269 | "_channels = %d, rnchannels = %d\n" | |
270 | "_freq = %d, freq = %d\n", | |
271 | _fmt, fmt_, | |
272 | _nchannels, rnchannels, | |
273 | _freq, rfreq); | |
274 | ||
275 | if (_fmt != fmt_) { | |
276 | Fail ("format %d != %d", _fmt, fmt_); | |
277 | } | |
278 | ||
279 | if (_nchannels != rnchannels) { | |
280 | Fail ("channels %d != %d", _nchannels, rnchannels); | |
281 | } | |
282 | ||
283 | if (_freq != rfreq) { | |
284 | Fail ("freq %d != %d", _freq, rfreq); | |
285 | } | |
286 | ||
287 | IOCTL ((audio_fd, SNDCTL_DSP_GETOSPACE, &abinfo)); | |
288 | ||
289 | nfrags = abinfo.fragstotal; | |
290 | fragsize = abinfo.fragsize; | |
291 | freq = _freq; | |
292 | fmt = _fmt; | |
293 | nchannels = rnchannels; | |
294 | atom <<= nchannels >> 1; | |
295 | bufsize = nfrags * fragsize; | |
296 | ||
297 | bytes_per_second = (freq << (nchannels >> 1)) << bits16; | |
298 | ||
299 | linfo ("bytes per second %d\n", bytes_per_second); | |
300 | ||
301 | linfo ("fragments %d, fragstotal %d, fragsize %d, bytes %d, bufsize %d\n", | |
302 | abinfo.fragments, | |
303 | abinfo.fragstotal, | |
304 | abinfo.fragsize, | |
305 | abinfo.bytes, | |
306 | bufsize); | |
307 | ||
308 | if (NULL == buf) { | |
309 | buf = malloc (bufsize); | |
310 | if (NULL == buf) { | |
311 | abort (); | |
312 | } | |
313 | } | |
314 | ||
315 | rpos = 0; | |
316 | wpos = 0; | |
317 | live = 0; | |
318 | } | |
319 | ||
320 | int AUD_write (void *in_buf, int size) | |
321 | { | |
322 | int to_copy, temp; | |
323 | uint8_t *in, *out; | |
324 | ||
325 | to_copy = MIN (bufsize - live, size); | |
326 | ||
327 | temp = to_copy; | |
328 | ||
329 | in = in_buf; | |
330 | out = buf; | |
331 | ||
332 | while (temp) { | |
333 | int copy; | |
334 | ||
335 | copy = MIN (temp, bufsize - wpos); | |
336 | copy_fn (out + wpos, in, copy); | |
337 | ||
338 | wpos += copy; | |
339 | if (wpos == bufsize) { | |
340 | wpos = 0; | |
341 | } | |
342 | ||
343 | temp -= copy; | |
344 | in += copy; | |
345 | live += copy; | |
346 | } | |
347 | ||
348 | return to_copy; | |
349 | } | |
350 | ||
351 | void AUD_run (void) | |
352 | { | |
353 | int res; | |
354 | int bytes; | |
355 | struct audio_buf_info abinfo; | |
356 | ||
357 | if (0 == live) | |
358 | return; | |
359 | ||
360 | res = ioctl (audio_fd, SNDCTL_DSP_GETOSPACE, &abinfo); | |
361 | ||
362 | if (-1 == res) { | |
363 | int err; | |
364 | ||
365 | err = errno; | |
366 | lwarn ("SNDCTL_DSP_GETOSPACE failed with %s\n", strerror (err)); | |
367 | } | |
368 | ||
369 | bytes = abinfo.bytes; | |
370 | bytes = MIN (live, bytes); | |
371 | #if 0 | |
372 | bytes = (bytes / fragsize) * fragsize; | |
373 | #endif | |
374 | ||
375 | while (bytes) { | |
376 | int left, play, written; | |
377 | ||
378 | left = bufsize - rpos; | |
379 | play = MIN (left, bytes); | |
380 | written = write (audio_fd, (void *) ((uint32_t) buf + rpos), play); | |
381 | ||
382 | if (-1 == written) { | |
383 | if (EAGAIN == errno || EINTR == errno) { | |
384 | return; | |
385 | } | |
386 | else { | |
387 | ERRFail ("write audio"); | |
388 | } | |
389 | } | |
390 | ||
391 | play = written; | |
392 | live -= play; | |
393 | rpos += play; | |
394 | bytes -= play; | |
395 | ||
396 | if (rpos == bufsize) { | |
397 | rpos = 0; | |
398 | } | |
399 | } | |
400 | } | |
401 | ||
402 | static int get_dsp_bytes (void) | |
403 | { | |
404 | int res; | |
405 | struct count_info info; | |
406 | ||
407 | res = ioctl (audio_fd, SNDCTL_DSP_GETOPTR, &info); | |
408 | if (-1 == res) { | |
409 | int err; | |
410 | ||
411 | err = errno; | |
412 | lwarn ("SNDCTL_DSP_GETOPTR failed with %s\n", strerror (err)); | |
413 | return -1; | |
414 | } | |
415 | else { | |
416 | ldebug ("bytes %d\n", info.bytes); | |
417 | return info.bytes; | |
418 | } | |
419 | } | |
420 | ||
421 | void AUD_adjust_estimate (int _leftover) | |
422 | { | |
423 | leftover = _leftover; | |
424 | } | |
425 | ||
426 | int AUD_get_free (void) | |
427 | { | |
428 | int free, elapsed; | |
429 | ||
430 | free = bufsize - live; | |
431 | ||
432 | if (0 == free) | |
433 | return 0; | |
434 | ||
435 | elapsed = free; | |
436 | switch (estimate) { | |
437 | case DONT: | |
438 | break; | |
439 | ||
440 | case DSP: | |
441 | { | |
442 | static int old_bytes; | |
443 | int bytes; | |
444 | ||
445 | bytes = get_dsp_bytes (); | |
446 | if (bytes <= 0) | |
447 | return free; | |
448 | ||
449 | elapsed = bytes - old_bytes; | |
450 | old_bytes = bytes; | |
451 | ldebug ("dsp elapsed %d bytes\n", elapsed); | |
452 | break; | |
453 | } | |
454 | ||
455 | case TID: | |
456 | { | |
457 | static uint64_t old_ticks; | |
458 | uint64_t ticks, delta; | |
459 | uint64_t ua_elapsed; | |
460 | uint64_t al_elapsed; | |
461 | ||
462 | ticks = cpu_get_ticks (); | |
463 | delta = ticks - old_ticks; | |
464 | old_ticks = ticks; | |
465 | ||
466 | ua_elapsed = (delta * bytes_per_second) / ticks_per_sec; | |
467 | al_elapsed = ua_elapsed & ~3ULL; | |
468 | ||
469 | ldebug ("tid elapsed %llu bytes\n", ua_elapsed); | |
470 | ||
471 | if (al_elapsed > (uint64_t) INT_MAX) | |
472 | elapsed = INT_MAX; | |
473 | else | |
474 | elapsed = al_elapsed; | |
475 | ||
476 | elapsed += leftover; | |
477 | } | |
478 | } | |
479 | ||
480 | if (elapsed > free) { | |
481 | lwarn ("audio can not keep up elapsed %d free %d\n", elapsed, free); | |
482 | return free; | |
483 | } | |
484 | else { | |
485 | return elapsed; | |
486 | } | |
487 | } | |
488 | ||
489 | int AUD_get_live (void) | |
490 | { | |
491 | return live; | |
492 | } | |
493 | ||
494 | int AUD_get_buffer_size (void) | |
495 | { | |
496 | return bufsize; | |
497 | } | |
498 | ||
499 | void AUD_init (void) | |
500 | { | |
501 | int fsp; | |
502 | int _fragsize = 4096; | |
503 | ||
504 | DEREF (pab); | |
505 | ||
506 | fsp = _fragsize; | |
507 | if (0 != (fsp & (fsp - 1))) { | |
508 | Fail ("fragment size %d is not power of 2", fsp); | |
509 | } | |
510 | ||
511 | conf_fragsize = lsbindex (fsp); | |
512 | } |