symbian-qemu-0.9.1-12/qemu-symbian-svp/audio/sdlaudio.c
changeset 1 2fb8b9db1c86
equal deleted inserted replaced
0:ffa851df0825 1:2fb8b9db1c86
       
     1 /*
       
     2  * QEMU SDL audio driver
       
     3  *
       
     4  * Copyright (c) 2004-2005 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.h>
       
    25 #include <SDL_thread.h>
       
    26 #include "qemu-common.h"
       
    27 #include "audio.h"
       
    28 
       
    29 #ifndef _WIN32
       
    30 #ifdef __sun__
       
    31 #define _POSIX_PTHREAD_SEMANTICS 1
       
    32 #elif defined(__OpenBSD__) || defined(__FreeBSD__)
       
    33 #include <pthread.h>
       
    34 #endif
       
    35 #include <signal.h>
       
    36 #endif
       
    37 
       
    38 #define AUDIO_CAP "sdl"
       
    39 #include "audio_int.h"
       
    40 
       
    41 typedef struct SDLVoiceOut {
       
    42     HWVoiceOut hw;
       
    43     int live;
       
    44     int rpos;
       
    45     int decr;
       
    46 } SDLVoiceOut;
       
    47 
       
    48 static struct {
       
    49     int nb_samples;
       
    50 } conf = {
       
    51     1024
       
    52 };
       
    53 
       
    54 static struct SDLAudioState {
       
    55     int exit;
       
    56     SDL_mutex *mutex;
       
    57     SDL_sem *sem;
       
    58     int initialized;
       
    59 } glob_sdl;
       
    60 typedef struct SDLAudioState SDLAudioState;
       
    61 
       
    62 static void GCC_FMT_ATTR (1, 2) sdl_logerr (const char *fmt, ...)
       
    63 {
       
    64     va_list ap;
       
    65 
       
    66     va_start (ap, fmt);
       
    67     AUD_vlog (AUDIO_CAP, fmt, ap);
       
    68     va_end (ap);
       
    69 
       
    70     AUD_log (AUDIO_CAP, "Reason: %s\n", SDL_GetError ());
       
    71 }
       
    72 
       
    73 static int sdl_lock (SDLAudioState *s, const char *forfn)
       
    74 {
       
    75     if (SDL_LockMutex (s->mutex)) {
       
    76         sdl_logerr ("SDL_LockMutex for %s failed\n", forfn);
       
    77         return -1;
       
    78     }
       
    79     return 0;
       
    80 }
       
    81 
       
    82 static int sdl_unlock (SDLAudioState *s, const char *forfn)
       
    83 {
       
    84     if (SDL_UnlockMutex (s->mutex)) {
       
    85         sdl_logerr ("SDL_UnlockMutex for %s failed\n", forfn);
       
    86         return -1;
       
    87     }
       
    88     return 0;
       
    89 }
       
    90 
       
    91 static int sdl_post (SDLAudioState *s, const char *forfn)
       
    92 {
       
    93     if (SDL_SemPost (s->sem)) {
       
    94         sdl_logerr ("SDL_SemPost for %s failed\n", forfn);
       
    95         return -1;
       
    96     }
       
    97     return 0;
       
    98 }
       
    99 
       
   100 static int sdl_wait (SDLAudioState *s, const char *forfn)
       
   101 {
       
   102     if (SDL_SemWait (s->sem)) {
       
   103         sdl_logerr ("SDL_SemWait for %s failed\n", forfn);
       
   104         return -1;
       
   105     }
       
   106     return 0;
       
   107 }
       
   108 
       
   109 static int sdl_unlock_and_post (SDLAudioState *s, const char *forfn)
       
   110 {
       
   111     if (sdl_unlock (s, forfn)) {
       
   112         return -1;
       
   113     }
       
   114 
       
   115     return sdl_post (s, forfn);
       
   116 }
       
   117 
       
   118 static int aud_to_sdlfmt (audfmt_e fmt, int *shift)
       
   119 {
       
   120     switch (fmt) {
       
   121     case AUD_FMT_S8:
       
   122         *shift = 0;
       
   123         return AUDIO_S8;
       
   124 
       
   125     case AUD_FMT_U8:
       
   126         *shift = 0;
       
   127         return AUDIO_U8;
       
   128 
       
   129     case AUD_FMT_S16:
       
   130         *shift = 1;
       
   131         return AUDIO_S16LSB;
       
   132 
       
   133     case AUD_FMT_U16:
       
   134         *shift = 1;
       
   135         return AUDIO_U16LSB;
       
   136 
       
   137     default:
       
   138         dolog ("Internal logic error: Bad audio format %d\n", fmt);
       
   139 #ifdef DEBUG_AUDIO
       
   140         abort ();
       
   141 #endif
       
   142         return AUDIO_U8;
       
   143     }
       
   144 }
       
   145 
       
   146 static int sdl_to_audfmt (int sdlfmt, audfmt_e *fmt, int *endianess)
       
   147 {
       
   148     switch (sdlfmt) {
       
   149     case AUDIO_S8:
       
   150         *endianess = 0;
       
   151         *fmt = AUD_FMT_S8;
       
   152         break;
       
   153 
       
   154     case AUDIO_U8:
       
   155         *endianess = 0;
       
   156         *fmt = AUD_FMT_U8;
       
   157         break;
       
   158 
       
   159     case AUDIO_S16LSB:
       
   160         *endianess = 0;
       
   161         *fmt = AUD_FMT_S16;
       
   162         break;
       
   163 
       
   164     case AUDIO_U16LSB:
       
   165         *endianess = 0;
       
   166         *fmt = AUD_FMT_U16;
       
   167         break;
       
   168 
       
   169     case AUDIO_S16MSB:
       
   170         *endianess = 1;
       
   171         *fmt = AUD_FMT_S16;
       
   172         break;
       
   173 
       
   174     case AUDIO_U16MSB:
       
   175         *endianess = 1;
       
   176         *fmt = AUD_FMT_U16;
       
   177         break;
       
   178 
       
   179     default:
       
   180         dolog ("Unrecognized SDL audio format %d\n", sdlfmt);
       
   181         return -1;
       
   182     }
       
   183 
       
   184     return 0;
       
   185 }
       
   186 
       
   187 static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt)
       
   188 {
       
   189     int status;
       
   190 #ifndef _WIN32
       
   191     sigset_t new, old;
       
   192 
       
   193     /* Make sure potential threads created by SDL don't hog signals.  */
       
   194     sigfillset (&new);
       
   195     pthread_sigmask (SIG_BLOCK, &new, &old);
       
   196 #endif
       
   197 
       
   198     status = SDL_OpenAudio (req, obt);
       
   199     if (status) {
       
   200         sdl_logerr ("SDL_OpenAudio failed\n");
       
   201     }
       
   202 
       
   203 #ifndef _WIN32
       
   204     pthread_sigmask (SIG_SETMASK, &old, 0);
       
   205 #endif
       
   206     return status;
       
   207 }
       
   208 
       
   209 static void sdl_close (SDLAudioState *s)
       
   210 {
       
   211     if (s->initialized) {
       
   212         sdl_lock (s, "sdl_close");
       
   213         s->exit = 1;
       
   214         sdl_unlock_and_post (s, "sdl_close");
       
   215         SDL_PauseAudio (1);
       
   216         SDL_CloseAudio ();
       
   217         s->initialized = 0;
       
   218     }
       
   219 }
       
   220 
       
   221 static void sdl_callback (void *opaque, Uint8 *buf, int len)
       
   222 {
       
   223     SDLVoiceOut *sdl = opaque;
       
   224     SDLAudioState *s = &glob_sdl;
       
   225     HWVoiceOut *hw = &sdl->hw;
       
   226     int samples = len >> hw->info.shift;
       
   227 
       
   228     if (s->exit) {
       
   229         return;
       
   230     }
       
   231 
       
   232     while (samples) {
       
   233         int to_mix, decr;
       
   234 
       
   235         /* dolog ("in callback samples=%d\n", samples); */
       
   236         sdl_wait (s, "sdl_callback");
       
   237         if (s->exit) {
       
   238             return;
       
   239         }
       
   240 
       
   241         if (sdl_lock (s, "sdl_callback")) {
       
   242             return;
       
   243         }
       
   244 
       
   245         if (audio_bug (AUDIO_FUNC, sdl->live < 0 || sdl->live > hw->samples)) {
       
   246             dolog ("sdl->live=%d hw->samples=%d\n",
       
   247                    sdl->live, hw->samples);
       
   248             return;
       
   249         }
       
   250 
       
   251         if (!sdl->live) {
       
   252             goto again;
       
   253         }
       
   254 
       
   255         /* dolog ("in callback live=%d\n", live); */
       
   256         to_mix = audio_MIN (samples, sdl->live);
       
   257         decr = to_mix;
       
   258         while (to_mix) {
       
   259             int chunk = audio_MIN (to_mix, hw->samples - hw->rpos);
       
   260             struct st_sample *src = hw->mix_buf + hw->rpos;
       
   261 
       
   262             /* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */
       
   263             hw->clip (buf, src, chunk);
       
   264             sdl->rpos = (sdl->rpos + chunk) % hw->samples;
       
   265             to_mix -= chunk;
       
   266             buf += chunk << hw->info.shift;
       
   267         }
       
   268         samples -= decr;
       
   269         sdl->live -= decr;
       
   270         sdl->decr += decr;
       
   271 
       
   272     again:
       
   273         if (sdl_unlock (s, "sdl_callback")) {
       
   274             return;
       
   275         }
       
   276     }
       
   277     /* dolog ("done len=%d\n", len); */
       
   278 }
       
   279 
       
   280 static int sdl_write_out (SWVoiceOut *sw, void *buf, int len)
       
   281 {
       
   282     return audio_pcm_sw_write (sw, buf, len);
       
   283 }
       
   284 
       
   285 static int sdl_run_out (HWVoiceOut *hw)
       
   286 {
       
   287     int decr, live;
       
   288     SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
       
   289     SDLAudioState *s = &glob_sdl;
       
   290 
       
   291     if (sdl_lock (s, "sdl_callback")) {
       
   292         return 0;
       
   293     }
       
   294 
       
   295     live = audio_pcm_hw_get_live_out (hw);
       
   296 
       
   297     if (sdl->decr > live) {
       
   298         ldebug ("sdl->decr %d live %d sdl->live %d\n",
       
   299                 sdl->decr,
       
   300                 live,
       
   301                 sdl->live);
       
   302     }
       
   303 
       
   304     decr = audio_MIN (sdl->decr, live);
       
   305     sdl->decr -= decr;
       
   306 
       
   307     sdl->live = live - decr;
       
   308     hw->rpos = sdl->rpos;
       
   309 
       
   310     if (sdl->live > 0) {
       
   311         sdl_unlock_and_post (s, "sdl_callback");
       
   312     }
       
   313     else {
       
   314         sdl_unlock (s, "sdl_callback");
       
   315     }
       
   316     return decr;
       
   317 }
       
   318 
       
   319 static void sdl_fini_out (HWVoiceOut *hw)
       
   320 {
       
   321     (void) hw;
       
   322 
       
   323     sdl_close (&glob_sdl);
       
   324 }
       
   325 
       
   326 static int sdl_init_out (HWVoiceOut *hw, struct audsettings *as)
       
   327 {
       
   328     SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
       
   329     SDLAudioState *s = &glob_sdl;
       
   330     SDL_AudioSpec req, obt;
       
   331     int shift;
       
   332     int endianess;
       
   333     int err;
       
   334     audfmt_e effective_fmt;
       
   335     struct audsettings obt_as;
       
   336 
       
   337     shift <<= as->nchannels == 2;
       
   338 
       
   339     req.freq = as->freq;
       
   340     req.format = aud_to_sdlfmt (as->fmt, &shift);
       
   341     req.channels = as->nchannels;
       
   342     req.samples = conf.nb_samples;
       
   343     req.callback = sdl_callback;
       
   344     req.userdata = sdl;
       
   345 
       
   346     if (sdl_open (&req, &obt)) {
       
   347         return -1;
       
   348     }
       
   349 
       
   350     err = sdl_to_audfmt (obt.format, &effective_fmt, &endianess);
       
   351     if (err) {
       
   352         sdl_close (s);
       
   353         return -1;
       
   354     }
       
   355 
       
   356     obt_as.freq = obt.freq;
       
   357     obt_as.nchannels = obt.channels;
       
   358     obt_as.fmt = effective_fmt;
       
   359     obt_as.endianness = endianess;
       
   360 
       
   361     audio_pcm_init_info (&hw->info, &obt_as);
       
   362     hw->samples = obt.samples;
       
   363 
       
   364     s->initialized = 1;
       
   365     s->exit = 0;
       
   366     SDL_PauseAudio (0);
       
   367     return 0;
       
   368 }
       
   369 
       
   370 static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...)
       
   371 {
       
   372     (void) hw;
       
   373 
       
   374     switch (cmd) {
       
   375     case VOICE_ENABLE:
       
   376         SDL_PauseAudio (0);
       
   377         break;
       
   378 
       
   379     case VOICE_DISABLE:
       
   380         SDL_PauseAudio (1);
       
   381         break;
       
   382     }
       
   383     return 0;
       
   384 }
       
   385 
       
   386 static void *sdl_audio_init (void)
       
   387 {
       
   388     SDLAudioState *s = &glob_sdl;
       
   389 
       
   390     if (SDL_InitSubSystem (SDL_INIT_AUDIO)) {
       
   391         sdl_logerr ("SDL failed to initialize audio subsystem\n");
       
   392         return NULL;
       
   393     }
       
   394 
       
   395     s->mutex = SDL_CreateMutex ();
       
   396     if (!s->mutex) {
       
   397         sdl_logerr ("Failed to create SDL mutex\n");
       
   398         SDL_QuitSubSystem (SDL_INIT_AUDIO);
       
   399         return NULL;
       
   400     }
       
   401 
       
   402     s->sem = SDL_CreateSemaphore (0);
       
   403     if (!s->sem) {
       
   404         sdl_logerr ("Failed to create SDL semaphore\n");
       
   405         SDL_DestroyMutex (s->mutex);
       
   406         SDL_QuitSubSystem (SDL_INIT_AUDIO);
       
   407         return NULL;
       
   408     }
       
   409 
       
   410     return s;
       
   411 }
       
   412 
       
   413 static void sdl_audio_fini (void *opaque)
       
   414 {
       
   415     SDLAudioState *s = opaque;
       
   416     sdl_close (s);
       
   417     SDL_DestroySemaphore (s->sem);
       
   418     SDL_DestroyMutex (s->mutex);
       
   419     SDL_QuitSubSystem (SDL_INIT_AUDIO);
       
   420 }
       
   421 
       
   422 static struct audio_option sdl_options[] = {
       
   423     {"SAMPLES", AUD_OPT_INT, &conf.nb_samples,
       
   424      "Size of SDL buffer in samples", NULL, 0},
       
   425     {NULL, 0, NULL, NULL, NULL, 0}
       
   426 };
       
   427 
       
   428 static struct audio_pcm_ops sdl_pcm_ops = {
       
   429     sdl_init_out,
       
   430     sdl_fini_out,
       
   431     sdl_run_out,
       
   432     sdl_write_out,
       
   433     sdl_ctl_out,
       
   434 
       
   435     NULL,
       
   436     NULL,
       
   437     NULL,
       
   438     NULL,
       
   439     NULL
       
   440 };
       
   441 
       
   442 struct audio_driver sdl_audio_driver = {
       
   443     INIT_FIELD (name           = ) "sdl",
       
   444     INIT_FIELD (descr          = ) "SDL http://www.libsdl.org",
       
   445     INIT_FIELD (options        = ) sdl_options,
       
   446     INIT_FIELD (init           = ) sdl_audio_init,
       
   447     INIT_FIELD (fini           = ) sdl_audio_fini,
       
   448     INIT_FIELD (pcm_ops        = ) &sdl_pcm_ops,
       
   449     INIT_FIELD (can_be_default = ) 1,
       
   450     INIT_FIELD (max_voices_out = ) 1,
       
   451     INIT_FIELD (max_voices_in  = ) 0,
       
   452     INIT_FIELD (voice_size_out = ) sizeof (SDLVoiceOut),
       
   453     INIT_FIELD (voice_size_in  = ) 0
       
   454 };