Statistics
| Branch: | Revision:

root / audio / sdlaudio.c @ 35f4b58c

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
#endif
33
#include <signal.h>
34
#endif
35

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
123
    case AUD_FMT_U8:
124
        *shift = 0;
125
        return AUDIO_U8;
126

    
127
    case AUD_FMT_S16:
128
        *shift = 1;
129
        return AUDIO_S16LSB;
130

    
131
    case AUD_FMT_U16:
132
        *shift = 1;
133
        return AUDIO_U16LSB;
134

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

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

    
152
    case AUDIO_U8:
153
        *endianess = 0;
154
        *fmt = AUD_FMT_U8;
155
        break;
156

    
157
    case AUDIO_S16LSB:
158
        *endianess = 0;
159
        *fmt = AUD_FMT_S16;
160
        break;
161

    
162
    case AUDIO_U16LSB:
163
        *endianess = 0;
164
        *fmt = AUD_FMT_U16;
165
        break;
166

    
167
    case AUDIO_S16MSB:
168
        *endianess = 1;
169
        *fmt = AUD_FMT_S16;
170
        break;
171

    
172
    case AUDIO_U16MSB:
173
        *endianess = 1;
174
        *fmt = AUD_FMT_U16;
175
        break;
176

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

    
182
    return 0;
183
}
184

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

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

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

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

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

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

    
226
    if (s->exit) {
227
        return;
228
    }
229

    
230
    while (samples) {
231
        int to_mix, decr;
232

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

    
239
        if (sdl_lock (s, "sdl_callback")) {
240
            return;
241
        }
242

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

    
249
        if (!sdl->live) {
250
            goto again;
251
        }
252

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

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

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

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

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

    
289
    if (sdl_lock (s, "sdl_callback")) {
290
        return 0;
291
    }
292

    
293
    live = audio_pcm_hw_get_live_out (hw);
294

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

    
302
    decr = audio_MIN (sdl->decr, live);
303
    sdl->decr -= decr;
304

    
305
    sdl->live = live - decr;
306
    hw->rpos = sdl->rpos;
307

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

    
317
static void sdl_fini_out (HWVoiceOut *hw)
318
{
319
    (void) hw;
320

    
321
    sdl_close (&glob_sdl);
322
}
323

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

    
335
    shift <<= as->nchannels == 2;
336

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

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

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

    
354
    obt_as.freq = obt.freq;
355
    obt_as.nchannels = obt.channels;
356
    obt_as.fmt = effective_fmt;
357
    obt_as.endianness = endianess;
358

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

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

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

    
372
    switch (cmd) {
373
    case VOICE_ENABLE:
374
        SDL_PauseAudio (0);
375
        break;
376

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

    
384
static void *sdl_audio_init (void)
385
{
386
    SDLAudioState *s = &glob_sdl;
387

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

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

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

    
408
    return s;
409
}
410

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

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

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

    
433
    NULL,
434
    NULL,
435
    NULL,
436
    NULL,
437
    NULL
438
};
439

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