Statistics
| Branch: | Revision:

root / audio / sdlaudio.c @ b1d8e52e

History | View | Annotate | Download (10.1 kB)

1
/*
2
 * QEMU SDL audio driver
3
 *
4
 * Copyright (c) 2004-2005 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 <SDL.h>
25
#include <SDL_thread.h>
26
#include "qemu-common.h"
27
#include "audio.h"
28

    
29
#ifndef _WIN32
30
#ifdef __sun__
31
#define _POSIX_PTHREAD_SEMANTICS 1
32
#elif defined(__OpenBSD__) || defined(__FreeBSD__)
33
#include <pthread.h>
34
#endif
35
#include <signal.h>
36
#endif
37

    
38
#define AUDIO_CAP "sdl"
39
#include "audio_int.h"
40

    
41
typedef struct SDLVoiceOut {
42
    HWVoiceOut hw;
43
    int live;
44
    int rpos;
45
    int decr;
46
} SDLVoiceOut;
47

    
48
static struct {
49
    int nb_samples;
50
} conf = {
51
    1024
52
};
53

    
54
static struct SDLAudioState {
55
    int exit;
56
    SDL_mutex *mutex;
57
    SDL_sem *sem;
58
    int initialized;
59
} glob_sdl;
60
typedef struct SDLAudioState SDLAudioState;
61

    
62
static void GCC_FMT_ATTR (1, 2) sdl_logerr (const char *fmt, ...)
63
{
64
    va_list ap;
65

    
66
    va_start (ap, fmt);
67
    AUD_vlog (AUDIO_CAP, fmt, ap);
68
    va_end (ap);
69

    
70
    AUD_log (AUDIO_CAP, "Reason: %s\n", SDL_GetError ());
71
}
72

    
73
static int sdl_lock (SDLAudioState *s, const char *forfn)
74
{
75
    if (SDL_LockMutex (s->mutex)) {
76
        sdl_logerr ("SDL_LockMutex for %s failed\n", forfn);
77
        return -1;
78
    }
79
    return 0;
80
}
81

    
82
static int sdl_unlock (SDLAudioState *s, const char *forfn)
83
{
84
    if (SDL_UnlockMutex (s->mutex)) {
85
        sdl_logerr ("SDL_UnlockMutex for %s failed\n", forfn);
86
        return -1;
87
    }
88
    return 0;
89
}
90

    
91
static int sdl_post (SDLAudioState *s, const char *forfn)
92
{
93
    if (SDL_SemPost (s->sem)) {
94
        sdl_logerr ("SDL_SemPost for %s failed\n", forfn);
95
        return -1;
96
    }
97
    return 0;
98
}
99

    
100
static int sdl_wait (SDLAudioState *s, const char *forfn)
101
{
102
    if (SDL_SemWait (s->sem)) {
103
        sdl_logerr ("SDL_SemWait for %s failed\n", forfn);
104
        return -1;
105
    }
106
    return 0;
107
}
108

    
109
static int sdl_unlock_and_post (SDLAudioState *s, const char *forfn)
110
{
111
    if (sdl_unlock (s, forfn)) {
112
        return -1;
113
    }
114

    
115
    return sdl_post (s, forfn);
116
}
117

    
118
static int aud_to_sdlfmt (audfmt_e fmt, int *shift)
119
{
120
    switch (fmt) {
121
    case AUD_FMT_S8:
122
        *shift = 0;
123
        return AUDIO_S8;
124

    
125
    case AUD_FMT_U8:
126
        *shift = 0;
127
        return AUDIO_U8;
128

    
129
    case AUD_FMT_S16:
130
        *shift = 1;
131
        return AUDIO_S16LSB;
132

    
133
    case AUD_FMT_U16:
134
        *shift = 1;
135
        return AUDIO_U16LSB;
136

    
137
    default:
138
        dolog ("Internal logic error: Bad audio format %d\n", fmt);
139
#ifdef DEBUG_AUDIO
140
        abort ();
141
#endif
142
        return AUDIO_U8;
143
    }
144
}
145

    
146
static int sdl_to_audfmt (int sdlfmt, audfmt_e *fmt, int *endianess)
147
{
148
    switch (sdlfmt) {
149
    case AUDIO_S8:
150
        *endianess = 0;
151
        *fmt = AUD_FMT_S8;
152
        break;
153

    
154
    case AUDIO_U8:
155
        *endianess = 0;
156
        *fmt = AUD_FMT_U8;
157
        break;
158

    
159
    case AUDIO_S16LSB:
160
        *endianess = 0;
161
        *fmt = AUD_FMT_S16;
162
        break;
163

    
164
    case AUDIO_U16LSB:
165
        *endianess = 0;
166
        *fmt = AUD_FMT_U16;
167
        break;
168

    
169
    case AUDIO_S16MSB:
170
        *endianess = 1;
171
        *fmt = AUD_FMT_S16;
172
        break;
173

    
174
    case AUDIO_U16MSB:
175
        *endianess = 1;
176
        *fmt = AUD_FMT_U16;
177
        break;
178

    
179
    default:
180
        dolog ("Unrecognized SDL audio format %d\n", sdlfmt);
181
        return -1;
182
    }
183

    
184
    return 0;
185
}
186

    
187
static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt)
188
{
189
    int status;
190
#ifndef _WIN32
191
    sigset_t new, old;
192

    
193
    /* Make sure potential threads created by SDL don't hog signals.  */
194
    sigfillset (&new);
195
    pthread_sigmask (SIG_BLOCK, &new, &old);
196
#endif
197

    
198
    status = SDL_OpenAudio (req, obt);
199
    if (status) {
200
        sdl_logerr ("SDL_OpenAudio failed\n");
201
    }
202

    
203
#ifndef _WIN32
204
    pthread_sigmask (SIG_SETMASK, &old, 0);
205
#endif
206
    return status;
207
}
208

    
209
static void sdl_close (SDLAudioState *s)
210
{
211
    if (s->initialized) {
212
        sdl_lock (s, "sdl_close");
213
        s->exit = 1;
214
        sdl_unlock_and_post (s, "sdl_close");
215
        SDL_PauseAudio (1);
216
        SDL_CloseAudio ();
217
        s->initialized = 0;
218
    }
219
}
220

    
221
static void sdl_callback (void *opaque, Uint8 *buf, int len)
222
{
223
    SDLVoiceOut *sdl = opaque;
224
    SDLAudioState *s = &glob_sdl;
225
    HWVoiceOut *hw = &sdl->hw;
226
    int samples = len >> hw->info.shift;
227

    
228
    if (s->exit) {
229
        return;
230
    }
231

    
232
    while (samples) {
233
        int to_mix, decr;
234

    
235
        /* dolog ("in callback samples=%d\n", samples); */
236
        sdl_wait (s, "sdl_callback");
237
        if (s->exit) {
238
            return;
239
        }
240

    
241
        if (sdl_lock (s, "sdl_callback")) {
242
            return;
243
        }
244

    
245
        if (audio_bug (AUDIO_FUNC, sdl->live < 0 || sdl->live > hw->samples)) {
246
            dolog ("sdl->live=%d hw->samples=%d\n",
247
                   sdl->live, hw->samples);
248
            return;
249
        }
250

    
251
        if (!sdl->live) {
252
            goto again;
253
        }
254

    
255
        /* dolog ("in callback live=%d\n", live); */
256
        to_mix = audio_MIN (samples, sdl->live);
257
        decr = to_mix;
258
        while (to_mix) {
259
            int chunk = audio_MIN (to_mix, hw->samples - hw->rpos);
260
            st_sample_t *src = hw->mix_buf + hw->rpos;
261

    
262
            /* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */
263
            hw->clip (buf, src, chunk);
264
            sdl->rpos = (sdl->rpos + chunk) % hw->samples;
265
            to_mix -= chunk;
266
            buf += chunk << hw->info.shift;
267
        }
268
        samples -= decr;
269
        sdl->live -= decr;
270
        sdl->decr += decr;
271

    
272
    again:
273
        if (sdl_unlock (s, "sdl_callback")) {
274
            return;
275
        }
276
    }
277
    /* dolog ("done len=%d\n", len); */
278
}
279

    
280
static int sdl_write_out (SWVoiceOut *sw, void *buf, int len)
281
{
282
    return audio_pcm_sw_write (sw, buf, len);
283
}
284

    
285
static int sdl_run_out (HWVoiceOut *hw)
286
{
287
    int decr, live;
288
    SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
289
    SDLAudioState *s = &glob_sdl;
290

    
291
    if (sdl_lock (s, "sdl_callback")) {
292
        return 0;
293
    }
294

    
295
    live = audio_pcm_hw_get_live_out (hw);
296

    
297
    if (sdl->decr > live) {
298
        ldebug ("sdl->decr %d live %d sdl->live %d\n",
299
                sdl->decr,
300
                live,
301
                sdl->live);
302
    }
303

    
304
    decr = audio_MIN (sdl->decr, live);
305
    sdl->decr -= decr;
306

    
307
    sdl->live = live - decr;
308
    hw->rpos = sdl->rpos;
309

    
310
    if (sdl->live > 0) {
311
        sdl_unlock_and_post (s, "sdl_callback");
312
    }
313
    else {
314
        sdl_unlock (s, "sdl_callback");
315
    }
316
    return decr;
317
}
318

    
319
static void sdl_fini_out (HWVoiceOut *hw)
320
{
321
    (void) hw;
322

    
323
    sdl_close (&glob_sdl);
324
}
325

    
326
static int sdl_init_out (HWVoiceOut *hw, audsettings_t *as)
327
{
328
    SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
329
    SDLAudioState *s = &glob_sdl;
330
    SDL_AudioSpec req, obt;
331
    int shift;
332
    int endianess;
333
    int err;
334
    audfmt_e effective_fmt;
335
    audsettings_t obt_as;
336

    
337
    shift <<= as->nchannels == 2;
338

    
339
    req.freq = as->freq;
340
    req.format = aud_to_sdlfmt (as->fmt, &shift);
341
    req.channels = as->nchannels;
342
    req.samples = conf.nb_samples;
343
    req.callback = sdl_callback;
344
    req.userdata = sdl;
345

    
346
    if (sdl_open (&req, &obt)) {
347
        return -1;
348
    }
349

    
350
    err = sdl_to_audfmt (obt.format, &effective_fmt, &endianess);
351
    if (err) {
352
        sdl_close (s);
353
        return -1;
354
    }
355

    
356
    obt_as.freq = obt.freq;
357
    obt_as.nchannels = obt.channels;
358
    obt_as.fmt = effective_fmt;
359
    obt_as.endianness = endianess;
360

    
361
    audio_pcm_init_info (&hw->info, &obt_as);
362
    hw->samples = obt.samples;
363

    
364
    s->initialized = 1;
365
    s->exit = 0;
366
    SDL_PauseAudio (0);
367
    return 0;
368
}
369

    
370
static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...)
371
{
372
    (void) hw;
373

    
374
    switch (cmd) {
375
    case VOICE_ENABLE:
376
        SDL_PauseAudio (0);
377
        break;
378

    
379
    case VOICE_DISABLE:
380
        SDL_PauseAudio (1);
381
        break;
382
    }
383
    return 0;
384
}
385

    
386
static void *sdl_audio_init (void)
387
{
388
    SDLAudioState *s = &glob_sdl;
389

    
390
    if (SDL_InitSubSystem (SDL_INIT_AUDIO)) {
391
        sdl_logerr ("SDL failed to initialize audio subsystem\n");
392
        return NULL;
393
    }
394

    
395
    s->mutex = SDL_CreateMutex ();
396
    if (!s->mutex) {
397
        sdl_logerr ("Failed to create SDL mutex\n");
398
        SDL_QuitSubSystem (SDL_INIT_AUDIO);
399
        return NULL;
400
    }
401

    
402
    s->sem = SDL_CreateSemaphore (0);
403
    if (!s->sem) {
404
        sdl_logerr ("Failed to create SDL semaphore\n");
405
        SDL_DestroyMutex (s->mutex);
406
        SDL_QuitSubSystem (SDL_INIT_AUDIO);
407
        return NULL;
408
    }
409

    
410
    return s;
411
}
412

    
413
static void sdl_audio_fini (void *opaque)
414
{
415
    SDLAudioState *s = opaque;
416
    sdl_close (s);
417
    SDL_DestroySemaphore (s->sem);
418
    SDL_DestroyMutex (s->mutex);
419
    SDL_QuitSubSystem (SDL_INIT_AUDIO);
420
}
421

    
422
static struct audio_option sdl_options[] = {
423
    {"SAMPLES", AUD_OPT_INT, &conf.nb_samples,
424
     "Size of SDL buffer in samples", NULL, 0},
425
    {NULL, 0, NULL, NULL, NULL, 0}
426
};
427

    
428
static struct audio_pcm_ops sdl_pcm_ops = {
429
    sdl_init_out,
430
    sdl_fini_out,
431
    sdl_run_out,
432
    sdl_write_out,
433
    sdl_ctl_out,
434

    
435
    NULL,
436
    NULL,
437
    NULL,
438
    NULL,
439
    NULL
440
};
441

    
442
struct audio_driver sdl_audio_driver = {
443
    INIT_FIELD (name           = ) "sdl",
444
    INIT_FIELD (descr          = ) "SDL http://www.libsdl.org",
445
    INIT_FIELD (options        = ) sdl_options,
446
    INIT_FIELD (init           = ) sdl_audio_init,
447
    INIT_FIELD (fini           = ) sdl_audio_fini,
448
    INIT_FIELD (pcm_ops        = ) &sdl_pcm_ops,
449
    INIT_FIELD (can_be_default = ) 1,
450
    INIT_FIELD (max_voices_out = ) 1,
451
    INIT_FIELD (max_voices_in  = ) 0,
452
    INIT_FIELD (voice_size_out = ) sizeof (SDLVoiceOut),
453
    INIT_FIELD (voice_size_in  = ) 0
454
};