xref: /openbmc/qemu/audio/sdlaudio.c (revision 85571bc7)
1*85571bc7Sbellard /*
2*85571bc7Sbellard  * QEMU SDL audio output driver
3*85571bc7Sbellard  *
4*85571bc7Sbellard  * Copyright (c) 2004 Vassili Karpov (malc)
5*85571bc7Sbellard  *
6*85571bc7Sbellard  * Permission is hereby granted, free of charge, to any person obtaining a copy
7*85571bc7Sbellard  * of this software and associated documentation files (the "Software"), to deal
8*85571bc7Sbellard  * in the Software without restriction, including without limitation the rights
9*85571bc7Sbellard  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10*85571bc7Sbellard  * copies of the Software, and to permit persons to whom the Software is
11*85571bc7Sbellard  * furnished to do so, subject to the following conditions:
12*85571bc7Sbellard  *
13*85571bc7Sbellard  * The above copyright notice and this permission notice shall be included in
14*85571bc7Sbellard  * all copies or substantial portions of the Software.
15*85571bc7Sbellard  *
16*85571bc7Sbellard  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17*85571bc7Sbellard  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18*85571bc7Sbellard  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19*85571bc7Sbellard  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20*85571bc7Sbellard  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21*85571bc7Sbellard  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22*85571bc7Sbellard  * THE SOFTWARE.
23*85571bc7Sbellard  */
24*85571bc7Sbellard #include <SDL/SDL.h>
25*85571bc7Sbellard #include <SDL/SDL_thread.h>
26*85571bc7Sbellard #include "vl.h"
27*85571bc7Sbellard 
28*85571bc7Sbellard #define AUDIO_CAP "sdl"
29*85571bc7Sbellard #include "audio/audio.h"
30*85571bc7Sbellard #include "audio/sdlaudio.h"
31*85571bc7Sbellard 
32*85571bc7Sbellard #define QC_SDL_SAMPLES "QEMU_SDL_SAMPLES"
33*85571bc7Sbellard 
34*85571bc7Sbellard #define errstr() SDL_GetError ()
35*85571bc7Sbellard 
36*85571bc7Sbellard static struct {
37*85571bc7Sbellard     int nb_samples;
38*85571bc7Sbellard } conf = {
39*85571bc7Sbellard     1024
40*85571bc7Sbellard };
41*85571bc7Sbellard 
42*85571bc7Sbellard struct SDLAudioState {
43*85571bc7Sbellard     int exit;
44*85571bc7Sbellard     SDL_mutex *mutex;
45*85571bc7Sbellard     SDL_sem *sem;
46*85571bc7Sbellard     int initialized;
47*85571bc7Sbellard } glob_sdl;
48*85571bc7Sbellard typedef struct SDLAudioState SDLAudioState;
49*85571bc7Sbellard 
50*85571bc7Sbellard static void sdl_hw_run (HWVoice *hw)
51*85571bc7Sbellard {
52*85571bc7Sbellard     (void) hw;
53*85571bc7Sbellard }
54*85571bc7Sbellard 
55*85571bc7Sbellard static int sdl_lock (SDLAudioState *s)
56*85571bc7Sbellard {
57*85571bc7Sbellard     if (SDL_LockMutex (s->mutex)) {
58*85571bc7Sbellard         dolog ("SDL_LockMutex failed\nReason: %s\n", errstr ());
59*85571bc7Sbellard         return -1;
60*85571bc7Sbellard     }
61*85571bc7Sbellard     return 0;
62*85571bc7Sbellard }
63*85571bc7Sbellard 
64*85571bc7Sbellard static int sdl_unlock (SDLAudioState *s)
65*85571bc7Sbellard {
66*85571bc7Sbellard     if (SDL_UnlockMutex (s->mutex)) {
67*85571bc7Sbellard         dolog ("SDL_UnlockMutex failed\nReason: %s\n", errstr ());
68*85571bc7Sbellard         return -1;
69*85571bc7Sbellard     }
70*85571bc7Sbellard     return 0;
71*85571bc7Sbellard }
72*85571bc7Sbellard 
73*85571bc7Sbellard static int sdl_post (SDLAudioState *s)
74*85571bc7Sbellard {
75*85571bc7Sbellard     if (SDL_SemPost (s->sem)) {
76*85571bc7Sbellard         dolog ("SDL_SemPost failed\nReason: %s\n", errstr ());
77*85571bc7Sbellard         return -1;
78*85571bc7Sbellard     }
79*85571bc7Sbellard     return 0;
80*85571bc7Sbellard }
81*85571bc7Sbellard 
82*85571bc7Sbellard static int sdl_wait (SDLAudioState *s)
83*85571bc7Sbellard {
84*85571bc7Sbellard     if (SDL_SemWait (s->sem)) {
85*85571bc7Sbellard         dolog ("SDL_SemWait failed\nReason: %s\n", errstr ());
86*85571bc7Sbellard         return -1;
87*85571bc7Sbellard     }
88*85571bc7Sbellard     return 0;
89*85571bc7Sbellard }
90*85571bc7Sbellard 
91*85571bc7Sbellard static int sdl_unlock_and_post (SDLAudioState *s)
92*85571bc7Sbellard {
93*85571bc7Sbellard     if (sdl_unlock (s))
94*85571bc7Sbellard         return -1;
95*85571bc7Sbellard 
96*85571bc7Sbellard     return sdl_post (s);
97*85571bc7Sbellard }
98*85571bc7Sbellard 
99*85571bc7Sbellard static int sdl_hw_write (SWVoice *sw, void *buf, int len)
100*85571bc7Sbellard {
101*85571bc7Sbellard     int ret;
102*85571bc7Sbellard     SDLAudioState *s = &glob_sdl;
103*85571bc7Sbellard     sdl_lock (s);
104*85571bc7Sbellard     ret = pcm_hw_write (sw, buf, len);
105*85571bc7Sbellard     sdl_unlock_and_post (s);
106*85571bc7Sbellard     return ret;
107*85571bc7Sbellard }
108*85571bc7Sbellard 
109*85571bc7Sbellard static int AUD_to_sdlfmt (audfmt_e fmt, int *shift)
110*85571bc7Sbellard {
111*85571bc7Sbellard     *shift = 0;
112*85571bc7Sbellard     switch (fmt) {
113*85571bc7Sbellard     case AUD_FMT_S8: return AUDIO_S8;
114*85571bc7Sbellard     case AUD_FMT_U8: return AUDIO_U8;
115*85571bc7Sbellard     case AUD_FMT_S16: *shift = 1; return AUDIO_S16LSB;
116*85571bc7Sbellard     case AUD_FMT_U16: *shift = 1; return AUDIO_U16LSB;
117*85571bc7Sbellard     default:
118*85571bc7Sbellard         dolog ("Internal logic error: Bad audio format %d\nAborting\n", fmt);
119*85571bc7Sbellard         exit (EXIT_FAILURE);
120*85571bc7Sbellard     }
121*85571bc7Sbellard }
122*85571bc7Sbellard 
123*85571bc7Sbellard static int sdl_to_audfmt (int fmt)
124*85571bc7Sbellard {
125*85571bc7Sbellard     switch (fmt) {
126*85571bc7Sbellard     case AUDIO_S8: return AUD_FMT_S8;
127*85571bc7Sbellard     case AUDIO_U8: return AUD_FMT_U8;
128*85571bc7Sbellard     case AUDIO_S16LSB: return AUD_FMT_S16;
129*85571bc7Sbellard     case AUDIO_U16LSB: return AUD_FMT_U16;
130*85571bc7Sbellard     default:
131*85571bc7Sbellard         dolog ("Internal logic error: Unrecognized SDL audio format %d\n"
132*85571bc7Sbellard                "Aborting\n", fmt);
133*85571bc7Sbellard         exit (EXIT_FAILURE);
134*85571bc7Sbellard     }
135*85571bc7Sbellard }
136*85571bc7Sbellard 
137*85571bc7Sbellard static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt)
138*85571bc7Sbellard {
139*85571bc7Sbellard     int status;
140*85571bc7Sbellard 
141*85571bc7Sbellard     status = SDL_OpenAudio (req, obt);
142*85571bc7Sbellard     if (status) {
143*85571bc7Sbellard         dolog ("SDL_OpenAudio failed\nReason: %s\n", errstr ());
144*85571bc7Sbellard     }
145*85571bc7Sbellard     return status;
146*85571bc7Sbellard }
147*85571bc7Sbellard 
148*85571bc7Sbellard static void sdl_close (SDLAudioState *s)
149*85571bc7Sbellard {
150*85571bc7Sbellard     if (s->initialized) {
151*85571bc7Sbellard         sdl_lock (s);
152*85571bc7Sbellard         s->exit = 1;
153*85571bc7Sbellard         sdl_unlock_and_post (s);
154*85571bc7Sbellard         SDL_PauseAudio (1);
155*85571bc7Sbellard         SDL_CloseAudio ();
156*85571bc7Sbellard         s->initialized = 0;
157*85571bc7Sbellard     }
158*85571bc7Sbellard }
159*85571bc7Sbellard 
160*85571bc7Sbellard static void sdl_callback (void *opaque, Uint8 *buf, int len)
161*85571bc7Sbellard {
162*85571bc7Sbellard     SDLVoice *sdl = opaque;
163*85571bc7Sbellard     SDLAudioState *s = &glob_sdl;
164*85571bc7Sbellard     HWVoice *hw = &sdl->hw;
165*85571bc7Sbellard     int samples = len >> hw->shift;
166*85571bc7Sbellard 
167*85571bc7Sbellard     if (s->exit) {
168*85571bc7Sbellard         return;
169*85571bc7Sbellard     }
170*85571bc7Sbellard 
171*85571bc7Sbellard     while (samples) {
172*85571bc7Sbellard         int to_mix, live, decr;
173*85571bc7Sbellard 
174*85571bc7Sbellard         /* dolog ("in callback samples=%d\n", samples); */
175*85571bc7Sbellard         sdl_wait (s);
176*85571bc7Sbellard         if (s->exit) {
177*85571bc7Sbellard             return;
178*85571bc7Sbellard         }
179*85571bc7Sbellard 
180*85571bc7Sbellard         sdl_lock (s);
181*85571bc7Sbellard         live = pcm_hw_get_live (hw);
182*85571bc7Sbellard         if (live <= 0)
183*85571bc7Sbellard             goto again;
184*85571bc7Sbellard 
185*85571bc7Sbellard         /* dolog ("in callback live=%d\n", live); */
186*85571bc7Sbellard         to_mix = audio_MIN (samples, live);
187*85571bc7Sbellard         decr = to_mix;
188*85571bc7Sbellard         while (to_mix) {
189*85571bc7Sbellard             int chunk = audio_MIN (to_mix, hw->samples - hw->rpos);
190*85571bc7Sbellard             st_sample_t *src = hw->mix_buf + hw->rpos;
191*85571bc7Sbellard 
192*85571bc7Sbellard             /* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */
193*85571bc7Sbellard             hw->clip (buf, src, chunk);
194*85571bc7Sbellard             memset (src, 0, chunk * sizeof (st_sample_t));
195*85571bc7Sbellard             hw->rpos = (hw->rpos + chunk) % hw->samples;
196*85571bc7Sbellard             to_mix -= chunk;
197*85571bc7Sbellard             buf += chunk << hw->shift;
198*85571bc7Sbellard         }
199*85571bc7Sbellard         samples -= decr;
200*85571bc7Sbellard         pcm_hw_dec_live (hw, decr);
201*85571bc7Sbellard 
202*85571bc7Sbellard     again:
203*85571bc7Sbellard         sdl_unlock (s);
204*85571bc7Sbellard     }
205*85571bc7Sbellard     /* dolog ("done len=%d\n", len); */
206*85571bc7Sbellard }
207*85571bc7Sbellard 
208*85571bc7Sbellard static void sdl_hw_fini (HWVoice *hw)
209*85571bc7Sbellard {
210*85571bc7Sbellard     ldebug ("sdl_hw_fini %d fixed=%d\n",
211*85571bc7Sbellard              glob_sdl.initialized, audio_conf.fixed_format);
212*85571bc7Sbellard     sdl_close (&glob_sdl);
213*85571bc7Sbellard }
214*85571bc7Sbellard 
215*85571bc7Sbellard static int sdl_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt)
216*85571bc7Sbellard {
217*85571bc7Sbellard     SDLVoice *sdl = (SDLVoice *) hw;
218*85571bc7Sbellard     SDLAudioState *s = &glob_sdl;
219*85571bc7Sbellard     SDL_AudioSpec req, obt;
220*85571bc7Sbellard     int shift;
221*85571bc7Sbellard 
222*85571bc7Sbellard     ldebug ("sdl_hw_init %d freq=%d fixed=%d\n",
223*85571bc7Sbellard             s->initialized, freq, audio_conf.fixed_format);
224*85571bc7Sbellard 
225*85571bc7Sbellard     if (nchannels != 2) {
226*85571bc7Sbellard         dolog ("Bogus channel count %d\n", nchannels);
227*85571bc7Sbellard         return -1;
228*85571bc7Sbellard     }
229*85571bc7Sbellard 
230*85571bc7Sbellard     req.freq = freq;
231*85571bc7Sbellard     req.format = AUD_to_sdlfmt (fmt, &shift);
232*85571bc7Sbellard     req.channels = nchannels;
233*85571bc7Sbellard     req.samples = conf.nb_samples;
234*85571bc7Sbellard     shift <<= nchannels == 2;
235*85571bc7Sbellard 
236*85571bc7Sbellard     req.callback = sdl_callback;
237*85571bc7Sbellard     req.userdata = sdl;
238*85571bc7Sbellard 
239*85571bc7Sbellard     if (sdl_open (&req, &obt))
240*85571bc7Sbellard         return -1;
241*85571bc7Sbellard 
242*85571bc7Sbellard     hw->freq = obt.freq;
243*85571bc7Sbellard     hw->fmt = sdl_to_audfmt (obt.format);
244*85571bc7Sbellard     hw->nchannels = obt.channels;
245*85571bc7Sbellard     hw->bufsize = obt.samples << shift;
246*85571bc7Sbellard 
247*85571bc7Sbellard     s->initialized = 1;
248*85571bc7Sbellard     s->exit = 0;
249*85571bc7Sbellard     SDL_PauseAudio (0);
250*85571bc7Sbellard     return 0;
251*85571bc7Sbellard }
252*85571bc7Sbellard 
253*85571bc7Sbellard static int sdl_hw_ctl (HWVoice *hw, int cmd, ...)
254*85571bc7Sbellard {
255*85571bc7Sbellard     (void) hw;
256*85571bc7Sbellard 
257*85571bc7Sbellard     switch (cmd) {
258*85571bc7Sbellard     case VOICE_ENABLE:
259*85571bc7Sbellard         SDL_PauseAudio (0);
260*85571bc7Sbellard         break;
261*85571bc7Sbellard 
262*85571bc7Sbellard     case VOICE_DISABLE:
263*85571bc7Sbellard         SDL_PauseAudio (1);
264*85571bc7Sbellard         break;
265*85571bc7Sbellard     }
266*85571bc7Sbellard     return 0;
267*85571bc7Sbellard }
268*85571bc7Sbellard 
269*85571bc7Sbellard static void *sdl_audio_init (void)
270*85571bc7Sbellard {
271*85571bc7Sbellard     SDLAudioState *s = &glob_sdl;
272*85571bc7Sbellard     conf.nb_samples = audio_get_conf_int (QC_SDL_SAMPLES, conf.nb_samples);
273*85571bc7Sbellard 
274*85571bc7Sbellard     if (SDL_InitSubSystem (SDL_INIT_AUDIO)) {
275*85571bc7Sbellard         dolog ("SDL failed to initialize audio subsystem\nReason: %s\n",
276*85571bc7Sbellard                errstr ());
277*85571bc7Sbellard         return NULL;
278*85571bc7Sbellard     }
279*85571bc7Sbellard 
280*85571bc7Sbellard     s->mutex = SDL_CreateMutex ();
281*85571bc7Sbellard     if (!s->mutex) {
282*85571bc7Sbellard         dolog ("Failed to create SDL mutex\nReason: %s\n", errstr ());
283*85571bc7Sbellard         SDL_QuitSubSystem (SDL_INIT_AUDIO);
284*85571bc7Sbellard         return NULL;
285*85571bc7Sbellard     }
286*85571bc7Sbellard 
287*85571bc7Sbellard     s->sem = SDL_CreateSemaphore (0);
288*85571bc7Sbellard     if (!s->sem) {
289*85571bc7Sbellard         dolog ("Failed to create SDL semaphore\nReason: %s\n", errstr ());
290*85571bc7Sbellard         SDL_DestroyMutex (s->mutex);
291*85571bc7Sbellard         SDL_QuitSubSystem (SDL_INIT_AUDIO);
292*85571bc7Sbellard         return NULL;
293*85571bc7Sbellard     }
294*85571bc7Sbellard 
295*85571bc7Sbellard     return s;
296*85571bc7Sbellard }
297*85571bc7Sbellard 
298*85571bc7Sbellard static void sdl_audio_fini (void *opaque)
299*85571bc7Sbellard {
300*85571bc7Sbellard     SDLAudioState *s = opaque;
301*85571bc7Sbellard     sdl_close (s);
302*85571bc7Sbellard     SDL_DestroySemaphore (s->sem);
303*85571bc7Sbellard     SDL_DestroyMutex (s->mutex);
304*85571bc7Sbellard     SDL_QuitSubSystem (SDL_INIT_AUDIO);
305*85571bc7Sbellard }
306*85571bc7Sbellard 
307*85571bc7Sbellard struct pcm_ops sdl_pcm_ops = {
308*85571bc7Sbellard     sdl_hw_init,
309*85571bc7Sbellard     sdl_hw_fini,
310*85571bc7Sbellard     sdl_hw_run,
311*85571bc7Sbellard     sdl_hw_write,
312*85571bc7Sbellard     sdl_hw_ctl
313*85571bc7Sbellard };
314*85571bc7Sbellard 
315*85571bc7Sbellard struct audio_output_driver sdl_output_driver = {
316*85571bc7Sbellard     "sdl",
317*85571bc7Sbellard     sdl_audio_init,
318*85571bc7Sbellard     sdl_audio_fini,
319*85571bc7Sbellard     &sdl_pcm_ops,
320*85571bc7Sbellard     1,
321*85571bc7Sbellard     1,
322*85571bc7Sbellard     sizeof (SDLVoice)
323*85571bc7Sbellard };
324