• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * QEMU ESD audio driver
3  *
4  * Copyright (c) 2008 The Android Open Source Project
5  * Copyright (c) 2006 Frederick Reeve (brushed up by malc)
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy
8  * of this software and associated documentation files (the "Software"), to deal
9  * in the Software without restriction, including without limitation the rights
10  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11  * copies of the Software, and to permit persons to whom the Software is
12  * furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in
15  * all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23  * THE SOFTWARE.
24  */
25 
26 #include <esd.h>
27 #include "audio.h"
28 #include <signal.h>
29 
30 #define AUDIO_CAP "esd"
31 #include "audio_int.h"
32 #include "audio_pt_int.h"
33 #include <dlfcn.h>
34 
35 #include "qemu_debug.h"
36 
37 #define  DEBUG  1
38 
39 #if DEBUG
40 #  include <stdio.h>
41 #  define D(...)  VERBOSE_PRINT(audio,__VA_ARGS__)
42 #  define D_ACTIVE  VERBOSE_CHECK(audio)
43 #  define O(...)  VERBOSE_PRINT(audioout,__VA_ARGS__)
44 #  define I(...)  VERBOSE_PRINT(audioin,__VA_ARGS__)
45 #else
46 #  define D(...)  ((void)0)
47 #  define D_ACTIVE 0
48 #  define O(...)  ((void)0)
49 #  define I(...)  ((void)0)
50 #endif
51 
52 #define  STRINGIFY_(x)  #x
53 #define  STRINGIFY(x)   STRINGIFY_(x)
54 
55 typedef struct {
56     HWVoiceOut hw;
57     int done;
58     int live;
59     int decr;
60     int rpos;
61     void *pcm_buf;
62     int fd;
63     struct audio_pt pt;
64 } ESDVoiceOut;
65 
66 typedef struct {
67     HWVoiceIn hw;
68     int done;
69     int dead;
70     int incr;
71     int wpos;
72     void *pcm_buf;
73     int fd;
74     struct audio_pt pt;
75 } ESDVoiceIn;
76 
77 static struct {
78     int samples;
79     int divisor;
80     char *dac_host;
81     char *adc_host;
82 } conf = {
83     1024,
84     2,
85     NULL,
86     NULL
87 };
88 
89 /* link dynamically to the libesd.so */
90 
91 #define  DYNLINK_FUNCTIONS   \
92     DYNLINK_FUNC(int,esd_play_stream,(esd_format_t,int,const char*,const char*))   \
93     DYNLINK_FUNC(int,esd_record_stream,(esd_format_t,int,const char*,const char*)) \
94     DYNLINK_FUNC(int,esd_open_sound,( const char *host )) \
95     DYNLINK_FUNC(int,esd_close,(int)) \
96 
97 #define  DYNLINK_FUNCTIONS_INIT \
98     esd_dynlink_init
99 
100 #include "dynlink.h"
101 
102 static void*    esd_lib;
103 
qesd_logerr(int err,const char * fmt,...)104 static void GCC_FMT_ATTR (2, 3) qesd_logerr (int err, const char *fmt, ...)
105 {
106     va_list ap;
107 
108     va_start (ap, fmt);
109     AUD_vlog (AUDIO_CAP, fmt, ap);
110     va_end (ap);
111 
112     AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err));
113 }
114 
115 /* playback */
qesd_thread_out(void * arg)116 static void *qesd_thread_out (void *arg)
117 {
118     ESDVoiceOut* esd = arg;
119     HWVoiceOut*  hw  = &esd->hw;
120     int threshold;
121     sigset_t  set;
122 
123     threshold = conf.divisor ? hw->samples / conf.divisor : 0;
124 
125     if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
126         return NULL;
127     }
128 
129     /* ignore SIGALRM in this thread */
130     sigemptyset(&set);
131     sigaddset(&set, SIGALRM);
132 
133     pthread_sigmask( SIG_BLOCK, &set, NULL);
134 
135     O("EsounD output thread starting, threshold is %d samples", threshold);
136     for (;;) {
137         int decr, to_mix, rpos;
138 
139         for (;;) {
140             if (esd->done) {
141                 goto exit;
142             }
143 
144             if (esd->live > threshold) {
145                 break;
146             }
147 
148             if (audio_pt_wait (&esd->pt, AUDIO_FUNC)) {
149                 O("EsounD output thread aborting on wait error");
150                 goto exit;
151             }
152         }
153 
154         decr = to_mix = esd->live;
155         rpos = hw->rpos;
156 
157         if (audio_pt_unlock (&esd->pt, AUDIO_FUNC)) {
158             O("EsounD output thread aborting on unlock error");
159             return NULL;
160         }
161 
162         while (to_mix) {
163             ssize_t written;
164             int chunk = audio_MIN (to_mix, hw->samples - rpos);
165             st_sample_t *src = hw->mix_buf + rpos;
166 
167             hw->clip (esd->pcm_buf, src, chunk);
168 
169         again:
170             written = write (esd->fd, esd->pcm_buf, chunk << hw->info.shift);
171             if (written == -1) {
172                 if (errno == EINTR || errno == EAGAIN) {
173                     goto again;
174                 }
175                 qesd_logerr (errno, "write failed\n");
176                 O("EsounD output thread aborting on write error: %s", strerror(errno));
177                 return NULL;
178             }
179 
180             if (written != chunk << hw->info.shift) {
181                 int wsamples = written >> hw->info.shift;
182                 int wbytes = wsamples << hw->info.shift;
183                 if (wbytes != written) {
184                     dolog ("warning: Misaligned write %d (requested %d), "
185                            "alignment %d\n",
186                            wbytes, written, hw->info.align + 1);
187                 }
188                 to_mix -= wsamples;
189                 rpos = (rpos + wsamples) % hw->samples;
190                 break;
191             }
192 
193             rpos = (rpos + chunk) % hw->samples;
194             to_mix -= chunk;
195         }
196 
197         if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
198             O("EsounD output thread aborting on lock error\n");
199             return NULL;
200         }
201 
202         esd->rpos = rpos;
203         esd->live -= decr;
204         esd->decr += decr;
205     }
206     O("EsounD output thread exiting");
207 
208  exit:
209     audio_pt_unlock (&esd->pt, AUDIO_FUNC);
210     return NULL;
211 }
212 
qesd_run_out(HWVoiceOut * hw)213 static int qesd_run_out (HWVoiceOut *hw)
214 {
215     int live, decr;
216     ESDVoiceOut *esd = (ESDVoiceOut *) hw;
217 
218     if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
219         O("%s: exiting on lock error", __FUNCTION__);
220         return 0;
221     }
222 
223     live = audio_pcm_hw_get_live_out (hw);
224     decr = audio_MIN (live, esd->decr);
225     esd->decr -= decr;
226     esd->live = live - decr;
227     hw->rpos = esd->rpos;
228     if (esd->live > 0) {
229         O("%s: signaling %d samples\n", __FUNCTION__, esd->live);
230         audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
231     }
232     else {
233         O(".");
234         audio_pt_unlock (&esd->pt, AUDIO_FUNC);
235     }
236     return decr;
237 }
238 
qesd_write(SWVoiceOut * sw,void * buf,int len)239 static int qesd_write (SWVoiceOut *sw, void *buf, int len)
240 {
241     return audio_pcm_sw_write (sw, buf, len);
242 }
243 
qesd_init_out(HWVoiceOut * hw,audsettings_t * as)244 static int qesd_init_out (HWVoiceOut *hw, audsettings_t *as)
245 {
246     ESDVoiceOut *esd = (ESDVoiceOut *) hw;
247     audsettings_t obt_as = *as;
248     int esdfmt = ESD_STREAM | ESD_PLAY;
249     int result = -1;
250 
251     /* shut down verbose debug spew */
252     if (!D_ACTIVE)
253         stdio_disable();
254 
255     O("initializing EsoundD audio output");
256     esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO;
257     switch (as->fmt) {
258     case AUD_FMT_S8:
259     case AUD_FMT_U8:
260         esdfmt |= ESD_BITS8;
261         obt_as.fmt = AUD_FMT_U8;
262         break;
263 #if 0
264     case AUD_FMT_S32:
265     case AUD_FMT_U32:
266         dolog ("Will use 16 instead of 32 bit samples\n");
267 #endif
268     case AUD_FMT_S16:
269     case AUD_FMT_U16:
270     deffmt:
271         esdfmt |= ESD_BITS16;
272         obt_as.fmt = AUD_FMT_S16;
273         break;
274 
275     default:
276         dolog ("Internal logic error: Bad audio format %d\n", as->fmt);
277         goto deffmt;
278 
279     }
280     obt_as.endianness = AUDIO_HOST_ENDIANNESS;
281 
282     audio_pcm_init_info (&hw->info, &obt_as);
283 
284     hw->samples = conf.samples;
285     esd->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
286     if (!esd->pcm_buf) {
287         dolog ("Could not allocate buffer (%d bytes)\n",
288                hw->samples << hw->info.shift);
289         goto exit;
290     }
291 
292     esd->fd = FF(esd_play_stream) (esdfmt, as->freq, conf.dac_host, NULL);
293     if (esd->fd < 0) {
294         if (conf.dac_host == NULL) {
295             esd->fd = FF(esd_play_stream) (esdfmt, as->freq, "localhost", NULL);
296         }
297         if (esd->fd < 0) {
298             qesd_logerr (errno, "esd_play_stream failed\n");
299             goto fail2;
300         }
301     }
302 
303     if (audio_pt_init (&esd->pt, qesd_thread_out, esd, AUDIO_CAP, AUDIO_FUNC)) {
304         goto fail3;
305     }
306 
307     result = 0;  /* success */
308     goto exit;
309 
310  fail3:
311     if (close (esd->fd)) {
312         qesd_logerr (errno, "%s: close on esd socket(%d) failed\n",
313                      AUDIO_FUNC, esd->fd);
314     }
315     esd->fd = -1;
316 
317  fail2:
318     qemu_free (esd->pcm_buf);
319     esd->pcm_buf = NULL;
320 
321  exit:
322     if (!D_ACTIVE)
323         stdio_enable();
324 
325     return result;
326 }
327 
qesd_fini_out(HWVoiceOut * hw)328 static void qesd_fini_out (HWVoiceOut *hw)
329 {
330     void *ret;
331     ESDVoiceOut *esd = (ESDVoiceOut *) hw;
332 
333     audio_pt_lock (&esd->pt, AUDIO_FUNC);
334     esd->done = 1;
335     audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
336     audio_pt_join (&esd->pt, &ret, AUDIO_FUNC);
337 
338     if (esd->fd >= 0) {
339         if (close (esd->fd)) {
340             qesd_logerr (errno, "failed to close esd socket\n");
341         }
342         esd->fd = -1;
343     }
344 
345     audio_pt_fini (&esd->pt, AUDIO_FUNC);
346 
347     qemu_free (esd->pcm_buf);
348     esd->pcm_buf = NULL;
349 }
350 
qesd_ctl_out(HWVoiceOut * hw,int cmd,...)351 static int qesd_ctl_out (HWVoiceOut *hw, int cmd, ...)
352 {
353     (void) hw;
354     (void) cmd;
355     return 0;
356 }
357 
358 /* capture */
qesd_thread_in(void * arg)359 static void *qesd_thread_in (void *arg)
360 {
361     ESDVoiceIn *esd = arg;
362     HWVoiceIn *hw = &esd->hw;
363     int threshold;
364 
365     threshold = conf.divisor ? hw->samples / conf.divisor : 0;
366 
367     if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
368         return NULL;
369     }
370 
371     for (;;) {
372         int incr, to_grab, wpos;
373 
374         for (;;) {
375             if (esd->done) {
376                 goto exit;
377             }
378 
379             if (esd->dead > threshold) {
380                 break;
381             }
382 
383             if (audio_pt_wait (&esd->pt, AUDIO_FUNC)) {
384                 goto exit;
385             }
386         }
387 
388         incr = to_grab = esd->dead;
389         wpos = hw->wpos;
390 
391         if (audio_pt_unlock (&esd->pt, AUDIO_FUNC)) {
392             return NULL;
393         }
394 
395         while (to_grab) {
396             ssize_t nread;
397             int chunk = audio_MIN (to_grab, hw->samples - wpos);
398             void *buf = advance (esd->pcm_buf, wpos);
399 
400         again:
401             nread = read (esd->fd, buf, chunk << hw->info.shift);
402             if (nread == -1) {
403                 if (errno == EINTR || errno == EAGAIN) {
404                     goto again;
405                 }
406                 qesd_logerr (errno, "read failed\n");
407                 return NULL;
408             }
409 
410             if (nread != chunk << hw->info.shift) {
411                 int rsamples = nread >> hw->info.shift;
412                 int rbytes = rsamples << hw->info.shift;
413                 if (rbytes != nread) {
414                     dolog ("warning: Misaligned write %d (requested %d), "
415                            "alignment %d\n",
416                            rbytes, nread, hw->info.align + 1);
417                 }
418                 to_grab -= rsamples;
419                 wpos = (wpos + rsamples) % hw->samples;
420                 break;
421             }
422 
423             hw->conv (hw->conv_buf + wpos, buf, nread >> hw->info.shift,
424                       &nominal_volume);
425             wpos = (wpos + chunk) % hw->samples;
426             to_grab -= chunk;
427         }
428 
429         if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
430             return NULL;
431         }
432 
433         esd->wpos = wpos;
434         esd->dead -= incr;
435         esd->incr += incr;
436     }
437 
438  exit:
439     audio_pt_unlock (&esd->pt, AUDIO_FUNC);
440     return NULL;
441 }
442 
qesd_run_in(HWVoiceIn * hw)443 static int qesd_run_in (HWVoiceIn *hw)
444 {
445     int live, incr, dead;
446     ESDVoiceIn *esd = (ESDVoiceIn *) hw;
447 
448     if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
449         return 0;
450     }
451 
452     live = audio_pcm_hw_get_live_in (hw);
453     dead = hw->samples - live;
454     incr = audio_MIN (dead, esd->incr);
455     esd->incr -= incr;
456     esd->dead = dead - incr;
457     hw->wpos = esd->wpos;
458     if (esd->dead > 0) {
459         audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
460     }
461     else {
462         audio_pt_unlock (&esd->pt, AUDIO_FUNC);
463     }
464     return incr;
465 }
466 
qesd_read(SWVoiceIn * sw,void * buf,int len)467 static int qesd_read (SWVoiceIn *sw, void *buf, int len)
468 {
469     return audio_pcm_sw_read (sw, buf, len);
470 }
471 
qesd_init_in(HWVoiceIn * hw,audsettings_t * as)472 static int qesd_init_in (HWVoiceIn *hw, audsettings_t *as)
473 {
474     ESDVoiceIn *esd = (ESDVoiceIn *) hw;
475     audsettings_t obt_as = *as;
476     int esdfmt = ESD_STREAM | ESD_RECORD;
477     int result = -1;
478 
479     /* shut down verbose debug spew */
480     if (!D_ACTIVE)
481         stdio_disable();
482 
483     esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO;
484     switch (as->fmt) {
485     case AUD_FMT_S8:
486     case AUD_FMT_U8:
487         esdfmt |= ESD_BITS8;
488         obt_as.fmt = AUD_FMT_U8;
489         break;
490 
491     case AUD_FMT_S16:
492     case AUD_FMT_U16:
493         esdfmt |= ESD_BITS16;
494         obt_as.fmt = AUD_FMT_S16;
495         break;
496     case AUD_FMT_S32:
497     case AUD_FMT_U32:
498         dolog ("Will use 16 instead of 32 bit samples\n");
499         esdfmt |= ESD_BITS16;
500         obt_as.fmt = AUD_FMT_S16;
501         break;
502     }
503     obt_as.endianness = AUDIO_HOST_ENDIANNESS;
504 
505     audio_pcm_init_info (&hw->info, &obt_as);
506 
507     hw->samples = conf.samples;
508     esd->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
509     if (!esd->pcm_buf) {
510         dolog ("Could not allocate buffer (%d bytes)\n",
511                hw->samples << hw->info.shift);
512         goto exit;
513     }
514 
515     esd->fd = FF(esd_record_stream) (esdfmt, as->freq, conf.adc_host, NULL);
516     if (esd->fd < 0) {
517         if (conf.adc_host == NULL) {
518             esd->fd = FF(esd_record_stream) (esdfmt, as->freq, "localhost", NULL);
519         }
520         if (esd->fd < 0) {
521             qesd_logerr (errno, "esd_record_stream failed\n");
522             goto fail2;
523         }
524     }
525 
526     if (audio_pt_init (&esd->pt, qesd_thread_in, esd, AUDIO_CAP, AUDIO_FUNC)) {
527         goto fail3;
528     }
529 
530     result = 0;  /* success */
531     goto exit;
532 
533  fail3:
534     if (close (esd->fd)) {
535         qesd_logerr (errno, "%s: close on esd socket(%d) failed\n",
536                      AUDIO_FUNC, esd->fd);
537     }
538     esd->fd = -1;
539 
540  fail2:
541     qemu_free (esd->pcm_buf);
542     esd->pcm_buf = NULL;
543 
544  exit:
545     if (!D_ACTIVE)
546         stdio_enable();
547 
548     return result;
549 }
550 
qesd_fini_in(HWVoiceIn * hw)551 static void qesd_fini_in (HWVoiceIn *hw)
552 {
553     void *ret;
554     ESDVoiceIn *esd = (ESDVoiceIn *) hw;
555 
556     audio_pt_lock (&esd->pt, AUDIO_FUNC);
557     esd->done = 1;
558     audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
559     audio_pt_join (&esd->pt, &ret, AUDIO_FUNC);
560 
561     if (esd->fd >= 0) {
562         if (close (esd->fd)) {
563             qesd_logerr (errno, "failed to close esd socket\n");
564         }
565         esd->fd = -1;
566     }
567 
568     audio_pt_fini (&esd->pt, AUDIO_FUNC);
569 
570     qemu_free (esd->pcm_buf);
571     esd->pcm_buf = NULL;
572 }
573 
qesd_ctl_in(HWVoiceIn * hw,int cmd,...)574 static int qesd_ctl_in (HWVoiceIn *hw, int cmd, ...)
575 {
576     (void) hw;
577     (void) cmd;
578     return 0;
579 }
580 
581 /* common */
qesd_audio_init(void)582 static void *qesd_audio_init (void)
583 {
584     void*    result = NULL;
585 
586     D("%s: entering", __FUNCTION__);
587 
588     if (esd_lib == NULL) {
589         int  fd;
590 
591         esd_lib = dlopen( "libesd.so", RTLD_NOW );
592         if (esd_lib == NULL)
593             esd_lib = dlopen( "libesd.so.0", RTLD_NOW );
594 
595         if (esd_lib == NULL) {
596             D("could not find libesd on this system");
597             goto Exit;
598         }
599 
600         if (esd_dynlink_init(esd_lib) < 0)
601             goto Fail;
602 
603         fd = FF(esd_open_sound)(conf.dac_host);
604         if (fd < 0) {
605             D("%s: could not open direct sound server connection, trying localhost",
606               __FUNCTION__);
607             fd = FF(esd_open_sound)("localhost");
608             if (fd < 0) {
609                 D("%s: could not open localhost sound server connection", __FUNCTION__);
610                 goto Fail;
611             }
612         }
613 
614         D("%s: EsounD server connection succeeded", __FUNCTION__);
615         /* FF(esd_close)(fd); */
616     }
617     result = &conf;
618     goto Exit;
619 
620 Fail:
621     D("%s: failed to open library", __FUNCTION__);
622     dlclose(esd_lib);
623     esd_lib = NULL;
624 
625 Exit:
626     return  result;
627 }
628 
qesd_audio_fini(void * opaque)629 static void qesd_audio_fini (void *opaque)
630 {
631     (void) opaque;
632     if (esd_lib != NULL) {
633         dlclose(esd_lib);
634         esd_lib = NULL;
635     }
636     ldebug ("esd_fini");
637 }
638 
639 struct audio_option qesd_options[] = {
640     {"SAMPLES", AUD_OPT_INT, &conf.samples,
641      "buffer size in samples", NULL, 0},
642 
643     {"DIVISOR", AUD_OPT_INT, &conf.divisor,
644      "threshold divisor", NULL, 0},
645 
646     {"DAC_HOST", AUD_OPT_STR, &conf.dac_host,
647      "playback host", NULL, 0},
648 
649     {"ADC_HOST", AUD_OPT_STR, &conf.adc_host,
650      "capture host", NULL, 0},
651 
652     {NULL, 0, NULL, NULL, NULL, 0}
653 };
654 
655 struct audio_pcm_ops qesd_pcm_ops = {
656     qesd_init_out,
657     qesd_fini_out,
658     qesd_run_out,
659     qesd_write,
660     qesd_ctl_out,
661 
662     qesd_init_in,
663     qesd_fini_in,
664     qesd_run_in,
665     qesd_read,
666     qesd_ctl_in,
667 };
668 
669 struct audio_driver esd_audio_driver = {
670     INIT_FIELD (name           = ) "esd",
671     INIT_FIELD (descr          = )
672     "EsounD audio (en.wikipedia.org/wiki/Esound)",
673     INIT_FIELD (options        = ) qesd_options,
674     INIT_FIELD (init           = ) qesd_audio_init,
675     INIT_FIELD (fini           = ) qesd_audio_fini,
676     INIT_FIELD (pcm_ops        = ) &qesd_pcm_ops,
677     INIT_FIELD (can_be_default = ) 1,
678     INIT_FIELD (max_voices_out = ) INT_MAX,
679     INIT_FIELD (max_voices_in  = ) 1,
680     INIT_FIELD (voice_size_out = ) sizeof (ESDVoiceOut),
681     INIT_FIELD (voice_size_in  = ) sizeof (ESDVoiceIn)
682 };
683