185571bc7Sbellard /* 285571bc7Sbellard * QEMU SDL audio output driver 385571bc7Sbellard * 485571bc7Sbellard * Copyright (c) 2004 Vassili Karpov (malc) 585571bc7Sbellard * 685571bc7Sbellard * Permission is hereby granted, free of charge, to any person obtaining a copy 785571bc7Sbellard * of this software and associated documentation files (the "Software"), to deal 885571bc7Sbellard * in the Software without restriction, including without limitation the rights 985571bc7Sbellard * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 1085571bc7Sbellard * copies of the Software, and to permit persons to whom the Software is 1185571bc7Sbellard * furnished to do so, subject to the following conditions: 1285571bc7Sbellard * 1385571bc7Sbellard * The above copyright notice and this permission notice shall be included in 1485571bc7Sbellard * all copies or substantial portions of the Software. 1585571bc7Sbellard * 1685571bc7Sbellard * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1785571bc7Sbellard * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1885571bc7Sbellard * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 1985571bc7Sbellard * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 2085571bc7Sbellard * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 2185571bc7Sbellard * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 2285571bc7Sbellard * THE SOFTWARE. 2385571bc7Sbellard */ 24*9f059ecaSbellard #include <SDL.h> 25*9f059ecaSbellard #include <SDL_thread.h> 2685571bc7Sbellard #include "vl.h" 2785571bc7Sbellard 28fb065187Sbellard #include "audio/audio_int.h" 29fb065187Sbellard 30fb065187Sbellard typedef struct SDLVoice { 31fb065187Sbellard HWVoice hw; 32fb065187Sbellard } SDLVoice; 33fb065187Sbellard 34fb065187Sbellard #define dolog(...) AUD_log ("sdl", __VA_ARGS__) 35fb065187Sbellard #ifdef DEBUG 36fb065187Sbellard #define ldebug(...) dolog (__VA_ARGS__) 37fb065187Sbellard #else 38fb065187Sbellard #define ldebug(...) 39fb065187Sbellard #endif 4085571bc7Sbellard 4185571bc7Sbellard #define QC_SDL_SAMPLES "QEMU_SDL_SAMPLES" 4285571bc7Sbellard 4385571bc7Sbellard #define errstr() SDL_GetError () 4485571bc7Sbellard 4585571bc7Sbellard static struct { 4685571bc7Sbellard int nb_samples; 4785571bc7Sbellard } conf = { 4885571bc7Sbellard 1024 4985571bc7Sbellard }; 5085571bc7Sbellard 5185571bc7Sbellard struct SDLAudioState { 5285571bc7Sbellard int exit; 5385571bc7Sbellard SDL_mutex *mutex; 5485571bc7Sbellard SDL_sem *sem; 5585571bc7Sbellard int initialized; 5685571bc7Sbellard } glob_sdl; 5785571bc7Sbellard typedef struct SDLAudioState SDLAudioState; 5885571bc7Sbellard 5985571bc7Sbellard static void sdl_hw_run (HWVoice *hw) 6085571bc7Sbellard { 6185571bc7Sbellard (void) hw; 6285571bc7Sbellard } 6385571bc7Sbellard 6485571bc7Sbellard static int sdl_lock (SDLAudioState *s) 6585571bc7Sbellard { 6685571bc7Sbellard if (SDL_LockMutex (s->mutex)) { 6785571bc7Sbellard dolog ("SDL_LockMutex failed\nReason: %s\n", errstr ()); 6885571bc7Sbellard return -1; 6985571bc7Sbellard } 7085571bc7Sbellard return 0; 7185571bc7Sbellard } 7285571bc7Sbellard 7385571bc7Sbellard static int sdl_unlock (SDLAudioState *s) 7485571bc7Sbellard { 7585571bc7Sbellard if (SDL_UnlockMutex (s->mutex)) { 7685571bc7Sbellard dolog ("SDL_UnlockMutex failed\nReason: %s\n", errstr ()); 7785571bc7Sbellard return -1; 7885571bc7Sbellard } 7985571bc7Sbellard return 0; 8085571bc7Sbellard } 8185571bc7Sbellard 8285571bc7Sbellard static int sdl_post (SDLAudioState *s) 8385571bc7Sbellard { 8485571bc7Sbellard if (SDL_SemPost (s->sem)) { 8585571bc7Sbellard dolog ("SDL_SemPost failed\nReason: %s\n", errstr ()); 8685571bc7Sbellard return -1; 8785571bc7Sbellard } 8885571bc7Sbellard return 0; 8985571bc7Sbellard } 9085571bc7Sbellard 9185571bc7Sbellard static int sdl_wait (SDLAudioState *s) 9285571bc7Sbellard { 9385571bc7Sbellard if (SDL_SemWait (s->sem)) { 9485571bc7Sbellard dolog ("SDL_SemWait failed\nReason: %s\n", errstr ()); 9585571bc7Sbellard return -1; 9685571bc7Sbellard } 9785571bc7Sbellard return 0; 9885571bc7Sbellard } 9985571bc7Sbellard 10085571bc7Sbellard static int sdl_unlock_and_post (SDLAudioState *s) 10185571bc7Sbellard { 10285571bc7Sbellard if (sdl_unlock (s)) 10385571bc7Sbellard return -1; 10485571bc7Sbellard 10585571bc7Sbellard return sdl_post (s); 10685571bc7Sbellard } 10785571bc7Sbellard 10885571bc7Sbellard static int sdl_hw_write (SWVoice *sw, void *buf, int len) 10985571bc7Sbellard { 11085571bc7Sbellard int ret; 11185571bc7Sbellard SDLAudioState *s = &glob_sdl; 11285571bc7Sbellard sdl_lock (s); 11385571bc7Sbellard ret = pcm_hw_write (sw, buf, len); 11485571bc7Sbellard sdl_unlock_and_post (s); 11585571bc7Sbellard return ret; 11685571bc7Sbellard } 11785571bc7Sbellard 11885571bc7Sbellard static int AUD_to_sdlfmt (audfmt_e fmt, int *shift) 11985571bc7Sbellard { 12085571bc7Sbellard *shift = 0; 12185571bc7Sbellard switch (fmt) { 12285571bc7Sbellard case AUD_FMT_S8: return AUDIO_S8; 12385571bc7Sbellard case AUD_FMT_U8: return AUDIO_U8; 12485571bc7Sbellard case AUD_FMT_S16: *shift = 1; return AUDIO_S16LSB; 12585571bc7Sbellard case AUD_FMT_U16: *shift = 1; return AUDIO_U16LSB; 12685571bc7Sbellard default: 12785571bc7Sbellard dolog ("Internal logic error: Bad audio format %d\nAborting\n", fmt); 12885571bc7Sbellard exit (EXIT_FAILURE); 12985571bc7Sbellard } 13085571bc7Sbellard } 13185571bc7Sbellard 13285571bc7Sbellard static int sdl_to_audfmt (int fmt) 13385571bc7Sbellard { 13485571bc7Sbellard switch (fmt) { 13585571bc7Sbellard case AUDIO_S8: return AUD_FMT_S8; 13685571bc7Sbellard case AUDIO_U8: return AUD_FMT_U8; 13785571bc7Sbellard case AUDIO_S16LSB: return AUD_FMT_S16; 13885571bc7Sbellard case AUDIO_U16LSB: return AUD_FMT_U16; 13985571bc7Sbellard default: 14085571bc7Sbellard dolog ("Internal logic error: Unrecognized SDL audio format %d\n" 14185571bc7Sbellard "Aborting\n", fmt); 14285571bc7Sbellard exit (EXIT_FAILURE); 14385571bc7Sbellard } 14485571bc7Sbellard } 14585571bc7Sbellard 14685571bc7Sbellard static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt) 14785571bc7Sbellard { 14885571bc7Sbellard int status; 14985571bc7Sbellard 15085571bc7Sbellard status = SDL_OpenAudio (req, obt); 15185571bc7Sbellard if (status) { 15285571bc7Sbellard dolog ("SDL_OpenAudio failed\nReason: %s\n", errstr ()); 15385571bc7Sbellard } 15485571bc7Sbellard return status; 15585571bc7Sbellard } 15685571bc7Sbellard 15785571bc7Sbellard static void sdl_close (SDLAudioState *s) 15885571bc7Sbellard { 15985571bc7Sbellard if (s->initialized) { 16085571bc7Sbellard sdl_lock (s); 16185571bc7Sbellard s->exit = 1; 16285571bc7Sbellard sdl_unlock_and_post (s); 16385571bc7Sbellard SDL_PauseAudio (1); 16485571bc7Sbellard SDL_CloseAudio (); 16585571bc7Sbellard s->initialized = 0; 16685571bc7Sbellard } 16785571bc7Sbellard } 16885571bc7Sbellard 16985571bc7Sbellard static void sdl_callback (void *opaque, Uint8 *buf, int len) 17085571bc7Sbellard { 17185571bc7Sbellard SDLVoice *sdl = opaque; 17285571bc7Sbellard SDLAudioState *s = &glob_sdl; 17385571bc7Sbellard HWVoice *hw = &sdl->hw; 17485571bc7Sbellard int samples = len >> hw->shift; 17585571bc7Sbellard 17685571bc7Sbellard if (s->exit) { 17785571bc7Sbellard return; 17885571bc7Sbellard } 17985571bc7Sbellard 18085571bc7Sbellard while (samples) { 18185571bc7Sbellard int to_mix, live, decr; 18285571bc7Sbellard 18385571bc7Sbellard /* dolog ("in callback samples=%d\n", samples); */ 18485571bc7Sbellard sdl_wait (s); 18585571bc7Sbellard if (s->exit) { 18685571bc7Sbellard return; 18785571bc7Sbellard } 18885571bc7Sbellard 18985571bc7Sbellard sdl_lock (s); 19085571bc7Sbellard live = pcm_hw_get_live (hw); 19185571bc7Sbellard if (live <= 0) 19285571bc7Sbellard goto again; 19385571bc7Sbellard 19485571bc7Sbellard /* dolog ("in callback live=%d\n", live); */ 19585571bc7Sbellard to_mix = audio_MIN (samples, live); 19685571bc7Sbellard decr = to_mix; 19785571bc7Sbellard while (to_mix) { 19885571bc7Sbellard int chunk = audio_MIN (to_mix, hw->samples - hw->rpos); 19985571bc7Sbellard st_sample_t *src = hw->mix_buf + hw->rpos; 20085571bc7Sbellard 20185571bc7Sbellard /* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */ 20285571bc7Sbellard hw->clip (buf, src, chunk); 20385571bc7Sbellard memset (src, 0, chunk * sizeof (st_sample_t)); 20485571bc7Sbellard hw->rpos = (hw->rpos + chunk) % hw->samples; 20585571bc7Sbellard to_mix -= chunk; 20685571bc7Sbellard buf += chunk << hw->shift; 20785571bc7Sbellard } 20885571bc7Sbellard samples -= decr; 20985571bc7Sbellard pcm_hw_dec_live (hw, decr); 21085571bc7Sbellard 21185571bc7Sbellard again: 21285571bc7Sbellard sdl_unlock (s); 21385571bc7Sbellard } 21485571bc7Sbellard /* dolog ("done len=%d\n", len); */ 21585571bc7Sbellard } 21685571bc7Sbellard 21785571bc7Sbellard static void sdl_hw_fini (HWVoice *hw) 21885571bc7Sbellard { 21985571bc7Sbellard ldebug ("sdl_hw_fini %d fixed=%d\n", 22085571bc7Sbellard glob_sdl.initialized, audio_conf.fixed_format); 22185571bc7Sbellard sdl_close (&glob_sdl); 22285571bc7Sbellard } 22385571bc7Sbellard 22485571bc7Sbellard static int sdl_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt) 22585571bc7Sbellard { 22685571bc7Sbellard SDLVoice *sdl = (SDLVoice *) hw; 22785571bc7Sbellard SDLAudioState *s = &glob_sdl; 22885571bc7Sbellard SDL_AudioSpec req, obt; 22985571bc7Sbellard int shift; 23085571bc7Sbellard 23185571bc7Sbellard ldebug ("sdl_hw_init %d freq=%d fixed=%d\n", 23285571bc7Sbellard s->initialized, freq, audio_conf.fixed_format); 23385571bc7Sbellard 23485571bc7Sbellard if (nchannels != 2) { 23585571bc7Sbellard dolog ("Bogus channel count %d\n", nchannels); 23685571bc7Sbellard return -1; 23785571bc7Sbellard } 23885571bc7Sbellard 23985571bc7Sbellard req.freq = freq; 24085571bc7Sbellard req.format = AUD_to_sdlfmt (fmt, &shift); 24185571bc7Sbellard req.channels = nchannels; 24285571bc7Sbellard req.samples = conf.nb_samples; 24385571bc7Sbellard shift <<= nchannels == 2; 24485571bc7Sbellard 24585571bc7Sbellard req.callback = sdl_callback; 24685571bc7Sbellard req.userdata = sdl; 24785571bc7Sbellard 24885571bc7Sbellard if (sdl_open (&req, &obt)) 24985571bc7Sbellard return -1; 25085571bc7Sbellard 25185571bc7Sbellard hw->freq = obt.freq; 25285571bc7Sbellard hw->fmt = sdl_to_audfmt (obt.format); 25385571bc7Sbellard hw->nchannels = obt.channels; 25485571bc7Sbellard hw->bufsize = obt.samples << shift; 25585571bc7Sbellard 25685571bc7Sbellard s->initialized = 1; 25785571bc7Sbellard s->exit = 0; 25885571bc7Sbellard SDL_PauseAudio (0); 25985571bc7Sbellard return 0; 26085571bc7Sbellard } 26185571bc7Sbellard 26285571bc7Sbellard static int sdl_hw_ctl (HWVoice *hw, int cmd, ...) 26385571bc7Sbellard { 26485571bc7Sbellard (void) hw; 26585571bc7Sbellard 26685571bc7Sbellard switch (cmd) { 26785571bc7Sbellard case VOICE_ENABLE: 26885571bc7Sbellard SDL_PauseAudio (0); 26985571bc7Sbellard break; 27085571bc7Sbellard 27185571bc7Sbellard case VOICE_DISABLE: 27285571bc7Sbellard SDL_PauseAudio (1); 27385571bc7Sbellard break; 27485571bc7Sbellard } 27585571bc7Sbellard return 0; 27685571bc7Sbellard } 27785571bc7Sbellard 27885571bc7Sbellard static void *sdl_audio_init (void) 27985571bc7Sbellard { 28085571bc7Sbellard SDLAudioState *s = &glob_sdl; 28185571bc7Sbellard conf.nb_samples = audio_get_conf_int (QC_SDL_SAMPLES, conf.nb_samples); 28285571bc7Sbellard 28385571bc7Sbellard if (SDL_InitSubSystem (SDL_INIT_AUDIO)) { 28485571bc7Sbellard dolog ("SDL failed to initialize audio subsystem\nReason: %s\n", 28585571bc7Sbellard errstr ()); 28685571bc7Sbellard return NULL; 28785571bc7Sbellard } 28885571bc7Sbellard 28985571bc7Sbellard s->mutex = SDL_CreateMutex (); 29085571bc7Sbellard if (!s->mutex) { 29185571bc7Sbellard dolog ("Failed to create SDL mutex\nReason: %s\n", errstr ()); 29285571bc7Sbellard SDL_QuitSubSystem (SDL_INIT_AUDIO); 29385571bc7Sbellard return NULL; 29485571bc7Sbellard } 29585571bc7Sbellard 29685571bc7Sbellard s->sem = SDL_CreateSemaphore (0); 29785571bc7Sbellard if (!s->sem) { 29885571bc7Sbellard dolog ("Failed to create SDL semaphore\nReason: %s\n", errstr ()); 29985571bc7Sbellard SDL_DestroyMutex (s->mutex); 30085571bc7Sbellard SDL_QuitSubSystem (SDL_INIT_AUDIO); 30185571bc7Sbellard return NULL; 30285571bc7Sbellard } 30385571bc7Sbellard 30485571bc7Sbellard return s; 30585571bc7Sbellard } 30685571bc7Sbellard 30785571bc7Sbellard static void sdl_audio_fini (void *opaque) 30885571bc7Sbellard { 30985571bc7Sbellard SDLAudioState *s = opaque; 31085571bc7Sbellard sdl_close (s); 31185571bc7Sbellard SDL_DestroySemaphore (s->sem); 31285571bc7Sbellard SDL_DestroyMutex (s->mutex); 31385571bc7Sbellard SDL_QuitSubSystem (SDL_INIT_AUDIO); 31485571bc7Sbellard } 31585571bc7Sbellard 31685571bc7Sbellard struct pcm_ops sdl_pcm_ops = { 31785571bc7Sbellard sdl_hw_init, 31885571bc7Sbellard sdl_hw_fini, 31985571bc7Sbellard sdl_hw_run, 32085571bc7Sbellard sdl_hw_write, 32185571bc7Sbellard sdl_hw_ctl 32285571bc7Sbellard }; 32385571bc7Sbellard 32485571bc7Sbellard struct audio_output_driver sdl_output_driver = { 32585571bc7Sbellard "sdl", 32685571bc7Sbellard sdl_audio_init, 32785571bc7Sbellard sdl_audio_fini, 32885571bc7Sbellard &sdl_pcm_ops, 32985571bc7Sbellard 1, 33085571bc7Sbellard 1, 33185571bc7Sbellard sizeof (SDLVoice) 33285571bc7Sbellard }; 333