Statistics
| Branch: | Revision:

root / audio / ossaudio.c @ 85571bc7

History | View | Annotate | Download (12.5 kB)

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

    
25
/* Temporary kludge */
26
#if defined __linux__ || (defined _BSD && !defined __APPLE__)
27
#include <assert.h>
28
#include "vl.h"
29

    
30
#include <sys/mman.h>
31
#include <sys/types.h>
32
#include <sys/ioctl.h>
33
#include <sys/soundcard.h>
34

    
35
#define AUDIO_CAP "oss"
36
#include "audio/audio.h"
37
#include "audio/ossaudio.h"
38

    
39
#define QC_OSS_FRAGSIZE "QEMU_OSS_FRAGSIZE"
40
#define QC_OSS_NFRAGS   "QEMU_OSS_NFRAGS"
41
#define QC_OSS_MMAP     "QEMU_OSS_MMAP"
42
#define QC_OSS_DEV      "QEMU_OSS_DEV"
43

    
44
#define errstr() strerror (errno)
45

    
46
static struct {
47
    int try_mmap;
48
    int nfrags;
49
    int fragsize;
50
    const char *dspname;
51
} conf = {
52
    .try_mmap = 0,
53
    .nfrags = 4,
54
    .fragsize = 4096,
55
    .dspname = "/dev/dsp"
56
};
57

    
58
struct oss_params {
59
    int freq;
60
    audfmt_e fmt;
61
    int nchannels;
62
    int nfrags;
63
    int fragsize;
64
};
65

    
66
static int oss_hw_write (SWVoice *sw, void *buf, int len)
67
{
68
    return pcm_hw_write (sw, buf, len);
69
}
70

    
71
static int AUD_to_ossfmt (audfmt_e fmt)
72
{
73
    switch (fmt) {
74
    case AUD_FMT_S8: return AFMT_S8;
75
    case AUD_FMT_U8: return AFMT_U8;
76
    case AUD_FMT_S16: return AFMT_S16_LE;
77
    case AUD_FMT_U16: return AFMT_U16_LE;
78
    default:
79
        dolog ("Internal logic error: Bad audio format %d\nAborting\n", fmt);
80
        exit (EXIT_FAILURE);
81
    }
82
}
83

    
84
static int oss_to_audfmt (int fmt)
85
{
86
    switch (fmt) {
87
    case AFMT_S8: return AUD_FMT_S8;
88
    case AFMT_U8: return AUD_FMT_U8;
89
    case AFMT_S16_LE: return AUD_FMT_S16;
90
    case AFMT_U16_LE: return AUD_FMT_U16;
91
    default:
92
        dolog ("Internal logic error: Unrecognized OSS audio format %d\n"
93
               "Aborting\n",
94
               fmt);
95
        exit (EXIT_FAILURE);
96
    }
97
}
98

    
99
#ifdef DEBUG_PCM
100
static void oss_dump_pcm_info (struct oss_params *req, struct oss_params *obt)
101
{
102
    dolog ("parameter | requested value | obtained value\n");
103
    dolog ("format    |      %10d |     %10d\n", req->fmt, obt->fmt);
104
    dolog ("channels  |      %10d |     %10d\n", req->nchannels, obt->nchannels);
105
    dolog ("frequency |      %10d |     %10d\n", req->freq, obt->freq);
106
    dolog ("nfrags    |      %10d |     %10d\n", req->nfrags, obt->nfrags);
107
    dolog ("fragsize  |      %10d |     %10d\n", req->fragsize, obt->fragsize);
108
}
109
#endif
110

    
111
static int oss_open (struct oss_params *req, struct oss_params *obt, int *pfd)
112
{
113
    int fd;
114
    int mmmmssss;
115
    audio_buf_info abinfo;
116
    int fmt, freq, nchannels;
117
    const char *dspname = conf.dspname;
118

    
119
    fd = open (dspname, O_RDWR | O_NONBLOCK);
120
    if (-1 == fd) {
121
        dolog ("Could not initialize audio hardware. Failed to open `%s':\n"
122
               "Reason:%s\n",
123
               dspname,
124
               errstr ());
125
        return -1;
126
    }
127

    
128
    freq = req->freq;
129
    nchannels = req->nchannels;
130
    fmt = req->fmt;
131

    
132
    if (ioctl (fd, SNDCTL_DSP_SAMPLESIZE, &fmt)) {
133
        dolog ("Could not initialize audio hardware\n"
134
               "Failed to set sample size\n"
135
               "Reason: %s\n",
136
               errstr ());
137
        goto err;
138
    }
139

    
140
    if (ioctl (fd, SNDCTL_DSP_CHANNELS, &nchannels)) {
141
        dolog ("Could not initialize audio hardware\n"
142
               "Failed to set number of channels\n"
143
               "Reason: %s\n",
144
               errstr ());
145
        goto err;
146
    }
147

    
148
    if (ioctl (fd, SNDCTL_DSP_SPEED, &freq)) {
149
        dolog ("Could not initialize audio hardware\n"
150
               "Failed to set frequency\n"
151
               "Reason: %s\n",
152
               errstr ());
153
        goto err;
154
    }
155

    
156
    if (ioctl (fd, SNDCTL_DSP_NONBLOCK)) {
157
        dolog ("Could not initialize audio hardware\n"
158
               "Failed to set non-blocking mode\n"
159
               "Reason: %s\n",
160
               errstr ());
161
        goto err;
162
    }
163

    
164
    mmmmssss = (req->nfrags << 16) | lsbindex (req->fragsize);
165
    if (ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &mmmmssss)) {
166
        dolog ("Could not initialize audio hardware\n"
167
               "Failed to set buffer length (%d, %d)\n"
168
               "Reason:%s\n",
169
               conf.nfrags, conf.fragsize,
170
               errstr ());
171
        goto err;
172
    }
173

    
174
    if (ioctl (fd, SNDCTL_DSP_GETOSPACE, &abinfo)) {
175
        dolog ("Could not initialize audio hardware\n"
176
               "Failed to get buffer length\n"
177
               "Reason:%s\n",
178
               errstr ());
179
        goto err;
180
    }
181

    
182
    obt->fmt = fmt;
183
    obt->nchannels = nchannels;
184
    obt->freq = freq;
185
    obt->nfrags = abinfo.fragstotal;
186
    obt->fragsize = abinfo.fragsize;
187
    *pfd = fd;
188

    
189
    if ((req->fmt != obt->fmt) ||
190
        (req->nchannels != obt->nchannels) ||
191
        (req->freq != obt->freq) ||
192
        (req->fragsize != obt->fragsize) ||
193
        (req->nfrags != obt->nfrags)) {
194
#ifdef DEBUG_PCM
195
        dolog ("Audio parameters mismatch\n");
196
        oss_dump_pcm_info (req, obt);
197
#endif
198
    }
199

    
200
#ifdef DEBUG_PCM
201
    oss_dump_pcm_info (req, obt);
202
#endif
203
    return 0;
204

    
205
err:
206
    close (fd);
207
    return -1;
208
}
209

    
210
static void oss_hw_run (HWVoice *hw)
211
{
212
    OSSVoice *oss = (OSSVoice *) hw;
213
    int err, rpos, live, decr;
214
    int samples;
215
    uint8_t *dst;
216
    st_sample_t *src;
217
    struct audio_buf_info abinfo;
218
    struct count_info cntinfo;
219

    
220
    live = pcm_hw_get_live (hw);
221
    if (live <= 0)
222
        return;
223

    
224
    if (oss->mmapped) {
225
        int bytes;
226

    
227
        err = ioctl (oss->fd, SNDCTL_DSP_GETOPTR, &cntinfo);
228
        if (err < 0) {
229
            dolog ("SNDCTL_DSP_GETOPTR failed\nReason: %s\n", errstr ());
230
            return;
231
        }
232

    
233
        if (cntinfo.ptr == oss->old_optr) {
234
            if (abs (hw->samples - live) < 64)
235
                dolog ("overrun\n");
236
            return;
237
        }
238

    
239
        if (cntinfo.ptr > oss->old_optr) {
240
            bytes = cntinfo.ptr - oss->old_optr;
241
        }
242
        else {
243
            bytes = hw->bufsize + cntinfo.ptr - oss->old_optr;
244
        }
245

    
246
        decr = audio_MIN (bytes >> hw->shift, live);
247
    }
248
    else {
249
        err = ioctl (oss->fd, SNDCTL_DSP_GETOSPACE, &abinfo);
250
        if (err < 0) {
251
            dolog ("SNDCTL_DSP_GETOSPACE failed\nReason: %s\n", errstr ());
252
            return;
253
        }
254

    
255
        decr = audio_MIN (abinfo.bytes >> hw->shift, live);
256
        if (decr <= 0)
257
            return;
258
    }
259

    
260
    samples = decr;
261
    rpos = hw->rpos;
262
    while (samples) {
263
        int left_till_end_samples = hw->samples - rpos;
264
        int convert_samples = audio_MIN (samples, left_till_end_samples);
265

    
266
        src = advance (hw->mix_buf, rpos * sizeof (st_sample_t));
267
        dst = advance (oss->pcm_buf, rpos << hw->shift);
268

    
269
        hw->clip (dst, src, convert_samples);
270
        if (!oss->mmapped) {
271
            int written;
272

    
273
            written = write (oss->fd, dst, convert_samples << hw->shift);
274
            /* XXX: follow errno recommendations ? */
275
            if (written == -1) {
276
                dolog ("Failed to write audio\nReason: %s\n", errstr ());
277
                continue;
278
            }
279

    
280
            if (written != convert_samples << hw->shift) {
281
                int wsamples = written >> hw->shift;
282
                int wbytes = wsamples << hw->shift;
283
                if (wbytes != written) {
284
                    dolog ("Unaligned write %d, %d\n", wbytes, written);
285
                }
286
                memset (src, 0, wbytes);
287
                decr -= samples;
288
                rpos = (rpos + wsamples) % hw->samples;
289
                break;
290
            }
291
        }
292
        memset (src, 0, convert_samples * sizeof (st_sample_t));
293

    
294
        rpos = (rpos + convert_samples) % hw->samples;
295
        samples -= convert_samples;
296
    }
297
    if (oss->mmapped) {
298
        oss->old_optr = cntinfo.ptr;
299
    }
300

    
301
    pcm_hw_dec_live (hw, decr);
302
    hw->rpos = rpos;
303
}
304

    
305
static void oss_hw_fini (HWVoice *hw)
306
{
307
    int err;
308
    OSSVoice *oss = (OSSVoice *) hw;
309

    
310
    ldebug ("oss_hw_fini\n");
311
    err = close (oss->fd);
312
    if (err) {
313
        dolog ("Failed to close OSS descriptor\nReason: %s\n", errstr ());
314
    }
315
    oss->fd = -1;
316

    
317
    if (oss->pcm_buf) {
318
        if (oss->mmapped) {
319
            err = munmap (oss->pcm_buf, hw->bufsize);
320
            if (err) {
321
                dolog ("Failed to unmap OSS buffer\nReason: %s\n",
322
                       errstr ());
323
            }
324
        }
325
        else {
326
            qemu_free (oss->pcm_buf);
327
        }
328
        oss->pcm_buf = NULL;
329
    }
330
}
331

    
332
static int oss_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt)
333
{
334
    OSSVoice *oss = (OSSVoice *) hw;
335
    struct oss_params req, obt;
336

    
337
    assert (!oss->fd);
338
    req.fmt = AUD_to_ossfmt (fmt);
339
    req.freq = freq;
340
    req.nchannels = nchannels;
341
    req.fragsize = conf.fragsize;
342
    req.nfrags = conf.nfrags;
343

    
344
    if (oss_open (&req, &obt, &oss->fd))
345
        return -1;
346

    
347
    hw->freq = obt.freq;
348
    hw->fmt = oss_to_audfmt (obt.fmt);
349
    hw->nchannels = obt.nchannels;
350

    
351
    oss->nfrags = obt.nfrags;
352
    oss->fragsize = obt.fragsize;
353
    hw->bufsize = obt.nfrags * obt.fragsize;
354

    
355
    oss->mmapped = 0;
356
    if (conf.try_mmap) {
357
        oss->pcm_buf = mmap (0, hw->bufsize, PROT_READ | PROT_WRITE,
358
                             MAP_SHARED, oss->fd, 0);
359
        if (oss->pcm_buf == MAP_FAILED) {
360
            dolog ("Failed to mmap OSS device\nReason: %s\n",
361
                   errstr ());
362
        }
363

    
364
        for (;;) {
365
            int err;
366
            int trig = 0;
367
            if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
368
                dolog ("SNDCTL_DSP_SETTRIGGER 0 failed\nReason: %s\n",
369
                       errstr ());
370
                goto fail;
371
            }
372

    
373
            trig = PCM_ENABLE_OUTPUT;
374
            if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
375
                dolog ("SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
376
                       "Reason: %s\n", errstr ());
377
                goto fail;
378
            }
379
            oss->mmapped = 1;
380
            break;
381

    
382
        fail:
383
            err = munmap (oss->pcm_buf, hw->bufsize);
384
            if (err) {
385
                dolog ("Failed to unmap OSS device\nReason: %s\n",
386
                       errstr ());
387
            }
388
        }
389
    }
390

    
391
    if (!oss->mmapped) {
392
        oss->pcm_buf = qemu_mallocz (hw->bufsize);
393
        if (!oss->pcm_buf) {
394
            close (oss->fd);
395
            oss->fd = -1;
396
            return -1;
397
        }
398
    }
399

    
400
    return 0;
401
}
402

    
403
static int oss_hw_ctl (HWVoice *hw, int cmd, ...)
404
{
405
    int trig;
406
    OSSVoice *oss = (OSSVoice *) hw;
407

    
408
    if (!oss->mmapped)
409
        return 0;
410

    
411
    switch (cmd) {
412
    case VOICE_ENABLE:
413
        ldebug ("enabling voice\n");
414
        pcm_hw_clear (hw, oss->pcm_buf, hw->samples);
415
        trig = PCM_ENABLE_OUTPUT;
416
        if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
417
            dolog ("SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
418
                   "Reason: %s\n", errstr ());
419
            return -1;
420
        }
421
        break;
422

    
423
    case VOICE_DISABLE:
424
        ldebug ("disabling voice\n");
425
        trig = 0;
426
        if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
427
            dolog ("SNDCTL_DSP_SETTRIGGER 0 failed\nReason: %s\n",
428
                   errstr ());
429
            return -1;
430
        }
431
        break;
432
    }
433
    return 0;
434
}
435

    
436
static void *oss_audio_init (void)
437
{
438
    conf.fragsize = audio_get_conf_int (QC_OSS_FRAGSIZE, conf.fragsize);
439
    conf.nfrags = audio_get_conf_int (QC_OSS_NFRAGS, conf.nfrags);
440
    conf.try_mmap = audio_get_conf_int (QC_OSS_MMAP, conf.try_mmap);
441
    conf.dspname = audio_get_conf_str (QC_OSS_DEV, conf.dspname);
442
    return &conf;
443
}
444

    
445
static void oss_audio_fini (void *opaque)
446
{
447
}
448

    
449
struct pcm_ops oss_pcm_ops = {
450
    oss_hw_init,
451
    oss_hw_fini,
452
    oss_hw_run,
453
    oss_hw_write,
454
    oss_hw_ctl
455
};
456

    
457
struct audio_output_driver oss_output_driver = {
458
    "oss",
459
    oss_audio_init,
460
    oss_audio_fini,
461
    &oss_pcm_ops,
462
    1,
463
    INT_MAX,
464
    sizeof (OSSVoice)
465
};
466
#endif