Statistics
| Branch: | Revision:

root / audio / esdaudio.c @ 197bc219

History | View | Annotate | Download (14.6 kB)

1
/*
2
 * QEMU ESD audio driver
3
 *
4
 * Copyright (c) 2006 Frederick Reeve (brushed up by 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 <esd.h>
25
#include "qemu-common.h"
26
#include "audio.h"
27
#include <signal.h>
28

    
29
#define AUDIO_CAP "esd"
30
#include "audio_int.h"
31
#include "audio_pt_int.h"
32

    
33
typedef struct {
34
    HWVoiceOut hw;
35
    int done;
36
    int live;
37
    int decr;
38
    int rpos;
39
    void *pcm_buf;
40
    int fd;
41
    struct audio_pt pt;
42
} ESDVoiceOut;
43

    
44
typedef struct {
45
    HWVoiceIn hw;
46
    int done;
47
    int dead;
48
    int incr;
49
    int wpos;
50
    void *pcm_buf;
51
    int fd;
52
    struct audio_pt pt;
53
} ESDVoiceIn;
54

    
55
static struct {
56
    int samples;
57
    int divisor;
58
    char *dac_host;
59
    char *adc_host;
60
} conf = {
61
    .samples = 1024,
62
    .divisor = 2,
63
};
64

    
65
static void GCC_FMT_ATTR (2, 3) qesd_logerr (int err, const char *fmt, ...)
66
{
67
    va_list ap;
68

    
69
    va_start (ap, fmt);
70
    AUD_vlog (AUDIO_CAP, fmt, ap);
71
    va_end (ap);
72

    
73
    AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err));
74
}
75

    
76
/* playback */
77
static void *qesd_thread_out (void *arg)
78
{
79
    ESDVoiceOut *esd = arg;
80
    HWVoiceOut *hw = &esd->hw;
81
    int threshold;
82

    
83
    threshold = conf.divisor ? hw->samples / conf.divisor : 0;
84

    
85
    if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
86
        return NULL;
87
    }
88

    
89
    for (;;) {
90
        int decr, to_mix, rpos;
91

    
92
        for (;;) {
93
            if (esd->done) {
94
                goto exit;
95
            }
96

    
97
            if (esd->live > threshold) {
98
                break;
99
            }
100

    
101
            if (audio_pt_wait (&esd->pt, AUDIO_FUNC)) {
102
                goto exit;
103
            }
104
        }
105

    
106
        decr = to_mix = esd->live;
107
        rpos = hw->rpos;
108

    
109
        if (audio_pt_unlock (&esd->pt, AUDIO_FUNC)) {
110
            return NULL;
111
        }
112

    
113
        while (to_mix) {
114
            ssize_t written;
115
            int chunk = audio_MIN (to_mix, hw->samples - rpos);
116
            struct st_sample *src = hw->mix_buf + rpos;
117

    
118
            hw->clip (esd->pcm_buf, src, chunk);
119

    
120
        again:
121
            written = write (esd->fd, esd->pcm_buf, chunk << hw->info.shift);
122
            if (written == -1) {
123
                if (errno == EINTR || errno == EAGAIN) {
124
                    goto again;
125
                }
126
                qesd_logerr (errno, "write failed\n");
127
                return NULL;
128
            }
129

    
130
            if (written != chunk << hw->info.shift) {
131
                int wsamples = written >> hw->info.shift;
132
                int wbytes = wsamples << hw->info.shift;
133
                if (wbytes != written) {
134
                    dolog ("warning: Misaligned write %d (requested %d), "
135
                           "alignment %d\n",
136
                           wbytes, written, hw->info.align + 1);
137
                }
138
                to_mix -= wsamples;
139
                rpos = (rpos + wsamples) % hw->samples;
140
                break;
141
            }
142

    
143
            rpos = (rpos + chunk) % hw->samples;
144
            to_mix -= chunk;
145
        }
146

    
147
        if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
148
            return NULL;
149
        }
150

    
151
        esd->rpos = rpos;
152
        esd->live -= decr;
153
        esd->decr += decr;
154
    }
155

    
156
 exit:
157
    audio_pt_unlock (&esd->pt, AUDIO_FUNC);
158
    return NULL;
159
}
160

    
161
static int qesd_run_out (HWVoiceOut *hw)
162
{
163
    int live, decr;
164
    ESDVoiceOut *esd = (ESDVoiceOut *) hw;
165

    
166
    if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
167
        return 0;
168
    }
169

    
170
    live = audio_pcm_hw_get_live_out (hw);
171
    decr = audio_MIN (live, esd->decr);
172
    esd->decr -= decr;
173
    esd->live = live - decr;
174
    hw->rpos = esd->rpos;
175
    if (esd->live > 0) {
176
        audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
177
    }
178
    else {
179
        audio_pt_unlock (&esd->pt, AUDIO_FUNC);
180
    }
181
    return decr;
182
}
183

    
184
static int qesd_write (SWVoiceOut *sw, void *buf, int len)
185
{
186
    return audio_pcm_sw_write (sw, buf, len);
187
}
188

    
189
static int qesd_init_out (HWVoiceOut *hw, struct audsettings *as)
190
{
191
    ESDVoiceOut *esd = (ESDVoiceOut *) hw;
192
    struct audsettings obt_as = *as;
193
    int esdfmt = ESD_STREAM | ESD_PLAY;
194
    int err;
195
    sigset_t set, old_set;
196

    
197
    sigfillset (&set);
198

    
199
    esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO;
200
    switch (as->fmt) {
201
    case AUD_FMT_S8:
202
    case AUD_FMT_U8:
203
        esdfmt |= ESD_BITS8;
204
        obt_as.fmt = AUD_FMT_U8;
205
        break;
206

    
207
    case AUD_FMT_S32:
208
    case AUD_FMT_U32:
209
        dolog ("Will use 16 instead of 32 bit samples\n");
210

    
211
    case AUD_FMT_S16:
212
    case AUD_FMT_U16:
213
    deffmt:
214
        esdfmt |= ESD_BITS16;
215
        obt_as.fmt = AUD_FMT_S16;
216
        break;
217

    
218
    default:
219
        dolog ("Internal logic error: Bad audio format %d\n", as->fmt);
220
        goto deffmt;
221

    
222
    }
223
    obt_as.endianness = AUDIO_HOST_ENDIANNESS;
224

    
225
    audio_pcm_init_info (&hw->info, &obt_as);
226

    
227
    hw->samples = conf.samples;
228
    esd->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
229
    if (!esd->pcm_buf) {
230
        dolog ("Could not allocate buffer (%d bytes)\n",
231
               hw->samples << hw->info.shift);
232
        return -1;
233
    }
234

    
235
    esd->fd = -1;
236
    err = pthread_sigmask (SIG_BLOCK, &set, &old_set);
237
    if (err) {
238
        qesd_logerr (err, "pthread_sigmask failed\n");
239
        goto fail1;
240
    }
241

    
242
    esd->fd = esd_play_stream (esdfmt, as->freq, conf.dac_host, NULL);
243
    if (esd->fd < 0) {
244
        qesd_logerr (errno, "esd_play_stream failed\n");
245
        goto fail2;
246
    }
247

    
248
    if (audio_pt_init (&esd->pt, qesd_thread_out, esd, AUDIO_CAP, AUDIO_FUNC)) {
249
        goto fail3;
250
    }
251

    
252
    err = pthread_sigmask (SIG_SETMASK, &old_set, NULL);
253
    if (err) {
254
        qesd_logerr (err, "pthread_sigmask(restore) failed\n");
255
    }
256

    
257
    return 0;
258

    
259
 fail3:
260
    if (close (esd->fd)) {
261
        qesd_logerr (errno, "%s: close on esd socket(%d) failed\n",
262
                     AUDIO_FUNC, esd->fd);
263
    }
264
    esd->fd = -1;
265

    
266
 fail2:
267
    err = pthread_sigmask (SIG_SETMASK, &old_set, NULL);
268
    if (err) {
269
        qesd_logerr (err, "pthread_sigmask(restore) failed\n");
270
    }
271

    
272
 fail1:
273
    qemu_free (esd->pcm_buf);
274
    esd->pcm_buf = NULL;
275
    return -1;
276
}
277

    
278
static void qesd_fini_out (HWVoiceOut *hw)
279
{
280
    void *ret;
281
    ESDVoiceOut *esd = (ESDVoiceOut *) hw;
282

    
283
    audio_pt_lock (&esd->pt, AUDIO_FUNC);
284
    esd->done = 1;
285
    audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
286
    audio_pt_join (&esd->pt, &ret, AUDIO_FUNC);
287

    
288
    if (esd->fd >= 0) {
289
        if (close (esd->fd)) {
290
            qesd_logerr (errno, "failed to close esd socket\n");
291
        }
292
        esd->fd = -1;
293
    }
294

    
295
    audio_pt_fini (&esd->pt, AUDIO_FUNC);
296

    
297
    qemu_free (esd->pcm_buf);
298
    esd->pcm_buf = NULL;
299
}
300

    
301
static int qesd_ctl_out (HWVoiceOut *hw, int cmd, ...)
302
{
303
    (void) hw;
304
    (void) cmd;
305
    return 0;
306
}
307

    
308
/* capture */
309
static void *qesd_thread_in (void *arg)
310
{
311
    ESDVoiceIn *esd = arg;
312
    HWVoiceIn *hw = &esd->hw;
313
    int threshold;
314

    
315
    threshold = conf.divisor ? hw->samples / conf.divisor : 0;
316

    
317
    if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
318
        return NULL;
319
    }
320

    
321
    for (;;) {
322
        int incr, to_grab, wpos;
323

    
324
        for (;;) {
325
            if (esd->done) {
326
                goto exit;
327
            }
328

    
329
            if (esd->dead > threshold) {
330
                break;
331
            }
332

    
333
            if (audio_pt_wait (&esd->pt, AUDIO_FUNC)) {
334
                goto exit;
335
            }
336
        }
337

    
338
        incr = to_grab = esd->dead;
339
        wpos = hw->wpos;
340

    
341
        if (audio_pt_unlock (&esd->pt, AUDIO_FUNC)) {
342
            return NULL;
343
        }
344

    
345
        while (to_grab) {
346
            ssize_t nread;
347
            int chunk = audio_MIN (to_grab, hw->samples - wpos);
348
            void *buf = advance (esd->pcm_buf, wpos);
349

    
350
        again:
351
            nread = read (esd->fd, buf, chunk << hw->info.shift);
352
            if (nread == -1) {
353
                if (errno == EINTR || errno == EAGAIN) {
354
                    goto again;
355
                }
356
                qesd_logerr (errno, "read failed\n");
357
                return NULL;
358
            }
359

    
360
            if (nread != chunk << hw->info.shift) {
361
                int rsamples = nread >> hw->info.shift;
362
                int rbytes = rsamples << hw->info.shift;
363
                if (rbytes != nread) {
364
                    dolog ("warning: Misaligned write %d (requested %d), "
365
                           "alignment %d\n",
366
                           rbytes, nread, hw->info.align + 1);
367
                }
368
                to_grab -= rsamples;
369
                wpos = (wpos + rsamples) % hw->samples;
370
                break;
371
            }
372

    
373
            hw->conv (hw->conv_buf + wpos, buf, nread >> hw->info.shift,
374
                      &nominal_volume);
375
            wpos = (wpos + chunk) % hw->samples;
376
            to_grab -= chunk;
377
        }
378

    
379
        if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
380
            return NULL;
381
        }
382

    
383
        esd->wpos = wpos;
384
        esd->dead -= incr;
385
        esd->incr += incr;
386
    }
387

    
388
 exit:
389
    audio_pt_unlock (&esd->pt, AUDIO_FUNC);
390
    return NULL;
391
}
392

    
393
static int qesd_run_in (HWVoiceIn *hw)
394
{
395
    int live, incr, dead;
396
    ESDVoiceIn *esd = (ESDVoiceIn *) hw;
397

    
398
    if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
399
        return 0;
400
    }
401

    
402
    live = audio_pcm_hw_get_live_in (hw);
403
    dead = hw->samples - live;
404
    incr = audio_MIN (dead, esd->incr);
405
    esd->incr -= incr;
406
    esd->dead = dead - incr;
407
    hw->wpos = esd->wpos;
408
    if (esd->dead > 0) {
409
        audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
410
    }
411
    else {
412
        audio_pt_unlock (&esd->pt, AUDIO_FUNC);
413
    }
414
    return incr;
415
}
416

    
417
static int qesd_read (SWVoiceIn *sw, void *buf, int len)
418
{
419
    return audio_pcm_sw_read (sw, buf, len);
420
}
421

    
422
static int qesd_init_in (HWVoiceIn *hw, struct audsettings *as)
423
{
424
    ESDVoiceIn *esd = (ESDVoiceIn *) hw;
425
    struct audsettings obt_as = *as;
426
    int esdfmt = ESD_STREAM | ESD_RECORD;
427
    int err;
428
    sigset_t set, old_set;
429

    
430
    sigfillset (&set);
431

    
432
    esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO;
433
    switch (as->fmt) {
434
    case AUD_FMT_S8:
435
    case AUD_FMT_U8:
436
        esdfmt |= ESD_BITS8;
437
        obt_as.fmt = AUD_FMT_U8;
438
        break;
439

    
440
    case AUD_FMT_S16:
441
    case AUD_FMT_U16:
442
        esdfmt |= ESD_BITS16;
443
        obt_as.fmt = AUD_FMT_S16;
444
        break;
445

    
446
    case AUD_FMT_S32:
447
    case AUD_FMT_U32:
448
        dolog ("Will use 16 instead of 32 bit samples\n");
449
        esdfmt |= ESD_BITS16;
450
        obt_as.fmt = AUD_FMT_S16;
451
        break;
452
    }
453
    obt_as.endianness = AUDIO_HOST_ENDIANNESS;
454

    
455
    audio_pcm_init_info (&hw->info, &obt_as);
456

    
457
    hw->samples = conf.samples;
458
    esd->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
459
    if (!esd->pcm_buf) {
460
        dolog ("Could not allocate buffer (%d bytes)\n",
461
               hw->samples << hw->info.shift);
462
        return -1;
463
    }
464

    
465
    esd->fd = -1;
466

    
467
    err = pthread_sigmask (SIG_BLOCK, &set, &old_set);
468
    if (err) {
469
        qesd_logerr (err, "pthread_sigmask failed\n");
470
        goto fail1;
471
    }
472

    
473
    esd->fd = esd_record_stream (esdfmt, as->freq, conf.adc_host, NULL);
474
    if (esd->fd < 0) {
475
        qesd_logerr (errno, "esd_record_stream failed\n");
476
        goto fail2;
477
    }
478

    
479
    if (audio_pt_init (&esd->pt, qesd_thread_in, esd, AUDIO_CAP, AUDIO_FUNC)) {
480
        goto fail3;
481
    }
482

    
483
    err = pthread_sigmask (SIG_SETMASK, &old_set, NULL);
484
    if (err) {
485
        qesd_logerr (err, "pthread_sigmask(restore) failed\n");
486
    }
487

    
488
    return 0;
489

    
490
 fail3:
491
    if (close (esd->fd)) {
492
        qesd_logerr (errno, "%s: close on esd socket(%d) failed\n",
493
                     AUDIO_FUNC, esd->fd);
494
    }
495
    esd->fd = -1;
496

    
497
 fail2:
498
    err = pthread_sigmask (SIG_SETMASK, &old_set, NULL);
499
    if (err) {
500
        qesd_logerr (err, "pthread_sigmask(restore) failed\n");
501
    }
502

    
503
 fail1:
504
    qemu_free (esd->pcm_buf);
505
    esd->pcm_buf = NULL;
506
    return -1;
507
}
508

    
509
static void qesd_fini_in (HWVoiceIn *hw)
510
{
511
    void *ret;
512
    ESDVoiceIn *esd = (ESDVoiceIn *) hw;
513

    
514
    audio_pt_lock (&esd->pt, AUDIO_FUNC);
515
    esd->done = 1;
516
    audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
517
    audio_pt_join (&esd->pt, &ret, AUDIO_FUNC);
518

    
519
    if (esd->fd >= 0) {
520
        if (close (esd->fd)) {
521
            qesd_logerr (errno, "failed to close esd socket\n");
522
        }
523
        esd->fd = -1;
524
    }
525

    
526
    audio_pt_fini (&esd->pt, AUDIO_FUNC);
527

    
528
    qemu_free (esd->pcm_buf);
529
    esd->pcm_buf = NULL;
530
}
531

    
532
static int qesd_ctl_in (HWVoiceIn *hw, int cmd, ...)
533
{
534
    (void) hw;
535
    (void) cmd;
536
    return 0;
537
}
538

    
539
/* common */
540
static void *qesd_audio_init (void)
541
{
542
    return &conf;
543
}
544

    
545
static void qesd_audio_fini (void *opaque)
546
{
547
    (void) opaque;
548
    ldebug ("esd_fini");
549
}
550

    
551
struct audio_option qesd_options[] = {
552
    {
553
        .name  = "SAMPLES",
554
        .tag   = AUD_OPT_INT,
555
        .valp  = &conf.samples,
556
        .descr = "buffer size in samples"
557
    },
558
    {
559
        .name  = "DIVISOR",
560
        .tag   = AUD_OPT_INT,
561
        .valp  = &conf.divisor,
562
        .descr = "threshold divisor"
563
    },
564
    {
565
        .name  = "DAC_HOST",
566
        .tag   = AUD_OPT_STR,
567
        .valp  = &conf.dac_host,
568
        .descr = "playback host"
569
    },
570
    {
571
        .name  = "ADC_HOST",
572
        .tag   = AUD_OPT_STR,
573
        .valp  = &conf.adc_host,
574
        .descr = "capture host"
575
    },
576
    { /* End of list */ }
577
};
578

    
579
static struct audio_pcm_ops qesd_pcm_ops = {
580
    .init_out = qesd_init_out,
581
    .fini_out = qesd_fini_out,
582
    .run_out  = qesd_run_out,
583
    .write    = qesd_write,
584
    .ctl_out  = qesd_ctl_out,
585

    
586
    .init_in  = qesd_init_in,
587
    .fini_in  = qesd_fini_in,
588
    .run_in   = qesd_run_in,
589
    .read     = qesd_read,
590
    .ctl_in   = qesd_ctl_in,
591
};
592

    
593
struct audio_driver esd_audio_driver = {
594
    .name           = "esd",
595
    .descr          = "http://en.wikipedia.org/wiki/Esound",
596
    .options        = qesd_options,
597
    .init           = qesd_audio_init,
598
    .fini           = qesd_audio_fini,
599
    .pcm_ops        = &qesd_pcm_ops,
600
    .can_be_default = 0,
601
    .max_voices_out = INT_MAX,
602
    .max_voices_in  = INT_MAX,
603
    .voice_size_out = sizeof (ESDVoiceOut),
604
    .voice_size_in  = sizeof (ESDVoiceIn)
605
};