Statistics
| Branch: | Revision:

root / audio / sdlaudio.c @ cf2c1839

History | View | Annotate | Download (10.4 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__) || defined(__DragonFly__)
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
    .nb_samples = 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)
119
{
120
    switch (fmt) {
121
    case AUD_FMT_S8:
122
        return AUDIO_S8;
123

    
124
    case AUD_FMT_U8:
125
        return AUDIO_U8;
126

    
127
    case AUD_FMT_S16:
128
        return AUDIO_S16LSB;
129

    
130
    case AUD_FMT_U16:
131
        return AUDIO_U16LSB;
132

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

    
142
static int sdl_to_audfmt (int sdlfmt, audfmt_e *fmt, int *endianess)
143
{
144
    switch (sdlfmt) {
145
    case AUDIO_S8:
146
        *endianess = 0;
147
        *fmt = AUD_FMT_S8;
148
        break;
149

    
150
    case AUDIO_U8:
151
        *endianess = 0;
152
        *fmt = AUD_FMT_U8;
153
        break;
154

    
155
    case AUDIO_S16LSB:
156
        *endianess = 0;
157
        *fmt = AUD_FMT_S16;
158
        break;
159

    
160
    case AUDIO_U16LSB:
161
        *endianess = 0;
162
        *fmt = AUD_FMT_U16;
163
        break;
164

    
165
    case AUDIO_S16MSB:
166
        *endianess = 1;
167
        *fmt = AUD_FMT_S16;
168
        break;
169

    
170
    case AUDIO_U16MSB:
171
        *endianess = 1;
172
        *fmt = AUD_FMT_U16;
173
        break;
174

    
175
    default:
176
        dolog ("Unrecognized SDL audio format %d\n", sdlfmt);
177
        return -1;
178
    }
179

    
180
    return 0;
181
}
182

    
183
static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt)
184
{
185
    int status;
186
#ifndef _WIN32
187
    int err;
188
    sigset_t new, old;
189

    
190
    /* Make sure potential threads created by SDL don't hog signals.  */
191
    err = sigfillset (&new);
192
    if (err) {
193
        dolog ("sdl_open: sigfillset failed: %s\n", strerror (errno));
194
        return -1;
195
    }
196
    err = pthread_sigmask (SIG_BLOCK, &new, &old);
197
    if (err) {
198
        dolog ("sdl_open: pthread_sigmask failed: %s\n", strerror (err));
199
        return -1;
200
    }
201
#endif
202

    
203
    status = SDL_OpenAudio (req, obt);
204
    if (status) {
205
        sdl_logerr ("SDL_OpenAudio failed\n");
206
    }
207

    
208
#ifndef _WIN32
209
    err = pthread_sigmask (SIG_SETMASK, &old, NULL);
210
    if (err) {
211
        dolog ("sdl_open: pthread_sigmask (restore) failed: %s\n",
212
               strerror (errno));
213
        /* We have failed to restore original signal mask, all bets are off,
214
           so exit the process */
215
        exit (EXIT_FAILURE);
216
    }
217
#endif
218
    return status;
219
}
220

    
221
static void sdl_close (SDLAudioState *s)
222
{
223
    if (s->initialized) {
224
        sdl_lock (s, "sdl_close");
225
        s->exit = 1;
226
        sdl_unlock_and_post (s, "sdl_close");
227
        SDL_PauseAudio (1);
228
        SDL_CloseAudio ();
229
        s->initialized = 0;
230
    }
231
}
232

    
233
static void sdl_callback (void *opaque, Uint8 *buf, int len)
234
{
235
    SDLVoiceOut *sdl = opaque;
236
    SDLAudioState *s = &glob_sdl;
237
    HWVoiceOut *hw = &sdl->hw;
238
    int samples = len >> hw->info.shift;
239

    
240
    if (s->exit) {
241
        return;
242
    }
243

    
244
    while (samples) {
245
        int to_mix, decr;
246

    
247
        /* dolog ("in callback samples=%d\n", samples); */
248
        sdl_wait (s, "sdl_callback");
249
        if (s->exit) {
250
            return;
251
        }
252

    
253
        if (sdl_lock (s, "sdl_callback")) {
254
            return;
255
        }
256

    
257
        if (audio_bug (AUDIO_FUNC, sdl->live < 0 || sdl->live > hw->samples)) {
258
            dolog ("sdl->live=%d hw->samples=%d\n",
259
                   sdl->live, hw->samples);
260
            return;
261
        }
262

    
263
        if (!sdl->live) {
264
            goto again;
265
        }
266

    
267
        /* dolog ("in callback live=%d\n", live); */
268
        to_mix = audio_MIN (samples, sdl->live);
269
        decr = to_mix;
270
        while (to_mix) {
271
            int chunk = audio_MIN (to_mix, hw->samples - hw->rpos);
272
            struct st_sample *src = hw->mix_buf + hw->rpos;
273

    
274
            /* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */
275
            hw->clip (buf, src, chunk);
276
            sdl->rpos = (sdl->rpos + chunk) % hw->samples;
277
            to_mix -= chunk;
278
            buf += chunk << hw->info.shift;
279
        }
280
        samples -= decr;
281
        sdl->live -= decr;
282
        sdl->decr += decr;
283

    
284
    again:
285
        if (sdl_unlock (s, "sdl_callback")) {
286
            return;
287
        }
288
    }
289
    /* dolog ("done len=%d\n", len); */
290
}
291

    
292
static int sdl_write_out (SWVoiceOut *sw, void *buf, int len)
293
{
294
    return audio_pcm_sw_write (sw, buf, len);
295
}
296

    
297
static int sdl_run_out (HWVoiceOut *hw, int live)
298
{
299
    int decr;
300
    SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
301
    SDLAudioState *s = &glob_sdl;
302

    
303
    if (sdl_lock (s, "sdl_run_out")) {
304
        return 0;
305
    }
306

    
307
    if (sdl->decr > live) {
308
        ldebug ("sdl->decr %d live %d sdl->live %d\n",
309
                sdl->decr,
310
                live,
311
                sdl->live);
312
    }
313

    
314
    decr = audio_MIN (sdl->decr, live);
315
    sdl->decr -= decr;
316

    
317
    sdl->live = live - decr;
318
    hw->rpos = sdl->rpos;
319

    
320
    if (sdl->live > 0) {
321
        sdl_unlock_and_post (s, "sdl_run_out");
322
    }
323
    else {
324
        sdl_unlock (s, "sdl_run_out");
325
    }
326
    return decr;
327
}
328

    
329
static void sdl_fini_out (HWVoiceOut *hw)
330
{
331
    (void) hw;
332

    
333
    sdl_close (&glob_sdl);
334
}
335

    
336
static int sdl_init_out (HWVoiceOut *hw, struct audsettings *as)
337
{
338
    SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
339
    SDLAudioState *s = &glob_sdl;
340
    SDL_AudioSpec req, obt;
341
    int endianess;
342
    int err;
343
    audfmt_e effective_fmt;
344
    struct audsettings obt_as;
345

    
346
    req.freq = as->freq;
347
    req.format = aud_to_sdlfmt (as->fmt);
348
    req.channels = as->nchannels;
349
    req.samples = conf.nb_samples;
350
    req.callback = sdl_callback;
351
    req.userdata = sdl;
352

    
353
    if (sdl_open (&req, &obt)) {
354
        return -1;
355
    }
356

    
357
    err = sdl_to_audfmt (obt.format, &effective_fmt, &endianess);
358
    if (err) {
359
        sdl_close (s);
360
        return -1;
361
    }
362

    
363
    obt_as.freq = obt.freq;
364
    obt_as.nchannels = obt.channels;
365
    obt_as.fmt = effective_fmt;
366
    obt_as.endianness = endianess;
367

    
368
    audio_pcm_init_info (&hw->info, &obt_as);
369
    hw->samples = obt.samples;
370

    
371
    s->initialized = 1;
372
    s->exit = 0;
373
    SDL_PauseAudio (0);
374
    return 0;
375
}
376

    
377
static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...)
378
{
379
    (void) hw;
380

    
381
    switch (cmd) {
382
    case VOICE_ENABLE:
383
        SDL_PauseAudio (0);
384
        break;
385

    
386
    case VOICE_DISABLE:
387
        SDL_PauseAudio (1);
388
        break;
389
    }
390
    return 0;
391
}
392

    
393
static void *sdl_audio_init (void)
394
{
395
    SDLAudioState *s = &glob_sdl;
396

    
397
    if (SDL_InitSubSystem (SDL_INIT_AUDIO)) {
398
        sdl_logerr ("SDL failed to initialize audio subsystem\n");
399
        return NULL;
400
    }
401

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

    
409
    s->sem = SDL_CreateSemaphore (0);
410
    if (!s->sem) {
411
        sdl_logerr ("Failed to create SDL semaphore\n");
412
        SDL_DestroyMutex (s->mutex);
413
        SDL_QuitSubSystem (SDL_INIT_AUDIO);
414
        return NULL;
415
    }
416

    
417
    return s;
418
}
419

    
420
static void sdl_audio_fini (void *opaque)
421
{
422
    SDLAudioState *s = opaque;
423
    sdl_close (s);
424
    SDL_DestroySemaphore (s->sem);
425
    SDL_DestroyMutex (s->mutex);
426
    SDL_QuitSubSystem (SDL_INIT_AUDIO);
427
}
428

    
429
static struct audio_option sdl_options[] = {
430
    {
431
        .name  = "SAMPLES",
432
        .tag   = AUD_OPT_INT,
433
        .valp  = &conf.nb_samples,
434
        .descr = "Size of SDL buffer in samples"
435
    },
436
    { /* End of list */ }
437
};
438

    
439
static struct audio_pcm_ops sdl_pcm_ops = {
440
    .init_out = sdl_init_out,
441
    .fini_out = sdl_fini_out,
442
    .run_out  = sdl_run_out,
443
    .write    = sdl_write_out,
444
    .ctl_out  = sdl_ctl_out,
445
};
446

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