diff options
-rw-r--r-- | polyp/Makefile.am | 2 | ||||
-rw-r--r-- | polyp/channelmap.c | 140 | ||||
-rw-r--r-- | polyp/channelmap.h | 75 | ||||
-rw-r--r-- | polyp/client.c | 6 | ||||
-rw-r--r-- | polyp/client.h | 5 | ||||
-rw-r--r-- | polyp/resampler.c | 136 | ||||
-rw-r--r-- | polyp/resampler.h | 19 | ||||
-rw-r--r-- | polyp/sample-util.c | 76 | ||||
-rw-r--r-- | polyp/sample-util.h | 13 | ||||
-rw-r--r-- | polyp/sample.c | 60 | ||||
-rw-r--r-- | polyp/sample.h | 48 | ||||
-rw-r--r-- | polyp/sink-input.c | 40 | ||||
-rw-r--r-- | polyp/sink-input.h | 29 | ||||
-rw-r--r-- | polyp/sink.c | 85 | ||||
-rw-r--r-- | polyp/sink.h | 42 | ||||
-rw-r--r-- | polyp/source-output.c | 20 | ||||
-rw-r--r-- | polyp/source-output.h | 23 | ||||
-rw-r--r-- | polyp/source.c | 23 | ||||
-rw-r--r-- | polyp/source.h | 27 | ||||
-rw-r--r-- | polyp/tagstruct.h | 4 | ||||
-rw-r--r-- | polyp/voltest.c | 17 | ||||
-rw-r--r-- | polyp/volume.c | 161 | ||||
-rw-r--r-- | polyp/volume.h | 98 |
23 files changed, 917 insertions, 232 deletions
diff --git a/polyp/Makefile.am b/polyp/Makefile.am index 009707c2..18e1a2ab 100644 --- a/polyp/Makefile.am +++ b/polyp/Makefile.am @@ -542,7 +542,7 @@ mainloop_test_SOURCES = mainloop-test.c mainloop_test_CFLAGS = $(AM_CFLAGS) mainloop_test_LDADD = $(AM_LDADD) libpolyp-mainloop-@PA_MAJORMINOR@.la libpolyp-@PA_MAJORMINOR@.la -voltest_SOURCES = voltest.c sample.c +voltest_SOURCES = voltest.c volume.c voltest_CFLAGS = $(AM_CFLAGS) voltest_LDADD = $(AM_LDADD) diff --git a/polyp/channelmap.c b/polyp/channelmap.c new file mode 100644 index 00000000..97876528 --- /dev/null +++ b/polyp/channelmap.c @@ -0,0 +1,140 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <assert.h> + +#include "channelmap.h" + + +struct pa_channel_map* pa_channel_map_init(struct pa_channel_map *m) { + unsigned c; + assert(m); + + for (c = 0; c < PA_CHANNELS_MAX; c++) + m->map[c] = PA_CHANNEL_POSITION_INVALID; + + return m; +} + +struct pa_channel_map* pa_channel_map_init_mono(struct pa_channel_map *m) { + assert(m); + + pa_channel_map_init(m); + m->map[0] = PA_CHANNEL_POSITION_MONO; + return m; +} + +struct pa_channel_map* pa_channel_map_init_stereo(struct pa_channel_map *m) { + assert(m); + + pa_channel_map_init(m); + m->map[0] = PA_CHANNEL_POSITION_LEFT; + m->map[1] = PA_CHANNEL_POSITION_RIGHT; + return m; +} + +struct pa_channel_map* pa_channel_map_init_auto(struct pa_channel_map *m, int channels) { + assert(m); + assert(channels > 0); + + pa_channel_map_init(m); + + switch (channels) { + case 1: + m->map[0] = PA_CHANNEL_POSITION_MONO; + return m; + + case 8: + m->mpa[6] = PA_CHANNEL_POSITION_SIDE_LEFT; + m->mpa[7] = PA_CHANNEL_POSITION_SIDE_RIGHT; + /* Fall through */ + + case 6: + m->mpa[5] = PA_CHANNEL_POSITION_LFE; + /* Fall through */ + + case 5: + m->map[4] = PA_CHANNEL_POSITION_FRONT_CENTER; + /* Fall through */ + + case 4: + m->map[2] = PA_CHANNEL_POSITION_REAR_LEFT; + m->map[3] = PA_CHANNEL_POSITION_REAR_RIGHT; + /* Fall through */ + + case 2: + m->map[0] = PA_CHANNEL_MAP_FRONT_LEFT; + m->map[1] = PA_CHANNEL_MAP_FRONT_RIGHT; + return m; + + default: + return NULL; + } +} + + +const char* pa_channel_position_to_string(pa_channel_position_t pos) { + const char *const table[] = { + [PA_CHANNEL_POSITION_MONO] = "mono", + + [PA_CHANNEL_POSITION_FRONT_CENTER] = "front-center", + [PA_CHANNEL_POSITION_FRONT_LEFT] = "front-left", + [PA_CHANNEL_POSITION_FRONT_RIGHT] = "front-right", + + [PA_CHANNEL_POSITION_REAR_CENTER] = "rear-center", + [PA_CHANNEL_POSITION_REAR_LEFT] = "rear-left", + [PA_CHANNEL_POSITION_REAR_RIGHT] = "rear-right", + + [PA_CHANNEL_POSITION_LFE] = "lfe", + + [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = "front-left-of-center", + [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = "front-right-of-center", + + [PA_CHANNEL_POSITION_SIDE_LEFT] = "side-left", + [PA_CHANNEL_POSITION_SIDE_RIGHT] = "side-right" + }; + + if (pos < 0 || pos >= PA_CHANNEL_POSITION_MAX) + return NULL; + + return table[pos]; +} + +int pa_channel_map_equal(struct pa_channel_map *a, struct pa_channel_map *b, int channels) { + char c; + + assert(a); + assert(b); + assert(channels > 0); + + if (channels > PA_CHANNELS_MAX) + channels = PA_CHANNELS_MAX; + + for (c = 0; c < channels; c++) + if (a->map[c] != b->map[c]) + return 1; + + return 0; +} diff --git a/polyp/channelmap.h b/polyp/channelmap.h new file mode 100644 index 00000000..946247a1 --- /dev/null +++ b/polyp/channelmap.h @@ -0,0 +1,75 @@ +#ifndef foochannelmaphfoo +#define foochannelmaphfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include <polyp/sample.h> +#include <polyp/cdecl.h> + +/** \file + * Constants and routines for channel mapping handling */ + +PA_C_DECL_BEGIN + +typedef enum { + PA_CHANNEL_POSITION_INVALID = -1, + PA_CHANNEL_POSITION_MONO = 0, + + PA_CHANNEL_POSITION_LEFT, + PA_CHANNEL_POSITION_RIGHT, + + PA_CHANNEL_POSITION_FRONT_CENTER, + PA_CHANNEL_POSITION_FRONT_LEFT = PA_CHANNEL_POSITION_LEFT, + PA_CHANNEL_POSITION_FRONT_RIGHT = PA_CHANNEL_POSITION_RIGHT, + + PA_CHANNEL_POSITION_REAR_CENTER, + PA_CHANNEL_POSITION_REAR_LEFT, + PA_CHANNEL_POSITION_REAR_RIGHT, + + PA_CHANNEL_POSITION_LFE, + PA_CHANNEL_POSITION_SUBWOOFER = PA_CHANNEL_LFE, + + PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, + PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER, + + PA_CHANNEL_POSITION_SIDE_LEFT, + PA_CHANNEL_POSITION_SIDE_RIGHT, + + PA_CHANNEL_POSITION_MAX +} pa_channel_position_t; + +struct { + pa_channel_position_t map[PA_CHANNELS_MAX]; +} pa_channel_map; + +struct pa_channel_map* pa_channel_map_init(struct pa_channel_map *m); +struct pa_channel_map* pa_channel_map_init_mono(struct pa_channel_map *m); +struct pa_channel_map* pa_channel_map_init_stereo(struct pa_channel_map *m); +struct pa_channel_map* pa_channel_map_init_auto(struct pa_channel_map *m, int channels); + +const char* pa_channel_position_to_string(pa_channel_position_t pos); + +int pa_channel_map_equal(struct pa_channel_map *a, struct pa_channel_map *b, int channels) + +PA_C_DECL_END + +#endif diff --git a/polyp/client.c b/polyp/client.c index 8c7f4800..dca7b526 100644 --- a/polyp/client.c +++ b/polyp/client.c @@ -33,16 +33,16 @@ #include "subscribe.h" #include "log.h" -struct pa_client *pa_client_new(struct pa_core *core, pa_typeid_t typeid, const char *name) { +struct pa_client *pa_client_new(struct pa_core *core, const char *name, const char *driver) { struct pa_client *c; int r; assert(core); c = pa_xmalloc(sizeof(struct pa_client)); c->name = pa_xstrdup(name); + c->driver = pa_xstrdup(driver); c->owner = NULL; c->core = core; - c->typeid = typeid; c->kill = NULL; c->userdata = NULL; @@ -68,8 +68,8 @@ void pa_client_free(struct pa_client *c) { pa_log_info(__FILE__": freed %u \"%s\"\n", c->index, c->name); pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_REMOVE, c->index); pa_xfree(c->name); + pa_xfree(c->driver); pa_xfree(c); - } void pa_client_kill(struct pa_client *c) { diff --git a/polyp/client.h b/polyp/client.h index 2a3a09e0..082dc29f 100644 --- a/polyp/client.h +++ b/polyp/client.h @@ -32,17 +32,16 @@ struct pa_client { uint32_t index; - pa_typeid_t typeid; struct pa_module *owner; - char *name; + char *name, *driver; struct pa_core *core; void (*kill)(struct pa_client *c); void *userdata; }; -struct pa_client *pa_client_new(struct pa_core *c, pa_typeid_t typeid, const char *name); +struct pa_client *pa_client_new(struct pa_core *c, const char *name, const char *driver); /* This function should be called only by the code that created the client */ void pa_client_free(struct pa_client *c); diff --git a/polyp/resampler.c b/polyp/resampler.c index 28e49209..d3165c95 100644 --- a/polyp/resampler.c +++ b/polyp/resampler.c @@ -35,11 +35,12 @@ struct pa_resampler { struct pa_sample_spec i_ss, o_ss; + struct pa_channel_map i_cm, o_cm; size_t i_fz, o_fz; struct pa_memblock_stat *memblock_stat; void *impl_data; int channels; - enum pa_resample_method resample_method; + pa_resample_method_t resample_method; void (*impl_free)(struct pa_resampler *r); void (*impl_set_input_rate)(struct pa_resampler *r, uint32_t rate); @@ -62,7 +63,14 @@ struct impl_trivial { static int libsamplerate_init(struct pa_resampler*r); static int trivial_init(struct pa_resampler*r); -struct pa_resampler* pa_resampler_new(const struct pa_sample_spec *a, const struct pa_sample_spec *b, struct pa_memblock_stat *s, enum pa_resample_method resample_method) { +struct pa_resampler* pa_resampler_new( + const struct pa_sample_spec *a, + const struct pa_channel_map *am, + const struct pa_sample_spec *b, + const struct pa_channel_map *bm, + struct pa_memblock_stat *s, + pa_resample_method_t resample_method) { + struct pa_resampler *r = NULL; assert(a && b && pa_sample_spec_valid(a) && pa_sample_spec_valid(b) && resample_method != PA_RESAMPLER_INVALID); @@ -82,6 +90,17 @@ struct pa_resampler* pa_resampler_new(const struct pa_sample_spec *a, const stru r->i_ss = *a; r->o_ss = *b; + if (am) + r->i_cm = *am; + else + pa_channel_map_init_auto(&r->i_cm, r->i_ss.channels); + + if (bm) + r->o_cm = *bm; + else + pa_channel_map_init_auto(&r->o_cm, r->o_ss.channels); + + r->i_fz = pa_frame_size(a); r->o_fz = pa_frame_size(b); @@ -90,7 +109,7 @@ struct pa_resampler* pa_resampler_new(const struct pa_sample_spec *a, const stru r->channels = b->channels; /* Choose implementation */ - if (a->channels != b->channels || a->format != b->format || resample_method != PA_RESAMPLER_TRIVIAL) { + if (a->channels != b->channels || a->format != b->format || resample_method != PA_RESAMPLER_TRIVIAL || !pa_channel_map_equal(&r->i_cm, &r->o_cm)) { /* Use the libsamplerate based resampler for the complicated cases */ if (resample_method == PA_RESAMPLER_TRIVIAL) r->resample_method = PA_RESAMPLER_SRC_ZERO_ORDER_HOLD; @@ -141,31 +160,11 @@ size_t pa_resampler_request(struct pa_resampler *r, size_t out_length) { return (((out_length / r->o_fz)*r->i_ss.rate)/r->o_ss.rate) * r->i_fz; } -enum pa_resample_method pa_resampler_get_method(struct pa_resampler *r) { +pa_resample_method_t pa_resampler_get_method(struct pa_resampler *r) { assert(r); return r->resample_method; } -/* Parse a libsamplrate compatible resampling implementation */ -enum pa_resample_method pa_parse_resample_method(const char *string) { - assert(string); - - if (!strcmp(string, "src-sinc-best-quality")) - return PA_RESAMPLER_SRC_SINC_BEST_QUALITY; - else if (!strcmp(string, "src-sinc-medium-quality")) - return PA_RESAMPLER_SRC_SINC_MEDIUM_QUALITY; - else if (!strcmp(string, "src-sinc-fastest")) - return PA_RESAMPLER_SRC_SINC_FASTEST; - else if (!strcmp(string, "src-zero-order-hold")) - return PA_RESAMPLER_SRC_ZERO_ORDER_HOLD; - else if (!strcmp(string, "src-linear")) - return PA_RESAMPLER_SRC_LINEAR; - else if (!strcmp(string, "trivial")) - return PA_RESAMPLER_TRIVIAL; - else - return PA_RESAMPLER_INVALID; -} - /*** libsamplerate based implementation ***/ static void libsamplerate_free(struct pa_resampler *r) { @@ -181,6 +180,70 @@ static void libsamplerate_free(struct pa_resampler *r) { pa_xfree(i); } +static void calc_map_table(struct pa_resampler *r) { + struct impl_libsamplerate *u; + unsigned oc; + assert(r); + assert(r->impl_data); + + u = r->impl_data; + + if ((u->map_required = (!pa_channel_map_equal(&r->i_cm, r->o_cm) || r->i_ss.channels != r->o_ss.channels))) { + + memset(u->map_table, -1, sizeof(u->map_table)); + + for (oc = 0; oc < r->o_iss.channels; oc++) { + unsigned i = 0, ic; + + for (ic = 0; ic < r->i_ss.channels; ic++) { + pa_channel_position_t a, b; + + a = r->i_cm.map[ic]; + b = r->o_cm.map[oc]; + + if (a == b || + (a == PA_CHANNEL_POSITION_MONO && b == PA_CHANNEL_POSITION_LEFT) || + (a == PA_CHANNEL_POSITION_MONO && b == PA_CHANNEL_POSITION_RIGHT) || + (a == PA_CHANNEL_POSITION_LEFT && b == PA_CHANNEL_POSITION_MONO) || + (a == PA_CHANNEL_POSITION_RIGHT && b == PA_CHANNEL_POSITION_MONO)) + + u->map_table[oc][i++] = ic; + } + } + } +} + + +static float *remap_to_float(struct pa_resampler *r, const struct pa_memchunk *in) { + unsigned nsamples; + struct impl_libsamplerate *u; + assert(r); + assert(r->impl_data); + + u = r->impl_data; + + nsamples = in->length / u->i_fz; + + if () { + + if (u->i_buf_samples < nsamples) + u->i_buf = pa_xrealloc(i->i_buf, sizeof(float) * (i->i_buf_samples = nsamples)); + + i->to_float32ne_func(ff_ins, (uint8_t*) in->memblock->data+in->index, i_nchannels, i->i_buf); + + } + + +} + + +static void libsamplerate_run(struct pa_resampler *r, const struct pa_memchunk *in, struct pa_memchunk *out) { + + + +} + + static void libsamplerate_run(struct pa_resampler *r, const struct pa_memchunk *in, struct pa_memchunk *out) { unsigned i_nchannels, o_nchannels, ins, ons, eff_ins, eff_ons; float *cbuf; @@ -191,7 +254,8 @@ static void libsamplerate_run(struct pa_resampler *r, const struct pa_memchunk * /* How many input samples? */ ins = in->length/r->i_fz; -/* pa_log("%u / %u = %u\n", in->length, r->i_fz, ins); */ + + /* pa_log("%u / %u = %u\n", in->length, r->i_fz, ins); */ /* How much space for output samples? */ if (i->src_state) @@ -395,7 +459,7 @@ static int trivial_init(struct pa_resampler*r) { return 0; } -const char *pa_resample_method_to_string(enum pa_resample_method m) { +const char *pa_resample_method_to_string(pa_resample_method_t m) { static const char * const resample_methods[] = { "src-sinc-best-quality", "src-sinc-medium-quality", @@ -410,3 +474,23 @@ const char *pa_resample_method_to_string(enum pa_resample_method m) { return resample_methods[m]; } + +pa_resample_method_t pa_parse_resample_method(const char *string) { + assert(string); + + if (!strcmp(string, "src-sinc-best-quality")) + return PA_RESAMPLER_SRC_SINC_BEST_QUALITY; + else if (!strcmp(string, "src-sinc-medium-quality")) + return PA_RESAMPLER_SRC_SINC_MEDIUM_QUALITY; + else if (!strcmp(string, "src-sinc-fastest")) + return PA_RESAMPLER_SRC_SINC_FASTEST; + else if (!strcmp(string, "src-zero-order-hold")) + return PA_RESAMPLER_SRC_ZERO_ORDER_HOLD; + else if (!strcmp(string, "src-linear")) + return PA_RESAMPLER_SRC_LINEAR; + else if (!strcmp(string, "trivial")) + return PA_RESAMPLER_TRIVIAL; + else + return PA_RESAMPLER_INVALID; +} + diff --git a/polyp/resampler.h b/polyp/resampler.h index 0109e790..ec6a8080 100644 --- a/polyp/resampler.h +++ b/polyp/resampler.h @@ -30,7 +30,7 @@ struct pa_resampler; -enum pa_resample_method { +typedef enum { PA_RESAMPLER_INVALID = -1, PA_RESAMPLER_SRC_SINC_BEST_QUALITY = SRC_SINC_BEST_QUALITY, PA_RESAMPLER_SRC_SINC_MEDIUM_QUALITY = SRC_SINC_MEDIUM_QUALITY, @@ -39,9 +39,16 @@ enum pa_resample_method { PA_RESAMPLER_SRC_LINEAR = SRC_LINEAR, PA_RESAMPLER_TRIVIAL, PA_RESAMPLER_MAX -}; +} pa_resample_method_t; + +struct pa_resampler* pa_resampler_new( + const struct pa_sample_spec *a, + const struct pa_channel_map *am, + const struct pa_sample_spec *b, + const struct pa_channel_map *bm, + struct pa_memblock_stat *s, + pa_resample_method_t resample_method); -struct pa_resampler* pa_resampler_new(const struct pa_sample_spec *a, const struct pa_sample_spec *b, struct pa_memblock_stat *s, int resample_method); void pa_resampler_free(struct pa_resampler *r); /* Returns the size of an input memory block which is required to return the specified amount of output data */ @@ -54,12 +61,12 @@ void pa_resampler_run(struct pa_resampler *r, const struct pa_memchunk *in, stru void pa_resampler_set_input_rate(struct pa_resampler *r, uint32_t rate); /* Return the resampling method of the resampler object */ -enum pa_resample_method pa_resampler_get_method(struct pa_resampler *r); +pa_resample_method_t pa_resampler_get_method(struct pa_resampler *r); /* Try to parse the resampler method */ -enum pa_resample_method pa_parse_resample_method(const char *string); +pa_resample_method_t pa_parse_resample_method(const char *string); /* return a human readable string for the specified resampling method. Inverse of pa_parse_resample_method() */ -const char *pa_resample_method_to_string(enum pa_resample_method m); +const char *pa_resample_method_to_string(pa_resample_method_t m); #endif diff --git a/polyp/sample-util.c b/polyp/sample-util.c index d521afe4..44cacfc6 100644 --- a/polyp/sample-util.c +++ b/polyp/sample-util.c @@ -65,30 +65,37 @@ void pa_silence_memory(void *p, size_t length, const struct pa_sample_spec *spec memset(p, c, length); } -size_t pa_mix(struct pa_mix_info channels[], unsigned nchannels, void *data, size_t length, const struct pa_sample_spec *spec, pa_volume_t volume) { - assert(channels && data && length && spec); +size_t pa_mix(struct pa_mix_info streams[], + unsigned nstreams, + void *data, + size_t length, + const struct pa_sample_spec *spec, + const struct pa_cvolume *volume) { + + assert(streams && data && length && spec); if (spec->format == PA_SAMPLE_S16NE) { size_t d; + unsigned channel = 0; for (d = 0;; d += sizeof(int16_t)) { - unsigned c; + unsigned i; int32_t sum = 0; if (d >= length) return d; - for (c = 0; c < nchannels; c++) { + for (i = 0; i < nstreams; i++) { int32_t v; - pa_volume_t cvolume = channels[c].volume; + pa_volume_t cvolume = streams[i].volume.values[channel]; - if (d >= channels[c].chunk.length) + if (d >= streams[i].chunk.length) return d; if (cvolume == PA_VOLUME_MUTED) v = 0; else { - v = *((int16_t*) ((uint8_t*) channels[c].chunk.memblock->data + channels[c].chunk.index + d)); + v = *((int16_t*) ((uint8_t*) streams[i].chunk.memblock->data + streams[i].chunk.index + d)); if (cvolume != PA_VOLUME_NORM) { v *= cvolume; @@ -111,28 +118,32 @@ size_t pa_mix(struct pa_mix_info channels[], unsigned nchannels, void *data, siz *((int16_t*) data) = sum; data = (uint8_t*) data + sizeof(int16_t); + + if (++channel >= spec->channels) + channel = 0; } } else if (spec->format == PA_SAMPLE_U8) { size_t d; + unsigned channel = 0; for (d = 0;; d ++) { int32_t sum = 0; - unsigned c; + unsigned i; if (d >= length) return d; - for (c = 0; c < nchannels; c++) { + for (i = 0; i < nstreams; i++) { int32_t v; - pa_volume_t cvolume = channels[c].volume; + pa_volume_t cvolume = streams[i].volume.values[channel]; - if (d >= channels[c].chunk.length) + if (d >= streams[i].chunk.length) return d; if (cvolume == PA_VOLUME_MUTED) v = 0; else { - v = (int32_t) *((uint8_t*) channels[c].chunk.memblock->data + channels[c].chunk.index + d) - 0x80; + v = (int32_t) *((uint8_t*) streams[i].chunk.memblock->data + streams[i].chunk.index + d) - 0x80; if (cvolume != PA_VOLUME_NORM) { v *= cvolume; @@ -155,29 +166,33 @@ size_t pa_mix(struct pa_mix_info channels[], unsigned nchannels, void *data, siz *((uint8_t*) data) = (uint8_t) (sum + 0x80); data = (uint8_t*) data + 1; + + if (++channel >= spec->channels) + channel = 0; } } else if (spec->format == PA_SAMPLE_FLOAT32NE) { size_t d; + unsigned channel = 0; for (d = 0;; d += sizeof(float)) { float_t sum = 0; - unsigned c; + unsigned i; if (d >= length) return d; - for (c = 0; c < nchannels; c++) { + for (i = 0; i < nstreams; i++) { float v; - pa_volume_t cvolume = channels[c].volume; + pa_volume_t cvolume = streams[i].volume.values[channel]; - if (d >= channels[c].chunk.length) + if (d >= streams[i].chunk.length) return d; if (cvolume == PA_VOLUME_MUTED) v = 0; else { - v = *((float*) ((uint8_t*) channels[c].chunk.memblock->data + channels[c].chunk.index + d)); + v = *((float*) ((uint8_t*) streams[i].chunk.memblock->data + streams[i].chunk.index + d)); if (cvolume != PA_VOLUME_NORM) v = v*cvolume/PA_VOLUME_NORM; @@ -196,6 +211,9 @@ size_t pa_mix(struct pa_mix_info channels[], unsigned nchannels, void *data, siz *((float*) data) = sum; data = (uint8_t*) data + sizeof(float); + + if (++channel >= spec->channels) + channel = 0; } } else { abort(); @@ -203,13 +221,14 @@ size_t pa_mix(struct pa_mix_info channels[], unsigned nchannels, void *data, siz } -void pa_volume_memchunk(struct pa_memchunk*c, const struct pa_sample_spec *spec, pa_volume_t volume) { +void pa_volume_memchunk(struct pa_memchunk*c, const struct pa_sample_spec *spec, const struct pa_cvolume *volume) { assert(c && spec && (c->length % pa_frame_size(spec) == 0)); + assert(volume); - if (volume == PA_VOLUME_NORM) + if (pa_cvolume_channels_equal_to(volume, spec->channels, PA_VOLUME_NORM)) return; - if (volume == PA_VOLUME_MUTED) { + if (pa_cvolume_channels_equal_to(volume, spec->channels, PA_VOLUME_MUTED)) { pa_silence_memchunk(c, spec); return; } @@ -217,26 +236,31 @@ void pa_volume_memchunk(struct pa_memchunk*c, const struct pa_sample_spec *spec, if (spec->format == PA_SAMPLE_S16NE) { int16_t *d; size_t n; + unsigned c = 0; for (d = (int16_t*) ((uint8_t*) c->memblock->data+c->index), n = c->length/sizeof(int16_t); n > 0; d++, n--) { int32_t t = (int32_t)(*d); - t *= volume; + t *= volume->values[c]; t /= PA_VOLUME_NORM; if (t < -0x8000) t = -0x8000; if (t > 0x7FFF) t = 0x7FFF; *d = (int16_t) t; + + if (++c >= spec->channels) + c = 0; } } else if (spec->format == PA_SAMPLE_U8) { uint8_t *d; size_t n; + unsigned c = 0; for (d = (uint8_t*) c->memblock->data + c->index, n = c->length; n > 0; d++, n--) { int32_t t = (int32_t) *d - 0x80; - t *= volume; + t *= volume->values[c]; t /= PA_VOLUME_NORM; if (t < -0x80) t = -0x80; @@ -244,21 +268,27 @@ void pa_volume_memchunk(struct pa_memchunk*c, const struct pa_sample_spec *spec, *d = (uint8_t) (t + 0x80); + if (++c >= spec->channels) + c = 0; } } else if (spec->format == PA_SAMPLE_FLOAT32NE) { float *d; size_t n; + unsigned c = 0; for (d = (float*) ((uint8_t*) c->memblock->data+c->index), n = c->length/sizeof(float); n > 0; d++, n--) { float t = *d; - t *= volume; + t *= volume->values[c]; t /= PA_VOLUME_NORM; if (t < -1) t = -1; if (t > 1) t = 1; *d = t; + + if (++c >= spec->channels) + c = 0; } } else { diff --git a/polyp/sample-util.h b/polyp/sample-util.h index aafdda63..f0c71b8c 100644 --- a/polyp/sample-util.h +++ b/polyp/sample-util.h @@ -33,12 +33,19 @@ void pa_silence_memory(void *p, size_t length, const struct pa_sample_spec *spec struct pa_mix_info { struct pa_memchunk chunk; - pa_volume_t volume; + struct pa_cvolume cvolume; void *userdata; }; -size_t pa_mix(struct pa_mix_info channels[], unsigned nchannels, void *data, size_t length, const struct pa_sample_spec *spec, pa_volume_t volume); +size_t pa_mix(const struct pa_mix_info channels[], + unsigned nchannels, + void *data, + size_t length, + const struct pa_sample_spec *spec, + const struct pa_cvolume *volume); -void pa_volume_memchunk(struct pa_memchunk*c, const struct pa_sample_spec *spec, pa_volume_t volume); +void pa_volume_memchunk(struct pa_memchunk*c, + const struct pa_sample_spec *spec, + const struct pa_cvolume *volume); #endif diff --git a/polyp/sample.c b/polyp/sample.c index f9d0c458..d38cc1b0 100644 --- a/polyp/sample.c +++ b/polyp/sample.c @@ -69,10 +69,11 @@ pa_usec_t pa_bytes_to_usec(uint64_t length, const struct pa_sample_spec *spec) { int pa_sample_spec_valid(const struct pa_sample_spec *spec) { assert(spec); - if (spec->rate <= 0 || spec->channels <= 0) - return 0; - - if (spec->format >= PA_SAMPLE_MAX || spec->format < 0) + if (spec->rate <= 0 || + spec->channels <= 0 || + spec->channels >= PA_CHANNELS_MAX || + spec->format >= PA_SAMPLE_MAX || + spec->format < 0) return 0; return 1; @@ -86,13 +87,13 @@ int pa_sample_spec_equal(const struct pa_sample_spec*a, const struct pa_sample_s const char *pa_sample_format_to_string(enum pa_sample_format f) { static const char* const table[]= { - [PA_SAMPLE_U8] = "U8", - [PA_SAMPLE_ALAW] = "ALAW", - [PA_SAMPLE_ULAW] = "ULAW", - [PA_SAMPLE_S16LE] = "S16LE", - [PA_SAMPLE_S16BE] = "S16BE", - [PA_SAMPLE_FLOAT32LE] = "FLOAT32LE", - [PA_SAMPLE_FLOAT32BE] = "FLOAT32BE", + [PA_SAMPLE_U8] = "u8", + [PA_SAMPLE_ALAW] = "aLaw", + [PA_SAMPLE_ULAW] = "uLaw", + [PA_SAMPLE_S16LE] = "s16le", + [PA_SAMPLE_S16BE] = "s16be", + [PA_SAMPLE_FLOAT32LE] = "float32le", + [PA_SAMPLE_FLOAT32BE] = "float32be", }; if (f >= PA_SAMPLE_MAX) @@ -112,43 +113,6 @@ char *pa_sample_spec_snprint(char *s, size_t l, const struct pa_sample_spec *spe return s; } -pa_volume_t pa_volume_multiply(pa_volume_t a, pa_volume_t b) { - uint64_t p = a; - p *= b; - p /= PA_VOLUME_NORM; - - return (pa_volume_t) p; -} - -pa_volume_t pa_volume_from_dB(double f) { - if (f <= PA_DECIBEL_MININFTY) - return PA_VOLUME_MUTED; - - return (pa_volume_t) (pow(10, f/20)*PA_VOLUME_NORM); -} - -double pa_volume_to_dB(pa_volume_t v) { - if (v == PA_VOLUME_MUTED) - return PA_DECIBEL_MININFTY; - - return 20*log10((double) v/PA_VOLUME_NORM); -} - -#define USER_DECIBEL_RANGE 30 - -double pa_volume_to_user(pa_volume_t v) { - double dB = pa_volume_to_dB(v); - - return dB < -USER_DECIBEL_RANGE ? 0 : dB/USER_DECIBEL_RANGE+1; -} - -pa_volume_t pa_volume_from_user(double v) { - - if (v <= 0) - return PA_VOLUME_MUTED; - - return pa_volume_from_dB((v-1)*USER_DECIBEL_RANGE); -} void pa_bytes_snprint(char *s, size_t l, unsigned v) { if (v >= ((unsigned) 1024)*1024*1024) diff --git a/polyp/sample.h b/polyp/sample.h index 0494c7de..c4ccd3da 100644 --- a/polyp/sample.h +++ b/polyp/sample.h @@ -33,8 +33,11 @@ PA_C_DECL_BEGIN +/* Maximum allowed channels */ +#define PA_CHANNELS_MAX 16 + /** Sample format */ -enum pa_sample_format { +typedef enum { PA_SAMPLE_U8, /**< Unsigned 8 Bit PCM */ PA_SAMPLE_ALAW, /**< 8 Bit a-Law */ PA_SAMPLE_ULAW, /**< 8 Bit mu-Law */ @@ -44,7 +47,7 @@ enum pa_sample_format { PA_SAMPLE_FLOAT32BE, /**< 32 Bit IEEE floating point, big endian, range -1..1 */ PA_SAMPLE_MAX, /**< Upper limit of valid sample types */ PA_SAMPLE_INVALID = -1 /**< An invalid value */ -}; +} pa_sample_format_t; #ifdef WORDS_BIGENDIAN /** Signed 16 Bit PCM, native endian */ @@ -63,7 +66,7 @@ enum pa_sample_format { /** A sample format and attribute specification */ struct pa_sample_spec { - enum pa_sample_format format; /**< The sample format */ + pa_sample_format_t format; /**< The sample format */ uint32_t rate; /**< The sample rate. (e.g. 44100) */ uint8_t channels; /**< Audio channels. (1 for mono, 2 for stereo, ...) */ }; @@ -87,7 +90,10 @@ int pa_sample_spec_valid(const struct pa_sample_spec *spec); int pa_sample_spec_equal(const struct pa_sample_spec*a, const struct pa_sample_spec*b); /* Return a descriptive string for the specified sample format. \since 0.8 */ -const char *pa_sample_format_to_string(enum pa_sample_format f); +const char *pa_sample_format_to_string(pa_sample_format_t f); + +/** Parse a sample format text. Inverse of pa_sample_format_to_string() */ +pa_sample_format_t pa_parse_sample_format(const char *format); /** Maximum required string length for pa_sample_spec_snprint() */ #define PA_SAMPLE_SPEC_SNPRINT_MAX 32 @@ -95,43 +101,9 @@ const char *pa_sample_format_to_string(enum pa_sample_format f); /** Pretty print a sample type specification to a string */ char* pa_sample_spec_snprint(char *s, size_t l, const struct pa_sample_spec *spec); -/** Volume specification: 0: silence; < 256: diminished volume; 256: normal volume; > 256 amplified volume */ -typedef uint32_t pa_volume_t; - -/** Normal volume (100%) */ -#define PA_VOLUME_NORM (0x100) - -/** Muted volume (0%) */ -#define PA_VOLUME_MUTED (0) - -/** Multiply two volumes specifications, return the result. This uses PA_VOLUME_NORM as neutral element of multiplication. */ -pa_volume_t pa_volume_multiply(pa_volume_t a, pa_volume_t b); - -/** Convert volume from decibel to linear level. \since 0.4 */ -pa_volume_t pa_volume_from_dB(double f); - -/** Convert volume from linear level to decibel. \since 0.4 */ -double pa_volume_to_dB(pa_volume_t v); - -/** Convert volume to scaled value understandable by the user (between 0 and 1). \since 0.6 */ -double pa_volume_to_user(pa_volume_t v); - -/** Convert user volume to polypaudio volume. \since 0.6 */ -pa_volume_t pa_volume_from_user(double v); - -#ifdef INFINITY -#define PA_DECIBEL_MININFTY (-INFINITY) -#else -/** This value is used as minus infinity when using pa_volume_{to,from}_dB(). \since 0.4 */ -#define PA_DECIBEL_MININFTY (-200) -#endif - /** Pretty print a byte size value. (i.e. "2.5 MB") */ void pa_bytes_snprint(char *s, size_t l, unsigned v); -/** Parse a sample format text. Inverse of pa_sample_format_to_string() */ -enum pa_sample_format pa_parse_sample_format(const char *format); - PA_C_DECL_END #endif diff --git a/polyp/sink-input.c b/polyp/sink-input.c index 3e7fdf7b..40ee7052 100644 --- a/polyp/sink-input.c +++ b/polyp/sink-input.c @@ -36,12 +36,23 @@ #define CONVERT_BUFFER_LENGTH 4096 -struct pa_sink_input* pa_sink_input_new(struct pa_sink *s, pa_typeid_t typeid, const char *name, const struct pa_sample_spec *spec, int variable_rate, int resample_method) { +struct pa_sink_input* pa_sink_input_new( + struct pa_sink *s, + const char *name, + const char *driver, + const struct pa_sample_spec *spec, + const struct pa_channel_map *map, + int variable_rate, + int resample_method) { + struct pa_sink_input *i; struct pa_resampler *resampler = NULL; int r; char st[256]; - assert(s && spec && s->state == PA_SINK_RUNNING); + + assert(s); + assert(spec); + assert(s->state == PA_SINK_RUNNING); if (pa_idxset_ncontents(s->inputs) >= PA_MAX_INPUTS_PER_SINK) { pa_log(__FILE__": Failed to create sink input: too many inputs per sink.\n"); @@ -52,18 +63,23 @@ struct pa_sink_input* pa_sink_input_new(struct pa_sink *s, pa_typeid_t typeid, c resample_method = s->core->resample_method; if (variable_rate || !pa_sample_spec_equal(spec, &s->sample_spec)) - if (!(resampler = pa_resampler_new(spec, &s->sample_spec, s->core->memblock_stat, resample_method))) + if (!(resampler = pa_resampler_new(spec, map, &s->sample_spec, &s->channel_map, s->core->memblock_stat, resample_method))) return NULL; i = pa_xmalloc(sizeof(struct pa_sink_input)); i->ref = 1; i->state = PA_SINK_INPUT_RUNNING; i->name = pa_xstrdup(name); - i->typeid = typeid; + i->driver = pa_xstrdup(driver); i->client = NULL; i->owner = NULL; i->sink = s; + i->sample_spec = *spec; + if (map) + i->channel_map = *map; + else + pa_channel_map_init_auto(&i->channel_map, spec->channels); i->peek = NULL; i->drop = NULL; @@ -72,7 +88,7 @@ struct pa_sink_input* pa_sink_input_new(struct pa_sink *s, pa_typeid_t typeid, c i->userdata = NULL; i->underrun = NULL; - i->volume = PA_VOLUME_NORM; + pa_cvolume_reset(&i->volume); i->playing = 0; i->resampled_chunk.memblock = NULL; @@ -124,6 +140,7 @@ static void sink_input_free(struct pa_sink_input* i) { pa_resampler_free(i->resampler); pa_xfree(i->name); + pa_xfree(i->driver); pa_xfree(i); } @@ -234,15 +251,22 @@ void pa_sink_input_drop(struct pa_sink_input *i, const struct pa_memchunk *chunk } } -void pa_sink_input_set_volume(struct pa_sink_input *i, pa_volume_t volume) { +void pa_sink_input_set_volume(struct pa_sink_input *i, const struct pa_cvolume *volume) { assert(i && i->sink && i->sink->core && i->ref >= 1); - if (i->volume != volume) { - i->volume = volume; + if (!pa_cvolume_equal(&i->volume, volume)) { + i->volume = *volume; pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); } } +const struct pa_cvolume * pa_sink_input_get_volume(struct pa_sink_input *i) { + assert(i); + assert(i->ref >= 1); + + return &i->volume; +} + void pa_sink_input_cork(struct pa_sink_input *i, int b) { int n; assert(i && i->ref >= 1); diff --git a/polyp/sink-input.h b/polyp/sink-input.h index 83abe537..90723c0d 100644 --- a/polyp/sink-input.h +++ b/polyp/sink-input.h @@ -31,25 +31,25 @@ #include "module.h" #include "client.h" -enum pa_sink_input_state { +typedef enum { PA_SINK_INPUT_RUNNING, PA_SINK_INPUT_CORKED, PA_SINK_INPUT_DISCONNECTED -}; +} pa_sink_input_state_t; struct pa_sink_input { int ref; - enum pa_sink_input_state state; - uint32_t index; - pa_typeid_t typeid; - - char *name; + pa_sink_input_state_t state; + + char *name, *driver; struct pa_module *owner; struct pa_client *client; struct pa_sink *sink; + struct pa_sample_spec sample_spec; - uint32_t volume; + struct pa_channel_map channel_map; + struct pa_cvolume volume; int (*peek) (struct pa_sink_input *i, struct pa_memchunk *chunk); void (*drop) (struct pa_sink_input *i, const struct pa_memchunk *chunk, size_t length); @@ -65,7 +65,15 @@ struct pa_sink_input { struct pa_resampler *resampler; }; -struct pa_sink_input* pa_sink_input_new(struct pa_sink *s, pa_typeid_t typeid, const char *name, const struct pa_sample_spec *spec, int variable_rate, int resample_method); +struct pa_sink_input* pa_sink_input_new( + struct pa_sink *s, + const char *name, + const char *driver, + const struct pa_sample_spec *spec, + const struct pa_channel_map *map, + int variable_rate, + int resample_method); + void pa_sink_input_unref(struct pa_sink_input* i); struct pa_sink_input* pa_sink_input_ref(struct pa_sink_input* i); @@ -80,7 +88,8 @@ pa_usec_t pa_sink_input_get_latency(struct pa_sink_input *i); int pa_sink_input_peek(struct pa_sink_input *i, struct pa_memchunk *chunk); void pa_sink_input_drop(struct pa_sink_input *i, const struct pa_memchunk *chunk, size_t length); -void pa_sink_input_set_volume(struct pa_sink_input *i, pa_volume_t volume); +void pa_sink_input_set_volume(struct pa_sink_input *i, const struct pa_cvolume *volume); +const struct pa_cvolume *volume pa_sink_input_get_volume(struct pa_sink_input *i); void pa_sink_input_cork(struct pa_sink_input *i, int b); diff --git a/polyp/sink.c b/polyp/sink.c index 481e5cf7..3b074721 100644 --- a/polyp/sink.c +++ b/polyp/sink.c @@ -39,12 +39,23 @@ #define MAX_MIX_CHANNELS 32 -struct pa_sink* pa_sink_new(struct pa_core *core, pa_typeid_t typeid, const char *name, int fail, const struct pa_sample_spec *spec) { +struct pa_sink* pa_sink_new( + struct pa_core *core, + const char *name, + const char *driver, + int fail, + const struct pa_sample_spec *spec, + const struct pa_channel_map *map) { + struct pa_sink *s; char *n = NULL; char st[256]; int r; - assert(core && name && *name && spec); + + assert(core); + assert(name); + assert(*name); + assert(spec); s = pa_xmalloc(sizeof(struct pa_sink)); @@ -53,31 +64,41 @@ struct pa_sink* pa_sink_new(struct pa_core *core, pa_typeid_t typeid, const char return NULL; } - s->name = pa_xstrdup(name); - s->description = NULL; - s->typeid = typeid; - s->ref = 1; + s->core = core; + s->state = PA_SINK_RUNNING; - + s->name = pa_xstrdup(name); + s->description = NULL; + s->driver = pa_xstrdup(driver); s->owner = NULL; - s->core = core; + s->sample_spec = *spec; + if (map) + s->channel_map = *map; + else + pa_channel_map_init_auto(&s->channel_map, spec->channels); + s->inputs = pa_idxset_new(NULL, NULL); n = pa_sprintf_malloc("%s_monitor", name); - s->monitor_source = pa_source_new(core, typeid, n, 0, spec); + s->monitor_source = pa_source_new(core, n, driver, 0, spec, map); assert(s->monitor_source); pa_xfree(n); s->monitor_source->monitor_of = s; s->monitor_source->description = pa_sprintf_malloc("Monitor source of sink '%s'", s->name); - - s->volume = PA_VOLUME_NORM; + + pa_cvolume_reset(&s->sw_volume); + pa_cvolume_reset(&s->hw_volume); s->notify = NULL; s->get_latency = NULL; + s->set_volume = NULL; + s->get_volume = NULL; s->userdata = NULL; + s->flags = 0; + r = pa_idxset_put(core->sinks, s, &s->index); assert(s->index != PA_IDXSET_INVALID && r >= 0); @@ -127,6 +148,7 @@ static void sink_free(struct pa_sink *s) { pa_xfree(s->name); pa_xfree(s->description); + pa_xfree(s->driver); pa_xfree(s); } @@ -360,11 +382,38 @@ void pa_sink_set_owner(struct pa_sink *s, struct pa_module *m) { pa_source_set_owner(s->monitor_source, m); } -void pa_sink_set_volume(struct pa_sink *s, pa_volume_t volume) { - assert(s && s->ref >= 1); - - if (s->volume != volume) { - s->volume = volume; - pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); - } +void pa_sink_set_volume(struct pa_sink *s, pa_mixer_t m, const struct pa_cvolume *volume) { + struct pa_cvolume *v; + assert(s); + assert(s->ref >= 1); + assert(volume); + + if ((m == PA_MIXER_HARDWARE || m == PA_MIXER_AUTO) && s->set_volume) + v = &s->hw_volume; + else + v = &s->sw_volume; + + if (pa_cvolume_equal(v, volume)) + return; + + *v = volume; + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); + + if (v == &s->hw_volume) + s->set_volume(s); +} + +const struct pa_cvolume *pa_sink_get_volume(struct pa_sink *sink, pa_mixer_t m) { + struct pa_cvolume *v; + assert(s); + assert(s->ref >= 1); + + if ((m == PA_MIXER_HARDWARE || m == PA_MIXER_AUTO) && s->set_volume) { + + if (s->get_volume) + s->get_volume(s); + + return &s->hw_volume; + } else + return &s->sw_volume; } diff --git a/polyp/sink.h b/polyp/sink.h index 844af964..a6d7efa1 100644 --- a/polyp/sink.h +++ b/polyp/sink.h @@ -30,38 +30,51 @@ struct pa_sink; #include "sample.h" #include "idxset.h" #include "source.h" -#include "typeid.h" +#include "channelmap.h" #define PA_MAX_INPUTS_PER_SINK 6 -enum pa_sink_state { +typedef enum { PA_SINK_RUNNING, PA_SINK_DISCONNECTED -}; +} pa_sink_state_t; + +typedef enum { + PA_MIXER_AUTO, + PA_MIXER_SOFTWARE, + PA_MIXER_HARDWARE +} pa_mixer_t; struct pa_sink { int ref; - enum pa_sink_state state; - uint32_t index; - pa_typeid_t typeid; - - char *name, *description; - struct pa_module *owner; struct pa_core *core; + pa_sink_state_t state; + + char *name, *description, *driver; struct pa_sample_spec sample_spec; + struct pa_channel_map channel_map; struct pa_idxset *inputs; - + struct pa_module *owner; struct pa_source *monitor_source; - - pa_volume_t volume; + struct pa_cvolume hw_volume, sw_volume; void (*notify)(struct pa_sink*sink); pa_usec_t (*get_latency)(struct pa_sink *s); + void (*set_volume)(struct pa_sink *s); + void (*get_volume)(struct pa_sink *s); + void *userdata; }; -struct pa_sink* pa_sink_new(struct pa_core *core, pa_typeid_t typeid, const char *name, int fail, const struct pa_sample_spec *spec); +struct pa_sink* pa_sink_new( + struct pa_core *core, + const char *name, + const char *driver, + int fail, + const struct pa_sample_spec *spec, + const struct pa_channel_map *map); + void pa_sink_disconnect(struct pa_sink* s); void pa_sink_unref(struct pa_sink*s); struct pa_sink* pa_sink_ref(struct pa_sink *s); @@ -77,6 +90,7 @@ void pa_sink_notify(struct pa_sink*s); void pa_sink_set_owner(struct pa_sink *sink, struct pa_module *m); -void pa_sink_set_volume(struct pa_sink *sink, pa_volume_t volume); +void pa_sink_set_volume(struct pa_sink *sink, pa_mixer_t m, const struct pa_cvolume *volume); +const struct pa_cvolume *pa_sink_get_volume(struct pa_sink *sink, pa_mixer_t m); #endif diff --git a/polyp/source-output.c b/polyp/source-output.c index f954c23f..fa9f252f 100644 --- a/polyp/source-output.c +++ b/polyp/source-output.c @@ -33,7 +33,14 @@ #include "subscribe.h" #include "log.h" -struct pa_source_output* pa_source_output_new(struct pa_source *s, pa_typeid_t typeid, const char *name, const struct pa_sample_spec *spec, int resample_method) { +struct pa_source_output* pa_source_output_new( + struct pa_source *s, + const char *name, + const char *driver, + const struct pa_sample_spec *spec, + const struct pa_channel_map *map, + int resample_method) { + struct pa_source_output *o; struct pa_resampler *resampler = NULL; int r; @@ -49,19 +56,24 @@ struct pa_source_output* pa_source_output_new(struct pa_source *s, pa_typeid_t t resample_method = s->core->resample_method; if (!pa_sample_spec_equal(&s->sample_spec, spec)) - if (!(resampler = pa_resampler_new(&s->sample_spec, spec, s->core->memblock_stat, resample_method))) + if (!(resampler = pa_resampler_new(&s->sample_spec, &s->channel_map, spec, map, s->core->memblock_stat, resample_method))) return NULL; o = pa_xmalloc(sizeof(struct pa_source_output)); o->ref = 1; o->state = PA_SOURCE_OUTPUT_RUNNING; o->name = pa_xstrdup(name); - o->typeid = typeid; + o->driver = pa_xstrdup(driver); o->client = NULL; o->owner = NULL; o->source = s; + o->sample_spec = *spec; + if (map) + c->channel_map = *map; + else + pa_channel_map_init_auto(&c->channel_map, spec->channels); o->push = NULL; o->kill = NULL; @@ -96,7 +108,6 @@ void pa_source_output_disconnect(struct pa_source_output*o) { o->push = NULL; o->kill = NULL; - o->state = PA_SOURCE_OUTPUT_DISCONNECTED; } @@ -112,6 +123,7 @@ static void source_output_free(struct pa_source_output* o) { pa_resampler_free(o->resampler); pa_xfree(o->name); + pa_xfree(o->driver); pa_xfree(o); } diff --git a/polyp/source-output.h b/polyp/source-output.h index f3187aa9..f561e050 100644 --- a/polyp/source-output.h +++ b/polyp/source-output.h @@ -31,24 +31,24 @@ #include "module.h" #include "client.h" -enum pa_source_output_state { +typedef enum { PA_SOURCE_OUTPUT_RUNNING, PA_SOURCE_OUTPUT_CORKED, PA_SOURCE_OUTPUT_DISCONNECTED -}; +} pa_source_output_state_t; struct pa_source_output { int ref; - enum pa_source_output_state state; - uint32_t index; - pa_typeid_t typeid; - - char *name; + pa_source_output_state_t state; + + char *name, *driver; struct pa_module *owner; struct pa_client *client; struct pa_source *source; + struct pa_sample_spec sample_spec; + struct pa_channel_map channel_map; void (*push)(struct pa_source_output *o, const struct pa_memchunk *chunk); void (*kill)(struct pa_source_output* o); @@ -59,7 +59,14 @@ struct pa_source_output { void *userdata; }; -struct pa_source_output* pa_source_output_new(struct pa_source *s, pa_typeid_t typeid, const char *name, const struct pa_sample_spec *spec, int resample_method); +struct pa_source_output* pa_source_output_new( + struct pa_source *s, + const char *name, + const char *driver, + const struct pa_sample_spec *spec, + const struct pa_channel_map *map; + int resample_method); + void pa_source_output_unref(struct pa_source_output* o); struct pa_source_output* pa_source_output_ref(struct pa_source_output *o); diff --git a/polyp/source.c b/polyp/source.c index fc73272c..a80c6af7 100644 --- a/polyp/source.c +++ b/polyp/source.c @@ -35,11 +35,22 @@ #include "subscribe.h" #include "log.h" -struct pa_source* pa_source_new(struct pa_core *core, pa_typeid_t typeid, const char *name, int fail, const struct pa_sample_spec *spec) { +struct pa_source* pa_source_new( + struct pa_core *core, + const char *name, + const char *driver, + int fail, + const struct pa_sample_spec *spec, + const struct pa_channel_map *map) { + struct pa_source *s; char st[256]; int r; - assert(core && spec && name && *name); + + assert(core); + assert(name); + assert(*name); + assert(spec); s = pa_xmalloc(sizeof(struct pa_source)); @@ -53,11 +64,16 @@ struct pa_source* pa_source_new(struct pa_core *core, pa_typeid_t typeid, const s->name = pa_xstrdup(name); s->description = NULL; - s->typeid = typeid; + s->driver = pa_xstrdup(driver); s->owner = NULL; s->core = core; s->sample_spec = *spec; + if (map) + s->channel_map = *map; + else + pa_channel_map_init_auto(&s->channel_map, spec->channels); + s->outputs = pa_idxset_new(NULL, NULL); s->monitor_of = NULL; @@ -108,6 +124,7 @@ static void source_free(struct pa_source *s) { pa_xfree(s->name); pa_xfree(s->description); + pa_xfree(s->driver); pa_xfree(s); } diff --git a/polyp/source.h b/polyp/source.h index 0fac2b34..21ebda53 100644 --- a/polyp/source.h +++ b/polyp/source.h @@ -31,35 +31,42 @@ struct pa_source; #include "memblock.h" #include "memchunk.h" #include "sink.h" -#include "typeid.h" +#include "channelmap.h" #define PA_MAX_OUTPUTS_PER_SOURCE 16 -enum pa_source_state { +typedef enum { PA_SOURCE_RUNNING, PA_SOURCE_DISCONNECTED -}; +} pa_source_state_t; struct pa_source { int ref; - enum pa_source_state state; - uint32_t index; - pa_typeid_t typeid; - - char *name, *description; - struct pa_module *owner; struct pa_core *core; + pa_source_state_t state; + + char *name, *description, *driver; struct pa_sample_spec sample_spec; + struct pa_channel_map channel_map; struct pa_idxset *outputs; struct pa_sink *monitor_of; + struct pa_module *owner; void (*notify)(struct pa_source*source); pa_usec_t (*get_latency)(struct pa_source *s); + void *userdata; }; -struct pa_source* pa_source_new(struct pa_core *core, pa_typeid_t typeid, const char *name, int fail, const struct pa_sample_spec *spec); +struct pa_source* pa_source_new( + struct pa_core *core, + const char *name, + const char *driver, + int fail, + const struct pa_sample_spec *spec, + const struct pa_channel_map *map); + void pa_source_disconnect(struct pa_source *s); void pa_source_unref(struct pa_source *s); struct pa_source* pa_source_ref(struct pa_source *c); diff --git a/polyp/tagstruct.h b/polyp/tagstruct.h index 135825e6..f867cfb4 100644 --- a/polyp/tagstruct.h +++ b/polyp/tagstruct.h @@ -44,6 +44,8 @@ void pa_tagstruct_put_arbitrary(struct pa_tagstruct*t, const void *p, size_t len void pa_tagstruct_put_boolean(struct pa_tagstruct*t, int b); void pa_tagstruct_put_timeval(struct pa_tagstruct*t, const struct timeval *tv); void pa_tagstruct_put_usec(struct pa_tagstruct*t, pa_usec_t u); +void pa_tagstruct_put_channel_map(struct pa_tagstruct *t, const struct pa_channel_map *map); +void pa_tagstruct_put_cvolume(struct pa_tagstruct *t, const struct pa_cvolume *cvolume); int pa_tagstruct_gets(struct pa_tagstruct*t, const char **s); int pa_tagstruct_getu8(struct pa_tagstruct*t, uint8_t *c); @@ -54,6 +56,8 @@ int pa_tagstruct_get_arbitrary(struct pa_tagstruct *t, const void **p, size_t le int pa_tagstruct_get_boolean(struct pa_tagstruct *t, int *b); int pa_tagstruct_get_timeval(struct pa_tagstruct*t, struct timeval *tv); int pa_tagstruct_get_usec(struct pa_tagstruct*t, pa_usec_t *u); +int pa_tagstruct_get_channel_map(struct pa_tagstruct *t, struct pa_channel_map *map); +int pa_tagstruct_get_cvolume(struct pa_tagstruct *t, struct pa_cvolume *v); int pa_tagstruct_eof(struct pa_tagstruct*t); const uint8_t* pa_tagstruct_data(struct pa_tagstruct*t, size_t *l); diff --git a/polyp/voltest.c b/polyp/voltest.c index d8d5c569..0c4e2326 100644 --- a/polyp/voltest.c +++ b/polyp/voltest.c @@ -2,13 +2,18 @@ #include <stdio.h> -#include <polyp/sample.h> +#include <polyp/volume.h> int main() { - int p; - for (p = 0; p <= 200; p++) { - pa_volume_t v = pa_volume_from_user((double) p/100); - double dB = pa_volume_to_dB(v); - printf("%3i%% = %u = %0.2f dB = %u = %3i%%\n", p, v, dB, pa_volume_from_dB(dB), (int) (pa_volume_to_user(v)*100)); + pa_volume_t v; + + for (v = PA_VOLUME_MUTED; v <= PA_VOLUME_NORM*2; v += 256) { + + double dB = pa_sw_volume_to_dB(v); + double f = pa_sw_volume_to_linear(v); + + printf("Volume: %3i; percent: %i%%; decibel %0.2f; linear = %0.2f; volume(decibel): %3i; volume(linear): %3i\n", + v, (v*100)/PA_VOLUME_NORM, dB, f, pa_sw_volume_from_dB(dB), pa_sw_volume_from_linear(f)); + } } diff --git a/polyp/volume.c b/polyp/volume.c new file mode 100644 index 00000000..bb9d30db --- /dev/null +++ b/polyp/volume.c @@ -0,0 +1,161 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <assert.h> + +#include "volume.h" + +int pa_cvolume_equal(const struct pa_cvolume *a, const struct pa_cvolume *b) { + int i; + assert(a); + assert(b); + + if (a->channels != b->channels) + return 0; + + for (i = 0; i < a->channels; i++) + if (a->values[i] != b->values[i]) + return 0; + + return 1; +} + +void pa_cvolume_set(struct pa_cvolume *a, pa_volume_t v) { + int i; + assert(a); + + a->channels = PA_CHANNELS_MAX; + + for (i = 0; i < a->channels; i++) + a->values[i] = v; +} + +void pa_cvolume_reset(struct pa_cvolume *a) { + assert(a); + pa_cvolume_set(a, PA_VOLUME_NORM); +} + +void pa_cvolume_mute(struct pa_cvolume *a) { + assert(a); + pa_cvolume_set(a, PA_VOLUME_MUTED); +} + +pa_volume_t pa_cvolume_avg(const struct pa_cvolume *a) { + uint64_t sum = 0; + int i; + assert(a); + + for (i = 0; i < a->channels; i++) + sum += a->values[i]; + + sum /= a->channels; + + return (pa_volume_t) sum; +} + +pa_volume_t pa_sw_volume_multiply(pa_volume_t a, pa_volume_t b) { + uint64_t p = a; + p *= b; + p /= PA_VOLUME_NORM; + + return pa_sw_volume_from_linear(pa_sw_volume_to_linear(a)* pa_sw_volume_to_linear(b)); +} + +#define USER_DECIBEL_RANGE 30 + +pa_volume_t pa_sw_volume_from_dB(double dB) { + if (dB <= -USER_DECIBEL_RANGE) + return PA_VOLUME_MUTED; + + return (pa_volume_t) ((dB/USER_DECIBEL_RANGE+1)*PA_VOLUME_NORM); +} + +double pa_sw_volume_to_dB(pa_volume_t v) { + if (v == PA_VOLUME_MUTED) + return PA_DECIBEL_MININFTY; + + return ((double) v/PA_VOLUME_NORM-1)*USER_DECIBEL_RANGE; +} + +pa_volume_t pa_sw_volume_from_linear(double v) { + + if (v <= 0) + return PA_VOLUME_MUTED; + + if (v == 1) + return PA_VOLUME_NORM; + + return pa_sw_volume_from_dB(20*log10(v)); +} + +double pa_sw_volume_to_linear(pa_volume_t v) { + + if (v == PA_VOLUME_MUTED) + return 0; + + return pow(10, pa_sw_volume_to_dB(v)/20); + +} + +char *pa_cvolume_snprintf(char *s, size_t l, const struct pa_cvolume *c, unsigned channels) { + unsigned c; + int first = 1; + + assert(s); + assert(l > 0); + assert(c); + + if (channels > PA_CHANNELS_MAX || channels <= 0) + channels = PA_CHANNELS_MAX; + + *s = 0; + + for (c = 0; c < channels && l > 1; c++) { + l -= snprintf(s, l, "%s%u: %3u%%", + first ? "" : " ", + c, + (c->channels[c]*100)/PA_VOLUME_NORM); + + s = strchr(s, 0); + } + + return s; +} + + +/** Return non-zero if the volume of all channels is equal to the specified value */ +int pa_cvolume_channels_equal_to(const struct pa_cvolume *a, uint8_t channels, pa_volume_t v) { + unsigned c; + assert(a); + + if (channels > PA_CHANNELS_MAX) + channels = PA_CHANNELS_MAX; + + for (c = 0; c < channels; c++) + if (a->map[c] != v) + return 0; + + return 1; +} diff --git a/polyp/volume.h b/polyp/volume.h new file mode 100644 index 00000000..c013ddde --- /dev/null +++ b/polyp/volume.h @@ -0,0 +1,98 @@ +#ifndef foovolumehfoo +#define foovolumehfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include <inttypes.h> +#include <polyp/cdecl.h> +#include <polyp/sample.h> + +/** \file + * Constants and routines for volume handling */ + +PA_C_DECL_BEGIN + +/** Volume specification: + * PA_VOLUME_MUTED: silence; + * < PA_VOLUME_NORM: decreased volume; + * PA_VOLUME_NORM: normal volume; + * > PA_VOLUME_NORM: increased volume */ +typedef uint32_t pa_volume_t; + +/** Normal volume (100%) */ +#define PA_VOLUME_NORM (0x10000) + +/** Muted volume (0%) */ +#define PA_VOLUME_MUTED (0) + +/** A structure encapsulating a per-channel volume */ +struct pa_cvolume { + uint8_t channels; + pa_volume_t values[PA_CHANNELS_MAX]; +}; + +/** Return non-zero when *a == *b */ +int pa_cvolume_equal(const struct pa_cvolume *a, const struct pa_cvolume *b); + +/** Set the volume of all channels to PA_VOLUME_NORM */ +void pa_cvolume_reset(struct pa_cvolume *a); + +/** Set the volume of all channels to PA_VOLUME_MUTED */ +void pa_cvolume_mute(struct pa_cvolume *a); + +/** Set the volume of all channels to the specified parameter */ +void pa_cvolume_set(struct pa_cvolume *a, pa_volume_t v); + +/** Pretty print a volume structure */ +char *pa_cvolume_snprintf(char *s, size_t l, const struct pa_cvolume *c, unsigned channels); + +/** Return the average volume of all channels */ +pa_volume_t pa_cvolume_avg(const struct pa_cvolume *a); + +/** Return non-zero if the volume of all channels is equal to the specified value */ +int pa_cvolume_channels_equal_to(const struct pa_cvolume *a, uint8_t channels, pa_volume_t v); + +/** Multiply two volumes specifications, return the result. This uses PA_VOLUME_NORM as neutral element of multiplication. */ +pa_volume_t pa_sw_volume_multiply(pa_volume_t a, pa_volume_t b); + +/** Convert a decibel value to a volume. \since 0.4 */ +pa_volume_t pa_sw_volume_from_dB(double f); + +/** Convert a volume to a decibel value. \since 0.4 */ +double pa_sw_volume_to_dB(pa_volume_t v); + +/** Convert a linear factor to a volume. \since 0.8 */ +pa_volume_t pa_sw_volume_from_linear(double v); + +/** Convert a volume to a linear factor. \since 0.8 */ +double pa_sw_volume_to_linear(pa_volume_t v); + +#ifdef INFINITY +#define PA_DECIBEL_MININFTY (-INFINITY) +#else +/** This value is used as minus infinity when using pa_volume_{to,from}_dB(). \since 0.4 */ +#define PA_DECIBEL_MININFTY (-200) +#endif + +PA_C_DECL_END + +#endif |