Merge remote-tracking branch 'arun/no-ramping'

This commit is contained in:
Colin Guthrie 2011-02-28 19:39:10 +00:00
commit f834150ace
9 changed files with 82 additions and 1642 deletions

View file

@ -109,7 +109,6 @@ src/pulsecore/core-error.c
src/pulsecore/strbuf.c
src/pulsecore/play-memblockq.c
src/pulsecore/dllmain.c
src/pulsecore/envelope.c
src/pulsecore/pid.c
src/pulsecore/thread-mq.c
src/pulsecore/shm.c

1
src/.gitignore vendored
View file

@ -24,7 +24,6 @@ cpulimit-test2
daemon.conf
default.pa
system.pa
envelope-test
esdcompat
flist-test
gconf-helper

View file

@ -259,7 +259,6 @@ TESTS = \
smoother-test \
mix-test \
remix-test \
envelope-test \
proplist-test \
lock-autospawn-test \
prioq-test
@ -294,7 +293,6 @@ TESTS_BINARIES = \
smoother-test \
mix-test \
remix-test \
envelope-test \
proplist-test \
rtstutter \
stripnul \
@ -514,11 +512,6 @@ smoother_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulse.la li
smoother_test_CFLAGS = $(AM_CFLAGS)
smoother_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
envelope_test_SOURCES = tests/envelope-test.c
envelope_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
envelope_test_CFLAGS = $(AM_CFLAGS)
envelope_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
proplist_test_SOURCES = tests/proplist-test.c
proplist_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
proplist_test_CFLAGS = $(AM_CFLAGS)
@ -837,7 +830,6 @@ libpulsecore_@PA_MAJORMINOR@_la_SOURCES = \
pulsecore/core-scache.c pulsecore/core-scache.h \
pulsecore/core-subscribe.c pulsecore/core-subscribe.h \
pulsecore/core.c pulsecore/core.h \
pulsecore/envelope.c pulsecore/envelope.h \
pulsecore/fdsem.c pulsecore/fdsem.h \
pulsecore/g711.c pulsecore/g711.h \
pulsecore/hook-list.c pulsecore/hook-list.h \

View file

@ -1,989 +0,0 @@
/***
This file is part of PulseAudio.
Copyright 2007 Lennart Poettering
PulseAudio 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.1 of the
License, or (at your option) any later version.
PulseAudio 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; 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 <stdio.h>
#include <pulse/sample.h>
#include <pulse/xmalloc.h>
#include <pulsecore/endianmacros.h>
#include <pulsecore/memchunk.h>
#include <pulsecore/macro.h>
#include <pulsecore/flist.h>
#include <pulsecore/semaphore.h>
#include <pulsecore/g711.h>
#include "envelope.h"
/*
Envelope subsystem for applying linear interpolated volume
envelopes on audio data. If multiple enevelopes shall be applied
at the same time, the "minimum" envelope is determined and
applied.
Envelopes are defined in a statically allocated constant structure
pa_envelope_def. It may be activated using pa_envelope_add(). And
already active envelope may be replaced with pa_envelope_replace()
and removed with pa_envelope_remove().The combined "minimum"
envelope can be applied to audio data with pa_envelope_apply().
_apply() on one hand and _add()/_replace()/_remove() on the other
can be executed in seperate threads, in which case no locking is
used.
*/
PA_STATIC_FLIST_DECLARE(items, 0, pa_xfree);
struct pa_envelope_item {
PA_LLIST_FIELDS(pa_envelope_item);
const pa_envelope_def *def;
pa_usec_t start_x;
union {
int32_t i;
float f;
} start_y;
unsigned j;
};
enum envelope_state {
STATE_VALID0,
STATE_VALID1,
STATE_READ0,
STATE_READ1,
STATE_WAIT0,
STATE_WAIT1,
STATE_WRITE0,
STATE_WRITE1
};
struct pa_envelope {
pa_sample_spec sample_spec;
PA_LLIST_HEAD(pa_envelope_item, items);
pa_atomic_t state;
size_t x;
struct {
unsigned n_points, n_allocated, n_current;
size_t *x;
union {
int32_t *i;
float *f;
} y;
size_t cached_dx;
int32_t cached_dy_i;
float cached_dy_dx;
pa_bool_t cached_valid;
} points[2];
pa_bool_t is_float;
pa_semaphore *semaphore;
};
pa_envelope *pa_envelope_new(const pa_sample_spec *ss) {
pa_envelope *e;
pa_assert(ss);
e = pa_xnew(pa_envelope, 1);
e->sample_spec = *ss;
PA_LLIST_HEAD_INIT(pa_envelope_item, e->items);
e->x = 0;
e->points[0].n_points = e->points[1].n_points = 0;
e->points[0].n_allocated = e->points[1].n_allocated = 0;
e->points[0].n_current = e->points[1].n_current = 0;
e->points[0].x = e->points[1].x = NULL;
e->points[0].y.i = e->points[1].y.i = NULL;
e->points[0].cached_valid = e->points[1].cached_valid = FALSE;
pa_atomic_store(&e->state, STATE_VALID0);
e->is_float =
ss->format == PA_SAMPLE_FLOAT32LE ||
ss->format == PA_SAMPLE_FLOAT32BE;
e->semaphore = pa_semaphore_new(0);
return e;
}
void pa_envelope_free(pa_envelope *e) {
pa_assert(e);
while (e->items)
pa_envelope_remove(e, e->items);
pa_xfree(e->points[0].x);
pa_xfree(e->points[1].x);
pa_xfree(e->points[0].y.i);
pa_xfree(e->points[1].y.i);
pa_semaphore_free(e->semaphore);
pa_xfree(e);
}
static int32_t linear_interpolate_int(pa_usec_t x1, int32_t _y1, pa_usec_t x2, int32_t y2, pa_usec_t x3) {
return (int32_t) ((double) _y1 + (double) (x3 - x1) * (double) (y2 - _y1) / (double) (x2 - x1));
}
static float linear_interpolate_float(pa_usec_t x1, float _y1, pa_usec_t x2, float y2, pa_usec_t x3) {
return _y1 + ((float) x3 - (float) x1) * (y2 - _y1) / ((float) x2 - (float) x1);
}
static int32_t item_get_int(pa_envelope_item *i, pa_usec_t x) {
pa_assert(i);
if (x <= i->start_x)
return i->start_y.i;
x -= i->start_x;
if (x <= i->def->points_x[0])
return linear_interpolate_int(0, i->start_y.i,
i->def->points_x[0], i->def->points_y.i[0], x);
if (x >= i->def->points_x[i->def->n_points-1])
return i->def->points_y.i[i->def->n_points-1];
pa_assert(i->j > 0);
pa_assert(i->def->points_x[i->j-1] <= x);
pa_assert(x <= i->def->points_x[i->j]);
return linear_interpolate_int(i->def->points_x[i->j-1], i->def->points_y.i[i->j-1],
i->def->points_x[i->j], i->def->points_y.i[i->j], x);
}
static float item_get_float(pa_envelope_item *i, pa_usec_t x) {
pa_assert(i);
if (x <= i->start_x)
return i->start_y.f;
x -= i->start_x;
if (x <= i->def->points_x[0])
return linear_interpolate_float(0, i->start_y.f,
i->def->points_x[0], i->def->points_y.f[0], x);
if (x >= i->def->points_x[i->def->n_points-1])
return i->def->points_y.f[i->def->n_points-1];
pa_assert(i->j > 0);
pa_assert(i->def->points_x[i->j-1] <= x);
pa_assert(x <= i->def->points_x[i->j]);
return linear_interpolate_float(i->def->points_x[i->j-1], i->def->points_y.f[i->j-1],
i->def->points_x[i->j], i->def->points_y.f[i->j], x);
}
static void envelope_begin_write(pa_envelope *e, int *v) {
enum envelope_state new_state, old_state;
pa_bool_t wait_sem;
pa_assert(e);
pa_assert(v);
for (;;) {
do {
wait_sem = FALSE;
old_state = pa_atomic_load(&e->state);
switch (old_state) {
case STATE_VALID0:
*v = 1;
new_state = STATE_WRITE0;
break;
case STATE_VALID1:
*v = 0;
new_state = STATE_WRITE1;
break;
case STATE_READ0:
new_state = STATE_WAIT0;
wait_sem = TRUE;
break;
case STATE_READ1:
new_state = STATE_WAIT1;
wait_sem = TRUE;
break;
default:
pa_assert_not_reached();
}
} while (!pa_atomic_cmpxchg(&e->state, old_state, new_state));
if (!wait_sem)
break;
pa_semaphore_wait(e->semaphore);
}
}
static pa_bool_t envelope_commit_write(pa_envelope *e, int v) {
enum envelope_state new_state, old_state;
pa_assert(e);
do {
old_state = pa_atomic_load(&e->state);
switch (old_state) {
case STATE_WRITE0:
pa_assert(v == 1);
new_state = STATE_VALID1;
break;
case STATE_WRITE1:
pa_assert(v == 0);
new_state = STATE_VALID0;
break;
case STATE_VALID0:
case STATE_VALID1:
case STATE_READ0:
case STATE_READ1:
return FALSE;
default:
pa_assert_not_reached();
}
} while (!pa_atomic_cmpxchg(&e->state, old_state, new_state));
return TRUE;
}
static void envelope_begin_read(pa_envelope *e, int *v) {
enum envelope_state new_state, old_state;
pa_assert(e);
pa_assert(v);
do {
old_state = pa_atomic_load(&e->state);
switch (old_state) {
case STATE_VALID0:
case STATE_WRITE0:
*v = 0;
new_state = STATE_READ0;
break;
case STATE_VALID1:
case STATE_WRITE1:
*v = 1;
new_state = STATE_READ1;
break;
default:
pa_assert_not_reached();
}
} while (!pa_atomic_cmpxchg(&e->state, old_state, new_state));
}
static void envelope_commit_read(pa_envelope *e, int v) {
enum envelope_state new_state, old_state;
pa_bool_t post_sem;
pa_assert(e);
do {
post_sem = FALSE;
old_state = pa_atomic_load(&e->state);
switch (old_state) {
case STATE_READ0:
pa_assert(v == 0);
new_state = STATE_VALID0;
break;
case STATE_READ1:
pa_assert(v == 1);
new_state = STATE_VALID1;
break;
case STATE_WAIT0:
pa_assert(v == 0);
new_state = STATE_VALID0;
post_sem = TRUE;
break;
case STATE_WAIT1:
pa_assert(v == 1);
new_state = STATE_VALID1;
post_sem = TRUE;
break;
default:
pa_assert_not_reached();
}
} while (!pa_atomic_cmpxchg(&e->state, old_state, new_state));
if (post_sem)
pa_semaphore_post(e->semaphore);
}
static void envelope_merge(pa_envelope *e, int v) {
e->points[v].n_points = 0;
if (e->items) {
pa_envelope_item *i;
pa_usec_t x = (pa_usec_t) -1;
for (i = e->items; i; i = i->next)
i->j = 0;
for (;;) {
pa_bool_t min_is_set;
pa_envelope_item *s = NULL;
/* Let's find the next spot on the X axis to analyze */
for (i = e->items; i; i = i->next) {
for (;;) {
if (i->j >= i->def->n_points)
break;
if ((x != (pa_usec_t) -1) && i->start_x + i->def->points_x[i->j] <= x) {
i->j++;
continue;
}
if (!s || (i->start_x + i->def->points_x[i->j] < s->start_x + s->def->points_x[s->j]))
s = i;
break;
}
}
if (!s)
break;
if (e->points[v].n_points >= e->points[v].n_allocated) {
e->points[v].n_allocated = PA_MAX(e->points[v].n_points*2, PA_ENVELOPE_POINTS_MAX);
e->points[v].x = pa_xrealloc(e->points[v].x, sizeof(size_t) * e->points[v].n_allocated);
e->points[v].y.i = pa_xrealloc(e->points[v].y.i, sizeof(int32_t) * e->points[v].n_allocated);
}
x = s->start_x + s->def->points_x[s->j];
e->points[v].x[e->points[v].n_points] = pa_usec_to_bytes(x, &e->sample_spec);
min_is_set = FALSE;
/* Now let's find the lowest value */
if (e->is_float) {
float min_f;
for (i = e->items; i; i = i->next) {
float f = item_get_float(i, x);
if (!min_is_set || f < min_f) {
min_f = f;
min_is_set = TRUE;
}
}
e->points[v].y.f[e->points[v].n_points] = min_f;
} else {
int32_t min_k;
for (i = e->items; i; i = i->next) {
int32_t k = item_get_int(i, x);
if (!min_is_set || k < min_k) {
min_k = k;
min_is_set = TRUE;
}
}
e->points[v].y.i[e->points[v].n_points] = min_k;
}
pa_assert_se(min_is_set);
e->points[v].n_points++;
}
}
e->points[v].n_current = 0;
e->points[v].cached_valid = FALSE;
}
pa_envelope_item *pa_envelope_add(pa_envelope *e, const pa_envelope_def *def) {
pa_envelope_item *i;
int v;
pa_assert(e);
pa_assert(def);
pa_assert(def->n_points > 0);
if (!(i = pa_flist_pop(PA_STATIC_FLIST_GET(items))))
i = pa_xnew(pa_envelope_item, 1);
i->def = def;
if (e->is_float)
i->start_y.f = def->points_y.f[0];
else
i->start_y.i = def->points_y.i[0];
PA_LLIST_PREPEND(pa_envelope_item, e->items, i);
envelope_begin_write(e, &v);
do {
i->start_x = pa_bytes_to_usec(e->x, &e->sample_spec);
envelope_merge(e, v);
} while (!envelope_commit_write(e, v));
return i;
}
pa_envelope_item *pa_envelope_replace(pa_envelope *e, pa_envelope_item *i, const pa_envelope_def *def) {
pa_usec_t x;
int v;
pa_assert(e);
pa_assert(i);
pa_assert(def->n_points > 0);
envelope_begin_write(e, &v);
for (;;) {
float saved_f;
int32_t saved_i;
uint64_t saved_start_x;
const pa_envelope_def *saved_def;
x = pa_bytes_to_usec(e->x, &e->sample_spec);
if (e->is_float) {
saved_f = i->start_y.f;
i->start_y.f = item_get_float(i, x);
} else {
saved_i = i->start_y.i;
i->start_y.i = item_get_int(i, x);
}
saved_start_x = i->start_x;
saved_def = i->def;
i->start_x = x;
i->def = def;
envelope_merge(e, v);
if (envelope_commit_write(e, v))
break;
i->start_x = saved_start_x;
i->def = saved_def;
if (e->is_float)
i->start_y.f = saved_f;
else
i->start_y.i = saved_i;
}
return i;
}
void pa_envelope_remove(pa_envelope *e, pa_envelope_item *i) {
int v;
pa_assert(e);
pa_assert(i);
PA_LLIST_REMOVE(pa_envelope_item, e->items, i);
if (pa_flist_push(PA_STATIC_FLIST_GET(items), i) < 0)
pa_xfree(i);
envelope_begin_write(e, &v);
do {
envelope_merge(e, v);
} while (!envelope_commit_write(e, v));
}
static int32_t linear_get_int(pa_envelope *e, int v) {
pa_assert(e);
/* The repeated division could be replaced by Bresenham, as an
* optimization */
if (e->x < e->points[v].x[0])
return e->points[v].y.i[0];
for (;;) {
if (e->points[v].n_current+1 >= e->points[v].n_points)
return e->points[v].y.i[e->points[v].n_points-1];
if (e->x < e->points[v].x[e->points[v].n_current+1])
break;
e->points[v].n_current++;
e->points[v].cached_valid = FALSE;
}
if (!e->points[v].cached_valid) {
e->points[v].cached_dx = e->points[v].x[e->points[v].n_current+1] - e->points[v].x[e->points[v].n_current];
e->points[v].cached_dy_i = e->points[v].y.i[e->points[v].n_current+1] - e->points[v].y.i[e->points[v].n_current];
e->points[v].cached_valid = TRUE;
}
return e->points[v].y.i[e->points[v].n_current] + ((float)e->points[v].cached_dy_i * (int32_t) (e->x - e->points[v].x[e->points[v].n_current])) / (int32_t) e->points[v].cached_dx;
}
static float linear_get_float(pa_envelope *e, int v) {
pa_assert(e);
if (e->x < e->points[v].x[0])
return e->points[v].y.f[0];
for (;;) {
if (e->points[v].n_current+1 >= e->points[v].n_points)
return e->points[v].y.f[e->points[v].n_points-1];
if (e->x < e->points[v].x[e->points[v].n_current+1])
break;
e->points[v].n_current++;
e->points[v].cached_valid = FALSE;
}
if (!e->points[v].cached_valid) {
e->points[v].cached_dy_dx =
(e->points[v].y.f[e->points[v].n_current+1] - e->points[v].y.f[e->points[v].n_current]) /
((float) e->points[v].x[e->points[v].n_current+1] - (float) e->points[v].x[e->points[v].n_current]);
e->points[v].cached_valid = TRUE;
}
return e->points[v].y.f[e->points[v].n_current] + (float) (e->x - e->points[v].x[e->points[v].n_current]) * e->points[v].cached_dy_dx;
}
void pa_envelope_apply(pa_envelope *e, pa_memchunk *chunk) {
int v;
pa_assert(e);
pa_assert(chunk);
envelope_begin_read(e, &v);
if (e->points[v].n_points > 0) {
void *p;
size_t fs, n;
pa_memchunk_make_writable(chunk, 0);
p = (uint8_t*) pa_memblock_acquire(chunk->memblock) + chunk->index;
fs = pa_frame_size(&e->sample_spec);
n = chunk->length;
pa_log_debug("Envelop position %zu applying factor %d=%f, sample spec is %d, chunk's length is %zu, fs is %zu\n", e->x, linear_get_int(e, v), ((float) linear_get_int(e,v))/0x10000, e->sample_spec.format, n, fs);
switch (e->sample_spec.format) {
case PA_SAMPLE_U8: {
uint8_t *d, *s;
unsigned channel;
int32_t factor = linear_get_int(e, v);
s = (uint8_t*) p + n;
for (channel = 0, d = p; d < s; d++) {
int32_t t, hi, lo;
hi = factor >> 16;
lo = factor & 0xFFFF;
t = (int32_t) *d - 0x80;
t = ((t * lo) >> 16) + (t * hi);
t = PA_CLAMP_UNLIKELY(t, -0x80, 0x7F);
*d = (uint8_t) (t + 0x80);
if (PA_UNLIKELY(++channel >= e->sample_spec.channels)) {
channel = 0;
e->x += fs;
factor = linear_get_int(e, v);
}
}
break;
}
case PA_SAMPLE_ULAW: {
uint8_t *d, *s;
unsigned channel;
int32_t factor = linear_get_int(e, v);
s = (uint8_t*) p + n;
for (channel = 0, d = p; d < s; d++) {
int32_t t, hi, lo;
hi = factor >> 16;
lo = factor & 0xFFFF;
t = (int32_t) st_ulaw2linear16(*d);
t = ((t * lo) >> 16) + (t * hi);
t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
*d = (uint8_t) st_14linear2ulaw((int16_t) t >> 2);
if (PA_UNLIKELY(++channel >= e->sample_spec.channels)) {
channel = 0;
e->x += fs;
factor = linear_get_int(e, v);
}
}
break;
}
case PA_SAMPLE_ALAW: {
uint8_t *d, *s;
unsigned channel;
int32_t factor = linear_get_int(e, v);
s = (uint8_t*) p + n;
for (channel = 0, d = p; d < s; d++) {
int32_t t, hi, lo;
hi = factor >> 16;
lo = factor & 0xFFFF;
t = (int32_t) st_alaw2linear16(*d);
t = ((t * lo) >> 16) + (t * hi);
t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
*d = (uint8_t) st_13linear2alaw((int16_t) t >> 3);
if (PA_UNLIKELY(++channel >= e->sample_spec.channels)) {
channel = 0;
e->x += fs;
factor = linear_get_int(e, v);
}
}
break;
}
case PA_SAMPLE_S16NE: {
int16_t *d, *s;
unsigned channel;
int32_t factor = linear_get_int(e, v);
s = (int16_t*) p + n/sizeof(int16_t);
for (channel = 0, d = p; d < s; d++) {
int32_t t, hi, lo;
hi = factor >> 16;
lo = factor & 0xFFFF;
t = (int32_t)(*d);
t = ((t * lo) >> 16) + (t * hi);
t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
*d = (int16_t) t;
if (PA_UNLIKELY(++channel >= e->sample_spec.channels)) {
channel = 0;
e->x += fs;
factor = linear_get_int(e, v);
}
}
break;
}
case PA_SAMPLE_S16RE: {
int16_t *d, *s;
unsigned channel;
int32_t factor = linear_get_int(e, v);
s = (int16_t*) p + n/sizeof(int16_t);
for (channel = 0, d = p; d < s; d++) {
int32_t t, hi, lo;
hi = factor >> 16;
lo = factor & 0xFFFF;
t = (int32_t) PA_INT16_SWAP(*d);
t = ((t * lo) >> 16) + (t * hi);
t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
*d = PA_INT16_SWAP((int16_t) t);
if (PA_UNLIKELY(++channel >= e->sample_spec.channels)) {
channel = 0;
e->x += fs;
factor = linear_get_int(e, v);
}
}
break;
}
case PA_SAMPLE_S32NE: {
int32_t *d, *s;
unsigned channel;
int32_t factor = linear_get_int(e, v);
s = (int32_t*) p + n/sizeof(int32_t);
for (channel = 0, d = p; d < s; d++) {
int64_t t;
t = (int64_t)(*d);
t = (t * factor) >> 16;
t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
*d = (int32_t) t;
if (PA_UNLIKELY(++channel >= e->sample_spec.channels)) {
channel = 0;
e->x += fs;
factor = linear_get_int(e, v);
}
}
break;
}
case PA_SAMPLE_S32RE: {
int32_t *d, *s;
unsigned channel;
int32_t factor = linear_get_int(e, v);
s = (int32_t*) p + n/sizeof(int32_t);
for (channel = 0, d = p; d < s; d++) {
int64_t t;
t = (int64_t) PA_INT32_SWAP(*d);
t = (t * factor) >> 16;
t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
*d = PA_INT32_SWAP((int32_t) t);
if (PA_UNLIKELY(++channel >= e->sample_spec.channels)) {
channel = 0;
e->x += fs;
factor = linear_get_int(e, v);
}
}
break;
}
case PA_SAMPLE_FLOAT32NE: {
/*Seems the FLOAT32NE part of pa_volume_memchunk not right, do not reuse here*/
float *t;
for (t = p; n > 0; n -= fs) {
float factor = linear_get_float(e, v);
unsigned c;
e->x += fs;
for (c = 0; c < e->sample_spec.channels; c++, t++)
*t = *t * factor;
}
break;
}
case PA_SAMPLE_FLOAT32RE: {
/*Seems the FLOAT32RE part of pa_volume_memchunk not right, do not reuse here*/
float *t;
for (t = p; n > 0; n -= fs) {
float factor = linear_get_float(e, v);
unsigned c;
e->x += fs;
for (c = 0; c < e->sample_spec.channels; c++, t++) {
float r = PA_FLOAT32_SWAP(*t) * factor;
*t = PA_FLOAT32_SWAP(r);
}
}
break;
}
case PA_SAMPLE_S24NE: {
uint8_t *d, *s;
unsigned channel;
int32_t factor = linear_get_int(e, v);
s = (uint8_t*) p + n/3;
for (channel = 0, d = p; d < s; d++) {
int64_t t;
t = (int64_t)((int32_t) (PA_READ24NE(d) << 8));
t = (t * factor) >> 16;
t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
PA_WRITE24NE(d, ((uint32_t) (int32_t) t) >> 8);
if (PA_UNLIKELY(++channel >= e->sample_spec.channels)) {
channel = 0;
e->x += fs;
factor = linear_get_int(e, v);
}
}
break;
}
case PA_SAMPLE_S24RE: {
uint8_t *d, *s;
unsigned channel;
int32_t factor = linear_get_int(e, v);
s = (uint8_t*) p + n/3;
for (channel = 0, d = p; d < s; d++) {
int64_t t;
t = (int64_t)((int32_t) (PA_READ24RE(d) << 8));
t = (t * factor) >> 16;
t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
PA_WRITE24RE(d, ((uint32_t) (int32_t) t) >> 8);
if (PA_UNLIKELY(++channel >= e->sample_spec.channels)) {
channel = 0;
e->x += fs;
factor = linear_get_int(e, v);
}
}
break;
}
case PA_SAMPLE_S24_32NE: {
uint32_t *d, *s;
unsigned channel;
int32_t factor = linear_get_int(e, v);
s = (uint32_t*) p + n/sizeof(uint32_t);
for (channel = 0, d = p; d < s; d++) {
int64_t t;
t = (int64_t) ((int32_t) (*d << 8));
t = (t * factor) >> 16;
t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
*d = ((uint32_t) ((int32_t) t)) >> 8;
if (PA_UNLIKELY(++channel >= e->sample_spec.channels)) {
channel = 0;
e->x += fs;
factor = linear_get_int(e, v);
}
}
break;
}
case PA_SAMPLE_S24_32RE: {
uint32_t *d, *s;
unsigned channel;
int32_t factor = linear_get_int(e, v);
s = (uint32_t*) p + n/sizeof(uint32_t);
for (channel = 0, d = p; d < s; d++) {
int64_t t;
t = (int64_t) ((int32_t) (PA_UINT32_SWAP(*d) << 8));
t = (t * factor) >> 16;
t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
*d = PA_UINT32_SWAP(((uint32_t) ((int32_t) t)) >> 8);
if (PA_UNLIKELY(++channel >= e->sample_spec.channels)) {
channel = 0;
e->x += fs;
factor = linear_get_int(e, v);
}
}
break;
}
/* FIXME */
pa_assert_not_reached();
case PA_SAMPLE_MAX:
case PA_SAMPLE_INVALID:
pa_assert_not_reached();
}
pa_memblock_release(chunk->memblock);
} else {
/* When we have no envelope to apply we reset our origin */
e->x = 0;
}
envelope_commit_read(e, v);
}
void pa_envelope_rewind(pa_envelope *e, size_t n_bytes) {
int v;
pa_assert(e);
envelope_begin_read(e, &v);
if (e->x - n_bytes <= e->points[v].x[0])
e->x = e->points[v].x[0];
else
e->x -= n_bytes;
e->points[v].n_current = 0;
e->points[v].cached_valid = FALSE;
envelope_commit_read(e, v);
}
void pa_envelope_restart(pa_envelope* e) {
int v;
pa_assert(e);
envelope_begin_read(e, &v);
e->x = e->points[v].x[0];
envelope_commit_read(e, v);
}
pa_bool_t pa_envelope_is_finished(pa_envelope* e) {
int v;
pa_bool_t finished;
pa_assert(e);
envelope_begin_read(e, &v);
finished = (e->x >= e->points[v].x[e->points[v].n_points-1]);
envelope_commit_read(e, v);
return finished;
}
int32_t pa_envelope_length(pa_envelope *e) {
int v;
size_t size;
pa_assert(e);
envelope_begin_read(e, &v);
size = e->points[v].x[e->points[v].n_points-1] - e->points[v].x[0];
envelope_commit_read(e, v);
return size;
}

View file

@ -1,56 +0,0 @@
#ifndef foopulseenvelopehfoo
#define foopulseenvelopehfoo
/***
This file is part of PulseAudio.
Copyright 2007 Lennart Poettering
PulseAudio 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.1 of the
License, or (at your option) any later version.
PulseAudio 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <pulsecore/macro.h>
#include <pulsecore/memchunk.h>
#include <pulse/sample.h>
#define PA_ENVELOPE_POINTS_MAX 4U
typedef struct pa_envelope pa_envelope;
typedef struct pa_envelope_item pa_envelope_item;
typedef struct pa_envelope_def {
unsigned n_points;
pa_usec_t points_x[PA_ENVELOPE_POINTS_MAX];
struct {
int32_t i[PA_ENVELOPE_POINTS_MAX];
float f[PA_ENVELOPE_POINTS_MAX];
} points_y;
} pa_envelope_def;
pa_envelope *pa_envelope_new(const pa_sample_spec *ss);
void pa_envelope_free(pa_envelope *e);
pa_envelope_item *pa_envelope_add(pa_envelope *e, const pa_envelope_def *def);
pa_envelope_item *pa_envelope_replace(pa_envelope *e, pa_envelope_item *i, const pa_envelope_def *def);
void pa_envelope_remove(pa_envelope *e, pa_envelope_item *i);
void pa_envelope_apply(pa_envelope *e, pa_memchunk *chunk);
void pa_envelope_rewind(pa_envelope *e, size_t n_bytes);
void pa_envelope_restart(pa_envelope* e);
pa_bool_t pa_envelope_is_finished(pa_envelope* e);
int32_t pa_envelope_length(pa_envelope *e);
#endif

View file

@ -38,7 +38,6 @@
#include <pulsecore/play-memblockq.h>
#include <pulsecore/namereg.h>
#include <pulsecore/core-util.h>
#include <pulse/timeval.h>
#include "sink-input.h"
@ -49,11 +48,6 @@ PA_DEFINE_PUBLIC_CLASS(pa_sink_input, pa_msgobject);
static void sink_input_free(pa_object *o);
static void set_real_ratio(pa_sink_input *i, const pa_cvolume *v);
static void sink_input_set_ramping_info(pa_sink_input* i, pa_volume_t pre_virtual_volume, pa_volume_t target_virtual_volume, pa_usec_t t);
static void sink_input_set_ramping_info_for_mute(pa_sink_input* i, pa_bool_t mute, pa_usec_t t);
static void sink_input_volume_ramping(pa_sink_input* i, pa_memchunk* chunk);
static void sink_input_rewind_ramp_info(pa_sink_input *i, size_t nbytes);
static void sink_input_release_envelope(pa_sink_input *i);
static int check_passthrough_connection(pa_sink_input_flags_t flags, pa_sink *dest) {
@ -380,16 +374,6 @@ int pa_sink_input_new(
reset_callbacks(i);
i->userdata = NULL;
/* Set Ramping info */
i->thread_info.ramp_info.is_ramping = FALSE;
i->thread_info.ramp_info.envelope_dead = TRUE;
i->thread_info.ramp_info.envelope = NULL;
i->thread_info.ramp_info.item = NULL;
i->thread_info.ramp_info.envelope_dying = 0;
pa_atomic_store(&i->before_ramping_v, 0);
pa_atomic_store(&i->before_ramping_m, 0);
i->thread_info.state = i->state;
i->thread_info.attached = FALSE;
pa_atomic_store(&i->thread_info.drained, 1);
@ -580,12 +564,6 @@ static void sink_input_free(pa_object *o) {
* "half-moved" or are connected to sinks that have no asyncmsgq
* and are hence half-destructed themselves! */
if (i->thread_info.ramp_info.envelope) {
pa_log_debug ("Freeing envelope\n");
pa_envelope_free(i->thread_info.ramp_info.envelope);
i->thread_info.ramp_info.envelope = NULL;
}
if (i->thread_info.render_memblockq)
pa_memblockq_free(i->thread_info.render_memblockq);
@ -679,7 +657,6 @@ pa_usec_t pa_sink_input_get_latency(pa_sink_input *i, pa_usec_t *sink_latency) {
void pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa_memchunk *chunk, pa_cvolume *volume) {
pa_bool_t do_volume_adj_here, need_volume_factor_sink;
pa_bool_t volume_is_norm;
pa_bool_t ramping;
size_t block_size_max_sink, block_size_max_sink_input;
size_t ilength;
@ -724,7 +701,7 @@ void pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, p
* to adjust the volume *before* we resample. Otherwise we can do
* it after and leave it for the sink code */
do_volume_adj_here = !pa_channel_map_equal(&i->channel_map, &i->sink->channel_map) || i->thread_info.ramp_info.is_ramping;
do_volume_adj_here = !pa_channel_map_equal(&i->channel_map, &i->sink->channel_map);
volume_is_norm = pa_cvolume_is_norm(&i->thread_info.soft_volume) && !i->thread_info.muted;
need_volume_factor_sink = !pa_cvolume_is_norm(&i->volume_factor_sink);
@ -767,7 +744,7 @@ void pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, p
wchunk.length = block_size_max_sink_input;
/* It might be necessary to adjust the volume here */
if (do_volume_adj_here && !volume_is_norm && !i->thread_info.ramp_info.is_ramping) {
if (do_volume_adj_here && !volume_is_norm) {
pa_memchunk_make_writable(&wchunk, 0);
if (i->thread_info.muted) {
@ -833,23 +810,6 @@ void pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, p
if (chunk->length > block_size_max_sink)
chunk->length = block_size_max_sink;
ramping = i->thread_info.ramp_info.is_ramping;
if (ramping)
sink_input_volume_ramping(i, chunk);
if (!i->thread_info.ramp_info.envelope_dead) {
i->thread_info.ramp_info.envelope_dying += chunk->length;
pa_log_debug("Envelope dying is %d, chunk length is %zu, dead thresholder is %lu\n", i->thread_info.ramp_info.envelope_dying,
chunk->length,
i->sink->thread_info.max_rewind + pa_envelope_length(i->thread_info.ramp_info.envelope));
if (i->thread_info.ramp_info.envelope_dying >= (int32_t) (i->sink->thread_info.max_rewind + pa_envelope_length(i->thread_info.ramp_info.envelope))) {
pa_log_debug("RELEASE Envelop");
i->thread_info.ramp_info.envelope_dead = TRUE;
sink_input_release_envelope(i);
}
}
/* Let's see if we had to apply the volume adjustment ourselves,
* or if this can be done by the sink for us */
@ -894,7 +854,6 @@ void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sam
if (nbytes > 0 && !i->thread_info.dont_rewind_render) {
pa_log_debug("Have to rewind %lu bytes on render memblockq.", (unsigned long) nbytes);
pa_memblockq_rewind(i->thread_info.render_memblockq, nbytes);
sink_input_rewind_ramp_info(i, nbytes);
}
if (i->thread_info.rewrite_nbytes == (size_t) -1) {
@ -1057,6 +1016,64 @@ pa_usec_t pa_sink_input_get_requested_latency(pa_sink_input *i) {
return i->thread_info.requested_sink_latency;
}
/* Called from main context */
void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_bool_t save, pa_bool_t absolute) {
pa_cvolume v;
pa_sink_input_assert_ref(i);
pa_assert_ctl_context();
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
pa_assert(volume);
pa_assert(pa_cvolume_valid(volume));
pa_assert(volume->channels == 1 || pa_cvolume_compatible(volume, &i->sample_spec));
pa_assert(pa_sink_input_is_volume_writable(i));
if ((i->sink->flags & PA_SINK_FLAT_VOLUME) && !absolute) {
v = i->sink->reference_volume;
pa_cvolume_remap(&v, &i->sink->channel_map, &i->channel_map);
if (pa_cvolume_compatible(volume, &i->sample_spec))
volume = pa_sw_cvolume_multiply(&v, &v, volume);
else
volume = pa_sw_cvolume_multiply_scalar(&v, &v, pa_cvolume_max(volume));
} else {
if (!pa_cvolume_compatible(volume, &i->sample_spec)) {
v = i->volume;
volume = pa_cvolume_scale(&v, pa_cvolume_max(volume));
}
}
if (pa_cvolume_equal(volume, &i->volume)) {
i->save_volume = i->save_volume || save;
return;
}
i->volume = *volume;
i->save_volume = save;
if (i->sink->flags & PA_SINK_FLAT_VOLUME) {
/* We are in flat volume mode, so let's update all sink input
* volumes and update the flat volume of the sink */
pa_sink_set_volume(i->sink, NULL, TRUE, save);
} else {
/* OK, we are in normal volume mode. The volume only affects
* ourselves */
set_real_ratio(i, volume);
/* Copy the new soft_volume to the thread_info struct */
pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_SOFT_VOLUME, NULL, 0, NULL) == 0);
}
/* The volume changed, let's tell people so */
if (i->volume_changed)
i->volume_changed(i);
/* The virtual volume changed, let's tell people so */
pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
}
/* Called from main context */
static void set_real_ratio(pa_sink_input *i, const pa_cvolume *v) {
pa_sink_input_assert_ref(i);
@ -1100,12 +1117,6 @@ pa_bool_t pa_sink_input_is_volume_writable(pa_sink_input *i) {
return TRUE;
}
/* Called from main context */
void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_bool_t save, pa_bool_t absolute) {
/* test ramping -> return pa_sink_input_set_volume_with_ramping(i, volume, save, absolute, 2000 * PA_USEC_PER_MSEC); */
return pa_sink_input_set_volume_with_ramping(i, volume, save, absolute, 0);
}
/* Called from main context */
pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i, pa_cvolume *volume, pa_bool_t absolute) {
pa_sink_input_assert_ref(i);
@ -1123,8 +1134,25 @@ pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i, pa_cvolume *volume, pa_bo
/* Called from main context */
void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute, pa_bool_t save) {
/* test ramping -> return pa_sink_input_set_mute_with_ramping(i, mute, save, 2000 * PA_USEC_PER_MSEC); */
return pa_sink_input_set_mute_with_ramping(i, mute, save, 0);
pa_sink_input_assert_ref(i);
pa_assert_ctl_context();
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
if (!i->muted == !mute) {
i->save_muted = i->save_muted || mute;
return;
}
i->muted = mute;
i->save_muted = save;
pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_SOFT_MUTE, NULL, 0, NULL) == 0);
/* The mute status changed, let's tell people so */
if (i->mute_changed)
i->mute_changed(i);
pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
}
/* Called from main context */
@ -1640,22 +1668,14 @@ int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t
switch (code) {
case PA_SINK_INPUT_MESSAGE_SET_SOFT_VOLUME:
if (pa_atomic_load(&i->before_ramping_v))
i->thread_info.future_soft_volume = i->soft_volume;
if (!pa_cvolume_equal(&i->thread_info.soft_volume, &i->soft_volume)) {
if (!pa_atomic_load(&i->before_ramping_v))
i->thread_info.soft_volume = i->soft_volume;
pa_sink_input_request_rewind(i, 0, TRUE, FALSE, FALSE);
}
return 0;
case PA_SINK_INPUT_MESSAGE_SET_SOFT_MUTE:
if (pa_atomic_load(&i->before_ramping_m))
i->thread_info.future_muted = i->muted;
if (i->thread_info.muted != i->muted) {
if (!pa_atomic_load(&i->before_ramping_m))
i->thread_info.muted = i->muted;
pa_sink_input_request_rewind(i, 0, TRUE, FALSE, FALSE);
}
@ -1704,26 +1724,6 @@ int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t
*r = i->thread_info.requested_sink_latency;
return 0;
}
case PA_SINK_INPUT_MESSAGE_SET_ENVELOPE: {
if (!i->thread_info.ramp_info.envelope)
i->thread_info.ramp_info.envelope = pa_envelope_new(&i->sink->sample_spec);
if (i->thread_info.ramp_info.envelope && i->thread_info.ramp_info.item) {
pa_envelope_remove(i->thread_info.ramp_info.envelope, i->thread_info.ramp_info.item);
i->thread_info.ramp_info.item = NULL;
}
i->thread_info.ramp_info.item = pa_envelope_add(i->thread_info.ramp_info.envelope, &i->using_def);
i->thread_info.ramp_info.is_ramping = TRUE;
i->thread_info.ramp_info.envelope_dead = FALSE;
i->thread_info.ramp_info.envelope_dying = 0;
if (i->thread_info.ramp_info.envelope)
pa_envelope_restart(i->thread_info.ramp_info.envelope);
return 0;
}
}
return -PA_ERR_NOTIMPLEMENTED;
@ -1886,239 +1886,3 @@ finish:
if (pl)
pa_proplist_free(pl);
}
/* Called from IO context */
static void sink_input_volume_ramping(pa_sink_input* i, pa_memchunk* chunk) {
pa_assert(i);
pa_assert(chunk);
pa_assert(chunk->memblock);
pa_assert(i->thread_info.ramp_info.is_ramping);
/* Volume is adjusted with ramping effect here */
pa_envelope_apply(i->thread_info.ramp_info.envelope, chunk);
if (pa_envelope_is_finished(i->thread_info.ramp_info.envelope)) {
i->thread_info.ramp_info.is_ramping = FALSE;
if (pa_atomic_load(&i->before_ramping_v)) {
i->thread_info.soft_volume = i->thread_info.future_soft_volume;
pa_atomic_store(&i->before_ramping_v, 0);
}
else if (pa_atomic_load(&i->before_ramping_m)) {
i->thread_info.muted = i->thread_info.future_muted;
pa_atomic_store(&i->before_ramping_m, 0);
}
}
}
/*
* Called from main context
* This function should be called inside pa_sink_input_set_volume_with_ramping
* should be called after soft_volume of sink_input and sink are all adjusted
*/
static void sink_input_set_ramping_info(pa_sink_input* i, pa_volume_t pre_virtual_volume, pa_volume_t target_virtual_volume, pa_usec_t t) {
int32_t target_abs_vol, target_apply_vol, pre_apply_vol;
pa_assert(i);
pa_log_debug("Sink input's soft volume is %d= %f ", pa_cvolume_avg(&i->soft_volume), pa_sw_volume_to_linear(pa_cvolume_avg(&i->soft_volume)));
/* Calculation formula are target_abs_vol := i->soft_volume
* target_apply_vol := lrint(pa_sw_volume_to_linear(target_abs_vol) * 0x10000)
* pre_apply_vol := ( previous_virtual_volume / target_virtual_volume ) * target_apply_vol
*
* Will do volume adjustment inside pa_sink_input_peek
*/
target_abs_vol = pa_cvolume_avg(&i->soft_volume);
target_apply_vol = (int32_t) lrint(pa_sw_volume_to_linear(target_abs_vol) * 0x10000);
pre_apply_vol = (int32_t) ((pa_sw_volume_to_linear(pre_virtual_volume) / pa_sw_volume_to_linear(target_virtual_volume)) * target_apply_vol);
i->using_def.n_points = 2;
i->using_def.points_x[0] = 0;
i->using_def.points_x[1] = t;
i->using_def.points_y.i[0] = pre_apply_vol;
i->using_def.points_y.i[1] = target_apply_vol;
i->using_def.points_y.f[0] = ((float) i->using_def.points_y.i[0]) /0x10000;
i->using_def.points_y.f[1] = ((float) i->using_def.points_y.i[1]) /0x10000;
pa_log_debug("Volume Ramping: Point 1 is %d=%f, Point 2 is %d=%f\n", i->using_def.points_y.i[0], i->using_def.points_y.f[0],
i->using_def.points_y.i[1], i->using_def.points_y.f[1]);
pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_ENVELOPE, NULL, 0, NULL) == 0);
}
/* Called from main context */
static void sink_input_set_ramping_info_for_mute(pa_sink_input* i, pa_bool_t mute, pa_usec_t t) {
int32_t cur_vol;
pa_assert(i);
i->using_def.n_points = 2;
i->using_def.points_x[0] = 0;
i->using_def.points_x[1] = t;
cur_vol = (int32_t) lrint( pa_sw_volume_to_linear(pa_cvolume_avg(&i->soft_volume)) * 0x10000);
if (mute) {
i->using_def.points_y.i[0] = cur_vol;
i->using_def.points_y.i[1] = 0;
} else {
i->using_def.points_y.i[0] = 0;
i->using_def.points_y.i[1] = cur_vol;
}
i->using_def.points_y.f[0] = ((float) i->using_def.points_y.i[0]) /0x10000;
i->using_def.points_y.f[1] = ((float) i->using_def.points_y.i[1]) /0x10000;
pa_log_debug("Mute Ramping: Point 1 is %d=%f, Point 2 is %d=%f\n", i->using_def.points_y.i[0], i->using_def.points_y.f[0],
i->using_def.points_y.i[1], i->using_def.points_y.f[1]);
pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_ENVELOPE, NULL, 0, NULL) == 0);
}
/* Called from IO context */
static void sink_input_release_envelope(pa_sink_input *i) {
pa_assert(i);
pa_assert(!i->thread_info.ramp_info.is_ramping);
pa_assert(i->thread_info.ramp_info.envelope_dead);
pa_envelope_free(i->thread_info.ramp_info.envelope);
i->thread_info.ramp_info.envelope = NULL;
i->thread_info.ramp_info.item = NULL;
}
/* Called from IO context */
static void sink_input_rewind_ramp_info(pa_sink_input *i, size_t nbytes) {
pa_assert(i);
if (!i->thread_info.ramp_info.envelope_dead) {
int32_t envelope_length;
pa_assert(i->thread_info.ramp_info.envelope);
envelope_length = pa_envelope_length(i->thread_info.ramp_info.envelope);
if (i->thread_info.ramp_info.envelope_dying > envelope_length) {
if ((int32_t) (i->thread_info.ramp_info.envelope_dying - nbytes) < envelope_length) {
pa_log_debug("Envelope Become Alive");
pa_envelope_rewind(i->thread_info.ramp_info.envelope, envelope_length - (i->thread_info.ramp_info.envelope_dying - nbytes));
i->thread_info.ramp_info.is_ramping = TRUE;
}
} else if (i->thread_info.ramp_info.envelope_dying < envelope_length) {
if ((i->thread_info.ramp_info.envelope_dying - (ssize_t) nbytes) <= 0) {
pa_log_debug("Envelope Restart");
pa_envelope_restart(i->thread_info.ramp_info.envelope);
}
else {
pa_log_debug("Envelope Simple Rewind");
pa_envelope_rewind(i->thread_info.ramp_info.envelope, nbytes);
}
}
i->thread_info.ramp_info.envelope_dying -= nbytes;
if (i->thread_info.ramp_info.envelope_dying <= 0)
i->thread_info.ramp_info.envelope_dying = 0;
}
}
void pa_sink_input_set_volume_with_ramping(pa_sink_input *i, const pa_cvolume *volume, pa_bool_t save, pa_bool_t absolute, pa_usec_t t){
pa_cvolume v;
pa_volume_t previous_virtual_volume, target_virtual_volume;
pa_sink_input_assert_ref(i);
pa_assert_ctl_context();
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
pa_assert(volume);
pa_assert(pa_cvolume_valid(volume));
pa_assert(volume->channels == 1 || pa_cvolume_compatible(volume, &i->sample_spec));
pa_assert(pa_sink_input_is_volume_writable(i));
if (!absolute && pa_sink_flat_volume_enabled(i->sink)) {
v = i->sink->reference_volume;
pa_cvolume_remap(&v, &i->sink->channel_map, &i->channel_map);
if (pa_cvolume_compatible(volume, &i->sample_spec))
volume = pa_sw_cvolume_multiply(&v, &v, volume);
else
volume = pa_sw_cvolume_multiply_scalar(&v, &v, pa_cvolume_max(volume));
} else {
if (!pa_cvolume_compatible(volume, &i->sample_spec)) {
v = i->volume;
volume = pa_cvolume_scale(&v, pa_cvolume_max(volume));
}
}
if (pa_cvolume_equal(volume, &i->volume)) {
i->save_volume = i->save_volume || save;
return;
}
previous_virtual_volume = pa_cvolume_avg(&i->volume);
target_virtual_volume = pa_cvolume_avg(volume);
if (t > 0 && target_virtual_volume > 0)
pa_log_debug("SetVolumeWithRamping: Virtual Volume From %u=%f to %u=%f\n", previous_virtual_volume, pa_sw_volume_to_linear(previous_virtual_volume),
target_virtual_volume, pa_sw_volume_to_linear(target_virtual_volume));
i->volume = *volume;
i->save_volume = save;
/* Set this flag before the following code modify i->thread_info.soft_volume */
if (t > 0 && target_virtual_volume > 0)
pa_atomic_store(&i->before_ramping_v, 1);
if (pa_sink_flat_volume_enabled(i->sink)) {
/* We are in flat volume mode, so let's update all sink input
* volumes and update the flat volume of the sink */
pa_sink_set_volume(i->sink, NULL, TRUE, save);
} else {
/* OK, we are in normal volume mode. The volume only affects
* ourselves */
i->reference_ratio = *volume;
set_real_ratio(i, volume);
/* Copy the new soft_volume to the thread_info struct */
pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_SOFT_VOLUME, NULL, 0, NULL) == 0);
}
if (t > 0 && target_virtual_volume > 0)
sink_input_set_ramping_info(i, previous_virtual_volume, target_virtual_volume, t);
/* The volume changed, let's tell people so */
if (i->volume_changed)
i->volume_changed(i);
/* The virtual volume changed, let's tell people so */
pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
}
void pa_sink_input_set_mute_with_ramping(pa_sink_input *i, pa_bool_t mute, pa_bool_t save, pa_usec_t t){
pa_sink_input_assert_ref(i);
pa_assert_ctl_context();
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
if (!i->muted == !mute) {
i->save_muted = i->save_muted || mute;
return;
}
i->muted = mute;
i->save_muted = save;
/* Set this flag before the following code modify i->thread_info.muted, otherwise distortion will be heard */
if (t > 0)
pa_atomic_store(&i->before_ramping_m, 1);
pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_SOFT_MUTE, NULL, 0, NULL) == 0);
if (t > 0)
sink_input_set_ramping_info_for_mute(i, mute, t);
/* The mute status changed, let's tell people so */
if (i->mute_changed)
i->mute_changed(i);
pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
}

View file

@ -35,7 +35,6 @@ typedef struct pa_sink_input pa_sink_input;
#include <pulsecore/client.h>
#include <pulsecore/sink.h>
#include <pulsecore/core.h>
#include <pulsecore/envelope.h>
typedef enum pa_sink_input_state {
PA_SINK_INPUT_INIT, /*< The stream is not active yet, because pa_sink_input_put() has not been called yet */
@ -235,23 +234,8 @@ struct pa_sink_input {
pa_usec_t requested_sink_latency;
pa_hashmap *direct_outputs;
struct {
pa_bool_t is_ramping:1;
pa_bool_t envelope_dead:1;
int32_t envelope_dying; /* Increasing while envelop is not dead. Reduce it while process_rewind. */
pa_envelope *envelope;
pa_envelope_item *item;
} ramp_info;
pa_cvolume future_soft_volume;
pa_bool_t future_muted;
} thread_info;
pa_atomic_t before_ramping_v; /* Indicates future volume */
pa_atomic_t before_ramping_m; /* Indicates future mute */
pa_envelope_def using_def;
void *userdata;
};
@ -266,7 +250,6 @@ enum {
PA_SINK_INPUT_MESSAGE_SET_STATE,
PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY,
PA_SINK_INPUT_MESSAGE_GET_REQUESTED_LATENCY,
PA_SINK_INPUT_MESSAGE_SET_ENVELOPE,
PA_SINK_INPUT_MESSAGE_MAX
};
@ -407,8 +390,4 @@ pa_memchunk* pa_sink_input_get_silence(pa_sink_input *i, pa_memchunk *ret);
#define pa_sink_input_assert_io_context(s) \
pa_assert(pa_thread_mq_get() || !PA_SINK_INPUT_IS_LINKED((s)->state))
/* Volume ramping*/
void pa_sink_input_set_volume_with_ramping(pa_sink_input *i, const pa_cvolume *volume, pa_bool_t save, pa_bool_t absolute, pa_usec_t t);
void pa_sink_input_set_mute_with_ramping(pa_sink_input *i, pa_bool_t mute, pa_bool_t save, pa_usec_t t);
#endif

View file

@ -2056,15 +2056,10 @@ static void sync_input_volumes_within_thread(pa_sink *s) {
pa_sink_assert_io_context(s);
PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state) {
if (pa_atomic_load(&i->before_ramping_v))
i->thread_info.future_soft_volume = i->soft_volume;
if (pa_cvolume_equal(&i->thread_info.soft_volume, &i->soft_volume))
continue;
if (!pa_atomic_load(&i->before_ramping_v))
i->thread_info.soft_volume = i->soft_volume;
pa_sink_input_request_rewind(i, 0, TRUE, FALSE, FALSE);
}
}

View file

@ -1,243 +0,0 @@
/***
This file is part of PulseAudio.
PulseAudio 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.1 of the License,
or (at your option) any later version.
PulseAudio 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 PulseAudio; 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 <stdio.h>
#include <stdlib.h>
#include <pulse/sample.h>
#include <pulse/volume.h>
#include <pulse/timeval.h>
#include <pulsecore/envelope.h>
#include <pulsecore/macro.h>
#include <pulsecore/endianmacros.h>
#include <pulsecore/memblock.h>
#include <pulsecore/sample-util.h>
const pa_envelope_def ramp_down = {
.n_points = 2,
.points_x = { 100*PA_USEC_PER_MSEC, 300*PA_USEC_PER_MSEC },
.points_y = {
.f = { 1.0f, 0.2f },
.i = { 0x10000, 0x10000/5 }
}
};
const pa_envelope_def ramp_up = {
.n_points = 2,
.points_x = { 100*PA_USEC_PER_MSEC, 300*PA_USEC_PER_MSEC },
.points_y = {
.f = { 0.2f, 1.0f },
.i = { 0x10000/5, 0x10000 }
}
};
const pa_envelope_def ramp_down2 = {
.n_points = 2,
.points_x = { 50*PA_USEC_PER_MSEC, 900*PA_USEC_PER_MSEC },
.points_y = {
.f = { 0.8f, 0.7f },
.i = { 0x10000*4/5, 0x10000*7/10 }
}
};
const pa_envelope_def ramp_up2 = {
.n_points = 2,
.points_x = { 50*PA_USEC_PER_MSEC, 900*PA_USEC_PER_MSEC },
.points_y = {
.f = { 0.7f, 0.9f },
.i = { 0x10000*7/10, 0x10000*9/10 }
}
};
static void dump_block(const pa_sample_spec *ss, const pa_memchunk *chunk) {
void *d;
unsigned i;
static unsigned j = 0;
d = pa_memblock_acquire(chunk->memblock);
switch (ss->format) {
case PA_SAMPLE_U8:
case PA_SAMPLE_ULAW:
case PA_SAMPLE_ALAW: {
uint8_t *u = d;
for (i = 0; i < chunk->length / pa_frame_size(ss); i++)
printf("0x%02x ", *(u++));
break;
}
case PA_SAMPLE_S16NE:
case PA_SAMPLE_S16RE: {
int16_t *u = d;
for (i = 0; i < chunk->length / pa_frame_size(ss); i++)
printf("%i\t%i\n", j++, *(u++));
break;
}
case PA_SAMPLE_S32NE:
case PA_SAMPLE_S32RE: {
int32_t *u = d;
for (i = 0; i < chunk->length / pa_frame_size(ss); i++)
printf("%i\t%i\n", j++, *(u++));
break;
}
case PA_SAMPLE_FLOAT32NE:
case PA_SAMPLE_FLOAT32RE: {
float *u = d;
for (i = 0; i < chunk->length / pa_frame_size(ss); i++) {
printf("%i\t%1.3g\n", j++, PA_MAYBE_FLOAT32_SWAP(ss->format == PA_SAMPLE_FLOAT32RE, *u));
u++;
}
break;
}
default:
pa_assert_not_reached();
}
printf("\n");
pa_memblock_release(chunk->memblock);
}
static pa_memblock * generate_block(pa_mempool *pool, const pa_sample_spec *ss) {
pa_memblock *block;
void *d;
unsigned n_samples;
block = pa_memblock_new(pool, pa_bytes_per_second(ss));
n_samples = (unsigned) (pa_memblock_get_length(block) / pa_sample_size(ss));
d = pa_memblock_acquire(block);
switch (ss->format) {
case PA_SAMPLE_S16NE:
case PA_SAMPLE_S16RE: {
int16_t *i;
for (i = d; n_samples > 0; n_samples--, i++)
*i = 0x7FFF;
break;
}
case PA_SAMPLE_S32NE:
case PA_SAMPLE_S32RE: {
int32_t *i;
for (i = d; n_samples > 0; n_samples--, i++)
*i = 0x7FFFFFFF;
break;
}
case PA_SAMPLE_FLOAT32RE:
case PA_SAMPLE_FLOAT32NE: {
float *f;
for (f = d; n_samples > 0; n_samples--, f++)
*f = PA_MAYBE_FLOAT32_SWAP(ss->format == PA_SAMPLE_FLOAT32RE, 1.0f);
break;
}
default:
pa_assert_not_reached();
}
pa_memblock_release(block);
return block;
}
int main(int argc, char *argv[]) {
pa_mempool *pool;
pa_memblock *block;
pa_memchunk chunk;
pa_envelope *envelope;
pa_envelope_item *item1, *item2;
const pa_sample_spec ss = {
.format = PA_SAMPLE_S16NE,
.channels = 1,
.rate = 200
};
const pa_cvolume v = {
.channels = 1,
.values = { PA_VOLUME_NORM, PA_VOLUME_NORM/2 }
};
pa_log_set_level(PA_LOG_DEBUG);
pa_assert_se(pool = pa_mempool_new(FALSE, 0));
pa_assert_se(envelope = pa_envelope_new(&ss));
block = generate_block(pool, &ss);
chunk.memblock = pa_memblock_ref(block);
chunk.length = pa_memblock_get_length(block);
chunk.index = 0;
pa_volume_memchunk(&chunk, &ss, &v);
item1 = pa_envelope_add(envelope, &ramp_down);
item2 = pa_envelope_add(envelope, &ramp_down2);
pa_envelope_apply(envelope, &chunk);
dump_block(&ss, &chunk);
pa_memblock_unref(chunk.memblock);
chunk.memblock = pa_memblock_ref(block);
chunk.length = pa_memblock_get_length(block);
chunk.index = 0;
item1 = pa_envelope_replace(envelope, item1, &ramp_up);
item2 = pa_envelope_replace(envelope, item2, &ramp_up2);
pa_envelope_apply(envelope, &chunk);
dump_block(&ss, &chunk);
pa_memblock_unref(chunk.memblock);
pa_envelope_remove(envelope, item1);
pa_envelope_remove(envelope, item2);
pa_envelope_free(envelope);
pa_memblock_unref(block);
pa_mempool_free(pool);
return 0;
}