add preliminary alsa driver too
authorGrazvydas Ignotas <notasas@gmail.com>
Sun, 23 Jun 2013 23:42:36 +0000 (02:42 +0300)
committerGrazvydas Ignotas <notasas@gmail.com>
Mon, 24 Jun 2013 22:32:48 +0000 (01:32 +0300)
linux/sndout_alsa.c [new file with mode: 0644]
linux/sndout_alsa.h [new file with mode: 0644]
sndout.c

diff --git a/linux/sndout_alsa.c b/linux/sndout_alsa.c
new file mode 100644 (file)
index 0000000..c10e39a
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * (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 <stdio.h>
+#include <alsa/asoundlib.h>
+#include <unistd.h>
+
+#include "sndout_alsa.h"
+
+static snd_pcm_t *handle;
+static snd_pcm_uframes_t buffer_size, period_size;
+static unsigned int channels;
+static void *silent_period;
+
+int sndout_alsa_init(void)
+{
+       int ret;
+
+       ret = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
+       if (ret != 0)
+               return -1;
+
+       return 0;
+}
+
+int sndout_alsa_start(int rate_, int stereo)
+{
+       snd_pcm_hw_params_t *hwparams = NULL;
+       unsigned int rate = rate_;
+       int samples, shift;
+       int ret;
+
+       samples = rate * 40 / 1000;
+       for (shift = 8; (1 << shift) < samples; shift++)
+               ;
+       period_size = 1 << shift;
+       buffer_size = 8 * period_size;
+
+       snd_pcm_hw_params_alloca(&hwparams);
+
+       ret  = snd_pcm_hw_params_any(handle, hwparams);
+       ret |= snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
+       ret |= snd_pcm_hw_params_set_format(handle, hwparams, SND_PCM_FORMAT_S16_LE);
+       ret |= snd_pcm_hw_params_set_channels(handle, hwparams, stereo ? 2 : 1);
+       ret |= snd_pcm_hw_params_set_rate_near(handle, hwparams, &rate, 0);
+       ret |= snd_pcm_hw_params_set_buffer_size_near(handle, hwparams, &buffer_size);
+       ret |= snd_pcm_hw_params_set_period_size_near(handle, hwparams, &period_size, NULL);
+
+       if (ret != 0) {
+               fprintf(stderr, "sndout_alsa: failed to set hwparams\n");
+               return -1;
+       }
+
+       ret = snd_pcm_hw_params(handle, hwparams);
+       if (ret != 0) {
+               fprintf(stderr, "sndout_alsa: failed to apply hwparams: %d\n",
+                       ret);
+               return -1;
+       }
+
+       snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size);
+       snd_pcm_hw_params_get_period_size(hwparams, &period_size, NULL);
+       snd_pcm_hw_params_get_channels(hwparams, &channels);
+
+       silent_period = realloc(silent_period, period_size * 2 * channels);
+       if (silent_period != NULL)
+               memset(silent_period, 0, period_size * 2 * channels);
+
+       ret  = snd_pcm_prepare(handle);
+       ret |= snd_pcm_start(handle);
+
+       if (ret != 0) {
+               fprintf(stderr, "sndout_alsa: failed to start pcm: %d\n",
+                       ret);
+               return -1;
+       }
+
+       return 0;
+}
+
+void sndout_alsa_stop(void)
+{
+       // nothing?
+}
+
+void sndout_alsa_wait(void)
+{
+       snd_pcm_sframes_t left;
+
+       while (1)
+       {
+               left = snd_pcm_avail(handle);
+               if (left < 0 || left >= buffer_size / 2)
+                       break;
+
+               usleep(4000);
+       }
+}
+
+int sndout_alsa_write_nb(const void *samples, int len)
+{
+       snd_pcm_sframes_t left;
+       int ret;
+
+       len /= 2;
+       if (channels == 2)
+               len /= 2;
+
+       left = snd_pcm_avail(handle);
+       if (left >= 0 && left < len)
+               return 0;
+
+       ret = snd_pcm_writei(handle, samples, len);
+       if (ret < 0) {
+               ret = snd_pcm_recover(handle, ret, 1);
+               if (ret != 0)
+                       fprintf(stderr, "sndout_alsa snd_pcm_recover: %d\n", ret);
+
+               if (silent_period)
+                       snd_pcm_writei(handle, silent_period, period_size);
+               snd_pcm_writei(handle, samples, len);
+       }
+
+       return len;
+}
+
+void sndout_alsa_exit(void)
+{
+       snd_pcm_close(handle);
+       handle = NULL;
+}
diff --git a/linux/sndout_alsa.h b/linux/sndout_alsa.h
new file mode 100644 (file)
index 0000000..52e95e3
--- /dev/null
@@ -0,0 +1,7 @@
+
+int  sndout_alsa_init(void);
+int  sndout_alsa_start(int rate, int stereo);
+void sndout_alsa_stop(void);
+void sndout_alsa_wait(void);
+int  sndout_alsa_write_nb(const void *samples, int len);
+void sndout_alsa_exit(void);
index ba34f3c..2750423 100644 (file)
--- a/sndout.c
+++ b/sndout.c
@@ -13,6 +13,7 @@
 #include <string.h>
 
 #include "linux/sndout_oss.h"
+#include "linux/sndout_alsa.h"
 #include "sndout_sdl.h"
 #include "sndout.h"
 
@@ -58,6 +59,9 @@ static struct sndout_driver sndout_avail[] =
 #ifdef HAVE_SDL
        SNDOUT_DRIVER(sdl),
 #endif
+#ifdef HAVE_ALSA
+       SNDOUT_DRIVER(alsa),
+#endif
 #ifdef HAVE_OSS
        SNDOUT_DRIVER(oss),
 #endif