• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * QEMU ESD audio driver
3  *
4  * Copyright (c) 2008-2009 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 #include <esd.h>
26 #include "qemu-common.h"
27 #include "audio.h"
28 #include <signal.h>
29 
30 #define AUDIO_CAP "esd"
31 #include "audio_int.h"
32 #include <dlfcn.h>
33 
34 #include "qemu_debug.h"
35 
36 #define  DEBUG  1
37 
38 #if DEBUG
39 #  include <stdio.h>
40 #  define D(...)  VERBOSE_PRINT(audio,__VA_ARGS__)
41 #  define D_ACTIVE  VERBOSE_CHECK(audio)
42 #  define O(...)  VERBOSE_PRINT(audioout,__VA_ARGS__)
43 #  define I(...)  VERBOSE_PRINT(audioin,__VA_ARGS__)
44 #else
45 #  define D(...)  ((void)0)
46 #  define D_ACTIVE 0
47 #  define O(...)  ((void)0)
48 #  define I(...)  ((void)0)
49 #endif
50 
51 #define  STRINGIFY_(x)  #x
52 #define  STRINGIFY(x)   STRINGIFY_(x)
53 
54 typedef struct {
55     HWVoiceOut hw;
56     int done;
57     int live;
58     int decr;
59     int rpos;
60     void *pcm_buf;
61     int fd;
62 } ESDVoiceOut;
63 
64 typedef struct {
65     HWVoiceIn hw;
66     int done;
67     int dead;
68     int incr;
69     int wpos;
70     void *pcm_buf;
71     int fd;
72 } ESDVoiceIn;
73 
74 static struct {
75     int samples;
76     int divisor;
77     char *dac_host;
78     char *adc_host;
79 } conf = {
80     1024,
81     2,
82     NULL,
83     NULL
84 };
85 
86 /* link dynamically to the libesd.so */
87 
88 #define  DYNLINK_FUNCTIONS   \
89     DYNLINK_FUNC(int,esd_play_stream,(esd_format_t,int,const char*,const char*))   \
90     DYNLINK_FUNC(int,esd_record_stream,(esd_format_t,int,const char*,const char*)) \
91     DYNLINK_FUNC(int,esd_open_sound,( const char *host )) \
92     DYNLINK_FUNC(int,esd_close,(int)) \
93 
94 #define  DYNLINK_FUNCTIONS_INIT \
95     esd_dynlink_init
96 
97 #include "dynlink.h"
98 
99 static void*    esd_lib;
100 
qesd_logerr(int err,const char * fmt,...)101 static void GCC_FMT_ATTR (2, 3) qesd_logerr (int err, const char *fmt, ...)
102 {
103     va_list ap;
104 
105     va_start (ap, fmt);
106     AUD_vlog (AUDIO_CAP, fmt, ap);
107     va_end (ap);
108 
109     AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err));
110 }
111 
qesd_run_out(HWVoiceOut * hw)112 static int qesd_run_out (HWVoiceOut *hw)
113 {
114     ESDVoiceOut *esd = (ESDVoiceOut *) hw;
115     int  liveSamples, totalSamples;
116     int  rpos, nwrite, writeSamples, writeBytes;
117 
118     liveSamples  = audio_pcm_hw_get_live_out (hw);
119     rpos         = hw->rpos;
120     totalSamples = 0;
121 
122     while (liveSamples > 0) {
123         int  chunkSamples = audio_MIN (liveSamples, hw->samples - rpos);
124         int  chunkBytes   = chunkSamples << hw->info.shift;
125         struct st_sample *src = hw->mix_buf + rpos;
126 
127         hw->clip (esd->pcm_buf, src, chunkSamples);
128 
129     AGAIN:
130         nwrite = write (esd->fd, esd->pcm_buf, chunkBytes);
131         if (nwrite == -1) {
132             if (errno == EINTR)
133                 goto AGAIN;
134             if (errno == EAGAIN || errno == EWOULDBLOCK)
135                 break;
136             qesd_logerr (errno, "write failed: %s\n", strerror(errno));
137             O("EsounD output thread write error: %s", strerror(errno));
138             break;
139         }
140         if (nwrite == 0)
141             break;
142 
143         writeSamples = nwrite >> hw->info.shift;
144         writeBytes   = writeSamples << hw->info.shift;
145         if (writeBytes != nwrite) {
146             dolog ("warning: Misaligned write %d (requested %d), "
147                     "alignment %d\n",
148                     nwrite, writeBytes, hw->info.align + 1);
149         }
150         rpos          = (rpos + writeSamples) % hw->samples;
151         totalSamples += writeSamples;
152         liveSamples  -= writeSamples;
153     }
154     hw->rpos = rpos;
155     return totalSamples;
156 }
157 
qesd_write(SWVoiceOut * sw,void * buf,int len)158 static int qesd_write (SWVoiceOut *sw, void *buf, int len)
159 {
160     return audio_pcm_sw_write (sw, buf, len);
161 }
162 
qesd_init_out(HWVoiceOut * hw,struct audsettings * as)163 static int qesd_init_out (HWVoiceOut *hw, struct audsettings *as)
164 {
165     ESDVoiceOut *esd = (ESDVoiceOut *) hw;
166     struct audsettings obt_as = *as;
167     int esdfmt = ESD_STREAM | ESD_PLAY;
168     int result = -1;
169 
170     /* shut down verbose debug spew */
171     if (!D_ACTIVE)
172         stdio_disable();
173 
174     O("initializing EsoundD audio output");
175     esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO;
176     switch (as->fmt) {
177     case AUD_FMT_S8:
178     case AUD_FMT_U8:
179         esdfmt |= ESD_BITS8;
180         obt_as.fmt = AUD_FMT_U8;
181         break;
182 #if 0
183     case AUD_FMT_S32:
184     case AUD_FMT_U32:
185         dolog ("Will use 16 instead of 32 bit samples\n");
186 #endif
187     case AUD_FMT_S16:
188     case AUD_FMT_U16:
189     deffmt:
190         esdfmt |= ESD_BITS16;
191         obt_as.fmt = AUD_FMT_S16;
192         break;
193 
194     default:
195         dolog ("Internal logic error: Bad audio format %d\n", as->fmt);
196         goto deffmt;
197 
198     }
199     obt_as.endianness = AUDIO_HOST_ENDIANNESS;
200 
201     audio_pcm_init_info (&hw->info, &obt_as);
202 
203     hw->samples = conf.samples;
204     esd->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
205     if (!esd->pcm_buf) {
206         dolog ("Could not allocate buffer (%d bytes)\n",
207                hw->samples << hw->info.shift);
208         goto exit;
209     }
210 
211     esd->fd = FF(esd_play_stream) (esdfmt, as->freq, conf.dac_host, NULL);
212     if (esd->fd < 0) {
213         if (conf.dac_host == NULL) {
214             esd->fd = FF(esd_play_stream) (esdfmt, as->freq, "localhost", NULL);
215         }
216         if (esd->fd < 0) {
217             qesd_logerr (errno, "esd_play_stream failed\n");
218             goto fail2;
219         }
220     }
221 
222     {
223         int  flags;
224         flags = fcntl(esd->fd, F_GETFL);
225         fcntl(esd->fd, F_SETFL, flags | O_NONBLOCK);
226     }
227 
228     result = 0;  /* success */
229     goto exit;
230 
231  fail2:
232     qemu_free (esd->pcm_buf);
233     esd->pcm_buf = NULL;
234 
235  exit:
236     if (!D_ACTIVE)
237         stdio_enable();
238 
239     return result;
240 }
241 
qesd_fini_out(HWVoiceOut * hw)242 static void qesd_fini_out (HWVoiceOut *hw)
243 {
244     ESDVoiceOut *esd = (ESDVoiceOut *) hw;
245 
246     if (esd->fd >= 0) {
247         if (close (esd->fd)) {
248             qesd_logerr (errno, "failed to close esd socket\n");
249         }
250         esd->fd = -1;
251     }
252     qemu_free (esd->pcm_buf);
253     esd->pcm_buf = NULL;
254 }
255 
qesd_ctl_out(HWVoiceOut * hw,int cmd,...)256 static int qesd_ctl_out (HWVoiceOut *hw, int cmd, ...)
257 {
258     (void) hw;
259     (void) cmd;
260     return 0;
261 }
262 
263 /* capture */
qesd_run_in(HWVoiceIn * hw)264 static int qesd_run_in (HWVoiceIn *hw)
265 {
266     int  wpos, liveSamples, totalSamples;
267     int  grabSamples;
268     ESDVoiceIn *esd = (ESDVoiceIn *) hw;
269 
270     wpos        = hw->wpos;
271     liveSamples = audio_pcm_hw_get_live_in (hw);
272     grabSamples = hw->samples - liveSamples;
273     totalSamples = 0;
274 
275     while (grabSamples > 0) {
276         ssize_t  nread;
277         int      chunkSamples = audio_MIN (grabSamples, hw->samples - wpos);
278         int      chunkBytes   = chunkSamples << hw->info.shift;
279         int      readSamples, readBytes;
280         void*    buf          = advance (esd->pcm_buf, wpos);
281 
282     AGAIN:
283         nread = read (esd->fd, buf, chunkBytes);
284         if (nread == -1) {
285             if (errno == EINTR)
286                 goto AGAIN;
287             if (errno == EAGAIN || errno == EWOULDBLOCK)
288                 break;
289 
290             qesd_logerr (errno, "read failed: %s\n", strerror(errno));
291             break;
292         }
293         if (nread == 0)
294             break;
295 
296         readSamples = nread >> hw->info.shift;
297         readBytes   = readSamples << hw->info.shift;
298 
299         if (readBytes != nread) {
300             dolog ("warning: Misaligned read %d (requested %d), "
301                     "alignment %d\n",
302                     nread, readBytes, hw->info.align + 1);
303         }
304 
305         hw->conv (hw->conv_buf + wpos, buf, readSamples,
306                   &nominal_volume);
307 
308         wpos = (wpos + readSamples) % hw->samples;
309         grabSamples  -= readSamples;
310         totalSamples += readSamples;
311     }
312     hw->wpos = wpos;
313     return totalSamples;
314 }
315 
qesd_read(SWVoiceIn * sw,void * buf,int len)316 static int qesd_read (SWVoiceIn *sw, void *buf, int len)
317 {
318     return audio_pcm_sw_read (sw, buf, len);
319 }
320 
qesd_init_in(HWVoiceIn * hw,struct audsettings * as)321 static int qesd_init_in (HWVoiceIn *hw, struct audsettings *as)
322 {
323     ESDVoiceIn *esd = (ESDVoiceIn *) hw;
324     struct audsettings obt_as = *as;
325     int esdfmt = ESD_STREAM | ESD_RECORD;
326     int result = -1;
327 
328     /* shut down verbose debug spew */
329     if (!D_ACTIVE)
330         stdio_disable();
331 
332     esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO;
333     switch (as->fmt) {
334     case AUD_FMT_S8:
335     case AUD_FMT_U8:
336         esdfmt |= ESD_BITS8;
337         obt_as.fmt = AUD_FMT_U8;
338         break;
339 
340     case AUD_FMT_S16:
341     case AUD_FMT_U16:
342         esdfmt |= ESD_BITS16;
343         obt_as.fmt = AUD_FMT_S16;
344         break;
345 
346     case AUD_FMT_S32:
347     case AUD_FMT_U32:
348         dolog ("Will use 16 instead of 32 bit samples\n");
349         esdfmt |= ESD_BITS16;
350         obt_as.fmt = AUD_FMT_S16;
351         break;
352     }
353     obt_as.endianness = AUDIO_HOST_ENDIANNESS;
354 
355     audio_pcm_init_info (&hw->info, &obt_as);
356 
357     hw->samples = conf.samples;
358     esd->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
359     if (!esd->pcm_buf) {
360         dolog ("Could not allocate buffer (%d bytes)\n",
361                hw->samples << hw->info.shift);
362         goto exit;
363     }
364 
365     esd->fd = FF(esd_record_stream) (esdfmt, as->freq, conf.adc_host, NULL);
366     if (esd->fd < 0) {
367         if (conf.adc_host == NULL) {
368             esd->fd = FF(esd_record_stream) (esdfmt, as->freq, "localhost", NULL);
369         }
370         if (esd->fd < 0) {
371             qesd_logerr (errno, "esd_record_stream failed\n");
372             goto fail2;
373         }
374     }
375 
376     {
377         int  flags;
378         flags = fcntl(esd->fd, F_GETFL);
379         fcntl(esd->fd, F_SETFL, flags | O_NONBLOCK);
380     }
381 
382     result = 0;  /* success */
383     goto exit;
384 
385  fail2:
386     qemu_free (esd->pcm_buf);
387     esd->pcm_buf = NULL;
388 
389  exit:
390     if (!D_ACTIVE)
391         stdio_enable();
392 
393     return result;
394 }
395 
qesd_fini_in(HWVoiceIn * hw)396 static void qesd_fini_in (HWVoiceIn *hw)
397 {
398     ESDVoiceIn *esd = (ESDVoiceIn *) hw;
399 
400     if (esd->fd >= 0) {
401         if (close (esd->fd)) {
402             qesd_logerr (errno, "failed to close esd socket\n");
403         }
404         esd->fd = -1;
405     }
406     qemu_free (esd->pcm_buf);
407     esd->pcm_buf = NULL;
408 }
409 
qesd_ctl_in(HWVoiceIn * hw,int cmd,...)410 static int qesd_ctl_in (HWVoiceIn *hw, int cmd, ...)
411 {
412     (void) hw;
413     (void) cmd;
414     return 0;
415 }
416 
417 /* common */
qesd_audio_init(void)418 static void *qesd_audio_init (void)
419 {
420     void*    result = NULL;
421 
422     D("%s: entering", __FUNCTION__);
423 
424     if (esd_lib == NULL) {
425         int  fd;
426 
427         esd_lib = dlopen( "libesd.so", RTLD_NOW );
428         if (esd_lib == NULL)
429             esd_lib = dlopen( "libesd.so.0", RTLD_NOW );
430 
431         if (esd_lib == NULL) {
432             D("could not find libesd on this system");
433             goto Exit;
434         }
435 
436         if (esd_dynlink_init(esd_lib) < 0)
437             goto Fail;
438 
439         fd = FF(esd_open_sound)(conf.dac_host);
440         if (fd < 0) {
441             D("%s: could not open direct sound server connection, trying localhost",
442               __FUNCTION__);
443             fd = FF(esd_open_sound)("localhost");
444             if (fd < 0) {
445                 D("%s: could not open localhost sound server connection", __FUNCTION__);
446                 goto Fail;
447             }
448         }
449 
450         D("%s: EsounD server connection succeeded", __FUNCTION__);
451         /* FF(esd_close)(fd); */
452     }
453     result = &conf;
454     goto Exit;
455 
456 Fail:
457     D("%s: failed to open library", __FUNCTION__);
458     dlclose(esd_lib);
459     esd_lib = NULL;
460 
461 Exit:
462     return  result;
463 }
464 
qesd_audio_fini(void * opaque)465 static void qesd_audio_fini (void *opaque)
466 {
467     (void) opaque;
468     if (esd_lib != NULL) {
469         dlclose(esd_lib);
470         esd_lib = NULL;
471     }
472     ldebug ("esd_fini");
473 }
474 
475 struct audio_option qesd_options[] = {
476     {"SAMPLES", AUD_OPT_INT, &conf.samples,
477      "buffer size in samples", NULL, 0},
478 
479     {"DIVISOR", AUD_OPT_INT, &conf.divisor,
480      "threshold divisor", NULL, 0},
481 
482     {"DAC_HOST", AUD_OPT_STR, &conf.dac_host,
483      "playback host", NULL, 0},
484 
485     {"ADC_HOST", AUD_OPT_STR, &conf.adc_host,
486      "capture host", NULL, 0},
487 
488     {NULL, 0, NULL, NULL, NULL, 0}
489 };
490 
491 static struct audio_pcm_ops qesd_pcm_ops = {
492     qesd_init_out,
493     qesd_fini_out,
494     qesd_run_out,
495     qesd_write,
496     qesd_ctl_out,
497 
498     qesd_init_in,
499     qesd_fini_in,
500     qesd_run_in,
501     qesd_read,
502     qesd_ctl_in,
503 };
504 
505 struct audio_driver esd_audio_driver = {
506     INIT_FIELD (name           = ) "esd",
507     INIT_FIELD (descr          = )
508     "EsounD audio (en.wikipedia.org/wiki/Esound)",
509     INIT_FIELD (options        = ) qesd_options,
510     INIT_FIELD (init           = ) qesd_audio_init,
511     INIT_FIELD (fini           = ) qesd_audio_fini,
512     INIT_FIELD (pcm_ops        = ) &qesd_pcm_ops,
513     INIT_FIELD (can_be_default = ) 1,
514     INIT_FIELD (max_voices_out = ) INT_MAX,
515     INIT_FIELD (max_voices_in  = ) 1,
516     INIT_FIELD (voice_size_out = ) sizeof (ESDVoiceOut),
517     INIT_FIELD (voice_size_in  = ) sizeof (ESDVoiceIn)
518 };
519