xref: /openbmc/qemu/audio/sdlaudio.c (revision b097ba37)
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 typedef struct SDLVoiceOut {
42     HWVoiceOut hw;
43     int live;
44     int decr;
45 } SDLVoiceOut;
46 
47 static struct SDLAudioState {
48     int exit;
49     int initialized;
50     bool driver_created;
51     Audiodev *dev;
52 } glob_sdl;
53 typedef struct SDLAudioState SDLAudioState;
54 
55 static void GCC_FMT_ATTR (1, 2) sdl_logerr (const char *fmt, ...)
56 {
57     va_list ap;
58 
59     va_start (ap, fmt);
60     AUD_vlog (AUDIO_CAP, fmt, ap);
61     va_end (ap);
62 
63     AUD_log (AUDIO_CAP, "Reason: %s\n", SDL_GetError ());
64 }
65 
66 static int aud_to_sdlfmt (AudioFormat fmt)
67 {
68     switch (fmt) {
69     case AUDIO_FORMAT_S8:
70         return AUDIO_S8;
71 
72     case AUDIO_FORMAT_U8:
73         return AUDIO_U8;
74 
75     case AUDIO_FORMAT_S16:
76         return AUDIO_S16LSB;
77 
78     case AUDIO_FORMAT_U16:
79         return AUDIO_U16LSB;
80 
81     default:
82         dolog ("Internal logic error: Bad audio format %d\n", fmt);
83 #ifdef DEBUG_AUDIO
84         abort ();
85 #endif
86         return AUDIO_U8;
87     }
88 }
89 
90 static int sdl_to_audfmt(int sdlfmt, AudioFormat *fmt, int *endianness)
91 {
92     switch (sdlfmt) {
93     case AUDIO_S8:
94         *endianness = 0;
95         *fmt = AUDIO_FORMAT_S8;
96         break;
97 
98     case AUDIO_U8:
99         *endianness = 0;
100         *fmt = AUDIO_FORMAT_U8;
101         break;
102 
103     case AUDIO_S16LSB:
104         *endianness = 0;
105         *fmt = AUDIO_FORMAT_S16;
106         break;
107 
108     case AUDIO_U16LSB:
109         *endianness = 0;
110         *fmt = AUDIO_FORMAT_U16;
111         break;
112 
113     case AUDIO_S16MSB:
114         *endianness = 1;
115         *fmt = AUDIO_FORMAT_S16;
116         break;
117 
118     case AUDIO_U16MSB:
119         *endianness = 1;
120         *fmt = AUDIO_FORMAT_U16;
121         break;
122 
123     default:
124         dolog ("Unrecognized SDL audio format %d\n", sdlfmt);
125         return -1;
126     }
127 
128     return 0;
129 }
130 
131 static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt)
132 {
133     int status;
134 #ifndef _WIN32
135     int err;
136     sigset_t new, old;
137 
138     /* Make sure potential threads created by SDL don't hog signals.  */
139     err = sigfillset (&new);
140     if (err) {
141         dolog ("sdl_open: sigfillset failed: %s\n", strerror (errno));
142         return -1;
143     }
144     err = pthread_sigmask (SIG_BLOCK, &new, &old);
145     if (err) {
146         dolog ("sdl_open: pthread_sigmask failed: %s\n", strerror (err));
147         return -1;
148     }
149 #endif
150 
151     status = SDL_OpenAudio (req, obt);
152     if (status) {
153         sdl_logerr ("SDL_OpenAudio failed\n");
154     }
155 
156 #ifndef _WIN32
157     err = pthread_sigmask (SIG_SETMASK, &old, NULL);
158     if (err) {
159         dolog ("sdl_open: pthread_sigmask (restore) failed: %s\n",
160                strerror (errno));
161         /* We have failed to restore original signal mask, all bets are off,
162            so exit the process */
163         exit (EXIT_FAILURE);
164     }
165 #endif
166     return status;
167 }
168 
169 static void sdl_close (SDLAudioState *s)
170 {
171     if (s->initialized) {
172         SDL_LockAudio();
173         s->exit = 1;
174         SDL_UnlockAudio();
175         SDL_PauseAudio (1);
176         SDL_CloseAudio ();
177         s->initialized = 0;
178     }
179 }
180 
181 static void sdl_callback (void *opaque, Uint8 *buf, int len)
182 {
183     SDLVoiceOut *sdl = opaque;
184     SDLAudioState *s = &glob_sdl;
185     HWVoiceOut *hw = &sdl->hw;
186     int samples = len >> hw->info.shift;
187     int to_mix, decr;
188 
189     if (s->exit || !sdl->live) {
190         return;
191     }
192 
193     /* dolog ("in callback samples=%d live=%d\n", samples, sdl->live); */
194 
195     to_mix = audio_MIN(samples, sdl->live);
196     decr = to_mix;
197     while (to_mix) {
198         int chunk = audio_MIN(to_mix, hw->samples - hw->rpos);
199         struct st_sample *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         hw->rpos = (hw->rpos + chunk) % hw->samples;
204         to_mix -= chunk;
205         buf += chunk << hw->info.shift;
206     }
207     samples -= decr;
208     sdl->live -= decr;
209     sdl->decr += decr;
210 
211     /* dolog ("done len=%d\n", len); */
212 
213     /* SDL2 does not clear the remaining buffer for us, so do it on our own */
214     if (samples) {
215         memset(buf, 0, samples << hw->info.shift);
216     }
217 }
218 
219 static int sdl_write_out (SWVoiceOut *sw, void *buf, int len)
220 {
221     return audio_pcm_sw_write (sw, buf, len);
222 }
223 
224 static int sdl_run_out (HWVoiceOut *hw, int live)
225 {
226     int decr;
227     SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
228 
229     SDL_LockAudio();
230 
231     if (sdl->decr > live) {
232         ldebug ("sdl->decr %d live %d sdl->live %d\n",
233                 sdl->decr,
234                 live,
235                 sdl->live);
236     }
237 
238     decr = audio_MIN (sdl->decr, live);
239     sdl->decr -= decr;
240 
241     sdl->live = live;
242 
243     SDL_UnlockAudio();
244 
245     return decr;
246 }
247 
248 static void sdl_fini_out (HWVoiceOut *hw)
249 {
250     (void) hw;
251 
252     sdl_close (&glob_sdl);
253 }
254 
255 static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as,
256                         void *drv_opaque)
257 {
258     SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
259     SDLAudioState *s = &glob_sdl;
260     SDL_AudioSpec req, obt;
261     int endianness;
262     int err;
263     AudioFormat effective_fmt;
264     struct audsettings obt_as;
265 
266     req.freq = as->freq;
267     req.format = aud_to_sdlfmt (as->fmt);
268     req.channels = as->nchannels;
269     req.samples = audio_buffer_samples(s->dev->u.sdl.out, as, 11610);
270     req.callback = sdl_callback;
271     req.userdata = sdl;
272 
273     if (sdl_open (&req, &obt)) {
274         return -1;
275     }
276 
277     err = sdl_to_audfmt(obt.format, &effective_fmt, &endianness);
278     if (err) {
279         sdl_close (s);
280         return -1;
281     }
282 
283     obt_as.freq = obt.freq;
284     obt_as.nchannels = obt.channels;
285     obt_as.fmt = effective_fmt;
286     obt_as.endianness = endianness;
287 
288     audio_pcm_init_info (&hw->info, &obt_as);
289     hw->samples = obt.samples;
290 
291     s->initialized = 1;
292     s->exit = 0;
293     SDL_PauseAudio (0);
294     return 0;
295 }
296 
297 static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...)
298 {
299     (void) hw;
300 
301     switch (cmd) {
302     case VOICE_ENABLE:
303         SDL_PauseAudio (0);
304         break;
305 
306     case VOICE_DISABLE:
307         SDL_PauseAudio (1);
308         break;
309     }
310     return 0;
311 }
312 
313 static void *sdl_audio_init(Audiodev *dev)
314 {
315     SDLAudioState *s = &glob_sdl;
316     if (s->driver_created) {
317         sdl_logerr("Can't create multiple sdl backends\n");
318         return NULL;
319     }
320 
321     if (SDL_InitSubSystem (SDL_INIT_AUDIO)) {
322         sdl_logerr ("SDL failed to initialize audio subsystem\n");
323         return NULL;
324     }
325 
326     s->driver_created = true;
327     s->dev = dev;
328     return s;
329 }
330 
331 static void sdl_audio_fini (void *opaque)
332 {
333     SDLAudioState *s = opaque;
334     sdl_close (s);
335     SDL_QuitSubSystem (SDL_INIT_AUDIO);
336     s->driver_created = false;
337     s->dev = NULL;
338 }
339 
340 static struct audio_pcm_ops sdl_pcm_ops = {
341     .init_out = sdl_init_out,
342     .fini_out = sdl_fini_out,
343     .run_out  = sdl_run_out,
344     .write    = sdl_write_out,
345     .ctl_out  = sdl_ctl_out,
346 };
347 
348 static struct audio_driver sdl_audio_driver = {
349     .name           = "sdl",
350     .descr          = "SDL http://www.libsdl.org",
351     .init           = sdl_audio_init,
352     .fini           = sdl_audio_fini,
353     .pcm_ops        = &sdl_pcm_ops,
354     .can_be_default = 1,
355     .max_voices_out = 1,
356     .max_voices_in  = 0,
357     .voice_size_out = sizeof (SDLVoiceOut),
358     .voice_size_in  = 0
359 };
360 
361 static void register_audio_sdl(void)
362 {
363     audio_driver_register(&sdl_audio_driver);
364 }
365 type_init(register_audio_sdl);
366