symbian-qemu-0.9.1-12/qemu-symbian-svp/audio/paaudio.c
changeset 1 2fb8b9db1c86
equal deleted inserted replaced
0:ffa851df0825 1:2fb8b9db1c86
       
     1 /* public domain */
       
     2 #include "qemu-common.h"
       
     3 #include "audio.h"
       
     4 
       
     5 #include <pulse/simple.h>
       
     6 #include <pulse/error.h>
       
     7 
       
     8 #define AUDIO_CAP "pulseaudio"
       
     9 #include "audio_int.h"
       
    10 #include "audio_pt_int.h"
       
    11 
       
    12 typedef struct {
       
    13     HWVoiceOut hw;
       
    14     int done;
       
    15     int live;
       
    16     int decr;
       
    17     int rpos;
       
    18     pa_simple *s;
       
    19     void *pcm_buf;
       
    20     struct audio_pt pt;
       
    21 } PAVoiceOut;
       
    22 
       
    23 typedef struct {
       
    24     HWVoiceIn hw;
       
    25     int done;
       
    26     int dead;
       
    27     int incr;
       
    28     int wpos;
       
    29     pa_simple *s;
       
    30     void *pcm_buf;
       
    31     struct audio_pt pt;
       
    32 } PAVoiceIn;
       
    33 
       
    34 static struct {
       
    35     int samples;
       
    36     int divisor;
       
    37     char *server;
       
    38     char *sink;
       
    39     char *source;
       
    40 } conf = {
       
    41     1024,
       
    42     2,
       
    43     NULL,
       
    44     NULL,
       
    45     NULL
       
    46 };
       
    47 
       
    48 static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...)
       
    49 {
       
    50     va_list ap;
       
    51 
       
    52     va_start (ap, fmt);
       
    53     AUD_vlog (AUDIO_CAP, fmt, ap);
       
    54     va_end (ap);
       
    55 
       
    56     AUD_log (AUDIO_CAP, "Reason: %s\n", pa_strerror (err));
       
    57 }
       
    58 
       
    59 static void *qpa_thread_out (void *arg)
       
    60 {
       
    61     PAVoiceOut *pa = arg;
       
    62     HWVoiceOut *hw = &pa->hw;
       
    63     int threshold;
       
    64 
       
    65     threshold = conf.divisor ? hw->samples / conf.divisor : 0;
       
    66 
       
    67     if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
       
    68         return NULL;
       
    69     }
       
    70 
       
    71     for (;;) {
       
    72         int decr, to_mix, rpos;
       
    73 
       
    74         for (;;) {
       
    75             if (pa->done) {
       
    76                 goto exit;
       
    77             }
       
    78 
       
    79             if (pa->live > threshold) {
       
    80                 break;
       
    81             }
       
    82 
       
    83             if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) {
       
    84                 goto exit;
       
    85             }
       
    86         }
       
    87 
       
    88         decr = to_mix = pa->live;
       
    89         rpos = hw->rpos;
       
    90 
       
    91         if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
       
    92             return NULL;
       
    93         }
       
    94 
       
    95         while (to_mix) {
       
    96             int error;
       
    97             int chunk = audio_MIN (to_mix, hw->samples - rpos);
       
    98             struct st_sample *src = hw->mix_buf + rpos;
       
    99 
       
   100             hw->clip (pa->pcm_buf, src, chunk);
       
   101 
       
   102             if (pa_simple_write (pa->s, pa->pcm_buf,
       
   103                                  chunk << hw->info.shift, &error) < 0) {
       
   104                 qpa_logerr (error, "pa_simple_write failed\n");
       
   105                 return NULL;
       
   106             }
       
   107 
       
   108             rpos = (rpos + chunk) % hw->samples;
       
   109             to_mix -= chunk;
       
   110         }
       
   111 
       
   112         if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
       
   113             return NULL;
       
   114         }
       
   115 
       
   116         pa->rpos = rpos;
       
   117         pa->live -= decr;
       
   118         pa->decr += decr;
       
   119     }
       
   120 
       
   121  exit:
       
   122     audio_pt_unlock (&pa->pt, AUDIO_FUNC);
       
   123     return NULL;
       
   124 }
       
   125 
       
   126 static int qpa_run_out (HWVoiceOut *hw)
       
   127 {
       
   128     int live, decr;
       
   129     PAVoiceOut *pa = (PAVoiceOut *) hw;
       
   130 
       
   131     if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
       
   132         return 0;
       
   133     }
       
   134 
       
   135     live = audio_pcm_hw_get_live_out (hw);
       
   136     decr = audio_MIN (live, pa->decr);
       
   137     pa->decr -= decr;
       
   138     pa->live = live - decr;
       
   139     hw->rpos = pa->rpos;
       
   140     if (pa->live > 0) {
       
   141         audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
       
   142     }
       
   143     else {
       
   144         audio_pt_unlock (&pa->pt, AUDIO_FUNC);
       
   145     }
       
   146     return decr;
       
   147 }
       
   148 
       
   149 static int qpa_write (SWVoiceOut *sw, void *buf, int len)
       
   150 {
       
   151     return audio_pcm_sw_write (sw, buf, len);
       
   152 }
       
   153 
       
   154 /* capture */
       
   155 static void *qpa_thread_in (void *arg)
       
   156 {
       
   157     PAVoiceIn *pa = arg;
       
   158     HWVoiceIn *hw = &pa->hw;
       
   159     int threshold;
       
   160 
       
   161     threshold = conf.divisor ? hw->samples / conf.divisor : 0;
       
   162 
       
   163     if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
       
   164         return NULL;
       
   165     }
       
   166 
       
   167     for (;;) {
       
   168         int incr, to_grab, wpos;
       
   169 
       
   170         for (;;) {
       
   171             if (pa->done) {
       
   172                 goto exit;
       
   173             }
       
   174 
       
   175             if (pa->dead > threshold) {
       
   176                 break;
       
   177             }
       
   178 
       
   179             if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) {
       
   180                 goto exit;
       
   181             }
       
   182         }
       
   183 
       
   184         incr = to_grab = pa->dead;
       
   185         wpos = hw->wpos;
       
   186 
       
   187         if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
       
   188             return NULL;
       
   189         }
       
   190 
       
   191         while (to_grab) {
       
   192             int error;
       
   193             int chunk = audio_MIN (to_grab, hw->samples - wpos);
       
   194             void *buf = advance (pa->pcm_buf, wpos);
       
   195 
       
   196             if (pa_simple_read (pa->s, buf,
       
   197                                 chunk << hw->info.shift, &error) < 0) {
       
   198                 qpa_logerr (error, "pa_simple_read failed\n");
       
   199                 return NULL;
       
   200             }
       
   201 
       
   202             hw->conv (hw->conv_buf + wpos, buf, chunk, &nominal_volume);
       
   203             wpos = (wpos + chunk) % hw->samples;
       
   204             to_grab -= chunk;
       
   205         }
       
   206 
       
   207         if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
       
   208             return NULL;
       
   209         }
       
   210 
       
   211         pa->wpos = wpos;
       
   212         pa->dead -= incr;
       
   213         pa->incr += incr;
       
   214     }
       
   215 
       
   216  exit:
       
   217     audio_pt_unlock (&pa->pt, AUDIO_FUNC);
       
   218     return NULL;
       
   219 }
       
   220 
       
   221 static int qpa_run_in (HWVoiceIn *hw)
       
   222 {
       
   223     int live, incr, dead;
       
   224     PAVoiceIn *pa = (PAVoiceIn *) hw;
       
   225 
       
   226     if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
       
   227         return 0;
       
   228     }
       
   229 
       
   230     live = audio_pcm_hw_get_live_in (hw);
       
   231     dead = hw->samples - live;
       
   232     incr = audio_MIN (dead, pa->incr);
       
   233     pa->incr -= incr;
       
   234     pa->dead = dead - incr;
       
   235     hw->wpos = pa->wpos;
       
   236     if (pa->dead > 0) {
       
   237         audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
       
   238     }
       
   239     else {
       
   240         audio_pt_unlock (&pa->pt, AUDIO_FUNC);
       
   241     }
       
   242     return incr;
       
   243 }
       
   244 
       
   245 static int qpa_read (SWVoiceIn *sw, void *buf, int len)
       
   246 {
       
   247     return audio_pcm_sw_read (sw, buf, len);
       
   248 }
       
   249 
       
   250 static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness)
       
   251 {
       
   252     int format;
       
   253 
       
   254     switch (afmt) {
       
   255     case AUD_FMT_S8:
       
   256     case AUD_FMT_U8:
       
   257         format = PA_SAMPLE_U8;
       
   258         break;
       
   259     case AUD_FMT_S16:
       
   260     case AUD_FMT_U16:
       
   261         format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE;
       
   262         break;
       
   263     case AUD_FMT_S32:
       
   264     case AUD_FMT_U32:
       
   265         format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE;
       
   266         break;
       
   267     default:
       
   268         dolog ("Internal logic error: Bad audio format %d\n", afmt);
       
   269         format = PA_SAMPLE_U8;
       
   270         break;
       
   271     }
       
   272     return format;
       
   273 }
       
   274 
       
   275 static audfmt_e pa_to_audfmt (pa_sample_format_t fmt, int *endianness)
       
   276 {
       
   277     switch (fmt) {
       
   278     case PA_SAMPLE_U8:
       
   279         return AUD_FMT_U8;
       
   280     case PA_SAMPLE_S16BE:
       
   281         *endianness = 1;
       
   282         return AUD_FMT_S16;
       
   283     case PA_SAMPLE_S16LE:
       
   284         *endianness = 0;
       
   285         return AUD_FMT_S16;
       
   286     case PA_SAMPLE_S32BE:
       
   287         *endianness = 1;
       
   288         return AUD_FMT_S32;
       
   289     case PA_SAMPLE_S32LE:
       
   290         *endianness = 0;
       
   291         return AUD_FMT_S32;
       
   292     default:
       
   293         dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt);
       
   294         return AUD_FMT_U8;
       
   295     }
       
   296 }
       
   297 
       
   298 static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as)
       
   299 {
       
   300     int error;
       
   301     static pa_sample_spec ss;
       
   302     struct audsettings obt_as = *as;
       
   303     PAVoiceOut *pa = (PAVoiceOut *) hw;
       
   304 
       
   305     ss.format = audfmt_to_pa (as->fmt, as->endianness);
       
   306     ss.channels = as->nchannels;
       
   307     ss.rate = as->freq;
       
   308 
       
   309     obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
       
   310 
       
   311     pa->s = pa_simple_new (
       
   312         conf.server,
       
   313         "qemu",
       
   314         PA_STREAM_PLAYBACK,
       
   315         conf.sink,
       
   316         "pcm.playback",
       
   317         &ss,
       
   318         NULL,                   /* channel map */
       
   319         NULL,                   /* buffering attributes */
       
   320         &error
       
   321         );
       
   322     if (!pa->s) {
       
   323         qpa_logerr (error, "pa_simple_new for playback failed\n");
       
   324         goto fail1;
       
   325     }
       
   326 
       
   327     audio_pcm_init_info (&hw->info, &obt_as);
       
   328     hw->samples = conf.samples;
       
   329     pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
       
   330     if (!pa->pcm_buf) {
       
   331         dolog ("Could not allocate buffer (%d bytes)\n",
       
   332                hw->samples << hw->info.shift);
       
   333         goto fail2;
       
   334     }
       
   335 
       
   336     if (audio_pt_init (&pa->pt, qpa_thread_out, hw, AUDIO_CAP, AUDIO_FUNC)) {
       
   337         goto fail3;
       
   338     }
       
   339 
       
   340     return 0;
       
   341 
       
   342  fail3:
       
   343     free (pa->pcm_buf);
       
   344     pa->pcm_buf = NULL;
       
   345  fail2:
       
   346     pa_simple_free (pa->s);
       
   347     pa->s = NULL;
       
   348  fail1:
       
   349     return -1;
       
   350 }
       
   351 
       
   352 static int qpa_init_in (HWVoiceIn *hw, struct audsettings *as)
       
   353 {
       
   354     int error;
       
   355     static pa_sample_spec ss;
       
   356     struct audsettings obt_as = *as;
       
   357     PAVoiceIn *pa = (PAVoiceIn *) hw;
       
   358 
       
   359     ss.format = audfmt_to_pa (as->fmt, as->endianness);
       
   360     ss.channels = as->nchannels;
       
   361     ss.rate = as->freq;
       
   362 
       
   363     obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
       
   364 
       
   365     pa->s = pa_simple_new (
       
   366         conf.server,
       
   367         "qemu",
       
   368         PA_STREAM_RECORD,
       
   369         conf.source,
       
   370         "pcm.capture",
       
   371         &ss,
       
   372         NULL,                   /* channel map */
       
   373         NULL,                   /* buffering attributes */
       
   374         &error
       
   375         );
       
   376     if (!pa->s) {
       
   377         qpa_logerr (error, "pa_simple_new for capture failed\n");
       
   378         goto fail1;
       
   379     }
       
   380 
       
   381     audio_pcm_init_info (&hw->info, &obt_as);
       
   382     hw->samples = conf.samples;
       
   383     pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
       
   384     if (!pa->pcm_buf) {
       
   385         dolog ("Could not allocate buffer (%d bytes)\n",
       
   386                hw->samples << hw->info.shift);
       
   387         goto fail2;
       
   388     }
       
   389 
       
   390     if (audio_pt_init (&pa->pt, qpa_thread_in, hw, AUDIO_CAP, AUDIO_FUNC)) {
       
   391         goto fail3;
       
   392     }
       
   393 
       
   394     return 0;
       
   395 
       
   396  fail3:
       
   397     free (pa->pcm_buf);
       
   398     pa->pcm_buf = NULL;
       
   399  fail2:
       
   400     pa_simple_free (pa->s);
       
   401     pa->s = NULL;
       
   402  fail1:
       
   403     return -1;
       
   404 }
       
   405 
       
   406 static void qpa_fini_out (HWVoiceOut *hw)
       
   407 {
       
   408     void *ret;
       
   409     PAVoiceOut *pa = (PAVoiceOut *) hw;
       
   410 
       
   411     audio_pt_lock (&pa->pt, AUDIO_FUNC);
       
   412     pa->done = 1;
       
   413     audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
       
   414     audio_pt_join (&pa->pt, &ret, AUDIO_FUNC);
       
   415 
       
   416     if (pa->s) {
       
   417         pa_simple_free (pa->s);
       
   418         pa->s = NULL;
       
   419     }
       
   420 
       
   421     audio_pt_fini (&pa->pt, AUDIO_FUNC);
       
   422     qemu_free (pa->pcm_buf);
       
   423     pa->pcm_buf = NULL;
       
   424 }
       
   425 
       
   426 static void qpa_fini_in (HWVoiceIn *hw)
       
   427 {
       
   428     void *ret;
       
   429     PAVoiceIn *pa = (PAVoiceIn *) hw;
       
   430 
       
   431     audio_pt_lock (&pa->pt, AUDIO_FUNC);
       
   432     pa->done = 1;
       
   433     audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
       
   434     audio_pt_join (&pa->pt, &ret, AUDIO_FUNC);
       
   435 
       
   436     if (pa->s) {
       
   437         pa_simple_free (pa->s);
       
   438         pa->s = NULL;
       
   439     }
       
   440 
       
   441     audio_pt_fini (&pa->pt, AUDIO_FUNC);
       
   442     qemu_free (pa->pcm_buf);
       
   443     pa->pcm_buf = NULL;
       
   444 }
       
   445 
       
   446 static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
       
   447 {
       
   448     (void) hw;
       
   449     (void) cmd;
       
   450     return 0;
       
   451 }
       
   452 
       
   453 static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
       
   454 {
       
   455     (void) hw;
       
   456     (void) cmd;
       
   457     return 0;
       
   458 }
       
   459 
       
   460 /* common */
       
   461 static void *qpa_audio_init (void)
       
   462 {
       
   463     return &conf;
       
   464 }
       
   465 
       
   466 static void qpa_audio_fini (void *opaque)
       
   467 {
       
   468     (void) opaque;
       
   469 }
       
   470 
       
   471 struct audio_option qpa_options[] = {
       
   472     {"SAMPLES", AUD_OPT_INT, &conf.samples,
       
   473      "buffer size in samples", NULL, 0},
       
   474 
       
   475     {"DIVISOR", AUD_OPT_INT, &conf.divisor,
       
   476      "threshold divisor", NULL, 0},
       
   477 
       
   478     {"SERVER", AUD_OPT_STR, &conf.server,
       
   479      "server address", NULL, 0},
       
   480 
       
   481     {"SINK", AUD_OPT_STR, &conf.sink,
       
   482      "sink device name", NULL, 0},
       
   483 
       
   484     {"SOURCE", AUD_OPT_STR, &conf.source,
       
   485      "source device name", NULL, 0},
       
   486 
       
   487     {NULL, 0, NULL, NULL, NULL, 0}
       
   488 };
       
   489 
       
   490 static struct audio_pcm_ops qpa_pcm_ops = {
       
   491     qpa_init_out,
       
   492     qpa_fini_out,
       
   493     qpa_run_out,
       
   494     qpa_write,
       
   495     qpa_ctl_out,
       
   496     qpa_init_in,
       
   497     qpa_fini_in,
       
   498     qpa_run_in,
       
   499     qpa_read,
       
   500     qpa_ctl_in
       
   501 };
       
   502 
       
   503 struct audio_driver pa_audio_driver = {
       
   504     INIT_FIELD (name           = ) "pa",
       
   505     INIT_FIELD (descr          = ) "http://www.pulseaudio.org/",
       
   506     INIT_FIELD (options        = ) qpa_options,
       
   507     INIT_FIELD (init           = ) qpa_audio_init,
       
   508     INIT_FIELD (fini           = ) qpa_audio_fini,
       
   509     INIT_FIELD (pcm_ops        = ) &qpa_pcm_ops,
       
   510     INIT_FIELD (can_be_default = ) 0,
       
   511     INIT_FIELD (max_voices_out = ) INT_MAX,
       
   512     INIT_FIELD (max_voices_in  = ) INT_MAX,
       
   513     INIT_FIELD (voice_size_out = ) sizeof (PAVoiceOut),
       
   514     INIT_FIELD (voice_size_in  = ) sizeof (PAVoiceIn)
       
   515 };