summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlfred E. Heggestad <aeh@db.org>2016-01-03 19:42:14 +0100
committerAlfred E. Heggestad <aeh@db.org>2016-01-03 19:42:14 +0100
commit3bc3cf8571ee1f59950687d90364d2819b03faef (patch)
treefa27fe725442dea8dd263fb18ecf73992016fab7
parent0d9365bcf0d97591d4102a63811fc2b099292ca1 (diff)
alsa: add option to specify sample format
- Default sample format is still S16 - You can specify in baresip config: alsa_sample_format float - Supported formats: s16, float, s24_3le Thanks to Ola Palm for code snippets and testing
-rw-r--r--modules/alsa/alsa.c41
-rw-r--r--modules/alsa/alsa.h4
-rw-r--r--modules/alsa/alsa_play.c40
-rw-r--r--modules/alsa/alsa_src.c44
4 files changed, 120 insertions, 9 deletions
diff --git a/modules/alsa/alsa.c b/modules/alsa/alsa.c
index e7d983e..bed4269 100644
--- a/modules/alsa/alsa.c
+++ b/modules/alsa/alsa.c
@@ -28,19 +28,23 @@
char alsa_dev[64] = "default";
+enum aufmt alsa_sample_format = AUFMT_S16LE;
static struct ausrc *ausrc;
static struct auplay *auplay;
int alsa_reset(snd_pcm_t *pcm, uint32_t srate, uint32_t ch,
- uint32_t num_frames)
+ uint32_t num_frames,
+ snd_pcm_format_t pcmfmt)
{
snd_pcm_hw_params_t *hw_params = NULL;
- const snd_pcm_format_t pcmfmt = SND_PCM_FORMAT_S16;
snd_pcm_uframes_t period = num_frames, bufsize = num_frames * 4;
int err;
+ debug("alsa: reset: srate=%u, ch=%u, num_frames=%u, pcmfmt=%s\n",
+ srate, ch, num_frames, snd_pcm_format_name(pcmfmt));
+
err = snd_pcm_hw_params_malloc(&hw_params);
if (err < 0) {
warning("alsa: cannot allocate hw params (%s)\n",
@@ -124,10 +128,43 @@ int alsa_reset(snd_pcm_t *pcm, uint32_t srate, uint32_t ch,
}
+snd_pcm_format_t aufmt_to_alsaformat(enum aufmt fmt)
+{
+ switch (fmt) {
+
+ case AUFMT_S16LE: return SND_PCM_FORMAT_S16;
+ case AUFMT_FLOAT: return SND_PCM_FORMAT_FLOAT;
+ case AUFMT_S24_3LE: return SND_PCM_FORMAT_S24_3LE;
+ default: return SND_PCM_FORMAT_UNKNOWN;
+ }
+}
+
+
static int alsa_init(void)
{
+ struct pl val;
int err;
+ if (0 == conf_get(conf_cur(), "alsa_sample_format", &val)) {
+
+ if (0 == pl_strcasecmp(&val, "s16")) {
+ alsa_sample_format = AUFMT_S16LE;
+ }
+ else if (0 == pl_strcasecmp(&val, "float")) {
+ alsa_sample_format = AUFMT_FLOAT;
+ }
+ else if (0 == pl_strcasecmp(&val, "s24_3le")) {
+ alsa_sample_format = AUFMT_S24_3LE;
+ }
+ else {
+ warning("alsa: unknown sample format '%r'\n", &val);
+ return EINVAL;
+ }
+
+ info("alsa: configured sample format `%s'\n",
+ aufmt_name(alsa_sample_format));
+ }
+
err = ausrc_register(&ausrc, "alsa", alsa_src_alloc);
err |= auplay_register(&auplay, "alsa", alsa_play_alloc);
diff --git a/modules/alsa/alsa.h b/modules/alsa/alsa.h
index a121ebb..61c408c 100644
--- a/modules/alsa/alsa.h
+++ b/modules/alsa/alsa.h
@@ -6,9 +6,11 @@
extern char alsa_dev[64];
+extern enum aufmt alsa_sample_format;
int alsa_reset(snd_pcm_t *pcm, uint32_t srate, uint32_t ch,
- uint32_t num_frames);
+ uint32_t num_frames, snd_pcm_format_t pcmfmt);
+snd_pcm_format_t aufmt_to_alsaformat(enum aufmt fmt);
int alsa_src_alloc(struct ausrc_st **stp, const struct ausrc *as,
struct media_ctx **ctx,
struct ausrc_prm *prm, const char *device,
diff --git a/modules/alsa/alsa_play.c b/modules/alsa/alsa_play.c
index b21ce3d..327acf4 100644
--- a/modules/alsa/alsa_play.c
+++ b/modules/alsa/alsa_play.c
@@ -22,11 +22,13 @@ struct auplay_st {
bool run;
snd_pcm_t *write;
int16_t *sampv;
+ void *xsampv;
size_t sampc;
auplay_write_h *wh;
void *arg;
struct auplay_prm prm;
char *device;
+ enum aufmt aufmt;
};
@@ -45,6 +47,7 @@ static void auplay_destructor(void *arg)
snd_pcm_close(st->write);
mem_deref(st->sampv);
+ mem_deref(st->xsampv);
mem_deref(st->device);
}
@@ -59,14 +62,25 @@ static void *write_thread(void *arg)
while (st->run) {
const int samples = num_frames;
+ void *sampv;
st->wh(st->sampv, st->sampc, st->arg);
- n = snd_pcm_writei(st->write, st->sampv, samples);
+ if (st->aufmt == AUFMT_S16LE) {
+ sampv = st->sampv;
+ }
+ else {
+ sampv = st->xsampv;
+ auconv_from_s16(st->aufmt, st->xsampv,
+ st->sampv, st->sampc);
+ }
+
+ n = snd_pcm_writei(st->write, sampv, samples);
+
if (-EPIPE == n) {
snd_pcm_prepare(st->write);
- n = snd_pcm_writei(st->write, st->sampv, samples);
+ n = snd_pcm_writei(st->write, st->xsampv, samples);
if (n != samples) {
warning("alsa: write error: %s\n",
snd_strerror(n));
@@ -90,6 +104,7 @@ int alsa_play_alloc(struct auplay_st **stp, const struct auplay *ap,
auplay_write_h *wh, void *arg)
{
struct auplay_st *st;
+ snd_pcm_format_t pcmfmt;
int num_frames;
int err;
@@ -111,6 +126,7 @@ int alsa_play_alloc(struct auplay_st **stp, const struct auplay *ap,
st->ap = ap;
st->wh = wh;
st->arg = arg;
+ st->aufmt = alsa_sample_format;
st->sampc = prm->srate * prm->ch * prm->ptime / 1000;
num_frames = st->prm.srate * st->prm.ptime / 1000;
@@ -121,6 +137,15 @@ int alsa_play_alloc(struct auplay_st **stp, const struct auplay *ap,
goto out;
}
+ if (st->aufmt != AUFMT_S16LE) {
+ size_t sz = aufmt_sample_size(st->aufmt) * st->sampc;
+ st->xsampv = mem_alloc(sz, NULL);
+ if (!st->xsampv) {
+ err = ENOMEM;
+ goto out;
+ }
+ }
+
err = snd_pcm_open(&st->write, st->device, SND_PCM_STREAM_PLAYBACK, 0);
if (err < 0) {
warning("alsa: could not open auplay device '%s' (%s)\n",
@@ -128,7 +153,16 @@ int alsa_play_alloc(struct auplay_st **stp, const struct auplay *ap,
goto out;
}
- err = alsa_reset(st->write, st->prm.srate, st->prm.ch, num_frames);
+ pcmfmt = aufmt_to_alsaformat(st->aufmt);
+ if (pcmfmt == SND_PCM_FORMAT_UNKNOWN) {
+ warning("alsa: unknown sample format '%s'\n",
+ aufmt_name(st->aufmt));
+ err = EINVAL;
+ goto out;
+ }
+
+ err = alsa_reset(st->write, st->prm.srate, st->prm.ch, num_frames,
+ pcmfmt);
if (err) {
warning("alsa: could not reset player '%s' (%s)\n",
st->device, snd_strerror(err));
diff --git a/modules/alsa/alsa_src.c b/modules/alsa/alsa_src.c
index 0f611ea..6688f9d 100644
--- a/modules/alsa/alsa_src.c
+++ b/modules/alsa/alsa_src.c
@@ -22,11 +22,13 @@ struct ausrc_st {
bool run;
snd_pcm_t *read;
int16_t *sampv;
+ void *xsampv;
size_t sampc;
ausrc_read_h *rh;
void *arg;
struct ausrc_prm prm;
char *device;
+ enum aufmt aufmt;
};
@@ -45,6 +47,7 @@ static void ausrc_destructor(void *arg)
snd_pcm_close(st->read);
mem_deref(st->sampv);
+ mem_deref(st->xsampv);
mem_deref(st->device);
}
@@ -66,7 +69,15 @@ static void *read_thread(void *arg)
}
while (st->run) {
- err = snd_pcm_readi(st->read, st->sampv, num_frames);
+ size_t sampc;
+ void *sampv;
+
+ if (st->aufmt == AUFMT_S16LE)
+ sampv = st->sampv;
+ else
+ sampv = st->xsampv;
+
+ err = snd_pcm_readi(st->read, sampv, num_frames);
if (err == -EPIPE) {
snd_pcm_prepare(st->read);
continue;
@@ -75,7 +86,14 @@ static void *read_thread(void *arg)
continue;
}
- st->rh(st->sampv, err * st->prm.ch, st->arg);
+ sampc = err * st->prm.ch;
+
+ if (st->aufmt != AUFMT_S16LE) {
+ auconv_to_s16(st->sampv, st->aufmt,
+ st->xsampv, sampc);
+ }
+
+ st->rh(st->sampv, sampc, st->arg);
}
out:
@@ -89,6 +107,7 @@ int alsa_src_alloc(struct ausrc_st **stp, const struct ausrc *as,
ausrc_read_h *rh, ausrc_error_h *errh, void *arg)
{
struct ausrc_st *st;
+ snd_pcm_format_t pcmfmt;
int num_frames;
int err;
(void)ctx;
@@ -112,6 +131,7 @@ int alsa_src_alloc(struct ausrc_st **stp, const struct ausrc *as,
st->as = as;
st->rh = rh;
st->arg = arg;
+ st->aufmt = alsa_sample_format;
st->sampc = prm->srate * prm->ch * prm->ptime / 1000;
num_frames = st->prm.srate * st->prm.ptime / 1000;
@@ -122,6 +142,15 @@ int alsa_src_alloc(struct ausrc_st **stp, const struct ausrc *as,
goto out;
}
+ if (st->aufmt != AUFMT_S16LE) {
+ size_t sz = aufmt_sample_size(st->aufmt) * st->sampc;
+ st->xsampv = mem_alloc(sz, NULL);
+ if (!st->xsampv) {
+ err = ENOMEM;
+ goto out;
+ }
+ }
+
err = snd_pcm_open(&st->read, st->device, SND_PCM_STREAM_CAPTURE, 0);
if (err < 0) {
warning("alsa: could not open ausrc device '%s' (%s)\n",
@@ -129,7 +158,16 @@ int alsa_src_alloc(struct ausrc_st **stp, const struct ausrc *as,
goto out;
}
- err = alsa_reset(st->read, st->prm.srate, st->prm.ch, num_frames);
+ pcmfmt = aufmt_to_alsaformat(st->aufmt);
+ if (pcmfmt == SND_PCM_FORMAT_UNKNOWN) {
+ warning("alsa: unknown sample format '%s'\n",
+ aufmt_name(st->aufmt));
+ err = EINVAL;
+ goto out;
+ }
+
+ err = alsa_reset(st->read, st->prm.srate, st->prm.ch, num_frames,
+ pcmfmt);
if (err) {
warning("alsa: could not reset source '%s' (%s)\n",
st->device, snd_strerror(err));