Statistics
| Branch: | Revision:

root / audio / ossaudio.c @ a98d49b1

History | View | Annotate | Download (12.7 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
#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_RDWR | 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
};