Statistics
| Branch: | Revision:

root / audio / sdlaudio.c @ e784ba70

History | View | Annotate | Download (10 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 "vl.h"
27

    
28
#ifndef _WIN32
29
#ifdef __sun__
30
#define _POSIX_PTHREAD_SEMANTICS 1
31
#endif
32
#include <signal.h>
33
#endif
34

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
181
    return 0;
182
}
183

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
292
    live = audio_pcm_hw_get_live_out (hw);
293

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

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

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

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

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

    
320
    sdl_close (&glob_sdl);
321
}
322

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
407
    return s;
408
}
409

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

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

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

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

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