Recently Written · git

sad

sad (2f30.org simple audio daemon) with a Cygwin/OSS port on branch cygwin

git clone https://github.com/equwal/sad

Log | Files | Refs


commit 70dc06dc013ce4da8ab30f835bc81fefbbe6d78d
sin <sin@2f30.org>
2014-12-30 17:00:57 +0000

Add initial implementation of resampler

 Makefile     |   2 +-
 config.def.h |   8 +--
 library.c    |   4 ++
 mp3.c        |   2 +
 output.c     | 178 ++++++++++++++++++++++++++++++++++++++++-------------------
 sad.h        |   3 +-
 vorbis.c     |   2 +
 wav.c        |   2 +
 8 files changed, 137 insertions(+), 64 deletions(-)
diff --git a/Makefile b/Makefile
index b778bfd..c261f37 100644
--- a/Makefile
+++ b/Makefile
@@ -5,7 +5,7 @@ MANPREFIX = $(PREFIX)/man
 
 CFLAGS = -I/usr/local/include
 LDFLAGS = -L /usr/local/lib
-LDLIBS = -lsndfile -lmpg123 -lsndio -lasound -lvorbisfile
+LDLIBS = -lsndfile -lmpg123 -lsndio -lasound -lvorbisfile -lsoxr
 
 OBJ =\
 	alsa.o\
diff --git a/config.def.h b/config.def.h
index 268e70f..116eee9 100644
--- a/config.def.h
+++ b/config.def.h
@@ -1,5 +1,5 @@
-Outputdesc Outputdescs[] = {
-	{ "sndio", 16, 44100, 2, 1, 0, &sndiooutput },
-	{ "alsa" , 16, 44100, 2, 1, 0, &alsaoutput  },
-	{ "fifo",  16, 44100, 2, 1, 0, &fifooutput  },
+Outputdesc outputdescs[] = {
+	{ "sndio", 16, 44100, 2, 0, 0, &sndiooutput, NULL, -1 },
+	{ "alsa" , 16, 44100, 2, 1, 0, &alsaoutput,  NULL, -1 },
+	{ "fifo",  16, 44100, 2, 0, 0, &fifooutput,  NULL, -1 },
 };
diff --git a/library.c b/library.c
index e200b89..a857b5e 100644
--- a/library.c
+++ b/library.c
@@ -7,6 +7,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
 
 #include "sad.h"
 
@@ -19,6 +20,9 @@ addlibrary(const char *path)
 	Decoder *d;
 	Song    *s;
 
+	if (access(path, F_OK) < 0)
+		return NULL;
+
 	s = findsong(path);
 	if (s)
 		return s;
diff --git a/mp3.c b/mp3.c
index d7ebd30..d2c20b8 100644
--- a/mp3.c
+++ b/mp3.c
@@ -44,6 +44,8 @@ mp3open(const char *name)
 		goto err0;
 	}
 
+	initresamplers(rate);
+
 	return 0;
 err0:
 	mpg123_close(hdl);
diff --git a/output.c b/output.c
index c40db2d..444e900 100644
--- a/output.c
+++ b/output.c
@@ -3,8 +3,10 @@
 #include <err.h>
 #include <limits.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <strings.h>
+#include <soxr.h>
 
 #include "sad.h"
 
@@ -16,34 +18,74 @@ typedef struct {
 	int     enabled;
 	int     active;
 	Output *output;
+	soxr_t  resampler;
+	int     inrate;
 } Outputdesc;
 
 #include "config.h"
 
+static int
+initresampler(Outputdesc *desc, int inrate)
+{
+	soxr_quality_spec_t quality;
+	soxr_io_spec_t      iospec;
+	int i;
+
+	/* TODO: resampling quality should be configurable in config.h */
+	quality = soxr_quality_spec(SOXR_VHQ, 0);
+	iospec = soxr_io_spec(SOXR_INT16_I, SOXR_INT16_I);
+	if (desc->resampler)
+		soxr_delete(desc->resampler);
+
+	desc->resampler = soxr_create(inrate, desc->rate,
+	                              desc->channels,
+	                              NULL,
+	                              &iospec,
+	                              &quality,
+	                              NULL);
+	if (!desc->resampler) {
+		warnx("soxr_create: failed to initialize resampler");
+		return -1;
+	}
+
+	desc->inrate = inrate;
+	return 0;
+}
+
 int
-openoutput(const char *name)
+initresamplers(int inrate)
 {
 	Outputdesc *desc;
-	int i;
+	int i, r = 0;
 
-	for (i = 0; i < LEN(Outputdescs); i++) {
-		desc = &Outputdescs[i];
-		if (strcmp(desc->name, name))
+	for (i = 0; i < LEN(outputdescs); i++) {
+		desc = &outputdescs[i];
+		if (!desc->enabled)
 			continue;
-		if (desc->active)
-			return 0;
-		if (desc->output->open(desc->bits,
-		                       desc->rate,
-		                       desc->channels) < 0) {
-			desc->active = 0;
-			return -1;
-		} else {
-			printf("Opened %s output\n", desc->name);
-			desc->active = 1;
-			return 0;
-		}
+		if (initresampler(desc, inrate) < 0)
+			r = -1;
 	}
-	return -1;
+	return r;
+}
+
+static int
+openoutput(Outputdesc *desc)
+{
+	int i;
+
+	if (desc->active)
+		return 0;
+
+	if (desc->output->open(desc->bits,
+	                       desc->rate,
+	                       desc->channels) < 0) {
+		desc->active = 0;
+		return -1;
+	}
+
+	printf("Opened %s output\n", desc->name);
+	desc->active = 1;
+	return 0;
 }
 
 int
@@ -52,38 +94,32 @@ openoutputs(void)
 	Outputdesc *desc;
 	int i, r = 0;
 
-	for (i = 0; i < LEN(Outputdescs); i++) {
-		desc = &Outputdescs[i];
+	for (i = 0; i < LEN(outputdescs); i++) {
+		desc = &outputdescs[i];
 		if (!desc->enabled)
 			continue;
-		if (openoutput(desc->name) < 0)
+		if (openoutput(desc) < 0)
 			r = -1;
 	}
 	return r;
 }
 
-int
-closeoutput(const char *name)
+static int
+closeoutput(Outputdesc *desc)
 {
-	Outputdesc *desc;
 	int i;
 
-	for (i = 0; i < LEN(Outputdescs); i++) {
-		desc = &Outputdescs[i];
-		if (strcmp(desc->name, name))
-			continue;
-		if (!desc->active)
-			return 0;
-		if (desc->output->close() < 0) {
-			desc->active = 1;
-			return -1;
-		} else {
-			printf("Closed %s output\n", desc->name);
-			desc->active = 0;
-			return 0;
-		}
+	if (!desc->active)
+		return 0;
+
+	if (desc->output->close() < 0) {
+		desc->active = 1;
+		return -1;
 	}
-	return -1;
+
+	printf("Closed %s output\n", desc->name);
+	desc->active = 0;
+	return 0;
 }
 
 int
@@ -92,28 +128,56 @@ closeoutputs(void)
 	Outputdesc *desc;
 	int i, r = 0;
 
-	for (i = 0; i < LEN(Outputdescs); i++) {
-		desc = &Outputdescs[i];
+	for (i = 0; i < LEN(outputdescs); i++) {
+		desc = &outputdescs[i];
 		if (!desc->enabled)
 			continue;
-		if (closeoutput(desc->name) < 0)
+		if (closeoutput(desc) < 0)
 			r = -1;
 	}
 	return r;
 }
 
 int
-playoutput(void *buf, size_t nbytes)
+playoutput(void *inbuf, size_t nbytes)
 {
 	Outputdesc *desc;
-	int i, r = 0;
-
-	for (i = 0; i < LEN(Outputdescs); i++) {
-		desc = &Outputdescs[i];
+	soxr_error_t e;
+	size_t inframes, outframes;
+	size_t framesize;
+	size_t idone, odone;
+	void  *outbuf;
+	float  ratio;
+	int    i, r = 0;
+
+	for (i = 0; i < LEN(outputdescs); i++) {
+		desc = &outputdescs[i];
 		if (!desc->active)
 			continue;
-		if (desc->output->play(buf, nbytes) < 0)
-			r = -1;
+		if (desc->inrate == desc->rate) {
+			if (desc->output->play(inbuf, nbytes) <0)
+				r = -1;
+		} else {
+			framesize = (desc->bits + 7) / 8 * desc->channels;
+			inframes = nbytes / framesize;
+			ratio = (float)desc->rate / desc->inrate;
+			outframes = inframes * ratio + 1;
+			outbuf = malloc(outframes * framesize);
+			if (!outbuf)
+				err(1, "malloc");
+
+			e = soxr_process(desc->resampler, inbuf, inframes,
+			                 &idone, outbuf, outframes,
+			                 &odone);
+			if (e) {
+				warnx("soxr_process: failed");
+				free(outbuf);
+				continue;
+			}
+			if (desc->output->play(outbuf, odone * framesize) < 0)
+				r = -1;
+			free(outbuf);
+		}
 	}
 	return r;
 }
@@ -124,8 +188,8 @@ setvol(int vol)
 	Outputdesc *desc;
 	int i, r = 0;
 
-	for (i = 0; i < LEN(Outputdescs); i++) {
-		desc = &Outputdescs[i];
+	for (i = 0; i < LEN(outputdescs); i++) {
+		desc = &outputdescs[i];
 		if (!desc->active)
 			continue;
 		if (desc->output->vol(vol) < 0)
@@ -140,13 +204,13 @@ enableoutput(const char *name)
 	Outputdesc *desc;
 	int i, r;
 
-	for (i = 0; i < LEN(Outputdescs); i++) {
-		desc = &Outputdescs[i];
+	for (i = 0; i < LEN(outputdescs); i++) {
+		desc = &outputdescs[i];
 		if (strcmp(desc->name, name))
 			continue;
 		if (desc->active)
 			return -1;
-		if (openoutput(desc->name) < 0) {
+		if (openoutput(desc) < 0) {
 			desc->enabled = 0;
 			return -1;
 		} else {
@@ -163,13 +227,13 @@ disableoutput(const char *name)
 	Outputdesc *desc;
 	int i;
 
-	for (i = 0; i < LEN(Outputdescs); i++) {
-		desc = &Outputdescs[i];
+	for (i = 0; i < LEN(outputdescs); i++) {
+		desc = &outputdescs[i];
 		if (strcmp(desc->name, name))
 			continue;
 		if (!desc->active)
 			return -1;
-		if (closeoutput(desc->name) < 0) {
+		if (closeoutput(desc) < 0) {
 			desc->enabled = 1;
 			return -1;
 		} else {
diff --git a/sad.h b/sad.h
index 43cc38b..eddbc44 100644
--- a/sad.h
+++ b/sad.h
@@ -123,9 +123,8 @@ int initdecoders(void);
 Decoder *matchdecoder(const char *);
 
 /* output.c */
-int openoutput(const char *);
+int initresamplers(int);
 int openoutputs(void);
-int closeoutput(const char *);
 int closeoutputs(void);
 int enableoutput(const char *);
 int disableoutput(const char *);
diff --git a/vorbis.c b/vorbis.c
index 51d4cd6..7e46220 100644
--- a/vorbis.c
+++ b/vorbis.c
@@ -34,6 +34,8 @@ vorbisopen(const char *name)
 		goto err0;
 	}
 
+	initresamplers(vi->rate);
+
 	return 0;
 
 err0:
diff --git a/wav.c b/wav.c
index 34a25d5..3669cbb 100644
--- a/wav.c
+++ b/wav.c
@@ -51,6 +51,8 @@ wavopen(const char *name)
 		goto err0;
 	}
 
+	initresamplers(sfinfo.samplerate);
+
 	return 0;
 err0:
 	sf_close(sf);