185571bc7Sbellard /* 21d14ffa9Sbellard * QEMU SDL audio driver 385571bc7Sbellard * 41d14ffa9Sbellard * Copyright (c) 2004-2005 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 */ 249f059ecaSbellard #include <SDL.h> 259f059ecaSbellard #include <SDL_thread.h> 2687ecb68bSpbrook #include "qemu-common.h" 2787ecb68bSpbrook #include "audio.h" 2885571bc7Sbellard 29e784ba70Sths #ifndef _WIN32 30e784ba70Sths #ifdef __sun__ 31e784ba70Sths #define _POSIX_PTHREAD_SEMANTICS 1 32c5e97233Sblueswir1 #elif defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) 339b4c14c3Sblueswir1 #include <pthread.h> 34e784ba70Sths #endif 35e784ba70Sths #include <signal.h> 36e784ba70Sths #endif 37e784ba70Sths 381d14ffa9Sbellard #define AUDIO_CAP "sdl" 391d14ffa9Sbellard #include "audio_int.h" 40fb065187Sbellard 411d14ffa9Sbellard typedef struct SDLVoiceOut { 421d14ffa9Sbellard HWVoiceOut hw; 431d14ffa9Sbellard int live; 44ff541499Smalc int rpos; 451d14ffa9Sbellard int decr; 461d14ffa9Sbellard } SDLVoiceOut; 4785571bc7Sbellard 4885571bc7Sbellard static struct { 4985571bc7Sbellard int nb_samples; 5085571bc7Sbellard } conf = { 511a40d5e2SJuan Quintela .nb_samples = 1024 5285571bc7Sbellard }; 5385571bc7Sbellard 54b1d8e52eSblueswir1 static struct SDLAudioState { 5585571bc7Sbellard int exit; 5685571bc7Sbellard SDL_mutex *mutex; 5785571bc7Sbellard SDL_sem *sem; 5885571bc7Sbellard int initialized; 5985571bc7Sbellard } glob_sdl; 6085571bc7Sbellard typedef struct SDLAudioState SDLAudioState; 6185571bc7Sbellard 621d14ffa9Sbellard static void GCC_FMT_ATTR (1, 2) sdl_logerr (const char *fmt, ...) 6385571bc7Sbellard { 641d14ffa9Sbellard va_list ap; 651d14ffa9Sbellard 661d14ffa9Sbellard va_start (ap, fmt); 671d14ffa9Sbellard AUD_vlog (AUDIO_CAP, fmt, ap); 681d14ffa9Sbellard va_end (ap); 691d14ffa9Sbellard 701d14ffa9Sbellard AUD_log (AUDIO_CAP, "Reason: %s\n", SDL_GetError ()); 7185571bc7Sbellard } 7285571bc7Sbellard 731d14ffa9Sbellard static int sdl_lock (SDLAudioState *s, const char *forfn) 7485571bc7Sbellard { 7585571bc7Sbellard if (SDL_LockMutex (s->mutex)) { 761d14ffa9Sbellard sdl_logerr ("SDL_LockMutex for %s failed\n", forfn); 7785571bc7Sbellard return -1; 7885571bc7Sbellard } 7985571bc7Sbellard return 0; 8085571bc7Sbellard } 8185571bc7Sbellard 821d14ffa9Sbellard static int sdl_unlock (SDLAudioState *s, const char *forfn) 8385571bc7Sbellard { 8485571bc7Sbellard if (SDL_UnlockMutex (s->mutex)) { 851d14ffa9Sbellard sdl_logerr ("SDL_UnlockMutex for %s failed\n", forfn); 8685571bc7Sbellard return -1; 8785571bc7Sbellard } 8885571bc7Sbellard return 0; 8985571bc7Sbellard } 9085571bc7Sbellard 911d14ffa9Sbellard static int sdl_post (SDLAudioState *s, const char *forfn) 9285571bc7Sbellard { 9385571bc7Sbellard if (SDL_SemPost (s->sem)) { 941d14ffa9Sbellard sdl_logerr ("SDL_SemPost for %s failed\n", forfn); 9585571bc7Sbellard return -1; 9685571bc7Sbellard } 9785571bc7Sbellard return 0; 9885571bc7Sbellard } 9985571bc7Sbellard 1001d14ffa9Sbellard static int sdl_wait (SDLAudioState *s, const char *forfn) 10185571bc7Sbellard { 10285571bc7Sbellard if (SDL_SemWait (s->sem)) { 1031d14ffa9Sbellard sdl_logerr ("SDL_SemWait for %s failed\n", forfn); 10485571bc7Sbellard return -1; 10585571bc7Sbellard } 10685571bc7Sbellard return 0; 10785571bc7Sbellard } 10885571bc7Sbellard 1091d14ffa9Sbellard static int sdl_unlock_and_post (SDLAudioState *s, const char *forfn) 11085571bc7Sbellard { 1111d14ffa9Sbellard if (sdl_unlock (s, forfn)) { 11285571bc7Sbellard return -1; 11385571bc7Sbellard } 11485571bc7Sbellard 1151d14ffa9Sbellard return sdl_post (s, forfn); 11685571bc7Sbellard } 11785571bc7Sbellard 1186c557ab9SSerge Ziryukin static int aud_to_sdlfmt (audfmt_e fmt) 11985571bc7Sbellard { 1201d14ffa9Sbellard switch (fmt) { 1211d14ffa9Sbellard case AUD_FMT_S8: 1221d14ffa9Sbellard return AUDIO_S8; 1231d14ffa9Sbellard 1241d14ffa9Sbellard case AUD_FMT_U8: 1251d14ffa9Sbellard return AUDIO_U8; 1261d14ffa9Sbellard 1271d14ffa9Sbellard case AUD_FMT_S16: 1281d14ffa9Sbellard return AUDIO_S16LSB; 1291d14ffa9Sbellard 1301d14ffa9Sbellard case AUD_FMT_U16: 1311d14ffa9Sbellard return AUDIO_U16LSB; 1321d14ffa9Sbellard 13385571bc7Sbellard default: 1341d14ffa9Sbellard dolog ("Internal logic error: Bad audio format %d\n", fmt); 1351d14ffa9Sbellard #ifdef DEBUG_AUDIO 1361d14ffa9Sbellard abort (); 1371d14ffa9Sbellard #endif 1381d14ffa9Sbellard return AUDIO_U8; 13985571bc7Sbellard } 14085571bc7Sbellard } 14185571bc7Sbellard 1421d14ffa9Sbellard static int sdl_to_audfmt (int sdlfmt, audfmt_e *fmt, int *endianess) 14385571bc7Sbellard { 1441d14ffa9Sbellard switch (sdlfmt) { 1451d14ffa9Sbellard case AUDIO_S8: 1461d14ffa9Sbellard *endianess = 0; 1471d14ffa9Sbellard *fmt = AUD_FMT_S8; 1481d14ffa9Sbellard break; 1491d14ffa9Sbellard 1501d14ffa9Sbellard case AUDIO_U8: 1511d14ffa9Sbellard *endianess = 0; 1521d14ffa9Sbellard *fmt = AUD_FMT_U8; 1531d14ffa9Sbellard break; 1541d14ffa9Sbellard 1551d14ffa9Sbellard case AUDIO_S16LSB: 1561d14ffa9Sbellard *endianess = 0; 1571d14ffa9Sbellard *fmt = AUD_FMT_S16; 1581d14ffa9Sbellard break; 1591d14ffa9Sbellard 1601d14ffa9Sbellard case AUDIO_U16LSB: 1611d14ffa9Sbellard *endianess = 0; 1621d14ffa9Sbellard *fmt = AUD_FMT_U16; 1631d14ffa9Sbellard break; 1641d14ffa9Sbellard 1651d14ffa9Sbellard case AUDIO_S16MSB: 1661d14ffa9Sbellard *endianess = 1; 1671d14ffa9Sbellard *fmt = AUD_FMT_S16; 1681d14ffa9Sbellard break; 1691d14ffa9Sbellard 1701d14ffa9Sbellard case AUDIO_U16MSB: 1711d14ffa9Sbellard *endianess = 1; 1721d14ffa9Sbellard *fmt = AUD_FMT_U16; 1731d14ffa9Sbellard break; 1741d14ffa9Sbellard 17585571bc7Sbellard default: 1761d14ffa9Sbellard dolog ("Unrecognized SDL audio format %d\n", sdlfmt); 1771d14ffa9Sbellard return -1; 17885571bc7Sbellard } 1791d14ffa9Sbellard 1801d14ffa9Sbellard return 0; 18185571bc7Sbellard } 18285571bc7Sbellard 18385571bc7Sbellard static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt) 18485571bc7Sbellard { 18585571bc7Sbellard int status; 186e784ba70Sths #ifndef _WIN32 187d087bb3eSmalc int err; 188e784ba70Sths sigset_t new, old; 189e784ba70Sths 190e784ba70Sths /* Make sure potential threads created by SDL don't hog signals. */ 191d087bb3eSmalc err = sigfillset (&new); 192d087bb3eSmalc if (err) { 193d087bb3eSmalc dolog ("sdl_open: sigfillset failed: %s\n", strerror (errno)); 194*60592eddSmalc return -1; 195d087bb3eSmalc } 196d087bb3eSmalc err = pthread_sigmask (SIG_BLOCK, &new, &old); 197d087bb3eSmalc if (err) { 198d087bb3eSmalc dolog ("sdl_open: pthread_sigmask failed: %s\n", strerror (err)); 199d087bb3eSmalc return -1; 200d087bb3eSmalc } 201e784ba70Sths #endif 20285571bc7Sbellard 20385571bc7Sbellard status = SDL_OpenAudio (req, obt); 20485571bc7Sbellard if (status) { 2051d14ffa9Sbellard sdl_logerr ("SDL_OpenAudio failed\n"); 20685571bc7Sbellard } 207e784ba70Sths 208e784ba70Sths #ifndef _WIN32 209d087bb3eSmalc err = pthread_sigmask (SIG_SETMASK, &old, NULL); 210d087bb3eSmalc if (err) { 211d087bb3eSmalc dolog ("sdl_open: pthread_sigmask (restore) failed: %s\n", 212d087bb3eSmalc strerror (errno)); 213d087bb3eSmalc /* We have failed to restore original signal mask, all bets are off, 214d087bb3eSmalc so exit the process */ 215d087bb3eSmalc exit (EXIT_FAILURE); 216d087bb3eSmalc } 217e784ba70Sths #endif 21885571bc7Sbellard return status; 21985571bc7Sbellard } 22085571bc7Sbellard 22185571bc7Sbellard static void sdl_close (SDLAudioState *s) 22285571bc7Sbellard { 22385571bc7Sbellard if (s->initialized) { 2241d14ffa9Sbellard sdl_lock (s, "sdl_close"); 22585571bc7Sbellard s->exit = 1; 2261d14ffa9Sbellard sdl_unlock_and_post (s, "sdl_close"); 22785571bc7Sbellard SDL_PauseAudio (1); 22885571bc7Sbellard SDL_CloseAudio (); 22985571bc7Sbellard s->initialized = 0; 23085571bc7Sbellard } 23185571bc7Sbellard } 23285571bc7Sbellard 23385571bc7Sbellard static void sdl_callback (void *opaque, Uint8 *buf, int len) 23485571bc7Sbellard { 2351d14ffa9Sbellard SDLVoiceOut *sdl = opaque; 23685571bc7Sbellard SDLAudioState *s = &glob_sdl; 2371d14ffa9Sbellard HWVoiceOut *hw = &sdl->hw; 2381d14ffa9Sbellard int samples = len >> hw->info.shift; 23985571bc7Sbellard 24085571bc7Sbellard if (s->exit) { 24185571bc7Sbellard return; 24285571bc7Sbellard } 24385571bc7Sbellard 24485571bc7Sbellard while (samples) { 2451d14ffa9Sbellard int to_mix, decr; 24685571bc7Sbellard 247ff541499Smalc /* dolog ("in callback samples=%d\n", samples); */ 2481d14ffa9Sbellard sdl_wait (s, "sdl_callback"); 24985571bc7Sbellard if (s->exit) { 25085571bc7Sbellard return; 25185571bc7Sbellard } 25285571bc7Sbellard 2531d14ffa9Sbellard if (sdl_lock (s, "sdl_callback")) { 2541d14ffa9Sbellard return; 2551d14ffa9Sbellard } 256ff541499Smalc 257ff541499Smalc if (audio_bug (AUDIO_FUNC, sdl->live < 0 || sdl->live > hw->samples)) { 258ff541499Smalc dolog ("sdl->live=%d hw->samples=%d\n", 259ff541499Smalc sdl->live, hw->samples); 260ff541499Smalc return; 2611d14ffa9Sbellard } 2621d14ffa9Sbellard 263ff541499Smalc if (!sdl->live) { 264ff541499Smalc goto again; 265ff541499Smalc } 266ff541499Smalc 267ff541499Smalc /* dolog ("in callback live=%d\n", live); */ 268ff541499Smalc to_mix = audio_MIN (samples, sdl->live); 269ff541499Smalc decr = to_mix; 270ff541499Smalc while (to_mix) { 271ff541499Smalc int chunk = audio_MIN (to_mix, hw->samples - hw->rpos); 272ff541499Smalc struct st_sample *src = hw->mix_buf + hw->rpos; 273ff541499Smalc 274ff541499Smalc /* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */ 275ff541499Smalc hw->clip (buf, src, chunk); 276ff541499Smalc sdl->rpos = (sdl->rpos + chunk) % hw->samples; 277ff541499Smalc to_mix -= chunk; 278ff541499Smalc buf += chunk << hw->info.shift; 279ff541499Smalc } 28085571bc7Sbellard samples -= decr; 281ff541499Smalc sdl->live -= decr; 2821d14ffa9Sbellard sdl->decr += decr; 28385571bc7Sbellard 284ff541499Smalc again: 2851d14ffa9Sbellard if (sdl_unlock (s, "sdl_callback")) { 2861d14ffa9Sbellard return; 2871d14ffa9Sbellard } 28885571bc7Sbellard } 289ff541499Smalc /* dolog ("done len=%d\n", len); */ 290ff541499Smalc } 29185571bc7Sbellard 2921d14ffa9Sbellard static int sdl_write_out (SWVoiceOut *sw, void *buf, int len) 29385571bc7Sbellard { 2941d14ffa9Sbellard return audio_pcm_sw_write (sw, buf, len); 2951d14ffa9Sbellard } 2961d14ffa9Sbellard 297bdff253cSmalc static int sdl_run_out (HWVoiceOut *hw, int live) 2981d14ffa9Sbellard { 299bdff253cSmalc int decr; 3001d14ffa9Sbellard SDLVoiceOut *sdl = (SDLVoiceOut *) hw; 3011d14ffa9Sbellard SDLAudioState *s = &glob_sdl; 3021d14ffa9Sbellard 3033fd7f635Smalc if (sdl_lock (s, "sdl_run_out")) { 3041d14ffa9Sbellard return 0; 3051d14ffa9Sbellard } 3061d14ffa9Sbellard 307ff541499Smalc if (sdl->decr > live) { 308ff541499Smalc ldebug ("sdl->decr %d live %d sdl->live %d\n", 309ff541499Smalc sdl->decr, 310ff541499Smalc live, 311ff541499Smalc sdl->live); 312ff541499Smalc } 313ff541499Smalc 314ff541499Smalc decr = audio_MIN (sdl->decr, live); 315ff541499Smalc sdl->decr -= decr; 316ff541499Smalc 317ff541499Smalc sdl->live = live - decr; 318ff541499Smalc hw->rpos = sdl->rpos; 3191d14ffa9Sbellard 3201d14ffa9Sbellard if (sdl->live > 0) { 3213fd7f635Smalc sdl_unlock_and_post (s, "sdl_run_out"); 3221d14ffa9Sbellard } 3231d14ffa9Sbellard else { 3243fd7f635Smalc sdl_unlock (s, "sdl_run_out"); 3251d14ffa9Sbellard } 3261d14ffa9Sbellard return decr; 3271d14ffa9Sbellard } 3281d14ffa9Sbellard 3291d14ffa9Sbellard static void sdl_fini_out (HWVoiceOut *hw) 3301d14ffa9Sbellard { 3311d14ffa9Sbellard (void) hw; 3321d14ffa9Sbellard 33385571bc7Sbellard sdl_close (&glob_sdl); 33485571bc7Sbellard } 33585571bc7Sbellard 3361ea879e5Smalc static int sdl_init_out (HWVoiceOut *hw, struct audsettings *as) 33785571bc7Sbellard { 3381d14ffa9Sbellard SDLVoiceOut *sdl = (SDLVoiceOut *) hw; 33985571bc7Sbellard SDLAudioState *s = &glob_sdl; 34085571bc7Sbellard SDL_AudioSpec req, obt; 3411d14ffa9Sbellard int endianess; 3421d14ffa9Sbellard int err; 3431d14ffa9Sbellard audfmt_e effective_fmt; 3441ea879e5Smalc struct audsettings obt_as; 34585571bc7Sbellard 346c0fe3827Sbellard req.freq = as->freq; 3476c557ab9SSerge Ziryukin req.format = aud_to_sdlfmt (as->fmt); 348c0fe3827Sbellard req.channels = as->nchannels; 34985571bc7Sbellard req.samples = conf.nb_samples; 35085571bc7Sbellard req.callback = sdl_callback; 35185571bc7Sbellard req.userdata = sdl; 35285571bc7Sbellard 3531d14ffa9Sbellard if (sdl_open (&req, &obt)) { 35485571bc7Sbellard return -1; 3551d14ffa9Sbellard } 35685571bc7Sbellard 3571d14ffa9Sbellard err = sdl_to_audfmt (obt.format, &effective_fmt, &endianess); 3581d14ffa9Sbellard if (err) { 3591d14ffa9Sbellard sdl_close (s); 3601d14ffa9Sbellard return -1; 3611d14ffa9Sbellard } 3621d14ffa9Sbellard 363c0fe3827Sbellard obt_as.freq = obt.freq; 364c0fe3827Sbellard obt_as.nchannels = obt.channels; 365c0fe3827Sbellard obt_as.fmt = effective_fmt; 366d929eba5Sbellard obt_as.endianness = endianess; 367c0fe3827Sbellard 368d929eba5Sbellard audio_pcm_init_info (&hw->info, &obt_as); 369c0fe3827Sbellard hw->samples = obt.samples; 37085571bc7Sbellard 37185571bc7Sbellard s->initialized = 1; 37285571bc7Sbellard s->exit = 0; 37385571bc7Sbellard SDL_PauseAudio (0); 37485571bc7Sbellard return 0; 37585571bc7Sbellard } 37685571bc7Sbellard 3771d14ffa9Sbellard static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...) 37885571bc7Sbellard { 37985571bc7Sbellard (void) hw; 38085571bc7Sbellard 38185571bc7Sbellard switch (cmd) { 38285571bc7Sbellard case VOICE_ENABLE: 38385571bc7Sbellard SDL_PauseAudio (0); 38485571bc7Sbellard break; 38585571bc7Sbellard 38685571bc7Sbellard case VOICE_DISABLE: 38785571bc7Sbellard SDL_PauseAudio (1); 38885571bc7Sbellard break; 38985571bc7Sbellard } 39085571bc7Sbellard return 0; 39185571bc7Sbellard } 39285571bc7Sbellard 39385571bc7Sbellard static void *sdl_audio_init (void) 39485571bc7Sbellard { 39585571bc7Sbellard SDLAudioState *s = &glob_sdl; 39685571bc7Sbellard 39785571bc7Sbellard if (SDL_InitSubSystem (SDL_INIT_AUDIO)) { 3981d14ffa9Sbellard sdl_logerr ("SDL failed to initialize audio subsystem\n"); 39985571bc7Sbellard return NULL; 40085571bc7Sbellard } 40185571bc7Sbellard 40285571bc7Sbellard s->mutex = SDL_CreateMutex (); 40385571bc7Sbellard if (!s->mutex) { 4041d14ffa9Sbellard sdl_logerr ("Failed to create SDL mutex\n"); 40585571bc7Sbellard SDL_QuitSubSystem (SDL_INIT_AUDIO); 40685571bc7Sbellard return NULL; 40785571bc7Sbellard } 40885571bc7Sbellard 40985571bc7Sbellard s->sem = SDL_CreateSemaphore (0); 41085571bc7Sbellard if (!s->sem) { 4111d14ffa9Sbellard sdl_logerr ("Failed to create SDL semaphore\n"); 41285571bc7Sbellard SDL_DestroyMutex (s->mutex); 41385571bc7Sbellard SDL_QuitSubSystem (SDL_INIT_AUDIO); 41485571bc7Sbellard return NULL; 41585571bc7Sbellard } 41685571bc7Sbellard 41785571bc7Sbellard return s; 41885571bc7Sbellard } 41985571bc7Sbellard 42085571bc7Sbellard static void sdl_audio_fini (void *opaque) 42185571bc7Sbellard { 42285571bc7Sbellard SDLAudioState *s = opaque; 42385571bc7Sbellard sdl_close (s); 42485571bc7Sbellard SDL_DestroySemaphore (s->sem); 42585571bc7Sbellard SDL_DestroyMutex (s->mutex); 42685571bc7Sbellard SDL_QuitSubSystem (SDL_INIT_AUDIO); 42785571bc7Sbellard } 42885571bc7Sbellard 4291d14ffa9Sbellard static struct audio_option sdl_options[] = { 43098f9f48cSmalc { 43198f9f48cSmalc .name = "SAMPLES", 4322700efa3SJuan Quintela .tag = AUD_OPT_INT, 4332700efa3SJuan Quintela .valp = &conf.nb_samples, 43498f9f48cSmalc .descr = "Size of SDL buffer in samples" 43598f9f48cSmalc }, 4362700efa3SJuan Quintela { /* End of list */ } 43785571bc7Sbellard }; 43885571bc7Sbellard 43935f4b58cSblueswir1 static struct audio_pcm_ops sdl_pcm_ops = { 4401dd3e4d1SJuan Quintela .init_out = sdl_init_out, 4411dd3e4d1SJuan Quintela .fini_out = sdl_fini_out, 4421dd3e4d1SJuan Quintela .run_out = sdl_run_out, 4431dd3e4d1SJuan Quintela .write = sdl_write_out, 4441dd3e4d1SJuan Quintela .ctl_out = sdl_ctl_out, 4451d14ffa9Sbellard }; 4461d14ffa9Sbellard 4471d14ffa9Sbellard struct audio_driver sdl_audio_driver = { 448bee37f32SJuan Quintela .name = "sdl", 449bee37f32SJuan Quintela .descr = "SDL http://www.libsdl.org", 450bee37f32SJuan Quintela .options = sdl_options, 451bee37f32SJuan Quintela .init = sdl_audio_init, 452bee37f32SJuan Quintela .fini = sdl_audio_fini, 453bee37f32SJuan Quintela .pcm_ops = &sdl_pcm_ops, 454bee37f32SJuan Quintela .can_be_default = 1, 455bee37f32SJuan Quintela .max_voices_out = 1, 456bee37f32SJuan Quintela .max_voices_in = 0, 457bee37f32SJuan Quintela .voice_size_out = sizeof (SDLVoiceOut), 458bee37f32SJuan Quintela .voice_size_in = 0 45985571bc7Sbellard }; 460