From 13b35a2489e7e1d6341effe6e25b8cb8a0a94a02 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 2 Jul 2004 18:47:03 +0000 Subject: add resampler git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@44 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/Makefile.am | 11 ++-- src/endianmacros.h | 41 ++++++++++++++ src/iochannel.c | 2 +- src/resampler.c | 162 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/resampler.h | 15 +++++ src/sample.c | 8 +-- src/sample.h | 12 ++-- src/sconv.c | 99 ++++++++++++++++++++++++++++++++ src/sconv.h | 14 +++++ src/sinkinput.c | 1 + src/sinkinput.h | 3 + src/todo | 5 +- 12 files changed, 357 insertions(+), 16 deletions(-) create mode 100644 src/endianmacros.h create mode 100644 src/resampler.c create mode 100644 src/resampler.h create mode 100644 src/sconv.c create mode 100644 src/sconv.h diff --git a/src/Makefile.am b/src/Makefile.am index af4478be..7982802e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -52,12 +52,13 @@ polypaudio_SOURCES = idxset.c idxset.h \ mainloop-api.c mainloop-api.h \ util.c util.h \ hashset.c hashset.h \ - namereg.c namereg.h - -polypaudio_CFLAGS = $(AM_CFLAGS) - + namereg.c namereg.h \ + sconv.c sconv.h \ + resampler.c resampler.h \ + endianmacros.h +polypaudio_CFLAGS = $(AM_CFLAGS) $(LIBSAMPLERATE_CFLAGS) polypaudio_INCLUDES = $(INCLTDL) -polypaudio_LDADD = $(LIBLTDL) +polypaudio_LDADD = $(LIBLTDL) $(LIBSAMPLERATE_LIBS) polypaudio_LDFLAGS=-export-dynamic libprotocol_simple_la_SOURCES = protocol-simple.c protocol-simple.h diff --git a/src/endianmacros.h b/src/endianmacros.h new file mode 100644 index 00000000..2394b3e8 --- /dev/null +++ b/src/endianmacros.h @@ -0,0 +1,41 @@ +#ifndef fooendianmacroshfoo +#define fooendianmacroshfoo + +#include + +#ifdef HAVE_CONFIG_H +#include +#endif + +#define INT16_SWAP(x) ((int16_t)(((int16_t) x >> 8) | ((int16_t) x << 8))) +#define UINT16_SWAP(x) ((uint16_t)(((uint16_t) x >> 8) | ((uint16_t) x << 8))) +#define INT32_SWAP(x) ((int32_t)(((int32_t) x >> 24) | ((int32_t) x << 24) | (((int32_t) x & 0xFF00) << 16) | (((int32_t) x) >> 16) & 0xFF00)) +#define UINT32_SWAP(x) ((uint32_t)(((uint32_t) x >> 24) | ((uint32_t) x << 24) | (((uint32_t) x & 0xFF00) << 16) | (((uint32_t) x) >> 16) & 0xFF00)) + +#ifdef WORDS_BIGENDIAN + #define INT16_FROM_LE(x) INT16_SWAP(x) + #define INT16_FROM_BE(x) ((int16_t)(x)) + #define INT16_TO_LE(x) INT16_SWAP(x) + #define INT16_TO_BE(x) ((int16_t)(x)) + + #define UINT16_FROM_LE(x) UINT16_SWAP(x) + #define UINT16_FROM_BE(x) ((uint16_t)(x)) + #define INT32_FROM_LE(x) INT32_SWAP(x) + #define INT32_FROM_BE(x) ((int32_t)(x)) + #define UINT32_FROM_LE(x) UINT32_SWAP(x) + #define UINT32_FROM_BE(x) ((uint32_t)(x)) +#else + #define INT16_FROM_LE(x) ((int16_t)(x)) + #define INT16_FROM_BE(x) INT16_SWAP(x) + #define INT16_TO_LE(x) ((int16_t)(x)) + #define INT16_TO_BE(x) INT16_SWAP(x) + + #define UINT16_FROM_LE(x) ((uint16_t)(x)) + #define UINT16_FROM_BE(x) UINT16_SWAP(x) + #define INT32_FROM_LE(x) ((int32_t)(x)) + #define INT32_FROM_BE(x) INT32_SWAP(x) + #define UINT32_FROM_LE(x) ((uint32_t)(x)) + #define UINT32_FROM_BE(x) UINT32_SWAP(x) +#endif + +#endif diff --git a/src/iochannel.c b/src/iochannel.c index fa55875f..25d6b05e 100644 --- a/src/iochannel.c +++ b/src/iochannel.c @@ -1,6 +1,6 @@ +#include #include #include -#include #include #include "iochannel.h" diff --git a/src/resampler.c b/src/resampler.c new file mode 100644 index 00000000..aa37f1ac --- /dev/null +++ b/src/resampler.c @@ -0,0 +1,162 @@ +#include +#include + +#include + +#include "resampler.h" +#include "sconv.h" + +struct resampler { + struct pa_sample_spec i_ss, o_ss; + float* i_buf, *o_buf; + unsigned i_alloc, o_alloc; + size_t i_sz, o_sz; + + int channels; + + convert_to_float32_func_t to_float32_func; + convert_from_float32_func_t from_float32_func; + SRC_STATE *src_state; +}; + +struct resampler* resampler_new(const struct pa_sample_spec *a, const struct pa_sample_spec *b) { + struct resampler *r; + int err; + assert(a && b && pa_sample_spec_valid(a) && pa_sample_spec_valid(b)); + + if (a->channels != b->channels && a->channels != 1 && b->channels != 1) + goto fail; + + if (a->format == PA_SAMPLE_ALAW || a->format == PA_SAMPLE_ULAW || b->format == PA_SAMPLE_ALAW || b->format == PA_SAMPLE_ULAW) + goto fail; + + r->channels = a->channels; + if (b->channels < r->channels) + r->channels = b->channels; + + r = malloc(sizeof(struct resampler)); + assert(r); + r->i_buf = r->o_buf = NULL; + r->i_alloc = r->o_alloc = 0; + + if (a->rate != b->rate) { + r->src_state = src_new(SRC_SINC_FASTEST, r->channels, &err); + if (err != 0 || !r->src_state) + goto fail; + } else + r->src_state = NULL; + + r->i_ss = *a; + r->o_ss = *b; + + r->i_sz = pa_sample_size(a); + r->o_sz = pa_sample_size(b); + + r->to_float32_func = get_convert_to_float32_function(a->format); + r->from_float32_func = get_convert_from_float32_function(b->format); + + assert(r->to_float32_func && r->from_float32_func); + + return r; + +fail: + if (r) + free(r); + + return NULL; +} + +void resampler_free(struct resampler *r) { + assert(r); + if (r->src_state) + src_delete(r->src_state); + free(r->i_buf); + free(r->o_buf); + free(r); +} + +size_t resampler_request(struct resampler *r, size_t out_length) { + assert(r && (out_length % r->o_sz) == 0); + + return (((out_length / r->o_sz)*r->i_ss.rate)/r->o_ss.rate) * r->i_sz; +} + + +int resampler_run(struct resampler *r, struct memchunk *in, struct memchunk *out) { + unsigned i_nchannels, o_nchannels, ins, ons, eff_ins, eff_ons; + float *cbuf; + size_t in_bytes_used = 0; + assert(r && in && out && in->length && in->memblock); + + /* How many input samples? */ + ins = in->length/r->i_sz; + + /* How much space for output samples? */ + if (r->src_state) + ons = (ins*r->o_ss.rate/r->i_ss.rate)+1024; + else + ons = ins; + + /* How many channels? */ + if (r->i_ss.channels == r->o_ss.channels) { + i_nchannels = o_nchannels = 1; + eff_ins = ins*r->i_ss.channels; /* effective samples */ + eff_ons = ons*r->o_ss.channels; + } else { + i_nchannels = r->i_ss.channels; + o_nchannels = r->o_ss.channels; + eff_ins = ins; + eff_ons = ons; + } + + out->memblock = memblock_new(out->length = (ons*r->o_sz)); + out->index = 0; + assert(out->memblock); + + if (r->i_alloc < eff_ins) + r->i_buf = realloc(r->i_buf, sizeof(float) * (r->i_alloc = eff_ins)); + assert(r->i_buf); + + r->to_float32_func(eff_ins, in->memblock->data+in->index, i_nchannels, r->i_buf); + + if (r->src_state) { + int ret; + SRC_DATA data; + + if (r->o_alloc < eff_ons) + r->o_buf = realloc(r->o_buf, sizeof(float) * (r->o_alloc = eff_ons)); + assert(r->o_buf); + + data.data_in = r->i_buf; + data.input_frames = ins; + + data.data_out = r->o_buf; + data.output_frames = ons; + + data.src_ratio = (double) r->o_ss.rate / r->i_ss.rate; + data.end_of_input = 0; + + ret = src_process(r->src_state, &data); + assert(ret == 0); + + in_bytes_used = data.input_frames_used*r->i_sz; + cbuf = r->o_buf; + ons = data.output_frames_gen; + + if (r->i_ss.channels == r->o_ss.channels) + eff_ons = ons*r->o_ss.channels; + else + eff_ons = ons; + } else { + in_bytes_used = ins*r->i_sz; + cbuf = r->i_buf; + } + + assert(in_bytes_used < in->length); + in->index += in_bytes_used; + in->length -= in_bytes_used; + + r->from_float32_func(eff_ons, cbuf, out->memblock->data+out->index, o_nchannels); + out->length = ons*r->o_sz; + return 0; +} diff --git a/src/resampler.h b/src/resampler.h new file mode 100644 index 00000000..000f73ce --- /dev/null +++ b/src/resampler.h @@ -0,0 +1,15 @@ +#ifndef fooresamplerhfoo +#define fooresamplerhfoo + +#include "sample.h" +#include "memblock.h" + +struct resampler; + +struct resampler* resampler_new(const struct pa_sample_spec *a, const struct pa_sample_spec *b); +void resampler_free(struct resampler *r); + +size_t resampler_request(struct resampler *r, size_t out_length); +int resampler_run(struct resampler *r, struct memchunk *in, struct memchunk *out); + +#endif diff --git a/src/sample.c b/src/sample.c index b0d0cdbd..497358fa 100644 --- a/src/sample.c +++ b/src/sample.c @@ -2,7 +2,7 @@ #include "sample.h" -size_t pa_sample_size(struct pa_sample_spec *spec) { +size_t pa_sample_size(const struct pa_sample_spec *spec) { assert(spec); size_t b = 1; @@ -26,19 +26,19 @@ size_t pa_sample_size(struct pa_sample_spec *spec) { return b * spec->channels; } -size_t pa_bytes_per_second(struct pa_sample_spec *spec) { +size_t pa_bytes_per_second(const struct pa_sample_spec *spec) { assert(spec); return spec->rate*pa_sample_size(spec); } -uint32_t pa_samples_usec(size_t length, struct pa_sample_spec *spec) { +uint32_t pa_samples_usec(size_t length, const struct pa_sample_spec *spec) { assert(spec); return (uint32_t) (((double) length /pa_sample_size(spec))/spec->rate*1000000); } -int pa_sample_spec_valid(struct pa_sample_spec *spec) { +int pa_sample_spec_valid(const struct pa_sample_spec *spec) { assert(spec); if (!spec->rate || !spec->channels) diff --git a/src/sample.h b/src/sample.h index 697937e0..1fd764d7 100644 --- a/src/sample.h +++ b/src/sample.h @@ -14,7 +14,11 @@ enum pa_sample_format { PA_SAMPLE_MAX }; +#ifdef WORDS_BIGENDIAN +#define PA_SAMPLE_S16NE PA_SAMPLE_S16BE +#else #define PA_SAMPLE_S16NE PA_SAMPLE_S16LE +#endif struct pa_sample_spec { enum pa_sample_format format; @@ -22,10 +26,10 @@ struct pa_sample_spec { uint8_t channels; }; -size_t pa_bytes_per_second(struct pa_sample_spec *spec); -size_t pa_sample_size(struct pa_sample_spec *spec); -uint32_t pa_samples_usec(size_t length, struct pa_sample_spec *spec); +size_t pa_bytes_per_second(const struct pa_sample_spec *spec); +size_t pa_sample_size(const struct pa_sample_spec *spec); +uint32_t pa_samples_usec(size_t length, const struct pa_sample_spec *spec); -int pa_sample_spec_valid(struct pa_sample_spec *spec); +int pa_sample_spec_valid(const struct pa_sample_spec *spec); #endif diff --git a/src/sconv.c b/src/sconv.c new file mode 100644 index 00000000..11438b41 --- /dev/null +++ b/src/sconv.c @@ -0,0 +1,99 @@ +#include +#include +#include "endianmacros.h" +#include "sconv.h" + +static void s16le_to_float32(unsigned n, const void *a, unsigned an, float *b) { + const int16_t *ca = a; + assert(n && a && an && b); + + for (; n > 0; n--) { + unsigned i; + float sum = 0; + + for (i = 0; i < an; i++) { + int16_t s = *(ca++); + sum += ((float) INT16_FROM_LE(s))/0x7FFF; + } + + if (sum > 1) + sum = 1; + if (sum < -1) + sum = -1; + + *(b++) = sum; + } +} + +static void s16le_from_float32(unsigned n, const float *a, void *b, unsigned bn) { + int16_t *cb = b; + assert(n && a && b && bn); + + for (; n > 0; n--) { + unsigned i; + int16_t s; + float v = *(a++); + + if (v > 1) + v = 1; + if (v < -1) + v = -1; + + s = (int16_t) (v * 0x7FFF); + + for (i = 0; i < bn; i++) + *(cb++) = INT16_TO_LE(v); + } +} + +static void float32_to_float32(unsigned n, const void *a, unsigned an, float *b) { + unsigned i; + const float *ca = a; + assert(n && a && an && b); + for (; n > 0; n--) { + float sum = 0; + + for (i = 0; i < an; i++) + sum += *(ca++); + + if (sum > 1) + sum = 1; + if (sum < -1) + sum = -1; + + *(b++) = sum; + } +} + +static void float32_from_float32(unsigned n, const float *a, void *b, unsigned bn) { + unsigned i; + float *cb = b; + assert(n && a && b && bn); + for (; n > 0; n--) { + float v = *(a++); + for (i = 0; i < bn; i++) + *(cb++) = v; + } +} + +convert_to_float32_func_t get_convert_to_float32_function(enum pa_sample_format f) { + switch(f) { + case PA_SAMPLE_S16LE: + return s16le_to_float32; + case PA_SAMPLE_FLOAT32: + return float32_to_float32; + default: + return NULL; + } +} + +convert_from_float32_func_t get_convert_from_float32_function(enum pa_sample_format f) { + switch(f) { + case PA_SAMPLE_S16LE: + return s16le_from_float32; + case PA_SAMPLE_FLOAT32: + return float32_from_float32; + default: + return NULL; + } +} diff --git a/src/sconv.h b/src/sconv.h new file mode 100644 index 00000000..8667d2ae --- /dev/null +++ b/src/sconv.h @@ -0,0 +1,14 @@ +#ifndef foosconvhfoo +#define foosconvhfoo + +#include "sample.h" + +typedef void (*convert_to_float32_func_t)(unsigned n, const void *a, unsigned an, float *b); +typedef void (*convert_from_float32_func_t)(unsigned n, const float *a, void *b, unsigned bn); + +convert_to_float32_func_t get_convert_to_float32_function(enum pa_sample_format f); +convert_from_float32_func_t get_convert_from_float32_function(enum pa_sample_format f); + + + +#endif diff --git a/src/sinkinput.c b/src/sinkinput.c index 54bc98a6..dd0504d0 100644 --- a/src/sinkinput.c +++ b/src/sinkinput.c @@ -88,3 +88,4 @@ uint32_t sink_input_get_latency(struct sink_input *i) { return l; } + diff --git a/src/sinkinput.h b/src/sinkinput.h index 4fe39e2a..e3114d94 100644 --- a/src/sinkinput.h +++ b/src/sinkinput.h @@ -33,4 +33,7 @@ void sink_input_kill(struct sink_input *i); uint32_t sink_input_get_latency(struct sink_input *i); char *sink_input_list_to_string(struct core *c); + + + #endif diff --git a/src/todo b/src/todo index e1cdb9f1..0dd999a1 100644 --- a/src/todo +++ b/src/todo @@ -4,20 +4,21 @@ more functions - esound protocol: recording -- split oss-dma? +- move more stuff from module-oss[-dma] to liboss-util - simple library - simple control protocol: kill client/input/output +- kill() routines in all modules - resampling - config parser/cmdline - record testing - mixing/volume -- kill() routines in all modules -- 0.1 - future cancellation - client-ui - clip cache +- autoloading/autounloading drivers: - libao -- cgit