Statistics
| Branch: | Revision:

root / audio / sdlaudio.c @ 64e58fe5

History | View | Annotate | Download (9.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 decr;
45
    int pending;
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, 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, NULL);
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 (sdl_lock (s, "sdl_callback")) {
229
        return;
230
    }
231

    
232
    if (s->exit) {
233
        return;
234
    }
235

    
236
    while (samples) {
237
        int to_mix, decr;
238

    
239
        while (!sdl->pending) {
240
            if (sdl_unlock (s, "sdl_callback")) {
241
                return;
242
            }
243

    
244
            sdl_wait (s, "sdl_callback");
245
            if (s->exit) {
246
                return;
247
            }
248

    
249
            if (sdl_lock (s, "sdl_callback")) {
250
                return;
251
            }
252
            sdl->pending += sdl->live;
253
            sdl->live = 0;
254
        }
255

    
256
        to_mix = audio_MIN (samples, sdl->pending);
257
        decr = audio_pcm_hw_clip_out (hw, buf, to_mix, 0);
258
        buf += decr << hw->info.shift;
259
        samples -= decr;
260
        sdl->decr += decr;
261
        sdl->pending -= decr;
262
    }
263

    
264
    if (sdl_unlock (s, "sdl_callback")) {
265
        return;
266
    }
267
}
268

    
269
static int sdl_write_out (SWVoiceOut *sw, void *buf, int len)
270
{
271
    return audio_pcm_sw_write (sw, buf, len);
272
}
273

    
274
static int sdl_run_out (HWVoiceOut *hw, int live)
275
{
276
    int decr;
277
    SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
278
    SDLAudioState *s = &glob_sdl;
279

    
280
    if (sdl_lock (s, "sdl_run_out")) {
281
        return 0;
282
    }
283

    
284
    sdl->live = live;
285
    decr = sdl->decr;
286
    sdl->decr = 0;
287

    
288
    if (sdl->live > 0) {
289
        sdl_unlock_and_post (s, "sdl_run_out");
290
    }
291
    else {
292
        sdl_unlock (s, "sdl_run_out");
293
    }
294
    return decr;
295
}
296

    
297
static void sdl_fini_out (HWVoiceOut *hw)
298
{
299
    (void) hw;
300

    
301
    sdl_close (&glob_sdl);
302
}
303

    
304
static int sdl_init_out (HWVoiceOut *hw, struct audsettings *as)
305
{
306
    SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
307
    SDLAudioState *s = &glob_sdl;
308
    SDL_AudioSpec req, obt;
309
    int shift;
310
    int endianess;
311
    int err;
312
    audfmt_e effective_fmt;
313
    struct audsettings obt_as;
314

    
315
    shift <<= as->nchannels == 2;
316

    
317
    req.freq = as->freq;
318
    req.format = aud_to_sdlfmt (as->fmt, &shift);
319
    req.channels = as->nchannels;
320
    req.samples = conf.nb_samples;
321
    req.callback = sdl_callback;
322
    req.userdata = sdl;
323

    
324
    if (sdl_open (&req, &obt)) {
325
        return -1;
326
    }
327

    
328
    err = sdl_to_audfmt (obt.format, &effective_fmt, &endianess);
329
    if (err) {
330
        sdl_close (s);
331
        return -1;
332
    }
333

    
334
    obt_as.freq = obt.freq;
335
    obt_as.nchannels = obt.channels;
336
    obt_as.fmt = effective_fmt;
337
    obt_as.endianness = endianess;
338

    
339
    audio_pcm_init_info (&hw->info, &obt_as);
340
    hw->samples = obt.samples;
341

    
342
    s->initialized = 1;
343
    s->exit = 0;
344
    SDL_PauseAudio (0);
345
    return 0;
346
}
347

    
348
static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...)
349
{
350
    (void) hw;
351

    
352
    switch (cmd) {
353
    case VOICE_ENABLE:
354
        SDL_PauseAudio (0);
355
        break;
356

    
357
    case VOICE_DISABLE:
358
        SDL_PauseAudio (1);
359
        break;
360
    }
361
    return 0;
362
}
363

    
364
static void *sdl_audio_init (void)
365
{
366
    SDLAudioState *s = &glob_sdl;
367

    
368
    if (SDL_InitSubSystem (SDL_INIT_AUDIO)) {
369
        sdl_logerr ("SDL failed to initialize audio subsystem\n");
370
        return NULL;
371
    }
372

    
373
    s->mutex = SDL_CreateMutex ();
374
    if (!s->mutex) {
375
        sdl_logerr ("Failed to create SDL mutex\n");
376
        SDL_QuitSubSystem (SDL_INIT_AUDIO);
377
        return NULL;
378
    }
379

    
380
    s->sem = SDL_CreateSemaphore (0);
381
    if (!s->sem) {
382
        sdl_logerr ("Failed to create SDL semaphore\n");
383
        SDL_DestroyMutex (s->mutex);
384
        SDL_QuitSubSystem (SDL_INIT_AUDIO);
385
        return NULL;
386
    }
387

    
388
    return s;
389
}
390

    
391
static void sdl_audio_fini (void *opaque)
392
{
393
    SDLAudioState *s = opaque;
394
    sdl_close (s);
395
    SDL_DestroySemaphore (s->sem);
396
    SDL_DestroyMutex (s->mutex);
397
    SDL_QuitSubSystem (SDL_INIT_AUDIO);
398
}
399

    
400
static struct audio_option sdl_options[] = {
401
    {
402
        .name  = "SAMPLES",
403
        .tag   = AUD_OPT_INT,
404
        .valp  = &conf.nb_samples,
405
        .descr = "Size of SDL buffer in samples"
406
    },
407
    { /* End of list */ }
408
};
409

    
410
static struct audio_pcm_ops sdl_pcm_ops = {
411
    .init_out = sdl_init_out,
412
    .fini_out = sdl_fini_out,
413
    .run_out  = sdl_run_out,
414
    .write    = sdl_write_out,
415
    .ctl_out  = sdl_ctl_out,
416
};
417

    
418
struct audio_driver sdl_audio_driver = {
419
    .name           = "sdl",
420
    .descr          = "SDL http://www.libsdl.org",
421
    .options        = sdl_options,
422
    .init           = sdl_audio_init,
423
    .fini           = sdl_audio_fini,
424
    .pcm_ops        = &sdl_pcm_ops,
425
    .can_be_default = 1,
426
    .max_voices_out = 1,
427
    .max_voices_in  = 0,
428
    .voice_size_out = sizeof (SDLVoiceOut),
429
    .voice_size_in  = 0
430
};