xref: /openbmc/qemu/audio/sdlaudio.c (revision 7f709ce7)
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 "qemu/osdep.h"
25 #include <SDL.h>
26 #include <SDL_thread.h>
27 #include "qemu-common.h"
28 #include "audio.h"
29 
30 #ifndef _WIN32
31 #ifdef __sun__
32 #define _POSIX_PTHREAD_SEMANTICS 1
33 #elif defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__DragonFly__)
34 #include <pthread.h>
35 #endif
36 #endif
37 
38 #define AUDIO_CAP "sdl"
39 #include "audio_int.h"
40 
41 #define USE_SEMAPHORE (SDL_MAJOR_VERSION < 2)
42 
43 typedef struct SDLVoiceOut {
44     HWVoiceOut hw;
45     int live;
46 #if USE_SEMAPHORE
47     int rpos;
48 #endif
49     int decr;
50 } SDLVoiceOut;
51 
52 static struct {
53     int nb_samples;
54 } conf = {
55     .nb_samples = 1024
56 };
57 
58 static struct SDLAudioState {
59     int exit;
60 #if USE_SEMAPHORE
61     SDL_mutex *mutex;
62     SDL_sem *sem;
63 #endif
64     int initialized;
65     bool driver_created;
66 } glob_sdl;
67 typedef struct SDLAudioState SDLAudioState;
68 
69 static void GCC_FMT_ATTR (1, 2) sdl_logerr (const char *fmt, ...)
70 {
71     va_list ap;
72 
73     va_start (ap, fmt);
74     AUD_vlog (AUDIO_CAP, fmt, ap);
75     va_end (ap);
76 
77     AUD_log (AUDIO_CAP, "Reason: %s\n", SDL_GetError ());
78 }
79 
80 static int sdl_lock (SDLAudioState *s, const char *forfn)
81 {
82 #if USE_SEMAPHORE
83     if (SDL_LockMutex (s->mutex)) {
84         sdl_logerr ("SDL_LockMutex for %s failed\n", forfn);
85         return -1;
86     }
87 #else
88     SDL_LockAudio();
89 #endif
90 
91     return 0;
92 }
93 
94 static int sdl_unlock (SDLAudioState *s, const char *forfn)
95 {
96 #if USE_SEMAPHORE
97     if (SDL_UnlockMutex (s->mutex)) {
98         sdl_logerr ("SDL_UnlockMutex for %s failed\n", forfn);
99         return -1;
100     }
101 #else
102     SDL_UnlockAudio();
103 #endif
104 
105     return 0;
106 }
107 
108 static int sdl_post (SDLAudioState *s, const char *forfn)
109 {
110 #if USE_SEMAPHORE
111     if (SDL_SemPost (s->sem)) {
112         sdl_logerr ("SDL_SemPost for %s failed\n", forfn);
113         return -1;
114     }
115 #endif
116 
117     return 0;
118 }
119 
120 #if USE_SEMAPHORE
121 static int sdl_wait (SDLAudioState *s, const char *forfn)
122 {
123     if (SDL_SemWait (s->sem)) {
124         sdl_logerr ("SDL_SemWait for %s failed\n", forfn);
125         return -1;
126     }
127     return 0;
128 }
129 #endif
130 
131 static int sdl_unlock_and_post (SDLAudioState *s, const char *forfn)
132 {
133     if (sdl_unlock (s, forfn)) {
134         return -1;
135     }
136 
137     return sdl_post (s, forfn);
138 }
139 
140 static int aud_to_sdlfmt (audfmt_e fmt)
141 {
142     switch (fmt) {
143     case AUD_FMT_S8:
144         return AUDIO_S8;
145 
146     case AUD_FMT_U8:
147         return AUDIO_U8;
148 
149     case AUD_FMT_S16:
150         return AUDIO_S16LSB;
151 
152     case AUD_FMT_U16:
153         return AUDIO_U16LSB;
154 
155     default:
156         dolog ("Internal logic error: Bad audio format %d\n", fmt);
157 #ifdef DEBUG_AUDIO
158         abort ();
159 #endif
160         return AUDIO_U8;
161     }
162 }
163 
164 static int sdl_to_audfmt(int sdlfmt, audfmt_e *fmt, int *endianness)
165 {
166     switch (sdlfmt) {
167     case AUDIO_S8:
168         *endianness = 0;
169         *fmt = AUD_FMT_S8;
170         break;
171 
172     case AUDIO_U8:
173         *endianness = 0;
174         *fmt = AUD_FMT_U8;
175         break;
176 
177     case AUDIO_S16LSB:
178         *endianness = 0;
179         *fmt = AUD_FMT_S16;
180         break;
181 
182     case AUDIO_U16LSB:
183         *endianness = 0;
184         *fmt = AUD_FMT_U16;
185         break;
186 
187     case AUDIO_S16MSB:
188         *endianness = 1;
189         *fmt = AUD_FMT_S16;
190         break;
191 
192     case AUDIO_U16MSB:
193         *endianness = 1;
194         *fmt = AUD_FMT_U16;
195         break;
196 
197     default:
198         dolog ("Unrecognized SDL audio format %d\n", sdlfmt);
199         return -1;
200     }
201 
202     return 0;
203 }
204 
205 static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt)
206 {
207     int status;
208 #ifndef _WIN32
209     int err;
210     sigset_t new, old;
211 
212     /* Make sure potential threads created by SDL don't hog signals.  */
213     err = sigfillset (&new);
214     if (err) {
215         dolog ("sdl_open: sigfillset failed: %s\n", strerror (errno));
216         return -1;
217     }
218     err = pthread_sigmask (SIG_BLOCK, &new, &old);
219     if (err) {
220         dolog ("sdl_open: pthread_sigmask failed: %s\n", strerror (err));
221         return -1;
222     }
223 #endif
224 
225     status = SDL_OpenAudio (req, obt);
226     if (status) {
227         sdl_logerr ("SDL_OpenAudio failed\n");
228     }
229 
230 #ifndef _WIN32
231     err = pthread_sigmask (SIG_SETMASK, &old, NULL);
232     if (err) {
233         dolog ("sdl_open: pthread_sigmask (restore) failed: %s\n",
234                strerror (errno));
235         /* We have failed to restore original signal mask, all bets are off,
236            so exit the process */
237         exit (EXIT_FAILURE);
238     }
239 #endif
240     return status;
241 }
242 
243 static void sdl_close (SDLAudioState *s)
244 {
245     if (s->initialized) {
246         sdl_lock (s, "sdl_close");
247         s->exit = 1;
248         sdl_unlock_and_post (s, "sdl_close");
249         SDL_PauseAudio (1);
250         SDL_CloseAudio ();
251         s->initialized = 0;
252     }
253 }
254 
255 static void sdl_callback (void *opaque, Uint8 *buf, int len)
256 {
257     SDLVoiceOut *sdl = opaque;
258     SDLAudioState *s = &glob_sdl;
259     HWVoiceOut *hw = &sdl->hw;
260     int samples = len >> hw->info.shift;
261 
262     if (s->exit) {
263         return;
264     }
265 
266     while (samples) {
267         int to_mix, decr;
268 
269         /* dolog ("in callback samples=%d\n", samples); */
270 #if USE_SEMAPHORE
271         sdl_wait (s, "sdl_callback");
272         if (s->exit) {
273             return;
274         }
275 
276         if (sdl_lock (s, "sdl_callback")) {
277             return;
278         }
279 
280         if (audio_bug (AUDIO_FUNC, sdl->live < 0 || sdl->live > hw->samples)) {
281             dolog ("sdl->live=%d hw->samples=%d\n",
282                    sdl->live, hw->samples);
283             return;
284         }
285 
286         if (!sdl->live) {
287             goto again;
288         }
289 #else
290         if (s->exit || !sdl->live) {
291             break;
292         }
293 #endif
294 
295         /* dolog ("in callback live=%d\n", live); */
296         to_mix = audio_MIN (samples, sdl->live);
297         decr = to_mix;
298         while (to_mix) {
299             int chunk = audio_MIN (to_mix, hw->samples - hw->rpos);
300             struct st_sample *src = hw->mix_buf + hw->rpos;
301 
302             /* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */
303             hw->clip (buf, src, chunk);
304 #if USE_SEMAPHORE
305             sdl->rpos = (sdl->rpos + chunk) % hw->samples;
306 #else
307             hw->rpos = (hw->rpos + chunk) % hw->samples;
308 #endif
309             to_mix -= chunk;
310             buf += chunk << hw->info.shift;
311         }
312         samples -= decr;
313         sdl->live -= decr;
314         sdl->decr += decr;
315 
316 #if USE_SEMAPHORE
317     again:
318         if (sdl_unlock (s, "sdl_callback")) {
319             return;
320         }
321 #endif
322     }
323     /* dolog ("done len=%d\n", len); */
324 
325 #if (SDL_MAJOR_VERSION >= 2)
326     /* SDL2 does not clear the remaining buffer for us, so do it on our own */
327     if (samples) {
328         memset(buf, 0, samples << hw->info.shift);
329     }
330 #endif
331 }
332 
333 static int sdl_write_out (SWVoiceOut *sw, void *buf, int len)
334 {
335     return audio_pcm_sw_write (sw, buf, len);
336 }
337 
338 static int sdl_run_out (HWVoiceOut *hw, int live)
339 {
340     int decr;
341     SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
342     SDLAudioState *s = &glob_sdl;
343 
344     if (sdl_lock (s, "sdl_run_out")) {
345         return 0;
346     }
347 
348     if (sdl->decr > live) {
349         ldebug ("sdl->decr %d live %d sdl->live %d\n",
350                 sdl->decr,
351                 live,
352                 sdl->live);
353     }
354 
355     decr = audio_MIN (sdl->decr, live);
356     sdl->decr -= decr;
357 
358 #if USE_SEMAPHORE
359     sdl->live = live - decr;
360     hw->rpos = sdl->rpos;
361 #else
362     sdl->live = live;
363 #endif
364 
365     if (sdl->live > 0) {
366         sdl_unlock_and_post (s, "sdl_run_out");
367     }
368     else {
369         sdl_unlock (s, "sdl_run_out");
370     }
371     return decr;
372 }
373 
374 static void sdl_fini_out (HWVoiceOut *hw)
375 {
376     (void) hw;
377 
378     sdl_close (&glob_sdl);
379 }
380 
381 static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as,
382                         void *drv_opaque)
383 {
384     SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
385     SDLAudioState *s = &glob_sdl;
386     SDL_AudioSpec req, obt;
387     int endianness;
388     int err;
389     audfmt_e effective_fmt;
390     struct audsettings obt_as;
391 
392     req.freq = as->freq;
393     req.format = aud_to_sdlfmt (as->fmt);
394     req.channels = as->nchannels;
395     req.samples = conf.nb_samples;
396     req.callback = sdl_callback;
397     req.userdata = sdl;
398 
399     if (sdl_open (&req, &obt)) {
400         return -1;
401     }
402 
403     err = sdl_to_audfmt(obt.format, &effective_fmt, &endianness);
404     if (err) {
405         sdl_close (s);
406         return -1;
407     }
408 
409     obt_as.freq = obt.freq;
410     obt_as.nchannels = obt.channels;
411     obt_as.fmt = effective_fmt;
412     obt_as.endianness = endianness;
413 
414     audio_pcm_init_info (&hw->info, &obt_as);
415     hw->samples = obt.samples;
416 
417     s->initialized = 1;
418     s->exit = 0;
419     SDL_PauseAudio (0);
420     return 0;
421 }
422 
423 static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...)
424 {
425     (void) hw;
426 
427     switch (cmd) {
428     case VOICE_ENABLE:
429         SDL_PauseAudio (0);
430         break;
431 
432     case VOICE_DISABLE:
433         SDL_PauseAudio (1);
434         break;
435     }
436     return 0;
437 }
438 
439 static void *sdl_audio_init (void)
440 {
441     SDLAudioState *s = &glob_sdl;
442     if (s->driver_created) {
443         sdl_logerr("Can't create multiple sdl backends\n");
444         return NULL;
445     }
446 
447     if (SDL_InitSubSystem (SDL_INIT_AUDIO)) {
448         sdl_logerr ("SDL failed to initialize audio subsystem\n");
449         return NULL;
450     }
451 
452 #if USE_SEMAPHORE
453     s->mutex = SDL_CreateMutex ();
454     if (!s->mutex) {
455         sdl_logerr ("Failed to create SDL mutex\n");
456         SDL_QuitSubSystem (SDL_INIT_AUDIO);
457         return NULL;
458     }
459 
460     s->sem = SDL_CreateSemaphore (0);
461     if (!s->sem) {
462         sdl_logerr ("Failed to create SDL semaphore\n");
463         SDL_DestroyMutex (s->mutex);
464         SDL_QuitSubSystem (SDL_INIT_AUDIO);
465         return NULL;
466     }
467 #endif
468 
469     s->driver_created = true;
470     return s;
471 }
472 
473 static void sdl_audio_fini (void *opaque)
474 {
475     SDLAudioState *s = opaque;
476     sdl_close (s);
477 #if USE_SEMAPHORE
478     SDL_DestroySemaphore (s->sem);
479     SDL_DestroyMutex (s->mutex);
480 #endif
481     SDL_QuitSubSystem (SDL_INIT_AUDIO);
482     s->driver_created = false;
483 }
484 
485 static struct audio_option sdl_options[] = {
486     {
487         .name  = "SAMPLES",
488         .tag   = AUD_OPT_INT,
489         .valp  = &conf.nb_samples,
490         .descr = "Size of SDL buffer in samples"
491     },
492     { /* End of list */ }
493 };
494 
495 static struct audio_pcm_ops sdl_pcm_ops = {
496     .init_out = sdl_init_out,
497     .fini_out = sdl_fini_out,
498     .run_out  = sdl_run_out,
499     .write    = sdl_write_out,
500     .ctl_out  = sdl_ctl_out,
501 };
502 
503 struct audio_driver sdl_audio_driver = {
504     .name           = "sdl",
505     .descr          = "SDL http://www.libsdl.org",
506     .options        = sdl_options,
507     .init           = sdl_audio_init,
508     .fini           = sdl_audio_fini,
509     .pcm_ops        = &sdl_pcm_ops,
510     .can_be_default = 1,
511     .max_voices_out = 1,
512     .max_voices_in  = 0,
513     .voice_size_out = sizeof (SDLVoiceOut),
514     .voice_size_in  = 0
515 };
516