Annotation of qemu/audio/alsaaudio.c, revision 1.1

1.1     ! root        1: /*
        !             2:  * QEMU ALSA audio driver
        !             3:  *
        !             4:  * Copyright (c) 2005 Vassili Karpov (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 <alsa/asoundlib.h>
        !            25: #include "vl.h"
        !            26: 
        !            27: #define AUDIO_CAP "alsa"
        !            28: #include "audio_int.h"
        !            29: 
        !            30: typedef struct ALSAVoiceOut {
        !            31:     HWVoiceOut hw;
        !            32:     void *pcm_buf;
        !            33:     snd_pcm_t *handle;
        !            34: } ALSAVoiceOut;
        !            35: 
        !            36: typedef struct ALSAVoiceIn {
        !            37:     HWVoiceIn hw;
        !            38:     snd_pcm_t *handle;
        !            39:     void *pcm_buf;
        !            40: } ALSAVoiceIn;
        !            41: 
        !            42: static struct {
        !            43:     int size_in_usec_in;
        !            44:     int size_in_usec_out;
        !            45:     const char *pcm_name_in;
        !            46:     const char *pcm_name_out;
        !            47:     unsigned int buffer_size_in;
        !            48:     unsigned int period_size_in;
        !            49:     unsigned int buffer_size_out;
        !            50:     unsigned int period_size_out;
        !            51:     unsigned int threshold;
        !            52: 
        !            53:     int buffer_size_in_overriden;
        !            54:     int period_size_in_overriden;
        !            55: 
        !            56:     int buffer_size_out_overriden;
        !            57:     int period_size_out_overriden;
        !            58:     int verbose;
        !            59: } conf = {
        !            60: #ifdef HIGH_LATENCY
        !            61:     .size_in_usec_in = 1,
        !            62:     .size_in_usec_out = 1,
        !            63: #endif
        !            64:     .pcm_name_out = "hw:0,0",
        !            65:     .pcm_name_in = "hw:0,0",
        !            66: #ifdef HIGH_LATENCY
        !            67:     .buffer_size_in = 400000,
        !            68:     .period_size_in = 400000 / 4,
        !            69:     .buffer_size_out = 400000,
        !            70:     .period_size_out = 400000 / 4,
        !            71: #else
        !            72: #define DEFAULT_BUFFER_SIZE 1024
        !            73: #define DEFAULT_PERIOD_SIZE 256
        !            74:     .buffer_size_in = DEFAULT_BUFFER_SIZE * 4,
        !            75:     .period_size_in = DEFAULT_PERIOD_SIZE * 4,
        !            76:     .buffer_size_out = DEFAULT_BUFFER_SIZE,
        !            77:     .period_size_out = DEFAULT_PERIOD_SIZE,
        !            78:     .buffer_size_in_overriden = 0,
        !            79:     .buffer_size_out_overriden = 0,
        !            80:     .period_size_in_overriden = 0,
        !            81:     .period_size_out_overriden = 0,
        !            82: #endif
        !            83:     .threshold = 0,
        !            84:     .verbose = 0
        !            85: };
        !            86: 
        !            87: struct alsa_params_req {
        !            88:     int freq;
        !            89:     audfmt_e fmt;
        !            90:     int nchannels;
        !            91:     unsigned int buffer_size;
        !            92:     unsigned int period_size;
        !            93: };
        !            94: 
        !            95: struct alsa_params_obt {
        !            96:     int freq;
        !            97:     audfmt_e fmt;
        !            98:     int nchannels;
        !            99:     snd_pcm_uframes_t samples;
        !           100: };
        !           101: 
        !           102: static void GCC_FMT_ATTR (2, 3) alsa_logerr (int err, const char *fmt, ...)
        !           103: {
        !           104:     va_list ap;
        !           105: 
        !           106:     va_start (ap, fmt);
        !           107:     AUD_vlog (AUDIO_CAP, fmt, ap);
        !           108:     va_end (ap);
        !           109: 
        !           110:     AUD_log (AUDIO_CAP, "Reason: %s\n", snd_strerror (err));
        !           111: }
        !           112: 
        !           113: static void GCC_FMT_ATTR (3, 4) alsa_logerr2 (
        !           114:     int err,
        !           115:     const char *typ,
        !           116:     const char *fmt,
        !           117:     ...
        !           118:     )
        !           119: {
        !           120:     va_list ap;
        !           121: 
        !           122:     AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
        !           123: 
        !           124:     va_start (ap, fmt);
        !           125:     AUD_vlog (AUDIO_CAP, fmt, ap);
        !           126:     va_end (ap);
        !           127: 
        !           128:     AUD_log (AUDIO_CAP, "Reason: %s\n", snd_strerror (err));
        !           129: }
        !           130: 
        !           131: static void alsa_anal_close (snd_pcm_t **handlep)
        !           132: {
        !           133:     int err = snd_pcm_close (*handlep);
        !           134:     if (err) {
        !           135:         alsa_logerr (err, "Failed to close PCM handle %p\n", *handlep);
        !           136:     }
        !           137:     *handlep = NULL;
        !           138: }
        !           139: 
        !           140: static int alsa_write (SWVoiceOut *sw, void *buf, int len)
        !           141: {
        !           142:     return audio_pcm_sw_write (sw, buf, len);
        !           143: }
        !           144: 
        !           145: static int aud_to_alsafmt (audfmt_e fmt)
        !           146: {
        !           147:     switch (fmt) {
        !           148:     case AUD_FMT_S8:
        !           149:         return SND_PCM_FORMAT_S8;
        !           150: 
        !           151:     case AUD_FMT_U8:
        !           152:         return SND_PCM_FORMAT_U8;
        !           153: 
        !           154:     case AUD_FMT_S16:
        !           155:         return SND_PCM_FORMAT_S16_LE;
        !           156: 
        !           157:     case AUD_FMT_U16:
        !           158:         return SND_PCM_FORMAT_U16_LE;
        !           159: 
        !           160:     default:
        !           161:         dolog ("Internal logic error: Bad audio format %d\n", fmt);
        !           162: #ifdef DEBUG_AUDIO
        !           163:         abort ();
        !           164: #endif
        !           165:         return SND_PCM_FORMAT_U8;
        !           166:     }
        !           167: }
        !           168: 
        !           169: static int alsa_to_audfmt (int alsafmt, audfmt_e *fmt, int *endianness)
        !           170: {
        !           171:     switch (alsafmt) {
        !           172:     case SND_PCM_FORMAT_S8:
        !           173:         *endianness = 0;
        !           174:         *fmt = AUD_FMT_S8;
        !           175:         break;
        !           176: 
        !           177:     case SND_PCM_FORMAT_U8:
        !           178:         *endianness = 0;
        !           179:         *fmt = AUD_FMT_U8;
        !           180:         break;
        !           181: 
        !           182:     case SND_PCM_FORMAT_S16_LE:
        !           183:         *endianness = 0;
        !           184:         *fmt = AUD_FMT_S16;
        !           185:         break;
        !           186: 
        !           187:     case SND_PCM_FORMAT_U16_LE:
        !           188:         *endianness = 0;
        !           189:         *fmt = AUD_FMT_U16;
        !           190:         break;
        !           191: 
        !           192:     case SND_PCM_FORMAT_S16_BE:
        !           193:         *endianness = 1;
        !           194:         *fmt = AUD_FMT_S16;
        !           195:         break;
        !           196: 
        !           197:     case SND_PCM_FORMAT_U16_BE:
        !           198:         *endianness = 1;
        !           199:         *fmt = AUD_FMT_U16;
        !           200:         break;
        !           201: 
        !           202:     default:
        !           203:         dolog ("Unrecognized audio format %d\n", alsafmt);
        !           204:         return -1;
        !           205:     }
        !           206: 
        !           207:     return 0;
        !           208: }
        !           209: 
        !           210: #if defined DEBUG_MISMATCHES || defined DEBUG
        !           211: static void alsa_dump_info (struct alsa_params_req *req,
        !           212:                             struct alsa_params_obt *obt)
        !           213: {
        !           214:     dolog ("parameter | requested value | obtained value\n");
        !           215:     dolog ("format    |      %10d |     %10d\n", req->fmt, obt->fmt);
        !           216:     dolog ("channels  |      %10d |     %10d\n",
        !           217:            req->nchannels, obt->nchannels);
        !           218:     dolog ("frequency |      %10d |     %10d\n", req->freq, obt->freq);
        !           219:     dolog ("============================================\n");
        !           220:     dolog ("requested: buffer size %d period size %d\n",
        !           221:            req->buffer_size, req->period_size);
        !           222:     dolog ("obtained: samples %ld\n", obt->samples);
        !           223: }
        !           224: #endif
        !           225: 
        !           226: static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold)
        !           227: {
        !           228:     int err;
        !           229:     snd_pcm_sw_params_t *sw_params;
        !           230: 
        !           231:     snd_pcm_sw_params_alloca (&sw_params);
        !           232: 
        !           233:     err = snd_pcm_sw_params_current (handle, sw_params);
        !           234:     if (err < 0) {
        !           235:         dolog ("Could not fully initialize DAC\n");
        !           236:         alsa_logerr (err, "Failed to get current software parameters\n");
        !           237:         return;
        !           238:     }
        !           239: 
        !           240:     err = snd_pcm_sw_params_set_start_threshold (handle, sw_params, threshold);
        !           241:     if (err < 0) {
        !           242:         dolog ("Could not fully initialize DAC\n");
        !           243:         alsa_logerr (err, "Failed to set software threshold to %ld\n",
        !           244:                      threshold);
        !           245:         return;
        !           246:     }
        !           247: 
        !           248:     err = snd_pcm_sw_params (handle, sw_params);
        !           249:     if (err < 0) {
        !           250:         dolog ("Could not fully initialize DAC\n");
        !           251:         alsa_logerr (err, "Failed to set software parameters\n");
        !           252:         return;
        !           253:     }
        !           254: }
        !           255: 
        !           256: static int alsa_open (int in, struct alsa_params_req *req,
        !           257:                       struct alsa_params_obt *obt, snd_pcm_t **handlep)
        !           258: {
        !           259:     snd_pcm_t *handle;
        !           260:     snd_pcm_hw_params_t *hw_params;
        !           261:     int err, freq, nchannels;
        !           262:     const char *pcm_name = in ? conf.pcm_name_in : conf.pcm_name_out;
        !           263:     unsigned int period_size, buffer_size;
        !           264:     snd_pcm_uframes_t obt_buffer_size;
        !           265:     const char *typ = in ? "ADC" : "DAC";
        !           266: 
        !           267:     freq = req->freq;
        !           268:     period_size = req->period_size;
        !           269:     buffer_size = req->buffer_size;
        !           270:     nchannels = req->nchannels;
        !           271: 
        !           272:     snd_pcm_hw_params_alloca (&hw_params);
        !           273: 
        !           274:     err = snd_pcm_open (
        !           275:         &handle,
        !           276:         pcm_name,
        !           277:         in ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,
        !           278:         SND_PCM_NONBLOCK
        !           279:         );
        !           280:     if (err < 0) {
        !           281:         alsa_logerr2 (err, typ, "Failed to open `%s':\n", pcm_name);
        !           282:         return -1;
        !           283:     }
        !           284: 
        !           285:     err = snd_pcm_hw_params_any (handle, hw_params);
        !           286:     if (err < 0) {
        !           287:         alsa_logerr2 (err, typ, "Failed to initialize hardware parameters\n");
        !           288:         goto err;
        !           289:     }
        !           290: 
        !           291:     err = snd_pcm_hw_params_set_access (
        !           292:         handle,
        !           293:         hw_params,
        !           294:         SND_PCM_ACCESS_RW_INTERLEAVED
        !           295:         );
        !           296:     if (err < 0) {
        !           297:         alsa_logerr2 (err, typ, "Failed to set access type\n");
        !           298:         goto err;
        !           299:     }
        !           300: 
        !           301:     err = snd_pcm_hw_params_set_format (handle, hw_params, req->fmt);
        !           302:     if (err < 0) {
        !           303:         alsa_logerr2 (err, typ, "Failed to set format %d\n", req->fmt);
        !           304:         goto err;
        !           305:     }
        !           306: 
        !           307:     err = snd_pcm_hw_params_set_rate_near (handle, hw_params, &freq, 0);
        !           308:     if (err < 0) {
        !           309:         alsa_logerr2 (err, typ, "Failed to set frequency %d\n", req->freq);
        !           310:         goto err;
        !           311:     }
        !           312: 
        !           313:     err = snd_pcm_hw_params_set_channels_near (
        !           314:         handle,
        !           315:         hw_params,
        !           316:         &nchannels
        !           317:         );
        !           318:     if (err < 0) {
        !           319:         alsa_logerr2 (err, typ, "Failed to set number of channels %d\n",
        !           320:                       req->nchannels);
        !           321:         goto err;
        !           322:     }
        !           323: 
        !           324:     if (nchannels != 1 && nchannels != 2) {
        !           325:         alsa_logerr2 (err, typ,
        !           326:                       "Can not handle obtained number of channels %d\n",
        !           327:                       nchannels);
        !           328:         goto err;
        !           329:     }
        !           330: 
        !           331:     if (!((in && conf.size_in_usec_in) || (!in && conf.size_in_usec_out))) {
        !           332:         if (!buffer_size) {
        !           333:             buffer_size = DEFAULT_BUFFER_SIZE;
        !           334:             period_size= DEFAULT_PERIOD_SIZE;
        !           335:         }
        !           336:     }
        !           337: 
        !           338:     if (buffer_size) {
        !           339:         if ((in && conf.size_in_usec_in) || (!in && conf.size_in_usec_out)) {
        !           340:             if (period_size) {
        !           341:                 err = snd_pcm_hw_params_set_period_time_near (
        !           342:                     handle,
        !           343:                     hw_params,
        !           344:                     &period_size,
        !           345:                     0
        !           346:                     );
        !           347:                 if (err < 0) {
        !           348:                     alsa_logerr2 (err, typ,
        !           349:                                   "Failed to set period time %d\n",
        !           350:                                   req->period_size);
        !           351:                     goto err;
        !           352:                 }
        !           353:             }
        !           354: 
        !           355:             err = snd_pcm_hw_params_set_buffer_time_near (
        !           356:                 handle,
        !           357:                 hw_params,
        !           358:                 &buffer_size,
        !           359:                 0
        !           360:                 );
        !           361: 
        !           362:             if (err < 0) {
        !           363:                 alsa_logerr2 (err, typ,
        !           364:                               "Failed to set buffer time %d\n",
        !           365:                               req->buffer_size);
        !           366:                 goto err;
        !           367:             }
        !           368:         }
        !           369:         else {
        !           370:             int dir;
        !           371:             snd_pcm_uframes_t minval;
        !           372: 
        !           373:             if (period_size) {
        !           374:                 minval = period_size;
        !           375:                 dir = 0;
        !           376: 
        !           377:                 err = snd_pcm_hw_params_get_period_size_min (
        !           378:                     hw_params,
        !           379:                     &minval,
        !           380:                     &dir
        !           381:                     );
        !           382:                 if (err < 0) {
        !           383:                     alsa_logerr (
        !           384:                         err,
        !           385:                         "Could not get minmal period size for %s\n",
        !           386:                         typ
        !           387:                         );
        !           388:                 }
        !           389:                 else {
        !           390:                     if (period_size < minval) {
        !           391:                         if ((in && conf.period_size_in_overriden)
        !           392:                             || (!in && conf.period_size_out_overriden)) {
        !           393:                             dolog ("%s period size(%d) is less "
        !           394:                                    "than minmal period size(%ld)\n",
        !           395:                                    typ,
        !           396:                                    period_size,
        !           397:                                    minval);
        !           398:                         }
        !           399:                         period_size = minval;
        !           400:                     }
        !           401:                 }
        !           402: 
        !           403:                 err = snd_pcm_hw_params_set_period_size (
        !           404:                     handle,
        !           405:                     hw_params,
        !           406:                     period_size,
        !           407:                     0
        !           408:                     );
        !           409:                 if (err < 0) {
        !           410:                     alsa_logerr2 (err, typ, "Failed to set period size %d\n",
        !           411:                                   req->period_size);
        !           412:                     goto err;
        !           413:                 }
        !           414:             }
        !           415: 
        !           416:             minval = buffer_size;
        !           417:             err = snd_pcm_hw_params_get_buffer_size_min (
        !           418:                 hw_params,
        !           419:                 &minval
        !           420:                 );
        !           421:             if (err < 0) {
        !           422:                 alsa_logerr (err, "Could not get minmal buffer size for %s\n",
        !           423:                              typ);
        !           424:             }
        !           425:             else {
        !           426:                 if (buffer_size < minval) {
        !           427:                     if ((in && conf.buffer_size_in_overriden)
        !           428:                         || (!in && conf.buffer_size_out_overriden)) {
        !           429:                         dolog (
        !           430:                             "%s buffer size(%d) is less "
        !           431:                             "than minimal buffer size(%ld)\n",
        !           432:                             typ,
        !           433:                             buffer_size,
        !           434:                             minval
        !           435:                             );
        !           436:                     }
        !           437:                     buffer_size = minval;
        !           438:                 }
        !           439:             }
        !           440: 
        !           441:             err = snd_pcm_hw_params_set_buffer_size (
        !           442:                 handle,
        !           443:                 hw_params,
        !           444:                 buffer_size
        !           445:                 );
        !           446:             if (err < 0) {
        !           447:                 alsa_logerr2 (err, typ, "Failed to set buffer size %d\n",
        !           448:                               req->buffer_size);
        !           449:                 goto err;
        !           450:             }
        !           451:         }
        !           452:     }
        !           453:     else {
        !           454:         dolog ("warning: Buffer size is not set\n");
        !           455:     }
        !           456: 
        !           457:     err = snd_pcm_hw_params (handle, hw_params);
        !           458:     if (err < 0) {
        !           459:         alsa_logerr2 (err, typ, "Failed to apply audio parameters\n");
        !           460:         goto err;
        !           461:     }
        !           462: 
        !           463:     err = snd_pcm_hw_params_get_buffer_size (hw_params, &obt_buffer_size);
        !           464:     if (err < 0) {
        !           465:         alsa_logerr2 (err, typ, "Failed to get buffer size\n");
        !           466:         goto err;
        !           467:     }
        !           468: 
        !           469:     err = snd_pcm_prepare (handle);
        !           470:     if (err < 0) {
        !           471:         alsa_logerr2 (err, typ, "Could not prepare handle %p\n", handle);
        !           472:         goto err;
        !           473:     }
        !           474: 
        !           475:     if (!in && conf.threshold) {
        !           476:         snd_pcm_uframes_t threshold;
        !           477:         int bytes_per_sec;
        !           478: 
        !           479:         bytes_per_sec = freq
        !           480:             << (nchannels == 2)
        !           481:             << (req->fmt == AUD_FMT_S16 || req->fmt == AUD_FMT_U16);
        !           482: 
        !           483:         threshold = (conf.threshold * bytes_per_sec) / 1000;
        !           484:         alsa_set_threshold (handle, threshold);
        !           485:     }
        !           486: 
        !           487:     obt->fmt = req->fmt;
        !           488:     obt->nchannels = nchannels;
        !           489:     obt->freq = freq;
        !           490:     obt->samples = obt_buffer_size;
        !           491:     *handlep = handle;
        !           492: 
        !           493: #if defined DEBUG_MISMATCHES || defined DEBUG
        !           494:     if (obt->fmt != req->fmt ||
        !           495:         obt->nchannels != req->nchannels ||
        !           496:         obt->freq != req->freq) {
        !           497:         dolog ("Audio paramters mismatch for %s\n", typ);
        !           498:         alsa_dump_info (req, obt);
        !           499:     }
        !           500: #endif
        !           501: 
        !           502: #ifdef DEBUG
        !           503:     alsa_dump_info (req, obt);
        !           504: #endif
        !           505:     return 0;
        !           506: 
        !           507:  err:
        !           508:     alsa_anal_close (&handle);
        !           509:     return -1;
        !           510: }
        !           511: 
        !           512: static int alsa_recover (snd_pcm_t *handle)
        !           513: {
        !           514:     int err = snd_pcm_prepare (handle);
        !           515:     if (err < 0) {
        !           516:         alsa_logerr (err, "Failed to prepare handle %p\n", handle);
        !           517:         return -1;
        !           518:     }
        !           519:     return 0;
        !           520: }
        !           521: 
        !           522: static snd_pcm_sframes_t alsa_get_avail (snd_pcm_t *handle)
        !           523: {
        !           524:     snd_pcm_sframes_t avail;
        !           525: 
        !           526:     avail = snd_pcm_avail_update (handle);
        !           527:     if (avail < 0) {
        !           528:         if (avail == -EPIPE) {
        !           529:             if (!alsa_recover (handle)) {
        !           530:                 avail = snd_pcm_avail_update (handle);
        !           531:             }
        !           532:         }
        !           533: 
        !           534:         if (avail < 0) {
        !           535:             alsa_logerr (avail,
        !           536:                          "Could not obtain number of available frames\n");
        !           537:             return -1;
        !           538:         }
        !           539:     }
        !           540: 
        !           541:     return avail;
        !           542: }
        !           543: 
        !           544: static int alsa_run_out (HWVoiceOut *hw)
        !           545: {
        !           546:     ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
        !           547:     int rpos, live, decr;
        !           548:     int samples;
        !           549:     uint8_t *dst;
        !           550:     st_sample_t *src;
        !           551:     snd_pcm_sframes_t avail;
        !           552: 
        !           553:     live = audio_pcm_hw_get_live_out (hw);
        !           554:     if (!live) {
        !           555:         return 0;
        !           556:     }
        !           557: 
        !           558:     avail = alsa_get_avail (alsa->handle);
        !           559:     if (avail < 0) {
        !           560:         dolog ("Could not get number of available playback frames\n");
        !           561:         return 0;
        !           562:     }
        !           563: 
        !           564:     decr = audio_MIN (live, avail);
        !           565:     samples = decr;
        !           566:     rpos = hw->rpos;
        !           567:     while (samples) {
        !           568:         int left_till_end_samples = hw->samples - rpos;
        !           569:         int len = audio_MIN (samples, left_till_end_samples);
        !           570:         snd_pcm_sframes_t written;
        !           571: 
        !           572:         src = hw->mix_buf + rpos;
        !           573:         dst = advance (alsa->pcm_buf, rpos << hw->info.shift);
        !           574: 
        !           575:         hw->clip (dst, src, len);
        !           576: 
        !           577:         while (len) {
        !           578:             written = snd_pcm_writei (alsa->handle, dst, len);
        !           579: 
        !           580:             if (written <= 0) {
        !           581:                 switch (written) {
        !           582:                 case 0:
        !           583:                     if (conf.verbose) {
        !           584:                         dolog ("Failed to write %d frames (wrote zero)\n", len);
        !           585:                     }
        !           586:                     goto exit;
        !           587: 
        !           588:                 case -EPIPE:
        !           589:                     if (alsa_recover (alsa->handle)) {
        !           590:                         alsa_logerr (written, "Failed to write %d frames\n",
        !           591:                                      len);
        !           592:                         goto exit;
        !           593:                     }
        !           594:                     if (conf.verbose) {
        !           595:                         dolog ("Recovering from playback xrun\n");
        !           596:                     }
        !           597:                     continue;
        !           598: 
        !           599:                 case -EAGAIN:
        !           600:                     goto exit;
        !           601: 
        !           602:                 default:
        !           603:                     alsa_logerr (written, "Failed to write %d frames to %p\n",
        !           604:                                  len, dst);
        !           605:                     goto exit;
        !           606:                 }
        !           607:             }
        !           608: 
        !           609:             mixeng_clear (src, written);
        !           610:             rpos = (rpos + written) % hw->samples;
        !           611:             samples -= written;
        !           612:             len -= written;
        !           613:             dst = advance (dst, written << hw->info.shift);
        !           614:             src += written;
        !           615:         }
        !           616:     }
        !           617: 
        !           618:  exit:
        !           619:     hw->rpos = rpos;
        !           620:     return decr;
        !           621: }
        !           622: 
        !           623: static void alsa_fini_out (HWVoiceOut *hw)
        !           624: {
        !           625:     ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
        !           626: 
        !           627:     ldebug ("alsa_fini\n");
        !           628:     alsa_anal_close (&alsa->handle);
        !           629: 
        !           630:     if (alsa->pcm_buf) {
        !           631:         qemu_free (alsa->pcm_buf);
        !           632:         alsa->pcm_buf = NULL;
        !           633:     }
        !           634: }
        !           635: 
        !           636: static int alsa_init_out (HWVoiceOut *hw, audsettings_t *as)
        !           637: {
        !           638:     ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
        !           639:     struct alsa_params_req req;
        !           640:     struct alsa_params_obt obt;
        !           641:     audfmt_e effective_fmt;
        !           642:     int endianness;
        !           643:     int err;
        !           644:     snd_pcm_t *handle;
        !           645:     audsettings_t obt_as;
        !           646: 
        !           647:     req.fmt = aud_to_alsafmt (as->fmt);
        !           648:     req.freq = as->freq;
        !           649:     req.nchannels = as->nchannels;
        !           650:     req.period_size = conf.period_size_out;
        !           651:     req.buffer_size = conf.buffer_size_out;
        !           652: 
        !           653:     if (alsa_open (0, &req, &obt, &handle)) {
        !           654:         return -1;
        !           655:     }
        !           656: 
        !           657:     err = alsa_to_audfmt (obt.fmt, &effective_fmt, &endianness);
        !           658:     if (err) {
        !           659:         alsa_anal_close (&handle);
        !           660:         return -1;
        !           661:     }
        !           662: 
        !           663:     obt_as.freq = obt.freq;
        !           664:     obt_as.nchannels = obt.nchannels;
        !           665:     obt_as.fmt = effective_fmt;
        !           666: 
        !           667:     audio_pcm_init_info (
        !           668:         &hw->info,
        !           669:         &obt_as,
        !           670:         audio_need_to_swap_endian (endianness)
        !           671:         );
        !           672:     hw->samples = obt.samples;
        !           673: 
        !           674:     alsa->pcm_buf = audio_calloc (AUDIO_FUNC, obt.samples, 1 << hw->info.shift);
        !           675:     if (!alsa->pcm_buf) {
        !           676:         dolog ("Could not allocate DAC buffer (%d samples, each %d bytes)\n",
        !           677:                hw->samples, 1 << hw->info.shift);
        !           678:         alsa_anal_close (&handle);
        !           679:         return -1;
        !           680:     }
        !           681: 
        !           682:     alsa->handle = handle;
        !           683:     return 0;
        !           684: }
        !           685: 
        !           686: static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int pause)
        !           687: {
        !           688:     int err;
        !           689: 
        !           690:     if (pause) {
        !           691:         err = snd_pcm_drop (handle);
        !           692:         if (err < 0) {
        !           693:             alsa_logerr (err, "Could not stop %s\n", typ);
        !           694:             return -1;
        !           695:         }
        !           696:     }
        !           697:     else {
        !           698:         err = snd_pcm_prepare (handle);
        !           699:         if (err < 0) {
        !           700:             alsa_logerr (err, "Could not prepare handle for %s\n", typ);
        !           701:             return -1;
        !           702:         }
        !           703:     }
        !           704: 
        !           705:     return 0;
        !           706: }
        !           707: 
        !           708: static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...)
        !           709: {
        !           710:     ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
        !           711: 
        !           712:     switch (cmd) {
        !           713:     case VOICE_ENABLE:
        !           714:         ldebug ("enabling voice\n");
        !           715:         return alsa_voice_ctl (alsa->handle, "playback", 0);
        !           716: 
        !           717:     case VOICE_DISABLE:
        !           718:         ldebug ("disabling voice\n");
        !           719:         return alsa_voice_ctl (alsa->handle, "playback", 1);
        !           720:     }
        !           721: 
        !           722:     return -1;
        !           723: }
        !           724: 
        !           725: static int alsa_init_in (HWVoiceIn *hw, audsettings_t *as)
        !           726: {
        !           727:     ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
        !           728:     struct alsa_params_req req;
        !           729:     struct alsa_params_obt obt;
        !           730:     int endianness;
        !           731:     int err;
        !           732:     audfmt_e effective_fmt;
        !           733:     snd_pcm_t *handle;
        !           734:     audsettings_t obt_as;
        !           735: 
        !           736:     req.fmt = aud_to_alsafmt (as->fmt);
        !           737:     req.freq = as->freq;
        !           738:     req.nchannels = as->nchannels;
        !           739:     req.period_size = conf.period_size_in;
        !           740:     req.buffer_size = conf.buffer_size_in;
        !           741: 
        !           742:     if (alsa_open (1, &req, &obt, &handle)) {
        !           743:         return -1;
        !           744:     }
        !           745: 
        !           746:     err = alsa_to_audfmt (obt.fmt, &effective_fmt, &endianness);
        !           747:     if (err) {
        !           748:         alsa_anal_close (&handle);
        !           749:         return -1;
        !           750:     }
        !           751: 
        !           752:     obt_as.freq = obt.freq;
        !           753:     obt_as.nchannels = obt.nchannels;
        !           754:     obt_as.fmt = effective_fmt;
        !           755: 
        !           756:     audio_pcm_init_info (
        !           757:         &hw->info,
        !           758:         &obt_as,
        !           759:         audio_need_to_swap_endian (endianness)
        !           760:         );
        !           761:     hw->samples = obt.samples;
        !           762: 
        !           763:     alsa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
        !           764:     if (!alsa->pcm_buf) {
        !           765:         dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n",
        !           766:                hw->samples, 1 << hw->info.shift);
        !           767:         alsa_anal_close (&handle);
        !           768:         return -1;
        !           769:     }
        !           770: 
        !           771:     alsa->handle = handle;
        !           772:     return 0;
        !           773: }
        !           774: 
        !           775: static void alsa_fini_in (HWVoiceIn *hw)
        !           776: {
        !           777:     ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
        !           778: 
        !           779:     alsa_anal_close (&alsa->handle);
        !           780: 
        !           781:     if (alsa->pcm_buf) {
        !           782:         qemu_free (alsa->pcm_buf);
        !           783:         alsa->pcm_buf = NULL;
        !           784:     }
        !           785: }
        !           786: 
        !           787: static int alsa_run_in (HWVoiceIn *hw)
        !           788: {
        !           789:     ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
        !           790:     int hwshift = hw->info.shift;
        !           791:     int i;
        !           792:     int live = audio_pcm_hw_get_live_in (hw);
        !           793:     int dead = hw->samples - live;
        !           794:     int decr;
        !           795:     struct {
        !           796:         int add;
        !           797:         int len;
        !           798:     } bufs[2] = {
        !           799:         { hw->wpos, 0 },
        !           800:         { 0, 0 }
        !           801:     };
        !           802:     snd_pcm_sframes_t avail;
        !           803:     snd_pcm_uframes_t read_samples = 0;
        !           804: 
        !           805:     if (!dead) {
        !           806:         return 0;
        !           807:     }
        !           808: 
        !           809:     avail = alsa_get_avail (alsa->handle);
        !           810:     if (avail < 0) {
        !           811:         dolog ("Could not get number of captured frames\n");
        !           812:         return 0;
        !           813:     }
        !           814: 
        !           815:     if (!avail && (snd_pcm_state (alsa->handle) == SND_PCM_STATE_PREPARED)) {
        !           816:         avail = hw->samples;
        !           817:     }
        !           818: 
        !           819:     decr = audio_MIN (dead, avail);
        !           820:     if (!decr) {
        !           821:         return 0;
        !           822:     }
        !           823: 
        !           824:     if (hw->wpos + decr > hw->samples) {
        !           825:         bufs[0].len = (hw->samples - hw->wpos);
        !           826:         bufs[1].len = (decr - (hw->samples - hw->wpos));
        !           827:     }
        !           828:     else {
        !           829:         bufs[0].len = decr;
        !           830:     }
        !           831: 
        !           832:     for (i = 0; i < 2; ++i) {
        !           833:         void *src;
        !           834:         st_sample_t *dst;
        !           835:         snd_pcm_sframes_t nread;
        !           836:         snd_pcm_uframes_t len;
        !           837: 
        !           838:         len = bufs[i].len;
        !           839: 
        !           840:         src = advance (alsa->pcm_buf, bufs[i].add << hwshift);
        !           841:         dst = hw->conv_buf + bufs[i].add;
        !           842: 
        !           843:         while (len) {
        !           844:             nread = snd_pcm_readi (alsa->handle, src, len);
        !           845: 
        !           846:             if (nread <= 0) {
        !           847:                 switch (nread) {
        !           848:                 case 0:
        !           849:                     if (conf.verbose) {
        !           850:                         dolog ("Failed to read %ld frames (read zero)\n", len);
        !           851:                     }
        !           852:                     goto exit;
        !           853: 
        !           854:                 case -EPIPE:
        !           855:                     if (alsa_recover (alsa->handle)) {
        !           856:                         alsa_logerr (nread, "Failed to read %ld frames\n", len);
        !           857:                         goto exit;
        !           858:                     }
        !           859:                     if (conf.verbose) {
        !           860:                         dolog ("Recovering from capture xrun\n");
        !           861:                     }
        !           862:                     continue;
        !           863: 
        !           864:                 case -EAGAIN:
        !           865:                     goto exit;
        !           866: 
        !           867:                 default:
        !           868:                     alsa_logerr (
        !           869:                         nread,
        !           870:                         "Failed to read %ld frames from %p\n",
        !           871:                         len,
        !           872:                         src
        !           873:                         );
        !           874:                     goto exit;
        !           875:                 }
        !           876:             }
        !           877: 
        !           878:             hw->conv (dst, src, nread, &nominal_volume);
        !           879: 
        !           880:             src = advance (src, nread << hwshift);
        !           881:             dst += nread;
        !           882: 
        !           883:             read_samples += nread;
        !           884:             len -= nread;
        !           885:         }
        !           886:     }
        !           887: 
        !           888:  exit:
        !           889:     hw->wpos = (hw->wpos + read_samples) % hw->samples;
        !           890:     return read_samples;
        !           891: }
        !           892: 
        !           893: static int alsa_read (SWVoiceIn *sw, void *buf, int size)
        !           894: {
        !           895:     return audio_pcm_sw_read (sw, buf, size);
        !           896: }
        !           897: 
        !           898: static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
        !           899: {
        !           900:     ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
        !           901: 
        !           902:     switch (cmd) {
        !           903:     case VOICE_ENABLE:
        !           904:         ldebug ("enabling voice\n");
        !           905:         return alsa_voice_ctl (alsa->handle, "capture", 0);
        !           906: 
        !           907:     case VOICE_DISABLE:
        !           908:         ldebug ("disabling voice\n");
        !           909:         return alsa_voice_ctl (alsa->handle, "capture", 1);
        !           910:     }
        !           911: 
        !           912:     return -1;
        !           913: }
        !           914: 
        !           915: static void *alsa_audio_init (void)
        !           916: {
        !           917:     return &conf;
        !           918: }
        !           919: 
        !           920: static void alsa_audio_fini (void *opaque)
        !           921: {
        !           922:     (void) opaque;
        !           923: }
        !           924: 
        !           925: static struct audio_option alsa_options[] = {
        !           926:     {"DAC_SIZE_IN_USEC", AUD_OPT_BOOL, &conf.size_in_usec_out,
        !           927:      "DAC period/buffer size in microseconds (otherwise in frames)", NULL, 0},
        !           928:     {"DAC_PERIOD_SIZE", AUD_OPT_INT, &conf.period_size_out,
        !           929:      "DAC period size", &conf.period_size_out_overriden, 0},
        !           930:     {"DAC_BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_size_out,
        !           931:      "DAC buffer size", &conf.buffer_size_out_overriden, 0},
        !           932: 
        !           933:     {"ADC_SIZE_IN_USEC", AUD_OPT_BOOL, &conf.size_in_usec_in,
        !           934:      "ADC period/buffer size in microseconds (otherwise in frames)", NULL, 0},
        !           935:     {"ADC_PERIOD_SIZE", AUD_OPT_INT, &conf.period_size_in,
        !           936:      "ADC period size", &conf.period_size_in_overriden, 0},
        !           937:     {"ADC_BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_size_in,
        !           938:      "ADC buffer size", &conf.buffer_size_in_overriden, 0},
        !           939: 
        !           940:     {"THRESHOLD", AUD_OPT_INT, &conf.threshold,
        !           941:      "(undocumented)", NULL, 0},
        !           942: 
        !           943:     {"DAC_DEV", AUD_OPT_STR, &conf.pcm_name_out,
        !           944:      "DAC device name (for instance dmix)", NULL, 0},
        !           945: 
        !           946:     {"ADC_DEV", AUD_OPT_STR, &conf.pcm_name_in,
        !           947:      "ADC device name", NULL, 0},
        !           948: 
        !           949:     {"VERBOSE", AUD_OPT_BOOL, &conf.verbose,
        !           950:      "Behave in a more verbose way", NULL, 0},
        !           951: 
        !           952:     {NULL, 0, NULL, NULL, NULL, 0}
        !           953: };
        !           954: 
        !           955: static struct audio_pcm_ops alsa_pcm_ops = {
        !           956:     alsa_init_out,
        !           957:     alsa_fini_out,
        !           958:     alsa_run_out,
        !           959:     alsa_write,
        !           960:     alsa_ctl_out,
        !           961: 
        !           962:     alsa_init_in,
        !           963:     alsa_fini_in,
        !           964:     alsa_run_in,
        !           965:     alsa_read,
        !           966:     alsa_ctl_in
        !           967: };
        !           968: 
        !           969: struct audio_driver alsa_audio_driver = {
        !           970:     INIT_FIELD (name           = ) "alsa",
        !           971:     INIT_FIELD (descr          = ) "ALSA http://www.alsa-project.org",
        !           972:     INIT_FIELD (options        = ) alsa_options,
        !           973:     INIT_FIELD (init           = ) alsa_audio_init,
        !           974:     INIT_FIELD (fini           = ) alsa_audio_fini,
        !           975:     INIT_FIELD (pcm_ops        = ) &alsa_pcm_ops,
        !           976:     INIT_FIELD (can_be_default = ) 1,
        !           977:     INIT_FIELD (max_voices_out = ) INT_MAX,
        !           978:     INIT_FIELD (max_voices_in  = ) INT_MAX,
        !           979:     INIT_FIELD (voice_size_out = ) sizeof (ALSAVoiceOut),
        !           980:     INIT_FIELD (voice_size_in  = ) sizeof (ALSAVoiceIn)
        !           981: };

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.