Statistics
| Branch: | Revision:

root / audio / sdlaudio.c @ 1d14ffa9

History | View | Annotate | Download (9.8 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
#define AUDIO_CAP "sdl"
29
#include "audio_int.h"
30

    
31
typedef struct SDLVoiceOut {
32
    HWVoiceOut hw;
33
    int live;
34
    int rpos;
35
    int decr;
36
} SDLVoiceOut;
37

    
38
static struct {
39
    int nb_samples;
40
} conf = {
41
    1024
42
};
43

    
44
struct SDLAudioState {
45
    int exit;
46
    SDL_mutex *mutex;
47
    SDL_sem *sem;
48
    int initialized;
49
} glob_sdl;
50
typedef struct SDLAudioState SDLAudioState;
51

    
52
static void GCC_FMT_ATTR (1, 2) sdl_logerr (const char *fmt, ...)
53
{
54
    va_list ap;
55

    
56
    va_start (ap, fmt);
57
    AUD_vlog (AUDIO_CAP, fmt, ap);
58
    va_end (ap);
59

    
60
    AUD_log (AUDIO_CAP, "Reason: %s\n", SDL_GetError ());
61
}
62

    
63
static int sdl_lock (SDLAudioState *s, const char *forfn)
64
{
65
    if (SDL_LockMutex (s->mutex)) {
66
        sdl_logerr ("SDL_LockMutex for %s failed\n", forfn);
67
        return -1;
68
    }
69
    return 0;
70
}
71

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

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

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

    
99
static int sdl_unlock_and_post (SDLAudioState *s, const char *forfn)
100
{
101
    if (sdl_unlock (s, forfn)) {
102
        return -1;
103
    }
104

    
105
    return sdl_post (s, forfn);
106
}
107

    
108
static int aud_to_sdlfmt (audfmt_e fmt, int *shift)
109
{
110
    switch (fmt) {
111
    case AUD_FMT_S8:
112
        *shift = 0;
113
        return AUDIO_S8;
114

    
115
    case AUD_FMT_U8:
116
        *shift = 0;
117
        return AUDIO_U8;
118

    
119
    case AUD_FMT_S16:
120
        *shift = 1;
121
        return AUDIO_S16LSB;
122

    
123
    case AUD_FMT_U16:
124
        *shift = 1;
125
        return AUDIO_U16LSB;
126

    
127
    default:
128
        dolog ("Internal logic error: Bad audio format %d\n", fmt);
129
#ifdef DEBUG_AUDIO
130
        abort ();
131
#endif
132
        return AUDIO_U8;
133
    }
134
}
135

    
136
static int sdl_to_audfmt (int sdlfmt, audfmt_e *fmt, int *endianess)
137
{
138
    switch (sdlfmt) {
139
    case AUDIO_S8:
140
        *endianess = 0;
141
        *fmt = AUD_FMT_S8;
142
        break;
143

    
144
    case AUDIO_U8:
145
        *endianess = 0;
146
        *fmt = AUD_FMT_U8;
147
        break;
148

    
149
    case AUDIO_S16LSB:
150
        *endianess = 0;
151
        *fmt = AUD_FMT_S16;
152
        break;
153

    
154
    case AUDIO_U16LSB:
155
        *endianess = 0;
156
        *fmt = AUD_FMT_U16;
157
        break;
158

    
159
    case AUDIO_S16MSB:
160
        *endianess = 1;
161
        *fmt = AUD_FMT_S16;
162
        break;
163

    
164
    case AUDIO_U16MSB:
165
        *endianess = 1;
166
        *fmt = AUD_FMT_U16;
167
        break;
168

    
169
    default:
170
        dolog ("Unrecognized SDL audio format %d\n", sdlfmt);
171
        return -1;
172
    }
173

    
174
    return 0;
175
}
176

    
177
static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt)
178
{
179
    int status;
180

    
181
    status = SDL_OpenAudio (req, obt);
182
    if (status) {
183
        sdl_logerr ("SDL_OpenAudio failed\n");
184
    }
185
    return status;
186
}
187

    
188
static void sdl_close (SDLAudioState *s)
189
{
190
    if (s->initialized) {
191
        sdl_lock (s, "sdl_close");
192
        s->exit = 1;
193
        sdl_unlock_and_post (s, "sdl_close");
194
        SDL_PauseAudio (1);
195
        SDL_CloseAudio ();
196
        s->initialized = 0;
197
    }
198
}
199

    
200
static void sdl_callback (void *opaque, Uint8 *buf, int len)
201
{
202
    SDLVoiceOut *sdl = opaque;
203
    SDLAudioState *s = &glob_sdl;
204
    HWVoiceOut *hw = &sdl->hw;
205
    int samples = len >> hw->info.shift;
206

    
207
    if (s->exit) {
208
        return;
209
    }
210

    
211
    while (samples) {
212
        int to_mix, decr;
213

    
214
        /* dolog ("in callback samples=%d\n", samples); */
215
        sdl_wait (s, "sdl_callback");
216
        if (s->exit) {
217
            return;
218
        }
219

    
220
        if (sdl_lock (s, "sdl_callback")) {
221
            return;
222
        }
223

    
224
        if (audio_bug (AUDIO_FUNC, sdl->live < 0 || sdl->live > hw->samples)) {
225
            dolog ("sdl->live=%d hw->samples=%d\n",
226
                   sdl->live, hw->samples);
227
            return;
228
        }
229

    
230
        if (!sdl->live) {
231
            goto again;
232
        }
233

    
234
        /* dolog ("in callback live=%d\n", live); */
235
        to_mix = audio_MIN (samples, sdl->live);
236
        decr = to_mix;
237
        while (to_mix) {
238
            int chunk = audio_MIN (to_mix, hw->samples - hw->rpos);
239
            st_sample_t *src = hw->mix_buf + hw->rpos;
240

    
241
            /* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */
242
            hw->clip (buf, src, chunk);
243
            mixeng_clear (src, chunk);
244
            sdl->rpos = (sdl->rpos + chunk) % hw->samples;
245
            to_mix -= chunk;
246
            buf += chunk << hw->info.shift;
247
        }
248
        samples -= decr;
249
        sdl->live -= decr;
250
        sdl->decr += decr;
251

    
252
    again:
253
        if (sdl_unlock (s, "sdl_callback")) {
254
            return;
255
        }
256
    }
257
    /* dolog ("done len=%d\n", len); */
258
}
259

    
260
static int sdl_write_out (SWVoiceOut *sw, void *buf, int len)
261
{
262
    return audio_pcm_sw_write (sw, buf, len);
263
}
264

    
265
static int sdl_run_out (HWVoiceOut *hw)
266
{
267
    int decr, live;
268
    SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
269
    SDLAudioState *s = &glob_sdl;
270

    
271
    if (sdl_lock (s, "sdl_callback")) {
272
        return 0;
273
    }
274

    
275
    live = audio_pcm_hw_get_live_out (hw);
276

    
277
    if (sdl->decr > live) {
278
        ldebug ("sdl->decr %d live %d sdl->live %d\n",
279
                sdl->decr,
280
                live,
281
                sdl->live);
282
    }
283

    
284
    decr = audio_MIN (sdl->decr, live);
285
    sdl->decr -= decr;
286

    
287
    sdl->live = live - decr;
288
    hw->rpos = sdl->rpos;
289

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

    
299
static void sdl_fini_out (HWVoiceOut *hw)
300
{
301
    (void) hw;
302

    
303
    sdl_close (&glob_sdl);
304
}
305

    
306
static int sdl_init_out (HWVoiceOut *hw, int freq, int nchannels, audfmt_e fmt)
307
{
308
    SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
309
    SDLAudioState *s = &glob_sdl;
310
    SDL_AudioSpec req, obt;
311
    int shift;
312
    int endianess;
313
    int err;
314
    audfmt_e effective_fmt;
315

    
316
    if (nchannels != 2) {
317
        dolog ("Can not init DAC. Bogus channel count %d\n", nchannels);
318
        return -1;
319
    }
320

    
321
    req.freq = freq;
322
    req.format = aud_to_sdlfmt (fmt, &shift);
323
    req.channels = nchannels;
324
    req.samples = conf.nb_samples;
325
    shift <<= nchannels == 2;
326

    
327
    req.callback = sdl_callback;
328
    req.userdata = sdl;
329

    
330
    if (sdl_open (&req, &obt)) {
331
        return -1;
332
    }
333

    
334
    err = sdl_to_audfmt (obt.format, &effective_fmt, &endianess);
335
    if (err) {
336
        sdl_close (s);
337
        return -1;
338
    }
339

    
340
    audio_pcm_init_info (
341
        &hw->info,
342
        obt.freq,
343
        obt.channels,
344
        effective_fmt,
345
        audio_need_to_swap_endian (endianess)
346
        );
347
    hw->bufsize = obt.samples << shift;
348

    
349
    s->initialized = 1;
350
    s->exit = 0;
351
    SDL_PauseAudio (0);
352
    return 0;
353
}
354

    
355
static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...)
356
{
357
    (void) hw;
358

    
359
    switch (cmd) {
360
    case VOICE_ENABLE:
361
        SDL_PauseAudio (0);
362
        break;
363

    
364
    case VOICE_DISABLE:
365
        SDL_PauseAudio (1);
366
        break;
367
    }
368
    return 0;
369
}
370

    
371
static void *sdl_audio_init (void)
372
{
373
    SDLAudioState *s = &glob_sdl;
374

    
375
    if (SDL_InitSubSystem (SDL_INIT_AUDIO)) {
376
        sdl_logerr ("SDL failed to initialize audio subsystem\n");
377
        return NULL;
378
    }
379

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

    
387
    s->sem = SDL_CreateSemaphore (0);
388
    if (!s->sem) {
389
        sdl_logerr ("Failed to create SDL semaphore\n");
390
        SDL_DestroyMutex (s->mutex);
391
        SDL_QuitSubSystem (SDL_INIT_AUDIO);
392
        return NULL;
393
    }
394

    
395
    return s;
396
}
397

    
398
static void sdl_audio_fini (void *opaque)
399
{
400
    SDLAudioState *s = opaque;
401
    sdl_close (s);
402
    SDL_DestroySemaphore (s->sem);
403
    SDL_DestroyMutex (s->mutex);
404
    SDL_QuitSubSystem (SDL_INIT_AUDIO);
405
}
406

    
407
static struct audio_option sdl_options[] = {
408
    {"SAMPLES", AUD_OPT_INT, &conf.nb_samples,
409
     "Size of SDL buffer in samples", NULL, 0},
410
    {NULL, 0, NULL, NULL, NULL, 0}
411
};
412

    
413
static struct audio_pcm_ops sdl_pcm_ops = {
414
    sdl_init_out,
415
    sdl_fini_out,
416
    sdl_run_out,
417
    sdl_write_out,
418
    sdl_ctl_out,
419

    
420
    NULL,
421
    NULL,
422
    NULL,
423
    NULL,
424
    NULL
425
};
426

    
427
struct audio_driver sdl_audio_driver = {
428
    INIT_FIELD (name           = ) "sdl",
429
    INIT_FIELD (descr          = ) "SDL http://www.libsdl.org",
430
    INIT_FIELD (options        = ) sdl_options,
431
    INIT_FIELD (init           = ) sdl_audio_init,
432
    INIT_FIELD (fini           = ) sdl_audio_fini,
433
    INIT_FIELD (pcm_ops        = ) &sdl_pcm_ops,
434
    INIT_FIELD (can_be_default = ) 1,
435
    INIT_FIELD (max_voices_out = ) 1,
436
    INIT_FIELD (max_voices_in  = ) 0,
437
    INIT_FIELD (voice_size_out = ) sizeof (SDLVoiceOut),
438
    INIT_FIELD (voice_size_in  = ) 0
439
};