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

1.1     ! root        1: /*
        !             2:  * QEMU OSS audio output driver
        !             3:  * 
        !             4:  * Copyright (c) 2003-2004 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 <sys/mman.h>
        !            25: #include <sys/types.h>
        !            26: #include <sys/ioctl.h>
        !            27: #include <sys/soundcard.h>
        !            28: #include <assert.h>
        !            29: #include "vl.h"
        !            30: 
        !            31: #include "audio/audio_int.h"
        !            32: 
        !            33: typedef struct OSSVoice {
        !            34:     HWVoice hw;
        !            35:     void *pcm_buf;
        !            36:     int fd;
        !            37:     int nfrags;
        !            38:     int fragsize;
        !            39:     int mmapped;
        !            40:     int old_optr;
        !            41: } OSSVoice;
        !            42: 
        !            43: #define dolog(...) AUD_log ("oss", __VA_ARGS__)
        !            44: #ifdef DEBUG
        !            45: #define ldebug(...) dolog (__VA_ARGS__)
        !            46: #else
        !            47: #define ldebug(...)
        !            48: #endif
        !            49: 
        !            50: #define QC_OSS_FRAGSIZE "QEMU_OSS_FRAGSIZE"
        !            51: #define QC_OSS_NFRAGS   "QEMU_OSS_NFRAGS"
        !            52: #define QC_OSS_MMAP     "QEMU_OSS_MMAP"
        !            53: #define QC_OSS_DEV      "QEMU_OSS_DEV"
        !            54: 
        !            55: #define errstr() strerror (errno)
        !            56: 
        !            57: static struct {
        !            58:     int try_mmap;
        !            59:     int nfrags;
        !            60:     int fragsize;
        !            61:     const char *dspname;
        !            62: } conf = {
        !            63:     .try_mmap = 0,
        !            64:     .nfrags = 4,
        !            65:     .fragsize = 4096,
        !            66:     .dspname = "/dev/dsp"
        !            67: };
        !            68: 
        !            69: struct oss_params {
        !            70:     int freq;
        !            71:     audfmt_e fmt;
        !            72:     int nchannels;
        !            73:     int nfrags;
        !            74:     int fragsize;
        !            75: };
        !            76: 
        !            77: static int oss_hw_write (SWVoice *sw, void *buf, int len)
        !            78: {
        !            79:     return pcm_hw_write (sw, buf, len);
        !            80: }
        !            81: 
        !            82: static int AUD_to_ossfmt (audfmt_e fmt)
        !            83: {
        !            84:     switch (fmt) {
        !            85:     case AUD_FMT_S8: return AFMT_S8;
        !            86:     case AUD_FMT_U8: return AFMT_U8;
        !            87:     case AUD_FMT_S16: return AFMT_S16_LE;
        !            88:     case AUD_FMT_U16: return AFMT_U16_LE;
        !            89:     default:
        !            90:         dolog ("Internal logic error: Bad audio format %d\nAborting\n", fmt);
        !            91:         exit (EXIT_FAILURE);
        !            92:     }
        !            93: }
        !            94: 
        !            95: static int oss_to_audfmt (int fmt)
        !            96: {
        !            97:     switch (fmt) {
        !            98:     case AFMT_S8: return AUD_FMT_S8;
        !            99:     case AFMT_U8: return AUD_FMT_U8;
        !           100:     case AFMT_S16_LE: return AUD_FMT_S16;
        !           101:     case AFMT_U16_LE: return AUD_FMT_U16;
        !           102:     default:
        !           103:         dolog ("Internal logic error: Unrecognized OSS audio format %d\n"
        !           104:                "Aborting\n",
        !           105:                fmt);
        !           106:         exit (EXIT_FAILURE);
        !           107:     }
        !           108: }
        !           109: 
        !           110: #ifdef DEBUG_PCM
        !           111: static void oss_dump_pcm_info (struct oss_params *req, struct oss_params *obt)
        !           112: {
        !           113:     dolog ("parameter | requested value | obtained value\n");
        !           114:     dolog ("format    |      %10d |     %10d\n", req->fmt, obt->fmt);
        !           115:     dolog ("channels  |      %10d |     %10d\n", req->nchannels, obt->nchannels);
        !           116:     dolog ("frequency |      %10d |     %10d\n", req->freq, obt->freq);
        !           117:     dolog ("nfrags    |      %10d |     %10d\n", req->nfrags, obt->nfrags);
        !           118:     dolog ("fragsize  |      %10d |     %10d\n", req->fragsize, obt->fragsize);
        !           119: }
        !           120: #endif
        !           121: 
        !           122: static int oss_open (struct oss_params *req, struct oss_params *obt, int *pfd)
        !           123: {
        !           124:     int fd;
        !           125:     int mmmmssss;
        !           126:     audio_buf_info abinfo;
        !           127:     int fmt, freq, nchannels;
        !           128:     const char *dspname = conf.dspname;
        !           129: 
        !           130:     fd = open (dspname, O_WRONLY | O_NONBLOCK);
        !           131:     if (-1 == fd) {
        !           132:         dolog ("Could not initialize audio hardware. Failed to open `%s':\n"
        !           133:                "Reason:%s\n",
        !           134:                dspname,
        !           135:                errstr ());
        !           136:         return -1;
        !           137:     }
        !           138: 
        !           139:     freq = req->freq;
        !           140:     nchannels = req->nchannels;
        !           141:     fmt = req->fmt;
        !           142: 
        !           143:     if (ioctl (fd, SNDCTL_DSP_SAMPLESIZE, &fmt)) {
        !           144:         dolog ("Could not initialize audio hardware\n"
        !           145:                "Failed to set sample size\n"
        !           146:                "Reason: %s\n",
        !           147:                errstr ());
        !           148:         goto err;
        !           149:     }
        !           150: 
        !           151:     if (ioctl (fd, SNDCTL_DSP_CHANNELS, &nchannels)) {
        !           152:         dolog ("Could not initialize audio hardware\n"
        !           153:                "Failed to set number of channels\n"
        !           154:                "Reason: %s\n",
        !           155:                errstr ());
        !           156:         goto err;
        !           157:     }
        !           158: 
        !           159:     if (ioctl (fd, SNDCTL_DSP_SPEED, &freq)) {
        !           160:         dolog ("Could not initialize audio hardware\n"
        !           161:                "Failed to set frequency\n"
        !           162:                "Reason: %s\n",
        !           163:                errstr ());
        !           164:         goto err;
        !           165:     }
        !           166: 
        !           167:     if (ioctl (fd, SNDCTL_DSP_NONBLOCK)) {
        !           168:         dolog ("Could not initialize audio hardware\n"
        !           169:                "Failed to set non-blocking mode\n"
        !           170:                "Reason: %s\n",
        !           171:                errstr ());
        !           172:         goto err;
        !           173:     }
        !           174: 
        !           175:     mmmmssss = (req->nfrags << 16) | lsbindex (req->fragsize);
        !           176:     if (ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &mmmmssss)) {
        !           177:         dolog ("Could not initialize audio hardware\n"
        !           178:                "Failed to set buffer length (%d, %d)\n"
        !           179:                "Reason:%s\n",
        !           180:                conf.nfrags, conf.fragsize,
        !           181:                errstr ());
        !           182:         goto err;
        !           183:     }
        !           184: 
        !           185:     if (ioctl (fd, SNDCTL_DSP_GETOSPACE, &abinfo)) {
        !           186:         dolog ("Could not initialize audio hardware\n"
        !           187:                "Failed to get buffer length\n"
        !           188:                "Reason:%s\n",
        !           189:                errstr ());
        !           190:         goto err;
        !           191:     }
        !           192: 
        !           193:     obt->fmt = fmt;
        !           194:     obt->nchannels = nchannels;
        !           195:     obt->freq = freq;
        !           196:     obt->nfrags = abinfo.fragstotal;
        !           197:     obt->fragsize = abinfo.fragsize;
        !           198:     *pfd = fd;
        !           199: 
        !           200:     if ((req->fmt != obt->fmt) ||
        !           201:         (req->nchannels != obt->nchannels) ||
        !           202:         (req->freq != obt->freq) ||
        !           203:         (req->fragsize != obt->fragsize) ||
        !           204:         (req->nfrags != obt->nfrags)) {
        !           205: #ifdef DEBUG_PCM
        !           206:         dolog ("Audio parameters mismatch\n");
        !           207:         oss_dump_pcm_info (req, obt);
        !           208: #endif
        !           209:     }
        !           210: 
        !           211: #ifdef DEBUG_PCM
        !           212:     oss_dump_pcm_info (req, obt);
        !           213: #endif
        !           214:     return 0;
        !           215: 
        !           216: err:
        !           217:     close (fd);
        !           218:     return -1;
        !           219: }
        !           220: 
        !           221: static void oss_hw_run (HWVoice *hw)
        !           222: {
        !           223:     OSSVoice *oss = (OSSVoice *) hw;
        !           224:     int err, rpos, live, decr;
        !           225:     int samples;
        !           226:     uint8_t *dst;
        !           227:     st_sample_t *src;
        !           228:     struct audio_buf_info abinfo;
        !           229:     struct count_info cntinfo;
        !           230: 
        !           231:     live = pcm_hw_get_live (hw);
        !           232:     if (live <= 0)
        !           233:         return;
        !           234: 
        !           235:     if (oss->mmapped) {
        !           236:         int bytes;
        !           237: 
        !           238:         err = ioctl (oss->fd, SNDCTL_DSP_GETOPTR, &cntinfo);
        !           239:         if (err < 0) {
        !           240:             dolog ("SNDCTL_DSP_GETOPTR failed\nReason: %s\n", errstr ());
        !           241:             return;
        !           242:         }
        !           243: 
        !           244:         if (cntinfo.ptr == oss->old_optr) {
        !           245:             if (abs (hw->samples - live) < 64)
        !           246:                 dolog ("overrun\n");
        !           247:             return;
        !           248:         }
        !           249: 
        !           250:         if (cntinfo.ptr > oss->old_optr) {
        !           251:             bytes = cntinfo.ptr - oss->old_optr;
        !           252:         }
        !           253:         else {
        !           254:             bytes = hw->bufsize + cntinfo.ptr - oss->old_optr;
        !           255:         }
        !           256: 
        !           257:         decr = audio_MIN (bytes >> hw->shift, live);
        !           258:     }
        !           259:     else {
        !           260:         err = ioctl (oss->fd, SNDCTL_DSP_GETOSPACE, &abinfo);
        !           261:         if (err < 0) {
        !           262:             dolog ("SNDCTL_DSP_GETOSPACE failed\nReason: %s\n", errstr ());
        !           263:             return;
        !           264:         }
        !           265: 
        !           266:         decr = audio_MIN (abinfo.bytes >> hw->shift, live);
        !           267:         if (decr <= 0)
        !           268:             return;
        !           269:     }
        !           270: 
        !           271:     samples = decr;
        !           272:     rpos = hw->rpos;
        !           273:     while (samples) {
        !           274:         int left_till_end_samples = hw->samples - rpos;
        !           275:         int convert_samples = audio_MIN (samples, left_till_end_samples);
        !           276: 
        !           277:         src = advance (hw->mix_buf, rpos * sizeof (st_sample_t));
        !           278:         dst = advance (oss->pcm_buf, rpos << hw->shift);
        !           279: 
        !           280:         hw->clip (dst, src, convert_samples);
        !           281:         if (!oss->mmapped) {
        !           282:             int written;
        !           283: 
        !           284:             written = write (oss->fd, dst, convert_samples << hw->shift);
        !           285:             /* XXX: follow errno recommendations ? */
        !           286:             if (written == -1) {
        !           287:                 dolog ("Failed to write audio\nReason: %s\n", errstr ());
        !           288:                 continue;
        !           289:             }
        !           290: 
        !           291:             if (written != convert_samples << hw->shift) {
        !           292:                 int wsamples = written >> hw->shift;
        !           293:                 int wbytes = wsamples << hw->shift;
        !           294:                 if (wbytes != written) {
        !           295:                     dolog ("Unaligned write %d, %d\n", wbytes, written);
        !           296:                 }
        !           297:                 memset (src, 0, wbytes);
        !           298:                 decr -= samples;
        !           299:                 rpos = (rpos + wsamples) % hw->samples;
        !           300:                 break;
        !           301:             }
        !           302:         }
        !           303:         memset (src, 0, convert_samples * sizeof (st_sample_t));
        !           304: 
        !           305:         rpos = (rpos + convert_samples) % hw->samples;
        !           306:         samples -= convert_samples;
        !           307:     }
        !           308:     if (oss->mmapped) {
        !           309:         oss->old_optr = cntinfo.ptr;
        !           310:     }
        !           311: 
        !           312:     pcm_hw_dec_live (hw, decr);
        !           313:     hw->rpos = rpos;
        !           314: }
        !           315: 
        !           316: static void oss_hw_fini (HWVoice *hw)
        !           317: {
        !           318:     int err;
        !           319:     OSSVoice *oss = (OSSVoice *) hw;
        !           320: 
        !           321:     ldebug ("oss_hw_fini\n");
        !           322:     err = close (oss->fd);
        !           323:     if (err) {
        !           324:         dolog ("Failed to close OSS descriptor\nReason: %s\n", errstr ());
        !           325:     }
        !           326:     oss->fd = -1;
        !           327: 
        !           328:     if (oss->pcm_buf) {
        !           329:         if (oss->mmapped) {
        !           330:             err = munmap (oss->pcm_buf, hw->bufsize);
        !           331:             if (err) {
        !           332:                 dolog ("Failed to unmap OSS buffer\nReason: %s\n",
        !           333:                        errstr ());
        !           334:             }
        !           335:         }
        !           336:         else {
        !           337:             qemu_free (oss->pcm_buf);
        !           338:         }
        !           339:         oss->pcm_buf = NULL;
        !           340:     }
        !           341: }
        !           342: 
        !           343: static int oss_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt)
        !           344: {
        !           345:     OSSVoice *oss = (OSSVoice *) hw;
        !           346:     struct oss_params req, obt;
        !           347: 
        !           348:     assert (!oss->fd);
        !           349:     req.fmt = AUD_to_ossfmt (fmt);
        !           350:     req.freq = freq;
        !           351:     req.nchannels = nchannels;
        !           352:     req.fragsize = conf.fragsize;
        !           353:     req.nfrags = conf.nfrags;
        !           354: 
        !           355:     if (oss_open (&req, &obt, &oss->fd))
        !           356:         return -1;
        !           357: 
        !           358:     hw->freq = obt.freq;
        !           359:     hw->fmt = oss_to_audfmt (obt.fmt);
        !           360:     hw->nchannels = obt.nchannels;
        !           361: 
        !           362:     oss->nfrags = obt.nfrags;
        !           363:     oss->fragsize = obt.fragsize;
        !           364:     hw->bufsize = obt.nfrags * obt.fragsize;
        !           365: 
        !           366:     oss->mmapped = 0;
        !           367:     if (conf.try_mmap) {
        !           368:         oss->pcm_buf = mmap (0, hw->bufsize, PROT_READ | PROT_WRITE,
        !           369:                              MAP_SHARED, oss->fd, 0);
        !           370:         if (oss->pcm_buf == MAP_FAILED) {
        !           371:             dolog ("Failed to mmap OSS device\nReason: %s\n",
        !           372:                    errstr ());
        !           373:         } else {
        !           374:             int err;
        !           375:             int trig = 0;
        !           376:             if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
        !           377:                 dolog ("SNDCTL_DSP_SETTRIGGER 0 failed\nReason: %s\n",
        !           378:                        errstr ());
        !           379:             }
        !           380:             else {
        !           381:                 trig = PCM_ENABLE_OUTPUT;
        !           382:                 if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
        !           383:                     dolog ("SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
        !           384:                            "Reason: %s\n", errstr ());
        !           385:                 }
        !           386:                 else {
        !           387:                     oss->mmapped = 1;
        !           388:                 }
        !           389:             }
        !           390: 
        !           391:             if (!oss->mmapped) {
        !           392:                 err = munmap (oss->pcm_buf, hw->bufsize);
        !           393:                 if (err) {
        !           394:                     dolog ("Failed to unmap OSS device\nReason: %s\n",
        !           395:                            errstr ());
        !           396:                 }
        !           397:             }
        !           398:         }
        !           399:     }
        !           400: 
        !           401:     if (!oss->mmapped) {
        !           402:         oss->pcm_buf = qemu_mallocz (hw->bufsize);
        !           403:         if (!oss->pcm_buf) {
        !           404:             close (oss->fd);
        !           405:             oss->fd = -1;
        !           406:             return -1;
        !           407:         }
        !           408:     }
        !           409: 
        !           410:     return 0;
        !           411: }
        !           412: 
        !           413: static int oss_hw_ctl (HWVoice *hw, int cmd, ...)
        !           414: {
        !           415:     int trig;
        !           416:     OSSVoice *oss = (OSSVoice *) hw;
        !           417: 
        !           418:     if (!oss->mmapped)
        !           419:         return 0;
        !           420: 
        !           421:     switch (cmd) {
        !           422:     case VOICE_ENABLE:
        !           423:         ldebug ("enabling voice\n");
        !           424:         pcm_hw_clear (hw, oss->pcm_buf, hw->samples);
        !           425:         trig = PCM_ENABLE_OUTPUT;
        !           426:         if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
        !           427:             dolog ("SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
        !           428:                    "Reason: %s\n", errstr ());
        !           429:             return -1;
        !           430:         }
        !           431:         break;
        !           432: 
        !           433:     case VOICE_DISABLE:
        !           434:         ldebug ("disabling voice\n");
        !           435:         trig = 0;
        !           436:         if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
        !           437:             dolog ("SNDCTL_DSP_SETTRIGGER 0 failed\nReason: %s\n",
        !           438:                    errstr ());
        !           439:             return -1;
        !           440:         }
        !           441:         break;
        !           442:     }
        !           443:     return 0;
        !           444: }
        !           445: 
        !           446: static void *oss_audio_init (void)
        !           447: {
        !           448:     conf.fragsize = audio_get_conf_int (QC_OSS_FRAGSIZE, conf.fragsize);
        !           449:     conf.nfrags = audio_get_conf_int (QC_OSS_NFRAGS, conf.nfrags);
        !           450:     conf.try_mmap = audio_get_conf_int (QC_OSS_MMAP, conf.try_mmap);
        !           451:     conf.dspname = audio_get_conf_str (QC_OSS_DEV, conf.dspname);
        !           452:     return &conf;
        !           453: }
        !           454: 
        !           455: static void oss_audio_fini (void *opaque)
        !           456: {
        !           457: }
        !           458: 
        !           459: struct pcm_ops oss_pcm_ops = {
        !           460:     oss_hw_init,
        !           461:     oss_hw_fini,
        !           462:     oss_hw_run,
        !           463:     oss_hw_write,
        !           464:     oss_hw_ctl
        !           465: };
        !           466: 
        !           467: struct audio_output_driver oss_output_driver = {
        !           468:     "oss",
        !           469:     oss_audio_init,
        !           470:     oss_audio_fini,
        !           471:     &oss_pcm_ops,
        !           472:     1,
        !           473:     INT_MAX,
        !           474:     sizeof (OSSVoice)
        !           475: };

unix.superglobalmegacorp.com

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