#define _GNU_SOURCE #include #include #include #include #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); }