qemu/audio/sdlaudio.c
<<
>>
Prefs
   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 "qemu/osdep.h"
  25#include <SDL.h>
  26#include <SDL_thread.h>
  27#include "qemu-common.h"
  28#include "audio.h"
  29
  30#ifndef _WIN32
  31#ifdef __sun__
  32#define _POSIX_PTHREAD_SEMANTICS 1
  33#elif defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__DragonFly__)
  34#include <pthread.h>
  35#endif
  36#endif
  37
  38#define AUDIO_CAP "sdl"
  39#include "audio_int.h"
  40
  41typedef struct SDLVoiceOut {
  42    HWVoiceOut hw;
  43    int live;
  44    int decr;
  45} SDLVoiceOut;
  46
  47static struct SDLAudioState {
  48    int exit;
  49    int initialized;
  50    bool driver_created;
  51    Audiodev *dev;
  52} glob_sdl;
  53typedef struct SDLAudioState SDLAudioState;
  54
  55static void GCC_FMT_ATTR (1, 2) sdl_logerr (const char *fmt, ...)
  56{
  57    va_list ap;
  58
  59    va_start (ap, fmt);
  60    AUD_vlog (AUDIO_CAP, fmt, ap);
  61    va_end (ap);
  62
  63    AUD_log (AUDIO_CAP, "Reason: %s\n", SDL_GetError ());
  64}
  65
  66static int aud_to_sdlfmt (AudioFormat fmt)
  67{
  68    switch (fmt) {
  69    case AUDIO_FORMAT_S8:
  70        return AUDIO_S8;
  71
  72    case AUDIO_FORMAT_U8:
  73        return AUDIO_U8;
  74
  75    case AUDIO_FORMAT_S16:
  76        return AUDIO_S16LSB;
  77
  78    case AUDIO_FORMAT_U16:
  79        return AUDIO_U16LSB;
  80
  81    default:
  82        dolog ("Internal logic error: Bad audio format %d\n", fmt);
  83#ifdef DEBUG_AUDIO
  84        abort ();
  85#endif
  86        return AUDIO_U8;
  87    }
  88}
  89
  90static int sdl_to_audfmt(int sdlfmt, AudioFormat *fmt, int *endianness)
  91{
  92    switch (sdlfmt) {
  93    case AUDIO_S8:
  94        *endianness = 0;
  95        *fmt = AUDIO_FORMAT_S8;
  96        break;
  97
  98    case AUDIO_U8:
  99        *endianness = 0;
 100        *fmt = AUDIO_FORMAT_U8;
 101        break;
 102
 103    case AUDIO_S16LSB:
 104        *endianness = 0;
 105        *fmt = AUDIO_FORMAT_S16;
 106        break;
 107
 108    case AUDIO_U16LSB:
 109        *endianness = 0;
 110        *fmt = AUDIO_FORMAT_U16;
 111        break;
 112
 113    case AUDIO_S16MSB:
 114        *endianness = 1;
 115        *fmt = AUDIO_FORMAT_S16;
 116        break;
 117
 118    case AUDIO_U16MSB:
 119        *endianness = 1;
 120        *fmt = AUDIO_FORMAT_U16;
 121        break;
 122
 123    default:
 124        dolog ("Unrecognized SDL audio format %d\n", sdlfmt);
 125        return -1;
 126    }
 127
 128    return 0;
 129}
 130
 131static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt)
 132{
 133    int status;
 134#ifndef _WIN32
 135    int err;
 136    sigset_t new, old;
 137
 138    /* Make sure potential threads created by SDL don't hog signals.  */
 139    err = sigfillset (&new);
 140    if (err) {
 141        dolog ("sdl_open: sigfillset failed: %s\n", strerror (errno));
 142        return -1;
 143    }
 144    err = pthread_sigmask (SIG_BLOCK, &new, &old);
 145    if (err) {
 146        dolog ("sdl_open: pthread_sigmask failed: %s\n", strerror (err));
 147        return -1;
 148    }
 149#endif
 150
 151    status = SDL_OpenAudio (req, obt);
 152    if (status) {
 153        sdl_logerr ("SDL_OpenAudio failed\n");
 154    }
 155
 156#ifndef _WIN32
 157    err = pthread_sigmask (SIG_SETMASK, &old, NULL);
 158    if (err) {
 159        dolog ("sdl_open: pthread_sigmask (restore) failed: %s\n",
 160               strerror (errno));
 161        /* We have failed to restore original signal mask, all bets are off,
 162           so exit the process */
 163        exit (EXIT_FAILURE);
 164    }
 165#endif
 166    return status;
 167}
 168
 169static void sdl_close (SDLAudioState *s)
 170{
 171    if (s->initialized) {
 172        SDL_LockAudio();
 173        s->exit = 1;
 174        SDL_UnlockAudio();
 175        SDL_PauseAudio (1);
 176        SDL_CloseAudio ();
 177        s->initialized = 0;
 178    }
 179}
 180
 181static void sdl_callback (void *opaque, Uint8 *buf, int len)
 182{
 183    SDLVoiceOut *sdl = opaque;
 184    SDLAudioState *s = &glob_sdl;
 185    HWVoiceOut *hw = &sdl->hw;
 186    int samples = len >> hw->info.shift;
 187    int to_mix, decr;
 188
 189    if (s->exit || !sdl->live) {
 190        return;
 191    }
 192
 193    /* dolog ("in callback samples=%d live=%d\n", samples, sdl->live); */
 194
 195    to_mix = audio_MIN(samples, sdl->live);
 196    decr = to_mix;
 197    while (to_mix) {
 198        int chunk = audio_MIN(to_mix, hw->samples - hw->rpos);
 199        struct st_sample *src = hw->mix_buf + hw->rpos;
 200
 201        /* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */
 202        hw->clip(buf, src, chunk);
 203        hw->rpos = (hw->rpos + chunk) % hw->samples;
 204        to_mix -= chunk;
 205        buf += chunk << hw->info.shift;
 206    }
 207    samples -= decr;
 208    sdl->live -= decr;
 209    sdl->decr += decr;
 210
 211    /* dolog ("done len=%d\n", len); */
 212
 213    /* SDL2 does not clear the remaining buffer for us, so do it on our own */
 214    if (samples) {
 215        memset(buf, 0, samples << hw->info.shift);
 216    }
 217}
 218
 219static int sdl_write_out (SWVoiceOut *sw, void *buf, int len)
 220{
 221    return audio_pcm_sw_write (sw, buf, len);
 222}
 223
 224static int sdl_run_out (HWVoiceOut *hw, int live)
 225{
 226    int decr;
 227    SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
 228
 229    SDL_LockAudio();
 230
 231    if (sdl->decr > live) {
 232        ldebug ("sdl->decr %d live %d sdl->live %d\n",
 233                sdl->decr,
 234                live,
 235                sdl->live);
 236    }
 237
 238    decr = audio_MIN (sdl->decr, live);
 239    sdl->decr -= decr;
 240
 241    sdl->live = live;
 242
 243    SDL_UnlockAudio();
 244
 245    return decr;
 246}
 247
 248static void sdl_fini_out (HWVoiceOut *hw)
 249{
 250    (void) hw;
 251
 252    sdl_close (&glob_sdl);
 253}
 254
 255static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as,
 256                        void *drv_opaque)
 257{
 258    SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
 259    SDLAudioState *s = &glob_sdl;
 260    SDL_AudioSpec req, obt;
 261    int endianness;
 262    int err;
 263    AudioFormat effective_fmt;
 264    struct audsettings obt_as;
 265
 266    req.freq = as->freq;
 267    req.format = aud_to_sdlfmt (as->fmt);
 268    req.channels = as->nchannels;
 269    req.samples = audio_buffer_samples(s->dev->u.sdl.out, as, 11610);
 270    req.callback = sdl_callback;
 271    req.userdata = sdl;
 272
 273    if (sdl_open (&req, &obt)) {
 274        return -1;
 275    }
 276
 277    err = sdl_to_audfmt(obt.format, &effective_fmt, &endianness);
 278    if (err) {
 279        sdl_close (s);
 280        return -1;
 281    }
 282
 283    obt_as.freq = obt.freq;
 284    obt_as.nchannels = obt.channels;
 285    obt_as.fmt = effective_fmt;
 286    obt_as.endianness = endianness;
 287
 288    audio_pcm_init_info (&hw->info, &obt_as);
 289    hw->samples = obt.samples;
 290
 291    s->initialized = 1;
 292    s->exit = 0;
 293    SDL_PauseAudio (0);
 294    return 0;
 295}
 296
 297static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...)
 298{
 299    (void) hw;
 300
 301    switch (cmd) {
 302    case VOICE_ENABLE:
 303        SDL_PauseAudio (0);
 304        break;
 305
 306    case VOICE_DISABLE:
 307        SDL_PauseAudio (1);
 308        break;
 309    }
 310    return 0;
 311}
 312
 313static void *sdl_audio_init(Audiodev *dev)
 314{
 315    SDLAudioState *s = &glob_sdl;
 316    if (s->driver_created) {
 317        sdl_logerr("Can't create multiple sdl backends\n");
 318        return NULL;
 319    }
 320
 321    if (SDL_InitSubSystem (SDL_INIT_AUDIO)) {
 322        sdl_logerr ("SDL failed to initialize audio subsystem\n");
 323        return NULL;
 324    }
 325
 326    s->driver_created = true;
 327    s->dev = dev;
 328    return s;
 329}
 330
 331static void sdl_audio_fini (void *opaque)
 332{
 333    SDLAudioState *s = opaque;
 334    sdl_close (s);
 335    SDL_QuitSubSystem (SDL_INIT_AUDIO);
 336    s->driver_created = false;
 337    s->dev = NULL;
 338}
 339
 340static struct audio_pcm_ops sdl_pcm_ops = {
 341    .init_out = sdl_init_out,
 342    .fini_out = sdl_fini_out,
 343    .run_out  = sdl_run_out,
 344    .write    = sdl_write_out,
 345    .ctl_out  = sdl_ctl_out,
 346};
 347
 348static struct audio_driver sdl_audio_driver = {
 349    .name           = "sdl",
 350    .descr          = "SDL http://www.libsdl.org",
 351    .init           = sdl_audio_init,
 352    .fini           = sdl_audio_fini,
 353    .pcm_ops        = &sdl_pcm_ops,
 354    .can_be_default = 1,
 355    .max_voices_out = 1,
 356    .max_voices_in  = 0,
 357    .voice_size_out = sizeof (SDLVoiceOut),
 358    .voice_size_in  = 0
 359};
 360
 361static void register_audio_sdl(void)
 362{
 363    audio_driver_register(&sdl_audio_driver);
 364}
 365type_init(register_audio_sdl);
 366