Statistics
| Branch: | Revision:

root / audio / sdlaudio.c @ 9f059eca

History | View | Annotate | Download (7.8 kB)

1
/*
2
 * QEMU SDL audio output driver
3
 * 
4
 * Copyright (c) 2004 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
#include "audio/audio_int.h"
29

    
30
typedef struct SDLVoice {
31
    HWVoice hw;
32
} SDLVoice;
33

    
34
#define dolog(...) AUD_log ("sdl", __VA_ARGS__)
35
#ifdef DEBUG
36
#define ldebug(...) dolog (__VA_ARGS__)
37
#else
38
#define ldebug(...)
39
#endif
40

    
41
#define QC_SDL_SAMPLES "QEMU_SDL_SAMPLES"
42

    
43
#define errstr() SDL_GetError ()
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 sdl_hw_run (HWVoice *hw)
60
{
61
    (void) hw;
62
}
63

    
64
static int sdl_lock (SDLAudioState *s)
65
{
66
    if (SDL_LockMutex (s->mutex)) {
67
        dolog ("SDL_LockMutex failed\nReason: %s\n", errstr ());
68
        return -1;
69
    }
70
    return 0;
71
}
72

    
73
static int sdl_unlock (SDLAudioState *s)
74
{
75
    if (SDL_UnlockMutex (s->mutex)) {
76
        dolog ("SDL_UnlockMutex failed\nReason: %s\n", errstr ());
77
        return -1;
78
    }
79
    return 0;
80
}
81

    
82
static int sdl_post (SDLAudioState *s)
83
{
84
    if (SDL_SemPost (s->sem)) {
85
        dolog ("SDL_SemPost failed\nReason: %s\n", errstr ());
86
        return -1;
87
    }
88
    return 0;
89
}
90

    
91
static int sdl_wait (SDLAudioState *s)
92
{
93
    if (SDL_SemWait (s->sem)) {
94
        dolog ("SDL_SemWait failed\nReason: %s\n", errstr ());
95
        return -1;
96
    }
97
    return 0;
98
}
99

    
100
static int sdl_unlock_and_post (SDLAudioState *s)
101
{
102
    if (sdl_unlock (s))
103
        return -1;
104

    
105
    return sdl_post (s);
106
}
107

    
108
static int sdl_hw_write (SWVoice *sw, void *buf, int len)
109
{
110
    int ret;
111
    SDLAudioState *s = &glob_sdl;
112
    sdl_lock (s);
113
    ret = pcm_hw_write (sw, buf, len);
114
    sdl_unlock_and_post (s);
115
    return ret;
116
}
117

    
118
static int AUD_to_sdlfmt (audfmt_e fmt, int *shift)
119
{
120
    *shift = 0;
121
    switch (fmt) {
122
    case AUD_FMT_S8: return AUDIO_S8;
123
    case AUD_FMT_U8: return AUDIO_U8;
124
    case AUD_FMT_S16: *shift = 1; return AUDIO_S16LSB;
125
    case AUD_FMT_U16: *shift = 1; return AUDIO_U16LSB;
126
    default:
127
        dolog ("Internal logic error: Bad audio format %d\nAborting\n", fmt);
128
        exit (EXIT_FAILURE);
129
    }
130
}
131

    
132
static int sdl_to_audfmt (int fmt)
133
{
134
    switch (fmt) {
135
    case AUDIO_S8: return AUD_FMT_S8;
136
    case AUDIO_U8: return AUD_FMT_U8;
137
    case AUDIO_S16LSB: return AUD_FMT_S16;
138
    case AUDIO_U16LSB: return AUD_FMT_U16;
139
    default:
140
        dolog ("Internal logic error: Unrecognized SDL audio format %d\n"
141
               "Aborting\n", fmt);
142
        exit (EXIT_FAILURE);
143
    }
144
}
145

    
146
static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt)
147
{
148
    int status;
149

    
150
    status = SDL_OpenAudio (req, obt);
151
    if (status) {
152
        dolog ("SDL_OpenAudio failed\nReason: %s\n", errstr ());
153
    }
154
    return status;
155
}
156

    
157
static void sdl_close (SDLAudioState *s)
158
{
159
    if (s->initialized) {
160
        sdl_lock (s);
161
        s->exit = 1;
162
        sdl_unlock_and_post (s);
163
        SDL_PauseAudio (1);
164
        SDL_CloseAudio ();
165
        s->initialized = 0;
166
    }
167
}
168

    
169
static void sdl_callback (void *opaque, Uint8 *buf, int len)
170
{
171
    SDLVoice *sdl = opaque;
172
    SDLAudioState *s = &glob_sdl;
173
    HWVoice *hw = &sdl->hw;
174
    int samples = len >> hw->shift;
175

    
176
    if (s->exit) {
177
        return;
178
    }
179

    
180
    while (samples) {
181
        int to_mix, live, decr;
182

    
183
        /* dolog ("in callback samples=%d\n", samples); */
184
        sdl_wait (s);
185
        if (s->exit) {
186
            return;
187
        }
188

    
189
        sdl_lock (s);
190
        live = pcm_hw_get_live (hw);
191
        if (live <= 0)
192
            goto again;
193

    
194
        /* dolog ("in callback live=%d\n", live); */
195
        to_mix = audio_MIN (samples, live);
196
        decr = to_mix;
197
        while (to_mix) {
198
            int chunk = audio_MIN (to_mix, hw->samples - hw->rpos);
199
            st_sample_t *src = hw->mix_buf + hw->rpos;
200

    
201
            /* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */
202
            hw->clip (buf, src, chunk);
203
            memset (src, 0, chunk * sizeof (st_sample_t));
204
            hw->rpos = (hw->rpos + chunk) % hw->samples;
205
            to_mix -= chunk;
206
            buf += chunk << hw->shift;
207
        }
208
        samples -= decr;
209
        pcm_hw_dec_live (hw, decr);
210

    
211
    again:
212
        sdl_unlock (s);
213
    }
214
    /* dolog ("done len=%d\n", len); */
215
}
216

    
217
static void sdl_hw_fini (HWVoice *hw)
218
{
219
    ldebug ("sdl_hw_fini %d fixed=%d\n",
220
             glob_sdl.initialized, audio_conf.fixed_format);
221
    sdl_close (&glob_sdl);
222
}
223

    
224
static int sdl_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt)
225
{
226
    SDLVoice *sdl = (SDLVoice *) hw;
227
    SDLAudioState *s = &glob_sdl;
228
    SDL_AudioSpec req, obt;
229
    int shift;
230

    
231
    ldebug ("sdl_hw_init %d freq=%d fixed=%d\n",
232
            s->initialized, freq, audio_conf.fixed_format);
233

    
234
    if (nchannels != 2) {
235
        dolog ("Bogus channel count %d\n", nchannels);
236
        return -1;
237
    }
238

    
239
    req.freq = freq;
240
    req.format = AUD_to_sdlfmt (fmt, &shift);
241
    req.channels = nchannels;
242
    req.samples = conf.nb_samples;
243
    shift <<= nchannels == 2;
244

    
245
    req.callback = sdl_callback;
246
    req.userdata = sdl;
247

    
248
    if (sdl_open (&req, &obt))
249
        return -1;
250

    
251
    hw->freq = obt.freq;
252
    hw->fmt = sdl_to_audfmt (obt.format);
253
    hw->nchannels = obt.channels;
254
    hw->bufsize = obt.samples << shift;
255

    
256
    s->initialized = 1;
257
    s->exit = 0;
258
    SDL_PauseAudio (0);
259
    return 0;
260
}
261

    
262
static int sdl_hw_ctl (HWVoice *hw, int cmd, ...)
263
{
264
    (void) hw;
265

    
266
    switch (cmd) {
267
    case VOICE_ENABLE:
268
        SDL_PauseAudio (0);
269
        break;
270

    
271
    case VOICE_DISABLE:
272
        SDL_PauseAudio (1);
273
        break;
274
    }
275
    return 0;
276
}
277

    
278
static void *sdl_audio_init (void)
279
{
280
    SDLAudioState *s = &glob_sdl;
281
    conf.nb_samples = audio_get_conf_int (QC_SDL_SAMPLES, conf.nb_samples);
282

    
283
    if (SDL_InitSubSystem (SDL_INIT_AUDIO)) {
284
        dolog ("SDL failed to initialize audio subsystem\nReason: %s\n",
285
               errstr ());
286
        return NULL;
287
    }
288

    
289
    s->mutex = SDL_CreateMutex ();
290
    if (!s->mutex) {
291
        dolog ("Failed to create SDL mutex\nReason: %s\n", errstr ());
292
        SDL_QuitSubSystem (SDL_INIT_AUDIO);
293
        return NULL;
294
    }
295

    
296
    s->sem = SDL_CreateSemaphore (0);
297
    if (!s->sem) {
298
        dolog ("Failed to create SDL semaphore\nReason: %s\n", errstr ());
299
        SDL_DestroyMutex (s->mutex);
300
        SDL_QuitSubSystem (SDL_INIT_AUDIO);
301
        return NULL;
302
    }
303

    
304
    return s;
305
}
306

    
307
static void sdl_audio_fini (void *opaque)
308
{
309
    SDLAudioState *s = opaque;
310
    sdl_close (s);
311
    SDL_DestroySemaphore (s->sem);
312
    SDL_DestroyMutex (s->mutex);
313
    SDL_QuitSubSystem (SDL_INIT_AUDIO);
314
}
315

    
316
struct pcm_ops sdl_pcm_ops = {
317
    sdl_hw_init,
318
    sdl_hw_fini,
319
    sdl_hw_run,
320
    sdl_hw_write,
321
    sdl_hw_ctl
322
};
323

    
324
struct audio_output_driver sdl_output_driver = {
325
    "sdl",
326
    sdl_audio_init,
327
    sdl_audio_fini,
328
    &sdl_pcm_ops,
329
    1,
330
    1,
331
    sizeof (SDLVoice)
332
};