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