]> git.proxmox.com Git - qemu.git/blob - audio/sdlaudio.c
audio merge (malc)
[qemu.git] / audio / sdlaudio.c
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/SDL.h>
25 #include <SDL/SDL_thread.h>
26 #include "vl.h"
27
28 #define AUDIO_CAP "sdl"
29 #include "audio/audio.h"
30 #include "audio/sdlaudio.h"
31
32 #define QC_SDL_SAMPLES "QEMU_SDL_SAMPLES"
33
34 #define errstr() SDL_GetError ()
35
36 static struct {
37 int nb_samples;
38 } conf = {
39 1024
40 };
41
42 struct SDLAudioState {
43 int exit;
44 SDL_mutex *mutex;
45 SDL_sem *sem;
46 int initialized;
47 } glob_sdl;
48 typedef struct SDLAudioState SDLAudioState;
49
50 static void sdl_hw_run (HWVoice *hw)
51 {
52 (void) hw;
53 }
54
55 static int sdl_lock (SDLAudioState *s)
56 {
57 if (SDL_LockMutex (s->mutex)) {
58 dolog ("SDL_LockMutex failed\nReason: %s\n", errstr ());
59 return -1;
60 }
61 return 0;
62 }
63
64 static int sdl_unlock (SDLAudioState *s)
65 {
66 if (SDL_UnlockMutex (s->mutex)) {
67 dolog ("SDL_UnlockMutex failed\nReason: %s\n", errstr ());
68 return -1;
69 }
70 return 0;
71 }
72
73 static int sdl_post (SDLAudioState *s)
74 {
75 if (SDL_SemPost (s->sem)) {
76 dolog ("SDL_SemPost failed\nReason: %s\n", errstr ());
77 return -1;
78 }
79 return 0;
80 }
81
82 static int sdl_wait (SDLAudioState *s)
83 {
84 if (SDL_SemWait (s->sem)) {
85 dolog ("SDL_SemWait failed\nReason: %s\n", errstr ());
86 return -1;
87 }
88 return 0;
89 }
90
91 static int sdl_unlock_and_post (SDLAudioState *s)
92 {
93 if (sdl_unlock (s))
94 return -1;
95
96 return sdl_post (s);
97 }
98
99 static int sdl_hw_write (SWVoice *sw, void *buf, int len)
100 {
101 int ret;
102 SDLAudioState *s = &glob_sdl;
103 sdl_lock (s);
104 ret = pcm_hw_write (sw, buf, len);
105 sdl_unlock_and_post (s);
106 return ret;
107 }
108
109 static int AUD_to_sdlfmt (audfmt_e fmt, int *shift)
110 {
111 *shift = 0;
112 switch (fmt) {
113 case AUD_FMT_S8: return AUDIO_S8;
114 case AUD_FMT_U8: return AUDIO_U8;
115 case AUD_FMT_S16: *shift = 1; return AUDIO_S16LSB;
116 case AUD_FMT_U16: *shift = 1; return AUDIO_U16LSB;
117 default:
118 dolog ("Internal logic error: Bad audio format %d\nAborting\n", fmt);
119 exit (EXIT_FAILURE);
120 }
121 }
122
123 static int sdl_to_audfmt (int fmt)
124 {
125 switch (fmt) {
126 case AUDIO_S8: return AUD_FMT_S8;
127 case AUDIO_U8: return AUD_FMT_U8;
128 case AUDIO_S16LSB: return AUD_FMT_S16;
129 case AUDIO_U16LSB: return AUD_FMT_U16;
130 default:
131 dolog ("Internal logic error: Unrecognized SDL audio format %d\n"
132 "Aborting\n", fmt);
133 exit (EXIT_FAILURE);
134 }
135 }
136
137 static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt)
138 {
139 int status;
140
141 status = SDL_OpenAudio (req, obt);
142 if (status) {
143 dolog ("SDL_OpenAudio failed\nReason: %s\n", errstr ());
144 }
145 return status;
146 }
147
148 static void sdl_close (SDLAudioState *s)
149 {
150 if (s->initialized) {
151 sdl_lock (s);
152 s->exit = 1;
153 sdl_unlock_and_post (s);
154 SDL_PauseAudio (1);
155 SDL_CloseAudio ();
156 s->initialized = 0;
157 }
158 }
159
160 static void sdl_callback (void *opaque, Uint8 *buf, int len)
161 {
162 SDLVoice *sdl = opaque;
163 SDLAudioState *s = &glob_sdl;
164 HWVoice *hw = &sdl->hw;
165 int samples = len >> hw->shift;
166
167 if (s->exit) {
168 return;
169 }
170
171 while (samples) {
172 int to_mix, live, decr;
173
174 /* dolog ("in callback samples=%d\n", samples); */
175 sdl_wait (s);
176 if (s->exit) {
177 return;
178 }
179
180 sdl_lock (s);
181 live = pcm_hw_get_live (hw);
182 if (live <= 0)
183 goto again;
184
185 /* dolog ("in callback live=%d\n", live); */
186 to_mix = audio_MIN (samples, live);
187 decr = to_mix;
188 while (to_mix) {
189 int chunk = audio_MIN (to_mix, hw->samples - hw->rpos);
190 st_sample_t *src = hw->mix_buf + hw->rpos;
191
192 /* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */
193 hw->clip (buf, src, chunk);
194 memset (src, 0, chunk * sizeof (st_sample_t));
195 hw->rpos = (hw->rpos + chunk) % hw->samples;
196 to_mix -= chunk;
197 buf += chunk << hw->shift;
198 }
199 samples -= decr;
200 pcm_hw_dec_live (hw, decr);
201
202 again:
203 sdl_unlock (s);
204 }
205 /* dolog ("done len=%d\n", len); */
206 }
207
208 static void sdl_hw_fini (HWVoice *hw)
209 {
210 ldebug ("sdl_hw_fini %d fixed=%d\n",
211 glob_sdl.initialized, audio_conf.fixed_format);
212 sdl_close (&glob_sdl);
213 }
214
215 static int sdl_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt)
216 {
217 SDLVoice *sdl = (SDLVoice *) hw;
218 SDLAudioState *s = &glob_sdl;
219 SDL_AudioSpec req, obt;
220 int shift;
221
222 ldebug ("sdl_hw_init %d freq=%d fixed=%d\n",
223 s->initialized, freq, audio_conf.fixed_format);
224
225 if (nchannels != 2) {
226 dolog ("Bogus channel count %d\n", nchannels);
227 return -1;
228 }
229
230 req.freq = freq;
231 req.format = AUD_to_sdlfmt (fmt, &shift);
232 req.channels = nchannels;
233 req.samples = conf.nb_samples;
234 shift <<= nchannels == 2;
235
236 req.callback = sdl_callback;
237 req.userdata = sdl;
238
239 if (sdl_open (&req, &obt))
240 return -1;
241
242 hw->freq = obt.freq;
243 hw->fmt = sdl_to_audfmt (obt.format);
244 hw->nchannels = obt.channels;
245 hw->bufsize = obt.samples << shift;
246
247 s->initialized = 1;
248 s->exit = 0;
249 SDL_PauseAudio (0);
250 return 0;
251 }
252
253 static int sdl_hw_ctl (HWVoice *hw, int cmd, ...)
254 {
255 (void) hw;
256
257 switch (cmd) {
258 case VOICE_ENABLE:
259 SDL_PauseAudio (0);
260 break;
261
262 case VOICE_DISABLE:
263 SDL_PauseAudio (1);
264 break;
265 }
266 return 0;
267 }
268
269 static void *sdl_audio_init (void)
270 {
271 SDLAudioState *s = &glob_sdl;
272 conf.nb_samples = audio_get_conf_int (QC_SDL_SAMPLES, conf.nb_samples);
273
274 if (SDL_InitSubSystem (SDL_INIT_AUDIO)) {
275 dolog ("SDL failed to initialize audio subsystem\nReason: %s\n",
276 errstr ());
277 return NULL;
278 }
279
280 s->mutex = SDL_CreateMutex ();
281 if (!s->mutex) {
282 dolog ("Failed to create SDL mutex\nReason: %s\n", errstr ());
283 SDL_QuitSubSystem (SDL_INIT_AUDIO);
284 return NULL;
285 }
286
287 s->sem = SDL_CreateSemaphore (0);
288 if (!s->sem) {
289 dolog ("Failed to create SDL semaphore\nReason: %s\n", errstr ());
290 SDL_DestroyMutex (s->mutex);
291 SDL_QuitSubSystem (SDL_INIT_AUDIO);
292 return NULL;
293 }
294
295 return s;
296 }
297
298 static void sdl_audio_fini (void *opaque)
299 {
300 SDLAudioState *s = opaque;
301 sdl_close (s);
302 SDL_DestroySemaphore (s->sem);
303 SDL_DestroyMutex (s->mutex);
304 SDL_QuitSubSystem (SDL_INIT_AUDIO);
305 }
306
307 struct pcm_ops sdl_pcm_ops = {
308 sdl_hw_init,
309 sdl_hw_fini,
310 sdl_hw_run,
311 sdl_hw_write,
312 sdl_hw_ctl
313 };
314
315 struct audio_output_driver sdl_output_driver = {
316 "sdl",
317 sdl_audio_init,
318 sdl_audio_fini,
319 &sdl_pcm_ops,
320 1,
321 1,
322 sizeof (SDLVoice)
323 };