xref: /qemu/audio/sdlaudio.c (revision 9f059eca52670b0fb2cc4562e97358331672fc0e)
185571bc7Sbellard /*
285571bc7Sbellard  * QEMU SDL audio output driver
385571bc7Sbellard  *
485571bc7Sbellard  * Copyright (c) 2004 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  */
24*9f059ecaSbellard #include <SDL.h>
25*9f059ecaSbellard #include <SDL_thread.h>
2685571bc7Sbellard #include "vl.h"
2785571bc7Sbellard 
28fb065187Sbellard #include "audio/audio_int.h"
29fb065187Sbellard 
30fb065187Sbellard typedef struct SDLVoice {
31fb065187Sbellard     HWVoice hw;
32fb065187Sbellard } SDLVoice;
33fb065187Sbellard 
34fb065187Sbellard #define dolog(...) AUD_log ("sdl", __VA_ARGS__)
35fb065187Sbellard #ifdef DEBUG
36fb065187Sbellard #define ldebug(...) dolog (__VA_ARGS__)
37fb065187Sbellard #else
38fb065187Sbellard #define ldebug(...)
39fb065187Sbellard #endif
4085571bc7Sbellard 
4185571bc7Sbellard #define QC_SDL_SAMPLES "QEMU_SDL_SAMPLES"
4285571bc7Sbellard 
4385571bc7Sbellard #define errstr() SDL_GetError ()
4485571bc7Sbellard 
4585571bc7Sbellard static struct {
4685571bc7Sbellard     int nb_samples;
4785571bc7Sbellard } conf = {
4885571bc7Sbellard     1024
4985571bc7Sbellard };
5085571bc7Sbellard 
5185571bc7Sbellard struct SDLAudioState {
5285571bc7Sbellard     int exit;
5385571bc7Sbellard     SDL_mutex *mutex;
5485571bc7Sbellard     SDL_sem *sem;
5585571bc7Sbellard     int initialized;
5685571bc7Sbellard } glob_sdl;
5785571bc7Sbellard typedef struct SDLAudioState SDLAudioState;
5885571bc7Sbellard 
5985571bc7Sbellard static void sdl_hw_run (HWVoice *hw)
6085571bc7Sbellard {
6185571bc7Sbellard     (void) hw;
6285571bc7Sbellard }
6385571bc7Sbellard 
6485571bc7Sbellard static int sdl_lock (SDLAudioState *s)
6585571bc7Sbellard {
6685571bc7Sbellard     if (SDL_LockMutex (s->mutex)) {
6785571bc7Sbellard         dolog ("SDL_LockMutex failed\nReason: %s\n", errstr ());
6885571bc7Sbellard         return -1;
6985571bc7Sbellard     }
7085571bc7Sbellard     return 0;
7185571bc7Sbellard }
7285571bc7Sbellard 
7385571bc7Sbellard static int sdl_unlock (SDLAudioState *s)
7485571bc7Sbellard {
7585571bc7Sbellard     if (SDL_UnlockMutex (s->mutex)) {
7685571bc7Sbellard         dolog ("SDL_UnlockMutex failed\nReason: %s\n", errstr ());
7785571bc7Sbellard         return -1;
7885571bc7Sbellard     }
7985571bc7Sbellard     return 0;
8085571bc7Sbellard }
8185571bc7Sbellard 
8285571bc7Sbellard static int sdl_post (SDLAudioState *s)
8385571bc7Sbellard {
8485571bc7Sbellard     if (SDL_SemPost (s->sem)) {
8585571bc7Sbellard         dolog ("SDL_SemPost failed\nReason: %s\n", errstr ());
8685571bc7Sbellard         return -1;
8785571bc7Sbellard     }
8885571bc7Sbellard     return 0;
8985571bc7Sbellard }
9085571bc7Sbellard 
9185571bc7Sbellard static int sdl_wait (SDLAudioState *s)
9285571bc7Sbellard {
9385571bc7Sbellard     if (SDL_SemWait (s->sem)) {
9485571bc7Sbellard         dolog ("SDL_SemWait failed\nReason: %s\n", errstr ());
9585571bc7Sbellard         return -1;
9685571bc7Sbellard     }
9785571bc7Sbellard     return 0;
9885571bc7Sbellard }
9985571bc7Sbellard 
10085571bc7Sbellard static int sdl_unlock_and_post (SDLAudioState *s)
10185571bc7Sbellard {
10285571bc7Sbellard     if (sdl_unlock (s))
10385571bc7Sbellard         return -1;
10485571bc7Sbellard 
10585571bc7Sbellard     return sdl_post (s);
10685571bc7Sbellard }
10785571bc7Sbellard 
10885571bc7Sbellard static int sdl_hw_write (SWVoice *sw, void *buf, int len)
10985571bc7Sbellard {
11085571bc7Sbellard     int ret;
11185571bc7Sbellard     SDLAudioState *s = &glob_sdl;
11285571bc7Sbellard     sdl_lock (s);
11385571bc7Sbellard     ret = pcm_hw_write (sw, buf, len);
11485571bc7Sbellard     sdl_unlock_and_post (s);
11585571bc7Sbellard     return ret;
11685571bc7Sbellard }
11785571bc7Sbellard 
11885571bc7Sbellard static int AUD_to_sdlfmt (audfmt_e fmt, int *shift)
11985571bc7Sbellard {
12085571bc7Sbellard     *shift = 0;
12185571bc7Sbellard     switch (fmt) {
12285571bc7Sbellard     case AUD_FMT_S8: return AUDIO_S8;
12385571bc7Sbellard     case AUD_FMT_U8: return AUDIO_U8;
12485571bc7Sbellard     case AUD_FMT_S16: *shift = 1; return AUDIO_S16LSB;
12585571bc7Sbellard     case AUD_FMT_U16: *shift = 1; return AUDIO_U16LSB;
12685571bc7Sbellard     default:
12785571bc7Sbellard         dolog ("Internal logic error: Bad audio format %d\nAborting\n", fmt);
12885571bc7Sbellard         exit (EXIT_FAILURE);
12985571bc7Sbellard     }
13085571bc7Sbellard }
13185571bc7Sbellard 
13285571bc7Sbellard static int sdl_to_audfmt (int fmt)
13385571bc7Sbellard {
13485571bc7Sbellard     switch (fmt) {
13585571bc7Sbellard     case AUDIO_S8: return AUD_FMT_S8;
13685571bc7Sbellard     case AUDIO_U8: return AUD_FMT_U8;
13785571bc7Sbellard     case AUDIO_S16LSB: return AUD_FMT_S16;
13885571bc7Sbellard     case AUDIO_U16LSB: return AUD_FMT_U16;
13985571bc7Sbellard     default:
14085571bc7Sbellard         dolog ("Internal logic error: Unrecognized SDL audio format %d\n"
14185571bc7Sbellard                "Aborting\n", fmt);
14285571bc7Sbellard         exit (EXIT_FAILURE);
14385571bc7Sbellard     }
14485571bc7Sbellard }
14585571bc7Sbellard 
14685571bc7Sbellard static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt)
14785571bc7Sbellard {
14885571bc7Sbellard     int status;
14985571bc7Sbellard 
15085571bc7Sbellard     status = SDL_OpenAudio (req, obt);
15185571bc7Sbellard     if (status) {
15285571bc7Sbellard         dolog ("SDL_OpenAudio failed\nReason: %s\n", errstr ());
15385571bc7Sbellard     }
15485571bc7Sbellard     return status;
15585571bc7Sbellard }
15685571bc7Sbellard 
15785571bc7Sbellard static void sdl_close (SDLAudioState *s)
15885571bc7Sbellard {
15985571bc7Sbellard     if (s->initialized) {
16085571bc7Sbellard         sdl_lock (s);
16185571bc7Sbellard         s->exit = 1;
16285571bc7Sbellard         sdl_unlock_and_post (s);
16385571bc7Sbellard         SDL_PauseAudio (1);
16485571bc7Sbellard         SDL_CloseAudio ();
16585571bc7Sbellard         s->initialized = 0;
16685571bc7Sbellard     }
16785571bc7Sbellard }
16885571bc7Sbellard 
16985571bc7Sbellard static void sdl_callback (void *opaque, Uint8 *buf, int len)
17085571bc7Sbellard {
17185571bc7Sbellard     SDLVoice *sdl = opaque;
17285571bc7Sbellard     SDLAudioState *s = &glob_sdl;
17385571bc7Sbellard     HWVoice *hw = &sdl->hw;
17485571bc7Sbellard     int samples = len >> hw->shift;
17585571bc7Sbellard 
17685571bc7Sbellard     if (s->exit) {
17785571bc7Sbellard         return;
17885571bc7Sbellard     }
17985571bc7Sbellard 
18085571bc7Sbellard     while (samples) {
18185571bc7Sbellard         int to_mix, live, decr;
18285571bc7Sbellard 
18385571bc7Sbellard         /* dolog ("in callback samples=%d\n", samples); */
18485571bc7Sbellard         sdl_wait (s);
18585571bc7Sbellard         if (s->exit) {
18685571bc7Sbellard             return;
18785571bc7Sbellard         }
18885571bc7Sbellard 
18985571bc7Sbellard         sdl_lock (s);
19085571bc7Sbellard         live = pcm_hw_get_live (hw);
19185571bc7Sbellard         if (live <= 0)
19285571bc7Sbellard             goto again;
19385571bc7Sbellard 
19485571bc7Sbellard         /* dolog ("in callback live=%d\n", live); */
19585571bc7Sbellard         to_mix = audio_MIN (samples, live);
19685571bc7Sbellard         decr = to_mix;
19785571bc7Sbellard         while (to_mix) {
19885571bc7Sbellard             int chunk = audio_MIN (to_mix, hw->samples - hw->rpos);
19985571bc7Sbellard             st_sample_t *src = hw->mix_buf + hw->rpos;
20085571bc7Sbellard 
20185571bc7Sbellard             /* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */
20285571bc7Sbellard             hw->clip (buf, src, chunk);
20385571bc7Sbellard             memset (src, 0, chunk * sizeof (st_sample_t));
20485571bc7Sbellard             hw->rpos = (hw->rpos + chunk) % hw->samples;
20585571bc7Sbellard             to_mix -= chunk;
20685571bc7Sbellard             buf += chunk << hw->shift;
20785571bc7Sbellard         }
20885571bc7Sbellard         samples -= decr;
20985571bc7Sbellard         pcm_hw_dec_live (hw, decr);
21085571bc7Sbellard 
21185571bc7Sbellard     again:
21285571bc7Sbellard         sdl_unlock (s);
21385571bc7Sbellard     }
21485571bc7Sbellard     /* dolog ("done len=%d\n", len); */
21585571bc7Sbellard }
21685571bc7Sbellard 
21785571bc7Sbellard static void sdl_hw_fini (HWVoice *hw)
21885571bc7Sbellard {
21985571bc7Sbellard     ldebug ("sdl_hw_fini %d fixed=%d\n",
22085571bc7Sbellard              glob_sdl.initialized, audio_conf.fixed_format);
22185571bc7Sbellard     sdl_close (&glob_sdl);
22285571bc7Sbellard }
22385571bc7Sbellard 
22485571bc7Sbellard static int sdl_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt)
22585571bc7Sbellard {
22685571bc7Sbellard     SDLVoice *sdl = (SDLVoice *) hw;
22785571bc7Sbellard     SDLAudioState *s = &glob_sdl;
22885571bc7Sbellard     SDL_AudioSpec req, obt;
22985571bc7Sbellard     int shift;
23085571bc7Sbellard 
23185571bc7Sbellard     ldebug ("sdl_hw_init %d freq=%d fixed=%d\n",
23285571bc7Sbellard             s->initialized, freq, audio_conf.fixed_format);
23385571bc7Sbellard 
23485571bc7Sbellard     if (nchannels != 2) {
23585571bc7Sbellard         dolog ("Bogus channel count %d\n", nchannels);
23685571bc7Sbellard         return -1;
23785571bc7Sbellard     }
23885571bc7Sbellard 
23985571bc7Sbellard     req.freq = freq;
24085571bc7Sbellard     req.format = AUD_to_sdlfmt (fmt, &shift);
24185571bc7Sbellard     req.channels = nchannels;
24285571bc7Sbellard     req.samples = conf.nb_samples;
24385571bc7Sbellard     shift <<= nchannels == 2;
24485571bc7Sbellard 
24585571bc7Sbellard     req.callback = sdl_callback;
24685571bc7Sbellard     req.userdata = sdl;
24785571bc7Sbellard 
24885571bc7Sbellard     if (sdl_open (&req, &obt))
24985571bc7Sbellard         return -1;
25085571bc7Sbellard 
25185571bc7Sbellard     hw->freq = obt.freq;
25285571bc7Sbellard     hw->fmt = sdl_to_audfmt (obt.format);
25385571bc7Sbellard     hw->nchannels = obt.channels;
25485571bc7Sbellard     hw->bufsize = obt.samples << shift;
25585571bc7Sbellard 
25685571bc7Sbellard     s->initialized = 1;
25785571bc7Sbellard     s->exit = 0;
25885571bc7Sbellard     SDL_PauseAudio (0);
25985571bc7Sbellard     return 0;
26085571bc7Sbellard }
26185571bc7Sbellard 
26285571bc7Sbellard static int sdl_hw_ctl (HWVoice *hw, int cmd, ...)
26385571bc7Sbellard {
26485571bc7Sbellard     (void) hw;
26585571bc7Sbellard 
26685571bc7Sbellard     switch (cmd) {
26785571bc7Sbellard     case VOICE_ENABLE:
26885571bc7Sbellard         SDL_PauseAudio (0);
26985571bc7Sbellard         break;
27085571bc7Sbellard 
27185571bc7Sbellard     case VOICE_DISABLE:
27285571bc7Sbellard         SDL_PauseAudio (1);
27385571bc7Sbellard         break;
27485571bc7Sbellard     }
27585571bc7Sbellard     return 0;
27685571bc7Sbellard }
27785571bc7Sbellard 
27885571bc7Sbellard static void *sdl_audio_init (void)
27985571bc7Sbellard {
28085571bc7Sbellard     SDLAudioState *s = &glob_sdl;
28185571bc7Sbellard     conf.nb_samples = audio_get_conf_int (QC_SDL_SAMPLES, conf.nb_samples);
28285571bc7Sbellard 
28385571bc7Sbellard     if (SDL_InitSubSystem (SDL_INIT_AUDIO)) {
28485571bc7Sbellard         dolog ("SDL failed to initialize audio subsystem\nReason: %s\n",
28585571bc7Sbellard                errstr ());
28685571bc7Sbellard         return NULL;
28785571bc7Sbellard     }
28885571bc7Sbellard 
28985571bc7Sbellard     s->mutex = SDL_CreateMutex ();
29085571bc7Sbellard     if (!s->mutex) {
29185571bc7Sbellard         dolog ("Failed to create SDL mutex\nReason: %s\n", errstr ());
29285571bc7Sbellard         SDL_QuitSubSystem (SDL_INIT_AUDIO);
29385571bc7Sbellard         return NULL;
29485571bc7Sbellard     }
29585571bc7Sbellard 
29685571bc7Sbellard     s->sem = SDL_CreateSemaphore (0);
29785571bc7Sbellard     if (!s->sem) {
29885571bc7Sbellard         dolog ("Failed to create SDL semaphore\nReason: %s\n", errstr ());
29985571bc7Sbellard         SDL_DestroyMutex (s->mutex);
30085571bc7Sbellard         SDL_QuitSubSystem (SDL_INIT_AUDIO);
30185571bc7Sbellard         return NULL;
30285571bc7Sbellard     }
30385571bc7Sbellard 
30485571bc7Sbellard     return s;
30585571bc7Sbellard }
30685571bc7Sbellard 
30785571bc7Sbellard static void sdl_audio_fini (void *opaque)
30885571bc7Sbellard {
30985571bc7Sbellard     SDLAudioState *s = opaque;
31085571bc7Sbellard     sdl_close (s);
31185571bc7Sbellard     SDL_DestroySemaphore (s->sem);
31285571bc7Sbellard     SDL_DestroyMutex (s->mutex);
31385571bc7Sbellard     SDL_QuitSubSystem (SDL_INIT_AUDIO);
31485571bc7Sbellard }
31585571bc7Sbellard 
31685571bc7Sbellard struct pcm_ops sdl_pcm_ops = {
31785571bc7Sbellard     sdl_hw_init,
31885571bc7Sbellard     sdl_hw_fini,
31985571bc7Sbellard     sdl_hw_run,
32085571bc7Sbellard     sdl_hw_write,
32185571bc7Sbellard     sdl_hw_ctl
32285571bc7Sbellard };
32385571bc7Sbellard 
32485571bc7Sbellard struct audio_output_driver sdl_output_driver = {
32585571bc7Sbellard     "sdl",
32685571bc7Sbellard     sdl_audio_init,
32785571bc7Sbellard     sdl_audio_fini,
32885571bc7Sbellard     &sdl_pcm_ops,
32985571bc7Sbellard     1,
33085571bc7Sbellard     1,
33185571bc7Sbellard     sizeof (SDLVoice)
33285571bc7Sbellard };
333