]> git.proxmox.com Git - qemu.git/blame - audio/esdaudio.c
Sparc32: port sun4c interrupt controller to VMState design
[qemu.git] / audio / esdaudio.c
CommitLineData
ca9cc28c
AZ
1/*
2 * QEMU ESD audio driver
3 *
4 * Copyright (c) 2006 Frederick Reeve (brushed up by 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 <esd.h>
25#include "qemu-common.h"
26#include "audio.h"
27#include <signal.h>
28
29#define AUDIO_CAP "esd"
30#include "audio_int.h"
31#include "audio_pt_int.h"
32
33typedef struct {
34 HWVoiceOut hw;
35 int done;
36 int live;
37 int decr;
38 int rpos;
39 void *pcm_buf;
40 int fd;
41 struct audio_pt pt;
42} ESDVoiceOut;
43
44typedef struct {
45 HWVoiceIn hw;
46 int done;
47 int dead;
48 int incr;
49 int wpos;
50 void *pcm_buf;
51 int fd;
52 struct audio_pt pt;
53} ESDVoiceIn;
54
55static struct {
56 int samples;
57 int divisor;
58 char *dac_host;
59 char *adc_host;
60} conf = {
1a40d5e2
JQ
61 .samples = 1024,
62 .divisor = 2,
ca9cc28c
AZ
63};
64
65static void GCC_FMT_ATTR (2, 3) qesd_logerr (int err, const char *fmt, ...)
66{
67 va_list ap;
68
69 va_start (ap, fmt);
70 AUD_vlog (AUDIO_CAP, fmt, ap);
71 va_end (ap);
72
73 AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err));
74}
75
76/* playback */
77static void *qesd_thread_out (void *arg)
78{
79 ESDVoiceOut *esd = arg;
80 HWVoiceOut *hw = &esd->hw;
81 int threshold;
82
83 threshold = conf.divisor ? hw->samples / conf.divisor : 0;
84
deb9d2ee
AZ
85 if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
86 return NULL;
87 }
88
ca9cc28c
AZ
89 for (;;) {
90 int decr, to_mix, rpos;
91
92 for (;;) {
93 if (esd->done) {
94 goto exit;
95 }
96
97 if (esd->live > threshold) {
98 break;
99 }
100
101 if (audio_pt_wait (&esd->pt, AUDIO_FUNC)) {
102 goto exit;
103 }
104 }
105
106 decr = to_mix = esd->live;
107 rpos = hw->rpos;
108
109 if (audio_pt_unlock (&esd->pt, AUDIO_FUNC)) {
110 return NULL;
111 }
112
113 while (to_mix) {
114 ssize_t written;
115 int chunk = audio_MIN (to_mix, hw->samples - rpos);
1ea879e5 116 struct st_sample *src = hw->mix_buf + rpos;
ca9cc28c
AZ
117
118 hw->clip (esd->pcm_buf, src, chunk);
119
120 again:
121 written = write (esd->fd, esd->pcm_buf, chunk << hw->info.shift);
122 if (written == -1) {
123 if (errno == EINTR || errno == EAGAIN) {
124 goto again;
125 }
126 qesd_logerr (errno, "write failed\n");
127 return NULL;
128 }
129
130 if (written != chunk << hw->info.shift) {
131 int wsamples = written >> hw->info.shift;
132 int wbytes = wsamples << hw->info.shift;
133 if (wbytes != written) {
134 dolog ("warning: Misaligned write %d (requested %d), "
135 "alignment %d\n",
136 wbytes, written, hw->info.align + 1);
137 }
138 to_mix -= wsamples;
139 rpos = (rpos + wsamples) % hw->samples;
140 break;
141 }
142
143 rpos = (rpos + chunk) % hw->samples;
144 to_mix -= chunk;
145 }
146
147 if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
148 return NULL;
149 }
150
151 esd->rpos = rpos;
152 esd->live -= decr;
153 esd->decr += decr;
154 }
155
156 exit:
157 audio_pt_unlock (&esd->pt, AUDIO_FUNC);
158 return NULL;
159}
160
161static int qesd_run_out (HWVoiceOut *hw)
162{
163 int live, decr;
164 ESDVoiceOut *esd = (ESDVoiceOut *) hw;
165
166 if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
167 return 0;
168 }
169
170 live = audio_pcm_hw_get_live_out (hw);
171 decr = audio_MIN (live, esd->decr);
172 esd->decr -= decr;
173 esd->live = live - decr;
174 hw->rpos = esd->rpos;
175 if (esd->live > 0) {
176 audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
177 }
178 else {
179 audio_pt_unlock (&esd->pt, AUDIO_FUNC);
180 }
181 return decr;
182}
183
184static int qesd_write (SWVoiceOut *sw, void *buf, int len)
185{
186 return audio_pcm_sw_write (sw, buf, len);
187}
188
1ea879e5 189static int qesd_init_out (HWVoiceOut *hw, struct audsettings *as)
ca9cc28c
AZ
190{
191 ESDVoiceOut *esd = (ESDVoiceOut *) hw;
1ea879e5 192 struct audsettings obt_as = *as;
ca9cc28c
AZ
193 int esdfmt = ESD_STREAM | ESD_PLAY;
194 int err;
195 sigset_t set, old_set;
196
197 sigfillset (&set);
198
199 esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO;
200 switch (as->fmt) {
201 case AUD_FMT_S8:
202 case AUD_FMT_U8:
203 esdfmt |= ESD_BITS8;
204 obt_as.fmt = AUD_FMT_U8;
205 break;
206
207 case AUD_FMT_S32:
208 case AUD_FMT_U32:
209 dolog ("Will use 16 instead of 32 bit samples\n");
210
211 case AUD_FMT_S16:
212 case AUD_FMT_U16:
213 deffmt:
214 esdfmt |= ESD_BITS16;
215 obt_as.fmt = AUD_FMT_S16;
216 break;
217
218 default:
219 dolog ("Internal logic error: Bad audio format %d\n", as->fmt);
ca9cc28c
AZ
220 goto deffmt;
221
222 }
deb9d2ee 223 obt_as.endianness = AUDIO_HOST_ENDIANNESS;
ca9cc28c
AZ
224
225 audio_pcm_init_info (&hw->info, &obt_as);
226
227 hw->samples = conf.samples;
228 esd->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
229 if (!esd->pcm_buf) {
230 dolog ("Could not allocate buffer (%d bytes)\n",
231 hw->samples << hw->info.shift);
232 return -1;
233 }
234
235 esd->fd = -1;
236 err = pthread_sigmask (SIG_BLOCK, &set, &old_set);
237 if (err) {
238 qesd_logerr (err, "pthread_sigmask failed\n");
239 goto fail1;
240 }
241
242 esd->fd = esd_play_stream (esdfmt, as->freq, conf.dac_host, NULL);
243 if (esd->fd < 0) {
244 qesd_logerr (errno, "esd_play_stream failed\n");
245 goto fail2;
246 }
247
248 if (audio_pt_init (&esd->pt, qesd_thread_out, esd, AUDIO_CAP, AUDIO_FUNC)) {
249 goto fail3;
250 }
251
252 err = pthread_sigmask (SIG_SETMASK, &old_set, NULL);
253 if (err) {
254 qesd_logerr (err, "pthread_sigmask(restore) failed\n");
255 }
256
257 return 0;
258
259 fail3:
260 if (close (esd->fd)) {
261 qesd_logerr (errno, "%s: close on esd socket(%d) failed\n",
262 AUDIO_FUNC, esd->fd);
263 }
264 esd->fd = -1;
265
266 fail2:
267 err = pthread_sigmask (SIG_SETMASK, &old_set, NULL);
268 if (err) {
269 qesd_logerr (err, "pthread_sigmask(restore) failed\n");
270 }
271
272 fail1:
273 qemu_free (esd->pcm_buf);
274 esd->pcm_buf = NULL;
275 return -1;
276}
277
278static void qesd_fini_out (HWVoiceOut *hw)
279{
280 void *ret;
281 ESDVoiceOut *esd = (ESDVoiceOut *) hw;
282
283 audio_pt_lock (&esd->pt, AUDIO_FUNC);
284 esd->done = 1;
285 audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
286 audio_pt_join (&esd->pt, &ret, AUDIO_FUNC);
287
288 if (esd->fd >= 0) {
289 if (close (esd->fd)) {
290 qesd_logerr (errno, "failed to close esd socket\n");
291 }
292 esd->fd = -1;
293 }
294
295 audio_pt_fini (&esd->pt, AUDIO_FUNC);
296
297 qemu_free (esd->pcm_buf);
298 esd->pcm_buf = NULL;
299}
300
301static int qesd_ctl_out (HWVoiceOut *hw, int cmd, ...)
302{
303 (void) hw;
304 (void) cmd;
305 return 0;
306}
307
308/* capture */
309static void *qesd_thread_in (void *arg)
310{
311 ESDVoiceIn *esd = arg;
312 HWVoiceIn *hw = &esd->hw;
313 int threshold;
314
315 threshold = conf.divisor ? hw->samples / conf.divisor : 0;
316
deb9d2ee
AZ
317 if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
318 return NULL;
319 }
320
ca9cc28c
AZ
321 for (;;) {
322 int incr, to_grab, wpos;
323
324 for (;;) {
325 if (esd->done) {
326 goto exit;
327 }
328
329 if (esd->dead > threshold) {
330 break;
331 }
332
333 if (audio_pt_wait (&esd->pt, AUDIO_FUNC)) {
334 goto exit;
335 }
336 }
337
338 incr = to_grab = esd->dead;
339 wpos = hw->wpos;
340
341 if (audio_pt_unlock (&esd->pt, AUDIO_FUNC)) {
342 return NULL;
343 }
344
345 while (to_grab) {
346 ssize_t nread;
347 int chunk = audio_MIN (to_grab, hw->samples - wpos);
348 void *buf = advance (esd->pcm_buf, wpos);
349
350 again:
351 nread = read (esd->fd, buf, chunk << hw->info.shift);
352 if (nread == -1) {
353 if (errno == EINTR || errno == EAGAIN) {
354 goto again;
355 }
356 qesd_logerr (errno, "read failed\n");
357 return NULL;
358 }
359
360 if (nread != chunk << hw->info.shift) {
361 int rsamples = nread >> hw->info.shift;
362 int rbytes = rsamples << hw->info.shift;
363 if (rbytes != nread) {
364 dolog ("warning: Misaligned write %d (requested %d), "
365 "alignment %d\n",
366 rbytes, nread, hw->info.align + 1);
367 }
368 to_grab -= rsamples;
369 wpos = (wpos + rsamples) % hw->samples;
370 break;
371 }
372
373 hw->conv (hw->conv_buf + wpos, buf, nread >> hw->info.shift,
374 &nominal_volume);
375 wpos = (wpos + chunk) % hw->samples;
376 to_grab -= chunk;
377 }
378
379 if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
380 return NULL;
381 }
382
383 esd->wpos = wpos;
384 esd->dead -= incr;
385 esd->incr += incr;
386 }
387
388 exit:
389 audio_pt_unlock (&esd->pt, AUDIO_FUNC);
390 return NULL;
391}
392
393static int qesd_run_in (HWVoiceIn *hw)
394{
395 int live, incr, dead;
396 ESDVoiceIn *esd = (ESDVoiceIn *) hw;
397
398 if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
399 return 0;
400 }
401
402 live = audio_pcm_hw_get_live_in (hw);
403 dead = hw->samples - live;
404 incr = audio_MIN (dead, esd->incr);
405 esd->incr -= incr;
406 esd->dead = dead - incr;
407 hw->wpos = esd->wpos;
408 if (esd->dead > 0) {
409 audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
410 }
411 else {
412 audio_pt_unlock (&esd->pt, AUDIO_FUNC);
413 }
414 return incr;
415}
416
417static int qesd_read (SWVoiceIn *sw, void *buf, int len)
418{
419 return audio_pcm_sw_read (sw, buf, len);
420}
421
1ea879e5 422static int qesd_init_in (HWVoiceIn *hw, struct audsettings *as)
ca9cc28c
AZ
423{
424 ESDVoiceIn *esd = (ESDVoiceIn *) hw;
1ea879e5 425 struct audsettings obt_as = *as;
ca9cc28c
AZ
426 int esdfmt = ESD_STREAM | ESD_RECORD;
427 int err;
428 sigset_t set, old_set;
429
430 sigfillset (&set);
431
432 esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO;
433 switch (as->fmt) {
434 case AUD_FMT_S8:
435 case AUD_FMT_U8:
436 esdfmt |= ESD_BITS8;
437 obt_as.fmt = AUD_FMT_U8;
438 break;
439
440 case AUD_FMT_S16:
441 case AUD_FMT_U16:
442 esdfmt |= ESD_BITS16;
443 obt_as.fmt = AUD_FMT_S16;
444 break;
445
446 case AUD_FMT_S32:
447 case AUD_FMT_U32:
448 dolog ("Will use 16 instead of 32 bit samples\n");
449 esdfmt |= ESD_BITS16;
450 obt_as.fmt = AUD_FMT_S16;
451 break;
452 }
deb9d2ee 453 obt_as.endianness = AUDIO_HOST_ENDIANNESS;
ca9cc28c
AZ
454
455 audio_pcm_init_info (&hw->info, &obt_as);
456
457 hw->samples = conf.samples;
458 esd->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
459 if (!esd->pcm_buf) {
460 dolog ("Could not allocate buffer (%d bytes)\n",
461 hw->samples << hw->info.shift);
462 return -1;
463 }
464
465 esd->fd = -1;
466
467 err = pthread_sigmask (SIG_BLOCK, &set, &old_set);
468 if (err) {
469 qesd_logerr (err, "pthread_sigmask failed\n");
470 goto fail1;
471 }
472
473 esd->fd = esd_record_stream (esdfmt, as->freq, conf.adc_host, NULL);
474 if (esd->fd < 0) {
475 qesd_logerr (errno, "esd_record_stream failed\n");
476 goto fail2;
477 }
478
479 if (audio_pt_init (&esd->pt, qesd_thread_in, esd, AUDIO_CAP, AUDIO_FUNC)) {
480 goto fail3;
481 }
482
483 err = pthread_sigmask (SIG_SETMASK, &old_set, NULL);
484 if (err) {
485 qesd_logerr (err, "pthread_sigmask(restore) failed\n");
486 }
487
488 return 0;
489
490 fail3:
491 if (close (esd->fd)) {
492 qesd_logerr (errno, "%s: close on esd socket(%d) failed\n",
493 AUDIO_FUNC, esd->fd);
494 }
495 esd->fd = -1;
496
497 fail2:
498 err = pthread_sigmask (SIG_SETMASK, &old_set, NULL);
499 if (err) {
500 qesd_logerr (err, "pthread_sigmask(restore) failed\n");
501 }
502
503 fail1:
504 qemu_free (esd->pcm_buf);
505 esd->pcm_buf = NULL;
506 return -1;
507}
508
509static void qesd_fini_in (HWVoiceIn *hw)
510{
511 void *ret;
512 ESDVoiceIn *esd = (ESDVoiceIn *) hw;
513
514 audio_pt_lock (&esd->pt, AUDIO_FUNC);
515 esd->done = 1;
516 audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
517 audio_pt_join (&esd->pt, &ret, AUDIO_FUNC);
518
519 if (esd->fd >= 0) {
520 if (close (esd->fd)) {
521 qesd_logerr (errno, "failed to close esd socket\n");
522 }
523 esd->fd = -1;
524 }
525
526 audio_pt_fini (&esd->pt, AUDIO_FUNC);
527
528 qemu_free (esd->pcm_buf);
529 esd->pcm_buf = NULL;
530}
531
532static int qesd_ctl_in (HWVoiceIn *hw, int cmd, ...)
533{
534 (void) hw;
535 (void) cmd;
536 return 0;
537}
538
539/* common */
540static void *qesd_audio_init (void)
541{
542 return &conf;
543}
544
545static void qesd_audio_fini (void *opaque)
546{
547 (void) opaque;
548 ldebug ("esd_fini");
549}
550
551struct audio_option qesd_options[] = {
98f9f48c 552 {
553 .name = "SAMPLES",
554 .tag = AUD_OPT_INT,
555 .valp = &conf.samples,
556 .descr = "buffer size in samples"
557 },
558 {
559 .name = "DIVISOR",
560 .tag = AUD_OPT_INT,
561 .valp = &conf.divisor,
562 .descr = "threshold divisor"
563 },
564 {
565 .name = "DAC_HOST",
566 .tag = AUD_OPT_STR,
567 .valp = &conf.dac_host,
568 .descr = "playback host"
569 },
570 {
571 .name = "ADC_HOST",
572 .tag = AUD_OPT_STR,
573 .valp = &conf.adc_host,
574 .descr = "capture host"
575 },
2700efa3 576 { /* End of list */ }
ca9cc28c
AZ
577};
578
35f4b58c 579static struct audio_pcm_ops qesd_pcm_ops = {
1dd3e4d1
JQ
580 .init_out = qesd_init_out,
581 .fini_out = qesd_fini_out,
582 .run_out = qesd_run_out,
583 .write = qesd_write,
584 .ctl_out = qesd_ctl_out,
585
586 .init_in = qesd_init_in,
587 .fini_in = qesd_fini_in,
588 .run_in = qesd_run_in,
589 .read = qesd_read,
590 .ctl_in = qesd_ctl_in,
ca9cc28c
AZ
591};
592
593struct audio_driver esd_audio_driver = {
bee37f32
JQ
594 .name = "esd",
595 .descr = "http://en.wikipedia.org/wiki/Esound",
596 .options = qesd_options,
597 .init = qesd_audio_init,
598 .fini = qesd_audio_fini,
599 .pcm_ops = &qesd_pcm_ops,
600 .can_be_default = 0,
601 .max_voices_out = INT_MAX,
602 .max_voices_in = INT_MAX,
603 .voice_size_out = sizeof (ESDVoiceOut),
604 .voice_size_in = sizeof (ESDVoiceIn)
ca9cc28c 605};