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