summaryrefslogtreecommitdiff
path: root/galsa.c
diff options
context:
space:
mode:
Diffstat (limited to 'galsa.c')
-rw-r--r--galsa.c160
1 files changed, 160 insertions, 0 deletions
diff --git a/galsa.c b/galsa.c
new file mode 100644
index 0000000..8e02c61
--- /dev/null
+++ b/galsa.c
@@ -0,0 +1,160 @@
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <ctype.h>
+
+#include "galsa.h"
+
+struct device *device_open(const char *path, unsigned rate, int chan, size_t pktsize)
+{
+ struct device *dev;
+ snd_pcm_hw_params_t *hw_params = NULL;
+ snd_pcm_t *hdl = NULL;
+ char errmsg[256];
+
+#define E(x) do { int err; if ((err = x) < 0) { \
+ snprintf(errmsg, sizeof(errmsg), "%s:%d/%s(): %s() failed: %s (%d)\n",\
+ __FILE__, __LINE__, __func__, #x, snd_strerror(err), err); \
+ goto out_err; \
+ } } while (0)
+
+ printf("Opening alsa://%s with %d ch %dHz...\n", path, chan, rate);
+ E(snd_pcm_open(&hdl, path, SND_PCM_STREAM_CAPTURE, 0));
+ E(snd_pcm_hw_params_malloc(&hw_params));
+ E(snd_pcm_hw_params_any(hdl, hw_params));
+ E(snd_pcm_hw_params_set_access(hdl, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED));
+ E(snd_pcm_hw_params_set_format(hdl, hw_params, SND_PCM_FORMAT_S16_LE));
+ E(snd_pcm_hw_params_set_rate_near(hdl, hw_params, &rate, 0));
+ E(snd_pcm_hw_params_set_channels(hdl, hw_params, chan));
+ E(snd_pcm_hw_params(hdl, hw_params));
+ snd_pcm_hw_params_free(hw_params);
+ hw_params = NULL;
+
+ E(snd_pcm_prepare(hdl));
+ printf("... Ok.\n");
+
+ dev = (struct device *)malloc(sizeof(struct device));
+ dev->path = strdup(path);
+ dev->rate = rate;
+ dev->chan = chan;
+ dev->handle = hdl;
+ dev->thread = NULL;
+ dev->queue = g_async_queue_new();
+ dev->state = DEV_OPEN;
+ dev->pktsize = pktsize;
+ return dev;
+
+out_err:
+ if (hw_params)
+ snd_pcm_hw_params_free(hw_params);
+ if (hdl)
+ snd_pcm_close(hdl);
+ return NULL;
+}
+
+static gpointer threadf(gpointer param)
+{
+ struct device *dev = (struct device *)param;
+ struct qe *ent, *entx;
+ int pcmreturn;
+ ptrdiff_t pos;
+
+ do {
+ ent = malloc(sizeof(struct qe) +
+ dev->pktsize * sizeof(sample) * dev->chan);
+ ent->message = QE_DATA;
+ ent->size = dev->pktsize;
+ pos = 0;
+
+ do {
+ while ((pcmreturn = snd_pcm_readi(dev->handle,
+ ent->samples + pos, ent->size - pos)) < 0) {
+
+ entx = calloc(sizeof(struct qe), 1);
+ entx->message = QE_ERROR;
+ entx->errcode = pcmreturn;
+ asprintf(&entx->errp,
+ "PCM read failed: %s (%d)",
+ snd_strerror(pcmreturn), pcmreturn);
+ g_async_queue_push(dev->queue, entx);
+
+ if (dev->state != DEV_RUNNING) {
+ free(ent);
+ goto out;
+ }
+
+ snd_pcm_prepare(dev->handle);
+ }
+ pos += pcmreturn;
+ } while (pos < ent->size);
+ g_async_queue_push(dev->queue, ent);
+ } while (dev->state == DEV_RUNNING);
+
+out:
+ entx = calloc(sizeof(struct qe), 1);
+ entx->message = QE_STOPPED;
+ g_async_queue_push(dev->queue, entx);
+
+ return NULL;
+}
+
+void device_start(struct device *dev)
+{
+ GError *error = NULL;
+ if (dev->thread)
+ return;
+ dev->state = DEV_RUNNING;
+ dev->thread = g_thread_create(threadf, dev, 0, &error);
+}
+
+void device_stop(struct device *dev)
+{
+ if (dev->state == DEV_RUNNING)
+ dev->state = DEV_STOPREQ;
+}
+
+void device_stop_wait(struct device *dev)
+{
+ int got_stop = 0, iter = 0;
+ struct qe *qe;
+
+ if (dev->state == DEV_OPEN)
+ return;
+
+ device_stop(dev);
+
+ while (!got_stop && iter++ < 10) {
+ qe = (struct qe *)g_async_queue_timeout_pop(dev->queue, 250000);
+ switch (qe->message) {
+ case QE_DATA:
+ break;
+ case QE_ERROR:
+ free(qe->errp);
+ break;
+ case QE_STOPPED:
+ got_stop = 1;
+ break;
+ }
+ free(qe);
+ }
+ if (!got_stop)
+ fprintf(stderr, "failed to terminate recording thread!\n");
+ dev->state = DEV_OPEN;
+}
+
+void device_stop_done(struct device *dev)
+{
+ dev->state = DEV_OPEN;
+}
+
+void device_close(struct device *dev)
+{
+ device_stop_wait(dev);
+
+ snd_pcm_close(dev->handle);
+ free(dev->path);
+ free(dev);
+}
+
+