xref: /qemu/audio/sdlaudio.c (revision 99a0949b720a0936da2052cb9a46db04ffc6db29)
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 };
431