Annotation of qemu/audio/ossaudio.c, revision 1.1.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.