Merge branch 'master' of git://0pointer.de/pulseaudio into dbus-work

Conflicts:
	src/Makefile.am
This commit is contained in:
Tanu Kaskinen 2009-08-24 14:43:11 +03:00
commit 2f3fc2f1d6
100 changed files with 4986 additions and 2224 deletions

1
src/.gitignore vendored
View file

@ -1,3 +1,4 @@
usergroup-test
sigbus-test
TAGS
alsa-time-test

View file

@ -74,6 +74,7 @@ AM_CFLAGS = \
$(LIBSAMPLERATE_CFLAGS) \
$(LIBSNDFILE_CFLAGS) \
$(LIBSPEEX_CFLAGS) \
-DPA_BUILDDIR=\"$(abs_builddir)\" \
-DPA_DLSEARCHPATH=\"$(modlibexecdir)\" \
-DPA_DEFAULT_CONFIG_DIR=\"$(PA_DEFAULT_CONFIG_DIR)\" \
-DPA_BINARY=\"$(PA_BINARY)\" \
@ -83,8 +84,8 @@ AM_CFLAGS = \
-DAO_REQUIRE_CAS \
-DPULSE_LOCALEDIR=\"$(pulselocaledir)\" \
-DPA_MACHINE_ID=\"$(localstatedir)/lib/dbus/machine-id\" \
-DPA_ALSA_PATHS_DIR=\"$(alsapathsdir)\" \
-DPA_ALSA_PROFILE_SETS_DIR=\"$(alsaprofilesetsdir)\"
-DPA_ALSA_PATHS_DIR=\"$(alsapathsdir)\" \
-DPA_ALSA_PROFILE_SETS_DIR=\"$(alsaprofilesetsdir)\"
AM_LIBADD = $(PTHREAD_LIBS) $(INTLLIBS)
AM_LDADD = $(PTHREAD_LIBS) $(INTLLIBS)
@ -280,7 +281,8 @@ TESTS = \
proplist-test \
lock-autospawn-test \
prioq-test \
sigbus-test
sigbus-test \
usergroup-test
TESTS_BINARIES = \
mainloop-test \
@ -318,7 +320,8 @@ TESTS_BINARIES = \
stripnul \
lock-autospawn-test \
prioq-test \
sigbus-test
sigbus-test \
usergroup-test
if HAVE_SIGXCPU
#TESTS += \
@ -557,6 +560,11 @@ alsa_time_test_LDADD = $(AM_LDADD)
alsa_time_test_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS)
alsa_time_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(ASOUNDLIB_LIBS)
usergroup_test_SOURCES = tests/usergroup-test.c
usergroup_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la
usergroup_test_CFLAGS = $(AM_CFLAGS)
usergroup_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
###################################
# Common library #
###################################
@ -621,6 +629,7 @@ libpulsecommon_@PA_MAJORMINORMICRO@_la_SOURCES = \
pulsecore/tagstruct.c pulsecore/tagstruct.h \
pulsecore/time-smoother.c pulsecore/time-smoother.h \
pulsecore/tokenizer.c pulsecore/tokenizer.h \
pulsecore/usergroup.c pulsecore/usergroup.h \
pulsecore/sndfile-util.c pulsecore/sndfile-util.h \
pulsecore/winsock.h
@ -823,10 +832,14 @@ libpulsecore_@PA_MAJORMINORMICRO@_la_SOURCES = \
pulsecore/object.c pulsecore/object.h \
pulsecore/play-memblockq.c pulsecore/play-memblockq.h \
pulsecore/play-memchunk.c pulsecore/play-memchunk.h \
pulsecore/protocol-dbus.h \
pulsecore/remap.c pulsecore/remap.h \
pulsecore/remap_mmx.c \
pulsecore/resampler.c pulsecore/resampler.h \
pulsecore/rtpoll.c pulsecore/rtpoll.h \
pulsecore/sample-util.c pulsecore/sample-util.h \
pulsecore/cpu-arm.c pulsecore/cpu-x86.c \
pulsecore/svolume_c.c pulsecore/svolume_arm.c\
pulsecore/svolume_mmx.c pulsecore/svolume_sse.c \
pulsecore/sconv-s16be.c pulsecore/sconv-s16be.h \
pulsecore/sconv-s16le.c pulsecore/sconv-s16le.h \
pulsecore/sconv.c pulsecore/sconv.h \

View file

@ -385,11 +385,6 @@ int pa_cmdline_parse(pa_daemon_conf *conf, int argc, char *const argv [], int *d
pa_xfree(conf->script_commands);
conf->script_commands = pa_strbuf_tostring_free(buf);
if (!conf->script_commands) {
pa_xfree(conf->script_commands);
conf->script_commands = NULL;
}
*d = optind;
return 0;

View file

@ -136,9 +136,25 @@ static const pa_daemon_conf default_conf = {
};
pa_daemon_conf* pa_daemon_conf_new(void) {
pa_daemon_conf *c = pa_xnewdup(pa_daemon_conf, &default_conf, 1);
pa_daemon_conf *c;
c = pa_xnewdup(pa_daemon_conf, &default_conf, 1);
#if defined(__linux__) && !defined(__OPTIMIZE__)
/* We abuse __OPTIMIZE__ as a check whether we are a debug build
* or not. If we are and are run from the build tree then we
* override the search path to point to our build tree */
if (pa_run_from_build_tree()) {
pa_log_notice("Detected that we are run from the build tree, fixing search path.");
c->dl_search_path = pa_xstrdup(PA_BUILDDIR "/.libs/");
} else
#endif
c->dl_search_path = pa_xstrdup(PA_DLSEARCHPATH);
c->dl_search_path = pa_xstrdup(PA_DLSEARCHPATH);
return c;
}

View file

@ -39,8 +39,6 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <liboil/liboil.h>
#ifdef HAVE_SYS_MMAN_H
#include <sys/mman.h>
#endif
@ -95,6 +93,8 @@
#ifdef HAVE_DBUS
#include <pulsecore/dbus-shared.h>
#endif
#include <pulsecore/cpu-arm.h>
#include <pulsecore/cpu-x86.h>
#include "cmdline.h"
#include "cpulimit.h"
@ -804,6 +804,8 @@ int main(int argc, char *argv[]) {
pa_log_info(_("Using state directory %s."), s);
pa_xfree(s);
pa_log_info(_("Using modules directory %s."), conf->dl_search_path);
pa_log_info(_("Running in system mode: %s"), pa_yes_no(pa_in_system_mode()));
if (pa_in_system_mode())
@ -851,6 +853,9 @@ int main(int argc, char *argv[]) {
pa_memtrap_install();
pa_cpu_init_x86();
pa_cpu_init_arm();
pa_assert_se(mainloop = pa_mainloop_new());
if (!(c = pa_core_new(pa_mainloop_get_api(mainloop), !conf->disable_shm, conf->shm_size))) {
@ -893,8 +898,6 @@ int main(int argc, char *argv[]) {
win32_timer = pa_mainloop_get_api(mainloop)->rtclock_time_new(pa_mainloop_get_api(mainloop), pa_gettimeofday(&win32_tv), message_cb, NULL);
#endif
oil_init();
if (!conf->no_cpu_limit)
pa_assert_se(pa_cpu_limit_init(pa_mainloop_get_api(mainloop)) == 0);

View file

@ -929,7 +929,7 @@ static int element_zero_volume(pa_alsa_element *e, snd_mixer_t *m) {
int pa_alsa_path_select(pa_alsa_path *p, snd_mixer_t *m) {
pa_alsa_element *e;
int r;
int r = 0;
pa_assert(m);
pa_assert(p);
@ -1849,7 +1849,12 @@ pa_alsa_path* pa_alsa_path_new(const char *fname, pa_alsa_direction_t direction)
items[1].data = &p->description;
items[2].data = &p->name;
fn = pa_maybe_prefix_path(fname, PA_ALSA_PATHS_DIR);
fn = pa_maybe_prefix_path(fname,
#if defined(__linux__) && !defined(__OPTIMIZE__)
pa_run_from_build_tree() ? PA_BUILDDIR "/modules/alsa/mixer/paths/" :
#endif
PA_ALSA_PATHS_DIR);
r = pa_config_parse(fn, NULL, items, p);
pa_xfree(fn);
@ -3110,7 +3115,12 @@ pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel
if (!fname)
fname = "default.conf";
fn = pa_maybe_prefix_path(fname, PA_ALSA_PROFILE_SETS_DIR);
fn = pa_maybe_prefix_path(fname,
#if defined(__linux__) && !defined(__OPTIMIZE__)
pa_run_from_build_tree() ? PA_BUILDDIR "/modules/alsa/mixer/profile-sets/" :
#endif
PA_ALSA_PROFILE_SETS_DIR);
r = pa_config_parse(fn, NULL, items, ps);
pa_xfree(fn);

View file

@ -62,11 +62,21 @@
/* #define DEBUG_TIMING */
#define DEFAULT_DEVICE "default"
#define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC) /* 2s -- Overall buffer size */
#define DEFAULT_TSCHED_WATERMARK_USEC (20*PA_USEC_PER_MSEC) /* 20ms -- Fill up when only this much is left in the buffer */
#define TSCHED_WATERMARK_STEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms -- On underrun, increase watermark by this */
#define TSCHED_MIN_SLEEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms -- Sleep at least 10ms on each iteration */
#define TSCHED_MIN_WAKEUP_USEC (4*PA_USEC_PER_MSEC) /* 4ms -- Wakeup at least this long before the buffer runs empty*/
#define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC) /* 2s -- Overall buffer size */
#define DEFAULT_TSCHED_WATERMARK_USEC (20*PA_USEC_PER_MSEC) /* 20ms -- Fill up when only this much is left in the buffer */
#define TSCHED_WATERMARK_INC_STEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms -- On underrun, increase watermark by this */
#define TSCHED_WATERMARK_DEC_STEP_USEC (5*PA_USEC_PER_MSEC) /* 5ms -- When everything's great, decrease watermark by this */
#define TSCHED_WATERMARK_VERIFY_AFTER_USEC (20*PA_USEC_PER_SEC) /* 20s -- How long after a drop out recheck if things are good now */
#define TSCHED_WATERMARK_INC_THRESHOLD_USEC (1*PA_USEC_PER_MSEC) /* 3ms -- If the buffer level ever below this theshold, increase the watermark */
#define TSCHED_WATERMARK_DEC_THRESHOLD_USEC (100*PA_USEC_PER_MSEC) /* 100ms -- If the buffer level didn't drop below this theshold in the verification time, decrease the watermark */
#define TSCHED_MIN_SLEEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms -- Sleep at least 10ms on each iteration */
#define TSCHED_MIN_WAKEUP_USEC (4*PA_USEC_PER_MSEC) /* 4ms -- Wakeup at least this long before the buffer runs empty*/
#define SMOOTHER_MIN_INTERVAL (2*PA_USEC_PER_MSEC) /* 2ms -- min smoother update interval */
#define SMOOTHER_MAX_INTERVAL (200*PA_USEC_PER_MSEC) /* 200ms -- max smoother update inteval */
#define VOLUME_ACCURACY (PA_VOLUME_NORM/100) /* don't require volume adjustments to be perfectly correct. don't necessarily extend granularity in software unless the differences get greater than this level */
@ -96,7 +106,12 @@ struct userdata {
hwbuf_unused,
min_sleep,
min_wakeup,
watermark_step;
watermark_inc_step,
watermark_dec_step,
watermark_inc_threshold,
watermark_dec_threshold;
pa_usec_t watermark_dec_not_before;
unsigned nfragments;
pa_memchunk memchunk;
@ -115,6 +130,8 @@ struct userdata {
pa_smoother *smoother;
uint64_t write_count;
uint64_t since_start;
pa_usec_t smoother_interval;
pa_usec_t last_smoother_update;
pa_reserve_wrapper *reserve;
pa_hook_slot *reserve_slot;
@ -243,6 +260,7 @@ static void fix_min_sleep_wakeup(struct userdata *u) {
size_t max_use, max_use_2;
pa_assert(u);
pa_assert(u->use_tsched);
max_use = u->hwbuf_size - u->hwbuf_unused;
max_use_2 = pa_frame_align(max_use/2, &u->sink->sample_spec);
@ -257,6 +275,7 @@ static void fix_min_sleep_wakeup(struct userdata *u) {
static void fix_tsched_watermark(struct userdata *u) {
size_t max_use;
pa_assert(u);
pa_assert(u->use_tsched);
max_use = u->hwbuf_size - u->hwbuf_unused;
@ -267,7 +286,7 @@ static void fix_tsched_watermark(struct userdata *u) {
u->tsched_watermark = u->min_wakeup;
}
static void adjust_after_underrun(struct userdata *u) {
static void increase_watermark(struct userdata *u) {
size_t old_watermark;
pa_usec_t old_min_latency, new_min_latency;
@ -276,31 +295,64 @@ static void adjust_after_underrun(struct userdata *u) {
/* First, just try to increase the watermark */
old_watermark = u->tsched_watermark;
u->tsched_watermark = PA_MIN(u->tsched_watermark * 2, u->tsched_watermark + u->watermark_step);
u->tsched_watermark = PA_MIN(u->tsched_watermark * 2, u->tsched_watermark + u->watermark_inc_step);
fix_tsched_watermark(u);
if (old_watermark != u->tsched_watermark) {
pa_log_notice("Increasing wakeup watermark to %0.2f ms",
(double) pa_bytes_to_usec(u->tsched_watermark, &u->sink->sample_spec) / PA_USEC_PER_MSEC);
pa_log_info("Increasing wakeup watermark to %0.2f ms",
(double) pa_bytes_to_usec(u->tsched_watermark, &u->sink->sample_spec) / PA_USEC_PER_MSEC);
return;
}
/* Hmm, we cannot increase the watermark any further, hence let's raise the latency */
old_min_latency = u->sink->thread_info.min_latency;
new_min_latency = PA_MIN(old_min_latency * 2, old_min_latency + TSCHED_WATERMARK_STEP_USEC);
new_min_latency = PA_MIN(old_min_latency * 2, old_min_latency + TSCHED_WATERMARK_INC_STEP_USEC);
new_min_latency = PA_MIN(new_min_latency, u->sink->thread_info.max_latency);
if (old_min_latency != new_min_latency) {
pa_log_notice("Increasing minimal latency to %0.2f ms",
(double) new_min_latency / PA_USEC_PER_MSEC);
pa_log_info("Increasing minimal latency to %0.2f ms",
(double) new_min_latency / PA_USEC_PER_MSEC);
pa_sink_set_latency_range_within_thread(u->sink, new_min_latency, u->sink->thread_info.max_latency);
return;
}
/* When we reach this we're officialy fucked! */
}
static void decrease_watermark(struct userdata *u) {
size_t old_watermark;
pa_usec_t now;
pa_assert(u);
pa_assert(u->use_tsched);
now = pa_rtclock_now();
if (u->watermark_dec_not_before <= 0)
goto restart;
if (u->watermark_dec_not_before > now)
return;
old_watermark = u->tsched_watermark;
if (u->tsched_watermark < u->watermark_dec_step)
u->tsched_watermark = u->tsched_watermark / 2;
else
u->tsched_watermark = PA_MAX(u->tsched_watermark / 2, u->tsched_watermark - u->watermark_dec_step);
fix_tsched_watermark(u);
if (old_watermark != u->tsched_watermark)
pa_log_info("Decreasing wakeup watermark to %0.2f ms",
(double) pa_bytes_to_usec(u->tsched_watermark, &u->sink->sample_spec) / PA_USEC_PER_MSEC);
/* We don't change the latency range*/
restart:
u->watermark_dec_not_before = now + TSCHED_WATERMARK_VERIFY_AFTER_USEC;
}
static void hw_sleep_time(struct userdata *u, pa_usec_t *sleep_usec, pa_usec_t*process_usec) {
pa_usec_t usec, wm;
@ -308,6 +360,7 @@ static void hw_sleep_time(struct userdata *u, pa_usec_t *sleep_usec, pa_usec_t*p
pa_assert(process_usec);
pa_assert(u);
pa_assert(u->use_tsched);
usec = pa_sink_get_requested_latency_within_thread(u->sink);
@ -355,7 +408,7 @@ static int try_recover(struct userdata *u, const char *call, int err) {
return 0;
}
static size_t check_left_to_play(struct userdata *u, size_t n_bytes) {
static size_t check_left_to_play(struct userdata *u, size_t n_bytes, pa_bool_t on_timeout) {
size_t left_to_play;
/* We use <= instead of < for this check here because an underrun
@ -363,34 +416,55 @@ static size_t check_left_to_play(struct userdata *u, size_t n_bytes) {
* it is removed from the buffer. This is particularly important
* when block transfer is used. */
if (n_bytes <= u->hwbuf_size) {
if (n_bytes <= u->hwbuf_size)
left_to_play = u->hwbuf_size - n_bytes;
else {
#ifdef DEBUG_TIMING
pa_log_debug("%0.2f ms left to play", (double) pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) / PA_USEC_PER_MSEC);
#endif
} else {
/* We got a dropout. What a mess! */
left_to_play = 0;
#ifdef DEBUG_TIMING
PA_DEBUG_TRAP;
#endif
if (!u->first && !u->after_rewind) {
if (!u->first && !u->after_rewind)
if (pa_log_ratelimit())
pa_log_info("Underrun!");
}
if (u->use_tsched)
adjust_after_underrun(u);
#ifdef DEBUG_TIMING
pa_log_debug("%0.2f ms left to play; inc threshold = %0.2f ms; dec threshold = %0.2f ms",
(double) pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) / PA_USEC_PER_MSEC,
(double) pa_bytes_to_usec(u->watermark_inc_threshold, &u->sink->sample_spec) / PA_USEC_PER_MSEC,
(double) pa_bytes_to_usec(u->watermark_dec_threshold, &u->sink->sample_spec) / PA_USEC_PER_MSEC);
#endif
if (u->use_tsched) {
pa_bool_t reset_not_before = TRUE;
if (!u->first && !u->after_rewind) {
if (left_to_play < u->watermark_inc_threshold)
increase_watermark(u);
else if (left_to_play > u->watermark_dec_threshold) {
reset_not_before = FALSE;
/* We decrease the watermark only if have actually
* been woken up by a timeout. If something else woke
* us up it's too easy to fulfill the deadlines... */
if (on_timeout)
decrease_watermark(u);
}
}
if (reset_not_before)
u->watermark_dec_not_before = 0;
}
return left_to_play;
}
static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled) {
static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled, pa_bool_t on_timeout) {
pa_bool_t work_done = TRUE;
pa_usec_t max_sleep_usec = 0, process_usec = 0;
size_t left_to_play;
@ -425,7 +499,8 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle
pa_log_debug("avail: %lu", (unsigned long) n_bytes);
#endif
left_to_play = check_left_to_play(u, n_bytes);
left_to_play = check_left_to_play(u, n_bytes, on_timeout);
on_timeout = FALSE;
if (u->use_tsched)
@ -560,7 +635,7 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle
return work_done ? 1 : 0;
}
static int unix_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled) {
static int unix_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled, pa_bool_t on_timeout) {
pa_bool_t work_done = FALSE;
pa_usec_t max_sleep_usec = 0, process_usec = 0;
size_t left_to_play;
@ -586,7 +661,8 @@ static int unix_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle
}
n_bytes = (size_t) n * u->frame_size;
left_to_play = check_left_to_play(u, n_bytes);
left_to_play = check_left_to_play(u, n_bytes, on_timeout);
on_timeout = FALSE;
if (u->use_tsched)
@ -723,18 +799,27 @@ static void update_smoother(struct userdata *u) {
now1 = pa_timespec_load(&htstamp);
}
/* Hmm, if the timestamp is 0, then it wasn't set and we take the current time */
if (now1 <= 0)
now1 = pa_rtclock_now();
/* check if the time since the last update is bigger than the interval */
if (u->last_smoother_update > 0)
if (u->last_smoother_update + u->smoother_interval > now1)
return;
position = (int64_t) u->write_count - ((int64_t) delay * (int64_t) u->frame_size);
if (PA_UNLIKELY(position < 0))
position = 0;
/* Hmm, if the timestamp is 0, then it wasn't set and we take the current time */
if (now1 <= 0)
now1 = pa_rtclock_now();
now2 = pa_bytes_to_usec((uint64_t) position, &u->sink->sample_spec);
pa_smoother_put(u->smoother, now1, now2);
u->last_smoother_update = now1;
/* exponentially increase the update interval up to the MAX limit */
u->smoother_interval = PA_MIN (u->smoother_interval * 2, SMOOTHER_MAX_INTERVAL);
}
static pa_usec_t sink_get_latency(struct userdata *u) {
@ -906,11 +991,12 @@ static int unsuspend(struct userdata *u) {
u->write_count = 0;
pa_smoother_reset(u->smoother, pa_rtclock_now(), TRUE);
u->smoother_interval = SMOOTHER_MIN_INTERVAL;
u->last_smoother_update = 0;
u->first = TRUE;
u->since_start = 0;
pa_log_info("Resumed successfully...");
return 0;
@ -1263,15 +1349,16 @@ static void thread_func(void *userdata) {
if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
int work_done;
pa_usec_t sleep_usec = 0;
pa_bool_t on_timeout = pa_rtpoll_timer_elapsed(u->rtpoll);
if (PA_UNLIKELY(u->sink->thread_info.rewind_requested))
if (process_rewind(u) < 0)
goto fail;
if (u->use_mmap)
work_done = mmap_write(u, &sleep_usec, revents & POLLOUT);
work_done = mmap_write(u, &sleep_usec, revents & POLLOUT, on_timeout);
else
work_done = unix_write(u, &sleep_usec, revents & POLLOUT);
work_done = unix_write(u, &sleep_usec, revents & POLLOUT, on_timeout);
if (work_done < 0)
goto fail;
@ -1622,6 +1709,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
5,
pa_rtclock_now(),
TRUE);
u->smoother_interval = SMOOTHER_MIN_INTERVAL;
dev_id = pa_modargs_get_value(
ma, "device_id",
@ -1771,7 +1859,6 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
u->fragment_size = frag_size = (uint32_t) (period_frames * frame_size);
u->nfragments = nfrags;
u->hwbuf_size = u->fragment_size * nfrags;
u->tsched_watermark = pa_usec_to_bytes_round_up(pa_bytes_to_usec_round_up(tsched_watermark, &requested_ss), &u->sink->sample_spec);
pa_cvolume_mute(&u->hardware_volume, u->sink->sample_spec.channels);
pa_log_info("Using %u fragments of size %lu bytes, buffer time is %0.2fms",
@ -1782,7 +1869,13 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
pa_sink_set_max_rewind(u->sink, u->hwbuf_size);
if (u->use_tsched) {
u->watermark_step = pa_usec_to_bytes(TSCHED_WATERMARK_STEP_USEC, &u->sink->sample_spec);
u->tsched_watermark = pa_usec_to_bytes_round_up(pa_bytes_to_usec_round_up(tsched_watermark, &requested_ss), &u->sink->sample_spec);
u->watermark_inc_step = pa_usec_to_bytes(TSCHED_WATERMARK_INC_STEP_USEC, &u->sink->sample_spec);
u->watermark_dec_step = pa_usec_to_bytes(TSCHED_WATERMARK_DEC_STEP_USEC, &u->sink->sample_spec);
u->watermark_inc_threshold = pa_usec_to_bytes_round_up(TSCHED_WATERMARK_INC_THRESHOLD_USEC, &u->sink->sample_spec);
u->watermark_dec_threshold = pa_usec_to_bytes_round_up(TSCHED_WATERMARK_DEC_THRESHOLD_USEC, &u->sink->sample_spec);
fix_min_sleep_wakeup(u);
fix_tsched_watermark(u);
@ -1796,6 +1889,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
} else
pa_sink_set_fixed_latency(u->sink, pa_bytes_to_usec(u->hwbuf_size, &ss));
reserve_update(u);
if (update_sw_params(u) < 0)

View file

@ -59,11 +59,22 @@
/* #define DEBUG_TIMING */
#define DEFAULT_DEVICE "default"
#define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC) /* 2s */
#define DEFAULT_TSCHED_WATERMARK_USEC (20*PA_USEC_PER_MSEC) /* 20ms */
#define TSCHED_WATERMARK_STEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms */
#define TSCHED_MIN_SLEEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms */
#define TSCHED_MIN_WAKEUP_USEC (4*PA_USEC_PER_MSEC) /* 4ms */
#define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC) /* 2s */
#define DEFAULT_TSCHED_WATERMARK_USEC (20*PA_USEC_PER_MSEC) /* 20ms */
#define TSCHED_WATERMARK_INC_STEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms */
#define TSCHED_WATERMARK_DEC_STEP_USEC (5*PA_USEC_PER_MSEC) /* 5ms */
#define TSCHED_WATERMARK_VERIFY_AFTER_USEC (20*PA_USEC_PER_SEC) /* 20s */
#define TSCHED_WATERMARK_INC_THRESHOLD_USEC (1*PA_USEC_PER_MSEC) /* 3ms */
#define TSCHED_WATERMARK_DEC_THRESHOLD_USEC (100*PA_USEC_PER_MSEC) /* 100ms */
#define TSCHED_WATERMARK_STEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms */
#define TSCHED_MIN_SLEEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms */
#define TSCHED_MIN_WAKEUP_USEC (4*PA_USEC_PER_MSEC) /* 4ms */
#define SMOOTHER_MIN_INTERVAL (2*PA_USEC_PER_MSEC) /* 2ms */
#define SMOOTHER_MAX_INTERVAL (200*PA_USEC_PER_MSEC) /* 200ms */
#define VOLUME_ACCURACY (PA_VOLUME_NORM/100)
@ -93,7 +104,12 @@ struct userdata {
hwbuf_unused,
min_sleep,
min_wakeup,
watermark_step;
watermark_inc_step,
watermark_dec_step,
watermark_inc_threshold,
watermark_dec_threshold;
pa_usec_t watermark_dec_not_before;
unsigned nfragments;
@ -108,6 +124,8 @@ struct userdata {
pa_smoother *smoother;
uint64_t read_count;
pa_usec_t smoother_interval;
pa_usec_t last_smoother_update;
pa_reserve_wrapper *reserve;
pa_hook_slot *reserve_slot;
@ -236,6 +254,7 @@ static int reserve_monitor_init(struct userdata *u, const char *dname) {
static void fix_min_sleep_wakeup(struct userdata *u) {
size_t max_use, max_use_2;
pa_assert(u);
pa_assert(u->use_tsched);
max_use = u->hwbuf_size - u->hwbuf_unused;
max_use_2 = pa_frame_align(max_use/2, &u->source->sample_spec);
@ -250,6 +269,7 @@ static void fix_min_sleep_wakeup(struct userdata *u) {
static void fix_tsched_watermark(struct userdata *u) {
size_t max_use;
pa_assert(u);
pa_assert(u->use_tsched);
max_use = u->hwbuf_size - u->hwbuf_unused;
@ -260,7 +280,7 @@ static void fix_tsched_watermark(struct userdata *u) {
u->tsched_watermark = u->min_wakeup;
}
static void adjust_after_overrun(struct userdata *u) {
static void increase_watermark(struct userdata *u) {
size_t old_watermark;
pa_usec_t old_min_latency, new_min_latency;
@ -269,36 +289,72 @@ static void adjust_after_overrun(struct userdata *u) {
/* First, just try to increase the watermark */
old_watermark = u->tsched_watermark;
u->tsched_watermark = PA_MIN(u->tsched_watermark * 2, u->tsched_watermark + u->watermark_step);
u->tsched_watermark = PA_MIN(u->tsched_watermark * 2, u->tsched_watermark + u->watermark_inc_step);
fix_tsched_watermark(u);
if (old_watermark != u->tsched_watermark) {
pa_log_notice("Increasing wakeup watermark to %0.2f ms",
(double) pa_bytes_to_usec(u->tsched_watermark, &u->source->sample_spec) / PA_USEC_PER_MSEC);
pa_log_info("Increasing wakeup watermark to %0.2f ms",
(double) pa_bytes_to_usec(u->tsched_watermark, &u->source->sample_spec) / PA_USEC_PER_MSEC);
return;
}
/* Hmm, we cannot increase the watermark any further, hence let's raise the latency */
old_min_latency = u->source->thread_info.min_latency;
new_min_latency = PA_MIN(old_min_latency * 2, old_min_latency + TSCHED_WATERMARK_STEP_USEC);
new_min_latency = PA_MIN(old_min_latency * 2, old_min_latency + TSCHED_WATERMARK_INC_STEP_USEC);
new_min_latency = PA_MIN(new_min_latency, u->source->thread_info.max_latency);
if (old_min_latency != new_min_latency) {
pa_log_notice("Increasing minimal latency to %0.2f ms",
(double) new_min_latency / PA_USEC_PER_MSEC);
pa_log_info("Increasing minimal latency to %0.2f ms",
(double) new_min_latency / PA_USEC_PER_MSEC);
pa_source_set_latency_range_within_thread(u->source, new_min_latency, u->source->thread_info.max_latency);
return;
}
/* When we reach this we're officialy fucked! */
}
static void decrease_watermark(struct userdata *u) {
size_t old_watermark;
pa_usec_t now;
pa_assert(u);
pa_assert(u->use_tsched);
now = pa_rtclock_now();
if (u->watermark_dec_not_before <= 0)
goto restart;
if (u->watermark_dec_not_before > now)
return;
old_watermark = u->tsched_watermark;
if (u->tsched_watermark < u->watermark_dec_step)
u->tsched_watermark = u->tsched_watermark / 2;
else
u->tsched_watermark = PA_MAX(u->tsched_watermark / 2, u->tsched_watermark - u->watermark_dec_step);
fix_tsched_watermark(u);
if (old_watermark != u->tsched_watermark)
pa_log_info("Decreasing wakeup watermark to %0.2f ms",
(double) pa_bytes_to_usec(u->tsched_watermark, &u->source->sample_spec) / PA_USEC_PER_MSEC);
/* We don't change the latency range*/
restart:
u->watermark_dec_not_before = now + TSCHED_WATERMARK_VERIFY_AFTER_USEC;
}
static pa_usec_t hw_sleep_time(struct userdata *u, pa_usec_t *sleep_usec, pa_usec_t*process_usec) {
pa_usec_t wm, usec;
pa_assert(sleep_usec);
pa_assert(process_usec);
pa_assert(u);
pa_assert(u->use_tsched);
usec = pa_source_get_requested_latency_within_thread(u->source);
@ -347,7 +403,7 @@ static int try_recover(struct userdata *u, const char *call, int err) {
return 0;
}
static size_t check_left_to_record(struct userdata *u, size_t n_bytes) {
static size_t check_left_to_record(struct userdata *u, size_t n_bytes, pa_bool_t on_timeout) {
size_t left_to_record;
size_t rec_space = u->hwbuf_size - u->hwbuf_unused;
@ -356,14 +412,11 @@ static size_t check_left_to_record(struct userdata *u, size_t n_bytes) {
* it is removed from the buffer. This is particularly important
* when block transfer is used. */
if (n_bytes <= rec_space) {
if (n_bytes <= rec_space)
left_to_record = rec_space - n_bytes;
else {
#ifdef DEBUG_TIMING
pa_log_debug("%0.2f ms left to record", (double) pa_bytes_to_usec(left_to_record, &u->source->sample_spec) / PA_USEC_PER_MSEC);
#endif
} else {
/* We got a dropout. What a mess! */
left_to_record = 0;
#ifdef DEBUG_TIMING
@ -372,15 +425,36 @@ static size_t check_left_to_record(struct userdata *u, size_t n_bytes) {
if (pa_log_ratelimit())
pa_log_info("Overrun!");
}
if (u->use_tsched)
adjust_after_overrun(u);
#ifdef DEBUG_TIMING
pa_log_debug("%0.2f ms left to record", (double) pa_bytes_to_usec(left_to_record, &u->source->sample_spec) / PA_USEC_PER_MSEC);
#endif
if (u->use_tsched) {
pa_bool_t reset_not_before = TRUE;
if (left_to_record < u->watermark_inc_threshold)
increase_watermark(u);
else if (left_to_record > u->watermark_dec_threshold) {
reset_not_before = FALSE;
/* We decrease the watermark only if have actually been
* woken up by a timeout. If something else woke us up
* it's too easy to fulfill the deadlines... */
if (on_timeout)
decrease_watermark(u);
}
if (reset_not_before)
u->watermark_dec_not_before = 0;
}
return left_to_record;
}
static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled) {
static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled, pa_bool_t on_timeout) {
pa_bool_t work_done = FALSE;
pa_usec_t max_sleep_usec = 0, process_usec = 0;
size_t left_to_record;
@ -412,7 +486,8 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
pa_log_debug("avail: %lu", (unsigned long) n_bytes);
#endif
left_to_record = check_left_to_record(u, n_bytes);
left_to_record = check_left_to_record(u, n_bytes, on_timeout);
on_timeout = FALSE;
if (u->use_tsched)
if (!polled &&
@ -538,7 +613,7 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
return work_done ? 1 : 0;
}
static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled) {
static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled, pa_bool_t on_timeout) {
int work_done = FALSE;
pa_usec_t max_sleep_usec = 0, process_usec = 0;
size_t left_to_record;
@ -565,7 +640,8 @@ static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
}
n_bytes = (size_t) n * u->frame_size;
left_to_record = check_left_to_record(u, n_bytes);
left_to_record = check_left_to_record(u, n_bytes, on_timeout);
on_timeout = FALSE;
if (u->use_tsched)
if (!polled &&
@ -691,15 +767,23 @@ static void update_smoother(struct userdata *u) {
now1 = pa_timespec_load(&htstamp);
}
position = u->read_count + ((uint64_t) delay * (uint64_t) u->frame_size);
/* Hmm, if the timestamp is 0, then it wasn't set and we take the current time */
if (now1 <= 0)
now1 = pa_rtclock_now();
/* check if the time since the last update is bigger than the interval */
if (u->last_smoother_update > 0)
if (u->last_smoother_update + u->smoother_interval > now1)
return;
position = u->read_count + ((uint64_t) delay * (uint64_t) u->frame_size);
now2 = pa_bytes_to_usec(position, &u->source->sample_spec);
pa_smoother_put(u->smoother, now1, now2);
u->last_smoother_update = now1;
/* exponentially increase the update interval up to the MAX limit */
u->smoother_interval = PA_MIN (u->smoother_interval * 2, SMOOTHER_MAX_INTERVAL);
}
static pa_usec_t source_get_latency(struct userdata *u) {
@ -862,6 +946,8 @@ static int unsuspend(struct userdata *u) {
u->read_count = 0;
pa_smoother_reset(u->smoother, pa_rtclock_now(), TRUE);
u->smoother_interval = SMOOTHER_MIN_INTERVAL;
u->last_smoother_update = 0;
pa_log_info("Resumed successfully...");
@ -1143,11 +1229,12 @@ static void thread_func(void *userdata) {
if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
int work_done;
pa_usec_t sleep_usec = 0;
pa_bool_t on_timeout = pa_rtpoll_timer_elapsed(u->rtpoll);
if (u->use_mmap)
work_done = mmap_read(u, &sleep_usec, revents & POLLIN);
work_done = mmap_read(u, &sleep_usec, revents & POLLIN, on_timeout);
else
work_done = unix_read(u, &sleep_usec, revents & POLLIN);
work_done = unix_read(u, &sleep_usec, revents & POLLIN, on_timeout);
if (work_done < 0)
goto fail;
@ -1469,6 +1556,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
5,
pa_rtclock_now(),
FALSE);
u->smoother_interval = SMOOTHER_MIN_INTERVAL;
dev_id = pa_modargs_get_value(
ma, "device_id",
@ -1616,7 +1704,6 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
u->fragment_size = frag_size = (uint32_t) (period_frames * frame_size);
u->nfragments = nfrags;
u->hwbuf_size = u->fragment_size * nfrags;
u->tsched_watermark = pa_usec_to_bytes_round_up(pa_bytes_to_usec_round_up(tsched_watermark, &requested_ss), &u->source->sample_spec);
pa_cvolume_mute(&u->hardware_volume, u->source->sample_spec.channels);
pa_log_info("Using %u fragments of size %lu bytes, buffer time is %0.2fms",
@ -1624,7 +1711,13 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
(double) pa_bytes_to_usec(u->hwbuf_size, &ss) / PA_USEC_PER_MSEC);
if (u->use_tsched) {
u->watermark_step = pa_usec_to_bytes(TSCHED_WATERMARK_STEP_USEC, &u->source->sample_spec);
u->tsched_watermark = pa_usec_to_bytes_round_up(pa_bytes_to_usec_round_up(tsched_watermark, &requested_ss), &u->source->sample_spec);
u->watermark_inc_step = pa_usec_to_bytes(TSCHED_WATERMARK_INC_STEP_USEC, &u->source->sample_spec);
u->watermark_dec_step = pa_usec_to_bytes(TSCHED_WATERMARK_DEC_STEP_USEC, &u->source->sample_spec);
u->watermark_inc_threshold = pa_usec_to_bytes_round_up(TSCHED_WATERMARK_INC_THRESHOLD_USEC, &u->source->sample_spec);
u->watermark_dec_threshold = pa_usec_to_bytes_round_up(TSCHED_WATERMARK_DEC_THRESHOLD_USEC, &u->source->sample_spec);
fix_min_sleep_wakeup(u);
fix_tsched_watermark(u);

View file

@ -41,9 +41,12 @@ volume = merge
override-map.1 = lfe
override-map.2 = lfe,lfe
; This profile path is intended to control the speaker, not the
; headphones. But it should not hurt if we leave the headphone jack
; enabled nonetheless.
[Element Headphone]
switch = off
volume = off
switch = mute
volume = zero
[Element Speaker]
switch = mute

View file

@ -38,9 +38,12 @@ volume = merge
override-map.1 = all
override-map.2 = all-left,all-right
; This profile path is intended to control the speaker, not the
; headphones. But it should not hurt if we leave the headphone jack
; enabled nonetheless.
[Element Headphone]
switch = off
volume = off
switch = mute
volume = zero
[Element Speaker]
switch = mute

View file

@ -37,9 +37,12 @@ override-map.2 = all-left,all-right
switch = off
volume = off
; This profile path is intended to control the speaker, not the
; headphones. But it should not hurt if we leave the headphone jack
; enabled nonetheless.
[Element Headphone]
switch = off
volume = off
switch = mute
volume = zero
[Element Speaker]
switch = mute

View file

@ -52,9 +52,6 @@ PA_MODULE_LOAD_ONCE(TRUE);
#define MAX_MODULES 10
#define BUF_MAX 2048
/* #undef PA_GCONF_HELPER */
/* #define PA_GCONF_HELPER "/home/lennart/projects/pulseaudio/src/gconf-helper" */
struct module_item {
char *name;
char *args;
@ -343,7 +340,11 @@ int pa__init(pa_module*m) {
u->io_event = NULL;
u->buf_fill = 0;
if ((u->fd = pa_start_child_for_read(PA_GCONF_HELPER, NULL, &u->pid)) < 0)
if ((u->fd = pa_start_child_for_read(
#if defined(__linux__) && !defined(__OPTIMIZE__)
pa_run_from_build_tree() ? PA_BUILDDIR "/.libs/gconf-helper" :
#endif
PA_GCONF_HELPER, NULL, &u->pid)) < 0)
goto fail;
u->io_event = m->core->mainloop->io_new(

View file

@ -1161,6 +1161,8 @@ int pa__init(pa_module*m) {
pa_channel_map slaves_map;
pa_bool_t is_first_slave = TRUE;
pa_sample_spec_init(&slaves_spec);
while ((n = pa_split(slaves, ",", &split_state))) {
pa_sink *slave_sink;

View file

@ -99,7 +99,7 @@ static const char* const valid_modargs[] = {
};
/* Called from I/O thread context */
static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
static int sink_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
struct userdata *u = PA_SINK(o)->userdata;
switch (code) {
@ -130,7 +130,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
}
/* Called from main context */
static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
static int sink_set_state_cb(pa_sink *s, pa_sink_state_t state) {
struct userdata *u;
pa_sink_assert_ref(s);
@ -145,7 +145,7 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
}
/* Called from I/O thread context */
static void sink_request_rewind(pa_sink *s) {
static void sink_request_rewind_cb(pa_sink *s) {
struct userdata *u;
pa_sink_assert_ref(s);
@ -160,7 +160,7 @@ static void sink_request_rewind(pa_sink *s) {
}
/* Called from I/O thread context */
static void sink_update_requested_latency(pa_sink *s) {
static void sink_update_requested_latency_cb(pa_sink *s) {
struct userdata *u;
pa_sink_assert_ref(s);
@ -176,6 +176,34 @@ static void sink_update_requested_latency(pa_sink *s) {
pa_sink_get_requested_latency_within_thread(s));
}
/* Called from main context */
static void sink_set_volume_cb(pa_sink *s) {
struct userdata *u;
pa_sink_assert_ref(s);
pa_assert_se(u = s->userdata);
if (!PA_SINK_IS_LINKED(pa_sink_get_state(s)) ||
!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input)))
return;
pa_sink_input_set_volume(u->sink_input, &s->real_volume, s->save_volume, TRUE);
}
/* Called from main context */
static void sink_set_mute_cb(pa_sink *s) {
struct userdata *u;
pa_sink_assert_ref(s);
pa_assert_se(u = s->userdata);
if (!PA_SINK_IS_LINKED(pa_sink_get_state(s)) ||
!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input)))
return;
pa_sink_input_set_mute(u->sink_input, s->muted, s->save_muted);
}
/* Called from I/O thread context */
static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
struct userdata *u;
@ -390,8 +418,31 @@ static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) {
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
pa_sink_set_asyncmsgq(u->sink, dest->asyncmsgq);
pa_sink_update_flags(u->sink, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY, dest->flags);
if (dest) {
pa_sink_set_asyncmsgq(u->sink, dest->asyncmsgq);
pa_sink_update_flags(u->sink, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY, dest->flags);
} else
pa_sink_set_asyncmsgq(u->sink, NULL);
}
/* Called from main context */
static void sink_input_volume_changed_cb(pa_sink_input *i) {
struct userdata *u;
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
pa_sink_volume_changed(u->sink, &i->volume);
}
/* Called from main context */
static void sink_input_mute_changed_cb(pa_sink_input *i) {
struct userdata *u;
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
pa_sink_mute_changed(u->sink, i->muted);
}
int pa__init(pa_module*m) {
@ -731,7 +782,9 @@ int pa__init(pa_module*m) {
goto fail;
}
u->sink = pa_sink_new(m->core, &sink_data, master->flags & (PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY));
u->sink = pa_sink_new(m->core, &sink_data,
PA_SINK_HW_MUTE_CTRL|PA_SINK_HW_VOLUME_CTRL|PA_SINK_DECIBEL_VOLUME|
(master->flags & (PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY)));
pa_sink_new_data_done(&sink_data);
if (!u->sink) {
@ -739,10 +792,12 @@ int pa__init(pa_module*m) {
goto fail;
}
u->sink->parent.process_msg = sink_process_msg;
u->sink->set_state = sink_set_state;
u->sink->update_requested_latency = sink_update_requested_latency;
u->sink->request_rewind = sink_request_rewind;
u->sink->parent.process_msg = sink_process_msg_cb;
u->sink->set_state = sink_set_state_cb;
u->sink->update_requested_latency = sink_update_requested_latency_cb;
u->sink->request_rewind = sink_request_rewind_cb;
u->sink->set_volume = sink_set_volume_cb;
u->sink->set_mute = sink_set_mute_cb;
u->sink->userdata = u;
pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq);
@ -775,6 +830,8 @@ int pa__init(pa_module*m) {
u->sink_input->state_change = sink_input_state_change_cb;
u->sink_input->may_move_to = sink_input_may_move_to_cb;
u->sink_input->moving = sink_input_moving_cb;
u->sink_input->volume_changed = sink_input_volume_changed_cb;
u->sink_input->mute_changed = sink_input_mute_changed_cb;
u->sink_input->userdata = u;
pa_sink_put(u->sink);

View file

@ -302,8 +302,11 @@ static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) {
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
pa_sink_set_asyncmsgq(u->sink, dest->asyncmsgq);
pa_sink_update_flags(u->sink, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY, dest->flags);
if (dest) {
pa_sink_set_asyncmsgq(u->sink, dest->asyncmsgq);
pa_sink_update_flags(u->sink, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY, dest->flags);
} else
pa_sink_set_asyncmsgq(u->sink, NULL);
}
int pa__init(pa_module*m) {

View file

@ -60,6 +60,7 @@
#include <pulsecore/thread-mq.h>
#include <pulsecore/rtpoll.h>
#include <pulsecore/thread.h>
#include <pulsecore/time-smoother.h>
#include "module-solaris-symdef.h"
@ -110,6 +111,8 @@ struct userdata {
uint32_t prev_playback_samples, prev_record_samples;
int32_t minimum_request;
pa_smoother *smoother;
};
static const char* const valid_modargs[] = {
@ -133,6 +136,9 @@ static const char* const valid_modargs[] = {
#define MAX_RENDER_HZ (300)
/* This render rate limit imposes a minimum latency, but without it we waste too much CPU time. */
#define MAX_BUFFER_SIZE (128 * 1024)
/* An attempt to buffer more than 128 KB causes write() to fail with errno == EAGAIN. */
static uint64_t get_playback_buffered_bytes(struct userdata *u) {
audio_info_t info;
uint64_t played_bytes;
@ -145,7 +151,12 @@ static uint64_t get_playback_buffered_bytes(struct userdata *u) {
/* Handle wrap-around of the device's sample counter, which is a uint_32. */
if (u->prev_playback_samples > info.play.samples) {
/* Unfortunately info.play.samples can sometimes go backwards, even before it wraps! */
/*
* Unfortunately info.play.samples can sometimes go backwards, even before it wraps!
* The bug seems to be absent on Solaris x86 nv117 with audio810 driver, at least on this (UP) machine.
* The bug is present on a different (SMP) machine running Solaris x86 nv103 with audioens driver.
* An earlier revision of this file mentions the same bug independently (unknown configuration).
*/
if (u->prev_playback_samples + info.play.samples < 240000) {
++u->play_samples_msw;
} else {
@ -155,6 +166,8 @@ static uint64_t get_playback_buffered_bytes(struct userdata *u) {
u->prev_playback_samples = info.play.samples;
played_bytes = (((uint64_t)u->play_samples_msw << 32) + info.play.samples) * u->frame_size;
pa_smoother_put(u->smoother, pa_rtclock_now(), pa_bytes_to_usec(played_bytes, &u->sink->sample_spec));
return u->written_bytes - played_bytes;
}
@ -387,6 +400,8 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state));
pa_smoother_pause(u->smoother, pa_rtclock_now());
if (!u->source || u->source_suspended) {
if (suspend(u) < 0)
return -1;
@ -398,6 +413,8 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
case PA_SINK_RUNNING:
if (u->sink->thread_info.state == PA_SINK_SUSPENDED) {
pa_smoother_resume(u->smoother, pa_rtclock_now(), TRUE);
if (!u->source || u->source_suspended) {
if (unsuspend(u) < 0)
return -1;
@ -479,7 +496,7 @@ static void sink_set_volume(pa_sink *s) {
if (u->fd >= 0) {
AUDIO_INITINFO(&info);
info.play.gain = pa_cvolume_max(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
info.play.gain = pa_cvolume_max(&s->real_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
assert(info.play.gain <= AUDIO_MAX_GAIN);
if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) {
@ -501,8 +518,7 @@ static void sink_get_volume(pa_sink *s) {
if (ioctl(u->fd, AUDIO_GETINFO, &info) < 0)
pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
else
pa_cvolume_set(&s->virtual_volume, s->sample_spec.channels,
info.play.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN);
pa_cvolume_set(&s->real_volume, s->sample_spec.channels, info.play.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN);
}
}
@ -515,7 +531,7 @@ static void source_set_volume(pa_source *s) {
if (u->fd >= 0) {
AUDIO_INITINFO(&info);
info.play.gain = pa_cvolume_max(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
info.play.gain = pa_cvolume_max(&s->volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
assert(info.play.gain <= AUDIO_MAX_GAIN);
if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) {
@ -537,8 +553,7 @@ static void source_get_volume(pa_source *s) {
if (ioctl(u->fd, AUDIO_GETINFO, &info) < 0)
pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
else
pa_cvolume_set(&s->virtual_volume, s->sample_spec.channels,
info.play.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN);
pa_cvolume_set(&s->volume, s->sample_spec.channels, info.play.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN);
}
}
@ -606,11 +621,13 @@ static void thread_func(void *userdata) {
pa_thread_mq_install(&u->thread_mq);
pa_smoother_set_time_offset(u->smoother, pa_rtclock_now());
for (;;) {
/* Render some data and write it to the dsp */
if (u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
pa_usec_t xtime0;
pa_usec_t xtime0, ysleep_interval, xsleep_interval;
uint64_t buffered_bytes;
if (u->sink->thread_info.rewind_requested)
@ -629,12 +646,15 @@ static void thread_func(void *userdata) {
info.play.error = 0;
if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0)
pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
pa_smoother_reset(u->smoother, pa_rtclock_now(), TRUE);
}
for (;;) {
void *p;
ssize_t w;
size_t len;
int write_type = 1;
/*
* Since we cannot modify the size of the output buffer we fake it
@ -652,38 +672,31 @@ static void thread_func(void *userdata) {
break;
if (u->memchunk.length < len)
pa_sink_render(u->sink, u->sink->thread_info.max_request, &u->memchunk);
pa_sink_render(u->sink, len - u->memchunk.length, &u->memchunk);
len = PA_MIN(u->memchunk.length, len);
p = pa_memblock_acquire(u->memchunk.memblock);
w = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length, NULL);
w = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, len, &write_type);
pa_memblock_release(u->memchunk.memblock);
if (w <= 0) {
switch (errno) {
case EINTR:
continue;
case EAGAIN:
/* If the buffer_size is too big, we get EAGAIN. Avoiding that limit by trial and error
* is not ideal, but I don't know how to get the system to tell me what the limit is.
*/
u->buffer_size = u->buffer_size * 18 / 25;
u->buffer_size -= u->buffer_size % u->frame_size;
u->buffer_size = PA_MAX(u->buffer_size, 2 * u->minimum_request);
pa_sink_set_max_request_within_thread(u->sink, u->buffer_size);
pa_sink_set_max_rewind_within_thread(u->sink, u->buffer_size);
pa_log("EAGAIN. Buffer size is now %u bytes (%llu buffered)", u->buffer_size, buffered_bytes);
break;
default:
pa_log("Failed to write data to DSP: %s", pa_cstrerror(errno));
goto fail;
if (errno == EINTR) {
continue;
} else if (errno == EAGAIN) {
/* We may have realtime priority so yield the CPU to ensure that fd can become writable again. */
pa_log_debug("EAGAIN with %llu bytes buffered.", buffered_bytes);
break;
} else {
pa_log("Failed to write data to DSP: %s", pa_cstrerror(errno));
goto fail;
}
} else {
pa_assert(w % u->frame_size == 0);
u->written_bytes += w;
u->memchunk.length -= w;
u->memchunk.index += w;
u->memchunk.length -= w;
if (u->memchunk.length <= 0) {
pa_memblock_unref(u->memchunk.memblock);
pa_memchunk_reset(&u->memchunk);
@ -691,7 +704,9 @@ static void thread_func(void *userdata) {
}
}
pa_rtpoll_set_timer_absolute(u->rtpoll, xtime0 + pa_bytes_to_usec(buffered_bytes / 2, &u->sink->sample_spec));
ysleep_interval = pa_bytes_to_usec(buffered_bytes / 2, &u->sink->sample_spec);
xsleep_interval = pa_smoother_translate(u->smoother, xtime0, ysleep_interval);
pa_rtpoll_set_timer_absolute(u->rtpoll, xtime0 + PA_MIN(xsleep_interval, ysleep_interval));
} else
pa_rtpoll_set_timer_disabled(u->rtpoll);
@ -797,7 +812,7 @@ static void sig_callback(pa_mainloop_api *api, pa_signal_event*e, int sig, void
pa_log_debug("caught signal");
if (u->sink) {
pa_sink_get_volume(u->sink, TRUE, FALSE);
pa_sink_get_volume(u->sink, TRUE);
pa_sink_get_mute(u->sink, TRUE);
}
@ -812,7 +827,7 @@ int pa__init(pa_module *m) {
pa_channel_map map;
pa_modargs *ma = NULL;
uint32_t buffer_length_msec;
int fd;
int fd = -1;
pa_sink_new_data sink_new_data;
pa_source_new_data source_new_data;
char const *name;
@ -838,6 +853,9 @@ int pa__init(pa_module *m) {
u = pa_xnew0(struct userdata, 1);
if (!(u->smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC * 2, TRUE, TRUE, 10, pa_rtclock_now(), TRUE)))
goto fail;
/*
* For a process (or several processes) to use the same audio device for both
* record and playback at the same time, the device's mixer must be enabled.
@ -861,7 +879,13 @@ int pa__init(pa_module *m) {
}
u->buffer_size = pa_usec_to_bytes(1000 * buffer_length_msec, &ss);
if (u->buffer_size < 2 * u->minimum_request) {
pa_log("supplied buffer size argument is too small");
pa_log("buffer_length argument cannot be smaller than %u",
(unsigned)(pa_bytes_to_usec(2 * u->minimum_request, &ss) / 1000));
goto fail;
}
if (u->buffer_size > MAX_BUFFER_SIZE) {
pa_log("buffer_length argument cannot be greater than %u",
(unsigned)(pa_bytes_to_usec(MAX_BUFFER_SIZE, &ss) / 1000));
goto fail;
}
@ -924,6 +948,7 @@ int pa__init(pa_module *m) {
pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
pa_source_set_rtpoll(u->source, u->rtpoll);
pa_source_set_fixed_latency(u->source, pa_bytes_to_usec(u->buffer_size, &u->source->sample_spec));
u->source->get_volume = source_get_volume;
u->source->set_volume = source_set_volume;
@ -966,15 +991,15 @@ int pa__init(pa_module *m) {
pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
pa_sink_set_rtpoll(u->sink, u->rtpoll);
pa_sink_set_fixed_latency(u->sink, pa_bytes_to_usec(u->buffer_size, &u->sink->sample_spec));
pa_sink_set_max_request(u->sink, u->buffer_size);
pa_sink_set_max_rewind(u->sink, u->buffer_size);
u->sink->get_volume = sink_get_volume;
u->sink->set_volume = sink_set_volume;
u->sink->get_mute = sink_get_mute;
u->sink->set_mute = sink_set_mute;
u->sink->refresh_volume = u->sink->refresh_muted = TRUE;
pa_sink_set_max_request(u->sink, u->buffer_size);
pa_sink_set_max_rewind(u->sink, u->buffer_size);
} else
u->sink = NULL;
@ -1075,6 +1100,9 @@ void pa__done(pa_module *m) {
if (u->fd >= 0)
close(u->fd);
if (u->smoother)
pa_smoother_free(u->smoother);
pa_xfree(u->device_name);
pa_xfree(u);

View file

@ -25,6 +25,7 @@
#include <errno.h>
#include <limits.h>
#include <dirent.h>
#include <sys/inotify.h>
#include <libudev.h>
@ -45,8 +46,9 @@ PA_MODULE_USAGE(
struct device {
char *path;
pa_bool_t accessible;
pa_bool_t need_verify;
char *card_name;
char *args;
uint32_t module;
};
@ -78,6 +80,7 @@ static void device_free(struct device *d) {
pa_xfree(d->path);
pa_xfree(d->card_name);
pa_xfree(d->args);
pa_xfree(d);
}
@ -96,30 +99,166 @@ static const char *path_get_card_id(const char *path) {
return e + 5;
}
static pa_bool_t is_card_busy(const char *id) {
char *card_path = NULL, *pcm_path = NULL, *sub_status = NULL;
DIR *card_dir = NULL, *pcm_dir = NULL;
FILE *status_file = NULL;
size_t len;
struct dirent *space = NULL, *de;
pa_bool_t busy = FALSE;
int r;
pa_assert(id);
card_path = pa_sprintf_malloc("/proc/asound/card%s", id);
if (!(card_dir = opendir(card_path))) {
pa_log_warn("Failed to open %s: %s", card_path, pa_cstrerror(errno));
goto fail;
}
len = offsetof(struct dirent, d_name) + fpathconf(dirfd(card_dir), _PC_NAME_MAX) + 1;
space = pa_xmalloc(len);
for (;;) {
de = NULL;
if ((r = readdir_r(card_dir, space, &de)) != 0) {
pa_log_warn("readdir_r() failed: %s", pa_cstrerror(r));
goto fail;
}
if (!de)
break;
if (!pa_startswith(de->d_name, "pcm"))
continue;
pa_xfree(pcm_path);
pcm_path = pa_sprintf_malloc("%s/%s", card_path, de->d_name);
if (pcm_dir)
closedir(pcm_dir);
if (!(pcm_dir = opendir(pcm_path))) {
pa_log_warn("Failed to open %s: %s", pcm_path, pa_cstrerror(errno));
continue;
}
for (;;) {
char line[32];
if ((r = readdir_r(pcm_dir, space, &de)) != 0) {
pa_log_warn("readdir_r() failed: %s", pa_cstrerror(r));
goto fail;
}
if (!de)
break;
if (!pa_startswith(de->d_name, "sub"))
continue;
pa_xfree(sub_status);
sub_status = pa_sprintf_malloc("%s/%s/status", pcm_path, de->d_name);
if (status_file)
fclose(status_file);
if (!(status_file = fopen(sub_status, "r"))) {
pa_log_warn("Failed to open %s: %s", sub_status, pa_cstrerror(errno));
continue;
}
if (!(fgets(line, sizeof(line)-1, status_file))) {
pa_log_warn("Failed to read from %s: %s", sub_status, pa_cstrerror(errno));
continue;
}
if (!pa_streq(line, "closed\n")) {
busy = TRUE;
break;
}
}
}
fail:
pa_xfree(card_path);
pa_xfree(pcm_path);
pa_xfree(sub_status);
pa_xfree(space);
if (card_dir)
closedir(card_dir);
if (pcm_dir)
closedir(pcm_dir);
if (status_file)
fclose(status_file);
return busy;
}
static void verify_access(struct userdata *u, struct device *d) {
char *cd;
pa_card *card;
pa_bool_t accessible;
pa_assert(u);
pa_assert(d);
if (!(card = pa_namereg_get(u->core, d->card_name, PA_NAMEREG_CARD)))
return;
cd = pa_sprintf_malloc("%s/snd/controlC%s", udev_get_dev_path(u->udev), path_get_card_id(d->path));
d->accessible = access(cd, W_OK) >= 0;
pa_log_info("%s is accessible: %s", cd, pa_yes_no(d->accessible));
accessible = access(cd, R_OK|W_OK) >= 0;
pa_log_debug("%s is accessible: %s", cd, pa_yes_no(accessible));
pa_xfree(cd);
pa_card_suspend(card, !d->accessible, PA_SUSPEND_SESSION);
if (d->module == PA_INVALID_INDEX) {
/* If we are not loaded, try to load */
if (accessible) {
pa_module *m;
pa_bool_t busy;
/* Check if any of the PCM devices that belong to this
* card are currently busy. If they are, don't try to load
* right now, to make sure the probing phase can
* successfully complete. When the current user of the
* device closes it we will get another notification via
* inotify and can then recheck. */
busy = is_card_busy(path_get_card_id(d->path));
pa_log_debug("%s is busy: %s", d->path, pa_yes_no(busy));
if (!busy) {
pa_log_debug("Loading module-alsa-card with arguments '%s'", d->args);
m = pa_module_load(u->core, "module-alsa-card", d->args);
if (m) {
d->module = m->index;
pa_log_info("Card %s (%s) module loaded.", d->path, d->card_name);
} else
pa_log_info("Card %s (%s) failed to load module.", d->path, d->card_name);
}
}
} else {
/* If we are already loaded update suspend status with
* accessible boolean */
if ((card = pa_namereg_get(u->core, d->card_name, PA_NAMEREG_CARD)))
pa_card_suspend(card, !accessible, PA_SUSPEND_SESSION);
}
}
static void card_changed(struct userdata *u, struct udev_device *dev) {
struct device *d;
const char *path;
const char *t;
char *card_name, *args;
pa_module *m;
char *n;
pa_assert(u);
@ -135,44 +274,33 @@ static void card_changed(struct userdata *u, struct udev_device *dev) {
return;
}
d = pa_xnew0(struct device, 1);
d->path = pa_xstrdup(path);
d->module = PA_INVALID_INDEX;
if (!(t = udev_device_get_property_value(dev, "PULSE_NAME")))
if (!(t = udev_device_get_property_value(dev, "ID_ID")))
if (!(t = udev_device_get_property_value(dev, "ID_PATH")))
t = path_get_card_id(path);
n = pa_namereg_make_valid_name(t);
card_name = pa_sprintf_malloc("alsa_card.%s", n);
args = pa_sprintf_malloc("device_id=\"%s\" "
"name=\"%s\" "
"card_name=\"%s\" "
"tsched=%s "
"ignore_dB=%s "
"card_properties=\"module-udev-detect.discovered=1\"",
path_get_card_id(path),
n,
card_name,
pa_yes_no(u->use_tsched),
pa_yes_no(u->ignore_dB));
pa_log_debug("Loading module-alsa-card with arguments '%s'", args);
m = pa_module_load(u->core, "module-alsa-card", args);
pa_xfree(args);
if (m) {
pa_log_info("Card %s (%s) added.", path, n);
d = pa_xnew(struct device, 1);
d->path = pa_xstrdup(path);
d->card_name = card_name;
d->module = m->index;
d->accessible = TRUE;
pa_hashmap_put(u->devices, d->path, d);
} else
pa_xfree(card_name);
d->card_name = pa_sprintf_malloc("alsa_card.%s", n);
d->args = pa_sprintf_malloc("device_id=\"%s\" "
"name=\"%s\" "
"card_name=\"%s\" "
"tsched=%s "
"ignore_dB=%s "
"card_properties=\"module-udev-detect.discovered=1\"",
path_get_card_id(path),
n,
d->card_name,
pa_yes_no(u->use_tsched),
pa_yes_no(u->ignore_dB));
pa_xfree(n);
pa_hashmap_put(u->devices, d->path, d);
verify_access(u, d);
}
static void remove_card(struct userdata *u, struct udev_device *dev) {
@ -185,7 +313,10 @@ static void remove_card(struct userdata *u, struct udev_device *dev) {
return;
pa_log_info("Card %s removed.", d->path);
pa_module_unload_request_by_index(u->core, d->module, TRUE);
if (d->module != PA_INVALID_INDEX)
pa_module_unload_request_by_index(u->core, d->module, TRUE);
device_free(d);
}
@ -262,6 +393,34 @@ fail:
u->udev_io = NULL;
}
static pa_bool_t pcm_node_belongs_to_device(
struct device *d,
const char *node) {
char *cd;
pa_bool_t b;
cd = pa_sprintf_malloc("pcmC%sD", path_get_card_id(d->path));
b = pa_startswith(node, cd);
pa_xfree(cd);
return b;
}
static pa_bool_t control_node_belongs_to_device(
struct device *d,
const char *node) {
char *cd;
pa_bool_t b;
cd = pa_sprintf_malloc("controlC%s", path_get_card_id(d->path));
b = pa_streq(node, cd);
pa_xfree(cd);
return b;
}
static void inotify_cb(
pa_mainloop_api*a,
pa_io_event* e,
@ -275,10 +434,13 @@ static void inotify_cb(
} buf;
struct userdata *u = userdata;
static int type = 0;
pa_bool_t verify = FALSE, deleted = FALSE;
pa_bool_t deleted = FALSE;
struct device *d;
void *state;
for (;;) {
ssize_t r;
struct inotify_event *event;
pa_zero(buf);
if ((r = pa_read(fd, &buf, sizeof(buf), &type)) <= 0) {
@ -290,22 +452,51 @@ static void inotify_cb(
goto fail;
}
if ((buf.e.mask & IN_CLOSE_WRITE) && pa_startswith(buf.e.name, "pcmC"))
verify = TRUE;
event = &buf.e;
while (r > 0) {
size_t len;
if ((buf.e.mask & (IN_DELETE_SELF|IN_MOVE_SELF)))
deleted = TRUE;
if ((size_t) r < sizeof(struct inotify_event)) {
pa_log("read() too short.");
goto fail;
}
len = sizeof(struct inotify_event) + event->len;
if ((size_t) r < len) {
pa_log("Payload missing.");
goto fail;
}
/* From udev we get the guarantee that the control
* device's ACL is changed last. To avoid races when ACLs
* are changed we hence watch only the control device */
if (((event->mask & IN_ATTRIB) && pa_startswith(event->name, "controlC")))
PA_HASHMAP_FOREACH(d, u->devices, state)
if (control_node_belongs_to_device(d, event->name))
d->need_verify = TRUE;
/* ALSA doesn't really give us any guarantee on the closing
* order, so let's simply hope */
if (((event->mask & IN_CLOSE_WRITE) && pa_startswith(event->name, "pcmC")))
PA_HASHMAP_FOREACH(d, u->devices, state)
if (pcm_node_belongs_to_device(d, event->name))
d->need_verify = TRUE;
/* /dev/snd/ might have been removed */
if ((event->mask & (IN_DELETE_SELF|IN_MOVE_SELF)))
deleted = TRUE;
event = (struct inotify_event*) ((uint8_t*) event + len);
r -= len;
}
}
if (verify) {
struct device *d;
void *state;
pa_log_debug("Verifying access.");
PA_HASHMAP_FOREACH(d, u->devices, state)
PA_HASHMAP_FOREACH(d, u->devices, state)
if (d->need_verify) {
d->need_verify = FALSE;
verify_access(u, d);
}
}
if (!deleted)
return;
@ -335,7 +526,7 @@ static int setup_inotify(struct userdata *u) {
}
dev_snd = pa_sprintf_malloc("%s/snd", udev_get_dev_path(u->udev));
r = inotify_add_watch(u->inotify_fd, dev_snd, IN_CLOSE_WRITE|IN_DELETE_SELF|IN_MOVE_SELF);
r = inotify_add_watch(u->inotify_fd, dev_snd, IN_ATTRIB|IN_CLOSE_WRITE|IN_DELETE_SELF|IN_MOVE_SELF);
pa_xfree(dev_snd);
if (r < 0) {
@ -449,7 +640,7 @@ int pa__init(pa_module *m) {
udev_enumerate_unref(enumerate);
pa_log_info("Loaded %u modules.", pa_hashmap_size(u->devices));
pa_log_info("Found %u cards.", pa_hashmap_size(u->devices));
pa_modargs_free(ma);

View file

@ -56,7 +56,9 @@ pa_glib_mainloop *pa_glib_mainloop_new(GMainContext *c);
/** Free the GLIB main loop object */
void pa_glib_mainloop_free(pa_glib_mainloop* g);
/** Return the abstract main loop API vtable for the GLIB main loop object */
/** Return the abstract main loop API vtable for the GLIB main loop
object. No need of freeing the API as it is owned by the loop and
it is destroyed when this dies */
pa_mainloop_api* pa_glib_mainloop_get_api(pa_glib_mainloop *g);
PA_C_DECL_END

View file

@ -108,7 +108,9 @@ int pa_mainloop_iterate(pa_mainloop *m, int block, int *retval);
/** Run unlimited iterations of the main loop object until the main loop's quit() routine is called. */
int pa_mainloop_run(pa_mainloop *m, int *retval);
/** Return the abstract main loop abstraction layer vtable for this main loop. */
/** Return the abstract main loop abstraction layer vtable for this
main loop. No need of freeing the API as it is owned by the loop
and it is destroyed when this dies */
pa_mainloop_api* pa_mainloop_get_api(pa_mainloop*m);
/** Shutdown the main loop */

View file

@ -36,28 +36,27 @@
#include "sample.h"
static const size_t size_table[] = {
[PA_SAMPLE_U8] = 1,
[PA_SAMPLE_ULAW] = 1,
[PA_SAMPLE_ALAW] = 1,
[PA_SAMPLE_S16LE] = 2,
[PA_SAMPLE_S16BE] = 2,
[PA_SAMPLE_FLOAT32LE] = 4,
[PA_SAMPLE_FLOAT32BE] = 4,
[PA_SAMPLE_S32LE] = 4,
[PA_SAMPLE_S32BE] = 4,
[PA_SAMPLE_S24LE] = 3,
[PA_SAMPLE_S24BE] = 3,
[PA_SAMPLE_S24_32LE] = 4,
[PA_SAMPLE_S24_32BE] = 4
};
size_t pa_sample_size_of_format(pa_sample_format_t f) {
static const size_t table[] = {
[PA_SAMPLE_U8] = 1,
[PA_SAMPLE_ULAW] = 1,
[PA_SAMPLE_ALAW] = 1,
[PA_SAMPLE_S16LE] = 2,
[PA_SAMPLE_S16BE] = 2,
[PA_SAMPLE_FLOAT32LE] = 4,
[PA_SAMPLE_FLOAT32BE] = 4,
[PA_SAMPLE_S32LE] = 4,
[PA_SAMPLE_S32BE] = 4,
[PA_SAMPLE_S24LE] = 3,
[PA_SAMPLE_S24BE] = 3,
[PA_SAMPLE_S24_32LE] = 4,
[PA_SAMPLE_S24_32BE] = 4
};
pa_assert(f >= 0);
pa_assert(f < PA_SAMPLE_MAX);
return table[f];
return size_table[f];
}
size_t pa_sample_size(const pa_sample_spec *spec) {
@ -65,35 +64,35 @@ size_t pa_sample_size(const pa_sample_spec *spec) {
pa_assert(spec);
pa_return_val_if_fail(pa_sample_spec_valid(spec), 0);
return pa_sample_size_of_format(spec->format);
return size_table[spec->format];
}
size_t pa_frame_size(const pa_sample_spec *spec) {
pa_assert(spec);
pa_return_val_if_fail(pa_sample_spec_valid(spec), 0);
return pa_sample_size(spec) * spec->channels;
return size_table[spec->format] * spec->channels;
}
size_t pa_bytes_per_second(const pa_sample_spec *spec) {
pa_assert(spec);
pa_return_val_if_fail(pa_sample_spec_valid(spec), 0);
return spec->rate*pa_frame_size(spec);
return spec->rate * size_table[spec->format] * spec->channels;
}
pa_usec_t pa_bytes_to_usec(uint64_t length, const pa_sample_spec *spec) {
pa_assert(spec);
pa_return_val_if_fail(pa_sample_spec_valid(spec), 0);
return (((pa_usec_t) (length / pa_frame_size(spec)) * PA_USEC_PER_SEC) / spec->rate);
return (((pa_usec_t) (length / (size_table[spec->format] * spec->channels)) * PA_USEC_PER_SEC) / spec->rate);
}
size_t pa_usec_to_bytes(pa_usec_t t, const pa_sample_spec *spec) {
pa_assert(spec);
pa_return_val_if_fail(pa_sample_spec_valid(spec), 0);
return (size_t) (((t * spec->rate) / PA_USEC_PER_SEC)) * pa_frame_size(spec);
return (size_t) (((t * spec->rate) / PA_USEC_PER_SEC)) * (size_table[spec->format] * spec->channels);
}
pa_sample_spec* pa_sample_spec_init(pa_sample_spec *spec) {
@ -109,12 +108,12 @@ pa_sample_spec* pa_sample_spec_init(pa_sample_spec *spec) {
int pa_sample_spec_valid(const pa_sample_spec *spec) {
pa_assert(spec);
if (spec->rate <= 0 ||
if (PA_UNLIKELY (spec->rate <= 0 ||
spec->rate > PA_RATE_MAX ||
spec->channels <= 0 ||
spec->channels > PA_CHANNELS_MAX ||
spec->format >= PA_SAMPLE_MAX ||
spec->format < 0)
spec->format < 0))
return 0;
return 1;

View file

@ -299,7 +299,9 @@ void pa_threaded_mainloop_accept(pa_threaded_mainloop *m);
/** Return the return value as specified with the main loop's quit() routine. */
int pa_threaded_mainloop_get_retval(pa_threaded_mainloop *m);
/** Return the abstract main loop abstraction layer vtable for this main loop. */
/** Return the abstract main loop abstraction layer vtable for this
main loop. No need of freeing the API as it is owned by the loop
and it is destroyed when this dies */
pa_mainloop_api* pa_threaded_mainloop_get_api(pa_threaded_mainloop*m);
/** Returns non-zero when called from withing the event loop thread. \since 0.9.7 */

View file

@ -61,38 +61,40 @@
#include <pulsecore/log.h>
#include <pulsecore/core-util.h>
#include <pulsecore/macro.h>
#include <pulsecore/usergroup.h>
#include "util.h"
char *pa_get_user_name(char *s, size_t l) {
const char *p;
char *name = NULL;
#ifdef OS_IS_WIN32
char buf[1024];
#endif
#ifdef HAVE_PWD_H
struct passwd pw, *r;
struct passwd *r;
#endif
pa_assert(s);
pa_assert(l > 0);
if (!(p = (getuid() == 0 ? "root" : NULL)) &&
!(p = getenv("USER")) &&
!(p = getenv("LOGNAME")) &&
!(p = getenv("USERNAME"))) {
if ((p = (getuid() == 0 ? "root" : NULL)) ||
(p = getenv("USER")) ||
(p = getenv("LOGNAME")) ||
(p = getenv("USERNAME")))
{
name = pa_strlcpy(s, p, l);
} else {
#ifdef HAVE_PWD_H
#ifdef HAVE_GETPWUID_R
if (getpwuid_r(getuid(), &pw, buf, sizeof(buf), &r) != 0 || !r) {
#else
/* XXX Not thread-safe, but needed on OSes (e.g. FreeBSD 4.X)
* that do not support getpwuid_r. */
if ((r = getpwuid(getuid())) == NULL) {
#endif
if ((r = pa_getpwuid_malloc(getuid())) == NULL) {
pa_snprintf(s, l, "%lu", (unsigned long) getuid());
return s;
}
p = r->pw_name;
name = pa_strlcpy(s, r->pw_name, l);
pa_getpwuid_free(r);
#elif defined(OS_IS_WIN32) /* HAVE_PWD_H */
DWORD size = sizeof(buf);
@ -102,7 +104,7 @@ char *pa_get_user_name(char *s, size_t l) {
return NULL;
}
p = buf;
name = pa_strlcpy(s, buf, l);
#else /* HAVE_PWD_H */
@ -110,7 +112,7 @@ char *pa_get_user_name(char *s, size_t l) {
#endif /* HAVE_PWD_H */
}
return pa_strlcpy(s, p, l);
return name;
}
char *pa_get_host_name(char *s, size_t l) {
@ -126,11 +128,10 @@ char *pa_get_host_name(char *s, size_t l) {
}
char *pa_get_home_dir(char *s, size_t l) {
char *e;
char *e, *dir;
#ifdef HAVE_PWD_H
char buf[1024];
struct passwd pw, *r;
struct passwd *r;
#endif
pa_assert(s);
@ -143,22 +144,19 @@ char *pa_get_home_dir(char *s, size_t l) {
return pa_strlcpy(s, e, l);
#ifdef HAVE_PWD_H
errno = 0;
#ifdef HAVE_GETPWUID_R
if (getpwuid_r(getuid(), &pw, buf, sizeof(buf), &r) != 0 || !r) {
#else
/* XXX Not thread-safe, but needed on OSes (e.g. FreeBSD 4.X)
* that do not support getpwuid_r. */
if ((r = getpwuid(getuid())) == NULL) {
#endif
if ((r = pa_getpwuid_malloc(getuid())) == NULL) {
if (!errno)
errno = ENOENT;
return NULL;
}
return pa_strlcpy(s, r->pw_dir, l);
dir = pa_strlcpy(s, r->pw_dir, l);
pa_getpwuid_free(r);
return dir;
#else /* HAVE_PWD_H */
errno = ENOENT;

View file

@ -115,6 +115,7 @@
#include <pulsecore/macro.h>
#include <pulsecore/thread.h>
#include <pulsecore/strbuf.h>
#include <pulsecore/usergroup.h>
#include "core-util.h"
@ -969,54 +970,24 @@ fail:
/* Check whether the specified GID and the group name match */
static int is_group(gid_t gid, const char *name) {
struct group group, *result = NULL;
long n;
void *data;
struct group *group = NULL;
int r = -1;
#ifdef HAVE_GETGRGID_R
#ifdef _SC_GETGR_R_SIZE_MAX
n = sysconf(_SC_GETGR_R_SIZE_MAX);
#else
n = -1;
#endif
if (n <= 0)
n = 512;
data = pa_xmalloc((size_t) n);
errno = 0;
if (getgrgid_r(gid, &group, data, (size_t) n, &result) < 0 || !result) {
pa_log("getgrgid_r(%u): %s", (unsigned) gid, pa_cstrerror(errno));
if (!(group = pa_getgrgid_malloc(gid)))
{
if (!errno)
errno = ENOENT;
pa_log("pa_getgrgid_malloc(%u): %s", gid, pa_cstrerror(errno));
goto finish;
}
r = strcmp(name, result->gr_name) == 0;
r = strcmp(name, group->gr_name) == 0;
finish:
pa_xfree(data);
#else
/* XXX Not thread-safe, but needed on OSes (e.g. FreeBSD 4.X) that do not
* support getgrgid_r. */
errno = 0;
if (!(result = getgrgid(gid))) {
pa_log("getgrgid(%u): %s", gid, pa_cstrerror(errno));
if (!errno)
errno = ENOENT;
goto finish;
}
r = strcmp(name, result->gr_name) == 0;
finish:
#endif
pa_getgrgid_free(group);
return r;
}
@ -1065,38 +1036,12 @@ finish:
/* Check whether the specifc user id is a member of the specified group */
int pa_uid_in_group(uid_t uid, const char *name) {
char *g_buf, *p_buf;
long g_n, p_n;
struct group grbuf, *gr;
struct group *group = NULL;
char **i;
int r = -1;
#ifdef _SC_GETGR_R_SIZE_MAX
g_n = sysconf(_SC_GETGR_R_SIZE_MAX);
#else
g_n = -1;
#endif
if (g_n <= 0)
g_n = 512;
g_buf = pa_xmalloc((size_t) g_n);
#ifdef _SC_GETPW_R_SIZE_MAX
p_n = sysconf(_SC_GETPW_R_SIZE_MAX);
#else
p_n = -1;
#endif
if (p_n <= 0)
p_n = 512;
p_buf = pa_xmalloc((size_t) p_n);
errno = 0;
#ifdef HAVE_GETGRNAM_R
if (getgrnam_r(name, &grbuf, g_buf, (size_t) g_n, &gr) != 0 || !gr)
#else
if (!(gr = getgrnam(name)))
#endif
if (!(group = pa_getgrnam_malloc(name)))
{
if (!errno)
errno = ENOENT;
@ -1104,25 +1049,24 @@ int pa_uid_in_group(uid_t uid, const char *name) {
}
r = 0;
for (i = gr->gr_mem; *i; i++) {
struct passwd pwbuf, *pw;
for (i = group->gr_mem; *i; i++) {
struct passwd *pw = NULL;
#ifdef HAVE_GETPWNAM_R
if (getpwnam_r(*i, &pwbuf, p_buf, (size_t) p_n, &pw) != 0 || !pw)
#else
if (!(pw = getpwnam(*i)))
#endif
errno = 0;
if (!(pw = pa_getpwnam_malloc(*i)))
continue;
if (pw->pw_uid == uid) {
if (pw->pw_uid == uid)
r = 1;
pa_getpwnam_free(pw);
if (r == 1)
break;
}
}
finish:
pa_xfree(g_buf);
pa_xfree(p_buf);
pa_getgrnam_free(group);
return r;
}
@ -1130,26 +1074,10 @@ finish:
/* Get the GID of a gfiven group, return (gid_t) -1 on failure. */
gid_t pa_get_gid_of_group(const char *name) {
gid_t ret = (gid_t) -1;
char *g_buf;
long g_n;
struct group grbuf, *gr;
#ifdef _SC_GETGR_R_SIZE_MAX
g_n = sysconf(_SC_GETGR_R_SIZE_MAX);
#else
g_n = -1;
#endif
if (g_n <= 0)
g_n = 512;
g_buf = pa_xmalloc((size_t) g_n);
struct group *gr = NULL;
errno = 0;
#ifdef HAVE_GETGRNAM_R
if (getgrnam_r(name, &grbuf, g_buf, (size_t) g_n, &gr) != 0 || !gr)
#else
if (!(gr = getgrnam(name)))
#endif
if (!(gr = pa_getgrnam_malloc(name)))
{
if (!errno)
errno = ENOENT;
@ -1159,7 +1087,7 @@ gid_t pa_get_gid_of_group(const char *name) {
ret = gr->gr_gid;
finish:
pa_xfree(g_buf);
pa_getgrnam_free(gr);
return ret;
}
@ -2295,7 +2223,7 @@ int pa_close_all(int except_fd, ...) {
va_end(ap);
r = pa_close_allv(p);
free(p);
pa_xfree(p);
return r;
}
@ -2890,3 +2818,22 @@ void pa_reset_personality(void) {
#endif
}
#if defined(__linux__) && !defined(__OPTIMIZE__)
pa_bool_t pa_run_from_build_tree(void) {
char *rp;
pa_bool_t b = FALSE;
/* We abuse __OPTIMIZE__ as a check whether we are a debug build
* or not. */
if ((rp = pa_readlink("/proc/self/exe"))) {
b = pa_startswith(rp, PA_BUILDDIR);
pa_xfree(rp);
}
return b;
}
#endif

View file

@ -250,4 +250,8 @@ size_t pa_pipe_buf(int fd);
void pa_reset_personality(void);
#if defined(__linux__) && !defined(__OPTIMIZE__)
pa_bool_t pa_run_from_build_tree(void);
#endif
#endif

View file

@ -47,7 +47,7 @@
#include "core.h"
static PA_DEFINE_CHECK_TYPE(pa_core, pa_msgobject);
PA_DEFINE_PUBLIC_CLASS(pa_core, pa_msgobject);
static int core_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
pa_core *c = PA_CORE(o);

View file

@ -174,7 +174,7 @@ struct pa_core {
pa_hook hooks[PA_CORE_HOOK_MAX];
};
PA_DECLARE_CLASS(pa_core);
PA_DECLARE_PUBLIC_CLASS(pa_core);
#define PA_CORE(o) pa_core_cast(o)
enum {

139
src/pulsecore/cpu-arm.c Normal file
View file

@ -0,0 +1,139 @@
/***
This file is part of PulseAudio.
Copyright 2004-2006 Lennart Poettering
Copyright 2009 Wim Taymans <wim.taymans@collabora.co.uk>
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 <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pulse/xmalloc.h>
#include <pulsecore/log.h>
#include "cpu-arm.h"
#if defined (__arm__) && defined (__linux__)
#define MAX_BUFFER 4096
static char *
get_cpuinfo_line (char *cpuinfo, const char *tag) {
char *line, *end, *colon;
if (!(line = strstr (cpuinfo, tag)))
return NULL;
if (!(end = strchr (line, '\n')))
return NULL;
if (!(colon = strchr (line, ':')))
return NULL;
if (++colon >= end)
return NULL;
return pa_xstrndup (colon, end - colon);
}
static char *get_cpuinfo(void) {
char *cpuinfo;
int n, fd;
cpuinfo = pa_xmalloc(MAX_BUFFER);
if ((fd = open("/proc/cpuinfo", O_RDONLY)) < 0) {
pa_xfree(cpuinfo);
return NULL;
}
if ((n = pa_read(fd, cpuinfo, MAX_BUFFER-1)) < 0) {
pa_xfree(cpuinfo);
pa_close(fd);
return NULL;
}
cpuinfo[n] = 0;
pa_close(fd);
return cpuinfo;
}
#endif /* defined (__arm__) && defined (__linux__) */
void pa_cpu_init_arm (void) {
#if defined (__arm__)
#if defined (__linux__)
char *cpuinfo, *line;
int arch;
pa_cpu_arm_flag_t flags = 0;
/* We need to read the CPU flags from /proc/cpuinfo because there is no user
* space support to get the CPU features. This only works on linux AFAIK. */
if (!(cpuinfo = get_cpuinfo ())) {
pa_log ("Can't read cpuinfo");
return;
}
/* get the CPU architecture */
if ((line = get_cpuinfo_line (cpuinfo, "CPU architecture"))) {
arch = strtoul (line, NULL, 0);
if (arch >= 6)
flags |= PA_CPU_ARM_V6;
if (arch >= 7)
flags |= PA_CPU_ARM_V7;
pa_xfree(line);
}
/* get the CPU features */
if ((line = get_cpuinfo_line (cpuinfo, "Features"))) {
char *state = NULL, *current;
while ((current = pa_split_spaces (line, &state))) {
if (!strcmp (current, "vfp"))
flags |= PA_CPU_ARM_VFP;
else if (!strcmp (current, "edsp"))
flags |= PA_CPU_ARM_EDSP;
else if (!strcmp (current, "neon"))
flags |= PA_CPU_ARM_NEON;
else if (!strcmp (current, "vfpv3"))
flags |= PA_CPU_ARM_VFPV3;
pa_xfree(current);
}
}
pa_xfree(cpuinfo);
pa_log_info ("CPU flags: %s%s%s%s%s%s",
(flags & PA_CPU_ARM_V6) ? "V6 " : "",
(flags & PA_CPU_ARM_V7) ? "V7 " : "",
(flags & PA_CPU_ARM_VFP) ? "VFP " : "",
(flags & PA_CPU_ARM_EDSP) ? "EDSP " : "",
(flags & PA_CPU_ARM_NEON) ? "NEON " : "",
(flags & PA_CPU_ARM_VFPV3) ? "VFPV3 " : "");
#else /* defined (__linux__) */
pa_log ("ARM cpu features not yet supported on this OS");
#endif /* defined (__linux__) */
if (flags & PA_CPU_ARM_V6)
pa_volume_func_init_arm (flags);
#endif /* defined (__arm__) */
}

42
src/pulsecore/cpu-arm.h Normal file
View file

@ -0,0 +1,42 @@
#ifndef foocpuarmhfoo
#define foocpuarmhfoo
/***
This file is part of PulseAudio.
Copyright 2004-2006 Lennart Poettering
Copyright 2009 Wim Taymans <wim.taymans@collabora.co.uk>
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.
***/
#include <stdint.h>
typedef enum pa_cpu_arm_flag {
PA_CPU_ARM_V6 = (1 << 0),
PA_CPU_ARM_V7 = (1 << 1),
PA_CPU_ARM_VFP = (1 << 2),
PA_CPU_ARM_EDSP = (1 << 3),
PA_CPU_ARM_NEON = (1 << 4),
PA_CPU_ARM_VFPV3 = (1 << 5)
} pa_cpu_arm_flag_t;
void pa_cpu_init_arm (void);
/* some optimized functions */
void pa_volume_func_init_arm(pa_cpu_arm_flag_t flags);
#endif /* foocpuarmhfoo */

122
src/pulsecore/cpu-x86.c Normal file
View file

@ -0,0 +1,122 @@
/***
This file is part of PulseAudio.
Copyright 2004-2006 Lennart Poettering
Copyright 2009 Wim Taymans <wim.taymans@collabora.co.uk>
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 <stdint.h>
#include <pulsecore/log.h>
#include "cpu-x86.h"
#if defined (__i386__) || defined (__amd64__)
static void
get_cpuid (uint32_t op, uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d)
{
__asm__ __volatile__ (
" push %%"PA_REG_b" \n\t"
" cpuid \n\t"
" mov %%ebx, %%esi \n\t"
" pop %%"PA_REG_b" \n\t"
: "=a" (*a), "=S" (*b), "=c" (*c), "=d" (*d)
: "0" (op)
);
}
#endif
void pa_cpu_init_x86 (void) {
#if defined (__i386__) || defined (__amd64__)
uint32_t eax, ebx, ecx, edx;
uint32_t level;
pa_cpu_x86_flag_t flags = 0;
/* get standard level */
get_cpuid (0x00000000, &level, &ebx, &ecx, &edx);
if (level >= 1) {
get_cpuid (0x00000001, &eax, &ebx, &ecx, &edx);
if (edx & (1<<23))
flags |= PA_CPU_X86_MMX;
if (edx & (1<<25))
flags |= PA_CPU_X86_SSE;
if (edx & (1<<26))
flags |= PA_CPU_X86_SSE2;
if (ecx & (1<<0))
flags |= PA_CPU_X86_SSE3;
if (ecx & (1<<9))
flags |= PA_CPU_X86_SSSE3;
if (ecx & (1<<19))
flags |= PA_CPU_X86_SSE4_1;
if (ecx & (1<<20))
flags |= PA_CPU_X86_SSE4_2;
}
/* get extended level */
get_cpuid (0x80000000, &level, &ebx, &ecx, &edx);
if (level >= 0x80000001) {
get_cpuid (0x80000001, &eax, &ebx, &ecx, &edx);
if (edx & (1<<22))
flags |= PA_CPU_X86_MMXEXT;
if (edx & (1<<23))
flags |= PA_CPU_X86_MMX;
if (edx & (1<<30))
flags |= PA_CPU_X86_3DNOWEXT;
if (edx & (1<<31))
flags |= PA_CPU_X86_3DNOW;
}
pa_log_info ("CPU flags: %s%s%s%s%s%s%s%s%s%s",
(flags & PA_CPU_X86_MMX) ? "MMX " : "",
(flags & PA_CPU_X86_SSE) ? "SSE " : "",
(flags & PA_CPU_X86_SSE2) ? "SSE2 " : "",
(flags & PA_CPU_X86_SSE3) ? "SSE3 " : "",
(flags & PA_CPU_X86_SSSE3) ? "SSSE3 " : "",
(flags & PA_CPU_X86_SSE4_1) ? "SSE4_1 " : "",
(flags & PA_CPU_X86_SSE4_2) ? "SSE4_2 " : "",
(flags & PA_CPU_X86_MMXEXT) ? "MMXEXT " : "",
(flags & PA_CPU_X86_3DNOW) ? "3DNOW " : "",
(flags & PA_CPU_X86_3DNOWEXT) ? "3DNOWEXT " : "");
/* activate various optimisations */
if (flags & PA_CPU_X86_MMX) {
pa_volume_func_init_mmx (flags);
pa_remap_func_init_mmx (flags);
}
if (flags & PA_CPU_X86_SSE)
pa_volume_func_init_sse (flags);
#endif /* defined (__i386__) || defined (__amd64__) */
}

68
src/pulsecore/cpu-x86.h Normal file
View file

@ -0,0 +1,68 @@
#ifndef foocpux86hfoo
#define foocpux86hfoo
/***
This file is part of PulseAudio.
Copyright 2004-2006 Lennart Poettering
Copyright 2009 Wim Taymans <wim.taymans@collabora.co.uk>
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.
***/
#include <stdint.h>
typedef enum pa_cpu_x86_flag {
PA_CPU_X86_MMX = (1 << 0),
PA_CPU_X86_MMXEXT = (1 << 1),
PA_CPU_X86_SSE = (1 << 2),
PA_CPU_X86_SSE2 = (1 << 3),
PA_CPU_X86_SSE3 = (1 << 4),
PA_CPU_X86_SSSE3 = (1 << 5),
PA_CPU_X86_SSE4_1 = (1 << 6),
PA_CPU_X86_SSE4_2 = (1 << 7),
PA_CPU_X86_3DNOW = (1 << 8),
PA_CPU_X86_3DNOWEXT = (1 << 9)
} pa_cpu_x86_flag_t;
void pa_cpu_init_x86 (void);
#if defined (__i386__)
typedef int32_t pa_reg_x86;
#define PA_REG_a "eax"
#define PA_REG_b "ebx"
#define PA_REG_c "ecx"
#define PA_REG_d "edx"
#define PA_REG_D "edi"
#define PA_REG_S "esi"
#elif defined (__amd64__)
typedef int64_t pa_reg_x86;
#define PA_REG_a "rax"
#define PA_REG_b "rbx"
#define PA_REG_c "rcx"
#define PA_REG_d "rdx"
#define PA_REG_D "rdi"
#define PA_REG_S "rsi"
#endif
/* some optimized functions */
void pa_volume_func_init_mmx(pa_cpu_x86_flag_t flags);
void pa_volume_func_init_sse(pa_cpu_x86_flag_t flags);
void pa_remap_func_init_mmx(pa_cpu_x86_flag_t flags);
#endif /* foocpux86hfoo */

View file

@ -26,22 +26,22 @@
#include "msgobject.h"
PA_DEFINE_CHECK_TYPE(pa_msgobject, pa_object);
PA_DEFINE_PUBLIC_CLASS(pa_msgobject, pa_object);
pa_msgobject *pa_msgobject_new_internal(size_t size, const char *type_name, int (*check_type)(const char *type_name)) {
pa_msgobject *pa_msgobject_new_internal(size_t size, const char *type_id, pa_bool_t (*check_type)(const char *type_name)) {
pa_msgobject *o;
pa_assert(size > sizeof(pa_msgobject));
pa_assert(type_name);
pa_assert(type_id);
if (!check_type)
check_type = pa_msgobject_check_type;
pa_assert(check_type(type_name));
pa_assert(check_type("pa_object"));
pa_assert(check_type("pa_msgobject"));
pa_assert(check_type(type_id));
pa_assert(check_type(pa_object_type_id));
pa_assert(check_type(pa_msgobject_type_id));
o = PA_MSGOBJECT(pa_object_new_internal(size, type_name, check_type));
o = PA_MSGOBJECT(pa_object_new_internal(size, type_id, check_type));
o->process_msg = NULL;
return o;
}

View file

@ -38,15 +38,13 @@ struct pa_msgobject {
int (*process_msg)(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
};
pa_msgobject *pa_msgobject_new_internal(size_t size, const char *type_name, int (*check_type)(const char *type_name));
pa_msgobject *pa_msgobject_new_internal(size_t size, const char *type_id, pa_bool_t (*check_type)(const char *type_name));
int pa_msgobject_check_type(const char *type);
#define pa_msgobject_new(type) ((type*) pa_msgobject_new_internal(sizeof(type), #type, type##_check_type))
#define pa_msgobject_new(type) ((type*) pa_msgobject_new_internal(sizeof(type), type##_type_id, type##_check_type))
#define pa_msgobject_free ((void (*) (pa_msgobject* o)) pa_object_free)
#define PA_MSGOBJECT(o) pa_msgobject_cast(o)
PA_DECLARE_CLASS(pa_msgobject);
PA_DECLARE_PUBLIC_CLASS(pa_msgobject);
#endif

View file

@ -28,21 +28,23 @@
#include "object.h"
pa_object *pa_object_new_internal(size_t size, const char *type_name, int (*check_type)(const char *type_name)) {
const char pa_object_type_id[] = "pa_object";
pa_object *pa_object_new_internal(size_t size, const char *type_id, pa_bool_t (*check_type)(const char *type_id)) {
pa_object *o;
pa_assert(size > sizeof(pa_object));
pa_assert(type_name);
pa_assert(type_id);
if (!check_type)
check_type = pa_object_check_type;
pa_assert(check_type(type_name));
pa_assert(check_type("pa_object"));
pa_assert(check_type(type_id));
pa_assert(check_type(pa_object_type_id));
o = pa_xmalloc(size);
PA_REFCNT_INIT(o);
o->type_name = type_name;
o->type_id = type_id;
o->free = pa_object_free;
o->check_type = check_type;
@ -65,8 +67,8 @@ void pa_object_unref(pa_object *o) {
}
}
int pa_object_check_type(const char *type_name) {
pa_assert(type_name);
pa_bool_t pa_object_check_type(const char *type_id) {
pa_assert(type_id);
return pa_streq(type_name, "pa_object");
return type_id == pa_object_type_id;
}

View file

@ -34,21 +34,23 @@ typedef struct pa_object pa_object;
struct pa_object {
PA_REFCNT_DECLARE;
const char *type_name;
const char *type_id;
void (*free)(pa_object *o);
int (*check_type)(const char *type_name);
pa_bool_t (*check_type)(const char *type_name);
};
pa_object *pa_object_new_internal(size_t size, const char *type_name, int (*check_type)(const char *type_name));
#define pa_object_new(type) ((type*) pa_object_new_internal(sizeof(type), #type, type##_check_type)
pa_object *pa_object_new_internal(size_t size, const char *type_id, pa_bool_t (*check_type)(const char *type_id));
#define pa_object_new(type) ((type*) pa_object_new_internal(sizeof(type), type##_type_id, type##_check_type)
#define pa_object_free ((void (*) (pa_object* _obj)) pa_xfree)
int pa_object_check_type(const char *type);
pa_bool_t pa_object_check_type(const char *type_id);
static inline int pa_object_isinstance(void *o) {
extern const char pa_object_type_id[];
static inline pa_bool_t pa_object_isinstance(void *o) {
pa_object *obj = (pa_object*) o;
return obj ? obj->check_type("pa_object") : 0;
return obj ? obj->check_type(pa_object_type_id) : TRUE;
}
pa_object *pa_object_ref(pa_object *o);
@ -60,7 +62,7 @@ static inline int pa_object_refcnt(pa_object *o) {
static inline pa_object* pa_object_cast(void *o) {
pa_object *obj = (pa_object*) o;
pa_assert(!obj || obj->check_type("pa_object"));
pa_assert(!obj || obj->check_type(pa_object_type_id));
return obj;
}
@ -68,10 +70,10 @@ static inline pa_object* pa_object_cast(void *o) {
#define PA_OBJECT(o) pa_object_cast(o)
#define PA_DECLARE_CLASS(c) \
static inline int c##_isinstance(void *o) { \
#define PA_DECLARE_CLASS_COMMON(c) \
static inline pa_bool_t c##_isinstance(void *o) { \
pa_object *obj = (pa_object*) o; \
return obj ? obj->check_type(#c) : 1; \
return obj ? obj->check_type(c##_type_id) : TRUE; \
} \
static inline c* c##_cast(void *o) { \
pa_assert(c##_isinstance(o)); \
@ -91,12 +93,27 @@ static inline pa_object* pa_object_cast(void *o) {
} \
struct __stupid_useless_struct_to_allow_trailing_semicolon
#define PA_DEFINE_CHECK_TYPE(c, parent) \
int c##_check_type(const char *type) { \
pa_assert(type); \
if (strcmp(type, #c) == 0) \
return 1; \
return parent##_check_type(type); \
#define PA_DECLARE_PUBLIC_CLASS(c) \
extern const char c##_type_id[]; \
PA_DECLARE_CLASS_COMMON(c); \
pa_bool_t c##_check_type(const char *type_id)
#define PA_DEFINE_PUBLIC_CLASS(c, parent) \
const char c##_type_id[] = #c; \
pa_bool_t c##_check_type(const char *type_id) { \
if (type_id == c##_type_id) \
return TRUE; \
return parent##_check_type(type_id); \
} \
struct __stupid_useless_struct_to_allow_trailing_semicolon
#define PA_DEFINE_PRIVATE_CLASS(c, parent) \
static const char c##_type_id[] = #c; \
PA_DECLARE_CLASS_COMMON(c); \
static pa_bool_t c##_check_type(const char *type_id) { \
if (type_id == c##_type_id) \
return TRUE; \
return parent##_check_type(type_id); \
} \
struct __stupid_useless_struct_to_allow_trailing_semicolon

View file

@ -47,9 +47,8 @@ enum {
MEMBLOCKQ_STREAM_MESSAGE_UNLINK,
};
PA_DECLARE_CLASS(memblockq_stream);
PA_DEFINE_PRIVATE_CLASS(memblockq_stream, pa_msgobject);
#define MEMBLOCKQ_STREAM(o) (memblockq_stream_cast(o))
static PA_DEFINE_CHECK_TYPE(memblockq_stream, pa_msgobject);
static void memblockq_stream_unlink(memblockq_stream *u) {
pa_assert(u);

View file

@ -120,9 +120,8 @@ typedef struct connection {
pa_time_event *auth_timeout_event;
} connection;
PA_DECLARE_CLASS(connection);
PA_DEFINE_PRIVATE_CLASS(connection, pa_msgobject);
#define CONNECTION(o) (connection_cast(o))
static PA_DEFINE_CHECK_TYPE(connection, pa_msgobject);
struct pa_esound_protocol {
PA_REFCNT_DECLARE;

View file

@ -98,17 +98,15 @@ typedef struct record_stream {
pa_usec_t current_source_latency;
} record_stream;
PA_DECLARE_CLASS(record_stream);
#define RECORD_STREAM(o) (record_stream_cast(o))
static PA_DEFINE_CHECK_TYPE(record_stream, pa_msgobject);
PA_DEFINE_PRIVATE_CLASS(record_stream, pa_msgobject);
typedef struct output_stream {
pa_msgobject parent;
} output_stream;
PA_DECLARE_CLASS(output_stream);
#define OUTPUT_STREAM(o) (output_stream_cast(o))
static PA_DEFINE_CHECK_TYPE(output_stream, pa_msgobject);
PA_DEFINE_PRIVATE_CLASS(output_stream, pa_msgobject);
typedef struct playback_stream {
output_stream parent;
@ -138,9 +136,8 @@ typedef struct playback_stream {
uint64_t playing_for, underrun_for;
} playback_stream;
PA_DECLARE_CLASS(playback_stream);
#define PLAYBACK_STREAM(o) (playback_stream_cast(o))
static PA_DEFINE_CHECK_TYPE(playback_stream, output_stream);
PA_DEFINE_PRIVATE_CLASS(playback_stream, output_stream);
typedef struct upload_stream {
output_stream parent;
@ -156,9 +153,8 @@ typedef struct upload_stream {
pa_proplist *proplist;
} upload_stream;
PA_DECLARE_CLASS(upload_stream);
#define UPLOAD_STREAM(o) (upload_stream_cast(o))
static PA_DEFINE_CHECK_TYPE(upload_stream, output_stream);
PA_DEFINE_PRIVATE_CLASS(upload_stream, output_stream);
struct pa_native_connection {
pa_msgobject parent;
@ -176,9 +172,8 @@ struct pa_native_connection {
pa_time_event *auth_timeout_event;
};
PA_DECLARE_CLASS(pa_native_connection);
#define PA_NATIVE_CONNECTION(o) (pa_native_connection_cast(o))
static PA_DEFINE_CHECK_TYPE(pa_native_connection, pa_msgobject);
PA_DEFINE_PRIVATE_CLASS(pa_native_connection, pa_msgobject);
struct pa_native_protocol {
PA_REFCNT_DECLARE;

View file

@ -69,9 +69,8 @@ typedef struct connection {
} playback;
} connection;
PA_DECLARE_CLASS(connection);
PA_DEFINE_PRIVATE_CLASS(connection, pa_msgobject);
#define CONNECTION(o) (connection_cast(o))
static PA_DEFINE_CHECK_TYPE(connection, pa_msgobject);
struct pa_simple_protocol {
PA_REFCNT_DECLARE;

204
src/pulsecore/remap.c Normal file
View file

@ -0,0 +1,204 @@
/***
This file is part of PulseAudio.
Copyright 2004-2006 Lennart Poettering
Copyright 2009 Wim Taymans <wim.taymans@collabora.co.uk.com>
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 <string.h>
#include <pulse/sample.h>
#include <pulsecore/log.h>
#include <pulsecore/macro.h>
#include "remap.h"
static void remap_mono_to_stereo_c (pa_remap_t *m, void *dst, const void *src, unsigned n) {
unsigned i;
switch (*m->format) {
case PA_SAMPLE_FLOAT32NE:
{
float *d, *s;
d = (float *) dst;
s = (float *) src;
for (i = n >> 2; i; i--) {
d[0] = d[1] = s[0];
d[2] = d[3] = s[1];
d[4] = d[5] = s[2];
d[6] = d[7] = s[3];
s += 4;
d += 8;
}
for (i = n & 3; i; i--) {
d[0] = d[1] = s[0];
s++;
d += 2;
}
break;
}
case PA_SAMPLE_S16NE:
{
int16_t *d, *s;
d = (int16_t *) dst;
s = (int16_t *) src;
for (i = n >> 2; i; i--) {
d[0] = d[1] = s[0];
d[2] = d[3] = s[1];
d[4] = d[5] = s[2];
d[6] = d[7] = s[3];
s += 4;
d += 8;
}
for (i = n & 3; i; i--) {
d[0] = d[1] = s[0];
s++;
d += 2;
}
break;
}
default:
pa_assert_not_reached();
}
}
static void remap_channels_matrix_c (pa_remap_t *m, void *dst, const void *src, unsigned n) {
unsigned oc, ic, i;
unsigned n_ic, n_oc;
n_ic = m->i_ss->channels;
n_oc = m->o_ss->channels;
switch (*m->format) {
case PA_SAMPLE_FLOAT32NE:
{
float *d, *s;
memset(dst, 0, n * sizeof (float) * n_oc);
for (oc = 0; oc < n_oc; oc++) {
for (ic = 0; ic < n_ic; ic++) {
float vol;
vol = m->map_table_f[oc][ic];
if (vol <= 0.0)
continue;
d = (float *)dst + oc;
s = (float *)src + ic;
if (vol >= 1.0) {
for (i = n; i > 0; i--, s += n_ic, d += n_oc)
*d += *s;
} else {
for (i = n; i > 0; i--, s += n_ic, d += n_oc)
*d += *s * vol;
}
}
}
break;
}
case PA_SAMPLE_S16NE:
{
int16_t *d, *s;
memset(dst, 0, n * sizeof (int16_t) * n_oc);
for (oc = 0; oc < n_oc; oc++) {
for (ic = 0; ic < n_ic; ic++) {
int32_t vol;
vol = m->map_table_i[oc][ic];
if (vol <= 0)
continue;
d = (int16_t *)dst + oc;
s = (int16_t *)src + ic;
if (vol >= 0x10000) {
for (i = n; i > 0; i--, s += n_ic, d += n_oc)
*d += *s;
} else {
for (i = n; i > 0; i--, s += n_ic, d += n_oc)
*d += (int16_t) (((int32_t)*s * vol) >> 16);
}
}
}
break;
}
default:
pa_assert_not_reached();
}
}
/* set the function that will execute the remapping based on the matrices */
static void init_remap_c (pa_remap_t *m) {
unsigned n_oc, n_ic;
n_oc = m->o_ss->channels;
n_ic = m->i_ss->channels;
/* find some common channel remappings, fall back to full matrix operation. */
if (n_ic == 1 && n_oc == 2 &&
m->map_table_f[0][0] >= 1.0 && m->map_table_f[1][0] >= 1.0) {
m->do_remap = (pa_do_remap_func_t) remap_mono_to_stereo_c;
pa_log_info("Using mono to stereo remapping");
} else {
m->do_remap = (pa_do_remap_func_t) remap_channels_matrix_c;
pa_log_info("Using generic matrix remapping");
}
}
/* default C implementation */
static pa_init_remap_func_t remap_func = init_remap_c;
void pa_init_remap (pa_remap_t *m) {
pa_assert (remap_func);
m->do_remap = NULL;
/* call the installed remap init function */
remap_func (m);
if (m->do_remap == NULL) {
/* nothing was installed, fallback to C version */
init_remap_c (m);
}
}
pa_init_remap_func_t pa_get_init_remap_func(void) {
return remap_func;
}
void pa_set_init_remap_func(pa_init_remap_func_t func) {
remap_func = func;
}

48
src/pulsecore/remap.h Normal file
View file

@ -0,0 +1,48 @@
#ifndef fooremapfoo
#define fooremapfoo
/***
This file is part of PulseAudio.
Copyright 2004-2006 Lennart Poettering
Copyright 2009 Wim Taymans <wim.taymans@collabora.co.uk.com>
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.
***/
#include <pulse/sample.h>
typedef struct pa_remap pa_remap_t;
typedef void (*pa_do_remap_func_t) (pa_remap_t *m, void *d, const void *s, unsigned n);
struct pa_remap {
pa_sample_format_t *format;
pa_sample_spec *i_ss, *o_ss;
float map_table_f[PA_CHANNELS_MAX][PA_CHANNELS_MAX];
int32_t map_table_i[PA_CHANNELS_MAX][PA_CHANNELS_MAX];
pa_do_remap_func_t do_remap;
};
void pa_init_remap (pa_remap_t *m);
/* custom installation of init functions */
typedef void (*pa_init_remap_func_t) (pa_remap_t *m);
pa_init_remap_func_t pa_get_init_remap_func(void);
void pa_set_init_remap_func(pa_init_remap_func_t func);
#endif /* fooremapfoo */

148
src/pulsecore/remap_mmx.c Normal file
View file

@ -0,0 +1,148 @@
/***
This file is part of PulseAudio.
Copyright 2004-2006 Lennart Poettering
Copyright 2009 Wim Taymans <wim.taymans@collabora.co.uk.com>
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 <string.h>
#include <pulse/sample.h>
#include <pulsecore/log.h>
#include <pulsecore/macro.h>
#include "cpu-x86.h"
#include "remap.h"
#define LOAD_SAMPLES \
" movq (%1), %%mm0 \n\t" \
" movq 8(%1), %%mm2 \n\t" \
" movq 16(%1), %%mm4 \n\t" \
" movq 24(%1), %%mm6 \n\t" \
" movq %%mm0, %%mm1 \n\t" \
" movq %%mm2, %%mm3 \n\t" \
" movq %%mm4, %%mm5 \n\t" \
" movq %%mm6, %%mm7 \n\t"
#define UNPACK_SAMPLES(s) \
" punpckl"#s" %%mm0, %%mm0 \n\t" \
" punpckh"#s" %%mm1, %%mm1 \n\t" \
" punpckl"#s" %%mm2, %%mm2 \n\t" \
" punpckh"#s" %%mm3, %%mm3 \n\t" \
" punpckl"#s" %%mm4, %%mm4 \n\t" \
" punpckh"#s" %%mm5, %%mm5 \n\t" \
" punpckl"#s" %%mm6, %%mm6 \n\t" \
" punpckh"#s" %%mm7, %%mm7 \n\t" \
#define STORE_SAMPLES \
" movq %%mm0, (%0) \n\t" \
" movq %%mm1, 8(%0) \n\t" \
" movq %%mm2, 16(%0) \n\t" \
" movq %%mm3, 24(%0) \n\t" \
" movq %%mm4, 32(%0) \n\t" \
" movq %%mm5, 40(%0) \n\t" \
" movq %%mm6, 48(%0) \n\t" \
" movq %%mm7, 56(%0) \n\t" \
" add $32, %1 \n\t" \
" add $64, %0 \n\t"
#define HANDLE_SINGLE(s) \
" movd (%1), %%mm0 \n\t" \
" movq %%mm0, %%mm1 \n\t" \
" punpckl"#s" %%mm0, %%mm0 \n\t" \
" movq %%mm0, (%0) \n\t" \
" add $4, %1 \n\t" \
" add $8, %0 \n\t"
#define MONO_TO_STEREO(s) \
" mov %3, %2 \n\t" \
" sar $3, %2 \n\t" \
" cmp $0, %2 \n\t" \
" je 2f \n\t" \
"1: \n\t" \
LOAD_SAMPLES \
UNPACK_SAMPLES(s) \
STORE_SAMPLES \
" dec %2 \n\t" \
" jne 1b \n\t" \
"2: \n\t" \
" mov %3, %2 \n\t" \
" and $7, %2 \n\t" \
" je 4f \n\t" \
"3: \n\t" \
HANDLE_SINGLE(s) \
" dec %2 \n\t" \
" jne 3b \n\t" \
"4: \n\t" \
" emms \n\t"
static void remap_mono_to_stereo_mmx (pa_remap_t *m, void *dst, const void *src, unsigned n) {
pa_reg_x86 temp;
switch (*m->format) {
case PA_SAMPLE_FLOAT32NE:
{
__asm__ __volatile__ (
MONO_TO_STEREO(dq) /* do doubles to quads */
: "+r" (dst), "+r" (src), "=&r" (temp)
: "r" ((pa_reg_x86)n)
: "cc"
);
break;
}
case PA_SAMPLE_S16NE:
{
__asm__ __volatile__ (
MONO_TO_STEREO(wd) /* do words to doubles */
: "+r" (dst), "+r" (src), "=&r" (temp)
: "r" ((pa_reg_x86)n)
: "cc"
);
break;
}
default:
pa_assert_not_reached();
}
}
/* set the function that will execute the remapping based on the matrices */
static void init_remap_mmx (pa_remap_t *m) {
unsigned n_oc, n_ic;
n_oc = m->o_ss->channels;
n_ic = m->i_ss->channels;
/* find some common channel remappings, fall back to full matrix operation. */
if (n_ic == 1 && n_oc == 2 &&
m->map_table_f[0][0] >= 1.0 && m->map_table_f[1][0] >= 1.0) {
m->do_remap = (pa_do_remap_func_t) remap_mono_to_stereo_mmx;
pa_log_info("Using MMX mono to stereo remapping");
}
}
void pa_remap_func_init_mmx (pa_cpu_x86_flag_t flags) {
#if defined (__i386__) || defined (__amd64__)
pa_log_info("Initialising MMX optimized remappers.");
pa_set_init_remap_func ((pa_init_remap_func_t) init_remap_mmx);
#endif /* defined (__i386__) || defined (__amd64__) */
}

View file

@ -31,9 +31,6 @@
#include <speex/speex_resampler.h>
#include <liboil/liboilfuncs.h>
#include <liboil/liboil.h>
#include <pulse/xmalloc.h>
#include <pulsecore/sconv.h>
#include <pulsecore/log.h>
@ -43,6 +40,7 @@
#include "ffmpeg/avcodec.h"
#include "resampler.h"
#include "remap.h"
/* Number of samples of extra space we allow the resamplers to return */
#define EXTRA_FRAMES 128
@ -64,7 +62,7 @@ struct pa_resampler {
pa_convert_func_t to_work_format_func;
pa_convert_func_t from_work_format_func;
float map_table[PA_CHANNELS_MAX][PA_CHANNELS_MAX];
pa_remap_t remap;
pa_bool_t map_required;
void (*impl_free)(pa_resampler *r);
@ -214,6 +212,11 @@ pa_resampler* pa_resampler_new(
r->i_ss = *a;
r->o_ss = *b;
/* set up the remap structure */
r->remap.i_ss = &r->i_ss;
r->remap.o_ss = &r->o_ss;
r->remap.format = &r->work_format;
if (am)
r->i_cm = *am;
else if (!pa_channel_map_init_auto(&r->i_cm, r->i_ss.channels, PA_CHANNEL_MAP_DEFAULT))
@ -580,32 +583,41 @@ static int front_rear_side(pa_channel_position_t p) {
static void calc_map_table(pa_resampler *r) {
unsigned oc, ic;
unsigned n_oc, n_ic;
pa_bool_t ic_connected[PA_CHANNELS_MAX];
pa_bool_t remix;
pa_strbuf *s;
char *t;
pa_remap_t *m;
pa_assert(r);
if (!(r->map_required = (r->i_ss.channels != r->o_ss.channels || (!(r->flags & PA_RESAMPLER_NO_REMAP) && !pa_channel_map_equal(&r->i_cm, &r->o_cm)))))
return;
memset(r->map_table, 0, sizeof(r->map_table));
m = &r->remap;
n_oc = r->o_ss.channels;
n_ic = r->i_ss.channels;
memset(m->map_table_f, 0, sizeof(m->map_table_f));
memset(m->map_table_i, 0, sizeof(m->map_table_i));
memset(ic_connected, 0, sizeof(ic_connected));
remix = (r->flags & (PA_RESAMPLER_NO_REMAP|PA_RESAMPLER_NO_REMIX)) == 0;
for (oc = 0; oc < r->o_ss.channels; oc++) {
for (oc = 0; oc < n_oc; oc++) {
pa_bool_t oc_connected = FALSE;
pa_channel_position_t b = r->o_cm.map[oc];
for (ic = 0; ic < r->i_ss.channels; ic++) {
for (ic = 0; ic < n_ic; ic++) {
pa_channel_position_t a = r->i_cm.map[ic];
if (r->flags & PA_RESAMPLER_NO_REMAP) {
/* We shall not do any remapping. Hence, just check by index */
if (ic == oc)
r->map_table[oc][ic] = 1.0;
m->map_table_f[oc][ic] = 1.0;
continue;
}
@ -614,7 +626,7 @@ static void calc_map_table(pa_resampler *r) {
/* We shall not do any remixing. Hence, just check by name */
if (a == b)
r->map_table[oc][ic] = 1.0;
m->map_table_f[oc][ic] = 1.0;
continue;
}
@ -689,7 +701,7 @@ static void calc_map_table(pa_resampler *r) {
*/
if (a == b || a == PA_CHANNEL_POSITION_MONO || b == PA_CHANNEL_POSITION_MONO) {
r->map_table[oc][ic] = 1.0;
m->map_table_f[oc][ic] = 1.0;
oc_connected = TRUE;
ic_connected[ic] = TRUE;
@ -707,14 +719,14 @@ static void calc_map_table(pa_resampler *r) {
/* We are not connected and on the left side, let's
* average all left side input channels. */
for (ic = 0; ic < r->i_ss.channels; ic++)
for (ic = 0; ic < n_ic; ic++)
if (on_left(r->i_cm.map[ic]))
n++;
if (n > 0)
for (ic = 0; ic < r->i_ss.channels; ic++)
for (ic = 0; ic < n_ic; ic++)
if (on_left(r->i_cm.map[ic])) {
r->map_table[oc][ic] = 1.0f / (float) n;
m->map_table_f[oc][ic] = 1.0f / (float) n;
ic_connected[ic] = TRUE;
}
@ -728,14 +740,14 @@ static void calc_map_table(pa_resampler *r) {
/* We are not connected and on the right side, let's
* average all right side input channels. */
for (ic = 0; ic < r->i_ss.channels; ic++)
for (ic = 0; ic < n_ic; ic++)
if (on_right(r->i_cm.map[ic]))
n++;
if (n > 0)
for (ic = 0; ic < r->i_ss.channels; ic++)
for (ic = 0; ic < n_ic; ic++)
if (on_right(r->i_cm.map[ic])) {
r->map_table[oc][ic] = 1.0f / (float) n;
m->map_table_f[oc][ic] = 1.0f / (float) n;
ic_connected[ic] = TRUE;
}
@ -749,14 +761,14 @@ static void calc_map_table(pa_resampler *r) {
/* We are not connected and at the center. Let's
* average all center input channels. */
for (ic = 0; ic < r->i_ss.channels; ic++)
for (ic = 0; ic < n_ic; ic++)
if (on_center(r->i_cm.map[ic]))
n++;
if (n > 0) {
for (ic = 0; ic < r->i_ss.channels; ic++)
for (ic = 0; ic < n_ic; ic++)
if (on_center(r->i_cm.map[ic])) {
r->map_table[oc][ic] = 1.0f / (float) n;
m->map_table_f[oc][ic] = 1.0f / (float) n;
ic_connected[ic] = TRUE;
}
} else {
@ -766,14 +778,14 @@ static void calc_map_table(pa_resampler *r) {
n = 0;
for (ic = 0; ic < r->i_ss.channels; ic++)
for (ic = 0; ic < n_ic; ic++)
if (on_left(r->i_cm.map[ic]) || on_right(r->i_cm.map[ic]))
n++;
if (n > 0)
for (ic = 0; ic < r->i_ss.channels; ic++)
for (ic = 0; ic < n_ic; ic++)
if (on_left(r->i_cm.map[ic]) || on_right(r->i_cm.map[ic])) {
r->map_table[oc][ic] = 1.0f / (float) n;
m->map_table_f[oc][ic] = 1.0f / (float) n;
ic_connected[ic] = TRUE;
}
@ -787,12 +799,12 @@ static void calc_map_table(pa_resampler *r) {
/* We are not connected and an LFE. Let's average all
* channels for LFE. */
for (ic = 0; ic < r->i_ss.channels; ic++) {
for (ic = 0; ic < n_ic; ic++) {
if (!(r->flags & PA_RESAMPLER_NO_LFE))
r->map_table[oc][ic] = 1.0f / (float) r->i_ss.channels;
m->map_table_f[oc][ic] = 1.0f / (float) n_ic;
else
r->map_table[oc][ic] = 0;
m->map_table_f[oc][ic] = 0;
/* Please note that a channel connected to LFE
* doesn't really count as connected. */
@ -808,7 +820,7 @@ static void calc_map_table(pa_resampler *r) {
ic_unconnected_center = 0,
ic_unconnected_lfe = 0;
for (ic = 0; ic < r->i_ss.channels; ic++) {
for (ic = 0; ic < n_ic; ic++) {
pa_channel_position_t a = r->i_cm.map[ic];
if (ic_connected[ic])
@ -831,20 +843,20 @@ static void calc_map_table(pa_resampler *r) {
* the left side by .9 and add in our averaged unconnected
* channels multplied by .1 */
for (oc = 0; oc < r->o_ss.channels; oc++) {
for (oc = 0; oc < n_oc; oc++) {
if (!on_left(r->o_cm.map[oc]))
continue;
for (ic = 0; ic < r->i_ss.channels; ic++) {
for (ic = 0; ic < n_ic; ic++) {
if (ic_connected[ic]) {
r->map_table[oc][ic] *= .9f;
m->map_table_f[oc][ic] *= .9f;
continue;
}
if (on_left(r->i_cm.map[ic]))
r->map_table[oc][ic] = .1f / (float) ic_unconnected_left;
m->map_table_f[oc][ic] = .1f / (float) ic_unconnected_left;
}
}
}
@ -856,20 +868,20 @@ static void calc_map_table(pa_resampler *r) {
* the right side by .9 and add in our averaged unconnected
* channels multplied by .1 */
for (oc = 0; oc < r->o_ss.channels; oc++) {
for (oc = 0; oc < n_oc; oc++) {
if (!on_right(r->o_cm.map[oc]))
continue;
for (ic = 0; ic < r->i_ss.channels; ic++) {
for (ic = 0; ic < n_ic; ic++) {
if (ic_connected[ic]) {
r->map_table[oc][ic] *= .9f;
m->map_table_f[oc][ic] *= .9f;
continue;
}
if (on_right(r->i_cm.map[ic]))
r->map_table[oc][ic] = .1f / (float) ic_unconnected_right;
m->map_table_f[oc][ic] = .1f / (float) ic_unconnected_right;
}
}
}
@ -882,20 +894,20 @@ static void calc_map_table(pa_resampler *r) {
* the center side by .9 and add in our averaged unconnected
* channels multplied by .1 */
for (oc = 0; oc < r->o_ss.channels; oc++) {
for (oc = 0; oc < n_oc; oc++) {
if (!on_center(r->o_cm.map[oc]))
continue;
for (ic = 0; ic < r->i_ss.channels; ic++) {
for (ic = 0; ic < n_ic; ic++) {
if (ic_connected[ic]) {
r->map_table[oc][ic] *= .9f;
m->map_table_f[oc][ic] *= .9f;
continue;
}
if (on_center(r->i_cm.map[ic])) {
r->map_table[oc][ic] = .1f / (float) ic_unconnected_center;
m->map_table_f[oc][ic] = .1f / (float) ic_unconnected_center;
mixed_in = TRUE;
}
}
@ -913,7 +925,7 @@ static void calc_map_table(pa_resampler *r) {
it into left and right. Using .375 and 0.75 as
factors. */
for (ic = 0; ic < r->i_ss.channels; ic++) {
for (ic = 0; ic < n_ic; ic++) {
if (ic_connected[ic])
continue;
@ -921,7 +933,7 @@ static void calc_map_table(pa_resampler *r) {
if (!on_center(r->i_cm.map[ic]))
continue;
for (oc = 0; oc < r->o_ss.channels; oc++) {
for (oc = 0; oc < n_oc; oc++) {
if (!on_left(r->o_cm.map[oc]) && !on_right(r->o_cm.map[oc]))
continue;
@ -932,7 +944,7 @@ static void calc_map_table(pa_resampler *r) {
}
}
for (oc = 0; oc < r->o_ss.channels; oc++) {
for (oc = 0; oc < n_oc; oc++) {
if (!on_left(r->o_cm.map[oc]) && !on_right(r->o_cm.map[oc]))
continue;
@ -942,7 +954,7 @@ static void calc_map_table(pa_resampler *r) {
}
}
for (oc = 0; oc < r->o_ss.channels; oc++) {
for (oc = 0; oc < n_oc; oc++) {
if (!on_left(r->o_cm.map[oc]) && !on_right(r->o_cm.map[oc]))
continue;
@ -950,10 +962,10 @@ static void calc_map_table(pa_resampler *r) {
if (ncenter[oc] <= 0)
continue;
for (ic = 0; ic < r->i_ss.channels; ic++) {
for (ic = 0; ic < n_ic; ic++) {
if (ic_connected[ic]) {
r->map_table[oc][ic] *= .75f;
m->map_table_f[oc][ic] *= .75f;
continue;
}
@ -961,7 +973,7 @@ static void calc_map_table(pa_resampler *r) {
continue;
if (!found_frs[ic] || front_rear_side(r->i_cm.map[ic]) == front_rear_side(r->o_cm.map[oc]))
r->map_table[oc][ic] = .375f / (float) ncenter[oc];
m->map_table_f[oc][ic] = .375f / (float) ncenter[oc];
}
}
}
@ -972,40 +984,46 @@ static void calc_map_table(pa_resampler *r) {
/* OK, so there is an unconnected LFE channel. Let's mix
* it into all channels, with factor 0.375 */
for (ic = 0; ic < r->i_ss.channels; ic++) {
for (ic = 0; ic < n_ic; ic++) {
if (!on_lfe(r->i_cm.map[ic]))
continue;
for (oc = 0; oc < r->o_ss.channels; oc++)
r->map_table[oc][ic] = 0.375f / (float) ic_unconnected_lfe;
for (oc = 0; oc < n_oc; oc++)
m->map_table_f[oc][ic] = 0.375f / (float) ic_unconnected_lfe;
}
}
}
/* make an 16:16 int version of the matrix */
for (oc = 0; oc < n_oc; oc++)
for (ic = 0; ic < n_ic; ic++)
m->map_table_i[oc][ic] = (int32_t) (m->map_table_f[oc][ic] * 0x10000);
s = pa_strbuf_new();
pa_strbuf_printf(s, " ");
for (ic = 0; ic < r->i_ss.channels; ic++)
for (ic = 0; ic < n_ic; ic++)
pa_strbuf_printf(s, " I%02u ", ic);
pa_strbuf_puts(s, "\n +");
for (ic = 0; ic < r->i_ss.channels; ic++)
for (ic = 0; ic < n_ic; ic++)
pa_strbuf_printf(s, "------");
pa_strbuf_puts(s, "\n");
for (oc = 0; oc < r->o_ss.channels; oc++) {
for (oc = 0; oc < n_oc; oc++) {
pa_strbuf_printf(s, "O%02u |", oc);
for (ic = 0; ic < r->i_ss.channels; ic++)
pa_strbuf_printf(s, " %1.3f", r->map_table[oc][ic]);
for (ic = 0; ic < n_ic; ic++)
pa_strbuf_printf(s, " %1.3f", m->map_table_f[oc][ic]);
pa_strbuf_puts(s, "\n");
}
pa_log_debug("Channel matrix:\n%s", t = pa_strbuf_tostring_free(s));
pa_xfree(t);
/* initialize the remapping function */
pa_init_remap (m);
}
static pa_memchunk* convert_to_work_format(pa_resampler *r, pa_memchunk *input) {
@ -1045,41 +1063,10 @@ static pa_memchunk* convert_to_work_format(pa_resampler *r, pa_memchunk *input)
return &r->buf1;
}
static void vectoradd_s16_with_fraction(
int16_t *d, int dstr,
const int16_t *s1, int sstr1,
const int16_t *s2, int sstr2,
int n,
float s3, float s4) {
int32_t i3, i4;
i3 = (int32_t) (s3 * 0x10000);
i4 = (int32_t) (s4 * 0x10000);
for (; n > 0; n--) {
int32_t a, b;
a = *s1;
b = *s2;
a = (a * i3) / 0x10000;
b = (b * i4) / 0x10000;
*d = (int16_t) (a + b);
s1 = (const int16_t*) ((const uint8_t*) s1 + sstr1);
s2 = (const int16_t*) ((const uint8_t*) s2 + sstr2);
d = (int16_t*) ((uint8_t*) d + dstr);
}
}
static pa_memchunk *remap_channels(pa_resampler *r, pa_memchunk *input) {
unsigned in_n_samples, out_n_samples, n_frames;
int i_skip, o_skip;
unsigned oc;
void *src, *dst;
pa_remap_t *remap;
pa_assert(r);
pa_assert(input);
@ -1108,76 +1095,14 @@ static pa_memchunk *remap_channels(pa_resampler *r, pa_memchunk *input) {
src = ((uint8_t*) pa_memblock_acquire(input->memblock) + input->index);
dst = pa_memblock_acquire(r->buf2.memblock);
memset(dst, 0, r->buf2.length);
remap = &r->remap;
o_skip = (int) (r->w_sz * r->o_ss.channels);
i_skip = (int) (r->w_sz * r->i_ss.channels);
switch (r->work_format) {
case PA_SAMPLE_FLOAT32NE:
for (oc = 0; oc < r->o_ss.channels; oc++) {
unsigned ic;
static const float one = 1.0;
for (ic = 0; ic < r->i_ss.channels; ic++) {
if (r->map_table[oc][ic] <= 0.0)
continue;
oil_vectoradd_f32(
(float*) dst + oc, o_skip,
(float*) dst + oc, o_skip,
(float*) src + ic, i_skip,
(int) n_frames,
&one, &r->map_table[oc][ic]);
}
}
break;
case PA_SAMPLE_S16NE:
for (oc = 0; oc < r->o_ss.channels; oc++) {
unsigned ic;
for (ic = 0; ic < r->i_ss.channels; ic++) {
if (r->map_table[oc][ic] <= 0.0)
continue;
if (r->map_table[oc][ic] >= 1.0) {
static const int16_t one = 1;
oil_vectoradd_s16(
(int16_t*) dst + oc, o_skip,
(int16_t*) dst + oc, o_skip,
(int16_t*) src + ic, i_skip,
(int) n_frames,
&one, &one);
} else
vectoradd_s16_with_fraction(
(int16_t*) dst + oc, o_skip,
(int16_t*) dst + oc, o_skip,
(int16_t*) src + ic, i_skip,
(int) n_frames,
1.0f, r->map_table[oc][ic]);
}
}
break;
default:
pa_assert_not_reached();
}
pa_assert (remap->do_remap);
remap->do_remap (remap, dst, src, n_frames);
pa_memblock_release(input->memblock);
pa_memblock_release(r->buf2.memblock);
r->buf2.length = out_n_samples * r->w_sz;
return &r->buf2;
}
@ -1469,7 +1394,7 @@ static void trivial_resample(pa_resampler *r, const pa_memchunk *input, unsigned
pa_assert(o_index * fz < pa_memblock_get_length(output->memblock));
oil_memcpy((uint8_t*) dst + fz * o_index,
memcpy((uint8_t*) dst + fz * o_index,
(uint8_t*) src + fz * j, (int) fz);
}

View file

@ -63,6 +63,7 @@ struct pa_rtpoll {
pa_bool_t running:1;
pa_bool_t rebuild_needed:1;
pa_bool_t quit:1;
pa_bool_t timer_elapsed:1;
#ifdef DEBUG_TIMING
pa_usec_t timestamp;
@ -94,26 +95,14 @@ PA_STATIC_FLIST_DECLARE(items, 0, pa_xfree);
pa_rtpoll *pa_rtpoll_new(void) {
pa_rtpoll *p;
p = pa_xnew(pa_rtpoll, 1);
p = pa_xnew0(pa_rtpoll, 1);
p->n_pollfd_alloc = 32;
p->pollfd = pa_xnew(struct pollfd, p->n_pollfd_alloc);
p->pollfd2 = pa_xnew(struct pollfd, p->n_pollfd_alloc);
p->n_pollfd_used = 0;
pa_zero(p->next_elapse);
p->timer_enabled = FALSE;
p->running = FALSE;
p->scan_for_dead = FALSE;
p->rebuild_needed = FALSE;
p->quit = FALSE;
PA_LLIST_HEAD_INIT(pa_rtpoll_item, p->items);
#ifdef DEBUG_TIMING
p->timestamp = pa_rtclock_now();
p->slept = p->awake = 0;
#endif
return p;
@ -229,6 +218,7 @@ int pa_rtpoll_run(pa_rtpoll *p, pa_bool_t wait_op) {
pa_assert(!p->running);
p->running = TRUE;
p->timer_elapsed = FALSE;
/* First, let's do some work */
for (i = p->items; i && i->priority < PA_RTPOLL_NEVER; i = i->next) {
@ -286,7 +276,7 @@ int pa_rtpoll_run(pa_rtpoll *p, pa_bool_t wait_op) {
if (p->rebuild_needed)
rtpoll_rebuild(p);
memset(&timeout, 0, sizeof(timeout));
pa_zero(timeout);
/* Calculate timeout */
if (wait_op && !p->quit && p->timer_enabled) {
@ -314,9 +304,11 @@ int pa_rtpoll_run(pa_rtpoll *p, pa_bool_t wait_op) {
r = ppoll(p->pollfd, p->n_pollfd_used, (!wait_op || p->quit || p->timer_enabled) ? &ts : NULL, NULL);
}
#else
r = poll(p->pollfd, p->n_pollfd_used, (!wait_op || p->quit || p->timer_enabled) ? (int) ((timeout.tv_sec*1000) + (timeout.tv_usec / 1000)) : -1);
r = poll(p->pollfd, p->n_pollfd_used, (!wait_op || p->quit || p->timer_enabled) ? (int) ((timeout.tv_sec*1000) + (timeout.tv_usec / 1000)) : -1);
#endif
p->timer_elapsed = r == 0;
#ifdef DEBUG_TIMING
{
pa_usec_t now = pa_rtclock_now();
@ -628,3 +620,9 @@ void pa_rtpoll_quit(pa_rtpoll *p) {
p->quit = TRUE;
}
pa_bool_t pa_rtpoll_timer_elapsed(pa_rtpoll *p) {
pa_assert(p);
return p->timer_elapsed;
}

View file

@ -73,6 +73,10 @@ void pa_rtpoll_set_timer_absolute(pa_rtpoll *p, pa_usec_t usec);
void pa_rtpoll_set_timer_relative(pa_rtpoll *p, pa_usec_t usec);
void pa_rtpoll_set_timer_disabled(pa_rtpoll *p);
/* Return TRUE when the elapsed timer was the reason for
* the last pa_rtpoll_run() invocation to finish */
pa_bool_t pa_rtpoll_timer_elapsed(pa_rtpoll *p);
/* A new fd wakeup item for pa_rtpoll */
pa_rtpoll_item *pa_rtpoll_item_new(pa_rtpoll *p, pa_rtpoll_priority_t prio, unsigned n_fds);
void pa_rtpoll_item_free(pa_rtpoll_item *i);

View file

@ -30,9 +30,6 @@
#include <stdio.h>
#include <errno.h>
#include <liboil/liboilfuncs.h>
#include <liboil/liboil.h>
#include <pulse/timeval.h>
#include <pulsecore/log.h>
@ -106,24 +103,36 @@ void* pa_silence_memory(void *p, size_t length, const pa_sample_spec *spec) {
return p;
}
#define VOLUME_PADDING 32
static void calc_linear_integer_volume(int32_t linear[], const pa_cvolume *volume) {
unsigned channel;
unsigned channel, nchannels, padding;
pa_assert(linear);
pa_assert(volume);
for (channel = 0; channel < volume->channels; channel++)
nchannels = volume->channels;
for (channel = 0; channel < nchannels; channel++)
linear[channel] = (int32_t) lrint(pa_sw_volume_to_linear(volume->values[channel]) * 0x10000);
for (padding = 0; padding < VOLUME_PADDING; padding++, channel++)
linear[channel] = linear[padding];
}
static void calc_linear_float_volume(float linear[], const pa_cvolume *volume) {
unsigned channel;
unsigned channel, nchannels, padding;
pa_assert(linear);
pa_assert(volume);
for (channel = 0; channel < volume->channels; channel++)
nchannels = volume->channels;
for (channel = 0; channel < nchannels; channel++)
linear[channel] = (float) pa_sw_volume_to_linear(volume->values[channel]);
for (padding = 0; padding < VOLUME_PADDING; padding++, channel++)
linear[channel] = linear[padding];
}
static void calc_linear_integer_stream_volumes(pa_mix_info streams[], unsigned nstreams, const pa_cvolume *volume, const pa_sample_spec *spec) {
@ -690,6 +699,28 @@ size_t pa_mix(
return length;
}
typedef union {
float f;
uint32_t i;
} volume_val;
typedef void (*pa_calc_volume_func_t) (void *volumes, const pa_cvolume *volume);
static const pa_calc_volume_func_t calc_volume_table[] = {
[PA_SAMPLE_U8] = (pa_calc_volume_func_t) calc_linear_integer_volume,
[PA_SAMPLE_ALAW] = (pa_calc_volume_func_t) calc_linear_integer_volume,
[PA_SAMPLE_ULAW] = (pa_calc_volume_func_t) calc_linear_integer_volume,
[PA_SAMPLE_S16LE] = (pa_calc_volume_func_t) calc_linear_integer_volume,
[PA_SAMPLE_S16BE] = (pa_calc_volume_func_t) calc_linear_integer_volume,
[PA_SAMPLE_FLOAT32LE] = (pa_calc_volume_func_t) calc_linear_float_volume,
[PA_SAMPLE_FLOAT32BE] = (pa_calc_volume_func_t) calc_linear_float_volume,
[PA_SAMPLE_S32LE] = (pa_calc_volume_func_t) calc_linear_integer_volume,
[PA_SAMPLE_S32BE] = (pa_calc_volume_func_t) calc_linear_integer_volume,
[PA_SAMPLE_S24LE] = (pa_calc_volume_func_t) calc_linear_integer_volume,
[PA_SAMPLE_S24BE] = (pa_calc_volume_func_t) calc_linear_integer_volume,
[PA_SAMPLE_S24_32LE] = (pa_calc_volume_func_t) calc_linear_integer_volume,
[PA_SAMPLE_S24_32BE] = (pa_calc_volume_func_t) calc_linear_integer_volume
};
void pa_volume_memchunk(
pa_memchunk*c,
@ -697,6 +728,8 @@ void pa_volume_memchunk(
const pa_cvolume *volume) {
void *ptr;
volume_val linear[PA_CHANNELS_MAX + VOLUME_PADDING];
pa_do_volume_func_t do_volume;
pa_assert(c);
pa_assert(spec);
@ -714,337 +747,19 @@ void pa_volume_memchunk(
return;
}
if (spec->format < 0 || spec->format > PA_SAMPLE_MAX) {
pa_log_warn(" Unable to change volume of format %s.", pa_sample_format_to_string(spec->format));
return;
}
do_volume = pa_get_volume_func (spec->format);
pa_assert(do_volume);
calc_volume_table[spec->format] ((void *)linear, volume);
ptr = (uint8_t*) pa_memblock_acquire(c->memblock) + c->index;
switch (spec->format) {
case PA_SAMPLE_S16NE: {
int16_t *d, *e;
unsigned channel;
int32_t linear[PA_CHANNELS_MAX];
calc_linear_integer_volume(linear, volume);
e = (int16_t*) ptr + c->length/sizeof(int16_t);
for (channel = 0, d = ptr; d < e; d++) {
int32_t t, hi, lo;
/* Multiplying the 32bit volume factor with the 16bit
* sample might result in an 48bit value. We want to
* do without 64 bit integers and hence do the
* multiplication independantly for the HI and LO part
* of the volume. */
hi = linear[channel] >> 16;
lo = linear[channel] & 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 >= spec->channels))
channel = 0;
}
break;
}
case PA_SAMPLE_S16RE: {
int16_t *d, *e;
unsigned channel;
int32_t linear[PA_CHANNELS_MAX];
calc_linear_integer_volume(linear, volume);
e = (int16_t*) ptr + c->length/sizeof(int16_t);
for (channel = 0, d = ptr; d < e; d++) {
int32_t t, hi, lo;
hi = linear[channel] >> 16;
lo = linear[channel] & 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 >= spec->channels))
channel = 0;
}
break;
}
case PA_SAMPLE_S32NE: {
int32_t *d, *e;
unsigned channel;
int32_t linear[PA_CHANNELS_MAX];
calc_linear_integer_volume(linear, volume);
e = (int32_t*) ptr + c->length/sizeof(int32_t);
for (channel = 0, d = ptr; d < e; d++) {
int64_t t;
t = (int64_t)(*d);
t = (t * linear[channel]) >> 16;
t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
*d = (int32_t) t;
if (PA_UNLIKELY(++channel >= spec->channels))
channel = 0;
}
break;
}
case PA_SAMPLE_S32RE: {
int32_t *d, *e;
unsigned channel;
int32_t linear[PA_CHANNELS_MAX];
calc_linear_integer_volume(linear, volume);
e = (int32_t*) ptr + c->length/sizeof(int32_t);
for (channel = 0, d = ptr; d < e; d++) {
int64_t t;
t = (int64_t) PA_INT32_SWAP(*d);
t = (t * linear[channel]) >> 16;
t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
*d = PA_INT32_SWAP((int32_t) t);
if (PA_UNLIKELY(++channel >= spec->channels))
channel = 0;
}
break;
}
case PA_SAMPLE_S24NE: {
uint8_t *d, *e;
unsigned channel;
int32_t linear[PA_CHANNELS_MAX];
calc_linear_integer_volume(linear, volume);
e = (uint8_t*) ptr + c->length;
for (channel = 0, d = ptr; d < e; d += 3) {
int64_t t;
t = (int64_t)((int32_t) (PA_READ24NE(d) << 8));
t = (t * linear[channel]) >> 16;
t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
PA_WRITE24NE(d, ((uint32_t) (int32_t) t) >> 8);
if (PA_UNLIKELY(++channel >= spec->channels))
channel = 0;
}
break;
}
case PA_SAMPLE_S24RE: {
uint8_t *d, *e;
unsigned channel;
int32_t linear[PA_CHANNELS_MAX];
calc_linear_integer_volume(linear, volume);
e = (uint8_t*) ptr + c->length;
for (channel = 0, d = ptr; d < e; d += 3) {
int64_t t;
t = (int64_t)((int32_t) (PA_READ24RE(d) << 8));
t = (t * linear[channel]) >> 16;
t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
PA_WRITE24RE(d, ((uint32_t) (int32_t) t) >> 8);
if (PA_UNLIKELY(++channel >= spec->channels))
channel = 0;
}
break;
}
case PA_SAMPLE_S24_32NE: {
uint32_t *d, *e;
unsigned channel;
int32_t linear[PA_CHANNELS_MAX];
calc_linear_integer_volume(linear, volume);
e = (uint32_t*) ptr + c->length/sizeof(uint32_t);
for (channel = 0, d = ptr; d < e; d++) {
int64_t t;
t = (int64_t) ((int32_t) (*d << 8));
t = (t * linear[channel]) >> 16;
t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
*d = ((uint32_t) ((int32_t) t)) >> 8;
if (PA_UNLIKELY(++channel >= spec->channels))
channel = 0;
}
break;
}
case PA_SAMPLE_S24_32RE: {
uint32_t *d, *e;
unsigned channel;
int32_t linear[PA_CHANNELS_MAX];
calc_linear_integer_volume(linear, volume);
e = (uint32_t*) ptr + c->length/sizeof(uint32_t);
for (channel = 0, d = ptr; d < e; d++) {
int64_t t;
t = (int64_t) ((int32_t) (PA_UINT32_SWAP(*d) << 8));
t = (t * linear[channel]) >> 16;
t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
*d = PA_UINT32_SWAP(((uint32_t) ((int32_t) t)) >> 8);
if (PA_UNLIKELY(++channel >= spec->channels))
channel = 0;
}
break;
}
case PA_SAMPLE_U8: {
uint8_t *d, *e;
unsigned channel;
int32_t linear[PA_CHANNELS_MAX];
calc_linear_integer_volume(linear, volume);
e = (uint8_t*) ptr + c->length;
for (channel = 0, d = ptr; d < e; d++) {
int32_t t, hi, lo;
hi = linear[channel] >> 16;
lo = linear[channel] & 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 >= spec->channels))
channel = 0;
}
break;
}
case PA_SAMPLE_ULAW: {
uint8_t *d, *e;
unsigned channel;
int32_t linear[PA_CHANNELS_MAX];
calc_linear_integer_volume(linear, volume);
e = (uint8_t*) ptr + c->length;
for (channel = 0, d = ptr; d < e; d++) {
int32_t t, hi, lo;
hi = linear[channel] >> 16;
lo = linear[channel] & 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 >= spec->channels))
channel = 0;
}
break;
}
case PA_SAMPLE_ALAW: {
uint8_t *d, *e;
unsigned channel;
int32_t linear[PA_CHANNELS_MAX];
calc_linear_integer_volume(linear, volume);
e = (uint8_t*) ptr + c->length;
for (channel = 0, d = ptr; d < e; d++) {
int32_t t, hi, lo;
hi = linear[channel] >> 16;
lo = linear[channel] & 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 >= spec->channels))
channel = 0;
}
break;
}
case PA_SAMPLE_FLOAT32NE: {
float *d;
int skip;
unsigned n;
unsigned channel;
d = ptr;
skip = (int) (spec->channels * sizeof(float));
n = (unsigned) (c->length/sizeof(float)/spec->channels);
for (channel = 0; channel < spec->channels; channel ++) {
float v, *t;
if (PA_UNLIKELY(volume->values[channel] == PA_VOLUME_NORM))
continue;
v = (float) pa_sw_volume_to_linear(volume->values[channel]);
t = d + channel;
oil_scalarmult_f32(t, skip, t, skip, &v, (int) n);
}
break;
}
case PA_SAMPLE_FLOAT32RE: {
float *d, *e;
unsigned channel;
float linear[PA_CHANNELS_MAX];
calc_linear_float_volume(linear, volume);
e = (float*) ptr + c->length/sizeof(float);
for (channel = 0, d = ptr; d < e; d++) {
float t;
t = PA_FLOAT32_SWAP(*d);
t *= linear[channel];
*d = PA_FLOAT32_SWAP(t);
if (PA_UNLIKELY(++channel >= spec->channels))
channel = 0;
}
break;
}
default:
pa_log_warn(" Unable to change volume of format %s.", pa_sample_format_to_string(spec->format));
/* If we cannot change the volume, we just don't do it */
}
do_volume (ptr, (void *)linear, spec->channels, c->length);
pa_memblock_release(c->memblock);
}
@ -1090,7 +805,7 @@ void pa_interleave(const void *src[], unsigned channels, void *dst, size_t ss, u
d = (uint8_t*) dst + c * ss;
for (j = 0; j < n; j ++) {
oil_memcpy(d, s, (int) ss);
memcpy(d, s, (int) ss);
s = (uint8_t*) s + ss;
d = (uint8_t*) d + fs;
}
@ -1118,7 +833,7 @@ void pa_deinterleave(const void *src, void *dst[], unsigned channels, size_t ss,
d = dst[c];
for (j = 0; j < n; j ++) {
oil_memcpy(d, s, (int) ss);
memcpy(d, s, (int) ss);
s = (uint8_t*) s + fs;
d = (uint8_t*) d + ss;
}
@ -1227,10 +942,15 @@ void pa_sample_clamp(pa_sample_format_t format, void *dst, size_t dstr, const vo
s = src; d = dst;
if (format == PA_SAMPLE_FLOAT32NE) {
for (; n > 0; n--) {
float f;
float minus_one = -1.0, plus_one = 1.0;
oil_clip_f32(d, (int) dstr, s, (int) sstr, (int) n, &minus_one, &plus_one);
f = *s;
*d = PA_CLAMP_UNLIKELY(f, -1.0f, 1.0f);
s = (const float*) ((const uint8_t*) s + sstr);
d = (float*) ((uint8_t*) d + dstr);
}
} else {
pa_assert(format == PA_SAMPLE_FLOAT32RE);

View file

@ -86,6 +86,11 @@ void pa_memchunk_dump_to_file(pa_memchunk *c, const char *fn);
void pa_memchunk_sine(pa_memchunk *c, pa_mempool *pool, unsigned rate, unsigned freq);
typedef void (*pa_do_volume_func_t) (void *samples, void *volumes, unsigned channels, unsigned length);
pa_do_volume_func_t pa_get_volume_func(pa_sample_format_t f);
void pa_set_volume_func(pa_sample_format_t f, pa_do_volume_func_t func);
#define PA_CHANNEL_POSITION_MASK_LEFT \
(PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_LEFT) \

View file

@ -28,8 +28,6 @@
#include <inttypes.h>
#include <stdio.h>
#include <liboil/liboilfuncs.h>
#include <pulsecore/sconv.h>
#include <pulsecore/macro.h>
#include <pulsecore/log.h>
@ -86,17 +84,13 @@ void pa_sconv_s16le_to_float32ne(unsigned n, const int16_t *a, float *b) {
pa_assert(b);
#if SWAP_WORDS == 1
for (; n > 0; n--) {
int16_t s = *(a++);
*(b++) = ((float) INT16_FROM(s))/(float) 0x7FFF;
}
#else
{
static const double add = 0, factor = 1.0/0x7FFF;
oil_scaleconv_f32_s16(b, a, (int) n, &add, &factor);
}
for (; n > 0; n--)
*(b++) = ((float) (*(a++)))/(float) 0x7FFF;
#endif
}
@ -105,17 +99,13 @@ void pa_sconv_s32le_to_float32ne(unsigned n, const int32_t *a, float *b) {
pa_assert(b);
#if SWAP_WORDS == 1
for (; n > 0; n--) {
int32_t s = *(a++);
*(b++) = (float) (((double) INT32_FROM(s))/0x7FFFFFFF);
}
#else
{
static const double add = 0, factor = 1.0/0x7FFFFFFF;
oil_scaleconv_f32_s32(b, a, (int) n, &add, &factor);
}
for (; n > 0; n--)
*(b++) = (float) (((double) (*(a++)))/0x7FFFFFFF);
#endif
}
@ -124,7 +114,6 @@ void pa_sconv_s16le_from_float32ne(unsigned n, const float *a, int16_t *b) {
pa_assert(b);
#if SWAP_WORDS == 1
for (; n > 0; n--) {
int16_t s;
float v = *(a++);
@ -133,12 +122,13 @@ void pa_sconv_s16le_from_float32ne(unsigned n, const float *a, int16_t *b) {
s = (int16_t) lrintf(v * 0x7FFF);
*(b++) = INT16_TO(s);
}
#else
{
static const double add = 0, factor = 0x7FFF;
oil_scaleconv_s16_f32(b, a, (int) n, &add, &factor);
}
for (; n > 0; n--) {
float v = *(a++);
v = PA_CLAMP_UNLIKELY(v, -1.0f, 1.f);
*(b++) = (int16_t) lrintf(v * 0x7FFF);
}
#endif
}
@ -147,7 +137,6 @@ void pa_sconv_s32le_from_float32ne(unsigned n, const float *a, int32_t *b) {
pa_assert(b);
#if SWAP_WORDS == 1
for (; n > 0; n--) {
int32_t s;
float v = *(a++);
@ -156,12 +145,13 @@ void pa_sconv_s32le_from_float32ne(unsigned n, const float *a, int32_t *b) {
s = (int32_t) lrint((double) v * (double) 0x7FFFFFFF);
*(b++) = INT32_TO(s);
}
#else
{
static const double add = 0, factor = 0x7FFFFFFF;
oil_scaleconv_s32_f32(b, a, (int) n, &add, &factor);
}
for (; n > 0; n--) {
float v = *(a++);
v = PA_CLAMP_UNLIKELY(v, -1.0f, 1.0f);
*(b++) = (int32_t) lrint((double) v * (double) 0x7FFFFFFF);
}
#endif
}

View file

@ -27,9 +27,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <liboil/liboilfuncs.h>
#include <liboil/liboil.h>
#include <pulsecore/g711.h>
#include <pulsecore/macro.h>
@ -41,32 +38,31 @@
/* u8 */
static void u8_to_float32ne(unsigned n, const uint8_t *a, float *b) {
static const double add = -1, factor = 1.0/128.0;
pa_assert(a);
pa_assert(b);
oil_scaleconv_f32_u8(b, a, (int) n, &add, &factor);
for (; n > 0; n--, a++, b++)
*b = (*a * 1.0/128.0) - 1.0;
}
static void u8_from_float32ne(unsigned n, const float *a, uint8_t *b) {
static const double add = 128, factor = 127.0;
pa_assert(a);
pa_assert(b);
oil_scaleconv_u8_f32(b, a, (int) n, &add, &factor);
for (; n > 0; n--, a++, b++) {
float v;
v = (*a * 127.0) + 128.0;
v = PA_CLAMP_UNLIKELY (v, 0.0, 255.0);
*b = rint (v);
}
}
static void u8_to_s16ne(unsigned n, const uint8_t *a, int16_t *b) {
static const int16_t add = -0x80, factor = 0x100;
pa_assert(a);
pa_assert(b);
oil_conv_s16_u8(b, 2, a, 1, (int) n);
oil_scalaradd_s16(b, 2, b, 2, &add, (int) n);
oil_scalarmult_s16(b, 2, b, 2, &factor, (int) n);
for (; n > 0; n--, a++, b++)
*b = (((int16_t)*a) - 128) << 8;
}
static void u8_from_s16ne(unsigned n, const int16_t *a, uint8_t *b) {
@ -84,7 +80,7 @@ static void float32ne_to_float32ne(unsigned n, const float *a, float *b) {
pa_assert(a);
pa_assert(b);
oil_memcpy(b, a, (int) (sizeof(float) * n));
memcpy(b, a, (int) (sizeof(float) * n));
}
static void float32re_to_float32ne(unsigned n, const float *a, float *b) {
@ -101,7 +97,7 @@ static void s16ne_to_s16ne(unsigned n, const int16_t *a, int16_t *b) {
pa_assert(a);
pa_assert(b);
oil_memcpy(b, a, (int) (sizeof(int16_t) * n));
memcpy(b, a, (int) (sizeof(int16_t) * n));
}
static void s16re_to_s16ne(unsigned n, const int16_t *a, int16_t *b) {
@ -188,98 +184,130 @@ static void alaw_from_s16ne(unsigned n, const int16_t *a, uint8_t *b) {
*b = st_13linear2alaw(*a >> 3);
}
pa_convert_func_t pa_get_convert_to_float32ne_function(pa_sample_format_t f) {
static pa_convert_func_t to_float32ne_table[] = {
[PA_SAMPLE_U8] = (pa_convert_func_t) u8_to_float32ne,
[PA_SAMPLE_ALAW] = (pa_convert_func_t) alaw_to_float32ne,
[PA_SAMPLE_ULAW] = (pa_convert_func_t) ulaw_to_float32ne,
[PA_SAMPLE_S16LE] = (pa_convert_func_t) pa_sconv_s16le_to_float32ne,
[PA_SAMPLE_S16BE] = (pa_convert_func_t) pa_sconv_s16be_to_float32ne,
[PA_SAMPLE_S32LE] = (pa_convert_func_t) pa_sconv_s32le_to_float32ne,
[PA_SAMPLE_S32BE] = (pa_convert_func_t) pa_sconv_s32be_to_float32ne,
[PA_SAMPLE_S24LE] = (pa_convert_func_t) pa_sconv_s24le_to_float32ne,
[PA_SAMPLE_S24BE] = (pa_convert_func_t) pa_sconv_s24be_to_float32ne,
[PA_SAMPLE_S24_32LE] = (pa_convert_func_t) pa_sconv_s24_32le_to_float32ne,
[PA_SAMPLE_S24_32BE] = (pa_convert_func_t) pa_sconv_s24_32be_to_float32ne,
[PA_SAMPLE_FLOAT32NE] = (pa_convert_func_t) float32ne_to_float32ne,
[PA_SAMPLE_FLOAT32RE] = (pa_convert_func_t) float32re_to_float32ne,
};
static const pa_convert_func_t table[] = {
[PA_SAMPLE_U8] = (pa_convert_func_t) u8_to_float32ne,
[PA_SAMPLE_ALAW] = (pa_convert_func_t) alaw_to_float32ne,
[PA_SAMPLE_ULAW] = (pa_convert_func_t) ulaw_to_float32ne,
[PA_SAMPLE_S16LE] = (pa_convert_func_t) pa_sconv_s16le_to_float32ne,
[PA_SAMPLE_S16BE] = (pa_convert_func_t) pa_sconv_s16be_to_float32ne,
[PA_SAMPLE_S32LE] = (pa_convert_func_t) pa_sconv_s32le_to_float32ne,
[PA_SAMPLE_S32BE] = (pa_convert_func_t) pa_sconv_s32be_to_float32ne,
[PA_SAMPLE_S24LE] = (pa_convert_func_t) pa_sconv_s24le_to_float32ne,
[PA_SAMPLE_S24BE] = (pa_convert_func_t) pa_sconv_s24be_to_float32ne,
[PA_SAMPLE_S24_32LE] = (pa_convert_func_t) pa_sconv_s24_32le_to_float32ne,
[PA_SAMPLE_S24_32BE] = (pa_convert_func_t) pa_sconv_s24_32be_to_float32ne,
[PA_SAMPLE_FLOAT32NE] = (pa_convert_func_t) float32ne_to_float32ne,
[PA_SAMPLE_FLOAT32RE] = (pa_convert_func_t) float32re_to_float32ne,
};
pa_convert_func_t pa_get_convert_to_float32ne_function(pa_sample_format_t f) {
pa_assert(f >= 0);
pa_assert(f < PA_SAMPLE_MAX);
return table[f];
return to_float32ne_table[f];
}
void pa_set_convert_to_float32ne_function(pa_sample_format_t f, pa_convert_func_t func) {
pa_assert(f >= 0);
pa_assert(f < PA_SAMPLE_MAX);
to_float32ne_table[f] = func;
}
static pa_convert_func_t from_float32ne_table[] = {
[PA_SAMPLE_U8] = (pa_convert_func_t) u8_from_float32ne,
[PA_SAMPLE_S16LE] = (pa_convert_func_t) pa_sconv_s16le_from_float32ne,
[PA_SAMPLE_S16BE] = (pa_convert_func_t) pa_sconv_s16be_from_float32ne,
[PA_SAMPLE_S32LE] = (pa_convert_func_t) pa_sconv_s32le_from_float32ne,
[PA_SAMPLE_S32BE] = (pa_convert_func_t) pa_sconv_s32be_from_float32ne,
[PA_SAMPLE_S24LE] = (pa_convert_func_t) pa_sconv_s24le_from_float32ne,
[PA_SAMPLE_S24BE] = (pa_convert_func_t) pa_sconv_s24be_from_float32ne,
[PA_SAMPLE_S24_32LE] = (pa_convert_func_t) pa_sconv_s24_32le_from_float32ne,
[PA_SAMPLE_S24_32BE] = (pa_convert_func_t) pa_sconv_s24_32be_from_float32ne,
[PA_SAMPLE_FLOAT32NE] = (pa_convert_func_t) float32ne_to_float32ne,
[PA_SAMPLE_FLOAT32RE] = (pa_convert_func_t) float32re_to_float32ne,
[PA_SAMPLE_ALAW] = (pa_convert_func_t) alaw_from_float32ne,
[PA_SAMPLE_ULAW] = (pa_convert_func_t) ulaw_from_float32ne
};
pa_convert_func_t pa_get_convert_from_float32ne_function(pa_sample_format_t f) {
static const pa_convert_func_t table[] = {
[PA_SAMPLE_U8] = (pa_convert_func_t) u8_from_float32ne,
[PA_SAMPLE_S16LE] = (pa_convert_func_t) pa_sconv_s16le_from_float32ne,
[PA_SAMPLE_S16BE] = (pa_convert_func_t) pa_sconv_s16be_from_float32ne,
[PA_SAMPLE_S32LE] = (pa_convert_func_t) pa_sconv_s32le_from_float32ne,
[PA_SAMPLE_S32BE] = (pa_convert_func_t) pa_sconv_s32be_from_float32ne,
[PA_SAMPLE_S24LE] = (pa_convert_func_t) pa_sconv_s24le_from_float32ne,
[PA_SAMPLE_S24BE] = (pa_convert_func_t) pa_sconv_s24be_from_float32ne,
[PA_SAMPLE_S24_32LE] = (pa_convert_func_t) pa_sconv_s24_32le_from_float32ne,
[PA_SAMPLE_S24_32BE] = (pa_convert_func_t) pa_sconv_s24_32be_from_float32ne,
[PA_SAMPLE_FLOAT32NE] = (pa_convert_func_t) float32ne_to_float32ne,
[PA_SAMPLE_FLOAT32RE] = (pa_convert_func_t) float32re_to_float32ne,
[PA_SAMPLE_ALAW] = (pa_convert_func_t) alaw_from_float32ne,
[PA_SAMPLE_ULAW] = (pa_convert_func_t) ulaw_from_float32ne
};
pa_assert(f >= 0);
pa_assert(f < PA_SAMPLE_MAX);
return from_float32ne_table[f];
}
void pa_set_convert_from_float32ne_function(pa_sample_format_t f, pa_convert_func_t func) {
pa_assert(f >= 0);
pa_assert(f < PA_SAMPLE_MAX);
return table[f];
from_float32ne_table[f] = func;
}
static pa_convert_func_t to_s16ne_table[] = {
[PA_SAMPLE_U8] = (pa_convert_func_t) u8_to_s16ne,
[PA_SAMPLE_S16NE] = (pa_convert_func_t) s16ne_to_s16ne,
[PA_SAMPLE_S16RE] = (pa_convert_func_t) s16re_to_s16ne,
[PA_SAMPLE_FLOAT32BE] = (pa_convert_func_t) pa_sconv_float32be_to_s16ne,
[PA_SAMPLE_FLOAT32LE] = (pa_convert_func_t) pa_sconv_float32le_to_s16ne,
[PA_SAMPLE_S32BE] = (pa_convert_func_t) pa_sconv_s32be_to_s16ne,
[PA_SAMPLE_S32LE] = (pa_convert_func_t) pa_sconv_s32le_to_s16ne,
[PA_SAMPLE_S24BE] = (pa_convert_func_t) pa_sconv_s24be_to_s16ne,
[PA_SAMPLE_S24LE] = (pa_convert_func_t) pa_sconv_s24le_to_s16ne,
[PA_SAMPLE_S24_32BE] = (pa_convert_func_t) pa_sconv_s24_32be_to_s16ne,
[PA_SAMPLE_S24_32LE] = (pa_convert_func_t) pa_sconv_s24_32le_to_s16ne,
[PA_SAMPLE_ALAW] = (pa_convert_func_t) alaw_to_s16ne,
[PA_SAMPLE_ULAW] = (pa_convert_func_t) ulaw_to_s16ne
};
pa_convert_func_t pa_get_convert_to_s16ne_function(pa_sample_format_t f) {
static const pa_convert_func_t table[] = {
[PA_SAMPLE_U8] = (pa_convert_func_t) u8_to_s16ne,
[PA_SAMPLE_S16NE] = (pa_convert_func_t) s16ne_to_s16ne,
[PA_SAMPLE_S16RE] = (pa_convert_func_t) s16re_to_s16ne,
[PA_SAMPLE_FLOAT32BE] = (pa_convert_func_t) pa_sconv_float32be_to_s16ne,
[PA_SAMPLE_FLOAT32LE] = (pa_convert_func_t) pa_sconv_float32le_to_s16ne,
[PA_SAMPLE_S32BE] = (pa_convert_func_t) pa_sconv_s32be_to_s16ne,
[PA_SAMPLE_S32LE] = (pa_convert_func_t) pa_sconv_s32le_to_s16ne,
[PA_SAMPLE_S24BE] = (pa_convert_func_t) pa_sconv_s24be_to_s16ne,
[PA_SAMPLE_S24LE] = (pa_convert_func_t) pa_sconv_s24le_to_s16ne,
[PA_SAMPLE_S24_32BE] = (pa_convert_func_t) pa_sconv_s24_32be_to_s16ne,
[PA_SAMPLE_S24_32LE] = (pa_convert_func_t) pa_sconv_s24_32le_to_s16ne,
[PA_SAMPLE_ALAW] = (pa_convert_func_t) alaw_to_s16ne,
[PA_SAMPLE_ULAW] = (pa_convert_func_t) ulaw_to_s16ne
};
pa_assert(f >= 0);
pa_assert(f < PA_SAMPLE_MAX);
return to_s16ne_table[f];
}
void pa_set_convert_to_s16ne_function(pa_sample_format_t f, pa_convert_func_t func) {
pa_assert(f >= 0);
pa_assert(f < PA_SAMPLE_MAX);
return table[f];
to_s16ne_table[f] = func;
}
static pa_convert_func_t from_s16ne_table[] = {
[PA_SAMPLE_U8] = (pa_convert_func_t) u8_from_s16ne,
[PA_SAMPLE_S16NE] = (pa_convert_func_t) s16ne_to_s16ne,
[PA_SAMPLE_S16RE] = (pa_convert_func_t) s16re_to_s16ne,
[PA_SAMPLE_FLOAT32BE] = (pa_convert_func_t) pa_sconv_float32be_from_s16ne,
[PA_SAMPLE_FLOAT32LE] = (pa_convert_func_t) pa_sconv_float32le_from_s16ne,
[PA_SAMPLE_S32BE] = (pa_convert_func_t) pa_sconv_s32be_from_s16ne,
[PA_SAMPLE_S32LE] = (pa_convert_func_t) pa_sconv_s32le_from_s16ne,
[PA_SAMPLE_S24BE] = (pa_convert_func_t) pa_sconv_s24be_from_s16ne,
[PA_SAMPLE_S24LE] = (pa_convert_func_t) pa_sconv_s24le_from_s16ne,
[PA_SAMPLE_S24_32BE] = (pa_convert_func_t) pa_sconv_s24_32be_from_s16ne,
[PA_SAMPLE_S24_32LE] = (pa_convert_func_t) pa_sconv_s24_32le_from_s16ne,
[PA_SAMPLE_ALAW] = (pa_convert_func_t) alaw_from_s16ne,
[PA_SAMPLE_ULAW] = (pa_convert_func_t) ulaw_from_s16ne,
};
pa_convert_func_t pa_get_convert_from_s16ne_function(pa_sample_format_t f) {
static const pa_convert_func_t table[] = {
[PA_SAMPLE_U8] = (pa_convert_func_t) u8_from_s16ne,
[PA_SAMPLE_S16NE] = (pa_convert_func_t) s16ne_to_s16ne,
[PA_SAMPLE_S16RE] = (pa_convert_func_t) s16re_to_s16ne,
[PA_SAMPLE_FLOAT32BE] = (pa_convert_func_t) pa_sconv_float32be_from_s16ne,
[PA_SAMPLE_FLOAT32LE] = (pa_convert_func_t) pa_sconv_float32le_from_s16ne,
[PA_SAMPLE_S32BE] = (pa_convert_func_t) pa_sconv_s32be_from_s16ne,
[PA_SAMPLE_S32LE] = (pa_convert_func_t) pa_sconv_s32le_from_s16ne,
[PA_SAMPLE_S24BE] = (pa_convert_func_t) pa_sconv_s24be_from_s16ne,
[PA_SAMPLE_S24LE] = (pa_convert_func_t) pa_sconv_s24le_from_s16ne,
[PA_SAMPLE_S24_32BE] = (pa_convert_func_t) pa_sconv_s24_32be_from_s16ne,
[PA_SAMPLE_S24_32LE] = (pa_convert_func_t) pa_sconv_s24_32le_from_s16ne,
[PA_SAMPLE_ALAW] = (pa_convert_func_t) alaw_from_s16ne,
[PA_SAMPLE_ULAW] = (pa_convert_func_t) ulaw_from_s16ne,
};
pa_assert(f >= 0);
pa_assert(f < PA_SAMPLE_MAX);
return from_s16ne_table[f];
}
void pa_set_convert_from_s16ne_function(pa_sample_format_t f, pa_convert_func_t func) {
pa_assert(f >= 0);
pa_assert(f < PA_SAMPLE_MAX);
return table[f];
from_s16ne_table[f] = func;
}

View file

@ -33,4 +33,10 @@ pa_convert_func_t pa_get_convert_from_float32ne_function(pa_sample_format_t f) P
pa_convert_func_t pa_get_convert_to_s16ne_function(pa_sample_format_t f) PA_GCC_PURE;
pa_convert_func_t pa_get_convert_from_s16ne_function(pa_sample_format_t f) PA_GCC_PURE;
void pa_set_convert_to_float32ne_function(pa_sample_format_t f, pa_convert_func_t func);
void pa_set_convert_from_float32ne_function(pa_sample_format_t f, pa_convert_func_t func);
void pa_set_convert_to_s16ne_function(pa_sample_format_t f, pa_convert_func_t func);
void pa_set_convert_from_s16ne_function(pa_sample_format_t f, pa_convert_func_t func);
#endif

View file

@ -44,7 +44,7 @@
#define MEMBLOCKQ_MAXLENGTH (32*1024*1024)
#define CONVERT_BUFFER_LENGTH (PA_PAGE_SIZE)
static PA_DEFINE_CHECK_TYPE(pa_sink_input, pa_msgobject);
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);
@ -126,6 +126,8 @@ static void reset_callbacks(pa_sink_input *i) {
i->state_change = NULL;
i->may_move_to = NULL;
i->send_event = NULL;
i->volume_changed = NULL;
i->mute_changed = NULL;
}
/* Called from main context */
@ -485,7 +487,10 @@ static void sink_input_free(pa_object *o) {
pa_log_info("Freeing input %u \"%s\"", i->index, pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME)));
pa_assert(!i->thread_info.attached);
/* Side note: this function must be able to destruct properly any
* kind of sink input in any state, even those which are
* "half-moved" or are connected to sinks that have no asyncmsgq
* and are hence half-destructed themselves! */
if (i->thread_info.render_memblockq)
pa_memblockq_free(i->thread_info.render_memblockq);
@ -968,7 +973,10 @@ void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_boo
pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_SOFT_VOLUME, NULL, 0, NULL) == 0);
}
/* The virtual volume changed, let's tell people so */
/* The volume changed, let's tell people so */
if (i->volume_changed)
i->volume_changed(i);
pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
}
@ -999,6 +1007,11 @@ void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute, pa_bool_t save) {
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);
}
@ -1263,6 +1276,10 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
/* Notify everyone */
pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH], i);
if (i->volume_changed)
i->volume_changed(i);
pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
return 0;

View file

@ -192,8 +192,16 @@ struct pa_sink_input {
pa_bool_t (*may_move_to) (pa_sink_input *i, pa_sink *s); /* may be NULL */
/* If non-NULL this function is used to dispatch asynchronous
* control events. */
void (*send_event)(pa_sink_input *i, const char *event, pa_proplist* data);
* control events. Called from main context. */
void (*send_event)(pa_sink_input *i, const char *event, pa_proplist* data); /* may be NULL */
/* If non-NULL this function is called whenever the sink input
* volume changes. Called from main context */
void (*volume_changed)(pa_sink_input *i); /* may be NULL */
/* If non-NULL this function is called whenever the sink input
* mute status changes. Called from main context */
void (*mute_changed)(pa_sink_input *i); /* may be NULL */
struct {
pa_sink_input_state_t state;
@ -227,7 +235,7 @@ struct pa_sink_input {
void *userdata;
};
PA_DECLARE_CLASS(pa_sink_input);
PA_DECLARE_PUBLIC_CLASS(pa_sink_input);
#define PA_SINK_INPUT(o) pa_sink_input_cast(o)
enum {

View file

@ -52,7 +52,7 @@
#define ABSOLUTE_MAX_LATENCY (10*PA_USEC_PER_SEC)
#define DEFAULT_FIXED_LATENCY (250*PA_USEC_PER_MSEC)
static PA_DEFINE_CHECK_TYPE(pa_sink, pa_msgobject);
PA_DEFINE_PUBLIC_CLASS(pa_sink, pa_msgobject);
static void sink_free(pa_object *s);
@ -1380,9 +1380,14 @@ static void propagate_reference_volume(pa_sink *s) {
pa_cvolume_remap(&remapped, &s->channel_map, &i->channel_map);
pa_sw_cvolume_multiply(&i->volume, &remapped, &i->reference_ratio);
/* The reference volume changed, let's tell people so */
if (!pa_cvolume_equal(&old_volume, &i->volume))
/* The volume changed, let's tell people so */
if (!pa_cvolume_equal(&old_volume, &i->volume)) {
if (i->volume_changed)
i->volume_changed(i);
pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
}
}
}
@ -1522,8 +1527,13 @@ static void propagate_real_volume(pa_sink *s, const pa_cvolume *old_real_volume)
pa_sw_cvolume_multiply(&i->volume, &remapped, &i->reference_ratio);
/* Notify if something changed */
if (!pa_cvolume_equal(&old_volume, &i->volume))
if (!pa_cvolume_equal(&old_volume, &i->volume)) {
if (i->volume_changed)
i->volume_changed(i);
pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
}
}
}

View file

@ -191,7 +191,7 @@ struct pa_sink {
void *userdata;
};
PA_DECLARE_CLASS(pa_sink);
PA_DECLARE_PUBLIC_CLASS(pa_sink);
#define PA_SINK(s) (pa_sink_cast(s))
typedef enum pa_sink_message {

View file

@ -64,9 +64,8 @@ enum {
FILE_STREAM_MESSAGE_UNLINK
};
PA_DECLARE_CLASS(file_stream);
PA_DEFINE_PRIVATE_CLASS(file_stream, pa_msgobject);
#define FILE_STREAM(o) (file_stream_cast(o))
static PA_DEFINE_CHECK_TYPE(file_stream, pa_msgobject);
/* Called from main context */
static void file_stream_unlink(file_stream *u) {

View file

@ -41,7 +41,7 @@
#define MEMBLOCKQ_MAXLENGTH (32*1024*1024)
static PA_DEFINE_CHECK_TYPE(pa_source_output, pa_msgobject);
PA_DEFINE_PUBLIC_CLASS(pa_source_output, pa_msgobject);
static void source_output_free(pa_object* mo);
@ -359,8 +359,6 @@ static void source_output_free(pa_object* mo) {
pa_log_info("Freeing output %u \"%s\"", o->index, pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_MEDIA_NAME)));
pa_assert(!o->thread_info.attached);
if (o->thread_info.delay_memblockq)
pa_memblockq_free(o->thread_info.delay_memblockq);

View file

@ -182,7 +182,7 @@ struct pa_source_output {
void *userdata;
};
PA_DECLARE_CLASS(pa_source_output);
PA_DECLARE_PUBLIC_CLASS(pa_source_output);
#define PA_SOURCE_OUTPUT(o) pa_source_output_cast(o)
enum {

View file

@ -46,7 +46,7 @@
#define ABSOLUTE_MAX_LATENCY (10*PA_USEC_PER_SEC)
#define DEFAULT_FIXED_LATENCY (250*PA_USEC_PER_MSEC)
static PA_DEFINE_CHECK_TYPE(pa_source, pa_msgobject);
PA_DEFINE_PUBLIC_CLASS(pa_source, pa_msgobject);
static void source_free(pa_object *o);

View file

@ -158,7 +158,7 @@ struct pa_source {
void *userdata;
};
PA_DECLARE_CLASS(pa_source);
PA_DECLARE_PUBLIC_CLASS(pa_source);
#define PA_SOURCE(s) pa_source_cast(s)
typedef enum pa_source_message {

195
src/pulsecore/svolume_arm.c Normal file
View file

@ -0,0 +1,195 @@
/***
This file is part of PulseAudio.
Copyright 2004-2006 Lennart Poettering
Copyright 2009 Wim Taymans <wim.taymans@collabora.co.uk>
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 <pulse/timeval.h>
#include <pulsecore/random.h>
#include <pulsecore/macro.h>
#include <pulsecore/g711.h>
#include <pulsecore/core-util.h>
#include "cpu-arm.h"
#include "sample-util.h"
#include "endianmacros.h"
#if defined (__arm__)
#define MOD_INC() \
" subs r0, r6, %2 \n\t" \
" addcs r0, %1 \n\t" \
" movcs r6, r0 \n\t"
static void
pa_volume_s16ne_arm (int16_t *samples, int32_t *volumes, unsigned channels, unsigned length)
{
int32_t *ve;
channels = PA_MAX (4U, channels);
ve = volumes + channels;
__asm__ __volatile__ (
" mov r6, %1 \n\t"
" mov %3, %3, LSR #1 \n\t" /* length /= sizeof (int16_t) */
" tst %3, #1 \n\t" /* check for odd samples */
" beq 2f \n\t"
"1: \n\t"
" ldr r0, [r6], #4 \n\t" /* odd samples volumes */
" ldrh r2, [%0] \n\t"
" smulwb r0, r0, r2 \n\t"
" ssat r0, #16, r0 \n\t"
" strh r0, [%0], #2 \n\t"
MOD_INC()
"2: \n\t"
" mov %3, %3, LSR #1 \n\t"
" tst %3, #1 \n\t" /* check for odd samples */
" beq 4f \n\t"
"3: \n\t"
" ldrd r2, [r6], #8 \n\t" /* 2 samples at a time */
" ldr r0, [%0] \n\t"
" smulwt r2, r2, r0 \n\t"
" smulwb r3, r3, r0 \n\t"
" ssat r2, #16, r2 \n\t"
" ssat r3, #16, r3 \n\t"
" pkhbt r0, r3, r2, LSL #16 \n\t"
" str r0, [%0], #4 \n\t"
MOD_INC()
"4: \n\t"
" movs %3, %3, LSR #1 \n\t"
" beq 6f \n\t"
"5: \n\t"
" ldrd r2, [r6], #8 \n\t" /* 4 samples at a time */
" ldrd r4, [r6], #8 \n\t"
" ldrd r0, [%0] \n\t"
" smulwt r2, r2, r0 \n\t"
" smulwb r3, r3, r0 \n\t"
" smulwt r4, r4, r1 \n\t"
" smulwb r5, r5, r1 \n\t"
" ssat r2, #16, r2 \n\t"
" ssat r3, #16, r3 \n\t"
" ssat r4, #16, r4 \n\t"
" ssat r5, #16, r5 \n\t"
" pkhbt r0, r3, r2, LSL #16 \n\t"
" pkhbt r1, r5, r4, LSL #16 \n\t"
" strd r0, [%0], #8 \n\t"
MOD_INC()
" subs %3, %3, #1 \n\t"
" bne 5b \n\t"
"6: \n\t"
: "+r" (samples), "+r" (volumes), "+r" (ve), "+r" (length)
:
: "r6", "r5", "r4", "r3", "r2", "r1", "r0", "cc"
);
}
#undef RUN_TEST
#ifdef RUN_TEST
#define CHANNELS 2
#define SAMPLES 1023
#define TIMES 1000
#define PADDING 16
static void run_test (void) {
int16_t samples[SAMPLES];
int16_t samples_ref[SAMPLES];
int16_t samples_orig[SAMPLES];
int32_t volumes[CHANNELS + PADDING];
int i, j, padding;
pa_do_volume_func_t func;
pa_usec_t start, stop;
func = pa_get_volume_func (PA_SAMPLE_S16NE);
printf ("checking ARM %zd\n", sizeof (samples));
pa_random (samples, sizeof (samples));
memcpy (samples_ref, samples, sizeof (samples));
memcpy (samples_orig, samples, sizeof (samples));
for (i = 0; i < CHANNELS; i++)
volumes[i] = rand() >> 1;
for (padding = 0; padding < PADDING; padding++, i++)
volumes[i] = volumes[padding];
func (samples_ref, volumes, CHANNELS, sizeof (samples));
pa_volume_s16ne_arm (samples, volumes, CHANNELS, sizeof (samples));
for (i = 0; i < SAMPLES; i++) {
if (samples[i] != samples_ref[i]) {
printf ("%d: %04x != %04x (%04x * %04x)\n", i, samples[i], samples_ref[i],
samples_orig[i], volumes[i % CHANNELS]);
}
}
start = pa_rtclock_now();
for (j = 0; j < TIMES; j++) {
memcpy (samples, samples_orig, sizeof (samples));
pa_volume_s16ne_arm (samples, volumes, CHANNELS, sizeof (samples));
}
stop = pa_rtclock_now();
pa_log_info("ARM: %llu usec.", (long long unsigned int) (stop - start));
start = pa_rtclock_now();
for (j = 0; j < TIMES; j++) {
memcpy (samples_ref, samples_orig, sizeof (samples));
func (samples_ref, volumes, CHANNELS, sizeof (samples));
}
stop = pa_rtclock_now();
pa_log_info("ref: %llu usec.", (long long unsigned int) (stop - start));
}
#endif
#endif /* defined (__arm__) */
void pa_volume_func_init_arm (pa_cpu_arm_flag_t flags) {
#if defined (__arm__)
pa_log_info("Initialising ARM optimized functions.");
#ifdef RUN_TEST
run_test ();
#endif
pa_set_volume_func (PA_SAMPLE_S16NE, (pa_do_volume_func_t) pa_volume_s16ne_arm);
#endif /* defined (__arm__) */
}

335
src/pulsecore/svolume_c.c Normal file
View file

@ -0,0 +1,335 @@
/***
This file is part of PulseAudio.
Copyright 2004-2006 Lennart Poettering
Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
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 <pulsecore/macro.h>
#include <pulsecore/g711.h>
#include <pulsecore/core-util.h>
#include "sample-util.h"
#include "endianmacros.h"
static void
pa_volume_u8_c (uint8_t *samples, int32_t *volumes, unsigned channels, unsigned length)
{
unsigned channel;
for (channel = 0; length; length--) {
int32_t t, hi, lo;
hi = volumes[channel] >> 16;
lo = volumes[channel] & 0xFFFF;
t = (int32_t) *samples - 0x80;
t = ((t * lo) >> 16) + (t * hi);
t = PA_CLAMP_UNLIKELY(t, -0x80, 0x7F);
*samples++ = (uint8_t) (t + 0x80);
if (PA_UNLIKELY(++channel >= channels))
channel = 0;
}
}
static void
pa_volume_alaw_c (uint8_t *samples, int32_t *volumes, unsigned channels, unsigned length)
{
unsigned channel;
for (channel = 0; length; length--) {
int32_t t, hi, lo;
hi = volumes[channel] >> 16;
lo = volumes[channel] & 0xFFFF;
t = (int32_t) st_alaw2linear16(*samples);
t = ((t * lo) >> 16) + (t * hi);
t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
*samples++ = (uint8_t) st_13linear2alaw((int16_t) t >> 3);
if (PA_UNLIKELY(++channel >= channels))
channel = 0;
}
}
static void
pa_volume_ulaw_c (uint8_t *samples, int32_t *volumes, unsigned channels, unsigned length)
{
unsigned channel;
for (channel = 0; length; length--) {
int32_t t, hi, lo;
hi = volumes[channel] >> 16;
lo = volumes[channel] & 0xFFFF;
t = (int32_t) st_ulaw2linear16(*samples);
t = ((t * lo) >> 16) + (t * hi);
t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
*samples++ = (uint8_t) st_14linear2ulaw((int16_t) t >> 2);
if (PA_UNLIKELY(++channel >= channels))
channel = 0;
}
}
static void
pa_volume_s16ne_c (int16_t *samples, int32_t *volumes, unsigned channels, unsigned length)
{
unsigned channel;
length /= sizeof (int16_t);
for (channel = 0; length; length--) {
int32_t t, hi, lo;
/* Multiplying the 32bit volume factor with the 16bit
* sample might result in an 48bit value. We want to
* do without 64 bit integers and hence do the
* multiplication independantly for the HI and LO part
* of the volume. */
hi = volumes[channel] >> 16;
lo = volumes[channel] & 0xFFFF;
t = (int32_t)(*samples);
t = ((t * lo) >> 16) + (t * hi);
t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
*samples++ = (int16_t) t;
if (PA_UNLIKELY(++channel >= channels))
channel = 0;
}
}
static void
pa_volume_s16re_c (int16_t *samples, int32_t *volumes, unsigned channels, unsigned length)
{
unsigned channel;
length /= sizeof (int16_t);
for (channel = 0; length; length--) {
int32_t t, hi, lo;
hi = volumes[channel] >> 16;
lo = volumes[channel] & 0xFFFF;
t = (int32_t) PA_INT16_SWAP(*samples);
t = ((t * lo) >> 16) + (t * hi);
t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
*samples++ = PA_INT16_SWAP((int16_t) t);
if (PA_UNLIKELY(++channel >= channels))
channel = 0;
}
}
static void
pa_volume_float32ne_c (float *samples, float *volumes, unsigned channels, unsigned length)
{
unsigned channel;
length /= sizeof (float);
for (channel = 0; length; length--) {
*samples++ *= volumes[channel];
if (PA_UNLIKELY(++channel >= channels))
channel = 0;
}
}
static void
pa_volume_float32re_c (float *samples, float *volumes, unsigned channels, unsigned length)
{
unsigned channel;
length /= sizeof (float);
for (channel = 0; length; length--) {
float t;
t = PA_FLOAT32_SWAP(*samples);
t *= volumes[channel];
*samples++ = PA_FLOAT32_SWAP(t);
if (PA_UNLIKELY(++channel >= channels))
channel = 0;
}
}
static void
pa_volume_s32ne_c (int32_t *samples, int32_t *volumes, unsigned channels, unsigned length)
{
unsigned channel;
length /= sizeof (int32_t);
for (channel = 0; length; length--) {
int64_t t;
t = (int64_t)(*samples);
t = (t * volumes[channel]) >> 16;
t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
*samples++ = (int32_t) t;
if (PA_UNLIKELY(++channel >= channels))
channel = 0;
}
}
static void
pa_volume_s32re_c (int32_t *samples, int32_t *volumes, unsigned channels, unsigned length)
{
unsigned channel;
length /= sizeof (int32_t);
for (channel = 0; length; length--) {
int64_t t;
t = (int64_t) PA_INT32_SWAP(*samples);
t = (t * volumes[channel]) >> 16;
t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
*samples++ = PA_INT32_SWAP((int32_t) t);
if (PA_UNLIKELY(++channel >= channels))
channel = 0;
}
}
static void
pa_volume_s24ne_c (uint8_t *samples, int32_t *volumes, unsigned channels, unsigned length)
{
unsigned channel;
uint8_t *e;
e = samples + length;
for (channel = 0; samples < e; samples += 3) {
int64_t t;
t = (int64_t)((int32_t) (PA_READ24NE(samples) << 8));
t = (t * volumes[channel]) >> 16;
t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
PA_WRITE24NE(samples, ((uint32_t) (int32_t) t) >> 8);
if (PA_UNLIKELY(++channel >= channels))
channel = 0;
}
}
static void
pa_volume_s24re_c (uint8_t *samples, int32_t *volumes, unsigned channels, unsigned length)
{
unsigned channel;
uint8_t *e;
e = samples + length;
for (channel = 0; samples < e; samples += 3) {
int64_t t;
t = (int64_t)((int32_t) (PA_READ24RE(samples) << 8));
t = (t * volumes[channel]) >> 16;
t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
PA_WRITE24RE(samples, ((uint32_t) (int32_t) t) >> 8);
if (PA_UNLIKELY(++channel >= channels))
channel = 0;
}
}
static void
pa_volume_s24_32ne_c (uint32_t *samples, int32_t *volumes, unsigned channels, unsigned length)
{
unsigned channel;
length /= sizeof (uint32_t);
for (channel = 0; length; length--) {
int64_t t;
t = (int64_t) ((int32_t) (*samples << 8));
t = (t * volumes[channel]) >> 16;
t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
*samples++ = ((uint32_t) ((int32_t) t)) >> 8;
if (PA_UNLIKELY(++channel >= channels))
channel = 0;
}
}
static void
pa_volume_s24_32re_c (uint32_t *samples, int32_t *volumes, unsigned channels, unsigned length)
{
unsigned channel;
length /= sizeof (uint32_t);
for (channel = 0; length; length--) {
int64_t t;
t = (int64_t) ((int32_t) (PA_UINT32_SWAP(*samples) << 8));
t = (t * volumes[channel]) >> 16;
t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
*samples++ = PA_UINT32_SWAP(((uint32_t) ((int32_t) t)) >> 8);
if (PA_UNLIKELY(++channel >= channels))
channel = 0;
}
}
static pa_do_volume_func_t do_volume_table[] =
{
[PA_SAMPLE_U8] = (pa_do_volume_func_t) pa_volume_u8_c,
[PA_SAMPLE_ALAW] = (pa_do_volume_func_t) pa_volume_alaw_c,
[PA_SAMPLE_ULAW] = (pa_do_volume_func_t) pa_volume_ulaw_c,
[PA_SAMPLE_S16NE] = (pa_do_volume_func_t) pa_volume_s16ne_c,
[PA_SAMPLE_S16RE] = (pa_do_volume_func_t) pa_volume_s16re_c,
[PA_SAMPLE_FLOAT32NE] = (pa_do_volume_func_t) pa_volume_float32ne_c,
[PA_SAMPLE_FLOAT32RE] = (pa_do_volume_func_t) pa_volume_float32re_c,
[PA_SAMPLE_S32NE] = (pa_do_volume_func_t) pa_volume_s32ne_c,
[PA_SAMPLE_S32RE] = (pa_do_volume_func_t) pa_volume_s32re_c,
[PA_SAMPLE_S24NE] = (pa_do_volume_func_t) pa_volume_s24ne_c,
[PA_SAMPLE_S24RE] = (pa_do_volume_func_t) pa_volume_s24re_c,
[PA_SAMPLE_S24_32NE] = (pa_do_volume_func_t) pa_volume_s24_32ne_c,
[PA_SAMPLE_S24_32RE] = (pa_do_volume_func_t) pa_volume_s24_32re_c
};
pa_do_volume_func_t pa_get_volume_func(pa_sample_format_t f) {
pa_assert(f >= 0);
pa_assert(f < PA_SAMPLE_MAX);
return do_volume_table[f];
}
void pa_set_volume_func(pa_sample_format_t f, pa_do_volume_func_t func) {
pa_assert(f >= 0);
pa_assert(f < PA_SAMPLE_MAX);
do_volume_table[f] = func;
}

313
src/pulsecore/svolume_mmx.c Normal file
View file

@ -0,0 +1,313 @@
/***
This file is part of PulseAudio.
Copyright 2004-2006 Lennart Poettering
Copyright 2009 Wim Taymans <wim.taymans@collabora.co.uk>
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 <pulse/timeval.h>
#include <pulsecore/random.h>
#include <pulsecore/macro.h>
#include <pulsecore/g711.h>
#include <pulsecore/core-util.h>
#include "cpu-x86.h"
#include "sample-util.h"
#include "endianmacros.h"
#if defined (__i386__) || defined (__amd64__)
/* in s: 2 int16_t samples
* in v: 2 int32_t volumes, fixed point 16:16
* out s: contains scaled and clamped int16_t samples.
*
* We calculate the high 32 bits of a 32x16 multiply which we then
* clamp to 16 bits. The calulcation is:
*
* vl = (v & 0xffff)
* vh = (v >> 16)
* s = ((s * vl) >> 16) + (s * vh);
*
* For the first multiply we have to do a sign correction as we need to
* multiply a signed int with an unsigned int. Hacker's delight 8-3 gives a
* simple formula to correct the sign of the high word after the signed
* multiply.
*/
#define VOLUME_32x16(s,v) /* .. | vh | vl | */ \
" pxor %%mm4, %%mm4 \n\t" /* .. | 0 | 0 | */ \
" punpcklwd %%mm4, "#s" \n\t" /* .. | 0 | p0 | */ \
" pcmpgtw "#v", %%mm4 \n\t" /* .. | 0 | s(vl) | */ \
" pand "#s", %%mm4 \n\t" /* .. | 0 | (p0) | (vl >> 15) & p */ \
" movq %%mm6, %%mm5 \n\t" /* .. | ffff | 0 | */ \
" pand "#v", %%mm5 \n\t" /* .. | vh | 0 | */ \
" por %%mm5, %%mm4 \n\t" /* .. | vh | (p0) | */ \
" pmulhw "#s", "#v" \n\t" /* .. | 0 | vl*p0 | */ \
" paddw %%mm4, "#v" \n\t" /* .. | vh | vl*p0 | vh + sign correct */ \
" pslld $16, "#s" \n\t" /* .. | p0 | 0 | */ \
" por %%mm7, "#s" \n\t" /* .. | p0 | 1 | */ \
" pmaddwd "#s", "#v" \n\t" /* .. | p0 * v0 | */ \
" packssdw "#v", "#v" \n\t" /* .. | p1*v1 | p0*v0 | */
/* approximately advances %3 = (%3 + a) % b. This function requires that
* a <= b. */
#define MOD_ADD(a,b) \
" add "#a", %3 \n\t" \
" mov %3, %4 \n\t" \
" sub "#b", %4 \n\t" \
" cmovae %4, %3 \n\t"
/* swap 16 bits */
#define SWAP_16(s) \
" movq "#s", %%mm4 \n\t" /* .. | h l | */ \
" psrlw $8, %%mm4 \n\t" /* .. | 0 h | */ \
" psllw $8, "#s" \n\t" /* .. | l 0 | */ \
" por %%mm4, "#s" \n\t" /* .. | l h | */
/* swap 2 registers 16 bits for better pairing */
#define SWAP_16_2(s1,s2) \
" movq "#s1", %%mm4 \n\t" /* .. | h l | */ \
" movq "#s2", %%mm5 \n\t" \
" psrlw $8, %%mm4 \n\t" /* .. | 0 h | */ \
" psrlw $8, %%mm5 \n\t" \
" psllw $8, "#s1" \n\t" /* .. | l 0 | */ \
" psllw $8, "#s2" \n\t" \
" por %%mm4, "#s1" \n\t" /* .. | l h | */ \
" por %%mm5, "#s2" \n\t"
static void
pa_volume_s16ne_mmx (int16_t *samples, int32_t *volumes, unsigned channels, unsigned length)
{
pa_reg_x86 channel, temp;
/* the max number of samples we process at a time, this is also the max amount
* we overread the volume array, which should have enough padding. */
channels = PA_MAX (4U, channels);
__asm__ __volatile__ (
" xor %3, %3 \n\t"
" sar $1, %2 \n\t" /* length /= sizeof (int16_t) */
" pcmpeqw %%mm6, %%mm6 \n\t" /* .. | ffff | ffff | */
" pcmpeqw %%mm7, %%mm7 \n\t" /* .. | ffff | ffff | */
" pslld $16, %%mm6 \n\t" /* .. | ffff | 0 | */
" psrld $31, %%mm7 \n\t" /* .. | 0 | 1 | */
" test $1, %2 \n\t" /* check for odd samples */
" je 2f \n\t"
" movd (%1, %3, 4), %%mm0 \n\t" /* | v0h | v0l | */
" movw (%0), %w4 \n\t" /* .. | p0 | */
" movd %4, %%mm1 \n\t"
VOLUME_32x16 (%%mm1, %%mm0)
" movd %%mm0, %4 \n\t" /* .. | p0*v0 | */
" movw %w4, (%0) \n\t"
" add $2, %0 \n\t"
MOD_ADD ($1, %5)
"2: \n\t"
" sar $1, %2 \n\t" /* prepare for processing 2 samples at a time */
" test $1, %2 \n\t" /* check for odd samples */
" je 4f \n\t"
"3: \n\t" /* do samples in groups of 2 */
" movq (%1, %3, 4), %%mm0 \n\t" /* | v1h | v1l | v0h | v0l | */
" movd (%0), %%mm1 \n\t" /* .. | p1 | p0 | */
VOLUME_32x16 (%%mm1, %%mm0)
" movd %%mm0, (%0) \n\t" /* .. | p1*v1 | p0*v0 | */
" add $4, %0 \n\t"
MOD_ADD ($2, %5)
"4: \n\t"
" sar $1, %2 \n\t" /* prepare for processing 4 samples at a time */
" cmp $0, %2 \n\t"
" je 6f \n\t"
"5: \n\t" /* do samples in groups of 4 */
" movq (%1, %3, 4), %%mm0 \n\t" /* | v1h | v1l | v0h | v0l | */
" movq 8(%1, %3, 4), %%mm2 \n\t" /* | v3h | v3l | v2h | v2l | */
" movd (%0), %%mm1 \n\t" /* .. | p1 | p0 | */
" movd 4(%0), %%mm3 \n\t" /* .. | p3 | p2 | */
VOLUME_32x16 (%%mm1, %%mm0)
VOLUME_32x16 (%%mm3, %%mm2)
" movd %%mm0, (%0) \n\t" /* .. | p1*v1 | p0*v0 | */
" movd %%mm2, 4(%0) \n\t" /* .. | p3*v3 | p2*v2 | */
" add $8, %0 \n\t"
MOD_ADD ($4, %5)
" dec %2 \n\t"
" jne 5b \n\t"
"6: \n\t"
" emms \n\t"
: "+r" (samples), "+r" (volumes), "+r" (length), "=D" ((pa_reg_x86)channel), "=&r" (temp)
: "r" ((pa_reg_x86)channels)
: "cc"
);
}
static void
pa_volume_s16re_mmx (int16_t *samples, int32_t *volumes, unsigned channels, unsigned length)
{
pa_reg_x86 channel, temp;
/* the max number of samples we process at a time, this is also the max amount
* we overread the volume array, which should have enough padding. */
channels = PA_MAX (4U, channels);
__asm__ __volatile__ (
" xor %3, %3 \n\t"
" sar $1, %2 \n\t" /* length /= sizeof (int16_t) */
" pcmpeqw %%mm6, %%mm6 \n\t" /* .. | ffff | ffff | */
" pcmpeqw %%mm7, %%mm7 \n\t" /* .. | ffff | ffff | */
" pslld $16, %%mm6 \n\t" /* .. | ffff | 0 | */
" psrld $31, %%mm7 \n\t" /* .. | 0 | 1 | */
" test $1, %2 \n\t" /* check for odd samples */
" je 2f \n\t"
" movd (%1, %3, 4), %%mm0 \n\t" /* | v0h | v0l | */
" movw (%0), %w4 \n\t" /* .. | p0 | */
" rorw $8, %w4 \n\t"
" movd %4, %%mm1 \n\t"
VOLUME_32x16 (%%mm1, %%mm0)
" movd %%mm0, %4 \n\t" /* .. | p0*v0 | */
" rorw $8, %w4 \n\t"
" movw %w4, (%0) \n\t"
" add $2, %0 \n\t"
MOD_ADD ($1, %5)
"2: \n\t"
" sar $1, %2 \n\t" /* prepare for processing 2 samples at a time */
" test $1, %2 \n\t" /* check for odd samples */
" je 4f \n\t"
"3: \n\t" /* do samples in groups of 2 */
" movq (%1, %3, 4), %%mm0 \n\t" /* | v1h | v1l | v0h | v0l | */
" movd (%0), %%mm1 \n\t" /* .. | p1 | p0 | */
SWAP_16 (%%mm1)
VOLUME_32x16 (%%mm1, %%mm0)
SWAP_16 (%%mm0)
" movd %%mm0, (%0) \n\t" /* .. | p1*v1 | p0*v0 | */
" add $4, %0 \n\t"
MOD_ADD ($2, %5)
"4: \n\t"
" sar $1, %2 \n\t" /* prepare for processing 4 samples at a time */
" cmp $0, %2 \n\t"
" je 6f \n\t"
"5: \n\t" /* do samples in groups of 4 */
" movq (%1, %3, 4), %%mm0 \n\t" /* | v1h | v1l | v0h | v0l | */
" movq 8(%1, %3, 4), %%mm2 \n\t" /* | v3h | v3l | v2h | v2l | */
" movd (%0), %%mm1 \n\t" /* .. | p1 | p0 | */
" movd 4(%0), %%mm3 \n\t" /* .. | p3 | p2 | */
SWAP_16_2 (%%mm1, %%mm3)
VOLUME_32x16 (%%mm1, %%mm0)
VOLUME_32x16 (%%mm3, %%mm2)
SWAP_16_2 (%%mm0, %%mm2)
" movd %%mm0, (%0) \n\t" /* .. | p1*v1 | p0*v0 | */
" movd %%mm2, 4(%0) \n\t" /* .. | p3*v3 | p2*v2 | */
" add $8, %0 \n\t"
MOD_ADD ($4, %5)
" dec %2 \n\t"
" jne 5b \n\t"
"6: \n\t"
" emms \n\t"
: "+r" (samples), "+r" (volumes), "+r" (length), "=D" ((pa_reg_x86)channel), "=&r" (temp)
: "r" ((pa_reg_x86)channels)
: "cc"
);
}
#undef RUN_TEST
#ifdef RUN_TEST
#define CHANNELS 2
#define SAMPLES 1021
#define TIMES 1000
#define PADDING 16
static void run_test (void) {
int16_t samples[SAMPLES];
int16_t samples_ref[SAMPLES];
int16_t samples_orig[SAMPLES];
int32_t volumes[CHANNELS + PADDING];
int i, j, padding;
pa_do_volume_func_t func;
pa_usec_t start, stop;
func = pa_get_volume_func (PA_SAMPLE_S16NE);
printf ("checking MMX %zd\n", sizeof (samples));
pa_random (samples, sizeof (samples));
memcpy (samples_ref, samples, sizeof (samples));
memcpy (samples_orig, samples, sizeof (samples));
for (i = 0; i < CHANNELS; i++)
volumes[i] = rand() >> 1;
for (padding = 0; padding < PADDING; padding++, i++)
volumes[i] = volumes[padding];
func (samples_ref, volumes, CHANNELS, sizeof (samples));
pa_volume_s16ne_mmx (samples, volumes, CHANNELS, sizeof (samples));
for (i = 0; i < SAMPLES; i++) {
if (samples[i] != samples_ref[i]) {
printf ("%d: %04x != %04x (%04x * %04x)\n", i, samples[i], samples_ref[i],
samples_orig[i], volumes[i % CHANNELS]);
}
}
start = pa_rtclock_now();
for (j = 0; j < TIMES; j++) {
memcpy (samples, samples_orig, sizeof (samples));
pa_volume_s16ne_mmx (samples, volumes, CHANNELS, sizeof (samples));
}
stop = pa_rtclock_now();
pa_log_info("MMX: %llu usec.", (long long unsigned int)(stop - start));
start = pa_rtclock_now();
for (j = 0; j < TIMES; j++) {
memcpy (samples_ref, samples_orig, sizeof (samples));
func (samples_ref, volumes, CHANNELS, sizeof (samples));
}
stop = pa_rtclock_now();
pa_log_info("ref: %llu usec.", (long long unsigned int)(stop - start));
}
#endif
#endif /* defined (__i386__) || defined (__amd64__) */
void pa_volume_func_init_mmx (pa_cpu_x86_flag_t flags) {
#if defined (__i386__) || defined (__amd64__)
pa_log_info("Initialising MMX optimized functions.");
#ifdef RUN_TEST
run_test ();
#endif
pa_set_volume_func (PA_SAMPLE_S16NE, (pa_do_volume_func_t) pa_volume_s16ne_mmx);
pa_set_volume_func (PA_SAMPLE_S16RE, (pa_do_volume_func_t) pa_volume_s16re_mmx);
#endif /* defined (__i386__) || defined (__amd64__) */
}

314
src/pulsecore/svolume_sse.c Normal file
View file

@ -0,0 +1,314 @@
/***
This file is part of PulseAudio.
Copyright 2004-2006 Lennart Poettering
Copyright 2009 Wim Taymans <wim.taymans@collabora.co.uk>
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 <pulse/timeval.h>
#include <pulsecore/random.h>
#include <pulsecore/macro.h>
#include <pulsecore/g711.h>
#include <pulsecore/core-util.h>
#include "cpu-x86.h"
#include "sample-util.h"
#include "endianmacros.h"
#if defined (__i386__) || defined (__amd64__)
#define VOLUME_32x16(s,v) /* .. | vh | vl | */ \
" pxor %%xmm4, %%xmm4 \n\t" /* .. | 0 | 0 | */ \
" punpcklwd %%xmm4, "#s" \n\t" /* .. | 0 | p0 | */ \
" pcmpgtw "#s", %%xmm4 \n\t" /* .. | 0 | s(p0) | */ \
" pand "#v", %%xmm4 \n\t" /* .. | 0 | (vl) | */ \
" movdqa "#s", %%xmm5 \n\t" \
" pmulhuw "#v", "#s" \n\t" /* .. | 0 | vl*p0 | */ \
" psubd %%xmm4, "#s" \n\t" /* .. | 0 | vl*p0 | + sign correct */ \
" psrld $16, "#v" \n\t" /* .. | p0 | 0 | */ \
" pmaddwd %%xmm5, "#v" \n\t" /* .. | p0 * vh | */ \
" paddd "#s", "#v" \n\t" /* .. | p0 * v0 | */ \
" packssdw "#v", "#v" \n\t" /* .. | p1*v1 | p0*v0 | */
#define MOD_ADD(a,b) \
" add "#a", %3 \n\t" /* channel += inc */ \
" mov %3, %4 \n\t" \
" sub "#b", %4 \n\t" /* tmp = channel - channels */ \
" cmovae %4, %3 \n\t" /* if (tmp >= 0) channel = tmp */
/* swap 16 bits */
#define SWAP_16(s) \
" movdqa "#s", %%xmm4 \n\t" /* .. | h l | */ \
" psrlw $8, %%xmm4 \n\t" /* .. | 0 h | */ \
" psllw $8, "#s" \n\t" /* .. | l 0 | */ \
" por %%xmm4, "#s" \n\t" /* .. | l h | */
/* swap 2 registers 16 bits for better pairing */
#define SWAP_16_2(s1,s2) \
" movdqa "#s1", %%xmm4 \n\t" /* .. | h l | */ \
" movdqa "#s2", %%xmm5 \n\t" \
" psrlw $8, %%xmm4 \n\t" /* .. | 0 h | */ \
" psrlw $8, %%xmm5 \n\t" \
" psllw $8, "#s1" \n\t" /* .. | l 0 | */ \
" psllw $8, "#s2" \n\t" \
" por %%xmm4, "#s1" \n\t" /* .. | l h | */ \
" por %%xmm5, "#s2" \n\t"
static void
pa_volume_s16ne_sse (int16_t *samples, int32_t *volumes, unsigned channels, unsigned length)
{
pa_reg_x86 channel, temp;
/* the max number of samples we process at a time, this is also the max amount
* we overread the volume array, which should have enough padding. */
channels = PA_MAX (8U, channels);
__asm__ __volatile__ (
" xor %3, %3 \n\t"
" sar $1, %2 \n\t" /* length /= sizeof (int16_t) */
" test $1, %2 \n\t" /* check for odd samples */
" je 2f \n\t"
" movd (%1, %3, 4), %%xmm0 \n\t" /* | v0h | v0l | */
" movw (%0), %w4 \n\t" /* .. | p0 | */
" movd %4, %%xmm1 \n\t"
VOLUME_32x16 (%%xmm1, %%xmm0)
" movd %%xmm0, %4 \n\t" /* .. | p0*v0 | */
" movw %w4, (%0) \n\t"
" add $2, %0 \n\t"
MOD_ADD ($1, %5)
"2: \n\t"
" sar $1, %2 \n\t" /* prepare for processing 2 samples at a time */
" test $1, %2 \n\t"
" je 4f \n\t"
"3: \n\t" /* do samples in groups of 2 */
" movq (%1, %3, 4), %%xmm0 \n\t" /* | v1h | v1l | v0h | v0l | */
" movd (%0), %%xmm1 \n\t" /* .. | p1 | p0 | */
VOLUME_32x16 (%%xmm1, %%xmm0)
" movd %%xmm0, (%0) \n\t" /* .. | p1*v1 | p0*v0 | */
" add $4, %0 \n\t"
MOD_ADD ($2, %5)
"4: \n\t"
" sar $1, %2 \n\t" /* prepare for processing 4 samples at a time */
" test $1, %2 \n\t"
" je 6f \n\t"
/* FIXME, we can do aligned access of the volume values if we can guarantee
* that the array is 16 bytes aligned, we probably have to do the odd values
* after this then. */
"5: \n\t" /* do samples in groups of 4 */
" movdqu (%1, %3, 4), %%xmm0 \n\t" /* | v3h | v3l .. v0h | v0l | */
" movq (%0), %%xmm1 \n\t" /* .. | p3 .. p0 | */
VOLUME_32x16 (%%xmm1, %%xmm0)
" movq %%xmm0, (%0) \n\t" /* .. | p3*v3 .. p0*v0 | */
" add $8, %0 \n\t"
MOD_ADD ($4, %5)
"6: \n\t"
" sar $1, %2 \n\t" /* prepare for processing 8 samples at a time */
" cmp $0, %2 \n\t"
" je 8f \n\t"
"7: \n\t" /* do samples in groups of 8 */
" movdqu (%1, %3, 4), %%xmm0 \n\t" /* | v3h | v3l .. v0h | v0l | */
" movdqu 16(%1, %3, 4), %%xmm2 \n\t" /* | v7h | v7l .. v4h | v4l | */
" movq (%0), %%xmm1 \n\t" /* .. | p3 .. p0 | */
" movq 8(%0), %%xmm3 \n\t" /* .. | p7 .. p4 | */
VOLUME_32x16 (%%xmm1, %%xmm0)
VOLUME_32x16 (%%xmm3, %%xmm2)
" movq %%xmm0, (%0) \n\t" /* .. | p3*v3 .. p0*v0 | */
" movq %%xmm2, 8(%0) \n\t" /* .. | p7*v7 .. p4*v4 | */
" add $16, %0 \n\t"
MOD_ADD ($8, %5)
" dec %2 \n\t"
" jne 7b \n\t"
"8: \n\t"
: "+r" (samples), "+r" (volumes), "+r" (length), "=D" (channel), "=&r" (temp)
: "r" ((pa_reg_x86)channels)
: "cc"
);
}
static void
pa_volume_s16re_sse (int16_t *samples, int32_t *volumes, unsigned channels, unsigned length)
{
pa_reg_x86 channel, temp;
/* the max number of samples we process at a time, this is also the max amount
* we overread the volume array, which should have enough padding. */
channels = PA_MAX (8U, channels);
__asm__ __volatile__ (
" xor %3, %3 \n\t"
" sar $1, %2 \n\t" /* length /= sizeof (int16_t) */
" test $1, %2 \n\t" /* check for odd samples */
" je 2f \n\t"
" movd (%1, %3, 4), %%xmm0 \n\t" /* | v0h | v0l | */
" movw (%0), %w4 \n\t" /* .. | p0 | */
" rorw $8, %w4 \n\t"
" movd %4, %%xmm1 \n\t"
VOLUME_32x16 (%%xmm1, %%xmm0)
" movd %%xmm0, %4 \n\t" /* .. | p0*v0 | */
" rorw $8, %w4 \n\t"
" movw %w4, (%0) \n\t"
" add $2, %0 \n\t"
MOD_ADD ($1, %5)
"2: \n\t"
" sar $1, %2 \n\t" /* prepare for processing 2 samples at a time */
" test $1, %2 \n\t"
" je 4f \n\t"
"3: \n\t" /* do samples in groups of 2 */
" movq (%1, %3, 4), %%xmm0 \n\t" /* | v1h | v1l | v0h | v0l | */
" movd (%0), %%xmm1 \n\t" /* .. | p1 | p0 | */
SWAP_16 (%%xmm1)
VOLUME_32x16 (%%xmm1, %%xmm0)
SWAP_16 (%%xmm0)
" movd %%xmm0, (%0) \n\t" /* .. | p1*v1 | p0*v0 | */
" add $4, %0 \n\t"
MOD_ADD ($2, %5)
"4: \n\t"
" sar $1, %2 \n\t" /* prepare for processing 4 samples at a time */
" test $1, %2 \n\t"
" je 6f \n\t"
/* FIXME, we can do aligned access of the volume values if we can guarantee
* that the array is 16 bytes aligned, we probably have to do the odd values
* after this then. */
"5: \n\t" /* do samples in groups of 4 */
" movdqu (%1, %3, 4), %%xmm0 \n\t" /* | v3h | v3l .. v0h | v0l | */
" movq (%0), %%xmm1 \n\t" /* .. | p3 .. p0 | */
SWAP_16 (%%xmm1)
VOLUME_32x16 (%%xmm1, %%xmm0)
SWAP_16 (%%xmm0)
" movq %%xmm0, (%0) \n\t" /* .. | p3*v3 .. p0*v0 | */
" add $8, %0 \n\t"
MOD_ADD ($4, %5)
"6: \n\t"
" sar $1, %2 \n\t" /* prepare for processing 8 samples at a time */
" cmp $0, %2 \n\t"
" je 8f \n\t"
"7: \n\t" /* do samples in groups of 8 */
" movdqu (%1, %3, 4), %%xmm0 \n\t" /* | v3h | v3l .. v0h | v0l | */
" movdqu 16(%1, %3, 4), %%xmm2 \n\t" /* | v7h | v7l .. v4h | v4l | */
" movq (%0), %%xmm1 \n\t" /* .. | p3 .. p0 | */
" movq 8(%0), %%xmm3 \n\t" /* .. | p7 .. p4 | */
SWAP_16_2 (%%xmm1, %%xmm3)
VOLUME_32x16 (%%xmm1, %%xmm0)
VOLUME_32x16 (%%xmm3, %%xmm2)
SWAP_16_2 (%%xmm0, %%xmm2)
" movq %%xmm0, (%0) \n\t" /* .. | p3*v3 .. p0*v0 | */
" movq %%xmm2, 8(%0) \n\t" /* .. | p7*v7 .. p4*v4 | */
" add $16, %0 \n\t"
MOD_ADD ($8, %5)
" dec %2 \n\t"
" jne 7b \n\t"
"8: \n\t"
: "+r" (samples), "+r" (volumes), "+r" (length), "=D" (channel), "=&r" (temp)
: "r" ((pa_reg_x86)channels)
: "cc"
);
}
#undef RUN_TEST
#ifdef RUN_TEST
#define CHANNELS 2
#define SAMPLES 1021
#define TIMES 1000
#define PADDING 16
static void run_test (void) {
int16_t samples[SAMPLES];
int16_t samples_ref[SAMPLES];
int16_t samples_orig[SAMPLES];
int32_t volumes[CHANNELS + PADDING];
int i, j, padding;
pa_do_volume_func_t func;
pa_usec_t start, stop;
func = pa_get_volume_func (PA_SAMPLE_S16NE);
printf ("checking SSE %zd\n", sizeof (samples));
pa_random (samples, sizeof (samples));
memcpy (samples_ref, samples, sizeof (samples));
memcpy (samples_orig, samples, sizeof (samples));
for (i = 0; i < CHANNELS; i++)
volumes[i] = rand() >> 1;
for (padding = 0; padding < PADDING; padding++, i++)
volumes[i] = volumes[padding];
func (samples_ref, volumes, CHANNELS, sizeof (samples));
pa_volume_s16ne_sse (samples, volumes, CHANNELS, sizeof (samples));
for (i = 0; i < SAMPLES; i++) {
if (samples[i] != samples_ref[i]) {
printf ("%d: %04x != %04x (%04x * %04x)\n", i, samples[i], samples_ref[i],
samples_orig[i], volumes[i % CHANNELS]);
}
}
start = pa_rtclock_now();
for (j = 0; j < TIMES; j++) {
memcpy (samples, samples_orig, sizeof (samples));
pa_volume_s16ne_sse (samples, volumes, CHANNELS, sizeof (samples));
}
stop = pa_rtclock_now();
pa_log_info("SSE: %llu usec.", (long long unsigned int)(stop - start));
start = pa_rtclock_now();
for (j = 0; j < TIMES; j++) {
memcpy (samples_ref, samples_orig, sizeof (samples));
func (samples_ref, volumes, CHANNELS, sizeof (samples));
}
stop = pa_rtclock_now();
pa_log_info("ref: %llu usec.", (long long unsigned int)(stop - start));
}
#endif
#endif /* defined (__i386__) || defined (__amd64__) */
void pa_volume_func_init_sse (pa_cpu_x86_flag_t flags) {
#if defined (__i386__) || defined (__amd64__)
pa_log_info("Initialising SSE optimized functions.");
#ifdef RUN_TEST
run_test ();
#endif
pa_set_volume_func (PA_SAMPLE_S16NE, (pa_do_volume_func_t) pa_volume_s16ne_sse);
pa_set_volume_func (PA_SAMPLE_S16RE, (pa_do_volume_func_t) pa_volume_s16re_sse);
#endif /* defined (__i386__) || defined (__amd64__) */
}

372
src/pulsecore/usergroup.c Normal file
View file

@ -0,0 +1,372 @@
/***
This file is part of PulseAudio.
Copyright 2009 Ted Percival
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 <sys/types.h>
#include <errno.h>
#ifdef HAVE_PWD_H
#include <pwd.h>
#endif
#ifdef HAVE_GRP_H
#include <grp.h>
#endif
#include <pulse/xmalloc.h>
#include <pulsecore/macro.h>
#include "usergroup.h"
#ifdef HAVE_GRP_H
/* Returns a suitable starting size for a getgrnam_r() or getgrgid_r() buffer,
plus the size of a struct group.
*/
static size_t starting_getgr_buflen(void) {
size_t full_size;
long n;
#ifdef _SC_GETGR_R_SIZE_MAX
n = sysconf(_SC_GETGR_R_SIZE_MAX);
#else
n = -1;
#endif
if (n <= 0)
n = 512;
full_size = (size_t) n + sizeof(struct group);
if (full_size < (size_t) n) /* check for integer overflow */
return (size_t) n;
return full_size;
}
/* Returns a suitable starting size for a getpwnam_r() or getpwuid_r() buffer,
plus the size of a struct passwd.
*/
static size_t starting_getpw_buflen(void) {
long n;
size_t full_size;
#ifdef _SC_GETPW_R_SIZE_MAX
n = sysconf(_SC_GETPW_R_SIZE_MAX);
#else
n = -1;
#endif
if (n <= 0)
n = 512;
full_size = (size_t) n + sizeof(struct passwd);
if (full_size < (size_t) n) /* check for integer overflow */
return (size_t) n;
return full_size;
}
/* Given a memory allocation (*bufptr) and its length (*buflenptr),
double the size of the allocation, updating the given buffer and length
arguments. This function should be used in conjunction with the pa_*alloc
and pa_xfree functions.
Unlike realloc(), this function does *not* retain the original buffer's
contents.
Returns 0 on success, nonzero on error. The error cause is indicated by
errno.
*/
static int expand_buffer_trashcontents(void **bufptr, size_t *buflenptr) {
size_t newlen;
if (!bufptr || !*bufptr || !buflenptr) {
errno = EINVAL;
return -1;
}
newlen = *buflenptr * 2;
if (newlen < *buflenptr) {
errno = EOVERFLOW;
return -1;
}
/* Don't bother retaining memory contents; free & alloc anew */
pa_xfree(*bufptr);
*bufptr = pa_xmalloc(newlen);
*buflenptr = newlen;
return 0;
}
#ifdef HAVE_GETGRGID_R
/* Thread-safe getgrgid() replacement.
Returned value should be freed using pa_getgrgid_free() when the caller is
finished with the returned group data.
API is the same as getgrgid(), errors are indicated by a NULL return;
consult errno for the error cause (zero it before calling).
*/
struct group *pa_getgrgid_malloc(gid_t gid) {
size_t buflen, getgr_buflen;
int err;
void *buf;
void *getgr_buf;
struct group *result = NULL;
buflen = starting_getgr_buflen();
buf = pa_xmalloc(buflen);
getgr_buflen = buflen - sizeof(struct group);
getgr_buf = (char *)buf + sizeof(struct group);
while ((err = getgrgid_r(gid, (struct group *)buf, getgr_buf,
getgr_buflen, &result)) == ERANGE)
{
if (expand_buffer_trashcontents(&buf, &buflen))
break;
getgr_buflen = buflen - sizeof(struct group);
getgr_buf = (char *)buf + sizeof(struct group);
}
if (err || !result) {
result = NULL;
if (buf) {
pa_xfree(buf);
buf = NULL;
}
}
pa_assert(result == buf || result == NULL);
return result;
}
void pa_getgrgid_free(struct group *grp) {
pa_xfree(grp);
}
#else /* !HAVE_GETGRGID_R */
struct group *pa_getgrgid_malloc(gid_t gid) {
return getgrgid(gid);
}
void pa_getgrgid_free(struct group *grp) {
/* nothing */
return;
}
#endif /* !HAVE_GETGRGID_R */
#ifdef HAVE_GETGRNAM_R
/* Thread-safe getgrnam() function.
Returned value should be freed using pa_getgrnam_free() when the caller is
finished with the returned group data.
API is the same as getgrnam(), errors are indicated by a NULL return;
consult errno for the error cause (zero it before calling).
*/
struct group *pa_getgrnam_malloc(const char *name) {
size_t buflen, getgr_buflen;
int err;
void *buf;
void *getgr_buf;
struct group *result = NULL;
buflen = starting_getgr_buflen();
buf = pa_xmalloc(buflen);
getgr_buflen = buflen - sizeof(struct group);
getgr_buf = (char *)buf + sizeof(struct group);
while ((err = getgrnam_r(name, (struct group *)buf, getgr_buf,
getgr_buflen, &result)) == ERANGE)
{
if (expand_buffer_trashcontents(&buf, &buflen))
break;
getgr_buflen = buflen - sizeof(struct group);
getgr_buf = (char *)buf + sizeof(struct group);
}
if (err || !result) {
result = NULL;
if (buf) {
pa_xfree(buf);
buf = NULL;
}
}
pa_assert(result == buf || result == NULL);
return result;
}
void pa_getgrnam_free(struct group *group) {
pa_xfree(group);
}
#else /* !HAVE_GETGRNAM_R */
struct group *pa_getgrnam_malloc(const char *name) {
return getgrnam(name);
}
void pa_getgrnam_free(struct group *group) {
/* nothing */
return;
}
#endif /* HAVE_GETGRNAM_R */
#endif /* HAVE_GRP_H */
#ifdef HAVE_PWD_H
#ifdef HAVE_GETPWNAM_R
/* Thread-safe getpwnam() function.
Returned value should be freed using pa_getpwnam_free() when the caller is
finished with the returned passwd data.
API is the same as getpwnam(), errors are indicated by a NULL return;
consult errno for the error cause (zero it before calling).
*/
struct passwd *pa_getpwnam_malloc(const char *name) {
size_t buflen, getpw_buflen;
int err;
void *buf;
void *getpw_buf;
struct passwd *result = NULL;
buflen = starting_getpw_buflen();
buf = pa_xmalloc(buflen);
getpw_buflen = buflen - sizeof(struct passwd);
getpw_buf = (char *)buf + sizeof(struct passwd);
while ((err = getpwnam_r(name, (struct passwd *)buf, getpw_buf,
getpw_buflen, &result)) == ERANGE)
{
if (expand_buffer_trashcontents(&buf, &buflen))
break;
getpw_buflen = buflen - sizeof(struct passwd);
getpw_buf = (char *)buf + sizeof(struct passwd);
}
if (err || !result) {
result = NULL;
if (buf) {
pa_xfree(buf);
buf = NULL;
}
}
pa_assert(result == buf || result == NULL);
return result;
}
void pa_getpwnam_free(struct passwd *passwd) {
pa_xfree(passwd);
}
#else /* !HAVE_GETPWNAM_R */
struct passwd *pa_getpwnam_malloc(const char *name) {
return getpwnam(name);
}
void pa_getpwnam_free(struct passwd *passwd) {
/* nothing */
return;
}
#endif /* !HAVE_GETPWNAM_R */
#ifdef HAVE_GETPWUID_R
/* Thread-safe getpwuid() function.
Returned value should be freed using pa_getpwuid_free() when the caller is
finished with the returned group data.
API is the same as getpwuid(), errors are indicated by a NULL return;
consult errno for the error cause (zero it before calling).
*/
struct passwd *pa_getpwuid_malloc(uid_t uid) {
size_t buflen, getpw_buflen;
int err;
void *buf;
void *getpw_buf;
struct passwd *result = NULL;
buflen = starting_getpw_buflen();
buf = pa_xmalloc(buflen);
getpw_buflen = buflen - sizeof(struct passwd);
getpw_buf = (char *)buf + sizeof(struct passwd);
while ((err = getpwuid_r(uid, (struct passwd *)buf, getpw_buf,
getpw_buflen, &result)) == ERANGE)
{
if (expand_buffer_trashcontents(&buf, &buflen))
break;
getpw_buflen = buflen - sizeof(struct passwd);
getpw_buf = (char *)buf + sizeof(struct passwd);
}
if (err || !result) {
result = NULL;
if (buf) {
pa_xfree(buf);
buf = NULL;
}
}
pa_assert(result == buf || result == NULL);
return result;
}
void pa_getpwuid_free(struct passwd *passwd) {
pa_xfree(passwd);
}
#else /* !HAVE_GETPWUID_R */
struct passwd *pa_getpwuid_malloc(uid_t uid) {
return getpwuid(uid);
}
void pa_getpwuid_free(struct passwd *passwd) {
/* nothing */
return;
}
#endif /* !HAVE_GETPWUID_R */
#endif /* HAVE_PWD_H */

51
src/pulsecore/usergroup.h Normal file
View file

@ -0,0 +1,51 @@
#ifndef foousergrouphfoo
#define foousergrouphfoo
/***
This file is part of PulseAudio.
Copyright 2009 Ted Percival
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 <sys/types.h>
#ifndef PACKAGE
#error "Please include config.h before including this file!"
#endif
#ifdef HAVE_GRP_H
struct group *pa_getgrgid_malloc(gid_t gid);
void pa_getgrgid_free(struct group *grp);
struct group *pa_getgrnam_malloc(const char *name);
void pa_getgrnam_free(struct group *group);
#endif /* HAVE_GRP_H */
#ifdef HAVE_PWD_H
struct passwd *pa_getpwuid_malloc(uid_t uid);
void pa_getpwuid_free(struct passwd *passwd);
struct passwd *pa_getpwnam_malloc(const char *name);
void pa_getpwnam_free(struct passwd *passwd);
#endif /* HAVE_PWD_H */
#endif /* foousergrouphfoo */

View file

@ -34,8 +34,6 @@
#include <pulsecore/memblock.h>
#include <pulsecore/sample-util.h>
#include <liboil/liboil.h>
const pa_envelope_def ramp_down = {
.n_points = 2,
.points_x = { 100*PA_USEC_PER_MSEC, 300*PA_USEC_PER_MSEC },
@ -202,7 +200,6 @@ int main(int argc, char *argv[]) {
.values = { PA_VOLUME_NORM, PA_VOLUME_NORM/2 }
};
oil_init();
pa_log_set_level(PA_LOG_DEBUG);
pa_assert_se(pool = pa_mempool_new(FALSE, 0));

View file

@ -32,8 +32,6 @@
#include <pulsecore/memblock.h>
#include <pulsecore/sample-util.h>
#include <liboil/liboil.h>
static float swap_float(float a) {
uint32_t *b = (uint32_t*) &a;
*b = PA_UINT32_SWAP(*b);
@ -211,7 +209,6 @@ int main(int argc, char *argv[]) {
pa_sample_spec a;
pa_cvolume v;
oil_init();
pa_log_set_level(PA_LOG_DEBUG);
pa_assert_se(pool = pa_mempool_new(FALSE, 0));

View file

@ -32,8 +32,6 @@
#include <pulsecore/memblock.h>
#include <pulsecore/sample-util.h>
#include <liboil/liboil.h>
int main(int argc, char *argv[]) {
static const pa_channel_map maps[] = {
@ -55,7 +53,6 @@ int main(int argc, char *argv[]) {
unsigned i, j;
pa_mempool *pool;
oil_init();
pa_log_set_level(PA_LOG_DEBUG);
pa_assert_se(pool = pa_mempool_new(FALSE, 0));

View file

@ -32,8 +32,6 @@
#include <pulsecore/memblock.h>
#include <pulsecore/sample-util.h>
#include <liboil/liboil.h>
static void dump_block(const pa_sample_spec *ss, const pa_memchunk *chunk) {
void *d;
unsigned i;
@ -248,7 +246,6 @@ int main(int argc, char *argv[]) {
pa_sample_spec a, b;
pa_cvolume v;
oil_init();
pa_log_set_level(PA_LOG_DEBUG);
pa_assert_se(pool = pa_mempool_new(FALSE, 0));

161
src/tests/usergroup-test.c Normal file
View file

@ -0,0 +1,161 @@
/***
This file is part of PulseAudio.
Copyright 2009 Ted Percival
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 <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
#include <errno.h>
#include <pulsecore/usergroup.h>
static int load_reference_structs(struct group **gr, struct passwd **pw) {
setpwent();
*pw = getpwent();
endpwent();
setgrent();
*gr = getgrent();
endgrent();
return (*gr && *pw) ? 0 : 1;
}
static int compare_group(const struct group *a, const struct group *b) {
char **amem, **bmem;
if (strcmp(a->gr_name, b->gr_name)) {
fprintf(stderr, "Group name mismatch: [%s] [%s]\n",
a->gr_name, b->gr_name);
return 1;
}
if (strcmp(a->gr_passwd, b->gr_passwd)) {
fprintf(stderr, "Group password mismatch: [%s] [%s]\n",
a->gr_passwd, b->gr_passwd);
return 1;
}
if (a->gr_gid != b->gr_gid) {
fprintf(stderr, "Gid mismatch: [%lu] [%lu]\n",
(unsigned long) a->gr_gid, (unsigned long) b->gr_gid);
return 1;
}
/* XXX: Assuming the group ordering is identical. */
for (amem = a->gr_mem, bmem = b->gr_mem; *amem && *bmem; ++amem, ++bmem) {
if (strcmp(*amem, *bmem)) {
fprintf(stderr, "Group member mismatch: [%s] [%s]\n",
*amem, *bmem);
return 1;
}
}
if (*amem || *bmem) {
fprintf(stderr, "Mismatched group count\n");
return 1;
}
return 0;
}
static int compare_passwd(const struct passwd *a, const struct passwd *b) {
if (strcmp(a->pw_name, b->pw_name)) {
fprintf(stderr, "pw_name mismatch: [%s] [%s]\n", a->pw_name, b->pw_name);
return 1;
}
if (strcmp(a->pw_passwd, b->pw_passwd)) {
fprintf(stderr, "pw_passwd mismatch: [%s] [%s]\n", a->pw_passwd, b->pw_passwd);
return 1;
}
if (a->pw_uid != b->pw_uid) {
fprintf(stderr, "pw_uid mismatch: [%lu] [%lu]\n",
(unsigned long) a->pw_uid, (unsigned long) b->pw_uid);
return 1;
}
if (a->pw_gid != b->pw_gid) {
fprintf(stderr, "pw_gid mismatch: [%lu] [%lu]\n",
(unsigned long) a->pw_gid, (unsigned long) b->pw_gid);
return 1;
}
if (strcmp(a->pw_gecos, b->pw_gecos)) {
fprintf(stderr, "pw_gecos mismatch: [%s] [%s]\n", a->pw_gecos, b->pw_gecos);
return 1;
}
if (strcmp(a->pw_dir, b->pw_dir)) {
fprintf(stderr, "pw_dir mismatch: [%s] [%s]\n", a->pw_dir, b->pw_dir);
return 1;
}
if (strcmp(a->pw_shell, b->pw_shell)) {
fprintf(stderr, "pw_shell mismatch: [%s] [%s]\n", a->pw_shell, b->pw_shell);
return 1;
}
return 0;
}
int main(int argc, char *argv[]) {
struct group *gr;
struct passwd *pw;
int err;
struct group *reference_group = NULL;
struct passwd *reference_passwd = NULL;
err = load_reference_structs(&reference_group, &reference_passwd);
if (err)
return 77;
errno = 0;
gr = pa_getgrgid_malloc(reference_group->gr_gid);
if (compare_group(reference_group, gr))
return 1;
pa_getgrgid_free(gr);
errno = 0;
gr = pa_getgrnam_malloc(reference_group->gr_name);
if (compare_group(reference_group, gr))
return 1;
pa_getgrnam_free(gr);
errno = 0;
pw = pa_getpwuid_malloc(reference_passwd->pw_uid);
if (compare_passwd(reference_passwd, pw))
return 1;
pa_getpwuid_free(pw);
errno = 0;
pw = pa_getpwnam_malloc(reference_passwd->pw_name);
if (compare_passwd(reference_passwd, pw))
return 1;
pa_getpwnam_free(pw);
return 0;
}