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