support multiple sound drivers, add sdl
[libpicofe.git] / sndout_sdl.c
diff --git a/sndout_sdl.c b/sndout_sdl.c
new file mode 100644 (file)
index 0000000..83b3ea2
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * (C) notaz, 2013
+ *
+ * This work is licensed under the terms of any of these licenses
+ * (at your option):
+ *  - GNU GPL, version 2 or later.
+ *  - GNU LGPL, version 2.1 or later.
+ *  - MAME license.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include <SDL.h>
+#include "sndout_sdl.h"
+
+// ~1/3s at 44kHz
+// set this to power of 2
+#define BUF_LEN 32768
+#define BUF_MASK (BUF_LEN - 1)
+
+static short buf[BUF_LEN];
+static int buf_w, buf_r;
+static int started;
+
+static void callback(void *userdata, Uint8 *stream, int len)
+{
+       int have = (buf_w - buf_r) & BUF_MASK;
+       int buf_left = BUF_LEN - buf_r;
+
+       len /= 2;
+       if (have > len)
+               have = len;
+       if (have > 0) {
+               if (have > buf_left) {
+                       memcpy(stream, buf + buf_r, buf_left * 2);
+                       stream += buf_left * 2;
+                       buf_r = 0;
+                       have -= buf_left;
+                       len -= buf_left;
+               }
+               memcpy(stream, buf + buf_r, have * 2);
+               stream += have * 2;
+               buf_r = (buf_r + have) & BUF_MASK;
+               len -= have;
+       }
+
+       if (len > 0) {
+               // put in some silence..
+               memset(stream, 0, len * 2);
+       }
+}
+
+int sndout_sdl_init(void)
+{
+       int ret;
+
+       ret = SDL_InitSubSystem(SDL_INIT_AUDIO);
+       if (ret != 0)
+               return -1;
+
+       return 0;
+}
+
+int sndout_sdl_start(int rate, int stereo)
+{
+       SDL_AudioSpec desired;
+       int samples, shift;
+       int ret;
+
+       if (started)
+               sndout_sdl_stop();
+
+       desired.freq = rate;
+       desired.format = AUDIO_S16LSB;
+       desired.channels = stereo ? 2 : 1;
+       desired.callback = callback;
+       desired.userdata = NULL;
+
+       samples = rate * 4 * 16 / 1000;
+       for (shift = 8; (1 << shift) < samples; shift++)
+               ;
+       desired.samples = 1 << shift;
+
+       ret = SDL_OpenAudio(&desired, NULL);
+       if (ret != 0) {
+               fprintf(stderr, "SDL_OpenAudio: %s\n", SDL_GetError());
+               return -1;
+       }
+
+       buf_w = buf_r = 0;
+
+       SDL_PauseAudio(0);
+       started = 1;
+
+       return 0;
+}
+
+void sndout_sdl_stop(void)
+{
+       SDL_PauseAudio(1);
+       SDL_CloseAudio();
+       started = 0;
+}
+
+void sndout_sdl_wait(void)
+{
+       int left;
+
+       while (1)
+       {
+               left = (buf_r - buf_w - 2) & BUF_MASK;
+               if (left >= BUF_LEN / 2)
+                       break;
+
+               SDL_Delay(4);
+       }
+}
+
+int sndout_sdl_write_nb(const void *samples, int len)
+{
+       int maxlen = (buf_r - buf_w - 2) & BUF_MASK;
+       int buf_left = BUF_LEN - buf_w;
+       int retval;
+
+       len /= 2;
+       if (len > maxlen)
+               // not enough space
+               len = maxlen;
+       if (len == 0)
+               // totally full
+               return 0;
+
+       retval = len;
+
+       if (len > buf_left) {
+               memcpy(buf + buf_w, samples, buf_left * 2);
+               samples = (const short *)samples + buf_left;
+               len -= buf_left;
+               buf_w = 0;
+       }
+       memcpy(buf + buf_w, samples, len * 2);
+       buf_w = (buf_w + len) & BUF_MASK;
+
+       return retval;
+}
+
+void sndout_sdl_exit(void)
+{
+       if (started)
+               sndout_sdl_stop();
+       SDL_QuitSubSystem(SDL_INIT_AUDIO);
+}