* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include <SDL.h>
#include <SDL_thread.h>
#include "qemu-common.h"
#elif defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__DragonFly__)
#include <pthread.h>
#endif
-#include <signal.h>
#endif
#define AUDIO_CAP "sdl"
#include "audio_int.h"
+#define USE_SEMAPHORE (SDL_MAJOR_VERSION < 2)
+
typedef struct SDLVoiceOut {
HWVoiceOut hw;
int live;
+#if USE_SEMAPHORE
int rpos;
+#endif
int decr;
} SDLVoiceOut;
static struct SDLAudioState {
int exit;
+#if USE_SEMAPHORE
SDL_mutex *mutex;
SDL_sem *sem;
+#endif
int initialized;
+ bool driver_created;
} glob_sdl;
typedef struct SDLAudioState SDLAudioState;
static int sdl_lock (SDLAudioState *s, const char *forfn)
{
+#if USE_SEMAPHORE
if (SDL_LockMutex (s->mutex)) {
sdl_logerr ("SDL_LockMutex for %s failed\n", forfn);
return -1;
}
+#else
+ SDL_LockAudio();
+#endif
+
return 0;
}
static int sdl_unlock (SDLAudioState *s, const char *forfn)
{
+#if USE_SEMAPHORE
if (SDL_UnlockMutex (s->mutex)) {
sdl_logerr ("SDL_UnlockMutex for %s failed\n", forfn);
return -1;
}
+#else
+ SDL_UnlockAudio();
+#endif
+
return 0;
}
static int sdl_post (SDLAudioState *s, const char *forfn)
{
+#if USE_SEMAPHORE
if (SDL_SemPost (s->sem)) {
sdl_logerr ("SDL_SemPost for %s failed\n", forfn);
return -1;
}
+#endif
+
return 0;
}
+#if USE_SEMAPHORE
static int sdl_wait (SDLAudioState *s, const char *forfn)
{
if (SDL_SemWait (s->sem)) {
}
return 0;
}
+#endif
static int sdl_unlock_and_post (SDLAudioState *s, const char *forfn)
{
return sdl_post (s, forfn);
}
-static int aud_to_sdlfmt (audfmt_e fmt, int *shift)
+static int aud_to_sdlfmt (audfmt_e fmt)
{
switch (fmt) {
case AUD_FMT_S8:
- *shift = 0;
return AUDIO_S8;
case AUD_FMT_U8:
- *shift = 0;
return AUDIO_U8;
case AUD_FMT_S16:
- *shift = 1;
return AUDIO_S16LSB;
case AUD_FMT_U16:
- *shift = 1;
return AUDIO_U16LSB;
default:
}
}
-static int sdl_to_audfmt (int sdlfmt, audfmt_e *fmt, int *endianess)
+static int sdl_to_audfmt(int sdlfmt, audfmt_e *fmt, int *endianness)
{
switch (sdlfmt) {
case AUDIO_S8:
- *endianess = 0;
+ *endianness = 0;
*fmt = AUD_FMT_S8;
break;
case AUDIO_U8:
- *endianess = 0;
+ *endianness = 0;
*fmt = AUD_FMT_U8;
break;
case AUDIO_S16LSB:
- *endianess = 0;
+ *endianness = 0;
*fmt = AUD_FMT_S16;
break;
case AUDIO_U16LSB:
- *endianess = 0;
+ *endianness = 0;
*fmt = AUD_FMT_U16;
break;
case AUDIO_S16MSB:
- *endianess = 1;
+ *endianness = 1;
*fmt = AUD_FMT_S16;
break;
case AUDIO_U16MSB:
- *endianess = 1;
+ *endianness = 1;
*fmt = AUD_FMT_U16;
break;
{
int status;
#ifndef _WIN32
+ int err;
sigset_t new, old;
/* Make sure potential threads created by SDL don't hog signals. */
- sigfillset (&new);
- pthread_sigmask (SIG_BLOCK, &new, &old);
+ err = sigfillset (&new);
+ if (err) {
+ dolog ("sdl_open: sigfillset failed: %s\n", strerror (errno));
+ return -1;
+ }
+ err = pthread_sigmask (SIG_BLOCK, &new, &old);
+ if (err) {
+ dolog ("sdl_open: pthread_sigmask failed: %s\n", strerror (err));
+ return -1;
+ }
#endif
status = SDL_OpenAudio (req, obt);
}
#ifndef _WIN32
- pthread_sigmask (SIG_SETMASK, &old, NULL);
+ err = pthread_sigmask (SIG_SETMASK, &old, NULL);
+ if (err) {
+ dolog ("sdl_open: pthread_sigmask (restore) failed: %s\n",
+ strerror (errno));
+ /* We have failed to restore original signal mask, all bets are off,
+ so exit the process */
+ exit (EXIT_FAILURE);
+ }
#endif
return status;
}
int to_mix, decr;
/* dolog ("in callback samples=%d\n", samples); */
+#if USE_SEMAPHORE
sdl_wait (s, "sdl_callback");
if (s->exit) {
return;
if (!sdl->live) {
goto again;
}
+#else
+ if (s->exit || !sdl->live) {
+ break;
+ }
+#endif
/* dolog ("in callback live=%d\n", live); */
to_mix = audio_MIN (samples, sdl->live);
/* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */
hw->clip (buf, src, chunk);
+#if USE_SEMAPHORE
sdl->rpos = (sdl->rpos + chunk) % hw->samples;
+#else
+ hw->rpos = (hw->rpos + chunk) % hw->samples;
+#endif
to_mix -= chunk;
buf += chunk << hw->info.shift;
}
sdl->live -= decr;
sdl->decr += decr;
+#if USE_SEMAPHORE
again:
if (sdl_unlock (s, "sdl_callback")) {
return;
}
+#endif
}
/* dolog ("done len=%d\n", len); */
+
+#if (SDL_MAJOR_VERSION >= 2)
+ /* SDL2 does not clear the remaining buffer for us, so do it on our own */
+ if (samples) {
+ memset(buf, 0, samples << hw->info.shift);
+ }
+#endif
}
static int sdl_write_out (SWVoiceOut *sw, void *buf, int len)
return audio_pcm_sw_write (sw, buf, len);
}
-static int sdl_run_out (HWVoiceOut *hw)
+static int sdl_run_out (HWVoiceOut *hw, int live)
{
- int decr, live;
+ int decr;
SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
SDLAudioState *s = &glob_sdl;
- if (sdl_lock (s, "sdl_callback")) {
+ if (sdl_lock (s, "sdl_run_out")) {
return 0;
}
- live = audio_pcm_hw_get_live_out (hw);
-
if (sdl->decr > live) {
ldebug ("sdl->decr %d live %d sdl->live %d\n",
sdl->decr,
decr = audio_MIN (sdl->decr, live);
sdl->decr -= decr;
+#if USE_SEMAPHORE
sdl->live = live - decr;
hw->rpos = sdl->rpos;
+#else
+ sdl->live = live;
+#endif
if (sdl->live > 0) {
- sdl_unlock_and_post (s, "sdl_callback");
+ sdl_unlock_and_post (s, "sdl_run_out");
}
else {
- sdl_unlock (s, "sdl_callback");
+ sdl_unlock (s, "sdl_run_out");
}
return decr;
}
sdl_close (&glob_sdl);
}
-static int sdl_init_out (HWVoiceOut *hw, struct audsettings *as)
+static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as,
+ void *drv_opaque)
{
SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
SDLAudioState *s = &glob_sdl;
SDL_AudioSpec req, obt;
- int shift;
- int endianess;
+ int endianness;
int err;
audfmt_e effective_fmt;
struct audsettings obt_as;
- shift <<= as->nchannels == 2;
-
req.freq = as->freq;
- req.format = aud_to_sdlfmt (as->fmt, &shift);
+ req.format = aud_to_sdlfmt (as->fmt);
req.channels = as->nchannels;
req.samples = conf.nb_samples;
req.callback = sdl_callback;
return -1;
}
- err = sdl_to_audfmt (obt.format, &effective_fmt, &endianess);
+ err = sdl_to_audfmt(obt.format, &effective_fmt, &endianness);
if (err) {
sdl_close (s);
return -1;
obt_as.freq = obt.freq;
obt_as.nchannels = obt.channels;
obt_as.fmt = effective_fmt;
- obt_as.endianness = endianess;
+ obt_as.endianness = endianness;
audio_pcm_init_info (&hw->info, &obt_as);
hw->samples = obt.samples;
static void *sdl_audio_init (void)
{
SDLAudioState *s = &glob_sdl;
+ if (s->driver_created) {
+ sdl_logerr("Can't create multiple sdl backends\n");
+ return NULL;
+ }
if (SDL_InitSubSystem (SDL_INIT_AUDIO)) {
sdl_logerr ("SDL failed to initialize audio subsystem\n");
return NULL;
}
+#if USE_SEMAPHORE
s->mutex = SDL_CreateMutex ();
if (!s->mutex) {
sdl_logerr ("Failed to create SDL mutex\n");
SDL_QuitSubSystem (SDL_INIT_AUDIO);
return NULL;
}
+#endif
+ s->driver_created = true;
return s;
}
{
SDLAudioState *s = opaque;
sdl_close (s);
+#if USE_SEMAPHORE
SDL_DestroySemaphore (s->sem);
SDL_DestroyMutex (s->mutex);
+#endif
SDL_QuitSubSystem (SDL_INIT_AUDIO);
+ s->driver_created = false;
}
static struct audio_option sdl_options[] = {
- {.name = "SAMPLES",
- .tag = AUD_OPT_INT,
- .valp = &conf.nb_samples,
- .descr = "Size of SDL buffer in samples"},
+ {
+ .name = "SAMPLES",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.nb_samples,
+ .descr = "Size of SDL buffer in samples"
+ },
{ /* End of list */ }
};