diff options
Diffstat (limited to 'src/pulsecore')
61 files changed, 1336 insertions, 1915 deletions
diff --git a/src/pulsecore/asyncq.c b/src/pulsecore/asyncq.c index e62d0c16..8c2d58a0 100644 --- a/src/pulsecore/asyncq.c +++ b/src/pulsecore/asyncq.c @@ -26,6 +26,8 @@ #include <unistd.h> #include <errno.h> +#include <pulse/xmalloc.h> + #include <pulsecore/atomic.h> #include <pulsecore/log.h> #include <pulsecore/thread.h> @@ -33,10 +35,9 @@ #include <pulsecore/core-util.h> #include <pulsecore/llist.h> #include <pulsecore/flist.h> -#include <pulse/xmalloc.h> +#include <pulsecore/fdsem.h> #include "asyncq.h" -#include "fdsem.h" #define ASYNCQ_SIZE 256 diff --git a/src/pulsecore/card.c b/src/pulsecore/card.c index 2f0a3af0..feaa4440 100644 --- a/src/pulsecore/card.c +++ b/src/pulsecore/card.c @@ -241,6 +241,8 @@ int pa_card_set_profile(pa_card *c, const char *name, pa_bool_t save) { c->active_profile = profile; c->save_profile = save; + pa_hook_fire(&c->core->hooks[PA_CORE_HOOK_CARD_PROFILE_CHANGED], c); + return 0; } diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c index a18ebd33..de4995eb 100644 --- a/src/pulsecore/cli-command.c +++ b/src/pulsecore/cli-command.c @@ -32,6 +32,7 @@ #include <ltdl.h> #include <sys/stat.h> #include <dirent.h> +#include <time.h> #include <pulse/xmalloc.h> #include <pulse/error.h> @@ -579,11 +580,16 @@ static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strb return -1; } - if (!(si = pa_idxset_get_by_index(c->sink_inputs, (uint32_t) idx))) { + if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx))) { pa_strbuf_puts(buf, "No sink input found with this index.\n"); return -1; } + if (!pa_sink_input_is_volume_writable(si)) { + pa_strbuf_puts(buf, "This sink input's volume can't be changed.\n"); + return -1; + } + pa_cvolume_set(&cvolume, 1, volume); pa_sink_input_set_volume(si, &cvolume, TRUE, TRUE); return 0; diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c index 23a57d37..e6018da2 100644 --- a/src/pulsecore/cli-text.c +++ b/src/pulsecore/cli-text.c @@ -553,8 +553,7 @@ char *pa_sink_input_list_to_string(pa_core *c) { pa_usec_t cl; const char *cmn; pa_cvolume v; - - pa_sink_input_get_volume(i, &v, TRUE); + char *volume_str = NULL; cmn = pa_channel_map_to_pretty_name(&i->channel_map); @@ -565,6 +564,15 @@ char *pa_sink_input_list_to_string(pa_core *c) { pa_assert(i->sink); + if (pa_sink_input_is_volume_readable(i)) { + pa_sink_input_get_volume(i, &v, TRUE); + volume_str = pa_sprintf_malloc("%s\n\t %s\n\t balance %0.2f", + pa_cvolume_snprint(cv, sizeof(cv), &v), + pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), &v), + pa_cvolume_get_balance(&v, &i->channel_map)); + } else + volume_str = pa_xstrdup("n/a"); + pa_strbuf_printf( s, " index: %u\n" @@ -573,8 +581,6 @@ char *pa_sink_input_list_to_string(pa_core *c) { "\tstate: %s\n" "\tsink: %u <%s>\n" "\tvolume: %s\n" - "\t %s\n" - "\t balance %0.2f\n" "\tmuted: %s\n" "\tcurrent latency: %0.2f ms\n" "\trequested latency: %s\n" @@ -596,9 +602,7 @@ char *pa_sink_input_list_to_string(pa_core *c) { i->flags & PA_SINK_INPUT_KILL_ON_SUSPEND ? "KILL_ON_SUSPEND " : "", state_table[pa_sink_input_get_state(i)], i->sink->index, i->sink->name, - pa_cvolume_snprint(cv, sizeof(cv), &v), - pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), &v), - pa_cvolume_get_balance(&v, &i->channel_map), + volume_str, pa_yes_no(pa_sink_input_get_mute(i)), (double) pa_sink_input_get_latency(i, NULL) / PA_USEC_PER_MSEC, clt, @@ -608,6 +612,8 @@ char *pa_sink_input_list_to_string(pa_core *c) { cmn ? cmn : "", pa_resample_method_to_string(pa_sink_input_get_resample_method(i))); + pa_xfree(volume_str); + if (i->module) pa_strbuf_printf(s, "\tmodule: %u\n", i->module->index); if (i->client) diff --git a/src/pulsecore/core-scache.c b/src/pulsecore/core-scache.c index 5ec6159d..1aed9077 100644 --- a/src/pulsecore/core-scache.c +++ b/src/pulsecore/core-scache.c @@ -32,6 +32,7 @@ #include <sys/stat.h> #include <errno.h> #include <limits.h> +#include <time.h> #ifdef HAVE_GLOB_H #include <glob.h> diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c index fd3cc67a..b5043a38 100644 --- a/src/pulsecore/core-util.c +++ b/src/pulsecore/core-util.c @@ -222,12 +222,12 @@ int pa_make_secure_dir(const char* dir, mode_t m, uid_t uid, gid_t gid) { #ifdef OS_IS_WIN32 r = mkdir(dir); #else - { +{ mode_t u; u = umask((~m) & 0777); r = mkdir(dir, m); umask(u); - } +} #endif if (r < 0 && errno != EEXIST) @@ -549,7 +549,7 @@ void pa_check_signal_is_blocked(int sig) { /* The following function is based on an example from the GNU libc * documentation. This function is similar to GNU's asprintf(). */ char *pa_sprintf_malloc(const char *format, ...) { - size_t size = 100; + size_t size = 100; char *c = NULL; pa_assert(format); @@ -579,7 +579,7 @@ char *pa_sprintf_malloc(const char *format, ...) { /* Same as the previous function, but use a va_list instead of an * ellipsis */ char *pa_vsprintf_malloc(const char *format, va_list ap) { - size_t size = 100; + size_t size = 100; char *c = NULL; pa_assert(format); @@ -625,6 +625,7 @@ char *pa_strlcpy(char *b, const char *s, size_t l) { return b; } +#ifdef _POSIX_PRIORITY_SCHEDULING static int set_scheduler(int rtprio) { #ifdef HAVE_SCHED_H struct sched_param sp; @@ -682,6 +683,7 @@ static int set_scheduler(int rtprio) { return -1; } +#endif /* Make the current thread a realtime thread, and acquire the highest * rtprio we can get that is less or equal the specified parameter. If @@ -718,6 +720,7 @@ int pa_make_realtime(int rtprio) { return -1; } +#ifdef HAVE_SYS_RESOURCE_H static int set_nice(int nice_level) { #ifdef HAVE_DBUS DBusError error; @@ -762,6 +765,7 @@ static int set_nice(int nice_level) { return -1; } +#endif /* Raise the priority of the current process as much as possible that * is <= the specified nice level..*/ @@ -1044,8 +1048,7 @@ static int is_group(gid_t gid, const char *name) { int r = -1; errno = 0; - if (!(group = pa_getgrgid_malloc(gid))) - { + if (!(group = pa_getgrgid_malloc(gid))) { if (!errno) errno = ENOENT; @@ -1111,8 +1114,7 @@ int pa_uid_in_group(uid_t uid, const char *name) { int r = -1; errno = 0; - if (!(group = pa_getgrnam_malloc(name))) - { + if (!(group = pa_getgrnam_malloc(name))) { if (!errno) errno = ENOENT; goto finish; @@ -1147,8 +1149,7 @@ gid_t pa_get_gid_of_group(const char *name) { struct group *gr = NULL; errno = 0; - if (!(gr = pa_getgrnam_malloc(name))) - { + if (!(gr = pa_getgrnam_malloc(name))) { if (!errno) errno = ENOENT; goto finish; @@ -1396,7 +1397,7 @@ char *pa_get_state_dir(void) { /* If PULSE_STATE_PATH and PULSE_RUNTIME_PATH point to the same * dir then this will break. */ - if (pa_make_secure_dir(d, 0700U, (uid_t) -1, (gid_t) -1) < 0) { + if (pa_make_secure_dir(d, 0700U, (uid_t) -1, (gid_t) -1) < 0) { pa_log_error("Failed to create secure directory: %s", pa_cstrerror(errno)); pa_xfree(d); return NULL; @@ -1536,7 +1537,7 @@ char *pa_get_runtime_dir(void) { if ((d = getenv("PULSE_RUNTIME_PATH"))) { - if (pa_make_secure_dir(d, m, (uid_t) -1, (gid_t) -1) < 0) { + if (pa_make_secure_dir(d, m, (uid_t) -1, (gid_t) -1) < 0) { pa_log_error("Failed to create secure directory: %s", pa_cstrerror(errno)); goto fail; } @@ -1547,7 +1548,7 @@ char *pa_get_runtime_dir(void) { if (!(d = get_pulse_home())) goto fail; - if (pa_make_secure_dir(d, m, (uid_t) -1, (gid_t) -1) < 0) { + if (pa_make_secure_dir(d, m, (uid_t) -1, (gid_t) -1) < 0) { pa_log_error("Failed to create secure directory: %s", pa_cstrerror(errno)); pa_xfree(d); goto fail; diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h index a1215bb5..358b98d7 100644 --- a/src/pulsecore/core.h +++ b/src/pulsecore/core.h @@ -74,6 +74,7 @@ typedef enum pa_core_hook { PA_CORE_HOOK_SINK_UNLINK_POST, PA_CORE_HOOK_SINK_STATE_CHANGED, PA_CORE_HOOK_SINK_PROPLIST_CHANGED, + PA_CORE_HOOK_SINK_PORT_CHANGED, PA_CORE_HOOK_SOURCE_NEW, PA_CORE_HOOK_SOURCE_FIXATE, PA_CORE_HOOK_SOURCE_PUT, @@ -81,6 +82,7 @@ typedef enum pa_core_hook { PA_CORE_HOOK_SOURCE_UNLINK_POST, PA_CORE_HOOK_SOURCE_STATE_CHANGED, PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED, + PA_CORE_HOOK_SOURCE_PORT_CHANGED, PA_CORE_HOOK_SINK_INPUT_NEW, PA_CORE_HOOK_SINK_INPUT_FIXATE, PA_CORE_HOOK_SINK_INPUT_PUT, @@ -111,6 +113,7 @@ typedef enum pa_core_hook { PA_CORE_HOOK_CARD_NEW, PA_CORE_HOOK_CARD_PUT, PA_CORE_HOOK_CARD_UNLINK, + PA_CORE_HOOK_CARD_PROFILE_CHANGED, PA_CORE_HOOK_MAX } pa_core_hook_t; diff --git a/src/pulsecore/cpu-arm.c b/src/pulsecore/cpu-arm.c index 1d0d7651..0287043e 100644 --- a/src/pulsecore/cpu-arm.c +++ b/src/pulsecore/cpu-arm.c @@ -37,24 +37,24 @@ #if defined (__arm__) && defined (__linux__) -#define MAX_BUFFER 4096 +#define MAX_BUFFER 4096 static char * -get_cpuinfo_line (char *cpuinfo, const char *tag) { +get_cpuinfo_line(char *cpuinfo, const char *tag) { char *line, *end, *colon; - if (!(line = strstr (cpuinfo, tag))) + if (!(line = strstr(cpuinfo, tag))) return NULL; - if (!(end = strchr (line, '\n'))) + if (!(end = strchr(line, '\n'))) return NULL; - if (!(colon = strchr (line, ':'))) + if (!(colon = strchr(line, ':'))) return NULL; if (++colon >= end) return NULL; - return pa_xstrndup (colon, end - colon); + return pa_xstrndup(colon, end - colon); } static char *get_cpuinfo(void) { @@ -80,7 +80,7 @@ static char *get_cpuinfo(void) { } #endif /* defined (__arm__) && defined (__linux__) */ -pa_bool_t pa_cpu_init_arm (pa_cpu_arm_flag_t *flags) { +pa_bool_t pa_cpu_init_arm(pa_cpu_arm_flag_t *flags) { #if defined (__arm__) #if defined (__linux__) char *cpuinfo, *line; @@ -88,16 +88,16 @@ pa_bool_t pa_cpu_init_arm (pa_cpu_arm_flag_t *flags) { /* 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"); + if (!(cpuinfo = get_cpuinfo())) { + pa_log("Can't read cpuinfo"); return; } *flags = 0; /* get the CPU architecture */ - if ((line = get_cpuinfo_line (cpuinfo, "CPU architecture"))) { - arch = strtoul (line, NULL, 0); + if ((line = get_cpuinfo_line(cpuinfo, "CPU architecture"))) { + arch = strtoul(line, NULL, 0); if (arch >= 6) *flags |= PA_CPU_ARM_V6; if (arch >= 7) @@ -106,18 +106,18 @@ pa_bool_t pa_cpu_init_arm (pa_cpu_arm_flag_t *flags) { pa_xfree(line); } /* get the CPU features */ - if ((line = get_cpuinfo_line (cpuinfo, "Features"))) { + if ((line = get_cpuinfo_line(cpuinfo, "Features"))) { const char *state = NULL; char *current; - while ((current = pa_split_spaces (line, &state))) { - if (!strcmp (current, "vfp")) + while ((current = pa_split_spaces(line, &state))) { + if (!strcmp(current, "vfp")) *flags |= PA_CPU_ARM_VFP; - else if (!strcmp (current, "edsp")) + else if (!strcmp(current, "edsp")) *flags |= PA_CPU_ARM_EDSP; - else if (!strcmp (current, "neon")) + else if (!strcmp(current, "neon")) *flags |= PA_CPU_ARM_NEON; - else if (!strcmp (current, "vfpv3")) + else if (!strcmp(current, "vfpv3")) *flags |= PA_CPU_ARM_VFPV3; pa_xfree(current); @@ -125,7 +125,7 @@ pa_bool_t pa_cpu_init_arm (pa_cpu_arm_flag_t *flags) { } pa_xfree(cpuinfo); - pa_log_info ("CPU flags: %s%s%s%s%s%s", + 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 " : "", @@ -134,7 +134,7 @@ pa_bool_t pa_cpu_init_arm (pa_cpu_arm_flag_t *flags) { (*flags & PA_CPU_ARM_VFPV3) ? "VFPV3 " : ""); if (*flags & PA_CPU_ARM_V6) - pa_volume_func_init_arm (*flags); + pa_volume_func_init_arm(*flags); return TRUE; diff --git a/src/pulsecore/cpu-orc.c b/src/pulsecore/cpu-orc.c new file mode 100644 index 00000000..d4a15357 --- /dev/null +++ b/src/pulsecore/cpu-orc.c @@ -0,0 +1,34 @@ +/*** + This file is part of PulseAudio. + + Copyright 2010 Arun Raghavan <arun.raghavan@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 "cpu-orc.h" + +void pa_cpu_init_orc(pa_cpu_info cpu_info) +{ +#ifndef DISABLE_ORC + /* Update these as we test on more architectures */ + pa_cpu_x86_flag_t x86_want_flags = PA_CPU_X86_MMX | PA_CPU_X86_SSE | PA_CPU_X86_SSE2 | PA_CPU_X86_SSE3 | PA_CPU_X86_SSSE3 | PA_CPU_X86_SSE4_1 | PA_CPU_X86_SSE4_2; + + /* Enable Orc svolume optimizations */ + if ((cpu_info.cpu_type == PA_CPU_X86) && (cpu_info.flags.x86 & x86_want_flags)) + pa_volume_func_init_orc(); +#endif +} diff --git a/src/pulsecore/cpu-orc.h b/src/pulsecore/cpu-orc.h new file mode 100644 index 00000000..9924d27b --- /dev/null +++ b/src/pulsecore/cpu-orc.h @@ -0,0 +1,37 @@ +#ifndef foocpuorchfoo +#define foocpuorchfoo + +/*** + This file is part of PulseAudio. + + Copyright 2010 Arun Raghavan <arun.raghavan@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 <pulsecore/cpu.h> + +/* Orc-optimised bits */ + +void pa_cpu_init_orc(pa_cpu_info cpu_info); + +void pa_volume_func_init_orc(void); + +#endif /* foocpuorchfoo */ diff --git a/src/pulsecore/cpu-x86.c b/src/pulsecore/cpu-x86.c index 062a4c1b..05a4b2f0 100644 --- a/src/pulsecore/cpu-x86.c +++ b/src/pulsecore/cpu-x86.c @@ -31,9 +31,7 @@ #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) -{ +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" @@ -46,7 +44,7 @@ get_cpuid (uint32_t op, uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d) } #endif -pa_bool_t pa_cpu_init_x86 (pa_cpu_x86_flag_t *flags) { +pa_bool_t pa_cpu_init_x86(pa_cpu_x86_flag_t *flags) { #if defined (__i386__) || defined (__amd64__) uint32_t eax, ebx, ecx, edx; uint32_t level; @@ -54,9 +52,9 @@ pa_bool_t pa_cpu_init_x86 (pa_cpu_x86_flag_t *flags) { *flags = 0; /* get standard level */ - get_cpuid (0x00000000, &level, &ebx, &ecx, &edx); + get_cpuid(0x00000000, &level, &ebx, &ecx, &edx); if (level >= 1) { - get_cpuid (0x00000001, &eax, &ebx, &ecx, &edx); + get_cpuid(0x00000001, &eax, &ebx, &ecx, &edx); if (edx & (1<<15)) *flags |= PA_CPU_X86_CMOV; @@ -84,9 +82,9 @@ pa_bool_t pa_cpu_init_x86 (pa_cpu_x86_flag_t *flags) { } /* get extended level */ - get_cpuid (0x80000000, &level, &ebx, &ecx, &edx); + get_cpuid(0x80000000, &level, &ebx, &ecx, &edx); if (level >= 0x80000001) { - get_cpuid (0x80000001, &eax, &ebx, &ecx, &edx); + get_cpuid(0x80000001, &eax, &ebx, &ecx, &edx); if (edx & (1<<22)) *flags |= PA_CPU_X86_MMXEXT; @@ -101,7 +99,7 @@ pa_bool_t pa_cpu_init_x86 (pa_cpu_x86_flag_t *flags) { *flags |= PA_CPU_X86_3DNOW; } - pa_log_info ("CPU flags: %s%s%s%s%s%s%s%s%s%s%s", + pa_log_info("CPU flags: %s%s%s%s%s%s%s%s%s%s%s", (*flags & PA_CPU_X86_CMOV) ? "CMOV " : "", (*flags & PA_CPU_X86_MMX) ? "MMX " : "", (*flags & PA_CPU_X86_SSE) ? "SSE " : "", @@ -116,14 +114,14 @@ pa_bool_t pa_cpu_init_x86 (pa_cpu_x86_flag_t *flags) { /* activate various optimisations */ if (*flags & PA_CPU_X86_MMX) { - pa_volume_func_init_mmx (*flags); - pa_remap_func_init_mmx (*flags); + pa_volume_func_init_mmx(*flags); + pa_remap_func_init_mmx(*flags); } if (*flags & (PA_CPU_X86_SSE | PA_CPU_X86_SSE2)) { - pa_volume_func_init_sse (*flags); - pa_remap_func_init_sse (*flags); - pa_convert_func_init_sse (*flags); + pa_volume_func_init_sse(*flags); + pa_remap_func_init_sse(*flags); + pa_convert_func_init_sse(*flags); } return TRUE; diff --git a/src/pulsecore/database-simple.c b/src/pulsecore/database-simple.c index 754930db..237d0bdd 100644 --- a/src/pulsecore/database-simple.c +++ b/src/pulsecore/database-simple.c @@ -429,7 +429,7 @@ static int write_uint(FILE *f, const uint32_t num) { errno = 0; for (i = 0; i < 4; i++) - values[i] = (num >> (i*8)) & 0xFF; + values[i] = (num >> (i*8)) & 0xFF; items = fwrite(&values, sizeof(values), sizeof(uint8_t), f); diff --git a/src/pulsecore/envelope.c b/src/pulsecore/envelope.c deleted file mode 100644 index 75e189cb..00000000 --- a/src/pulsecore/envelope.c +++ /dev/null @@ -1,989 +0,0 @@ -/*** - This file is part of PulseAudio. - - Copyright 2007 Lennart Poettering - - PulseAudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - PulseAudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with PulseAudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <stdio.h> - -#include <pulse/sample.h> -#include <pulse/xmalloc.h> - -#include <pulsecore/endianmacros.h> -#include <pulsecore/memchunk.h> -#include <pulsecore/macro.h> -#include <pulsecore/flist.h> -#include <pulsecore/semaphore.h> -#include <pulsecore/g711.h> - -#include "envelope.h" - -/* - Envelope subsystem for applying linear interpolated volume - envelopes on audio data. If multiple enevelopes shall be applied - at the same time, the "minimum" envelope is determined and - applied. - - Envelopes are defined in a statically allocated constant structure - pa_envelope_def. It may be activated using pa_envelope_add(). And - already active envelope may be replaced with pa_envelope_replace() - and removed with pa_envelope_remove().The combined "minimum" - envelope can be applied to audio data with pa_envelope_apply(). - - _apply() on one hand and _add()/_replace()/_remove() on the other - can be executed in seperate threads, in which case no locking is - used. -*/ - -PA_STATIC_FLIST_DECLARE(items, 0, pa_xfree); - -struct pa_envelope_item { - PA_LLIST_FIELDS(pa_envelope_item); - const pa_envelope_def *def; - pa_usec_t start_x; - union { - int32_t i; - float f; - } start_y; - unsigned j; -}; - -enum envelope_state { - STATE_VALID0, - STATE_VALID1, - STATE_READ0, - STATE_READ1, - STATE_WAIT0, - STATE_WAIT1, - STATE_WRITE0, - STATE_WRITE1 -}; - -struct pa_envelope { - pa_sample_spec sample_spec; - - PA_LLIST_HEAD(pa_envelope_item, items); - - pa_atomic_t state; - - size_t x; - - struct { - unsigned n_points, n_allocated, n_current; - - size_t *x; - union { - int32_t *i; - float *f; - } y; - - size_t cached_dx; - int32_t cached_dy_i; - float cached_dy_dx; - pa_bool_t cached_valid; - } points[2]; - - pa_bool_t is_float; - - pa_semaphore *semaphore; -}; - -pa_envelope *pa_envelope_new(const pa_sample_spec *ss) { - pa_envelope *e; - pa_assert(ss); - - e = pa_xnew(pa_envelope, 1); - - e->sample_spec = *ss; - PA_LLIST_HEAD_INIT(pa_envelope_item, e->items); - - e->x = 0; - - e->points[0].n_points = e->points[1].n_points = 0; - e->points[0].n_allocated = e->points[1].n_allocated = 0; - e->points[0].n_current = e->points[1].n_current = 0; - e->points[0].x = e->points[1].x = NULL; - e->points[0].y.i = e->points[1].y.i = NULL; - e->points[0].cached_valid = e->points[1].cached_valid = FALSE; - - pa_atomic_store(&e->state, STATE_VALID0); - - e->is_float = - ss->format == PA_SAMPLE_FLOAT32LE || - ss->format == PA_SAMPLE_FLOAT32BE; - - e->semaphore = pa_semaphore_new(0); - - return e; -} - -void pa_envelope_free(pa_envelope *e) { - pa_assert(e); - - while (e->items) - pa_envelope_remove(e, e->items); - - pa_xfree(e->points[0].x); - pa_xfree(e->points[1].x); - pa_xfree(e->points[0].y.i); - pa_xfree(e->points[1].y.i); - - pa_semaphore_free(e->semaphore); - - pa_xfree(e); -} - -static int32_t linear_interpolate_int(pa_usec_t x1, int32_t _y1, pa_usec_t x2, int32_t y2, pa_usec_t x3) { - return (int32_t) ((double) _y1 + (double) (x3 - x1) * (double) (y2 - _y1) / (double) (x2 - x1)); -} - -static float linear_interpolate_float(pa_usec_t x1, float _y1, pa_usec_t x2, float y2, pa_usec_t x3) { - return _y1 + ((float) x3 - (float) x1) * (y2 - _y1) / ((float) x2 - (float) x1); -} - -static int32_t item_get_int(pa_envelope_item *i, pa_usec_t x) { - pa_assert(i); - - if (x <= i->start_x) - return i->start_y.i; - - x -= i->start_x; - - if (x <= i->def->points_x[0]) - return linear_interpolate_int(0, i->start_y.i, - i->def->points_x[0], i->def->points_y.i[0], x); - - if (x >= i->def->points_x[i->def->n_points-1]) - return i->def->points_y.i[i->def->n_points-1]; - - pa_assert(i->j > 0); - pa_assert(i->def->points_x[i->j-1] <= x); - pa_assert(x <= i->def->points_x[i->j]); - - return linear_interpolate_int(i->def->points_x[i->j-1], i->def->points_y.i[i->j-1], - i->def->points_x[i->j], i->def->points_y.i[i->j], x); -} - -static float item_get_float(pa_envelope_item *i, pa_usec_t x) { - pa_assert(i); - - if (x <= i->start_x) - return i->start_y.f; - - x -= i->start_x; - - if (x <= i->def->points_x[0]) - return linear_interpolate_float(0, i->start_y.f, - i->def->points_x[0], i->def->points_y.f[0], x); - - if (x >= i->def->points_x[i->def->n_points-1]) - return i->def->points_y.f[i->def->n_points-1]; - - pa_assert(i->j > 0); - pa_assert(i->def->points_x[i->j-1] <= x); - pa_assert(x <= i->def->points_x[i->j]); - - return linear_interpolate_float(i->def->points_x[i->j-1], i->def->points_y.f[i->j-1], - i->def->points_x[i->j], i->def->points_y.f[i->j], x); -} - -static void envelope_begin_write(pa_envelope *e, int *v) { - enum envelope_state new_state, old_state; - pa_bool_t wait_sem; - - pa_assert(e); - pa_assert(v); - - for (;;) { - do { - wait_sem = FALSE; - old_state = pa_atomic_load(&e->state); - - switch (old_state) { - case STATE_VALID0: - *v = 1; - new_state = STATE_WRITE0; - break; - case STATE_VALID1: - *v = 0; - new_state = STATE_WRITE1; - break; - case STATE_READ0: - new_state = STATE_WAIT0; - wait_sem = TRUE; - break; - case STATE_READ1: - new_state = STATE_WAIT1; - wait_sem = TRUE; - break; - default: - pa_assert_not_reached(); - } - } while (!pa_atomic_cmpxchg(&e->state, old_state, new_state)); - - if (!wait_sem) - break; - - pa_semaphore_wait(e->semaphore); - } -} - -static pa_bool_t envelope_commit_write(pa_envelope *e, int v) { - enum envelope_state new_state, old_state; - - pa_assert(e); - - do { - old_state = pa_atomic_load(&e->state); - - switch (old_state) { - case STATE_WRITE0: - pa_assert(v == 1); - new_state = STATE_VALID1; - break; - case STATE_WRITE1: - pa_assert(v == 0); - new_state = STATE_VALID0; - break; - case STATE_VALID0: - case STATE_VALID1: - case STATE_READ0: - case STATE_READ1: - return FALSE; - default: - pa_assert_not_reached(); - } - } while (!pa_atomic_cmpxchg(&e->state, old_state, new_state)); - - return TRUE; -} - -static void envelope_begin_read(pa_envelope *e, int *v) { - enum envelope_state new_state, old_state; - pa_assert(e); - pa_assert(v); - - do { - old_state = pa_atomic_load(&e->state); - - switch (old_state) { - case STATE_VALID0: - case STATE_WRITE0: - *v = 0; - new_state = STATE_READ0; - break; - case STATE_VALID1: - case STATE_WRITE1: - *v = 1; - new_state = STATE_READ1; - break; - default: - pa_assert_not_reached(); - } - } while (!pa_atomic_cmpxchg(&e->state, old_state, new_state)); -} - -static void envelope_commit_read(pa_envelope *e, int v) { - enum envelope_state new_state, old_state; - pa_bool_t post_sem; - - pa_assert(e); - - do { - post_sem = FALSE; - old_state = pa_atomic_load(&e->state); - - switch (old_state) { - case STATE_READ0: - pa_assert(v == 0); - new_state = STATE_VALID0; - break; - case STATE_READ1: - pa_assert(v == 1); - new_state = STATE_VALID1; - break; - case STATE_WAIT0: - pa_assert(v == 0); - new_state = STATE_VALID0; - post_sem = TRUE; - break; - case STATE_WAIT1: - pa_assert(v == 1); - new_state = STATE_VALID1; - post_sem = TRUE; - break; - default: - pa_assert_not_reached(); - } - } while (!pa_atomic_cmpxchg(&e->state, old_state, new_state)); - - if (post_sem) - pa_semaphore_post(e->semaphore); -} - -static void envelope_merge(pa_envelope *e, int v) { - - e->points[v].n_points = 0; - - if (e->items) { - pa_envelope_item *i; - pa_usec_t x = (pa_usec_t) -1; - - for (i = e->items; i; i = i->next) - i->j = 0; - - for (;;) { - pa_bool_t min_is_set; - pa_envelope_item *s = NULL; - - /* Let's find the next spot on the X axis to analyze */ - for (i = e->items; i; i = i->next) { - - for (;;) { - - if (i->j >= i->def->n_points) - break; - - if ((x != (pa_usec_t) -1) && i->start_x + i->def->points_x[i->j] <= x) { - i->j++; - continue; - } - - if (!s || (i->start_x + i->def->points_x[i->j] < s->start_x + s->def->points_x[s->j])) - s = i; - - break; - } - } - - if (!s) - break; - - if (e->points[v].n_points >= e->points[v].n_allocated) { - e->points[v].n_allocated = PA_MAX(e->points[v].n_points*2, PA_ENVELOPE_POINTS_MAX); - - e->points[v].x = pa_xrealloc(e->points[v].x, sizeof(size_t) * e->points[v].n_allocated); - e->points[v].y.i = pa_xrealloc(e->points[v].y.i, sizeof(int32_t) * e->points[v].n_allocated); - } - - x = s->start_x + s->def->points_x[s->j]; - e->points[v].x[e->points[v].n_points] = pa_usec_to_bytes(x, &e->sample_spec); - - min_is_set = FALSE; - - /* Now let's find the lowest value */ - if (e->is_float) { - float min_f; - - for (i = e->items; i; i = i->next) { - float f = item_get_float(i, x); - if (!min_is_set || f < min_f) { - min_f = f; - min_is_set = TRUE; - } - } - - e->points[v].y.f[e->points[v].n_points] = min_f; - } else { - int32_t min_k; - - for (i = e->items; i; i = i->next) { - int32_t k = item_get_int(i, x); - if (!min_is_set || k < min_k) { - min_k = k; - min_is_set = TRUE; - } - } - - e->points[v].y.i[e->points[v].n_points] = min_k; - } - - pa_assert_se(min_is_set); - e->points[v].n_points++; - } - } - - e->points[v].n_current = 0; - e->points[v].cached_valid = FALSE; -} - -pa_envelope_item *pa_envelope_add(pa_envelope *e, const pa_envelope_def *def) { - pa_envelope_item *i; - int v; - - pa_assert(e); - pa_assert(def); - pa_assert(def->n_points > 0); - - if (!(i = pa_flist_pop(PA_STATIC_FLIST_GET(items)))) - i = pa_xnew(pa_envelope_item, 1); - - i->def = def; - - if (e->is_float) - i->start_y.f = def->points_y.f[0]; - else - i->start_y.i = def->points_y.i[0]; - - PA_LLIST_PREPEND(pa_envelope_item, e->items, i); - - envelope_begin_write(e, &v); - - do { - - i->start_x = pa_bytes_to_usec(e->x, &e->sample_spec); - envelope_merge(e, v); - - } while (!envelope_commit_write(e, v)); - - return i; -} - -pa_envelope_item *pa_envelope_replace(pa_envelope *e, pa_envelope_item *i, const pa_envelope_def *def) { - pa_usec_t x; - int v; - - pa_assert(e); - pa_assert(i); - pa_assert(def->n_points > 0); - - envelope_begin_write(e, &v); - - for (;;) { - float saved_f; - int32_t saved_i; - uint64_t saved_start_x; - const pa_envelope_def *saved_def; - - x = pa_bytes_to_usec(e->x, &e->sample_spec); - - if (e->is_float) { - saved_f = i->start_y.f; - i->start_y.f = item_get_float(i, x); - } else { - saved_i = i->start_y.i; - i->start_y.i = item_get_int(i, x); - } - - saved_start_x = i->start_x; - saved_def = i->def; - - i->start_x = x; - i->def = def; - - envelope_merge(e, v); - - if (envelope_commit_write(e, v)) - break; - - i->start_x = saved_start_x; - i->def = saved_def; - - if (e->is_float) - i->start_y.f = saved_f; - else - i->start_y.i = saved_i; - } - - return i; -} - -void pa_envelope_remove(pa_envelope *e, pa_envelope_item *i) { - int v; - - pa_assert(e); - pa_assert(i); - - PA_LLIST_REMOVE(pa_envelope_item, e->items, i); - - if (pa_flist_push(PA_STATIC_FLIST_GET(items), i) < 0) - pa_xfree(i); - - envelope_begin_write(e, &v); - do { - envelope_merge(e, v); - } while (!envelope_commit_write(e, v)); -} - -static int32_t linear_get_int(pa_envelope *e, int v) { - pa_assert(e); - - /* The repeated division could be replaced by Bresenham, as an - * optimization */ - - if (e->x < e->points[v].x[0]) - return e->points[v].y.i[0]; - - for (;;) { - if (e->points[v].n_current+1 >= e->points[v].n_points) - return e->points[v].y.i[e->points[v].n_points-1]; - - if (e->x < e->points[v].x[e->points[v].n_current+1]) - break; - - e->points[v].n_current++; - e->points[v].cached_valid = FALSE; - } - - if (!e->points[v].cached_valid) { - e->points[v].cached_dx = e->points[v].x[e->points[v].n_current+1] - e->points[v].x[e->points[v].n_current]; - e->points[v].cached_dy_i = e->points[v].y.i[e->points[v].n_current+1] - e->points[v].y.i[e->points[v].n_current]; - e->points[v].cached_valid = TRUE; - } - - return e->points[v].y.i[e->points[v].n_current] + ((float)e->points[v].cached_dy_i * (int32_t) (e->x - e->points[v].x[e->points[v].n_current])) / (int32_t) e->points[v].cached_dx; -} - -static float linear_get_float(pa_envelope *e, int v) { - pa_assert(e); - - if (e->x < e->points[v].x[0]) - return e->points[v].y.f[0]; - - for (;;) { - if (e->points[v].n_current+1 >= e->points[v].n_points) - return e->points[v].y.f[e->points[v].n_points-1]; - - if (e->x < e->points[v].x[e->points[v].n_current+1]) - break; - - e->points[v].n_current++; - e->points[v].cached_valid = FALSE; - } - - if (!e->points[v].cached_valid) { - e->points[v].cached_dy_dx = - (e->points[v].y.f[e->points[v].n_current+1] - e->points[v].y.f[e->points[v].n_current]) / - ((float) e->points[v].x[e->points[v].n_current+1] - (float) e->points[v].x[e->points[v].n_current]); - e->points[v].cached_valid = TRUE; - } - - return e->points[v].y.f[e->points[v].n_current] + (float) (e->x - e->points[v].x[e->points[v].n_current]) * e->points[v].cached_dy_dx; -} - -void pa_envelope_apply(pa_envelope *e, pa_memchunk *chunk) { - int v; - - pa_assert(e); - pa_assert(chunk); - - envelope_begin_read(e, &v); - - if (e->points[v].n_points > 0) { - void *p; - size_t fs, n; - - pa_memchunk_make_writable(chunk, 0); - p = (uint8_t*) pa_memblock_acquire(chunk->memblock) + chunk->index; - fs = pa_frame_size(&e->sample_spec); - n = chunk->length; - - pa_log_debug("Envelop position %zu applying factor %d=%f, sample spec is %d, chunk's length is %zu, fs is %zu\n", e->x, linear_get_int(e, v), ((float) linear_get_int(e,v))/0x10000, e->sample_spec.format, n, fs); - - switch (e->sample_spec.format) { - - case PA_SAMPLE_U8: { - uint8_t *d, *s; - unsigned channel; - int32_t factor = linear_get_int(e, v); - - s = (uint8_t*) p + n; - - for (channel = 0, d = p; d < s; d++) { - int32_t t, hi, lo; - - hi = factor >> 16; - lo = factor & 0xFFFF; - - t = (int32_t) *d - 0x80; - t = ((t * lo) >> 16) + (t * hi); - t = PA_CLAMP_UNLIKELY(t, -0x80, 0x7F); - *d = (uint8_t) (t + 0x80); - - if (PA_UNLIKELY(++channel >= e->sample_spec.channels)) { - channel = 0; - e->x += fs; - factor = linear_get_int(e, v); - } - } - - break; - } - - case PA_SAMPLE_ULAW: { - uint8_t *d, *s; - unsigned channel; - int32_t factor = linear_get_int(e, v); - - s = (uint8_t*) p + n; - - for (channel = 0, d = p; d < s; d++) { - int32_t t, hi, lo; - - hi = factor >> 16; - lo = factor & 0xFFFF; - - t = (int32_t) st_ulaw2linear16(*d); - t = ((t * lo) >> 16) + (t * hi); - t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF); - *d = (uint8_t) st_14linear2ulaw((int16_t) t >> 2); - - if (PA_UNLIKELY(++channel >= e->sample_spec.channels)) { - channel = 0; - e->x += fs; - factor = linear_get_int(e, v); - } - } - - break; - } - - case PA_SAMPLE_ALAW: { - uint8_t *d, *s; - unsigned channel; - int32_t factor = linear_get_int(e, v); - - s = (uint8_t*) p + n; - - for (channel = 0, d = p; d < s; d++) { - int32_t t, hi, lo; - - hi = factor >> 16; - lo = factor & 0xFFFF; - - t = (int32_t) st_alaw2linear16(*d); - t = ((t * lo) >> 16) + (t * hi); - t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF); - *d = (uint8_t) st_13linear2alaw((int16_t) t >> 3); - - if (PA_UNLIKELY(++channel >= e->sample_spec.channels)) { - channel = 0; - e->x += fs; - factor = linear_get_int(e, v); - } - } - - break; - } - - case PA_SAMPLE_S16NE: { - int16_t *d, *s; - unsigned channel; - int32_t factor = linear_get_int(e, v); - - s = (int16_t*) p + n/sizeof(int16_t); - - for (channel = 0, d = p; d < s; d++) { - int32_t t, hi, lo; - - hi = factor >> 16; - lo = factor & 0xFFFF; - - t = (int32_t)(*d); - t = ((t * lo) >> 16) + (t * hi); - t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF); - *d = (int16_t) t; - - if (PA_UNLIKELY(++channel >= e->sample_spec.channels)) { - channel = 0; - e->x += fs; - factor = linear_get_int(e, v); - } - } - - break; - } - - case PA_SAMPLE_S16RE: { - int16_t *d, *s; - unsigned channel; - int32_t factor = linear_get_int(e, v); - - s = (int16_t*) p + n/sizeof(int16_t); - - for (channel = 0, d = p; d < s; d++) { - int32_t t, hi, lo; - - hi = factor >> 16; - lo = factor & 0xFFFF; - - t = (int32_t) PA_INT16_SWAP(*d); - t = ((t * lo) >> 16) + (t * hi); - t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF); - *d = PA_INT16_SWAP((int16_t) t); - - if (PA_UNLIKELY(++channel >= e->sample_spec.channels)) { - channel = 0; - e->x += fs; - factor = linear_get_int(e, v); - } - } - - break; - } - - case PA_SAMPLE_S32NE: { - int32_t *d, *s; - unsigned channel; - int32_t factor = linear_get_int(e, v); - - s = (int32_t*) p + n/sizeof(int32_t); - - for (channel = 0, d = p; d < s; d++) { - int64_t t; - - t = (int64_t)(*d); - t = (t * factor) >> 16; - t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL); - *d = (int32_t) t; - - if (PA_UNLIKELY(++channel >= e->sample_spec.channels)) { - channel = 0; - e->x += fs; - factor = linear_get_int(e, v); - } - } - - break; - } - - case PA_SAMPLE_S32RE: { - int32_t *d, *s; - unsigned channel; - int32_t factor = linear_get_int(e, v); - - s = (int32_t*) p + n/sizeof(int32_t); - - for (channel = 0, d = p; d < s; d++) { - int64_t t; - - t = (int64_t) PA_INT32_SWAP(*d); - t = (t * factor) >> 16; - t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL); - *d = PA_INT32_SWAP((int32_t) t); - - if (PA_UNLIKELY(++channel >= e->sample_spec.channels)) { - channel = 0; - e->x += fs; - factor = linear_get_int(e, v); - } - } - - break; - } - - case PA_SAMPLE_FLOAT32NE: { - /*Seems the FLOAT32NE part of pa_volume_memchunk not right, do not reuse here*/ - float *t; - - for (t = p; n > 0; n -= fs) { - float factor = linear_get_float(e, v); - unsigned c; - e->x += fs; - - for (c = 0; c < e->sample_spec.channels; c++, t++) - *t = *t * factor; - } - - break; - } - - case PA_SAMPLE_FLOAT32RE: { - /*Seems the FLOAT32RE part of pa_volume_memchunk not right, do not reuse here*/ - float *t; - - for (t = p; n > 0; n -= fs) { - float factor = linear_get_float(e, v); - unsigned c; - e->x += fs; - - for (c = 0; c < e->sample_spec.channels; c++, t++) { - float r = PA_FLOAT32_SWAP(*t) * factor; - *t = PA_FLOAT32_SWAP(r); - } - } - - break; - } - - case PA_SAMPLE_S24NE: { - uint8_t *d, *s; - unsigned channel; - int32_t factor = linear_get_int(e, v); - - s = (uint8_t*) p + n/3; - - for (channel = 0, d = p; d < s; d++) { - int64_t t; - - t = (int64_t)((int32_t) (PA_READ24NE(d) << 8)); - t = (t * factor) >> 16; - t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL); - PA_WRITE24NE(d, ((uint32_t) (int32_t) t) >> 8); - - if (PA_UNLIKELY(++channel >= e->sample_spec.channels)) { - channel = 0; - e->x += fs; - factor = linear_get_int(e, v); - } - } - - break; - } - case PA_SAMPLE_S24RE: { - uint8_t *d, *s; - unsigned channel; - int32_t factor = linear_get_int(e, v); - - s = (uint8_t*) p + n/3; - - for (channel = 0, d = p; d < s; d++) { - int64_t t; - - t = (int64_t)((int32_t) (PA_READ24RE(d) << 8)); - t = (t * factor) >> 16; - t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL); - PA_WRITE24RE(d, ((uint32_t) (int32_t) t) >> 8); - - if (PA_UNLIKELY(++channel >= e->sample_spec.channels)) { - channel = 0; - e->x += fs; - factor = linear_get_int(e, v); - } - } - - break; - } - case PA_SAMPLE_S24_32NE: { - uint32_t *d, *s; - unsigned channel; - int32_t factor = linear_get_int(e, v); - - s = (uint32_t*) p + n/sizeof(uint32_t); - - for (channel = 0, d = p; d < s; d++) { - int64_t t; - - t = (int64_t) ((int32_t) (*d << 8)); - t = (t * factor) >> 16; - t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL); - *d = ((uint32_t) ((int32_t) t)) >> 8; - - if (PA_UNLIKELY(++channel >= e->sample_spec.channels)) { - channel = 0; - e->x += fs; - factor = linear_get_int(e, v); - } - } - - break; - } - case PA_SAMPLE_S24_32RE: { - uint32_t *d, *s; - unsigned channel; - int32_t factor = linear_get_int(e, v); - - s = (uint32_t*) p + n/sizeof(uint32_t); - - for (channel = 0, d = p; d < s; d++) { - int64_t t; - - t = (int64_t) ((int32_t) (PA_UINT32_SWAP(*d) << 8)); - t = (t * factor) >> 16; - t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL); - *d = PA_UINT32_SWAP(((uint32_t) ((int32_t) t)) >> 8); - - if (PA_UNLIKELY(++channel >= e->sample_spec.channels)) { - channel = 0; - e->x += fs; - factor = linear_get_int(e, v); - } - } - break; - } - /* FIXME */ - pa_assert_not_reached(); - - case PA_SAMPLE_MAX: - case PA_SAMPLE_INVALID: - pa_assert_not_reached(); - } - - pa_memblock_release(chunk->memblock); - } else { - /* When we have no envelope to apply we reset our origin */ - e->x = 0; - } - - envelope_commit_read(e, v); -} - -void pa_envelope_rewind(pa_envelope *e, size_t n_bytes) { - int v; - - pa_assert(e); - - envelope_begin_read(e, &v); - - if (e->x - n_bytes <= e->points[v].x[0]) - e->x = e->points[v].x[0]; - else - e->x -= n_bytes; - - e->points[v].n_current = 0; - e->points[v].cached_valid = FALSE; - - envelope_commit_read(e, v); -} - -void pa_envelope_restart(pa_envelope* e) { - int v; - pa_assert(e); - - envelope_begin_read(e, &v); - e->x = e->points[v].x[0]; - envelope_commit_read(e, v); -} - -pa_bool_t pa_envelope_is_finished(pa_envelope* e) { - int v; - pa_bool_t finished; - - pa_assert(e); - envelope_begin_read(e, &v); - finished = (e->x >= e->points[v].x[e->points[v].n_points-1]); - envelope_commit_read(e, v); - - return finished; -} - -int32_t pa_envelope_length(pa_envelope *e) { - int v; - size_t size; - - pa_assert(e); - envelope_begin_read(e, &v); - size = e->points[v].x[e->points[v].n_points-1] - e->points[v].x[0]; - envelope_commit_read(e, v); - - return size; -} diff --git a/src/pulsecore/envelope.h b/src/pulsecore/envelope.h deleted file mode 100644 index 4fa36579..00000000 --- a/src/pulsecore/envelope.h +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef foopulseenvelopehfoo -#define foopulseenvelopehfoo - -/*** - This file is part of PulseAudio. - - Copyright 2007 Lennart Poettering - - PulseAudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - PulseAudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with PulseAudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include <pulsecore/macro.h> -#include <pulsecore/memchunk.h> - -#include <pulse/sample.h> - -#define PA_ENVELOPE_POINTS_MAX 4U - -typedef struct pa_envelope pa_envelope; -typedef struct pa_envelope_item pa_envelope_item; - -typedef struct pa_envelope_def { - unsigned n_points; - - pa_usec_t points_x[PA_ENVELOPE_POINTS_MAX]; - struct { - int32_t i[PA_ENVELOPE_POINTS_MAX]; - float f[PA_ENVELOPE_POINTS_MAX]; - } points_y; -} pa_envelope_def; - -pa_envelope *pa_envelope_new(const pa_sample_spec *ss); -void pa_envelope_free(pa_envelope *e); -pa_envelope_item *pa_envelope_add(pa_envelope *e, const pa_envelope_def *def); -pa_envelope_item *pa_envelope_replace(pa_envelope *e, pa_envelope_item *i, const pa_envelope_def *def); -void pa_envelope_remove(pa_envelope *e, pa_envelope_item *i); -void pa_envelope_apply(pa_envelope *e, pa_memchunk *chunk); -void pa_envelope_rewind(pa_envelope *e, size_t n_bytes); -void pa_envelope_restart(pa_envelope* e); -pa_bool_t pa_envelope_is_finished(pa_envelope* e); -int32_t pa_envelope_length(pa_envelope *e); - -#endif diff --git a/src/pulsecore/ipacl.c b/src/pulsecore/ipacl.c index 3930ba61..5455d0e8 100644 --- a/src/pulsecore/ipacl.c +++ b/src/pulsecore/ipacl.c @@ -169,7 +169,7 @@ void pa_ip_acl_free(pa_ip_acl *acl) { int pa_ip_acl_check(pa_ip_acl *acl, int fd) { struct sockaddr_storage sa; struct acl_entry *e; - socklen_t salen; + socklen_t salen; pa_assert(acl); pa_assert(fd >= 0); @@ -206,7 +206,7 @@ int pa_ip_acl_check(pa_ip_acl *acl, int fd) { return 1; #ifdef HAVE_IPV6 } else if (e->family == AF_INET6) { - int i, bits ; + int i, bits; struct sockaddr_in6 *sai = (struct sockaddr_in6*) &sa; if (e->bits == 128) diff --git a/src/pulsecore/log.c b/src/pulsecore/log.c index 2c0e267a..b12cbf0c 100644 --- a/src/pulsecore/log.c +++ b/src/pulsecore/log.c @@ -70,6 +70,7 @@ static pa_log_level_t maximum_level = PA_LOG_ERROR, maximum_level_override = PA_ static unsigned show_backtrace = 0, show_backtrace_override = 0, skip_backtrace = 0; static pa_log_flags_t flags = 0, flags_override = 0; static pa_bool_t no_rate_limit = FALSE; +static int log_fd = -1; #ifdef HAVE_SYSLOG_H static const int level_to_syslog[] = { @@ -128,6 +129,15 @@ void pa_log_set_flags(pa_log_flags_t _flags, pa_log_merge_t merge) { flags = _flags; } +void pa_log_set_fd(int fd) { + if (fd >= 0) + log_fd = fd; + else if (log_fd >= 0) { + pa_close(log_fd); + log_fd = -1; + } +} + void pa_log_set_show_backtrace(unsigned nlevels) { show_backtrace = nlevels; } @@ -399,6 +409,23 @@ void pa_log_levelv_meta( } #endif + case PA_LOG_FD: { + if (log_fd >= 0) { + char metadata[256]; + + pa_snprintf(metadata, sizeof(metadata), "\n%c %s %s", level_to_char[level], timestamp, location); + + if ((write(log_fd, metadata, strlen(metadata)) < 0) || (write(log_fd, t, strlen(t)) < 0)) { + saved_errno = errno; + pa_log_set_fd(-1); + fprintf(stderr, "%s\n", "Error writing logs to a file descriptor. Redirect log messages to console."); + fprintf(stderr, "%s %s\n", metadata, t); + pa_log_set_target(PA_LOG_STDERR); + } + } + + break; + } case PA_LOG_NULL: default: break; diff --git a/src/pulsecore/log.h b/src/pulsecore/log.h index 1fd38d44..ad04e7bd 100644 --- a/src/pulsecore/log.h +++ b/src/pulsecore/log.h @@ -36,6 +36,7 @@ typedef enum pa_log_target { PA_LOG_STDERR, /* default */ PA_LOG_SYSLOG, PA_LOG_NULL, /* to /dev/null */ + PA_LOG_FD, /* to a file descriptor, e.g. a char device */ PA_LOG_TARGET_MAX } pa_log_target_t; @@ -74,6 +75,10 @@ void pa_log_set_level(pa_log_level_t l); /* Set flags */ void pa_log_set_flags(pa_log_flags_t flags, pa_log_merge_t merge); +/* Set the file descriptor of the logging device. + Daemon conf is in charge of opening this device */ +void pa_log_set_fd(int fd); + /* Enable backtrace */ void pa_log_set_show_backtrace(unsigned nlevels); diff --git a/src/pulsecore/ltdl-helper.c b/src/pulsecore/ltdl-helper.c index be200ca2..1a0e5558 100644 --- a/src/pulsecore/ltdl-helper.c +++ b/src/pulsecore/ltdl-helper.c @@ -42,7 +42,7 @@ pa_void_func_t pa_load_sym(lt_dlhandle handle, const char *module, const char *s pa_assert(handle); pa_assert(symbol); - *(void**) &f = lt_dlsym(handle, symbol); + f = (pa_void_func_t) lt_dlsym(handle, symbol); if (f) return f; @@ -59,7 +59,7 @@ pa_void_func_t pa_load_sym(lt_dlhandle handle, const char *module, const char *s if (!isalnum(*c)) *c = '_'; - *(void**) &f = lt_dlsym(handle, sn); + f = (pa_void_func_t) lt_dlsym(handle, sn); pa_xfree(sn); return f; diff --git a/src/pulsecore/memblock.c b/src/pulsecore/memblock.c index 454900d1..bc804577 100644 --- a/src/pulsecore/memblock.c +++ b/src/pulsecore/memblock.c @@ -82,7 +82,7 @@ struct pa_memblock { pa_free_cb_t free_cb; } user; - struct { + struct { uint32_t id; pa_memimport_segment *segment; } imported; @@ -531,9 +531,7 @@ static void memblock_free(pa_memblock *b) { pa_mutex_lock(import->mutex); - pa_assert_se(pa_hashmap_remove( - import->blocks, - PA_UINT32_TO_PTR(b->per_type.imported.id))); + pa_assert_se(pa_hashmap_remove(import->blocks, PA_UINT32_TO_PTR(b->per_type.imported.id))); pa_assert(segment->n_blocks >= 1); if (-- segment->n_blocks <= 0) @@ -693,9 +691,7 @@ static void memblock_replace_import(pa_memblock *b) { pa_mutex_lock(import->mutex); - pa_assert_se(pa_hashmap_remove( - import->blocks, - PA_UINT32_TO_PTR(b->per_type.imported.id))); + pa_assert_se(pa_hashmap_remove(import->blocks, PA_UINT32_TO_PTR(b->per_type.imported.id))); memblock_make_local(b); diff --git a/src/pulsecore/memblockq.c b/src/pulsecore/memblockq.c index 11faedac..c76ca841 100644 --- a/src/pulsecore/memblockq.c +++ b/src/pulsecore/memblockq.c @@ -376,8 +376,8 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) { size_t d; pa_assert(bq->write_index + (int64_t)chunk.length > q->index && - bq->write_index + (int64_t)chunk.length < q->index + (int64_t)q->chunk.length && - bq->write_index < q->index); + bq->write_index + (int64_t)chunk.length < q->index + (int64_t)q->chunk.length && + bq->write_index < q->index); /* The job overwrites the current entry at the end, so let's drop the beginning of this entry */ diff --git a/src/pulsecore/memtrap.c b/src/pulsecore/memtrap.c index b56d806a..4df1fb71 100644 --- a/src/pulsecore/memtrap.c +++ b/src/pulsecore/memtrap.c @@ -67,11 +67,11 @@ pa_bool_t pa_memtrap_is_good(pa_memtrap *m) { return !pa_atomic_load(&m->bad); } +#ifdef HAVE_SIGACTION static void sigsafe_error(const char *s) { (void) write(STDERR_FILENO, s, strlen(s)); } -#ifdef HAVE_SIGACTION static void signal_handler(int sig, siginfo_t* si, void *data) { unsigned j; pa_memtrap *m; diff --git a/src/pulsecore/modargs.c b/src/pulsecore/modargs.c index e78cdb9a..3106775f 100644 --- a/src/pulsecore/modargs.c +++ b/src/pulsecore/modargs.c @@ -124,7 +124,7 @@ pa_modargs *pa_modargs_new(const char *args, const char* const* valid_keys) { key_len++; break; - case VALUE_START: + case VALUE_START: if (*p == '\'') { state = VALUE_TICKS; value = p+1; diff --git a/src/pulsecore/module.c b/src/pulsecore/module.c index 74e94da4..1b1e1126 100644 --- a/src/pulsecore/module.c +++ b/src/pulsecore/module.c @@ -110,7 +110,7 @@ pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) { m->unload_requested = FALSE; if (m->init(m) < 0) { - pa_log_error("Failed to load module \"%s\" (argument: \"%s\"): initialization failed.", name, argument ? argument : ""); + pa_log_error("Failed to load module \"%s\" (argument: \"%s\"): initialization failed.", name, argument ? argument : ""); goto fail; } diff --git a/src/pulsecore/play-memblockq.c b/src/pulsecore/play-memblockq.c index 0d6da3ee..f075a5bf 100644 --- a/src/pulsecore/play-memblockq.c +++ b/src/pulsecore/play-memblockq.c @@ -135,6 +135,12 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk return -1; } + /* FIXME: u->memblockq doesn't have a silence memchunk set, so + * pa_memblockq_peek() will return 0 without returning any memblock if the + * read index points to a hole. If the memblockq is rewound beyond index 0, + * then there will be a hole. */ + pa_assert(chunk->memblock); + chunk->length = PA_MIN(chunk->length, nbytes); pa_memblockq_drop(u->memblockq, chunk->length); diff --git a/src/pulsecore/poll.c b/src/pulsecore/poll.c index b993c478..df4feb01 100644 --- a/src/pulsecore/poll.c +++ b/src/pulsecore/poll.c @@ -105,7 +105,7 @@ int pa_poll (struct pollfd *fds, unsigned long int nfds, int timeout) { tv.tv_sec = timeout / 1000; tv.tv_usec = (timeout % 1000) * 1000; - ready = select ((SELECT_TYPE_ARG1) maxfd + 1, SELECT_TYPE_ARG234 &rset, + ready = select((SELECT_TYPE_ARG1) maxfd + 1, SELECT_TYPE_ARG234 &rset, SELECT_TYPE_ARG234 &wset, SELECT_TYPE_ARG234 &xset, SELECT_TYPE_ARG5 (timeout == -1 ? NULL : &tv)); @@ -160,7 +160,7 @@ int pa_poll (struct pollfd *fds, unsigned long int nfds, int timeout) { /* Linux alters the tv struct... but it shouldn't matter here ... * as we're going to be a little bit out anyway as we've just eaten * more than a couple of cpu cycles above */ - ready = select ((SELECT_TYPE_ARG1) maxfd + 1, SELECT_TYPE_ARG234 &rset, + ready = select((SELECT_TYPE_ARG1) maxfd + 1, SELECT_TYPE_ARG234 &rset, SELECT_TYPE_ARG234 &wset, SELECT_TYPE_ARG234 &xset, SELECT_TYPE_ARG5 (timeout == -1 ? NULL : &tv)); } diff --git a/src/pulsecore/poll.h b/src/pulsecore/poll.h index a137d974..dc741e5e 100644 --- a/src/pulsecore/poll.h +++ b/src/pulsecore/poll.h @@ -43,12 +43,11 @@ #define POLLNVAL 0x020 /* Invalid polling request. */ /* Data structure describing a polling request. */ -struct pollfd - { +struct pollfd { int fd; /* File descriptor to poll. */ short int events; /* Types of events poller cares about. */ short int revents; /* Types of events that actually occurred. */ - }; +}; /* Poll the file descriptors described by the NFDS structures starting at @@ -62,5 +61,5 @@ struct pollfd #if defined(HAVE_POLL_H) && !defined(OS_IS_DARWIN) #define pa_poll(fds,nfds,timeout) poll((fds),(nfds),(timeout)) #else -int pa_poll (struct pollfd *fds, unsigned long nfds, int timeout); +int pa_poll(struct pollfd *fds, unsigned long nfds, int timeout); #endif diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c index 045c5c95..66fd73c8 100644 --- a/src/pulsecore/protocol-esound.c +++ b/src/pulsecore/protocol-esound.c @@ -55,8 +55,7 @@ #include <pulsecore/macro.h> #include <pulsecore/thread-mq.h> #include <pulsecore/shared.h> - -#include "endianmacros.h" +#include <pulsecore/endianmacros.h> #include "protocol-esound.h" diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index cc6a6b1d..bb4be726 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -1293,7 +1293,7 @@ static void native_connection_send_memblock(pa_native_connection *c) { else if (start == c->rrobin_index) return; - if (pa_memblockq_peek(r->memblockq, &chunk) >= 0) { + if (pa_memblockq_peek(r->memblockq, &chunk) >= 0) { pa_memchunk schunk = chunk; if (schunk.length > r->buffer_attr.fragsize) @@ -1387,7 +1387,7 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int /* If more data is in queue, we rewind later instead. */ if (s->seek_windex != -1) - windex = PA_MIN(windex, s->seek_windex); + windex = PA_MIN(windex, s->seek_windex); if (pa_atomic_dec(&s->seek_or_post_in_queue) > 1) s->seek_windex = windex; else { @@ -1406,7 +1406,7 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int pa_sink_input *isync; void (*func)(pa_memblockq *bq); - switch (code) { + switch (code) { case SINK_INPUT_MESSAGE_FLUSH: func = flush_write_no_account; break; @@ -1918,7 +1918,7 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u if (name) pa_proplist_sets(p, PA_PROP_MEDIA_NAME, name); - if (c->version >= 12) { + if (c->version >= 12) { /* Since 0.9.8 the user can ask for a couple of additional flags */ if (pa_tagstruct_get_boolean(t, &no_remap) < 0 || @@ -2009,14 +2009,14 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u } flags = - (corked ? PA_SINK_INPUT_START_CORKED : 0) | - (no_remap ? PA_SINK_INPUT_NO_REMAP : 0) | - (no_remix ? PA_SINK_INPUT_NO_REMIX : 0) | - (fix_format ? PA_SINK_INPUT_FIX_FORMAT : 0) | - (fix_rate ? PA_SINK_INPUT_FIX_RATE : 0) | - (fix_channels ? PA_SINK_INPUT_FIX_CHANNELS : 0) | - (no_move ? PA_SINK_INPUT_DONT_MOVE : 0) | - (variable_rate ? PA_SINK_INPUT_VARIABLE_RATE : 0) | + (corked ? PA_SINK_INPUT_START_CORKED : 0) | + (no_remap ? PA_SINK_INPUT_NO_REMAP : 0) | + (no_remix ? PA_SINK_INPUT_NO_REMIX : 0) | + (fix_format ? PA_SINK_INPUT_FIX_FORMAT : 0) | + (fix_rate ? PA_SINK_INPUT_FIX_RATE : 0) | + (fix_channels ? PA_SINK_INPUT_FIX_CHANNELS : 0) | + (no_move ? PA_SINK_INPUT_DONT_MOVE : 0) | + (variable_rate ? PA_SINK_INPUT_VARIABLE_RATE : 0) | (dont_inhibit_auto_suspend ? PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND : 0) | (fail_on_suspend ? PA_SINK_INPUT_NO_CREATE_ON_SUSPEND|PA_SINK_INPUT_KILL_ON_SUSPEND : 0) | (passthrough ? PA_SINK_INPUT_PASSTHROUGH : 0); @@ -2185,7 +2185,7 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin if (name) pa_proplist_sets(p, PA_PROP_MEDIA_NAME, name); - if (c->version >= 12) { + if (c->version >= 12) { /* Since 0.9.8 the user can ask for a couple of additional flags */ if (pa_tagstruct_get_boolean(t, &no_remap) < 0 || @@ -2266,14 +2266,14 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin } flags = - (corked ? PA_SOURCE_OUTPUT_START_CORKED : 0) | - (no_remap ? PA_SOURCE_OUTPUT_NO_REMAP : 0) | - (no_remix ? PA_SOURCE_OUTPUT_NO_REMIX : 0) | - (fix_format ? PA_SOURCE_OUTPUT_FIX_FORMAT : 0) | - (fix_rate ? PA_SOURCE_OUTPUT_FIX_RATE : 0) | - (fix_channels ? PA_SOURCE_OUTPUT_FIX_CHANNELS : 0) | - (no_move ? PA_SOURCE_OUTPUT_DONT_MOVE : 0) | - (variable_rate ? PA_SOURCE_OUTPUT_VARIABLE_RATE : 0) | + (corked ? PA_SOURCE_OUTPUT_START_CORKED : 0) | + (no_remap ? PA_SOURCE_OUTPUT_NO_REMAP : 0) | + (no_remix ? PA_SOURCE_OUTPUT_NO_REMIX : 0) | + (fix_format ? PA_SOURCE_OUTPUT_FIX_FORMAT : 0) | + (fix_rate ? PA_SOURCE_OUTPUT_FIX_RATE : 0) | + (fix_channels ? PA_SOURCE_OUTPUT_FIX_CHANNELS : 0) | + (no_move ? PA_SOURCE_OUTPUT_DONT_MOVE : 0) | + (variable_rate ? PA_SOURCE_OUTPUT_VARIABLE_RATE : 0) | (dont_inhibit_auto_suspend ? PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND : 0) | (fail_on_suspend ? PA_SOURCE_OUTPUT_NO_CREATE_ON_SUSPEND|PA_SOURCE_OUTPUT_KILL_ON_SUSPEND : 0); @@ -2902,7 +2902,7 @@ static void sink_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sin PA_TAG_STRING, sink->monitor_source ? sink->monitor_source->name : NULL, PA_TAG_USEC, pa_sink_get_latency(sink), PA_TAG_STRING, sink->driver, - PA_TAG_U32, sink->flags, + PA_TAG_U32, sink->flags & ~PA_SINK_SHARE_VOLUME_WITH_MASTER, PA_TAG_INVALID); if (c->version >= 13) { @@ -3056,12 +3056,19 @@ static void sink_input_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sample_spec fixed_ss; pa_usec_t sink_latency; pa_cvolume v; + pa_bool_t has_volume = FALSE; pa_assert(t); pa_sink_input_assert_ref(s); fixup_sample_spec(c, &fixed_ss, &s->sample_spec); + has_volume = pa_sink_input_is_volume_readable(s); + if (has_volume) + pa_sink_input_get_volume(s, &v, TRUE); + else + pa_cvolume_reset(&v, fixed_ss.channels); + pa_tagstruct_putu32(t, s->index); pa_tagstruct_puts(t, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_MEDIA_NAME))); pa_tagstruct_putu32(t, s->module ? s->module->index : PA_INVALID_INDEX); @@ -3069,7 +3076,7 @@ static void sink_input_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_tagstruct_putu32(t, s->sink->index); pa_tagstruct_put_sample_spec(t, &fixed_ss); pa_tagstruct_put_channel_map(t, &s->channel_map); - pa_tagstruct_put_cvolume(t, pa_sink_input_get_volume(s, &v, TRUE)); + pa_tagstruct_put_cvolume(t, &v); pa_tagstruct_put_usec(t, pa_sink_input_get_latency(s, &sink_latency)); pa_tagstruct_put_usec(t, sink_latency); pa_tagstruct_puts(t, pa_resample_method_to_string(pa_sink_input_get_resample_method(s))); @@ -3080,6 +3087,10 @@ static void sink_input_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_tagstruct_put_proplist(t, s->proplist); if (c->version >= 19) pa_tagstruct_put_boolean(t, (pa_sink_input_get_state(s) == PA_SINK_INPUT_CORKED)); + if (c->version >= 20) { + pa_tagstruct_put_boolean(t, has_volume); + pa_tagstruct_put_boolean(t, has_volume ? !pa_sink_input_is_volume_writable(s) : FALSE); + } } static void source_output_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_source_output *s) { @@ -3461,6 +3472,7 @@ static void command_set_volume( pa_log_debug("Client %s changes volume of source %s.", client_name, source->name); pa_source_set_volume(source, &volume, TRUE); } else if (si) { + CHECK_VALIDITY(c->pstream, pa_sink_input_is_volume_writable(si), tag, PA_ERR_INVALID); CHECK_VALIDITY(c->pstream, volume.channels == 1 || pa_cvolume_compatible(&volume, &si->sample_spec), tag, PA_ERR_INVALID); pa_log_debug("Client %s changes volume of sink input %s.", @@ -4927,7 +4939,7 @@ pa_pstream* pa_native_connection_get_pstream(pa_native_connection *c) { } pa_client* pa_native_connection_get_client(pa_native_connection *c) { - pa_native_connection_assert_ref(c); + pa_native_connection_assert_ref(c); - return c->client; + return c->client; } diff --git a/src/pulsecore/remap.c b/src/pulsecore/remap.c index a0fc85b9..b831f78c 100644 --- a/src/pulsecore/remap.c +++ b/src/pulsecore/remap.c @@ -32,7 +32,7 @@ #include "remap.h" -static void remap_mono_to_stereo_c (pa_remap_t *m, void *dst, const void *src, unsigned n) { +static void remap_mono_to_stereo_c(pa_remap_t *m, void *dst, const void *src, unsigned n) { unsigned i; switch (*m->format) { @@ -85,7 +85,7 @@ static void remap_mono_to_stereo_c (pa_remap_t *m, void *dst, const void *src, u } } -static void remap_channels_matrix_c (pa_remap_t *m, void *dst, const void *src, unsigned n) { +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; @@ -97,7 +97,7 @@ static void remap_channels_matrix_c (pa_remap_t *m, void *dst, const void *src, { float *d, *s; - memset(dst, 0, n * sizeof (float) * n_oc); + memset(dst, 0, n * sizeof(float) * n_oc); for (oc = 0; oc < n_oc; oc++) { @@ -128,7 +128,7 @@ static void remap_channels_matrix_c (pa_remap_t *m, void *dst, const void *src, { int16_t *d, *s; - memset(dst, 0, n * sizeof (int16_t) * n_oc); + memset(dst, 0, n * sizeof(int16_t) * n_oc); for (oc = 0; oc < n_oc; oc++) { @@ -160,7 +160,7 @@ static void remap_channels_matrix_c (pa_remap_t *m, void *dst, const void *src, } /* set the function that will execute the remapping based on the matrices */ -static void init_remap_c (pa_remap_t *m) { +static void init_remap_c(pa_remap_t *m) { unsigned n_oc, n_ic; n_oc = m->o_ss->channels; @@ -181,17 +181,17 @@ static void init_remap_c (pa_remap_t *m) { /* 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); +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); + remap_func(m); if (m->do_remap == NULL) { /* nothing was installed, fallback to C version */ - init_remap_c (m); + init_remap_c(m); } } diff --git a/src/pulsecore/remap_mmx.c b/src/pulsecore/remap_mmx.c index d358a58b..37d72da7 100644 --- a/src/pulsecore/remap_mmx.c +++ b/src/pulsecore/remap_mmx.c @@ -103,7 +103,7 @@ " emms \n\t" #if defined (__i386__) || defined (__amd64__) -static void remap_mono_to_stereo_mmx (pa_remap_t *m, void *dst, const void *src, unsigned n) { +static void remap_mono_to_stereo_mmx(pa_remap_t *m, void *dst, const void *src, unsigned n) { pa_reg_x86 temp, temp2; switch (*m->format) { @@ -133,7 +133,7 @@ static void remap_mono_to_stereo_mmx (pa_remap_t *m, void *dst, const void *src, } /* set the function that will execute the remapping based on the matrices */ -static void init_remap_mmx (pa_remap_t *m) { +static void init_remap_mmx(pa_remap_t *m) { unsigned n_oc, n_ic; n_oc = m->o_ss->channels; @@ -148,13 +148,13 @@ static void init_remap_mmx (pa_remap_t *m) { } #endif /* defined (__i386__) || defined (__amd64__) */ -void pa_remap_func_init_mmx (pa_cpu_x86_flag_t flags) { +void pa_remap_func_init_mmx(pa_cpu_x86_flag_t flags) { #if defined (__i386__) || defined (__amd64__) if (flags & PA_CPU_X86_MMX) { pa_log_info("Initialising MMX optimized remappers."); - pa_set_init_remap_func ((pa_init_remap_func_t) init_remap_mmx); + pa_set_init_remap_func((pa_init_remap_func_t) init_remap_mmx); } #endif /* defined (__i386__) || defined (__amd64__) */ diff --git a/src/pulsecore/remap_sse.c b/src/pulsecore/remap_sse.c index 0ccf3161..e1cb161d 100644 --- a/src/pulsecore/remap_sse.c +++ b/src/pulsecore/remap_sse.c @@ -102,7 +102,7 @@ "4: \n\t" #if defined (__i386__) || defined (__amd64__) -static void remap_mono_to_stereo_sse2 (pa_remap_t *m, void *dst, const void *src, unsigned n) { +static void remap_mono_to_stereo_sse2(pa_remap_t *m, void *dst, const void *src, unsigned n) { pa_reg_x86 temp, temp2; switch (*m->format) { @@ -132,7 +132,7 @@ static void remap_mono_to_stereo_sse2 (pa_remap_t *m, void *dst, const void *src } /* set the function that will execute the remapping based on the matrices */ -static void init_remap_sse2 (pa_remap_t *m) { +static void init_remap_sse2(pa_remap_t *m) { unsigned n_oc, n_ic; n_oc = m->o_ss->channels; @@ -147,7 +147,7 @@ static void init_remap_sse2 (pa_remap_t *m) { } #endif /* defined (__i386__) || defined (__amd64__) */ -void pa_remap_func_init_sse (pa_cpu_x86_flag_t flags) { +void pa_remap_func_init_sse(pa_cpu_x86_flag_t flags) { #if defined (__i386__) || defined (__amd64__) if (flags & PA_CPU_X86_SSE2) { diff --git a/src/pulsecore/resampler.c b/src/pulsecore/resampler.c index bed5a20d..b5c1611c 100644 --- a/src/pulsecore/resampler.c +++ b/src/pulsecore/resampler.c @@ -36,11 +36,11 @@ #include <pulsecore/log.h> #include <pulsecore/macro.h> #include <pulsecore/strbuf.h> +#include <pulsecore/remap.h> #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 @@ -898,7 +898,7 @@ static void calc_map_table(pa_resampler *r) { if (!on_center(r->o_cm.map[oc])) continue; - for (ic = 0; ic < n_ic; ic++) { + for (ic = 0; ic < n_ic; ic++) { if (ic_connected[ic]) { m->map_table_f[oc][ic] *= .9f; @@ -961,7 +961,7 @@ static void calc_map_table(pa_resampler *r) { if (ncenter[oc] <= 0) continue; - for (ic = 0; ic < n_ic; ic++) { + for (ic = 0; ic < n_ic; ic++) { if (ic_connected[ic]) { m->map_table_f[oc][ic] *= .75f; @@ -983,7 +983,7 @@ 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 < n_ic; ic++) { + for (ic = 0; ic < n_ic; ic++) { if (!on_lfe(r->i_cm.map[ic])) continue; @@ -1022,7 +1022,7 @@ static void calc_map_table(pa_resampler *r) { pa_xfree(t); /* initialize the remapping function */ - pa_init_remap (m); + pa_init_remap(m); } static pa_memchunk* convert_to_work_format(pa_resampler *r, pa_memchunk *input) { @@ -1096,8 +1096,8 @@ static pa_memchunk *remap_channels(pa_resampler *r, pa_memchunk *input) { remap = &r->remap; - pa_assert (remap->do_remap); - remap->do_remap (remap, dst, src, n_frames); + pa_assert(remap->do_remap); + remap->do_remap(remap, dst, src, n_frames); pa_memblock_release(input->memblock); pa_memblock_release(r->buf2.memblock); diff --git a/src/pulsecore/rtpoll.c b/src/pulsecore/rtpoll.c index 98d7d625..a5e990f6 100644 --- a/src/pulsecore/rtpoll.c +++ b/src/pulsecore/rtpoll.c @@ -124,7 +124,7 @@ static void rtpoll_rebuild(pa_rtpoll *p) { for (i = p->items; i; i = i->next) { - if (i->n_pollfd > 0) { + if (i->n_pollfd > 0) { size_t l = i->n_pollfd * sizeof(struct pollfd); if (i->pollfd) diff --git a/src/pulsecore/sample-util.c b/src/pulsecore/sample-util.c index 74600dec..62b7c468 100644 --- a/src/pulsecore/sample-util.c +++ b/src/pulsecore/sample-util.c @@ -37,9 +37,9 @@ #include <pulsecore/macro.h> #include <pulsecore/g711.h> #include <pulsecore/core-util.h> +#include <pulsecore/endianmacros.h> #include "sample-util.h" -#include "endianmacros.h" #define PA_SILENCE_MAX (PA_PAGE_SIZE*16) @@ -752,7 +752,7 @@ void pa_volume_memchunk( return; } - do_volume = pa_get_volume_func (spec->format); + do_volume = pa_get_volume_func(spec->format); pa_assert(do_volume); calc_volume_table[spec->format] ((void *)linear, volume); diff --git a/src/pulsecore/sconv-s16le.c b/src/pulsecore/sconv-s16le.c index 0fefdf1c..43587f3e 100644 --- a/src/pulsecore/sconv-s16le.c +++ b/src/pulsecore/sconv-s16le.c @@ -31,8 +31,7 @@ #include <pulsecore/sconv.h> #include <pulsecore/macro.h> #include <pulsecore/log.h> - -#include "endianmacros.h" +#include <pulsecore/endianmacros.h> #include "sconv-s16le.h" diff --git a/src/pulsecore/sconv.c b/src/pulsecore/sconv.c index 301f08b4..988d4b33 100644 --- a/src/pulsecore/sconv.c +++ b/src/pulsecore/sconv.c @@ -29,10 +29,10 @@ #include <pulsecore/g711.h> #include <pulsecore/macro.h> +#include <pulsecore/endianmacros.h> -#include "endianmacros.h" -#include "sconv-s16le.h" -#include "sconv-s16be.h" +#include <pulsecore/sconv-s16le.h> +#include <pulsecore/sconv-s16be.h> #include "sconv.h" diff --git a/src/pulsecore/sconv_sse.c b/src/pulsecore/sconv_sse.c index 3737af2a..26daa223 100644 --- a/src/pulsecore/sconv_sse.c +++ b/src/pulsecore/sconv_sse.c @@ -29,13 +29,12 @@ #include <pulsecore/g711.h> #include <pulsecore/macro.h> - -#include "endianmacros.h" +#include <pulsecore/endianmacros.h> #include "cpu-x86.h" #include "sconv.h" -#if defined (__i386__) || defined (__amd64__) +#if !defined(__APPLE__) && defined (__i386__) || defined (__amd64__) static const PA_DECLARE_ALIGNED (16, float, one[4]) = { 1.0, 1.0, 1.0, 1.0 }; static const PA_DECLARE_ALIGNED (16, float, mone[4]) = { -1.0, -1.0, -1.0, -1.0 }; @@ -170,7 +169,7 @@ static void pa_sconv_s16le_from_f32ne_sse2(unsigned n, const float *a, int16_t * #define SAMPLES 1019 #define TIMES 1000 -static void run_test (void) { +static void run_test(void) { int16_t samples[SAMPLES]; int16_t samples_ref[SAMPLES]; float floats[SAMPLES]; @@ -178,18 +177,18 @@ static void run_test (void) { pa_usec_t start, stop; pa_convert_func_t func; - printf ("checking SSE %zd\n", sizeof (samples)); + printf("checking SSE %zd\n", sizeof(samples)); - memset (samples_ref, 0, sizeof (samples_ref)); - memset (samples, 0, sizeof (samples)); + memset(samples_ref, 0, sizeof(samples_ref)); + memset(samples, 0, sizeof(samples)); for (i = 0; i < SAMPLES; i++) { floats[i] = (rand()/(RAND_MAX+2.2)) - 1.1; } - func = pa_get_convert_from_float32ne_function (PA_SAMPLE_S16LE); - func (SAMPLES, floats, samples_ref); - pa_sconv_s16le_from_f32ne_sse2 (SAMPLES, floats, samples); + func = pa_get_convert_from_float32ne_function(PA_SAMPLE_S16LE); + func(SAMPLES, floats, samples_ref); + pa_sconv_s16le_from_f32ne_sse2(SAMPLES, floats, samples); for (i = 0; i < SAMPLES; i++) { if (samples[i] != samples_ref[i]) { @@ -200,14 +199,14 @@ static void run_test (void) { start = pa_rtclock_now(); for (i = 0; i < TIMES; i++) { - pa_sconv_s16le_from_f32ne_sse2 (SAMPLES, floats, samples); + pa_sconv_s16le_from_f32ne_sse2(SAMPLES, floats, samples); } stop = pa_rtclock_now(); pa_log_info("SSE: %llu usec.", (long long unsigned int)(stop - start)); start = pa_rtclock_now(); for (i = 0; i < TIMES; i++) { - func (SAMPLES, floats, samples_ref); + func(SAMPLES, floats, samples_ref); } stop = pa_rtclock_now(); pa_log_info("ref: %llu usec.", (long long unsigned int)(stop - start)); @@ -216,19 +215,19 @@ static void run_test (void) { #endif /* defined (__i386__) || defined (__amd64__) */ -void pa_convert_func_init_sse (pa_cpu_x86_flag_t flags) { -#if defined (__i386__) || defined (__amd64__) +void pa_convert_func_init_sse(pa_cpu_x86_flag_t flags) { +#if !defined(__APPLE__) && defined (__i386__) || defined (__amd64__) #ifdef RUN_TEST - run_test (); + run_test(); #endif if (flags & PA_CPU_X86_SSE2) { pa_log_info("Initialising SSE2 optimized conversions."); - pa_set_convert_from_float32ne_function (PA_SAMPLE_S16LE, (pa_convert_func_t) pa_sconv_s16le_from_f32ne_sse2); + pa_set_convert_from_float32ne_function(PA_SAMPLE_S16LE, (pa_convert_func_t) pa_sconv_s16le_from_f32ne_sse2); } else { pa_log_info("Initialising SSE optimized conversions."); - pa_set_convert_from_float32ne_function (PA_SAMPLE_S16LE, (pa_convert_func_t) pa_sconv_s16le_from_f32ne_sse); + pa_set_convert_from_float32ne_function(PA_SAMPLE_S16LE, (pa_convert_func_t) pa_sconv_s16le_from_f32ne_sse); } #endif /* defined (__i386__) || defined (__amd64__) */ diff --git a/src/pulsecore/semaphore-osx.c b/src/pulsecore/semaphore-osx.c index 73f43559..42afd154 100644 --- a/src/pulsecore/semaphore-osx.c +++ b/src/pulsecore/semaphore-osx.c @@ -30,8 +30,7 @@ #include "semaphore.h" -struct pa_semaphore -{ +struct pa_semaphore { MPSemaphoreID sema; }; diff --git a/src/pulsecore/semaphore-win32.c b/src/pulsecore/semaphore-win32.c index 9ffbde66..c2e00c63 100644 --- a/src/pulsecore/semaphore-win32.c +++ b/src/pulsecore/semaphore-win32.c @@ -30,8 +30,7 @@ #include "semaphore.h" -struct pa_semaphore -{ +struct pa_semaphore { HANDLE sema; }; diff --git a/src/pulsecore/shm.c b/src/pulsecore/shm.c index 5d5d85ab..da8aff70 100644 --- a/src/pulsecore/shm.c +++ b/src/pulsecore/shm.c @@ -90,10 +90,12 @@ struct shm_marker { #define SHM_MARKER_SIZE PA_ALIGN(sizeof(struct shm_marker)) +#ifdef HAVE_SHM_OPEN static char *segment_name(char *fn, size_t l, unsigned id) { pa_snprintf(fn, l, "/pulse-shm-%u", id); return fn; } +#endif int pa_shm_create_rw(pa_shm *m, size_t size, pa_bool_t shared, mode_t mode) { char fn[32]; diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index 065fd2d3..e0e81be4 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -38,7 +38,6 @@ #include <pulsecore/play-memblockq.h> #include <pulsecore/namereg.h> #include <pulsecore/core-util.h> -#include <pulse/timeval.h> #include "sink-input.h" @@ -49,11 +48,6 @@ PA_DEFINE_PUBLIC_CLASS(pa_sink_input, pa_msgobject); static void sink_input_free(pa_object *o); static void set_real_ratio(pa_sink_input *i, const pa_cvolume *v); -static void sink_input_set_ramping_info(pa_sink_input* i, pa_volume_t pre_virtual_volume, pa_volume_t target_virtual_volume, pa_usec_t t); -static void sink_input_set_ramping_info_for_mute(pa_sink_input* i, pa_bool_t mute, pa_usec_t t); -static void sink_input_volume_ramping(pa_sink_input* i, pa_memchunk* chunk); -static void sink_input_rewind_ramp_info(pa_sink_input *i, size_t nbytes); -static void sink_input_release_envelope(pa_sink_input *i); static int check_passthrough_connection(pa_sink_input_flags_t flags, pa_sink *dest) { @@ -80,10 +74,10 @@ static int check_passthrough_connection(pa_sink_input_flags_t flags, pa_sink *de } } else { - if (flags & PA_SINK_INPUT_PASSTHROUGH) { - pa_log_warn("Cannot connect PASSTHROUGH sink input to sink without PASSTHROUGH capabilities"); - return -PA_ERR_INVALID; - } + if (flags & PA_SINK_INPUT_PASSTHROUGH) { + pa_log_warn("Cannot connect PASSTHROUGH sink input to sink without PASSTHROUGH capabilities"); + return -PA_ERR_INVALID; + } } return PA_OK; } @@ -112,8 +106,21 @@ void pa_sink_input_new_data_set_channel_map(pa_sink_input_new_data *data, const data->channel_map = *map; } +pa_bool_t pa_sink_input_new_data_is_volume_writable(pa_sink_input_new_data *data) { + pa_assert(data); + + if (data->flags & PA_SINK_INPUT_PASSTHROUGH) + return FALSE; + + if (data->origin_sink && (data->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) + return FALSE; + + return TRUE; +} + void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cvolume *volume) { pa_assert(data); + pa_assert(pa_sink_input_new_data_is_volume_writable(data)); if ((data->volume_is_set = !!volume)) data->volume = *volume; @@ -205,6 +212,7 @@ int pa_sink_input_new( if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], data)) < 0) return r; + pa_assert(!data->volume_is_set || pa_sink_input_new_data_is_volume_writable(data)); pa_return_val_if_fail(!data->driver || pa_utf8_valid(data->driver), -PA_ERR_INVALID); if (!data->sink) { @@ -321,6 +329,7 @@ int pa_sink_input_new( i->driver = pa_xstrdup(pa_path_get_filename(data->driver)); i->module = data->module; i->sink = data->sink; + i->origin_sink = data->origin_sink; i->client = data->client; i->requested_resample_method = data->resample_method; @@ -328,7 +337,7 @@ int pa_sink_input_new( i->sample_spec = data->sample_spec; i->channel_map = data->channel_map; - if ((i->sink->flags & PA_SINK_FLAT_VOLUME) && !data->volume_is_absolute) { + if (!data->volume_is_absolute && pa_sink_flat_volume_enabled(i->sink)) { pa_cvolume remapped; /* When the 'absolute' bool is not set then we'll treat the volume @@ -365,16 +374,6 @@ int pa_sink_input_new( reset_callbacks(i); i->userdata = NULL; - /* Set Ramping info */ - i->thread_info.ramp_info.is_ramping = FALSE; - i->thread_info.ramp_info.envelope_dead = TRUE; - i->thread_info.ramp_info.envelope = NULL; - i->thread_info.ramp_info.item = NULL; - i->thread_info.ramp_info.envelope_dying = 0; - - pa_atomic_store(&i->before_ramping_v, 0); - pa_atomic_store(&i->before_ramping_m, 0); - i->thread_info.state = i->state; i->thread_info.attached = FALSE; pa_atomic_store(&i->thread_info.drained, 1); @@ -481,7 +480,7 @@ static void sink_input_set_state(pa_sink_input *i, pa_sink_input_state_t state) /* Called from main context */ void pa_sink_input_unlink(pa_sink_input *i) { pa_bool_t linked; - pa_source_output *o, *p = NULL; + pa_source_output *o, *p = NULL; pa_assert(i); pa_assert_ctl_context(); @@ -523,7 +522,7 @@ void pa_sink_input_unlink(pa_sink_input *i) { if (linked && i->sink) { /* We might need to update the sink's volume if we are in flat volume mode. */ - if (i->sink->flags & PA_SINK_FLAT_VOLUME) + if (pa_sink_flat_volume_enabled(i->sink)) pa_sink_set_volume(i->sink, NULL, FALSE, FALSE); if (i->sink->asyncmsgq) @@ -565,12 +564,6 @@ static void sink_input_free(pa_object *o) { * "half-moved" or are connected to sinks that have no asyncmsgq * and are hence half-destructed themselves! */ - if (i->thread_info.ramp_info.envelope) { - pa_log_debug ("Freeing envelope\n"); - pa_envelope_free(i->thread_info.ramp_info.envelope); - i->thread_info.ramp_info.envelope = NULL; - } - if (i->thread_info.render_memblockq) pa_memblockq_free(i->thread_info.render_memblockq); @@ -610,10 +603,16 @@ void pa_sink_input_put(pa_sink_input *i) { i->state = state; /* We might need to update the sink's volume if we are in flat volume mode. */ - if (i->sink->flags & PA_SINK_FLAT_VOLUME) + if (pa_sink_flat_volume_enabled(i->sink)) pa_sink_set_volume(i->sink, NULL, FALSE, i->save_volume); - else + else { + if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) { + pa_assert(pa_cvolume_is_norm(&i->volume)); + pa_assert(pa_cvolume_is_norm(&i->reference_ratio)); + } + set_real_ratio(i, &i->volume); + } i->thread_info.soft_volume = i->soft_volume; i->thread_info.muted = i->muted; @@ -658,7 +657,6 @@ pa_usec_t pa_sink_input_get_latency(pa_sink_input *i, pa_usec_t *sink_latency) { void pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa_memchunk *chunk, pa_cvolume *volume) { pa_bool_t do_volume_adj_here, need_volume_factor_sink; pa_bool_t volume_is_norm; - pa_bool_t ramping; size_t block_size_max_sink, block_size_max_sink_input; size_t ilength; @@ -703,7 +701,7 @@ void pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, p * to adjust the volume *before* we resample. Otherwise we can do * it after and leave it for the sink code */ - do_volume_adj_here = !pa_channel_map_equal(&i->channel_map, &i->sink->channel_map) || i->thread_info.ramp_info.is_ramping; + do_volume_adj_here = !pa_channel_map_equal(&i->channel_map, &i->sink->channel_map); volume_is_norm = pa_cvolume_is_norm(&i->thread_info.soft_volume) && !i->thread_info.muted; need_volume_factor_sink = !pa_cvolume_is_norm(&i->volume_factor_sink); @@ -746,7 +744,7 @@ void pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, p wchunk.length = block_size_max_sink_input; /* It might be necessary to adjust the volume here */ - if (do_volume_adj_here && !volume_is_norm && !i->thread_info.ramp_info.is_ramping) { + if (do_volume_adj_here && !volume_is_norm) { pa_memchunk_make_writable(&wchunk, 0); if (i->thread_info.muted) { @@ -812,23 +810,6 @@ void pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, p if (chunk->length > block_size_max_sink) chunk->length = block_size_max_sink; - ramping = i->thread_info.ramp_info.is_ramping; - if (ramping) - sink_input_volume_ramping(i, chunk); - - if (!i->thread_info.ramp_info.envelope_dead) { - i->thread_info.ramp_info.envelope_dying += chunk->length; - pa_log_debug("Envelope dying is %d, chunk length is %zu, dead thresholder is %lu\n", i->thread_info.ramp_info.envelope_dying, - chunk->length, - i->sink->thread_info.max_rewind + pa_envelope_length(i->thread_info.ramp_info.envelope)); - - if (i->thread_info.ramp_info.envelope_dying >= (int32_t) (i->sink->thread_info.max_rewind + pa_envelope_length(i->thread_info.ramp_info.envelope))) { - pa_log_debug("RELEASE Envelop"); - i->thread_info.ramp_info.envelope_dead = TRUE; - sink_input_release_envelope(i); - } - } - /* Let's see if we had to apply the volume adjustment ourselves, * or if this can be done by the sink for us */ @@ -873,7 +854,6 @@ void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sam if (nbytes > 0 && !i->thread_info.dont_rewind_render) { pa_log_debug("Have to rewind %lu bytes on render memblockq.", (unsigned long) nbytes); pa_memblockq_rewind(i->thread_info.render_memblockq, nbytes); - sink_input_rewind_ramp_info(i, nbytes); } if (i->thread_info.rewrite_nbytes == (size_t) -1) { @@ -1010,7 +990,7 @@ pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec) if (usec != (pa_usec_t) -1) { pa_usec_t min_latency, max_latency; pa_sink_get_latency_range(i->sink, &min_latency, &max_latency); - usec = PA_CLAMP(usec, min_latency, max_latency); + usec = PA_CLAMP(usec, min_latency, max_latency); } } @@ -1037,6 +1017,64 @@ pa_usec_t pa_sink_input_get_requested_latency(pa_sink_input *i) { } /* Called from main context */ +void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_bool_t save, pa_bool_t absolute) { + pa_cvolume v; + + pa_sink_input_assert_ref(i); + pa_assert_ctl_context(); + pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); + pa_assert(volume); + pa_assert(pa_cvolume_valid(volume)); + pa_assert(volume->channels == 1 || pa_cvolume_compatible(volume, &i->sample_spec)); + pa_assert(pa_sink_input_is_volume_writable(i)); + + if ((i->sink->flags & PA_SINK_FLAT_VOLUME) && !absolute) { + v = i->sink->reference_volume; + pa_cvolume_remap(&v, &i->sink->channel_map, &i->channel_map); + + if (pa_cvolume_compatible(volume, &i->sample_spec)) + volume = pa_sw_cvolume_multiply(&v, &v, volume); + else + volume = pa_sw_cvolume_multiply_scalar(&v, &v, pa_cvolume_max(volume)); + } else { + if (!pa_cvolume_compatible(volume, &i->sample_spec)) { + v = i->volume; + volume = pa_cvolume_scale(&v, pa_cvolume_max(volume)); + } + } + + if (pa_cvolume_equal(volume, &i->volume)) { + i->save_volume = i->save_volume || save; + return; + } + + i->volume = *volume; + i->save_volume = save; + + if (i->sink->flags & PA_SINK_FLAT_VOLUME) { + /* We are in flat volume mode, so let's update all sink input + * volumes and update the flat volume of the sink */ + + pa_sink_set_volume(i->sink, NULL, TRUE, save); + + } else { + /* OK, we are in normal volume mode. The volume only affects + * ourselves */ + set_real_ratio(i, volume); + + /* Copy the new soft_volume to the thread_info struct */ + pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_SOFT_VOLUME, NULL, 0, NULL) == 0); + } + + /* The volume changed, let's tell people so */ + if (i->volume_changed) + i->volume_changed(i); + + /* The virtual volume changed, let's tell people so */ + pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); +} + +/* Called from main context */ static void set_real_ratio(pa_sink_input *i, const pa_cvolume *v) { pa_sink_input_assert_ref(i); pa_assert_ctl_context(); @@ -1058,14 +1096,25 @@ static void set_real_ratio(pa_sink_input *i, const pa_cvolume *v) { } /* Called from main context */ -void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_bool_t save, pa_bool_t absolute) { +pa_bool_t pa_sink_input_is_volume_readable(pa_sink_input *i) { + pa_sink_input_assert_ref(i); + pa_assert_ctl_context(); + + return !(i->flags & PA_SINK_INPUT_PASSTHROUGH); +} + +/* Called from main context */ +pa_bool_t pa_sink_input_is_volume_writable(pa_sink_input *i) { + pa_sink_input_assert_ref(i); + pa_assert_ctl_context(); - /* Do not allow for volume changes for non-audio types */ if (i->flags & PA_SINK_INPUT_PASSTHROUGH) - return; + return FALSE; + + if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) + return FALSE; - /* test ramping -> return pa_sink_input_set_volume_with_ramping(i, volume, save, absolute, 2000 * PA_USEC_PER_MSEC); */ - return pa_sink_input_set_volume_with_ramping(i, volume, save, absolute, 0); + return TRUE; } /* Called from main context */ @@ -1073,8 +1122,9 @@ pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i, pa_cvolume *volume, pa_bo pa_sink_input_assert_ref(i); pa_assert_ctl_context(); pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); + pa_assert(pa_sink_input_is_volume_readable(i)); - if (absolute || !(i->sink->flags & PA_SINK_FLAT_VOLUME)) + if (absolute || !pa_sink_flat_volume_enabled(i->sink)) *volume = i->volume; else *volume = i->reference_ratio; @@ -1084,8 +1134,25 @@ pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i, pa_cvolume *volume, pa_bo /* Called from main context */ void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute, pa_bool_t save) { - /* test ramping -> return pa_sink_input_set_mute_with_ramping(i, mute, save, 2000 * PA_USEC_PER_MSEC); */ - return pa_sink_input_set_mute_with_ramping(i, mute, save, 0); + pa_sink_input_assert_ref(i); + pa_assert_ctl_context(); + pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); + + if (!i->muted == !mute) { + i->save_muted = i->save_muted || mute; + return; + } + + i->muted = mute; + i->save_muted = save; + + pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_SOFT_MUTE, NULL, 0, NULL) == 0); + + /* The mute status changed, let's tell people so */ + if (i->mute_changed) + i->mute_changed(i); + + pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); } /* Called from main context */ @@ -1245,7 +1312,7 @@ int pa_sink_input_start_move(pa_sink_input *i) { if (pa_sink_input_get_state(i) == PA_SINK_INPUT_CORKED) pa_assert_se(i->sink->n_corked-- >= 1); - if (i->sink->flags & PA_SINK_FLAT_VOLUME) + if (pa_sink_flat_volume_enabled(i->sink)) /* We might need to update the sink's volume if we are in flat * volume mode. */ pa_sink_set_volume(i->sink, NULL, FALSE, FALSE); @@ -1261,6 +1328,156 @@ int pa_sink_input_start_move(pa_sink_input *i) { return 0; } +/* Called from main context. If i has an origin sink that uses volume sharing, + * then also the origin sink and all streams connected to it need to update + * their volume - this function does all that by using recursion. */ +static void update_volume_due_to_moving(pa_sink_input *i, pa_sink *dest) { + pa_cvolume old_volume; + + pa_assert(i); + pa_assert(dest); + pa_assert(i->sink); /* The destination sink should already be set. */ + + if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) { + pa_sink *root_sink = i->sink; + pa_sink_input *origin_sink_input; + uint32_t idx; + + while (root_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER) + root_sink = root_sink->input_to_master->sink; + + if (pa_sink_flat_volume_enabled(i->sink)) { + /* Ok, so the origin sink uses volume sharing, and flat volume is + * enabled. The volume will have to be updated as follows: + * + * i->volume := i->sink->real_volume + * (handled later by pa_sink_set_volume) + * i->reference_ratio := i->volume / i->sink->reference_volume + * (handled later by pa_sink_set_volume) + * i->real_ratio stays unchanged + * (streams whose origin sink uses volume sharing should + * always have real_ratio of 0 dB) + * i->soft_volume stays unchanged + * (streams whose origin sink uses volume sharing should + * always have volume_factor as soft_volume, so no change + * should be needed) */ + + pa_assert(pa_cvolume_is_norm(&i->real_ratio)); + pa_assert(pa_cvolume_equal(&i->soft_volume, &i->volume_factor)); + + /* Notifications will be sent by pa_sink_set_volume(). */ + + } else { + /* Ok, so the origin sink uses volume sharing, and flat volume is + * disabled. The volume will have to be updated as follows: + * + * i->volume := 0 dB + * i->reference_ratio := 0 dB + * i->real_ratio stays unchanged + * (streams whose origin sink uses volume sharing should + * always have real_ratio of 0 dB) + * i->soft_volume stays unchanged + * (streams whose origin sink uses volume sharing should + * always have volume_factor as soft_volume, so no change + * should be needed) */ + + old_volume = i->volume; + pa_cvolume_reset(&i->volume, i->volume.channels); + pa_cvolume_reset(&i->reference_ratio, i->reference_ratio.channels); + pa_assert(pa_cvolume_is_norm(&i->real_ratio)); + pa_assert(pa_cvolume_equal(&i->soft_volume, &i->volume_factor)); + + /* Notify others about the changed sink input volume. */ + if (!pa_cvolume_equal(&i->volume, &old_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); + } + } + + /* Additionally, the origin sink volume needs updating: + * + * i->origin_sink->reference_volume := root_sink->reference_volume + * i->origin_sink->real_volume := root_sink->real_volume + * i->origin_sink->soft_volume stays unchanged + * (sinks that use volume sharing should always have + * soft_volume of 0 dB) */ + + old_volume = i->origin_sink->reference_volume; + + i->origin_sink->reference_volume = root_sink->reference_volume; + pa_cvolume_remap(&i->origin_sink->reference_volume, &root_sink->channel_map, &i->origin_sink->channel_map); + + i->origin_sink->real_volume = root_sink->real_volume; + pa_cvolume_remap(&i->origin_sink->real_volume, &root_sink->channel_map, &i->origin_sink->channel_map); + + pa_assert(pa_cvolume_is_norm(&i->origin_sink->soft_volume)); + + /* Notify others about the changed sink volume. If you wonder whether + * i->origin_sink->set_volume() should be called somewhere, that's not + * the case, because sinks that use volume sharing shouldn't have any + * internal volume that set_volume() would update. If you wonder + * whether the thread_info variables should be synced, yes, they + * should, and it's done by the PA_SINK_MESSAGE_FINISH_MOVE message + * handler. */ + if (!pa_cvolume_equal(&i->origin_sink->reference_volume, &old_volume)) + pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, i->origin_sink->index); + + /* Recursively update origin sink inputs. */ + PA_IDXSET_FOREACH(origin_sink_input, i->origin_sink->inputs, idx) + update_volume_due_to_moving(origin_sink_input, dest); + + } else { + old_volume = i->volume; + + if (pa_sink_flat_volume_enabled(i->sink)) { + /* Ok, so this is a regular stream, and flat volume is enabled. The + * volume will have to be updated as follows: + * + * i->volume := i->reference_ratio * i->sink->reference_volume + * i->reference_ratio stays unchanged + * i->real_ratio := i->volume / i->sink->real_volume + * (handled later by pa_sink_set_volume) + * i->soft_volume := i->real_ratio * i->volume_factor + * (handled later by pa_sink_set_volume) */ + + i->volume = i->sink->reference_volume; + pa_cvolume_remap(&i->volume, &i->sink->channel_map, &i->channel_map); + pa_sw_cvolume_multiply(&i->volume, &i->volume, &i->reference_ratio); + + } else { + /* Ok, so this is a regular stream, and flat volume is disabled. + * The volume will have to be updated as follows: + * + * i->volume := i->reference_ratio + * i->reference_ratio stays unchanged + * i->real_ratio := i->reference_ratio + * i->soft_volume := i->real_ratio * i->volume_factor */ + + i->volume = i->reference_ratio; + i->real_ratio = i->reference_ratio; + pa_sw_cvolume_multiply(&i->soft_volume, &i->real_ratio, &i->volume_factor); + } + + /* Notify others about the changed sink input volume. */ + if (!pa_cvolume_equal(&i->volume, &old_volume)) { + /* XXX: In case i->sink has flat volume enabled, then real_ratio + * and soft_volume are not updated yet. Let's hope that the + * callback implementation doesn't care about those variables... */ + if (i->volume_changed) + i->volume_changed(i); + + pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); + } + } + + /* If i->sink == dest, then recursion has finished, and we can finally call + * pa_sink_set_volume(), which will do the rest of the updates. */ + if ((i->sink == dest) && pa_sink_flat_volume_enabled(i->sink)) + pa_sink_set_volume(i->sink, NULL, FALSE, i->save_volume); +} + /* Called from main context */ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) { pa_resampler *new_resampler; @@ -1334,17 +1551,7 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) { } pa_sink_update_status(dest); - if (i->sink->flags & PA_SINK_FLAT_VOLUME) { - pa_cvolume remapped; - - /* Make relative volumes absolute */ - remapped = dest->reference_volume; - pa_cvolume_remap(&remapped, &dest->channel_map, &i->channel_map); - pa_sw_cvolume_multiply(&i->volume, &i->reference_ratio, &remapped); - - /* We might need to update the sink's volume if we are in flat volume mode. */ - pa_sink_set_volume(i->sink, NULL, FALSE, i->save_volume); - } + update_volume_due_to_moving(i, dest); pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_FINISH_MOVE, i, 0, NULL) == 0); @@ -1353,9 +1560,6 @@ 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; @@ -1464,23 +1668,15 @@ int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t switch (code) { case PA_SINK_INPUT_MESSAGE_SET_SOFT_VOLUME: - if (pa_atomic_load(&i->before_ramping_v)) - i->thread_info.future_soft_volume = i->soft_volume; - if (!pa_cvolume_equal(&i->thread_info.soft_volume, &i->soft_volume)) { - if (!pa_atomic_load(&i->before_ramping_v)) - i->thread_info.soft_volume = i->soft_volume; + i->thread_info.soft_volume = i->soft_volume; pa_sink_input_request_rewind(i, 0, TRUE, FALSE, FALSE); } return 0; case PA_SINK_INPUT_MESSAGE_SET_SOFT_MUTE: - if (pa_atomic_load(&i->before_ramping_m)) - i->thread_info.future_muted = i->muted; - if (i->thread_info.muted != i->muted) { - if (!pa_atomic_load(&i->before_ramping_m)) - i->thread_info.muted = i->muted; + i->thread_info.muted = i->muted; pa_sink_input_request_rewind(i, 0, TRUE, FALSE, FALSE); } return 0; @@ -1528,26 +1724,6 @@ int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t *r = i->thread_info.requested_sink_latency; return 0; } - - case PA_SINK_INPUT_MESSAGE_SET_ENVELOPE: { - if (!i->thread_info.ramp_info.envelope) - i->thread_info.ramp_info.envelope = pa_envelope_new(&i->sink->sample_spec); - - if (i->thread_info.ramp_info.envelope && i->thread_info.ramp_info.item) { - pa_envelope_remove(i->thread_info.ramp_info.envelope, i->thread_info.ramp_info.item); - i->thread_info.ramp_info.item = NULL; - } - - i->thread_info.ramp_info.item = pa_envelope_add(i->thread_info.ramp_info.envelope, &i->using_def); - i->thread_info.ramp_info.is_ramping = TRUE; - i->thread_info.ramp_info.envelope_dead = FALSE; - i->thread_info.ramp_info.envelope_dying = 0; - - if (i->thread_info.ramp_info.envelope) - pa_envelope_restart(i->thread_info.ramp_info.envelope); - - return 0; - } } return -PA_ERR_NOTIMPLEMENTED; @@ -1710,237 +1886,3 @@ finish: if (pl) pa_proplist_free(pl); } - -/* Called from IO context */ -static void sink_input_volume_ramping(pa_sink_input* i, pa_memchunk* chunk) { - pa_assert(i); - pa_assert(chunk); - pa_assert(chunk->memblock); - pa_assert(i->thread_info.ramp_info.is_ramping); - - /* Volume is adjusted with ramping effect here */ - pa_envelope_apply(i->thread_info.ramp_info.envelope, chunk); - - if (pa_envelope_is_finished(i->thread_info.ramp_info.envelope)) { - i->thread_info.ramp_info.is_ramping = FALSE; - if (pa_atomic_load(&i->before_ramping_v)) { - i->thread_info.soft_volume = i->thread_info.future_soft_volume; - pa_atomic_store(&i->before_ramping_v, 0); - } - else if (pa_atomic_load(&i->before_ramping_m)) { - i->thread_info.muted = i->thread_info.future_muted; - pa_atomic_store(&i->before_ramping_m, 0); - } - } -} - -/* - * Called from main context - * This function should be called inside pa_sink_input_set_volume_with_ramping - * should be called after soft_volume of sink_input and sink are all adjusted - */ -static void sink_input_set_ramping_info(pa_sink_input* i, pa_volume_t pre_virtual_volume, pa_volume_t target_virtual_volume, pa_usec_t t) { - - int32_t target_abs_vol, target_apply_vol, pre_apply_vol; - pa_assert(i); - - pa_log_debug("Sink input's soft volume is %d= %f ", pa_cvolume_avg(&i->soft_volume), pa_sw_volume_to_linear(pa_cvolume_avg(&i->soft_volume))); - - /* Calculation formula are target_abs_vol := i->soft_volume - * target_apply_vol := lrint(pa_sw_volume_to_linear(target_abs_vol) * 0x10000) - * pre_apply_vol := ( previous_virtual_volume / target_virtual_volume ) * target_apply_vol - * - * Will do volume adjustment inside pa_sink_input_peek - */ - target_abs_vol = pa_cvolume_avg(&i->soft_volume); - target_apply_vol = (int32_t) lrint(pa_sw_volume_to_linear(target_abs_vol) * 0x10000); - pre_apply_vol = (int32_t) ((pa_sw_volume_to_linear(pre_virtual_volume) / pa_sw_volume_to_linear(target_virtual_volume)) * target_apply_vol); - - i->using_def.n_points = 2; - i->using_def.points_x[0] = 0; - i->using_def.points_x[1] = t; - i->using_def.points_y.i[0] = pre_apply_vol; - i->using_def.points_y.i[1] = target_apply_vol; - i->using_def.points_y.f[0] = ((float) i->using_def.points_y.i[0]) /0x10000; - i->using_def.points_y.f[1] = ((float) i->using_def.points_y.i[1]) /0x10000; - - pa_log_debug("Volume Ramping: Point 1 is %d=%f, Point 2 is %d=%f\n", i->using_def.points_y.i[0], i->using_def.points_y.f[0], - i->using_def.points_y.i[1], i->using_def.points_y.f[1]); - - pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_ENVELOPE, NULL, 0, NULL) == 0); -} - -/* Called from main context */ -static void sink_input_set_ramping_info_for_mute(pa_sink_input* i, pa_bool_t mute, pa_usec_t t) { - - int32_t cur_vol; - pa_assert(i); - - i->using_def.n_points = 2; - i->using_def.points_x[0] = 0; - i->using_def.points_x[1] = t; - cur_vol = (int32_t) lrint( pa_sw_volume_to_linear(pa_cvolume_avg(&i->soft_volume)) * 0x10000); - - if (mute) { - i->using_def.points_y.i[0] = cur_vol; - i->using_def.points_y.i[1] = 0; - } else { - i->using_def.points_y.i[0] = 0; - i->using_def.points_y.i[1] = cur_vol; - } - - i->using_def.points_y.f[0] = ((float) i->using_def.points_y.i[0]) /0x10000; - i->using_def.points_y.f[1] = ((float) i->using_def.points_y.i[1]) /0x10000; - - pa_log_debug("Mute Ramping: Point 1 is %d=%f, Point 2 is %d=%f\n", i->using_def.points_y.i[0], i->using_def.points_y.f[0], - i->using_def.points_y.i[1], i->using_def.points_y.f[1]); - - pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_ENVELOPE, NULL, 0, NULL) == 0); -} - -/* Called from IO context */ -static void sink_input_release_envelope(pa_sink_input *i) { - pa_assert(i); - pa_assert(!i->thread_info.ramp_info.is_ramping); - pa_assert(i->thread_info.ramp_info.envelope_dead); - - pa_envelope_free(i->thread_info.ramp_info.envelope); - i->thread_info.ramp_info.envelope = NULL; - i->thread_info.ramp_info.item = NULL; -} - -/* Called from IO context */ -static void sink_input_rewind_ramp_info(pa_sink_input *i, size_t nbytes) { - pa_assert(i); - - if (!i->thread_info.ramp_info.envelope_dead) { - int32_t envelope_length; - - pa_assert(i->thread_info.ramp_info.envelope); - - envelope_length = pa_envelope_length(i->thread_info.ramp_info.envelope); - - if (i->thread_info.ramp_info.envelope_dying > envelope_length) { - if ((int32_t) (i->thread_info.ramp_info.envelope_dying - nbytes) < envelope_length) { - pa_log_debug("Envelope Become Alive"); - pa_envelope_rewind(i->thread_info.ramp_info.envelope, envelope_length - (i->thread_info.ramp_info.envelope_dying - nbytes)); - i->thread_info.ramp_info.is_ramping = TRUE; - } - } else if (i->thread_info.ramp_info.envelope_dying < envelope_length) { - if ((i->thread_info.ramp_info.envelope_dying - (ssize_t) nbytes) <= 0) { - pa_log_debug("Envelope Restart"); - pa_envelope_restart(i->thread_info.ramp_info.envelope); - } - else { - pa_log_debug("Envelope Simple Rewind"); - pa_envelope_rewind(i->thread_info.ramp_info.envelope, nbytes); - } - } - - i->thread_info.ramp_info.envelope_dying -= nbytes; - if (i->thread_info.ramp_info.envelope_dying <= 0) - i->thread_info.ramp_info.envelope_dying = 0; - } -} - -void pa_sink_input_set_volume_with_ramping(pa_sink_input *i, const pa_cvolume *volume, pa_bool_t save, pa_bool_t absolute, pa_usec_t t){ - pa_cvolume v; - pa_volume_t previous_virtual_volume, target_virtual_volume; - - pa_sink_input_assert_ref(i); - pa_assert_ctl_context(); - pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); - pa_assert(volume); - pa_assert(pa_cvolume_valid(volume)); - pa_assert(volume->channels == 1 || pa_cvolume_compatible(volume, &i->sample_spec)); - - if ((i->sink->flags & PA_SINK_FLAT_VOLUME) && !absolute) { - v = i->sink->reference_volume; - pa_cvolume_remap(&v, &i->sink->channel_map, &i->channel_map); - - if (pa_cvolume_compatible(volume, &i->sample_spec)) - volume = pa_sw_cvolume_multiply(&v, &v, volume); - else - volume = pa_sw_cvolume_multiply_scalar(&v, &v, pa_cvolume_max(volume)); - } else { - - if (!pa_cvolume_compatible(volume, &i->sample_spec)) { - v = i->volume; - volume = pa_cvolume_scale(&v, pa_cvolume_max(volume)); - } - } - - if (pa_cvolume_equal(volume, &i->volume)) { - i->save_volume = i->save_volume || save; - return; - } - - previous_virtual_volume = pa_cvolume_avg(&i->volume); - target_virtual_volume = pa_cvolume_avg(volume); - - if (t > 0 && target_virtual_volume > 0) - pa_log_debug("SetVolumeWithRamping: Virtual Volume From %u=%f to %u=%f\n", previous_virtual_volume, pa_sw_volume_to_linear(previous_virtual_volume), - target_virtual_volume, pa_sw_volume_to_linear(target_virtual_volume)); - - i->volume = *volume; - i->save_volume = save; - - /* Set this flag before the following code modify i->thread_info.soft_volume */ - if (t > 0 && target_virtual_volume > 0) - pa_atomic_store(&i->before_ramping_v, 1); - - if (i->sink->flags & PA_SINK_FLAT_VOLUME) { - /* We are in flat volume mode, so let's update all sink input - * volumes and update the flat volume of the sink */ - - pa_sink_set_volume(i->sink, NULL, TRUE, save); - - } else { - /* OK, we are in normal volume mode. The volume only affects - * ourselves */ - set_real_ratio(i, volume); - - /* Copy the new soft_volume to the thread_info struct */ - pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_SOFT_VOLUME, NULL, 0, NULL) == 0); - } - - if (t > 0 && target_virtual_volume > 0) - sink_input_set_ramping_info(i, previous_virtual_volume, target_virtual_volume, t); - - /* The volume changed, let's tell people so */ - if (i->volume_changed) - i->volume_changed(i); - - /* The virtual volume changed, let's tell people so */ - pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); -} - -void pa_sink_input_set_mute_with_ramping(pa_sink_input *i, pa_bool_t mute, pa_bool_t save, pa_usec_t t){ - - pa_sink_input_assert_ref(i); - pa_assert_ctl_context(); - pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); - - if (!i->muted == !mute) { - i->save_muted = i->save_muted || mute; - return; - } - - i->muted = mute; - i->save_muted = save; - - /* Set this flag before the following code modify i->thread_info.muted, otherwise distortion will be heard */ - if (t > 0) - pa_atomic_store(&i->before_ramping_m, 1); - - pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_SOFT_MUTE, NULL, 0, NULL) == 0); - - if (t > 0) - sink_input_set_ramping_info_for_mute(i, mute, t); - - /* The mute status changed, let's tell people so */ - if (i->mute_changed) - i->mute_changed(i); - - pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); -} diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h index f81e2d4b..588005fd 100644 --- a/src/pulsecore/sink-input.h +++ b/src/pulsecore/sink-input.h @@ -35,7 +35,6 @@ typedef struct pa_sink_input pa_sink_input; #include <pulsecore/client.h> #include <pulsecore/sink.h> #include <pulsecore/core.h> -#include <pulsecore/envelope.h> typedef enum pa_sink_input_state { PA_SINK_INPUT_INIT, /*< The stream is not active yet, because pa_sink_input_put() has not been called yet */ @@ -83,7 +82,8 @@ struct pa_sink_input { pa_module *module; /* may be NULL */ pa_client *client; /* may be NULL */ - pa_sink *sink; /* NULL while we are being moved */ + pa_sink *sink; /* NULL while we are being moved */ + pa_sink *origin_sink; /* only set by filter sinks */ /* A sink input may be connected to multiple source outputs * directly, so that they don't get mixed data of the entire @@ -234,23 +234,8 @@ struct pa_sink_input { pa_usec_t requested_sink_latency; pa_hashmap *direct_outputs; - - struct { - pa_bool_t is_ramping:1; - pa_bool_t envelope_dead:1; - int32_t envelope_dying; /* Increasing while envelop is not dead. Reduce it while process_rewind. */ - pa_envelope *envelope; - pa_envelope_item *item; - } ramp_info; - pa_cvolume future_soft_volume; - pa_bool_t future_muted; - } thread_info; - pa_atomic_t before_ramping_v; /* Indicates future volume */ - pa_atomic_t before_ramping_m; /* Indicates future mute */ - pa_envelope_def using_def; - void *userdata; }; @@ -265,7 +250,6 @@ enum { PA_SINK_INPUT_MESSAGE_SET_STATE, PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY, PA_SINK_INPUT_MESSAGE_GET_REQUESTED_LATENCY, - PA_SINK_INPUT_MESSAGE_SET_ENVELOPE, PA_SINK_INPUT_MESSAGE_MAX }; @@ -285,6 +269,7 @@ typedef struct pa_sink_input_new_data { pa_client *client; pa_sink *sink; + pa_sink *origin_sink; pa_resample_method_t resample_method; @@ -310,6 +295,7 @@ typedef struct pa_sink_input_new_data { pa_sink_input_new_data* pa_sink_input_new_data_init(pa_sink_input_new_data *data); void pa_sink_input_new_data_set_sample_spec(pa_sink_input_new_data *data, const pa_sample_spec *spec); void pa_sink_input_new_data_set_channel_map(pa_sink_input_new_data *data, const pa_channel_map *map); +pa_bool_t pa_sink_input_new_data_is_volume_writable(pa_sink_input_new_data *data); void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cvolume *volume); void pa_sink_input_new_data_apply_volume_factor(pa_sink_input_new_data *data, const pa_cvolume *volume_factor); void pa_sink_input_new_data_apply_volume_factor_sink(pa_sink_input_new_data *data, const pa_cvolume *volume_factor); @@ -354,6 +340,8 @@ void pa_sink_input_kill(pa_sink_input*i); pa_usec_t pa_sink_input_get_latency(pa_sink_input *i, pa_usec_t *sink_latency); +pa_bool_t pa_sink_input_is_volume_readable(pa_sink_input *i); +pa_bool_t pa_sink_input_is_volume_writable(pa_sink_input *i); void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_bool_t save, pa_bool_t absolute); pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i, pa_cvolume *volume, pa_bool_t absolute); @@ -402,8 +390,4 @@ pa_memchunk* pa_sink_input_get_silence(pa_sink_input *i, pa_memchunk *ret); #define pa_sink_input_assert_io_context(s) \ pa_assert(pa_thread_mq_get() || !PA_SINK_INPUT_IS_LINKED((s)->state)) -/* Volume ramping*/ -void pa_sink_input_set_volume_with_ramping(pa_sink_input *i, const pa_cvolume *volume, pa_bool_t save, pa_bool_t absolute, pa_usec_t t); -void pa_sink_input_set_mute_with_ramping(pa_sink_input *i, pa_bool_t mute, pa_bool_t save, pa_usec_t t); - #endif diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 62000e0d..839b7d44 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -226,8 +226,14 @@ pa_sink* pa_sink_new( pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map)); pa_return_null_if_fail(data->channel_map.channels == data->sample_spec.channels); - if (!data->volume_is_set) + /* FIXME: There should probably be a general function for checking whether + * the sink volume is allowed to be set, like there is for sink inputs. */ + pa_assert(!data->volume_is_set || !(flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)); + + if (!data->volume_is_set) { pa_cvolume_reset(&data->volume, data->sample_spec.channels); + data->save_volume = FALSE; + } pa_return_null_if_fail(pa_cvolume_valid(&data->volume)); pa_return_null_if_fail(pa_cvolume_compatible(&data->volume, &data->sample_spec)); @@ -269,6 +275,7 @@ pa_sink* pa_sink_new( s->inputs = pa_idxset_new(NULL, NULL); s->n_corked = 0; + s->input_to_master = NULL; s->reference_volume = s->real_volume = data->volume; pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels); @@ -446,6 +453,7 @@ void pa_sink_put(pa_sink* s) { pa_assert_ctl_context(); pa_assert(s->state == PA_SINK_INIT); + pa_assert(!(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER) || s->input_to_master); /* The following fields must be initialized properly when calling _put() */ pa_assert(s->asyncmsgq); @@ -455,22 +463,43 @@ void pa_sink_put(pa_sink* s) { * special exception we allow volume related flags to be set * between _new() and _put(). */ - if (!(s->flags & PA_SINK_HW_VOLUME_CTRL)) + /* XXX: Currently decibel volume is disabled for all sinks that use volume + * sharing. When the master sink supports decibel volume, it would be good + * to have the flag also in the filter sink, but currently we don't do that + * so that the flags of the filter sink never change when it's moved from + * a master sink to another. One solution for this problem would be to + * remove user-visible volume altogether from filter sinks when volume + * sharing is used, but the current approach was easier to implement... */ + if (!(s->flags & PA_SINK_HW_VOLUME_CTRL) && !(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) s->flags |= PA_SINK_DECIBEL_VOLUME; if ((s->flags & PA_SINK_DECIBEL_VOLUME) && s->core->flat_volumes) s->flags |= PA_SINK_FLAT_VOLUME; - /* We assume that if the sink implementor changed the default - * volume he did so in real_volume, because that is the usual - * place where he is supposed to place his changes. */ - s->reference_volume = s->real_volume; + if (s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER) { + pa_sink *root_sink = s->input_to_master->sink; + + while (root_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER) + root_sink = root_sink->input_to_master->sink; + + s->reference_volume = root_sink->reference_volume; + pa_cvolume_remap(&s->reference_volume, &root_sink->channel_map, &s->channel_map); + + s->real_volume = root_sink->real_volume; + pa_cvolume_remap(&s->real_volume, &root_sink->channel_map, &s->channel_map); + } else + /* We assume that if the sink implementor changed the default + * volume he did so in real_volume, because that is the usual + * place where he is supposed to place his changes. */ + s->reference_volume = s->real_volume; s->thread_info.soft_volume = s->soft_volume; s->thread_info.soft_muted = s->muted; pa_sw_cvolume_multiply(&s->thread_info.current_hw_volume, &s->soft_volume, &s->real_volume); - pa_assert((s->flags & PA_SINK_HW_VOLUME_CTRL) || (s->base_volume == PA_VOLUME_NORM && s->flags & PA_SINK_DECIBEL_VOLUME)); + pa_assert((s->flags & PA_SINK_HW_VOLUME_CTRL) + || (s->base_volume == PA_VOLUME_NORM + && ((s->flags & PA_SINK_DECIBEL_VOLUME || (s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER))))); pa_assert(!(s->flags & PA_SINK_DECIBEL_VOLUME) || s->n_volume_steps == PA_VOLUME_NORM+1); pa_assert(!(s->flags & PA_SINK_DYNAMIC_LATENCY) == (s->thread_info.fixed_latency != 0)); pa_assert(!(s->flags & PA_SINK_LATENCY) == !(s->monitor_source->flags & PA_SOURCE_LATENCY)); @@ -1195,47 +1224,61 @@ pa_usec_t pa_sink_get_latency_within_thread(pa_sink *s) { return usec; } -static pa_cvolume* cvolume_remap_minimal_impact( - pa_cvolume *v, - const pa_cvolume *template, - const pa_channel_map *from, - const pa_channel_map *to) { +/* Called from the main thread (and also from the IO thread while the main + * thread is waiting). + * + * When a sink uses volume sharing, it never has the PA_SINK_FLAT_VOLUME flag + * set. Instead, flat volume mode is detected by checking whether the root sink + * has the flag set. */ +pa_bool_t pa_sink_flat_volume_enabled(pa_sink *s) { + pa_sink_assert_ref(s); - pa_cvolume t; + while (s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER) + s = s->input_to_master->sink; - pa_assert(v); - pa_assert(template); - pa_assert(from); - pa_assert(to); + return (s->flags & PA_SINK_FLAT_VOLUME); +} - pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, from), NULL); - pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(template, to), NULL); +/* Called from main context. */ +static void compute_reference_ratio(pa_sink_input *i) { + unsigned c = 0; + pa_cvolume remapped; - /* Much like pa_cvolume_remap(), but tries to minimize impact when - * mapping from sink input to sink volumes: - * - * If template is a possible remapping from v it is used instead - * of remapping anew. + pa_assert(i); + pa_assert(pa_sink_flat_volume_enabled(i->sink)); + + /* + * Calculates the reference ratio from the sink's reference + * volume. This basically calculates: * - * If the channel maps don't match we set an all-channel volume on - * the sink to ensure that changing a volume on one stream has no - * effect that cannot be compensated for in another stream that - * does not have the same channel map as the sink. */ + * i->reference_ratio = i->volume / i->sink->reference_volume + */ - if (pa_channel_map_equal(from, to)) - return v; + remapped = i->sink->reference_volume; + pa_cvolume_remap(&remapped, &i->sink->channel_map, &i->channel_map); - t = *template; - if (pa_cvolume_equal(pa_cvolume_remap(&t, to, from), v)) { - *v = *template; - return v; - } + i->reference_ratio.channels = i->sample_spec.channels; - pa_cvolume_set(v, to->channels, pa_cvolume_max(v)); - return v; + for (c = 0; c < i->sample_spec.channels; c++) { + + /* We don't update when the sink volume is 0 anyway */ + if (remapped.values[c] <= PA_VOLUME_MUTED) + continue; + + /* Don't update the reference ratio unless necessary */ + if (pa_sw_volume_multiply( + i->reference_ratio.values[c], + remapped.values[c]) == i->volume.values[c]) + continue; + + i->reference_ratio.values[c] = pa_sw_volume_divide( + i->volume.values[c], + remapped.values[c]); + } } -/* Called from main context */ +/* Called from main context. Only called for the root sink in volume sharing + * cases, except for internal recursive calls. */ static void compute_reference_ratios(pa_sink *s) { uint32_t idx; pa_sink_input *i; @@ -1243,44 +1286,18 @@ static void compute_reference_ratios(pa_sink *s) { pa_sink_assert_ref(s); pa_assert_ctl_context(); pa_assert(PA_SINK_IS_LINKED(s->state)); - pa_assert(s->flags & PA_SINK_FLAT_VOLUME); + pa_assert(pa_sink_flat_volume_enabled(s)); PA_IDXSET_FOREACH(i, s->inputs, idx) { - unsigned c; - pa_cvolume remapped; - - /* - * Calculates the reference volume from the sink's reference - * volume. This basically calculates: - * - * i->reference_ratio = i->volume / s->reference_volume - */ - - remapped = s->reference_volume; - pa_cvolume_remap(&remapped, &s->channel_map, &i->channel_map); - - i->reference_ratio.channels = i->sample_spec.channels; - - for (c = 0; c < i->sample_spec.channels; c++) { - - /* We don't update when the sink volume is 0 anyway */ - if (remapped.values[c] <= PA_VOLUME_MUTED) - continue; - - /* Don't update the reference ratio unless necessary */ - if (pa_sw_volume_multiply( - i->reference_ratio.values[c], - remapped.values[c]) == i->volume.values[c]) - continue; + compute_reference_ratio(i); - i->reference_ratio.values[c] = pa_sw_volume_divide( - i->volume.values[c], - remapped.values[c]); - } + if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) + compute_reference_ratios(i->origin_sink); } } -/* Called from main context */ +/* Called from main context. Only called for the root sink in volume sharing + * cases, except for internal recursive calls. */ static void compute_real_ratios(pa_sink *s) { pa_sink_input *i; uint32_t idx; @@ -1288,12 +1305,24 @@ static void compute_real_ratios(pa_sink *s) { pa_sink_assert_ref(s); pa_assert_ctl_context(); pa_assert(PA_SINK_IS_LINKED(s->state)); - pa_assert(s->flags & PA_SINK_FLAT_VOLUME); + pa_assert(pa_sink_flat_volume_enabled(s)); PA_IDXSET_FOREACH(i, s->inputs, idx) { unsigned c; pa_cvolume remapped; + if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) { + /* The origin sink uses volume sharing, so this input's real ratio + * is handled as a special case - the real ratio must be 0 dB, and + * as a result i->soft_volume must equal i->volume_factor. */ + pa_cvolume_reset(&i->real_ratio, i->real_ratio.channels); + i->soft_volume = i->volume_factor; + + compute_real_ratios(i->origin_sink); + + continue; + } + /* * This basically calculates: * @@ -1334,23 +1363,144 @@ static void compute_real_ratios(pa_sink *s) { } } -/* Called from main thread */ -static void compute_real_volume(pa_sink *s) { +static pa_cvolume *cvolume_remap_minimal_impact( + pa_cvolume *v, + const pa_cvolume *template, + const pa_channel_map *from, + const pa_channel_map *to) { + + pa_cvolume t; + + pa_assert(v); + pa_assert(template); + pa_assert(from); + pa_assert(to); + pa_assert(pa_cvolume_compatible_with_channel_map(v, from)); + pa_assert(pa_cvolume_compatible_with_channel_map(template, to)); + + /* Much like pa_cvolume_remap(), but tries to minimize impact when + * mapping from sink input to sink volumes: + * + * If template is a possible remapping from v it is used instead + * of remapping anew. + * + * If the channel maps don't match we set an all-channel volume on + * the sink to ensure that changing a volume on one stream has no + * effect that cannot be compensated for in another stream that + * does not have the same channel map as the sink. */ + + if (pa_channel_map_equal(from, to)) + return v; + + t = *template; + if (pa_cvolume_equal(pa_cvolume_remap(&t, to, from), v)) { + *v = *template; + return v; + } + + pa_cvolume_set(v, to->channels, pa_cvolume_max(v)); + return v; +} + +/* Called from main thread. Only called for the root sink in volume sharing + * cases, except for internal recursive calls. */ +static void get_maximum_input_volume(pa_sink *s, pa_cvolume *max_volume, const pa_channel_map *channel_map) { + pa_sink_input *i; + uint32_t idx; + + pa_sink_assert_ref(s); + pa_assert(max_volume); + pa_assert(channel_map); + pa_assert(pa_sink_flat_volume_enabled(s)); + + PA_IDXSET_FOREACH(i, s->inputs, idx) { + pa_cvolume remapped; + + if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) { + get_maximum_input_volume(i->origin_sink, max_volume, channel_map); + + /* Ignore this input. The origin sink uses volume sharing, so this + * input's volume will be set to be equal to the root sink's real + * volume. Obviously this input's current volume must not then + * affect what the root sink's real volume will be. */ + continue; + } + + remapped = i->volume; + cvolume_remap_minimal_impact(&remapped, max_volume, &i->channel_map, channel_map); + pa_cvolume_merge(max_volume, max_volume, &remapped); + } +} + +/* Called from main thread. Only called for the root sink in volume sharing + * cases, except for internal recursive calls. */ +static pa_bool_t has_inputs(pa_sink *s) { pa_sink_input *i; uint32_t idx; pa_sink_assert_ref(s); + + PA_IDXSET_FOREACH(i, s->inputs, idx) { + if (!i->origin_sink || !(i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER) || has_inputs(i->origin_sink)) + return TRUE; + } + + return FALSE; +} + +/* Called from main thread. Only called for the root sink in volume sharing + * cases, except for internal recursive calls. */ +static void update_real_volume(pa_sink *s, const pa_cvolume *new_volume, pa_channel_map *channel_map) { + pa_sink_input *i; + uint32_t idx; + + pa_sink_assert_ref(s); + pa_assert(new_volume); + pa_assert(channel_map); + + s->real_volume = *new_volume; + pa_cvolume_remap(&s->real_volume, channel_map, &s->channel_map); + + PA_IDXSET_FOREACH(i, s->inputs, idx) { + if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) { + if (pa_sink_flat_volume_enabled(s)) { + pa_cvolume old_volume = i->volume; + + /* Follow the root sink's real volume. */ + i->volume = *new_volume; + pa_cvolume_remap(&i->volume, channel_map, &i->channel_map); + compute_reference_ratio(i); + + /* 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); + } + } + + update_real_volume(i->origin_sink, new_volume, channel_map); + } + } +} + +/* Called from main thread. Only called for the root sink in shared volume + * cases. */ +static void compute_real_volume(pa_sink *s) { + pa_sink_assert_ref(s); pa_assert_ctl_context(); pa_assert(PA_SINK_IS_LINKED(s->state)); - pa_assert(s->flags & PA_SINK_FLAT_VOLUME); + pa_assert(pa_sink_flat_volume_enabled(s)); + pa_assert(!(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)); /* This determines the maximum volume of all streams and sets * s->real_volume accordingly. */ - if (pa_idxset_isempty(s->inputs)) { - /* In the special case that we have no sink input we leave the + if (!has_inputs(s)) { + /* In the special case that we have no sink inputs we leave the * volume unmodified. */ - s->real_volume = s->reference_volume; + update_real_volume(s, &s->reference_volume, &s->channel_map); return; } @@ -1358,20 +1508,16 @@ static void compute_real_volume(pa_sink *s) { /* First let's determine the new maximum volume of all inputs * connected to this sink */ - PA_IDXSET_FOREACH(i, s->inputs, idx) { - pa_cvolume remapped; - - remapped = i->volume; - cvolume_remap_minimal_impact(&remapped, &s->real_volume, &i->channel_map, &s->channel_map); - pa_cvolume_merge(&s->real_volume, &s->real_volume, &remapped); - } + get_maximum_input_volume(s, &s->real_volume, &s->channel_map); + update_real_volume(s, &s->real_volume, &s->channel_map); /* Then, let's update the real ratios/soft volumes of all inputs * connected to this sink */ compute_real_ratios(s); } -/* Called from main thread */ +/* Called from main thread. Only called for the root sink in shared volume + * cases, except for internal recursive calls. */ static void propagate_reference_volume(pa_sink *s) { pa_sink_input *i; uint32_t idx; @@ -1379,14 +1525,23 @@ static void propagate_reference_volume(pa_sink *s) { pa_sink_assert_ref(s); pa_assert_ctl_context(); pa_assert(PA_SINK_IS_LINKED(s->state)); - pa_assert(s->flags & PA_SINK_FLAT_VOLUME); + pa_assert(pa_sink_flat_volume_enabled(s)); /* This is called whenever the sink volume changes that is not * caused by a sink input volume change. We need to fix up the * sink input volumes accordingly */ PA_IDXSET_FOREACH(i, s->inputs, idx) { - pa_cvolume old_volume, remapped; + pa_cvolume old_volume; + + if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) { + propagate_reference_volume(i->origin_sink); + + /* Since the origin sink uses volume sharing, this input's volume + * needs to be updated to match the root sink's real volume, but + * that will be done later in update_shared_real_volume(). */ + continue; + } old_volume = i->volume; @@ -1394,9 +1549,9 @@ static void propagate_reference_volume(pa_sink *s) { * * i->volume := s->reference_volume * i->reference_ratio */ - remapped = s->reference_volume; - pa_cvolume_remap(&remapped, &s->channel_map, &i->channel_map); - pa_sw_cvolume_multiply(&i->volume, &remapped, &i->reference_ratio); + i->volume = s->reference_volume; + pa_cvolume_remap(&i->volume, &s->channel_map, &i->channel_map); + pa_sw_cvolume_multiply(&i->volume, &i->volume, &i->reference_ratio); /* The volume changed, let's tell people so */ if (!pa_cvolume_equal(&old_volume, &i->volume)) { @@ -1409,6 +1564,54 @@ static void propagate_reference_volume(pa_sink *s) { } } +/* Called from main thread. Only called for the root sink in volume sharing + * cases, except for internal recursive calls. The return value indicates + * whether any reference volume actually changed. */ +static pa_bool_t update_reference_volume(pa_sink *s, const pa_cvolume *v, const pa_channel_map *channel_map, pa_bool_t save) { + pa_cvolume volume; + pa_bool_t reference_volume_changed; + pa_sink_input *i; + uint32_t idx; + + pa_sink_assert_ref(s); + pa_assert(PA_SINK_IS_LINKED(s->state)); + pa_assert(v); + pa_assert(channel_map); + pa_assert(pa_cvolume_valid(v)); + + volume = *v; + pa_cvolume_remap(&volume, channel_map, &s->channel_map); + + reference_volume_changed = !pa_cvolume_equal(&volume, &s->reference_volume); + s->reference_volume = volume; + + s->save_volume = (!reference_volume_changed && s->save_volume) || save; + + if (reference_volume_changed) + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); + else if (!(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) + /* If the root sink's volume doesn't change, then there can't be any + * changes in the other sinks in the sink tree either. + * + * It's probably theoretically possible that even if the root sink's + * volume changes slightly, some filter sink doesn't change its volume + * due to rounding errors. If that happens, we still want to propagate + * the changed root sink volume to the sinks connected to the + * intermediate sink that didn't change its volume. This theoretical + * possiblity is the reason why we have that !(s->flags & + * PA_SINK_SHARE_VOLUME_WITH_MASTER) condition. Probably nobody would + * notice even if we returned here FALSE always if + * reference_volume_changed is FALSE. */ + return FALSE; + + PA_IDXSET_FOREACH(i, s->inputs, idx) { + if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) + update_reference_volume(i->origin_sink, v, channel_map, FALSE); + } + + return TRUE; +} + /* Called from main thread */ void pa_sink_set_volume( pa_sink *s, @@ -1416,14 +1619,14 @@ void pa_sink_set_volume( pa_bool_t send_msg, pa_bool_t save) { - pa_cvolume old_reference_volume; - pa_bool_t reference_changed; + pa_cvolume new_reference_volume; + pa_sink *root_sink = s; pa_sink_assert_ref(s); pa_assert_ctl_context(); pa_assert(PA_SINK_IS_LINKED(s->state)); pa_assert(!volume || pa_cvolume_valid(volume)); - pa_assert(volume || (s->flags & PA_SINK_FLAT_VOLUME)); + pa_assert(volume || pa_sink_flat_volume_enabled(s)); pa_assert(!volume || volume->channels == 1 || pa_cvolume_compatible(volume, &s->sample_spec)); /* make sure we don't change the volume when a PASSTHROUGH input is connected */ @@ -1444,76 +1647,80 @@ void pa_sink_set_volume( } } + /* In case of volume sharing, the volume is set for the root sink first, + * from which it's then propagated to the sharing sinks. */ + while (root_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER) + root_sink = root_sink->input_to_master->sink; + /* As a special exception we accept mono volumes on all sinks -- * even on those with more complex channel maps */ + if (volume) { + if (pa_cvolume_compatible(volume, &s->sample_spec)) + new_reference_volume = *volume; + else { + new_reference_volume = s->reference_volume; + pa_cvolume_scale(&new_reference_volume, pa_cvolume_max(volume)); + } + + pa_cvolume_remap(&new_reference_volume, &s->channel_map, &root_sink->channel_map); + } + /* If volume is NULL we synchronize the sink's real and reference * volumes with the stream volumes. If it is not NULL we update * the reference_volume with it. */ - old_reference_volume = s->reference_volume; - if (volume) { - - if (pa_cvolume_compatible(volume, &s->sample_spec)) - s->reference_volume = *volume; - else - pa_cvolume_scale(&s->reference_volume, pa_cvolume_max(volume)); - - if (s->flags & PA_SINK_FLAT_VOLUME) { - /* OK, propagate this volume change back to the inputs */ - propagate_reference_volume(s); - - /* And now recalculate the real volume */ - compute_real_volume(s); - } else - s->real_volume = s->reference_volume; + if (update_reference_volume(root_sink, &new_reference_volume, &root_sink->channel_map, save)) { + if (pa_sink_flat_volume_enabled(root_sink)) { + /* OK, propagate this volume change back to the inputs */ + propagate_reference_volume(root_sink); + + /* And now recalculate the real volume */ + compute_real_volume(root_sink); + } else + update_real_volume(root_sink, &root_sink->reference_volume, &root_sink->channel_map); + } } else { - pa_assert(s->flags & PA_SINK_FLAT_VOLUME); + pa_assert(pa_sink_flat_volume_enabled(root_sink)); /* Ok, let's determine the new real volume */ - compute_real_volume(s); + compute_real_volume(root_sink); /* Let's 'push' the reference volume if necessary */ - pa_cvolume_merge(&s->reference_volume, &s->reference_volume, &s->real_volume); + pa_cvolume_merge(&new_reference_volume, &s->reference_volume, &root_sink->real_volume); + update_reference_volume(root_sink, &new_reference_volume, &root_sink->channel_map, save); - /* We need to fix the reference ratios of all streams now that - * we changed the reference volume */ - compute_reference_ratios(s); + /* Now that the reference volume is updated, we can update the streams' + * reference ratios. */ + compute_reference_ratios(root_sink); } - reference_changed = !pa_cvolume_equal(&old_reference_volume, &s->reference_volume); - s->save_volume = (!reference_changed && s->save_volume) || save; - - if (s->set_volume) { + if (root_sink->set_volume) { /* If we have a function set_volume(), then we do not apply a * soft volume by default. However, set_volume() is free to - * apply one to s->soft_volume */ + * apply one to root_sink->soft_volume */ - pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels); - if (!(s->flags & PA_SINK_SYNC_VOLUME)) - s->set_volume(s); - else - send_msg = TRUE; + pa_cvolume_reset(&root_sink->soft_volume, root_sink->sample_spec.channels); + if (!(root_sink->flags & PA_SINK_SYNC_VOLUME)) + root_sink->set_volume(root_sink); } else /* If we have no function set_volume(), then the soft volume - * becomes the virtual volume */ - s->soft_volume = s->real_volume; + * becomes the real volume */ + root_sink->soft_volume = root_sink->real_volume; - /* This tells the sink that soft and/or virtual volume changed */ + /* This tells the sink that soft volume and/or real volume changed */ if (send_msg) - pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME_SYNCED, NULL, 0, NULL) == 0); - - if (reference_changed) - pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); + pa_assert_se(pa_asyncmsgq_send(root_sink->asyncmsgq, PA_MSGOBJECT(root_sink), PA_SINK_MESSAGE_SET_SHARED_VOLUME, NULL, 0, NULL) == 0); } /* Called from the io thread if sync volume is used, otherwise from the main thread. * Only to be called by sink implementor */ void pa_sink_set_soft_volume(pa_sink *s, const pa_cvolume *volume) { pa_sink_assert_ref(s); + pa_assert(!(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)); if (s->flags & PA_SINK_SYNC_VOLUME) pa_sink_assert_io_context(s); else @@ -1530,12 +1737,14 @@ void pa_sink_set_soft_volume(pa_sink *s, const pa_cvolume *volume) { s->thread_info.soft_volume = s->soft_volume; } +/* Called from the main thread. Only called for the root sink in volume sharing + * cases, except for internal recursive calls. */ static void propagate_real_volume(pa_sink *s, const pa_cvolume *old_real_volume) { pa_sink_input *i; uint32_t idx; - pa_cvolume old_reference_volume; pa_sink_assert_ref(s); + pa_assert(old_real_volume); pa_assert_ctl_context(); pa_assert(PA_SINK_IS_LINKED(s->state)); @@ -1544,20 +1753,18 @@ static void propagate_real_volume(pa_sink *s, const pa_cvolume *old_real_volume) * reference volume and then rebuild the stream volumes based on * i->real_ratio which should stay fixed. */ - if (old_real_volume && pa_cvolume_equal(old_real_volume, &s->real_volume)) - return; - - old_reference_volume = s->reference_volume; + if (!(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) { + if (pa_cvolume_equal(old_real_volume, &s->real_volume)) + return; - /* 1. Make the real volume the reference volume */ - s->reference_volume = s->real_volume; + /* 1. Make the real volume the reference volume */ + update_reference_volume(s, &s->real_volume, &s->channel_map, TRUE); + } - if (s->flags & PA_SINK_FLAT_VOLUME) { + if (pa_sink_flat_volume_enabled(s)) { PA_IDXSET_FOREACH(i, s->inputs, idx) { - pa_cvolume old_volume, remapped; - - old_volume = i->volume; + pa_cvolume old_volume = i->volume; /* 2. Since the sink's reference and real volumes are equal * now our ratios should be too. */ @@ -1571,9 +1778,9 @@ static void propagate_real_volume(pa_sink *s, const pa_cvolume *old_real_volume) * i->volume = s->reference_volume * i->reference_ratio * * This is identical to propagate_reference_volume() */ - remapped = s->reference_volume; - pa_cvolume_remap(&remapped, &s->channel_map, &i->channel_map); - pa_sw_cvolume_multiply(&i->volume, &remapped, &i->reference_ratio); + i->volume = s->reference_volume; + pa_cvolume_remap(&i->volume, &s->channel_map, &i->channel_map); + pa_sw_cvolume_multiply(&i->volume, &i->volume, &i->reference_ratio); /* Notify if something changed */ if (!pa_cvolume_equal(&old_volume, &i->volume)) { @@ -1583,16 +1790,17 @@ static void propagate_real_volume(pa_sink *s, const pa_cvolume *old_real_volume) pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); } + + if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) + propagate_real_volume(i->origin_sink, old_real_volume); } } /* Something got changed in the hardware. It probably makes sense * to save changed hw settings given that hw volume changes not * triggered by PA are almost certainly done by the user. */ - s->save_volume = TRUE; - - if (!pa_cvolume_equal(&old_reference_volume, &s->reference_volume)) - pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); + if (!(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) + s->save_volume = TRUE; } /* Called from io thread */ @@ -1612,6 +1820,8 @@ const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_bool_t force_refresh) { if (s->refresh_volume || force_refresh) { struct pa_cvolume old_real_volume; + pa_assert(!(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)); + old_real_volume = s->real_volume; if (!(s->flags & PA_SINK_SYNC_VOLUME) && s->get_volume) @@ -1619,25 +1829,27 @@ const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_bool_t force_refresh) { pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_VOLUME, NULL, 0, NULL) == 0); + update_real_volume(s, &s->real_volume, &s->channel_map); propagate_real_volume(s, &old_real_volume); } return &s->reference_volume; } -/* Called from main thread */ +/* Called from main thread. In volume sharing cases, only the root sink may + * call this. */ void pa_sink_volume_changed(pa_sink *s, const pa_cvolume *new_real_volume) { pa_cvolume old_real_volume; pa_sink_assert_ref(s); pa_assert_ctl_context(); pa_assert(PA_SINK_IS_LINKED(s->state)); + pa_assert(!(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)); /* The sink implementor may call this if the volume changed to make sure everyone is notified */ old_real_volume = s->real_volume; - s->real_volume = *new_real_volume; - + update_real_volume(s, new_real_volume, &s->channel_map); propagate_real_volume(s, &old_real_volume); } @@ -1844,18 +2056,30 @@ static void sync_input_volumes_within_thread(pa_sink *s) { pa_sink_assert_io_context(s); PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state) { - if (pa_atomic_load(&i->before_ramping_v)) - i->thread_info.future_soft_volume = i->soft_volume; - if (pa_cvolume_equal(&i->thread_info.soft_volume, &i->soft_volume)) continue; - if (!pa_atomic_load(&i->before_ramping_v)) - i->thread_info.soft_volume = i->soft_volume; + i->thread_info.soft_volume = i->soft_volume; pa_sink_input_request_rewind(i, 0, TRUE, FALSE, FALSE); } } +/* Called from the IO thread. Only called for the root sink in volume sharing + * cases, except for internal recursive calls. */ +static void set_shared_volume_within_thread(pa_sink *s) { + pa_sink_input *i = NULL; + void *state = NULL; + + pa_sink_assert_ref(s); + + PA_MSGOBJECT(s)->process_msg(PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME_SYNCED, NULL, 0, NULL); + + PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state) { + if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) + set_shared_volume_within_thread(i->origin_sink); + } +} + /* Called from IO thread, except when it is not */ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) { pa_sink *s = PA_SINK(o); @@ -1912,7 +2136,7 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse /* In flat volume mode we need to update the volume as * well */ - return o->process_msg(o, PA_SINK_MESSAGE_SET_VOLUME_SYNCED, NULL, 0, NULL); + return o->process_msg(o, PA_SINK_MESSAGE_SET_SHARED_VOLUME, NULL, 0, NULL); } case PA_SINK_MESSAGE_REMOVE_INPUT: { @@ -1955,7 +2179,7 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse /* In flat volume mode we need to update the volume as * well */ - return o->process_msg(o, PA_SINK_MESSAGE_SET_VOLUME_SYNCED, NULL, 0, NULL); + return o->process_msg(o, PA_SINK_MESSAGE_SET_SHARED_VOLUME, NULL, 0, NULL); } case PA_SINK_MESSAGE_START_MOVE: { @@ -2000,7 +2224,7 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse /* In flat volume mode we need to update the volume as * well */ - return o->process_msg(o, PA_SINK_MESSAGE_SET_VOLUME_SYNCED, NULL, 0, NULL); + return o->process_msg(o, PA_SINK_MESSAGE_SET_SHARED_VOLUME, NULL, 0, NULL); } case PA_SINK_MESSAGE_FINISH_MOVE: { @@ -2041,9 +2265,17 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse pa_sink_request_rewind(s, nbytes); } - /* In flat volume mode we need to update the volume as - * well */ - return o->process_msg(o, PA_SINK_MESSAGE_SET_VOLUME_SYNCED, NULL, 0, NULL); + return o->process_msg(o, PA_SINK_MESSAGE_SET_SHARED_VOLUME, NULL, 0, NULL); + } + + case PA_SINK_MESSAGE_SET_SHARED_VOLUME: { + pa_sink *root_sink = s; + + while (root_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER) + root_sink = root_sink->input_to_master->sink; + + set_shared_volume_within_thread(root_sink); + return 0; } case PA_SINK_MESSAGE_SET_VOLUME_SYNCED: @@ -2061,9 +2293,6 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse pa_sink_request_rewind(s, (size_t) -1); } - if (!(s->flags & PA_SINK_FLAT_VOLUME)) - return 0; - /* Fall through ... */ case PA_SINK_MESSAGE_SYNC_VOLUMES: @@ -2503,22 +2732,22 @@ void pa_sink_set_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_ /* Called from main thread */ void pa_sink_get_latency_range(pa_sink *s, pa_usec_t *min_latency, pa_usec_t *max_latency) { - pa_sink_assert_ref(s); - pa_assert_ctl_context(); - pa_assert(min_latency); - pa_assert(max_latency); + pa_sink_assert_ref(s); + pa_assert_ctl_context(); + pa_assert(min_latency); + pa_assert(max_latency); - if (PA_SINK_IS_LINKED(s->state)) { - pa_usec_t r[2] = { 0, 0 }; + if (PA_SINK_IS_LINKED(s->state)) { + pa_usec_t r[2] = { 0, 0 }; - pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY_RANGE, r, 0, NULL) == 0); + pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY_RANGE, r, 0, NULL) == 0); - *min_latency = r[0]; - *max_latency = r[1]; - } else { - *min_latency = s->thread_info.min_latency; - *max_latency = s->thread_info.max_latency; - } + *min_latency = r[0]; + *max_latency = r[1]; + } else { + *min_latency = s->thread_info.min_latency; + *max_latency = s->thread_info.max_latency; + } } /* Called from IO thread */ @@ -2699,6 +2928,8 @@ int pa_sink_set_port(pa_sink *s, const char *name, pa_bool_t save) { s->active_port = port; s->save_port = save; + pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PORT_CHANGED], s); + return 0; } @@ -2942,11 +3173,11 @@ void pa_sink_volume_change_push(pa_sink *s) { PA_LLIST_INSERT_AFTER(pa_sink_volume_change, s->thread_info.volume_changes, c, nc); } - pa_log_debug("Volume going %s to %d at %llu", direction, pa_cvolume_avg(&nc->hw_volume), nc->at); + pa_log_debug("Volume going %s to %d at %llu", direction, pa_cvolume_avg(&nc->hw_volume), (long long unsigned) nc->at); /* We can ignore volume events that came earlier but should happen later than this. */ PA_LLIST_FOREACH(c, nc->next) { - pa_log_debug("Volume change to %d at %llu was dropped", pa_cvolume_avg(&c->hw_volume), c->at); + pa_log_debug("Volume change to %d at %llu was dropped", pa_cvolume_avg(&c->hw_volume), (long long unsigned) c->at); pa_sink_volume_change_free(c); } nc->next = NULL; @@ -2977,7 +3208,8 @@ pa_bool_t pa_sink_volume_change_apply(pa_sink *s, pa_usec_t *usec_to_next) { while (s->thread_info.volume_changes && now >= s->thread_info.volume_changes->at) { pa_sink_volume_change *c = s->thread_info.volume_changes; PA_LLIST_REMOVE(pa_sink_volume_change, s->thread_info.volume_changes, c); - pa_log_debug("Volume change to %d at %llu was written %llu usec late", pa_cvolume_avg(&c->hw_volume), c->at, now - c->at); + pa_log_debug("Volume change to %d at %llu was written %llu usec late", + pa_cvolume_avg(&c->hw_volume), (long long unsigned) c->at, (long long unsigned) (now - c->at)); ret = TRUE; s->thread_info.current_hw_volume = c->hw_volume; pa_sink_volume_change_free(c); @@ -2990,7 +3222,7 @@ pa_bool_t pa_sink_volume_change_apply(pa_sink *s, pa_usec_t *usec_to_next) { if (usec_to_next) *usec_to_next = s->thread_info.volume_changes->at - now; if (pa_log_ratelimit(PA_LOG_DEBUG)) - pa_log_debug("Next volume change in %lld usec", s->thread_info.volume_changes->at - now); + pa_log_debug("Next volume change in %lld usec", (long long) (s->thread_info.volume_changes->at - now)); } else { if (usec_to_next) @@ -3008,7 +3240,7 @@ static void pa_sink_volume_change_rewind(pa_sink *s, size_t nbytes) { pa_usec_t rewound = pa_bytes_to_usec(nbytes, &s->sample_spec); pa_usec_t limit = pa_sink_get_latency_within_thread(s); - pa_log_debug("latency = %lld", limit); + pa_log_debug("latency = %lld", (long long) limit); limit += pa_rtclock_now() + s->thread_info.volume_change_extra_delay; PA_LLIST_FOREACH(c, s->thread_info.volume_changes) { diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h index 4d569ddc..ef698813 100644 --- a/src/pulsecore/sink.h +++ b/src/pulsecore/sink.h @@ -44,6 +44,7 @@ typedef struct pa_sink_volume_change pa_sink_volume_change; #include <pulsecore/card.h> #include <pulsecore/queue.h> #include <pulsecore/thread-mq.h> +#include <pulsecore/sink-input.h> #define PA_MAX_INPUTS_PER_SINK 32 @@ -86,6 +87,7 @@ struct pa_sink { pa_idxset *inputs; unsigned n_corked; pa_source *monitor_source; + pa_sink_input *input_to_master; /* non-NULL only for filter sinks */ pa_volume_t base_volume; /* shall be constant */ unsigned n_volume_steps; /* shall be constant */ @@ -262,6 +264,7 @@ typedef enum pa_sink_message { PA_SINK_MESSAGE_ADD_INPUT, PA_SINK_MESSAGE_REMOVE_INPUT, PA_SINK_MESSAGE_GET_VOLUME, + PA_SINK_MESSAGE_SET_SHARED_VOLUME, PA_SINK_MESSAGE_SET_VOLUME_SYNCED, PA_SINK_MESSAGE_SET_VOLUME, PA_SINK_MESSAGE_SYNC_VOLUMES, @@ -372,6 +375,9 @@ int pa_sink_update_status(pa_sink*s); int pa_sink_suspend(pa_sink *s, pa_bool_t suspend, pa_suspend_cause_t cause); int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend, pa_suspend_cause_t cause); +/* Use this instead of checking s->flags & PA_SINK_FLAT_VOLUME directly. */ +pa_bool_t pa_sink_flat_volume_enabled(pa_sink *s); + void pa_sink_set_volume(pa_sink *sink, const pa_cvolume *volume, pa_bool_t sendmsg, pa_bool_t save); const pa_cvolume *pa_sink_get_volume(pa_sink *sink, pa_bool_t force_refresh); diff --git a/src/pulsecore/sndfile-util.c b/src/pulsecore/sndfile-util.c index 9d15a868..292eb6e8 100644 --- a/src/pulsecore/sndfile-util.c +++ b/src/pulsecore/sndfile-util.c @@ -51,6 +51,9 @@ int pa_sndfile_read_sample_spec(SNDFILE *sf, pa_sample_spec *ss) { break; case SF_FORMAT_PCM_24: + ss->format = PA_SAMPLE_S24NE; + break; + case SF_FORMAT_PCM_32: ss->format = PA_SAMPLE_S32NE; break; @@ -106,10 +109,14 @@ int pa_sndfile_write_sample_spec(SF_INFO *sfi, pa_sample_spec *ss) { case PA_SAMPLE_S24LE: case PA_SAMPLE_S24BE: + ss->format = PA_SAMPLE_S24NE; + sfi->format |= SF_FORMAT_PCM_24; + break; + case PA_SAMPLE_S24_32LE: case PA_SAMPLE_S24_32BE: - ss->format = PA_SAMPLE_S32NE; - sfi->format |= SF_FORMAT_PCM_24; + ss->format = PA_SAMPLE_S24_32NE; + sfi->format |= SF_FORMAT_PCM_32; break; case PA_SAMPLE_S32LE: @@ -297,8 +304,7 @@ int pa_sndfile_write_channel_map(SNDFILE *sf, pa_channel_map *cm) { channels[c] = table[cm->map[c]]; } - if (!sf_command(sf, SFC_SET_CHANNEL_MAP_INFO, - channels, sizeof(channels[0]) * cm->channels)) { + if (!sf_command(sf, SFC_SET_CHANNEL_MAP_INFO, channels, sizeof(channels[0]) * cm->channels)) { pa_xfree(channels); return -1; } @@ -362,6 +368,7 @@ pa_sndfile_readf_t pa_sndfile_readf_function(const pa_sample_spec *ss) { return (pa_sndfile_readf_t) sf_readf_short; case PA_SAMPLE_S32NE: + case PA_SAMPLE_S24_32NE: return (pa_sndfile_readf_t) sf_readf_int; case PA_SAMPLE_FLOAT32NE: @@ -384,6 +391,7 @@ pa_sndfile_writef_t pa_sndfile_writef_function(const pa_sample_spec *ss) { return (pa_sndfile_writef_t) sf_writef_short; case PA_SAMPLE_S32NE: + case PA_SAMPLE_S24_32NE: return (pa_sndfile_writef_t) sf_writef_int; case PA_SAMPLE_FLOAT32NE: diff --git a/src/pulsecore/sound-file-stream.c b/src/pulsecore/sound-file-stream.c index 4037dca8..1ec19425 100644 --- a/src/pulsecore/sound-file-stream.c +++ b/src/pulsecore/sound-file-stream.c @@ -200,7 +200,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk } return -1; - } +} static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { file_stream *u; diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c index 88731e76..0bb8899c 100644 --- a/src/pulsecore/source-output.c +++ b/src/pulsecore/source-output.c @@ -208,6 +208,7 @@ int pa_source_output_new( o->driver = pa_xstrdup(pa_path_get_filename(data->driver)); o->module = data->module; o->source = data->source; + o->destination_source = data->destination_source; o->client = data->client; o->actual_resample_method = resampler ? pa_resampler_get_method(resampler) : PA_RESAMPLER_INVALID; diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h index 273b78fc..f16f9520 100644 --- a/src/pulsecore/source-output.h +++ b/src/pulsecore/source-output.h @@ -74,7 +74,8 @@ struct pa_source_output { pa_module *module; /* may be NULL */ pa_client *client; /* may be NULL */ - pa_source *source; /* NULL while being moved */ + pa_source *source; /* NULL while being moved */ + pa_source *destination_source; /* only set by filter sources */ /* A source output can monitor just a single input of a sink, in which case we find it here */ pa_sink_input *direct_on_input; /* may be NULL */ @@ -211,6 +212,7 @@ typedef struct pa_source_output_new_data { pa_client *client; pa_source *source; + pa_source *destination_source; pa_resample_method_t resample_method; diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c index 412a3db9..92fb80e0 100644 --- a/src/pulsecore/source.c +++ b/src/pulsecore/source.c @@ -221,6 +221,7 @@ pa_source* pa_source_new( s->outputs = pa_idxset_new(NULL, NULL); s->n_corked = 0; s->monitor_of = NULL; + s->output_from_master = NULL; s->volume = data->volume; pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels); @@ -1401,22 +1402,22 @@ void pa_source_set_latency_range(pa_source *s, pa_usec_t min_latency, pa_usec_t /* Called from main thread */ void pa_source_get_latency_range(pa_source *s, pa_usec_t *min_latency, pa_usec_t *max_latency) { - pa_source_assert_ref(s); - pa_assert_ctl_context(); - pa_assert(min_latency); - pa_assert(max_latency); - - if (PA_SOURCE_IS_LINKED(s->state)) { - pa_usec_t r[2] = { 0, 0 }; - - pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_LATENCY_RANGE, r, 0, NULL) == 0); - - *min_latency = r[0]; - *max_latency = r[1]; - } else { - *min_latency = s->thread_info.min_latency; - *max_latency = s->thread_info.max_latency; - } + pa_source_assert_ref(s); + pa_assert_ctl_context(); + pa_assert(min_latency); + pa_assert(max_latency); + + if (PA_SOURCE_IS_LINKED(s->state)) { + pa_usec_t r[2] = { 0, 0 }; + + pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_LATENCY_RANGE, r, 0, NULL) == 0); + + *min_latency = r[0]; + *max_latency = r[1]; + } else { + *min_latency = s->thread_info.min_latency; + *max_latency = s->thread_info.max_latency; + } } /* Called from IO thread, and from main thread before pa_source_put() is called */ @@ -1570,5 +1571,7 @@ int pa_source_set_port(pa_source *s, const char *name, pa_bool_t save) { s->active_port = port; s->save_port = save; + pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_PORT_CHANGED], s); + return 0; } diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h index e3e56bc4..9e8e2ada 100644 --- a/src/pulsecore/source.h +++ b/src/pulsecore/source.h @@ -75,6 +75,7 @@ struct pa_source { pa_idxset *outputs; unsigned n_corked; pa_sink *monitor_of; /* may be NULL */ + pa_source_output *output_from_master; /* non-NULL only for filter sources */ pa_volume_t base_volume; /* shall be constant */ unsigned n_volume_steps; /* shall be constant */ @@ -155,7 +156,7 @@ struct pa_source { pa_usec_t max_latency; /* An upper limit for the latencies */ pa_usec_t fixed_latency; /* for sources with PA_SOURCE_DYNAMIC_LATENCY this is 0 */ - } thread_info; +} thread_info; void *userdata; }; diff --git a/src/pulsecore/strbuf.c b/src/pulsecore/strbuf.c index 4fc82ded..f131d5cd 100644 --- a/src/pulsecore/strbuf.c +++ b/src/pulsecore/strbuf.c @@ -146,7 +146,7 @@ void pa_strbuf_putsn(pa_strbuf *sb, const char *t, size_t l) { pa_assert(t); if (!l) - return; + return; c = pa_xmalloc(PA_ALIGN(sizeof(struct chunk)) + l); c->length = l; diff --git a/src/pulsecore/strlist.c b/src/pulsecore/strlist.c index 0f4ca867..b2ba12ba 100644 --- a/src/pulsecore/strlist.c +++ b/src/pulsecore/strlist.c @@ -49,7 +49,7 @@ pa_strlist* pa_strlist_prepend(pa_strlist *l, const char *s) { memcpy(ITEM_TO_TEXT(n), s, size + 1); n->next = l; - return n; + return n; } char *pa_strlist_tostring(pa_strlist *l) { diff --git a/src/pulsecore/svolume.orc b/src/pulsecore/svolume.orc new file mode 100644 index 00000000..3411161c --- /dev/null +++ b/src/pulsecore/svolume.orc @@ -0,0 +1,84 @@ +# This file is part of PulseAudio. +# +# Copyright 2010 Lennart Poettering +# Copyright 2010 Wim Taymans <wim.taymans@collabora.co.uk> +# Copyright 2010 Arun Raghavan <arun.raghavan@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. + +# S16NE 1- and 2-channel volume scaling work as follows: +# +# params: samples s (signed 16-bit), volume v (signed 32-bit < 2^31) +# +# 32 16 0 (type of operation) +# sample = | sample | (signed) +# s = | 0 | sample | (unsigned) +# +# if (sample < 0) +# signc = | 0 | 0xffff | (unsigned) +# else +# signc = | 0 | 0 | (unsgined) +# +# if (sample < 0) +# ml = | 0 | -((s*vl) >> 16) | (unsgined) +# else +# ml = | 0 | (s*vl) >> 16 | (unsgined) +# +# vh = | v >> 16 | (signed, but value is always signed +# since PA_VOLUME_MAX is 0x0fffffff) +# mh = | (s * vh) >> 16 | (signed) +# ml = | ml + mh | (signed) +# sample = | (ml >> 16) | (signed, saturated) + +.function pa_volume_s16ne_orc_1ch +.dest 2 samples int16_t +.param 4 v int32_t +.temp 2 vh +.temp 4 s +.temp 4 mh +.temp 4 ml +.temp 4 signc + +convuwl s, samples +x2 cmpgtsw signc, 0, s +x2 andw signc, signc, v +x2 mulhuw ml, s, v +subl ml, ml, signc +convhlw vh, v +mulswl mh, samples, vh +addl ml, ml, mh +convssslw samples, ml + +.function pa_volume_s16ne_orc_2ch +.dest 4 samples int16_t +.longparam 8 vols +.temp 8 v +.temp 4 vh +.temp 8 s +.temp 8 mh +.temp 8 ml +.temp 8 signc + +loadpq v, vols +x2 convuwl s, samples +x4 cmpgtsw signc, 0, s +x4 andw signc, signc, v +x4 mulhuw ml, s, v +x2 subl ml, ml, signc +x2 convhlw vh, v +x2 mulswl mh, samples, vh +x2 addl ml, ml, mh +x2 convssslw samples, ml diff --git a/src/pulsecore/svolume_arm.c b/src/pulsecore/svolume_arm.c index 3973e518..42e8cbf9 100644 --- a/src/pulsecore/svolume_arm.c +++ b/src/pulsecore/svolume_arm.c @@ -29,22 +29,21 @@ #include <pulsecore/macro.h> #include <pulsecore/g711.h> #include <pulsecore/core-util.h> +#include <pulsecore/endianmacros.h> #include "cpu-arm.h" #include "sample-util.h" -#include "endianmacros.h" #if defined (__arm__) && defined (HAVE_ARMV6) #define MOD_INC() \ " subs r0, r6, %2 \n\t" \ + " itt cs \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) -{ +static void pa_volume_s16ne_arm(int16_t *samples, int32_t *volumes, unsigned channels, unsigned length) { int32_t *ve; /* Channels must be at least 4, and always a multiple of the original number. @@ -129,11 +128,11 @@ pa_volume_s16ne_arm (int16_t *samples, int32_t *volumes, unsigned channels, unsi #ifdef RUN_TEST #define CHANNELS 2 -#define SAMPLES 1023 +#define SAMPLES 1022 #define TIMES 1000 #define PADDING 16 -static void run_test (void) { +static void run_test(void) { int16_t samples[SAMPLES]; int16_t samples_ref[SAMPLES]; int16_t samples_orig[SAMPLES]; @@ -142,21 +141,21 @@ static void run_test (void) { pa_do_volume_func_t func; pa_usec_t start, stop; - func = pa_get_volume_func (PA_SAMPLE_S16NE); + func = pa_get_volume_func(PA_SAMPLE_S16NE); - printf ("checking ARM %zd\n", sizeof (samples)); + printf("checking ARM %zd\n", sizeof(samples)); - pa_random (samples, sizeof (samples)); - memcpy (samples_ref, samples, sizeof (samples)); - memcpy (samples_orig, samples, 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; + volumes[i] = PA_CLAMP_VOLUME(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)); + 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], @@ -166,16 +165,16 @@ static void run_test (void) { 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)); + 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)); + 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)); @@ -185,14 +184,14 @@ static void run_test (void) { #endif /* defined (__arm__) && defined (HAVE_ARMV6) */ -void pa_volume_func_init_arm (pa_cpu_arm_flag_t flags) { +void pa_volume_func_init_arm(pa_cpu_arm_flag_t flags) { #if defined (__arm__) && defined (HAVE_ARMV6) pa_log_info("Initialising ARM optimized functions."); #ifdef RUN_TEST - run_test (); + run_test(); #endif - pa_set_volume_func (PA_SAMPLE_S16NE, (pa_do_volume_func_t) pa_volume_s16ne_arm); + pa_set_volume_func(PA_SAMPLE_S16NE, (pa_do_volume_func_t) pa_volume_s16ne_arm); #endif /* defined (__arm__) && defined (HAVE_ARMV6) */ } diff --git a/src/pulsecore/svolume_c.c b/src/pulsecore/svolume_c.c index 5fc052b8..e55d0d7b 100644 --- a/src/pulsecore/svolume_c.c +++ b/src/pulsecore/svolume_c.c @@ -28,13 +28,11 @@ #include <pulsecore/macro.h> #include <pulsecore/g711.h> #include <pulsecore/core-util.h> +#include <pulsecore/endianmacros.h> #include "sample-util.h" -#include "endianmacros.h" -static void -pa_volume_u8_c (uint8_t *samples, int32_t *volumes, unsigned channels, unsigned length) -{ +static void pa_volume_u8_c(uint8_t *samples, int32_t *volumes, unsigned channels, unsigned length) { unsigned channel; for (channel = 0; length; length--) { @@ -53,9 +51,7 @@ pa_volume_u8_c (uint8_t *samples, int32_t *volumes, unsigned channels, unsigned } } -static void -pa_volume_alaw_c (uint8_t *samples, int32_t *volumes, unsigned channels, unsigned length) -{ +static void pa_volume_alaw_c(uint8_t *samples, int32_t *volumes, unsigned channels, unsigned length) { unsigned channel; for (channel = 0; length; length--) { @@ -74,9 +70,7 @@ pa_volume_alaw_c (uint8_t *samples, int32_t *volumes, unsigned channels, unsigne } } -static void -pa_volume_ulaw_c (uint8_t *samples, int32_t *volumes, unsigned channels, unsigned length) -{ +static void pa_volume_ulaw_c(uint8_t *samples, int32_t *volumes, unsigned channels, unsigned length) { unsigned channel; for (channel = 0; length; length--) { @@ -95,12 +89,10 @@ pa_volume_ulaw_c (uint8_t *samples, int32_t *volumes, unsigned channels, unsigne } } -static void -pa_volume_s16ne_c (int16_t *samples, int32_t *volumes, unsigned channels, unsigned length) -{ +static void pa_volume_s16ne_c(int16_t *samples, int32_t *volumes, unsigned channels, unsigned length) { unsigned channel; - length /= sizeof (int16_t); + length /= sizeof(int16_t); for (channel = 0; length; length--) { int32_t t, hi, lo; @@ -124,12 +116,10 @@ pa_volume_s16ne_c (int16_t *samples, int32_t *volumes, unsigned channels, unsign } } -static void -pa_volume_s16re_c (int16_t *samples, int32_t *volumes, unsigned channels, unsigned length) -{ +static void pa_volume_s16re_c(int16_t *samples, int32_t *volumes, unsigned channels, unsigned length) { unsigned channel; - length /= sizeof (int16_t); + length /= sizeof(int16_t); for (channel = 0; length; length--) { int32_t t, hi, lo; @@ -147,12 +137,10 @@ pa_volume_s16re_c (int16_t *samples, int32_t *volumes, unsigned channels, unsign } } -static void -pa_volume_float32ne_c (float *samples, float *volumes, unsigned channels, unsigned length) -{ +static void pa_volume_float32ne_c(float *samples, float *volumes, unsigned channels, unsigned length) { unsigned channel; - length /= sizeof (float); + length /= sizeof(float); for (channel = 0; length; length--) { *samples++ *= volumes[channel]; @@ -162,12 +150,10 @@ pa_volume_float32ne_c (float *samples, float *volumes, unsigned channels, unsign } } -static void -pa_volume_float32re_c (float *samples, float *volumes, unsigned channels, unsigned length) -{ +static void pa_volume_float32re_c(float *samples, float *volumes, unsigned channels, unsigned length) { unsigned channel; - length /= sizeof (float); + length /= sizeof(float); for (channel = 0; length; length--) { float t; @@ -181,12 +167,10 @@ pa_volume_float32re_c (float *samples, float *volumes, unsigned channels, unsign } } -static void -pa_volume_s32ne_c (int32_t *samples, int32_t *volumes, unsigned channels, unsigned length) -{ +static void pa_volume_s32ne_c(int32_t *samples, int32_t *volumes, unsigned channels, unsigned length) { unsigned channel; - length /= sizeof (int32_t); + length /= sizeof(int32_t); for (channel = 0; length; length--) { int64_t t; @@ -201,12 +185,10 @@ pa_volume_s32ne_c (int32_t *samples, int32_t *volumes, unsigned channels, unsign } } -static void -pa_volume_s32re_c (int32_t *samples, int32_t *volumes, unsigned channels, unsigned length) -{ +static void pa_volume_s32re_c(int32_t *samples, int32_t *volumes, unsigned channels, unsigned length) { unsigned channel; - length /= sizeof (int32_t); + length /= sizeof(int32_t); for (channel = 0; length; length--) { int64_t t; @@ -221,9 +203,7 @@ pa_volume_s32re_c (int32_t *samples, int32_t *volumes, unsigned channels, unsign } } -static void -pa_volume_s24ne_c (uint8_t *samples, int32_t *volumes, unsigned channels, unsigned length) -{ +static void pa_volume_s24ne_c(uint8_t *samples, int32_t *volumes, unsigned channels, unsigned length) { unsigned channel; uint8_t *e; @@ -242,9 +222,7 @@ pa_volume_s24ne_c (uint8_t *samples, int32_t *volumes, unsigned channels, unsign } } -static void -pa_volume_s24re_c (uint8_t *samples, int32_t *volumes, unsigned channels, unsigned length) -{ +static void pa_volume_s24re_c(uint8_t *samples, int32_t *volumes, unsigned channels, unsigned length) { unsigned channel; uint8_t *e; @@ -263,12 +241,10 @@ pa_volume_s24re_c (uint8_t *samples, int32_t *volumes, unsigned channels, unsign } } -static void -pa_volume_s24_32ne_c (uint32_t *samples, int32_t *volumes, unsigned channels, unsigned length) -{ +static void pa_volume_s24_32ne_c(uint32_t *samples, int32_t *volumes, unsigned channels, unsigned length) { unsigned channel; - length /= sizeof (uint32_t); + length /= sizeof(uint32_t); for (channel = 0; length; length--) { int64_t t; @@ -283,12 +259,10 @@ pa_volume_s24_32ne_c (uint32_t *samples, int32_t *volumes, unsigned channels, un } } -static void -pa_volume_s24_32re_c (uint32_t *samples, int32_t *volumes, unsigned channels, unsigned length) -{ +static void pa_volume_s24_32re_c(uint32_t *samples, int32_t *volumes, unsigned channels, unsigned length) { unsigned channel; - length /= sizeof (uint32_t); + length /= sizeof(uint32_t); for (channel = 0; length; length--) { int64_t t; @@ -303,8 +277,7 @@ pa_volume_s24_32re_c (uint32_t *samples, int32_t *volumes, unsigned channels, un } } -static pa_do_volume_func_t do_volume_table[] = -{ +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, diff --git a/src/pulsecore/svolume_mmx.c b/src/pulsecore/svolume_mmx.c index 3e2992a2..421156ea 100644 --- a/src/pulsecore/svolume_mmx.c +++ b/src/pulsecore/svolume_mmx.c @@ -31,11 +31,11 @@ #include <pulsecore/macro.h> #include <pulsecore/g711.h> #include <pulsecore/core-util.h> +#include <pulsecore/endianmacros.h> #include "cpu-x86.h" #include "sample-util.h" -#include "endianmacros.h" #if defined (__i386__) || defined (__amd64__) /* in s: 2 int16_t samples @@ -95,9 +95,7 @@ " 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) -{ +static void pa_volume_s16ne_mmx(int16_t *samples, int32_t *volumes, unsigned channels, unsigned length) { pa_reg_x86 channel, temp; /* Channels must be at least 4, and always a multiple of the original number. @@ -156,15 +154,13 @@ pa_volume_s16ne_mmx (int16_t *samples, int32_t *volumes, unsigned channels, unsi "6: \n\t" " emms \n\t" - : "+r" (samples), "+r" (volumes), "+r" (length), "=D" ((pa_reg_x86)channel), "=&r" (temp) + : "+r" (samples), "+r" (volumes), "+r" (length), "=D" (channel), "=&r" (temp) : "rm" ((pa_reg_x86)channels) : "cc" ); } -static void -pa_volume_s16re_mmx (int16_t *samples, int32_t *volumes, unsigned channels, unsigned length) -{ +static void pa_volume_s16re_mmx(int16_t *samples, int32_t *volumes, unsigned channels, unsigned length) { pa_reg_x86 channel, temp; /* Channels must be at least 4, and always a multiple of the original number. @@ -233,7 +229,7 @@ pa_volume_s16re_mmx (int16_t *samples, int32_t *volumes, unsigned channels, unsi "6: \n\t" " emms \n\t" - : "+r" (samples), "+r" (volumes), "+r" (length), "=D" ((pa_reg_x86)channel), "=&r" (temp) + : "+r" (samples), "+r" (volumes), "+r" (length), "=D" (channel), "=&r" (temp) : "rm" ((pa_reg_x86)channels) : "cc" ); @@ -243,11 +239,11 @@ pa_volume_s16re_mmx (int16_t *samples, int32_t *volumes, unsigned channels, unsi #ifdef RUN_TEST #define CHANNELS 2 -#define SAMPLES 1021 +#define SAMPLES 1022 #define TIMES 1000 #define PADDING 16 -static void run_test (void) { +static void run_test(void) { int16_t samples[SAMPLES]; int16_t samples_ref[SAMPLES]; int16_t samples_orig[SAMPLES]; @@ -256,43 +252,43 @@ static void run_test (void) { pa_do_volume_func_t func; pa_usec_t start, stop; - func = pa_get_volume_func (PA_SAMPLE_S16NE); + func = pa_get_volume_func(PA_SAMPLE_S16NE); - printf ("checking MMX %zd\n", sizeof (samples)); + printf("checking MMX %zd\n", sizeof(samples)); - pa_random (samples, sizeof (samples)); + pa_random(samples, sizeof(samples)); /* for (i = 0; i < SAMPLES; i++) samples[i] = -1; */ - memcpy (samples_ref, samples, sizeof (samples)); - memcpy (samples_orig, 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; + volumes[i] = PA_CLAMP_VOLUME(rand() >> 1); /* volumes[i] = 0x0000ffff; */ 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)); + 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 * %08x)\n", i, samples[i], samples_ref[i], + printf("%d: %04x != %04x (%04x * %08x)\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)); + 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)); + 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)); @@ -304,18 +300,18 @@ static void run_test (void) { #endif /* defined (__i386__) || defined (__amd64__) */ -void pa_volume_func_init_mmx (pa_cpu_x86_flag_t flags) { +void pa_volume_func_init_mmx(pa_cpu_x86_flag_t flags) { #if defined (__i386__) || defined (__amd64__) #ifdef RUN_TEST - run_test (); + run_test(); #endif if ((flags & PA_CPU_X86_MMX) && (flags & PA_CPU_X86_CMOV)) { pa_log_info("Initialising MMX optimized functions."); - 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); + 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__) */ } diff --git a/src/pulsecore/svolume_orc.c b/src/pulsecore/svolume_orc.c new file mode 100644 index 00000000..db07ba61 --- /dev/null +++ b/src/pulsecore/svolume_orc.c @@ -0,0 +1,117 @@ +/*** + This file is part of PulseAudio. + + Copyright 2004-2006 Lennart Poettering + Copyright 2009 Wim Taymans <wim.taymans@collabora.co.uk> + Copyright 2010 Arun Raghavan <arun.raghavan@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 "cpu-orc.h" +#include <pulse/xmalloc.h> +#include <pulse/rtclock.h> +#include <pulsecore/sample-util.h> +#include <pulsecore/random.h> +#include <pulsecore/svolume-orc-gen.h> + +pa_do_volume_func_t fallback; + +static void +pa_volume_s16ne_orc(int16_t *samples, int32_t *volumes, unsigned channels, unsigned length) +{ + if (channels == 2) { + int64_t v = (int64_t)volumes[1] << 32 | volumes[0]; + pa_volume_s16ne_orc_2ch (samples, v, ((length / (sizeof(int16_t))) / 2)); + } else if (channels == 1) + pa_volume_s16ne_orc_1ch (samples, volumes[0], length / (sizeof(int16_t))); + else + fallback(samples, volumes, channels, length); +} + +#undef RUN_TEST + +#ifdef RUN_TEST +#define CHANNELS 2 +#define SAMPLES 1022 +#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 ORC %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] = PA_CLAMP_VOLUME(rand() >> 1); + for (padding = 0; padding < PADDING; padding++, i++) + volumes[i] = volumes[padding]; + + func (samples_ref, volumes, CHANNELS, sizeof (samples)); + pa_volume_s16ne_orc (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_orc (samples, volumes, CHANNELS, sizeof (samples)); + } + stop = pa_rtclock_now(); + pa_log_info("ORC: %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)); + + pa_assert_se(memcmp(samples_ref, samples, sizeof(samples)) == 0); +} +#endif + +void pa_volume_func_init_orc(void) { + pa_log_info("Initialising ORC optimized functions."); + +#ifdef RUN_TEST + run_test(); +#endif + + fallback = pa_get_volume_func(PA_SAMPLE_S16NE); + pa_set_volume_func(PA_SAMPLE_S16NE, (pa_do_volume_func_t) pa_volume_s16ne_orc); +} diff --git a/src/pulsecore/svolume_sse.c b/src/pulsecore/svolume_sse.c index 200482ec..ef07a243 100644 --- a/src/pulsecore/svolume_sse.c +++ b/src/pulsecore/svolume_sse.c @@ -31,11 +31,11 @@ #include <pulsecore/macro.h> #include <pulsecore/g711.h> #include <pulsecore/core-util.h> +#include <pulsecore/endianmacros.h> #include "cpu-x86.h" #include "sample-util.h" -#include "endianmacros.h" #if defined (__i386__) || defined (__amd64__) @@ -79,9 +79,7 @@ static int channel_overread_table[8] = {8,8,8,12,8,10,12,14}; -static void -pa_volume_s16ne_sse2 (int16_t *samples, int32_t *volumes, unsigned channels, unsigned length) -{ +static void pa_volume_s16ne_sse2(int16_t *samples, int32_t *volumes, unsigned channels, unsigned length) { pa_reg_x86 channel, temp; /* Channels must be at least 8 and always a multiple of the original number. @@ -161,9 +159,7 @@ pa_volume_s16ne_sse2 (int16_t *samples, int32_t *volumes, unsigned channels, uns ); } -static void -pa_volume_s16re_sse2 (int16_t *samples, int32_t *volumes, unsigned channels, unsigned length) -{ +static void pa_volume_s16re_sse2(int16_t *samples, int32_t *volumes, unsigned channels, unsigned length) { pa_reg_x86 channel, temp; /* Channels must be at least 8 and always a multiple of the original number. @@ -255,11 +251,11 @@ pa_volume_s16re_sse2 (int16_t *samples, int32_t *volumes, unsigned channels, uns #ifdef RUN_TEST #define CHANNELS 2 -#define SAMPLES 1021 +#define SAMPLES 1022 #define TIMES 1000 #define PADDING 16 -static void run_test (void) { +static void run_test(void) { int16_t samples[SAMPLES]; int16_t samples_ref[SAMPLES]; int16_t samples_orig[SAMPLES]; @@ -268,21 +264,21 @@ static void run_test (void) { pa_do_volume_func_t func; pa_usec_t start, stop; - func = pa_get_volume_func (PA_SAMPLE_S16NE); + func = pa_get_volume_func(PA_SAMPLE_S16NE); - printf ("checking SSE2 %zd\n", sizeof (samples)); + printf("checking SSE2 %zd\n", sizeof(samples)); - pa_random (samples, sizeof (samples)); - memcpy (samples_ref, samples, sizeof (samples)); - memcpy (samples_orig, samples, 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; + volumes[i] = PA_CLAMP_VOLUME(rand() >> 1); for (padding = 0; padding < PADDING; padding++, i++) volumes[i] = volumes[padding]; - func (samples_ref, volumes, CHANNELS, sizeof (samples)); - pa_volume_s16ne_sse2 (samples, volumes, CHANNELS, sizeof (samples)); + func(samples_ref, volumes, CHANNELS, sizeof(samples)); + pa_volume_s16ne_sse2(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], @@ -292,16 +288,16 @@ static void run_test (void) { start = pa_rtclock_now(); for (j = 0; j < TIMES; j++) { - memcpy (samples, samples_orig, sizeof (samples)); - pa_volume_s16ne_sse2 (samples, volumes, CHANNELS, sizeof (samples)); + memcpy(samples, samples_orig, sizeof(samples)); + pa_volume_s16ne_sse2(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)); + 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)); @@ -311,18 +307,18 @@ static void run_test (void) { #endif #endif /* defined (__i386__) || defined (__amd64__) */ -void pa_volume_func_init_sse (pa_cpu_x86_flag_t flags) { +void pa_volume_func_init_sse(pa_cpu_x86_flag_t flags) { #if defined (__i386__) || defined (__amd64__) #ifdef RUN_TEST - run_test (); + run_test(); #endif if (flags & PA_CPU_X86_SSE2) { pa_log_info("Initialising SSE2 optimized functions."); - pa_set_volume_func (PA_SAMPLE_S16NE, (pa_do_volume_func_t) pa_volume_s16ne_sse2); - pa_set_volume_func (PA_SAMPLE_S16RE, (pa_do_volume_func_t) pa_volume_s16re_sse2); + pa_set_volume_func(PA_SAMPLE_S16NE, (pa_do_volume_func_t) pa_volume_s16ne_sse2); + pa_set_volume_func(PA_SAMPLE_S16RE, (pa_do_volume_func_t) pa_volume_s16re_sse2); } #endif /* defined (__i386__) || defined (__amd64__) */ } diff --git a/src/pulsecore/time-smoother.c b/src/pulsecore/time-smoother.c index 0696cabc..a8dd333f 100644 --- a/src/pulsecore/time-smoother.c +++ b/src/pulsecore/time-smoother.c @@ -385,7 +385,7 @@ void pa_smoother_put(pa_smoother *s, pa_usec_t x, pa_usec_t y) { s->abc_valid = FALSE; #ifdef DEBUG_DATA - pa_log_debug("%p, put(%llu | %llu) = %llu", s, (unsigned long long) (x + s->time_offset), (unsigned long long) x, (unsigned long long) y); + pa_log_debug("%p, put(%llu | %llu) = %llu", s, (unsigned long long) (x + s->time_offset), (unsigned long long) x, (unsigned long long) y); #endif } @@ -441,7 +441,7 @@ void pa_smoother_pause(pa_smoother *s, pa_usec_t x) { return; #ifdef DEBUG_DATA - pa_log_debug("pause(%llu)", (unsigned long long) x); + pa_log_debug("pause(%llu)", (unsigned long long) x); #endif s->paused = TRUE; diff --git a/src/pulsecore/usergroup.c b/src/pulsecore/usergroup.c index 71b13bca..c244865e 100644 --- a/src/pulsecore/usergroup.c +++ b/src/pulsecore/usergroup.c @@ -142,9 +142,7 @@ struct group *pa_getgrgid_malloc(gid_t gid) { 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) - { + while ((err = getgrgid_r(gid, (struct group *)buf, getgr_buf, getgr_buflen, &result)) == ERANGE) { if (expand_buffer_trashcontents(&buf, &buflen)) break; @@ -203,9 +201,7 @@ struct group *pa_getgrnam_malloc(const char *name) { 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) - { + while ((err = getgrnam_r(name, (struct group *)buf, getgr_buf, getgr_buflen, &result)) == ERANGE) { if (expand_buffer_trashcontents(&buf, &buflen)) break; @@ -268,9 +264,7 @@ struct passwd *pa_getpwnam_malloc(const char *name) { 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) - { + while ((err = getpwnam_r(name, (struct passwd *)buf, getpw_buf, getpw_buflen, &result)) == ERANGE) { if (expand_buffer_trashcontents(&buf, &buflen)) break; @@ -329,9 +323,7 @@ struct passwd *pa_getpwuid_malloc(uid_t uid) { 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) - { + while ((err = getpwuid_r(uid, (struct passwd *)buf, getpw_buf, getpw_buflen, &result)) == ERANGE) { if (expand_buffer_trashcontents(&buf, &buflen)) break; diff --git a/src/pulsecore/x11prop.c b/src/pulsecore/x11prop.c index 4cb21daa..8df32788 100644 --- a/src/pulsecore/x11prop.c +++ b/src/pulsecore/x11prop.c @@ -34,8 +34,7 @@ #define PA_XCB_FORMAT 8 -static xcb_screen_t *screen_of_display(xcb_connection_t *xcb, int screen) -{ +static xcb_screen_t *screen_of_display(xcb_connection_t *xcb, int screen) { const xcb_setup_t *s; xcb_screen_iterator_t iter; |