Statistics
| Branch: | Revision:

root / audio / sdlaudio.c @ e120d449

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
#endif
36

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

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

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

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

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

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

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

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

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

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

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

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

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

    
117
static int aud_to_sdlfmt (audfmt_e fmt)
118
{
119
    switch (fmt) {
120
    case AUD_FMT_S8:
121
        return AUDIO_S8;
122

    
123
    case AUD_FMT_U8:
124
        return AUDIO_U8;
125

    
126
    case AUD_FMT_S16:
127
        return AUDIO_S16LSB;
128

    
129
    case AUD_FMT_U16:
130
        return AUDIO_U16LSB;
131

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

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

    
149
    case AUDIO_U8:
150
        *endianness = 0;
151
        *fmt = AUD_FMT_U8;
152
        break;
153

    
154
    case AUDIO_S16LSB:
155
        *endianness = 0;
156
        *fmt = AUD_FMT_S16;
157
        break;
158

    
159
    case AUDIO_U16LSB:
160
        *endianness = 0;
161
        *fmt = AUD_FMT_U16;
162
        break;
163

    
164
    case AUDIO_S16MSB:
165
        *endianness = 1;
166
        *fmt = AUD_FMT_S16;
167
        break;
168

    
169
    case AUDIO_U16MSB:
170
        *endianness = 1;
171
        *fmt = AUD_FMT_U16;
172
        break;
173

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

    
179
    return 0;
180
}
181

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
332
    sdl_close (&glob_sdl);
333
}
334

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
416
    return s;
417
}
418

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

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

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

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