From: Richard Kettlewell Date: Fri, 20 Nov 2009 18:49:26 +0000 (+0000) Subject: disorder-normalize now uses resample_convert() if libsamplerate is X-Git-Tag: 5.0~56^2~1 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/disorder/commitdiff_plain/bfdadcc93fcb88261478c1b04d32e619fd961a13?ds=sidebyside disorder-normalize now uses resample_convert() if libsamplerate is available. --- diff --git a/lib/resample.c b/lib/resample.c index 99879a4..c4c814e 100644 --- a/lib/resample.c +++ b/lib/resample.c @@ -214,10 +214,8 @@ static size_t resample_put_sample(const struct resampler *rs, */ static void resample_prepare_input(const struct resampler *rs, const uint8_t *bytes, - size_t nbytes, + size_t nframes, float *floats) { - size_t nframes = nbytes / (rs->input_bytes_per_frame); - while(nframes > 0) { int n; @@ -261,7 +259,7 @@ size_t resample_convert(const struct resampler *rs, float *input = xcalloc(nframesin * rs->output_channels, sizeof (float)); float *output = 0; - resample_prepare_input(rs, bytes, nbytes, input); + resample_prepare_input(rs, bytes, nframesin, input); #if HAVE_SAMPLERATE_H if(rs->state) { /* A sample-rate conversion must be performed */ diff --git a/server/Makefile.am b/server/Makefile.am index f67db97..376b53f 100644 --- a/server/Makefile.am +++ b/server/Makefile.am @@ -51,7 +51,7 @@ disorder_decode_DEPENDENCIES=../lib/libdisorder.a disorder_normalize_SOURCES=normalize.c disorder-server.h disorder_normalize_LDADD=$(LIBOBJS) ../lib/libdisorder.a \ - $(LIBPCRE) $(LIBICONV) $(LIBGCRYPT) + $(LIBPCRE) $(LIBICONV) $(LIBGCRYPT) $(LIBSAMPLERATE) disorder_normalize_DEPENDENCIES=../lib/libdisorder.a disorder_rescan_SOURCES=rescan.c plugin.c api.c api-server.c exports.c \ diff --git a/server/normalize.c b/server/normalize.c index 717f3cc..2a28c1f 100644 --- a/server/normalize.c +++ b/server/normalize.c @@ -1,6 +1,6 @@ /* * This file is part of DisOrder - * Copyright (C) 2007, 2008 Richard Kettlewell + * Copyright (C) 2007-2009 Richard Kettlewell * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,12 +18,15 @@ /** @file server/normalize.c * @brief Convert "raw" format output to the configured format * - * Currently we invoke sox even for trivial conversions such as byte-swapping. - * Ideally we would do all conversion including resampling in this one process - * and eliminate the dependency on sox. + * If libsamplerate is available then resample_convert() is used to do all + * conversions. If not then we invoke sox (even for trivial conversions such + * as byte-swapping). The sox support might be removed in a future version. */ #include "disorder-server.h" +#include "resample.h" + +static char buffer[1024 * 1024]; static const struct option options[] = { { "help", no_argument, 0, 'h' }, @@ -59,7 +62,6 @@ static void help(void) { * @param n Number of bytes to copy */ static void copy(int infd, int outfd, size_t n) { - char buffer[4096]; ssize_t written; while(n > 0) { @@ -84,6 +86,7 @@ static void copy(int infd, int outfd, size_t n) { } } +#if !HAVE_SAMPLERATE_H static void soxargs(const char ***pp, char **qq, const struct stream_header *header) { *(*pp)++ = "-t.raw"; @@ -120,11 +123,30 @@ static void soxargs(const char ***pp, char **qq, fatal(0, "unknown sox_generation %ld", config->sox_generation); } } +#else +static void converted(uint8_t *bytes, + size_t nbytes, + void attribute((unused)) *cd) { + /*syslog(LOG_INFO, "out: %02x %02x %02x %02x", + bytes[0], + bytes[1], + bytes[2], + bytes[3]);*/ + while(nbytes > 0) { + ssize_t n = write(1, bytes, nbytes); + if(n < 0) + disorder_fatal(errno, "writing to stdout"); + bytes += n; + nbytes -= n; + } +} +#endif int main(int argc, char attribute((unused)) **argv) { struct stream_header header, latest_format; - int n, p[2], outfd = -1, logsyslog = !isatty(2); + int n, outfd = -1, logsyslog = !isatty(2), rs_in_use = 0; pid_t pid = -1; + struct resampler rs[1]; set_progname(argv); if(!setlocale(LC_CTYPE, "")) @@ -149,6 +171,7 @@ int main(int argc, char attribute((unused)) **argv) { } memset(&latest_format, 0, sizeof latest_format); for(;;) { + /* Read one header */ n = 0; while((size_t)n < sizeof header) { int r = read(0, (char *)&header + n, sizeof header - n); @@ -174,11 +197,86 @@ int main(int argc, char attribute((unused)) **argv) { if(header.bits % 8 || !header.bits || header.bits > 64) fatal(0, "unsupported sample size %d bits", header.bits); if(header.endian != ENDIAN_BIG && header.endian != ENDIAN_LITTLE) - fatal(0, "unsupported byte order %x", header.bits); + fatal(0, "unsupported byte order %d", header.endian); /* Skip empty chunks regardless of their alleged format */ if(header.nbytes == 0) continue; /* If the format has changed we stop/start the converter */ +#if HAVE_SAMPLERATE_H + /* We have libsamplerate */ + if(formats_equal(&header, &config->sample_format)) + /* If the format is already correct then we just write out the data */ + copy(0, 1, header.nbytes); + else { + /* If we have a resampler active already check it is suitable and destroy + * it if not */ + if(!formats_equal(&header, &latest_format) && rs_in_use) { + resample_close(rs); + rs_in_use = 0; + } + /*syslog(LOG_INFO, "%d/%d/%d/%d/%d -> %d/%d/%d/%d/%d", + header.bits, + header.channels, + header.rate, + 1, + header.endian, + config->sample_format.bits, + config->sample_format.channels, + config->sample_format.rate, + 1, + config->sample_format.endian);*/ + if(!rs_in_use) { + /* Create a suitable resampler. */ + resample_init(rs, + header.bits, + header.channels, + header.rate, + 1, /* signed */ + header.endian, + config->sample_format.bits, + config->sample_format.channels, + config->sample_format.rate, + 1, /* signed */ + config->sample_format.endian); + latest_format = header; + rs_in_use = 1; + /* TODO speaker protocol does not record signedness of samples. It's + * assumed that they are always signed. This should be fixed in the + * future (and the sample format syntax extended in a compatible + * way). */ + } + /* Feed data through the resampler */ + size_t used = 0, left = header.nbytes; + while(used || left) { + if(left) { + size_t limit = (sizeof buffer) - used; + if(limit > left) + limit = left; + ssize_t r = read(0, buffer + used, limit); + if(r < 0) + disorder_fatal(errno, "reading from stdin"); + if(r == 0) + disorder_fatal(0, "unexpected EOF"); + left -= r; + used += r; + //syslog(LOG_INFO, "read %zd bytes", r); + } + /*syslog(LOG_INFO, " in: %02x %02x %02x %02x", + (uint8_t)buffer[0], + (uint8_t)buffer[1], + (uint8_t)buffer[2], + (uint8_t)buffer[3]);*/ + const size_t consumed = resample_convert(rs, + (uint8_t *)buffer, used, + !left, + converted, 0); + //syslog(LOG_INFO, "used=%zu consumed=%zu", used, consumed); + memmove(buffer, buffer + consumed, used - consumed); + used -= consumed; + } + } +#else + /* We do not have libsamplerate. We will use sox instead. */ if(!formats_equal(&header, &latest_format)) { if(pid != -1) { /* There's a running converter, stop it */ @@ -202,6 +300,7 @@ int main(int argc, char attribute((unused)) **argv) { *pp++ = "-"; /* stdout */ *pp = 0; /* This pipe will be sox's stdin */ + int p[2]; xpipe(p); if(!(pid = xfork())) { exitfn = _exit; @@ -221,6 +320,7 @@ int main(int argc, char attribute((unused)) **argv) { } /* Convert or copy this chunk */ copy(0, outfd, header.nbytes); +#endif } if(outfd != -1) xclose(outfd); @@ -231,6 +331,8 @@ int main(int argc, char attribute((unused)) **argv) { if(n) fatal(0, "sox failed: %#x", n); } + if(rs_in_use) + resample_close(rs); return 0; }