diff options
Diffstat (limited to 'src')
180 files changed, 5425 insertions, 3357 deletions
diff --git a/src/.gitignore b/src/.gitignore index c93974bf..e56c225d 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -24,7 +24,6 @@ cpulimit-test2 daemon.conf default.pa system.pa -envelope-test esdcompat flist-test gconf-helper @@ -66,3 +65,4 @@ voltest start-pulseaudio-x11 start-pulseaudio-kde vector-test +*-symdef.h diff --git a/src/Makefile.am b/src/Makefile.am index f160c944..fca7edad 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -261,7 +261,6 @@ TESTS = \ smoother-test \ mix-test \ remix-test \ - envelope-test \ proplist-test \ lock-autospawn-test \ prioq-test @@ -296,7 +295,6 @@ TESTS_BINARIES = \ smoother-test \ mix-test \ remix-test \ - envelope-test \ proplist-test \ rtstutter \ stripnul \ @@ -516,11 +514,6 @@ smoother_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulse.la li smoother_test_CFLAGS = $(AM_CFLAGS) smoother_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) -envelope_test_SOURCES = tests/envelope-test.c -envelope_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la -envelope_test_CFLAGS = $(AM_CFLAGS) -envelope_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) - proplist_test_SOURCES = tests/proplist-test.c proplist_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la proplist_test_CFLAGS = $(AM_CFLAGS) @@ -838,7 +831,6 @@ libpulsecore_@PA_MAJORMINOR@_la_SOURCES = \ pulsecore/core-scache.c pulsecore/core-scache.h \ pulsecore/core-subscribe.c pulsecore/core-subscribe.h \ pulsecore/core.c pulsecore/core.h \ - pulsecore/envelope.c pulsecore/envelope.h \ pulsecore/fdsem.c pulsecore/fdsem.h \ pulsecore/g711.c pulsecore/g711.h \ pulsecore/hook-list.c pulsecore/hook-list.h \ @@ -859,6 +851,7 @@ libpulsecore_@PA_MAJORMINOR@_la_SOURCES = \ pulsecore/cpu.h \ pulsecore/cpu-arm.c pulsecore/cpu-arm.h \ pulsecore/cpu-x86.c pulsecore/cpu-x86.h \ + pulsecore/cpu-orc.c pulsecore/cpu-orc.h \ pulsecore/svolume_c.c pulsecore/svolume_arm.c \ pulsecore/svolume_mmx.c pulsecore/svolume_sse.c \ pulsecore/sconv-s16be.c pulsecore/sconv-s16be.h \ @@ -883,6 +876,14 @@ libpulsecore_@PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) $(LIBSAMPLERATE_CFLAGS) $( libpulsecore_@PA_MAJORMINOR@_la_LDFLAGS = $(AM_LDFLAGS) -avoid-version libpulsecore_@PA_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) $(LIBSAMPLERATE_LIBS) $(LIBSPEEX_LIBS) $(LIBSNDFILE_LIBS) $(WINSOCK_LIBS) $(LTLIBICONV) libpulsecommon-@PA_MAJORMINOR@.la libpulse.la libpulsecore-foreign.la +if HAVE_ORC +ORC_SOURCE += pulsecore/svolume +libpulsecore_@PA_MAJORMINOR@_la_SOURCES += pulsecore/svolume_orc.c +nodist_libpulsecore_@PA_MAJORMINOR@_la_SOURCES = pulsecore/svolume-orc-gen.c pulsecore/svolume-orc-gen.h +libpulsecore_@PA_MAJORMINOR@_la_CFLAGS += $(ORC_CFLAGS) +libpulsecore_@PA_MAJORMINOR@_la_LIBADD += $(ORC_LIBS) +endif + if HAVE_X11 libpulsecore_@PA_MAJORMINOR@_la_SOURCES += pulsecore/x11wrap.c pulsecore/x11wrap.h libpulsecore_@PA_MAJORMINOR@_la_CFLAGS += $(X11_CFLAGS) @@ -1108,6 +1109,8 @@ dist_alsaprofilesets_DATA = \ modules/alsa/mixer/profile-sets/maudio-fasttrack-pro.conf \ modules/alsa/mixer/profile-sets/native-instruments-audio4dj.conf \ modules/alsa/mixer/profile-sets/native-instruments-audio8dj.conf \ + modules/alsa/mixer/profile-sets/native-instruments-traktor-audio6.conf \ + modules/alsa/mixer/profile-sets/native-instruments-traktor-audio10.conf \ modules/alsa/mixer/profile-sets/native-instruments-traktorkontrol-s4.conf \ modules/alsa/mixer/profile-sets/native-instruments-korecontroller.conf @@ -1121,9 +1124,12 @@ dist_alsapaths_DATA = \ modules/alsa/mixer/paths/analog-input.conf \ modules/alsa/mixer/paths/analog-input.conf.common \ modules/alsa/mixer/paths/analog-input-fm.conf \ - modules/alsa/mixer/paths/analog-input-internal-mic.conf \ modules/alsa/mixer/paths/analog-input-linein.conf \ modules/alsa/mixer/paths/analog-input-mic.conf \ + modules/alsa/mixer/paths/analog-input-dock-mic.conf \ + modules/alsa/mixer/paths/analog-input-front-mic.conf \ + modules/alsa/mixer/paths/analog-input-internal-mic.conf \ + modules/alsa/mixer/paths/analog-input-rear-mic.conf \ modules/alsa/mixer/paths/analog-input-mic.conf.common \ modules/alsa/mixer/paths/analog-input-mic-line.conf \ modules/alsa/mixer/paths/analog-input-tvtuner.conf \ @@ -1332,7 +1338,7 @@ $(SYMDEF_FILES): modules/module-defs.h.m4 .PHONY: builddirs builddirs: - $(AM_V_at)$(MKDIR_P) daemon modules src/modules/echo-cancel + $(AM_V_at)$(MKDIR_P) daemon modules # Simple protocol @@ -1538,19 +1544,24 @@ module_oss_la_LIBADD = $(MODULE_LIBADD) liboss-util.la module_coreaudio_detect_la_SOURCES = modules/coreaudio/module-coreaudio-detect.c module_coreaudio_detect_la_LDFLAGS = $(MODULE_LDFLAGS) \ - -Wl,-framework -Wl,Cocoa -framework CoreAudio \ - -Wl,-framework -Wl,AudioUnit -framework AudioUnit + -Wl,-framework -Wl,Cocoa -framework CoreAudio \ + -Wl,-framework -Wl,AudioUnit -framework AudioUnit module_coreaudio_detect_la_LIBADD = $(MODULE_LIBADD) module_coreaudio_device_la_SOURCES = modules/coreaudio/module-coreaudio-device.c module_coreaudio_device_la_LDFLAGS = $(MODULE_LDFLAGS) \ - -Wl,-framework -Wl,Cocoa -framework CoreAudio \ - -Wl,-framework -Wl,AudioUnit -framework AudioUnit + -Wl,-framework -Wl,Cocoa -framework CoreAudio \ + -Wl,-framework -Wl,AudioUnit -framework AudioUnit module_coreaudio_device_la_LIBADD = $(MODULE_LIBADD) # ALSA -libalsa_util_la_SOURCES = modules/alsa/alsa-util.c modules/alsa/alsa-util.h modules/alsa/alsa-mixer.c modules/alsa/alsa-mixer.h modules/alsa/alsa-sink.c modules/alsa/alsa-sink.h modules/alsa/alsa-source.c modules/alsa/alsa-source.h modules/reserve-wrap.c modules/reserve-wrap.h +libalsa_util_la_SOURCES = \ + modules/alsa/alsa-util.c modules/alsa/alsa-util.h \ + modules/alsa/alsa-mixer.c modules/alsa/alsa-mixer.h \ + modules/alsa/alsa-sink.c modules/alsa/alsa-sink.h \ + modules/alsa/alsa-source.c modules/alsa/alsa-source.h \ + modules/reserve-wrap.c modules/reserve-wrap.h libalsa_util_la_LDFLAGS = -avoid-version libalsa_util_la_LIBADD = $(MODULE_LIBADD) $(ASOUNDLIB_LIBS) libalsa_util_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS) @@ -1724,18 +1735,19 @@ module_suspend_on_idle_la_LIBADD = $(MODULE_LIBADD) module_suspend_on_idle_la_CFLAGS = $(AM_CFLAGS) # echo-cancel module -module_echo_cancel_la_SOURCES = modules/echo-cancel/module-echo-cancel.c modules/echo-cancel/echo-cancel.h \ - modules/echo-cancel/speex.c \ - modules/echo-cancel/adrian-aec.c modules/echo-cancel/adrian-aec.h \ - modules/echo-cancel/adrian.c modules/echo-cancel/adrian.h +module_echo_cancel_la_SOURCES = \ + modules/echo-cancel/module-echo-cancel.c modules/echo-cancel/echo-cancel.h \ + modules/echo-cancel/speex.c \ + modules/echo-cancel/adrian-aec.c modules/echo-cancel/adrian-aec.h \ + modules/echo-cancel/adrian.c modules/echo-cancel/adrian.h module_echo_cancel_la_LDFLAGS = $(MODULE_LDFLAGS) module_echo_cancel_la_LIBADD = $(MODULE_LIBADD) $(LIBSPEEX_LIBS) module_echo_cancel_la_CFLAGS = $(AM_CFLAGS) $(LIBSPEEX_CFLAGS) if HAVE_ORC ORC_SOURCE += modules/echo-cancel/adrian-aec nodist_module_echo_cancel_la_SOURCES = \ - modules/echo-cancel/adrian-aec-orc-gen.c \ - modules/echo-cancel/adrian-aec-orc-gen.h + modules/echo-cancel/adrian-aec-orc-gen.c \ + modules/echo-cancel/adrian-aec-orc-gen.h module_echo_cancel_la_LIBADD += $(ORC_LIBS) module_echo_cancel_la_CFLAGS += $(ORC_CFLAGS) -I$(top_builddir)/src/modules/echo-cancel endif @@ -1817,15 +1829,23 @@ module_bluetooth_discover_la_LDFLAGS = $(MODULE_LDFLAGS) module_bluetooth_discover_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS) libbluetooth-util.la module_bluetooth_discover_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) -libbluetooth_sbc_la_SOURCES = modules/bluetooth/sbc.c modules/bluetooth/sbc.h modules/bluetooth/sbc_tables.h modules/bluetooth/sbc_math.h modules/bluetooth/sbc_primitives.h modules/bluetooth/sbc_primitives.c modules/bluetooth/sbc_primitives_mmx.h modules/bluetooth/sbc_primitives_neon.h modules/bluetooth/sbc_primitives_mmx.c modules/bluetooth/sbc_primitives_neon.c +libbluetooth_sbc_la_SOURCES = \ + modules/bluetooth/sbc/sbc.c modules/bluetooth/sbc/sbc.h \ + modules/bluetooth/sbc/sbc_primitives.c modules/bluetooth/sbc/sbc_primitives.h \ + modules/bluetooth/sbc/sbc_primitives_armv6.h modules/bluetooth/sbc/sbc_primitives_armv6.c \ + modules/bluetooth/sbc/sbc_primitives_iwmmxt.h modules/bluetooth/sbc/sbc_primitives_iwmmxt.c \ + modules/bluetooth/sbc/sbc_primitives_mmx.c modules/bluetooth/sbc/sbc_primitives_mmx.h \ + modules/bluetooth/sbc/sbc_primitives_neon.c modules/bluetooth/sbc/sbc_primitives_neon.h \ + modules/bluetooth/sbc/sbc_math.h \ + modules/bluetooth/sbc/sbc_tables.h libbluetooth_sbc_la_LDFLAGS = -avoid-version libbluetooth_sbc_la_LIBADD = $(MODULE_LIBADD) -libbluetooth_sbc_la_CFLAGS = $(AM_CFLAGS) +libbluetooth_sbc_la_CFLAGS = $(AM_CFLAGS) -I$(top_srcdir)/src/modules/bluetooth/sbc BLUETOOTH_SBC_FILES = $(subst modules/bluetooth/,,$(libbluetooth_sbc_la_SOURCES)) libbluetooth_ipc_la_SOURCES = modules/bluetooth/ipc.c modules/bluetooth/ipc.h libbluetooth_ipc_la_LDFLAGS = -avoid-version -libbluetooth_ipc_la_LIBADD = $(AM_LIBADD)libpulsecore-@PA_MAJORMINOR@.la libpulsecommon-@PA_MAJORMINOR@.la libpulse.la +libbluetooth_ipc_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINOR@.la libpulsecommon-@PA_MAJORMINOR@.la libpulse.la libbluetooth_ipc_la_CFLAGS = $(AM_CFLAGS) BLUETOOTH_IPC_FILES = $(subst modules/bluetooth/,,$(libbluetooth_ipc_la_SOURCES)) rtp.h @@ -1837,7 +1857,7 @@ libbluetooth_util_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) module_bluetooth_device_la_SOURCES = modules/bluetooth/module-bluetooth-device.c modules/bluetooth/rtp.h module_bluetooth_device_la_LDFLAGS = $(MODULE_LDFLAGS) module_bluetooth_device_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS) libbluetooth-util.la libbluetooth-ipc.la libbluetooth-sbc.la -module_bluetooth_device_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) +module_bluetooth_device_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) -I$(top_srcdir)/src/modules/bluetooth/sbc # Apple Airtunes/RAOP module_raop_sink_la_SOURCES = modules/raop/module-raop-sink.c @@ -1863,23 +1883,23 @@ module_rygel_media_server_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) CLEANFILES += esdcompat client.conf default.pa system.pa daemon.conf start-pulseaudio-x11 start-pulseaudio-kde daemon/pulseaudio.desktop daemon/pulseaudio-kde.desktop esdcompat: daemon/esdcompat.in Makefile - sed -e 's,@PACKAGE_VERSION\@,$(PACKAGE_VERSION),g' \ + $(AM_V_GEN) sed -e 's,@PACKAGE_VERSION\@,$(PACKAGE_VERSION),g' \ -e 's,@PACKAGE_NAME\@,$(PACKAGE_NAME),g' \ -e 's,@PA_BINARY\@,$(PA_BINARY),g' < $< > $@ - chmod +x esdcompat + $(AM_V_at) chmod +x esdcompat start-pulseaudio-x11: daemon/start-pulseaudio-x11.in Makefile - sed -e 's,@PA_BINARY\@,$(PA_BINARY),g' \ + $(AM_V_GEN) sed -e 's,@PA_BINARY\@,$(PA_BINARY),g' \ -e 's,@PACTL_BINARY\@,$(bindir)/pactl,g' < $< > $@ - chmod +x start-pulseaudio-x11 + $(AM_V_at) chmod +x start-pulseaudio-x11 start-pulseaudio-kde: daemon/start-pulseaudio-kde.in Makefile - sed -e 's,@PA_BINARY\@,$(PA_BINARY),g' \ + $(AM_V_GEN) sed -e 's,@PA_BINARY\@,$(PA_BINARY),g' \ -e 's,@PACTL_BINARY\@,$(bindir)/pactl,g' < $< > $@ - chmod +x start-pulseaudio-kde + $(AM_V_at) chmod +x start-pulseaudio-kde client.conf: pulse/client.conf.in Makefile - sed -e 's,@PA_BINARY\@,$(PA_BINARY),g' < $< > $@ + $(AM_V_GEN) sed -e 's,@PA_BINARY\@,$(PA_BINARY),g' < $< > $@ if OS_IS_WIN32 default.pa: daemon/default.pa.win32 @@ -1888,17 +1908,17 @@ system.pa: daemon/default.pa.win32 cp $< $@ else default.pa: daemon/default.pa.in Makefile - sed -e 's,@PA_BINARY\@,$(PA_BINARY),g' \ + $(AM_V_GEN) sed -e 's,@PA_BINARY\@,$(PA_BINARY),g' \ -e 's,@PA_DLSEARCHPATH\@,$(modlibexecdir),g' \ -e 's,@PA_SOEXT\@,.so,g' < $< > $@ system.pa: daemon/system.pa.in Makefile - sed -e 's,@PA_BINARY\@,$(PA_BINARY),g' \ + $(AM_V_GEN) sed -e 's,@PA_BINARY\@,$(PA_BINARY),g' \ -e 's,@PA_DLSEARCHPATH\@,$(modlibexecdir),g' \ -e 's,@PA_SOEXT\@,.so,g' < $< > $@ endif daemon.conf: daemon/daemon.conf.in Makefile - sed -e 's,@PA_DLSEARCHPATH\@,$(modlibexecdir),g' \ + $(AM_V_GEN) sed -e 's,@PA_DLSEARCHPATH\@,$(modlibexecdir),g' \ -e 's,@PA_DEFAULT_CONFIG_FILE\@,$(DEFAULT_CONFIG_DIR),g' < $< > $@ if OS_IS_WIN32 @@ -1935,8 +1955,8 @@ update-ffmpeg: # We get things twice here, because sometimes gitweb will us just give a "Generating..." otherwise. update-sbc: for i in $(BLUETOOTH_SBC_FILES) ; do \ - wget -O /dev/null http://git.kernel.org/\?p=bluetooth/bluez.git\;a=blob_plain\;f=sbc/$$i ; \ - wget -O modules/bluetooth/$$i http://git.kernel.org/\?p=bluetooth/bluez.git\;a=blob_plain\;f=sbc/$$i ; \ + wget -O /dev/null http://git.kernel.org/\?p=bluetooth/bluez.git\;a=blob_plain\;f=$$i ; \ + wget -O modules/bluetooth/$$i http://git.kernel.org/\?p=bluetooth/bluez.git\;a=blob_plain\;f=$$i ; \ done for i in $(BLUETOOTH_IPC_FILES); do \ wget -O /dev/null http://git.kernel.org/\?p=bluetooth/bluez.git\;a=blob_plain\;f=audio/$$i ; \ diff --git a/src/daemon/cmdline.c b/src/daemon/cmdline.c index f6cdcdc8..4854affc 100644 --- a/src/daemon/cmdline.c +++ b/src/daemon/cmdline.c @@ -145,7 +145,8 @@ void pa_cmdline_help(const char *argv0) { " this time passed\n" " --log-level[=LEVEL] Increase or set verbosity level\n" " -v Increase the verbosity level\n" - " --log-target={auto,syslog,stderr} Specify the log target\n" + " --log-target={auto,syslog,stderr,file:PATH}\n" + " Specify the log target\n" " --log-meta[=BOOL] Include code location in log messages\n" " --log-time[=BOOL] Include timestamps in log messages\n" " --log-backtrace=FRAMES Include a backtrace in log messages\n" @@ -318,7 +319,7 @@ int pa_cmdline_parse(pa_daemon_conf *conf, int argc, char *const argv [], int *d case ARG_LOG_TARGET: if (pa_daemon_conf_set_log_target(conf, optarg) < 0) { - pa_log(_("Invalid log target: use either 'syslog', 'stderr' or 'auto'.")); + pa_log(_("Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file name 'file:<path>'.")); goto fail; } break; diff --git a/src/daemon/cpulimit.c b/src/daemon/cpulimit.c index c1c2a6f6..9e22d7e8 100644 --- a/src/daemon/cpulimit.c +++ b/src/daemon/cpulimit.c @@ -86,7 +86,7 @@ static struct sigaction sigaction_prev; static pa_bool_t installed = FALSE; /* The current state of operation */ -static enum { +static enum { PHASE_IDLE, /* Normal state */ PHASE_SOFT /* After CPU overload has been detected */ } phase = PHASE_IDLE; diff --git a/src/daemon/daemon-conf.c b/src/daemon/daemon-conf.c index f640b039..9b530a80 100644 --- a/src/daemon/daemon-conf.c +++ b/src/daemon/daemon-conf.c @@ -28,6 +28,7 @@ #include <stdio.h> #include <string.h> #include <unistd.h> +#include <fcntl.h> #ifdef HAVE_SCHED_H #include <sched.h> @@ -180,6 +181,9 @@ pa_daemon_conf* pa_daemon_conf_new(void) { void pa_daemon_conf_free(pa_daemon_conf *c) { pa_assert(c); + + pa_log_set_fd(-1); + pa_xfree(c->script_commands); pa_xfree(c->dl_search_path); pa_xfree(c->default_script_file); @@ -225,6 +229,23 @@ int pa_daemon_conf_set_log_level(pa_daemon_conf *c, const char *string) { c->log_level = PA_LOG_WARN; else if (pa_startswith(string, "err")) c->log_level = PA_LOG_ERROR; + else if (pa_startswith(string, "file:")) { + char file_path[512]; + int log_fd; + + pa_strlcpy(file_path, string + 5, sizeof(file_path)); + + /* Open target file with user rights */ + if ((log_fd = open(file_path, O_RDWR|O_TRUNC|O_CREAT, S_IRWXU)) >= 0) { + c->auto_log_target = 0; + c->log_target = PA_LOG_FD; + pa_log_set_fd(log_fd); + } + else { + printf("Failed to open target file %s, error : %s\n", file_path, pa_cstrerror(errno)); + return -1; + } + } else return -1; @@ -307,8 +328,8 @@ static int parse_resample_method(const char *filename, unsigned line, const char return 0; } -static int parse_rlimit(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata) { #ifdef HAVE_SYS_RESOURCE_H +static int parse_rlimit(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata) { struct pa_rlimit *r = data; pa_assert(filename); @@ -329,12 +350,10 @@ static int parse_rlimit(const char *filename, unsigned line, const char *section r->is_set = k >= 0; r->value = k >= 0 ? (rlim_t) k : 0; } -#else - pa_log_warn(_("[%s:%u] rlimit not supported on this platform."), filename, line); -#endif return 0; } +#endif static int parse_sample_format(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata) { pa_daemon_conf *c = data; diff --git a/src/daemon/default.pa.in b/src/daemon/default.pa.in index ace0f09e..32ba5826 100755 --- a/src/daemon/default.pa.in +++ b/src/daemon/default.pa.in @@ -39,7 +39,7 @@ load-module module-card-restore load-module module-augment-properties ### Load audio drivers statically (it's probably better to not load -### these drivers manually, but instead use module-hal-detect -- +### these drivers manually, but instead use module-udev-detect -- ### see below -- for doing this automatically) #load-module module-alsa-sink #load-module module-alsa-source device=hw:1,0 diff --git a/src/daemon/esdcompat.in b/src/daemon/esdcompat.in index 66501803..41a12a02 100755 --- a/src/daemon/esdcompat.in +++ b/src/daemon/esdcompat.in @@ -59,7 +59,7 @@ Ignored directives: -terminate terminate esd daemone after last client exits -nobeeps disable startup beeps -trust start esd even if use of /tmp/.esd can be insecure - -port PORT listen for connections at PORT (only for tcp/ip) + -port PORT listen for connections at PORT (only for tcp/ip) -bind ADDRESS binds to ADDRESS (only for tcp/ip) EOF exit 0 diff --git a/src/daemon/main.c b/src/daemon/main.c index 243e7c0a..533c4c32 100644 --- a/src/daemon/main.c +++ b/src/daemon/main.c @@ -966,6 +966,7 @@ int main(int argc, char *argv[]) { c->cpu_info.cpu_type = PA_CPU_X86; if (pa_cpu_init_arm(&(c->cpu_info.flags.arm))) c->cpu_info.cpu_type = PA_CPU_ARM; + pa_cpu_init_orc(c->cpu_info); } pa_assert_se(pa_signal_init(pa_mainloop_get_api(mainloop)) == 0); diff --git a/src/daemon/system.pa.in b/src/daemon/system.pa.in index 0ca32bd3..aaefd1d1 100755 --- a/src/daemon/system.pa.in +++ b/src/daemon/system.pa.in @@ -20,11 +20,11 @@ # mode. ### Automatically load driver modules depending on the hardware available -.ifexists module-hal-detect@PA_SOEXT@ -load-module module-hal-detect +.ifexists module-udev-detect@PA_SOEXT@ +load-module module-udev-detect .else ### Alternatively use the static hardware detection module (for systems that -### lack HAL support) +### lack udev support) load-module module-detect .endif diff --git a/src/modules/.gitignore b/src/modules/.gitignore deleted file mode 100644 index 2d2d942d..00000000 --- a/src/modules/.gitignore +++ /dev/null @@ -1 +0,0 @@ -module-*-symdef.h diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c index 946cbe24..3eef5f9c 100644 --- a/src/modules/alsa/alsa-mixer.c +++ b/src/modules/alsa/alsa-mixer.c @@ -491,6 +491,15 @@ static void option_free(pa_alsa_option *o) { pa_xfree(o); } +static void decibel_fix_free(pa_alsa_decibel_fix *db_fix) { + pa_assert(db_fix); + + pa_xfree(db_fix->name); + pa_xfree(db_fix->db_values); + + pa_xfree(db_fix); +} + static void element_free(pa_alsa_element *e) { pa_alsa_option *o; pa_assert(e); @@ -500,6 +509,9 @@ static void element_free(pa_alsa_element *e) { option_free(o); } + if (e->db_fix) + decibel_fix_free(e->db_fix); + pa_xfree(e->alsa_name); pa_xfree(e); } @@ -593,14 +605,60 @@ static int element_get_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_chann long value = 0; if (e->direction == PA_ALSA_DIRECTION_OUTPUT) { - if (snd_mixer_selem_has_playback_channel(me, c)) - r = snd_mixer_selem_get_playback_dB(me, c, &value); - else + if (snd_mixer_selem_has_playback_channel(me, c)) { + if (e->db_fix) { + if ((r = snd_mixer_selem_get_playback_volume(me, c, &value)) >= 0) { + /* If the channel volume is outside the limits set + * by the dB fix, we clamp the hw volume to be + * within the limits. */ + if (value < e->db_fix->min_step) { + value = e->db_fix->min_step; + snd_mixer_selem_set_playback_volume(me, c, value); + pa_log_debug("Playback volume for element %s channel %i was below the dB fix limit. " + "Volume reset to %0.2f dB.", e->alsa_name, c, + e->db_fix->db_values[value - e->db_fix->min_step] / 100.0); + } else if (value > e->db_fix->max_step) { + value = e->db_fix->max_step; + snd_mixer_selem_set_playback_volume(me, c, value); + pa_log_debug("Playback volume for element %s channel %i was over the dB fix limit. " + "Volume reset to %0.2f dB.", e->alsa_name, c, + e->db_fix->db_values[value - e->db_fix->min_step] / 100.0); + } + + /* Volume step -> dB value conversion. */ + value = e->db_fix->db_values[value - e->db_fix->min_step]; + } + } else + r = snd_mixer_selem_get_playback_dB(me, c, &value); + } else r = -1; } else { - if (snd_mixer_selem_has_capture_channel(me, c)) - r = snd_mixer_selem_get_capture_dB(me, c, &value); - else + if (snd_mixer_selem_has_capture_channel(me, c)) { + if (e->db_fix) { + if ((r = snd_mixer_selem_get_capture_volume(me, c, &value)) >= 0) { + /* If the channel volume is outside the limits set + * by the dB fix, we clamp the hw volume to be + * within the limits. */ + if (value < e->db_fix->min_step) { + value = e->db_fix->min_step; + snd_mixer_selem_set_capture_volume(me, c, value); + pa_log_debug("Capture volume for element %s channel %i was below the dB fix limit. " + "Volume reset to %0.2f dB.", e->alsa_name, c, + e->db_fix->db_values[value - e->db_fix->min_step] / 100.0); + } else if (value > e->db_fix->max_step) { + value = e->db_fix->max_step; + snd_mixer_selem_set_capture_volume(me, c, value); + pa_log_debug("Capture volume for element %s channel %i was over the dB fix limit. " + "Volume reset to %0.2f dB.", e->alsa_name, c, + e->db_fix->db_values[value - e->db_fix->min_step] / 100.0); + } + + /* Volume step -> dB value conversion. */ + value = e->db_fix->db_values[value - e->db_fix->min_step]; + } + } else + r = snd_mixer_selem_get_capture_dB(me, c, &value); + } else r = -1; } @@ -760,6 +818,37 @@ int pa_alsa_path_get_mute(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t *muted) { return 0; } +/* Finds the closest item in db_fix->db_values and returns the corresponding + * step. *db_value is replaced with the value from the db_values table. + * Rounding is done based on the rounding parameter: -1 means rounding down and + * +1 means rounding up. */ +static long decibel_fix_get_step(pa_alsa_decibel_fix *db_fix, long *db_value, int rounding) { + unsigned i = 0; + unsigned max_i = 0; + + pa_assert(db_fix); + pa_assert(db_value); + pa_assert(rounding != 0); + + max_i = db_fix->max_step - db_fix->min_step; + + if (rounding > 0) { + for (i = 0; i < max_i; i++) { + if (db_fix->db_values[i] >= *db_value) + break; + } + } else { + for (i = 0; i < max_i; i++) { + if (db_fix->db_values[i + 1] > *db_value) + break; + } + } + + *db_value = db_fix->db_values[i]; + + return i + db_fix->min_step; +} + static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v, pa_bool_t write_to_hw) { snd_mixer_selem_id_t *sid; @@ -804,31 +893,55 @@ static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_chann if (e->has_dB) { long value = to_alsa_dB(f); + int rounding = value > 0 ? -1 : +1; + + if (e->volume_limit >= 0 && value > (e->max_dB * 100)) + value = e->max_dB * 100; if (e->direction == PA_ALSA_DIRECTION_OUTPUT) { - /* If we call set_play_volume() without checking first - * if the channel is available, ALSA behaves ver + /* If we call set_playback_volume() without checking first + * if the channel is available, ALSA behaves very * strangely and doesn't fail the call */ if (snd_mixer_selem_has_playback_channel(me, c)) { - if (write_to_hw) { - if ((r = snd_mixer_selem_set_playback_dB(me, c, value, +1)) >= 0) - r = snd_mixer_selem_get_playback_dB(me, c, &value); + if (e->db_fix) { + if (write_to_hw) + r = snd_mixer_selem_set_playback_volume(me, c, decibel_fix_get_step(e->db_fix, &value, rounding)); + else { + decibel_fix_get_step(e->db_fix, &value, rounding); + r = 0; + } + } else { - long alsa_val; - if ((r = snd_mixer_selem_ask_playback_dB_vol(me, value, +1, &alsa_val)) >= 0) - r = snd_mixer_selem_ask_playback_vol_dB(me, alsa_val, &value); + if (write_to_hw) { + if ((r = snd_mixer_selem_set_playback_dB(me, c, value, rounding)) >= 0) + r = snd_mixer_selem_get_playback_dB(me, c, &value); + } else { + long alsa_val; + if ((r = snd_mixer_selem_ask_playback_dB_vol(me, value, rounding, &alsa_val)) >= 0) + r = snd_mixer_selem_ask_playback_vol_dB(me, alsa_val, &value); + } } } else r = -1; } else { if (snd_mixer_selem_has_capture_channel(me, c)) { - if (write_to_hw) { - if ((r = snd_mixer_selem_set_capture_dB(me, c, value, +1)) >= 0) - r = snd_mixer_selem_get_capture_dB(me, c, &value); + if (e->db_fix) { + if (write_to_hw) + r = snd_mixer_selem_set_capture_volume(me, c, decibel_fix_get_step(e->db_fix, &value, rounding)); + else { + decibel_fix_get_step(e->db_fix, &value, rounding); + r = 0; + } + } else { - long alsa_val; - if ((r = snd_mixer_selem_ask_capture_dB_vol(me, value, +1, &alsa_val)) >= 0) - r = snd_mixer_selem_ask_capture_vol_dB(me, alsa_val, &value); + if (write_to_hw) { + if ((r = snd_mixer_selem_set_capture_dB(me, c, value, rounding)) >= 0) + r = snd_mixer_selem_get_capture_dB(me, c, &value); + } else { + long alsa_val; + if ((r = snd_mixer_selem_ask_capture_dB_vol(me, value, rounding, &alsa_val)) >= 0) + r = snd_mixer_selem_ask_capture_vol_dB(me, alsa_val, &value); + } } } else r = -1; @@ -1012,9 +1125,19 @@ static int element_zero_volume(pa_alsa_element *e, snd_mixer_t *m) { } if (e->direction == PA_ALSA_DIRECTION_OUTPUT) - r = snd_mixer_selem_set_playback_dB_all(me, 0, +1); + if (e->db_fix) { + long value = 0; + + r = snd_mixer_selem_set_playback_volume_all(me, decibel_fix_get_step(e->db_fix, &value, +1)); + } else + r = snd_mixer_selem_set_playback_dB_all(me, 0, +1); else - r = snd_mixer_selem_set_capture_dB_all(me, 0, +1); + if (e->db_fix) { + long value = 0; + + r = snd_mixer_selem_set_capture_volume_all(me, decibel_fix_get_step(e->db_fix, &value, +1)); + } else + r = snd_mixer_selem_set_capture_dB_all(me, 0, +1); if (r < 0) pa_log_warn("Failed to set volume to 0dB of %s: %s", e->alsa_name, pa_alsa_strerror(errno)); @@ -1121,6 +1244,40 @@ static int check_required(pa_alsa_element *e, snd_mixer_elem_t *me) { if (e->required_absent == PA_ALSA_REQUIRED_ANY && (has_switch || has_volume || has_enumeration)) return -1; + if (e->required_any != PA_ALSA_REQUIRED_IGNORE) { + switch (e->required_any) { + case PA_ALSA_REQUIRED_VOLUME: + e->path->req_any_present |= (e->volume_use != PA_ALSA_VOLUME_IGNORE); + break; + case PA_ALSA_REQUIRED_SWITCH: + e->path->req_any_present |= (e->switch_use != PA_ALSA_SWITCH_IGNORE); + break; + case PA_ALSA_REQUIRED_ENUMERATION: + e->path->req_any_present |= (e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE); + break; + case PA_ALSA_REQUIRED_ANY: + e->path->req_any_present |= + (e->volume_use != PA_ALSA_VOLUME_IGNORE) || + (e->switch_use != PA_ALSA_SWITCH_IGNORE) || + (e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE); + break; + default: + pa_assert_not_reached(); + } + } + + if (e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) { + pa_alsa_option *o; + PA_LLIST_FOREACH(o, e->options) { + e->path->req_any_present |= (o->required_any != PA_ALSA_REQUIRED_IGNORE) && + (o->alsa_idx >= 0); + if (o->required != PA_ALSA_REQUIRED_IGNORE && o->alsa_idx < 0) + return -1; + if (o->required_absent != PA_ALSA_REQUIRED_IGNORE && o->alsa_idx >= 0) + return -1; + } + } + return 0; } @@ -1130,6 +1287,7 @@ static int element_probe(pa_alsa_element *e, snd_mixer_t *m) { pa_assert(m); pa_assert(e); + pa_assert(e->path); SELEM_INIT(sid, e->alsa_name); @@ -1197,26 +1355,6 @@ static int element_probe(pa_alsa_element *e, snd_mixer_t *m) { e->direction_try_other = FALSE; if (e->direction == PA_ALSA_DIRECTION_OUTPUT) - e->has_dB = snd_mixer_selem_get_playback_dB_range(me, &min_dB, &max_dB) >= 0; - else - e->has_dB = snd_mixer_selem_get_capture_dB_range(me, &min_dB, &max_dB) >= 0; - - if (e->has_dB) { -#ifdef HAVE_VALGRIND_MEMCHECK_H - VALGRIND_MAKE_MEM_DEFINED(&min_dB, sizeof(min_dB)); - VALGRIND_MAKE_MEM_DEFINED(&max_dB, sizeof(max_dB)); -#endif - - e->min_dB = ((double) min_dB) / 100.0; - e->max_dB = ((double) max_dB) / 100.0; - - if (min_dB >= max_dB) { - pa_log_warn("Your kernel driver is broken: it reports a volume range from %0.2f dB to %0.2f dB which makes no sense.", e->min_dB, e->max_dB); - e->has_dB = FALSE; - } - } - - if (e->direction == PA_ALSA_DIRECTION_OUTPUT) r = snd_mixer_selem_get_playback_volume_range(me, &e->min_volume, &e->max_volume); else r = snd_mixer_selem_get_capture_volume_range(me, &e->min_volume, &e->max_volume); @@ -1226,7 +1364,6 @@ static int element_probe(pa_alsa_element *e, snd_mixer_t *m) { return -1; } - if (e->min_volume >= e->max_volume) { pa_log_warn("Your kernel driver is broken: it reports a volume range from %li to %li which makes no sense.", e->min_volume, e->max_volume); e->volume_use = PA_ALSA_VOLUME_IGNORE; @@ -1235,6 +1372,75 @@ static int element_probe(pa_alsa_element *e, snd_mixer_t *m) { pa_bool_t is_mono; pa_channel_position_t p; + if (e->db_fix && + ((e->min_volume > e->db_fix->min_step) || + (e->max_volume < e->db_fix->max_step))) { + pa_log_warn("The step range of the decibel fix for element %s (%li-%li) doesn't fit to the " + "real hardware range (%li-%li). Disabling the decibel fix.", e->alsa_name, + e->db_fix->min_step, e->db_fix->max_step, + e->min_volume, e->max_volume); + + decibel_fix_free(e->db_fix); + e->db_fix = NULL; + } + + if (e->db_fix) { + e->has_dB = TRUE; + e->min_volume = e->db_fix->min_step; + e->max_volume = e->db_fix->max_step; + min_dB = e->db_fix->db_values[0]; + max_dB = e->db_fix->db_values[e->db_fix->max_step - e->db_fix->min_step]; + } else if (e->direction == PA_ALSA_DIRECTION_OUTPUT) + e->has_dB = snd_mixer_selem_get_playback_dB_range(me, &min_dB, &max_dB) >= 0; + else + e->has_dB = snd_mixer_selem_get_capture_dB_range(me, &min_dB, &max_dB) >= 0; + + if (e->has_dB) { +#ifdef HAVE_VALGRIND_MEMCHECK_H + VALGRIND_MAKE_MEM_DEFINED(&min_dB, sizeof(min_dB)); + VALGRIND_MAKE_MEM_DEFINED(&max_dB, sizeof(max_dB)); +#endif + + e->min_dB = ((double) min_dB) / 100.0; + e->max_dB = ((double) max_dB) / 100.0; + + if (min_dB >= max_dB) { + pa_assert(!e->db_fix); + pa_log_warn("Your kernel driver is broken: it reports a volume range from %0.2f dB to %0.2f dB which makes no sense.", e->min_dB, e->max_dB); + e->has_dB = FALSE; + } + } + + if (e->volume_limit >= 0) { + if (e->volume_limit <= e->min_volume || e->volume_limit > e->max_volume) + pa_log_warn("Volume limit for element %s of path %s is invalid: %li isn't within the valid range " + "%li-%li. The volume limit is ignored.", + e->alsa_name, e->path->name, e->volume_limit, e->min_volume + 1, e->max_volume); + + else { + e->max_volume = e->volume_limit; + + if (e->has_dB) { + if (e->db_fix) { + e->db_fix->max_step = e->max_volume; + e->max_dB = ((double) e->db_fix->db_values[e->db_fix->max_step - e->db_fix->min_step]) / 100.0; + + } else { + if (e->direction == PA_ALSA_DIRECTION_OUTPUT) + r = snd_mixer_selem_ask_playback_vol_dB(me, e->max_volume, &max_dB); + else + r = snd_mixer_selem_ask_capture_vol_dB(me, e->max_volume, &max_dB); + + if (r < 0) { + pa_log_warn("Failed to get dB value of %s: %s", e->alsa_name, pa_alsa_strerror(r)); + e->has_dB = FALSE; + } else + e->max_dB = ((double) max_dB) / 100.0; + } + } + } + } + if (e->direction == PA_ALSA_DIRECTION_OUTPUT) is_mono = snd_mixer_selem_is_playback_mono(me) > 0; else @@ -1293,9 +1499,6 @@ static int element_probe(pa_alsa_element *e, snd_mixer_t *m) { } - if (check_required(e, me) < 0) - return -1; - if (e->switch_use == PA_ALSA_SWITCH_SELECT) { pa_alsa_option *o; @@ -1327,6 +1530,9 @@ static int element_probe(pa_alsa_element *e, snd_mixer_t *m) { } } + if (check_required(e, me) < 0) + return -1; + return 0; } @@ -1358,6 +1564,7 @@ static pa_alsa_element* element_get(pa_alsa_path *p, const char *section, pa_boo e->path = p; e->alsa_name = pa_xstrdup(section); e->direction = p->direction; + e->volume_limit = -1; PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e); @@ -1581,20 +1788,23 @@ static int element_parse_required( pa_alsa_path *p = userdata; pa_alsa_element *e; + pa_alsa_option *o; pa_alsa_required_t req; pa_assert(p); - if (!(e = element_get(p, section, TRUE))) { + e = element_get(p, section, TRUE); + o = option_get(p, section); + if (!e && !o) { pa_log("[%s:%u] Required makes no sense in '%s'", filename, line, section); return -1; } if (pa_streq(rvalue, "ignore")) req = PA_ALSA_REQUIRED_IGNORE; - else if (pa_streq(rvalue, "switch")) + else if (pa_streq(rvalue, "switch") && e) req = PA_ALSA_REQUIRED_SWITCH; - else if (pa_streq(rvalue, "volume")) + else if (pa_streq(rvalue, "volume") && e) req = PA_ALSA_REQUIRED_VOLUME; else if (pa_streq(rvalue, "enumeration")) req = PA_ALSA_REQUIRED_ENUMERATION; @@ -1605,10 +1815,28 @@ static int element_parse_required( return -1; } - if (pa_streq(lvalue, "required-absent")) - e->required_absent = req; - else - e->required = req; + if (pa_streq(lvalue, "required-absent")) { + if (e) + e->required_absent = req; + if (o) + o->required_absent = req; + } + else if (pa_streq(lvalue, "required-any")) { + if (e) { + e->required_any = req; + e->path->has_req_any = TRUE; + } + if (o) { + o->required_any = req; + o->element->path->has_req_any = TRUE; + } + } + else { + if (e) + e->required = req; + if (o) + o->required = req; + } return 0; } @@ -1671,6 +1899,33 @@ static int element_parse_direction_try_other( return 0; } +static int element_parse_volume_limit( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + const char *rvalue, + void *data, + void *userdata) { + + pa_alsa_path *p = userdata; + pa_alsa_element *e; + uint32_t volume_limit; + + if (!(e = element_get(p, section, TRUE))) { + pa_log("[%s:%u] volume-limit makes no sense in '%s'", filename, line, section); + return -1; + } + + if (pa_atou(rvalue, &volume_limit) < 0 || volume_limit > LONG_MAX) { + pa_log("[%s:%u] Invalid value for volume-limit", filename, line); + return -1; + } + + e->volume_limit = volume_limit; + return 0; +} + static pa_channel_position_mask_t parse_mask(const char *m) { pa_channel_position_mask_t v; @@ -1804,8 +2059,11 @@ static int option_verify(pa_alsa_option *o) { { "input", N_("Input") }, { "input-docking", N_("Docking Station Input") }, { "input-docking-microphone", N_("Docking Station Microphone") }, + { "input-docking-linein", N_("Docking Station Line-In") }, { "input-linein", N_("Line-In") }, { "input-microphone", N_("Microphone") }, + { "input-microphone-front", N_("Front Microphone") }, + { "input-microphone-rear", N_("Rear Microphone") }, { "input-microphone-external", N_("External Microphone") }, { "input-microphone-internal", N_("Internal Microphone") }, { "input-radio", N_("Radio") }, @@ -1857,7 +2115,10 @@ static int element_verify(pa_alsa_element *e) { pa_assert(e); +// pa_log_debug("Element %s, path %s: r=%d, r-any=%d, r-abs=%d", e->alsa_name, e->path->name, e->required, e->required_any, e->required_absent); if ((e->required != PA_ALSA_REQUIRED_IGNORE && e->required == e->required_absent) || + (e->required_any != PA_ALSA_REQUIRED_IGNORE && e->required_any == e->required_absent) || + (e->required_absent == PA_ALSA_REQUIRED_ANY && e->required_any != PA_ALSA_REQUIRED_IGNORE) || (e->required_absent == PA_ALSA_REQUIRED_ANY && e->required != PA_ALSA_REQUIRED_IGNORE)) { pa_log("Element %s cannot be required and absent at the same time.", e->alsa_name); return -1; @@ -1879,6 +2140,10 @@ static int path_verify(pa_alsa_path *p) { static const struct description_map well_known_descriptions[] = { { "analog-input", N_("Analog Input") }, { "analog-input-microphone", N_("Analog Microphone") }, + { "analog-input-microphone-front", N_("Front Microphone") }, + { "analog-input-microphone-rear", N_("Rear Microphone") }, + { "analog-input-microphone-dock", N_("Docking Station Microphone") }, + { "analog-input-microphone-internal", N_("Internal Microphone") }, { "analog-input-linein", N_("Analog Line-In") }, { "analog-input-radio", N_("Analog Radio") }, { "analog-input-video", N_("Analog Video") }, @@ -1934,9 +2199,11 @@ pa_alsa_path* pa_alsa_path_new(const char *fname, pa_alsa_direction_t direction) { "override-map.2", element_parse_override_map, NULL, NULL }, /* ... later on we might add override-map.3 and so on here ... */ { "required", element_parse_required, NULL, NULL }, + { "required-any", element_parse_required, NULL, NULL }, { "required-absent", element_parse_required, NULL, NULL }, { "direction", element_parse_direction, NULL, NULL }, { "direction-try-other", element_parse_direction_try_other, NULL, NULL }, + { "volume-limit", element_parse_volume_limit, NULL, NULL }, { NULL, NULL, NULL, NULL } }; @@ -1987,11 +2254,13 @@ pa_alsa_path* pa_alsa_path_synthesize(const char*element, pa_alsa_direction_t di e->path = p; e->alsa_name = pa_xstrdup(element); e->direction = direction; + e->volume_limit = -1; e->switch_use = PA_ALSA_SWITCH_MUTE; e->volume_use = PA_ALSA_VOLUME_MERGE; PA_LLIST_PREPEND(pa_alsa_element, p->elements, e); + p->last_element = e; return p; } @@ -2131,6 +2400,7 @@ int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t ignore_dB) { pa_alsa_element *e; double min_dB[PA_CHANNEL_POSITION_MAX], max_dB[PA_CHANNEL_POSITION_MAX]; pa_channel_position_t t; + pa_channel_position_mask_t path_volume_channels = 0; pa_assert(p); pa_assert(m); @@ -2149,6 +2419,7 @@ int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t ignore_dB) { pa_log_debug("Probe of element '%s' failed.", e->alsa_name); return -1; } + pa_log_debug("Probe of element '%s' succeeded (volume=%d, switch=%d, enumeration=%d).", e->alsa_name, e->volume_use, e->switch_use, e->enumeration_use); if (ignore_dB) e->has_dB = FALSE; @@ -2166,6 +2437,7 @@ int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t ignore_dB) { if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) { min_dB[t] = e->min_dB; max_dB[t] = e->max_dB; + path_volume_channels |= PA_CHANNEL_POSITION_MASK(t); } p->has_dB = TRUE; @@ -2176,17 +2448,21 @@ int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t ignore_dB) { if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) { min_dB[t] += e->min_dB; max_dB[t] += e->max_dB; + path_volume_channels |= PA_CHANNEL_POSITION_MASK(t); } - } else + } else { /* Hmm, there's another element before us * which cannot do dB volumes, so we we need * to 'neutralize' this slider */ e->volume_use = PA_ALSA_VOLUME_ZERO; + pa_log_info("Zeroing volume of '%s' on path '%s'", e->alsa_name, p->name); + } } - } else if (p->has_volume) + } else if (p->has_volume) { /* We can't use this volume, so let's ignore it */ e->volume_use = PA_ALSA_VOLUME_IGNORE; - + pa_log_info("Ignoring volume of '%s' on path '%s' (missing dB info)", e->alsa_name, p->name); + } p->has_volume = TRUE; } @@ -2194,6 +2470,12 @@ int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t ignore_dB) { p->has_mute = TRUE; } + if (p->has_req_any && !p->req_any_present) { + p->supported = FALSE; + pa_log_debug("Skipping path '%s', none of required-any elements preset.", p->name); + return -1; + } + path_drop_unsupported(p); path_make_options_unique(p); path_create_settings(p); @@ -2205,11 +2487,13 @@ int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t ignore_dB) { p->max_dB = -INFINITY; for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++) { - if (p->min_dB > min_dB[t]) - p->min_dB = min_dB[t]; + if (path_volume_channels & PA_CHANNEL_POSITION_MASK(t)) { + if (p->min_dB > min_dB[t]) + p->min_dB = min_dB[t]; - if (p->max_dB < max_dB[t]) - p->max_dB = max_dB[t]; + if (p->max_dB < max_dB[t]) + p->max_dB = max_dB[t]; + } } return 0; @@ -2239,13 +2523,15 @@ void pa_alsa_element_dump(pa_alsa_element *e) { pa_alsa_option *o; pa_assert(e); - pa_log_debug("Element %s, direction=%i, switch=%i, volume=%i, enumeration=%i, required=%i, required_absent=%i, mask=0x%llx, n_channels=%u, override_map=%s", + pa_log_debug("Element %s, direction=%i, switch=%i, volume=%i, volume_limit=%li, enumeration=%i, required=%i, required_any=%i, required_absent=%i, mask=0x%llx, n_channels=%u, override_map=%s", e->alsa_name, e->direction, e->switch_use, e->volume_use, + e->volume_limit, e->enumeration_use, e->required, + e->required_any, e->required_absent, (long long unsigned) e->merged_mask, e->n_channels, @@ -2324,8 +2610,12 @@ void pa_alsa_path_set_set_callback(pa_alsa_path_set *ps, snd_mixer_t *m, snd_mix pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t direction) { pa_alsa_path_set *ps; char **pn = NULL, **en = NULL, **ie; + pa_alsa_decibel_fix *db_fix; + void *state; pa_assert(m); + pa_assert(m->profile_set); + pa_assert(m->profile_set->decibel_fixes); pa_assert(direction == PA_ALSA_DIRECTION_OUTPUT || direction == PA_ALSA_DIRECTION_INPUT); if (m->direction != PA_ALSA_DIRECTION_ANY && m->direction != direction) @@ -2367,7 +2657,7 @@ pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t d pa_xfree(fn); } - return ps; + goto finish; } if (direction == PA_ALSA_DIRECTION_OUTPUT) @@ -2390,11 +2680,16 @@ pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t d /* Mark all other passed elements for require-absent */ for (je = en; *je; je++) { pa_alsa_element *e; + + if (je == ie) + continue; + e = pa_xnew0(pa_alsa_element, 1); e->path = p; e->alsa_name = pa_xstrdup(*je); e->direction = direction; e->required_absent = PA_ALSA_REQUIRED_ANY; + e->volume_limit = -1; PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e); p->last_element = e; @@ -2404,6 +2699,28 @@ pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t d ps->last_path = p; } +finish: + /* Assign decibel fixes to elements. */ + PA_HASHMAP_FOREACH(db_fix, m->profile_set->decibel_fixes, state) { + pa_alsa_path *p; + + PA_LLIST_FOREACH(p, ps->paths) { + pa_alsa_element *e; + + PA_LLIST_FOREACH(e, p->elements) { + if (e->volume_use != PA_ALSA_VOLUME_IGNORE && pa_streq(db_fix->name, e->alsa_name)) { + /* The profile set that contains the dB fix may be freed + * before the element, so we have to copy the dB fix + * object. */ + e->db_fix = pa_xnewdup(pa_alsa_decibel_fix, db_fix, 1); + e->db_fix->profile_set = NULL; + e->db_fix->name = pa_xstrdup(db_fix->name); + e->db_fix->db_values = pa_xmemdup(db_fix->db_values, (db_fix->max_step - db_fix->min_step + 1) * sizeof(long)); + } + } + } + } + return ps; } @@ -2580,6 +2897,15 @@ void pa_alsa_profile_set_free(pa_alsa_profile_set *ps) { pa_hashmap_free(ps->mappings, NULL, NULL); } + if (ps->decibel_fixes) { + pa_alsa_decibel_fix *db_fix; + + while ((db_fix = pa_hashmap_steal_first(ps->decibel_fixes))) + decibel_fix_free(db_fix); + + pa_hashmap_free(ps->decibel_fixes, NULL, NULL); + } + pa_xfree(ps); } @@ -2624,6 +2950,26 @@ static pa_alsa_profile *profile_get(pa_alsa_profile_set *ps, const char *name) { return p; } +static pa_alsa_decibel_fix *decibel_fix_get(pa_alsa_profile_set *ps, const char *name) { + pa_alsa_decibel_fix *db_fix; + + if (!pa_startswith(name, "DecibelFix ")) + return NULL; + + name += 11; + + if ((db_fix = pa_hashmap_get(ps->decibel_fixes, name))) + return db_fix; + + db_fix = pa_xnew0(pa_alsa_decibel_fix, 1); + db_fix->profile_set = ps; + db_fix->name = pa_xstrdup(name); + + pa_hashmap_put(ps->decibel_fixes, db_fix->name, db_fix); + + return db_fix; +} + static int mapping_parse_device_strings( const char *filename, unsigned line, @@ -2894,6 +3240,130 @@ static int profile_parse_skip_probe( return 0; } +static int decibel_fix_parse_db_values( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + const char *rvalue, + void *data, + void *userdata) { + + pa_alsa_profile_set *ps = userdata; + pa_alsa_decibel_fix *db_fix; + char **items; + char *item; + long *db_values; + unsigned n = 8; /* Current size of the db_values table. */ + unsigned min_step = 0; + unsigned max_step = 0; + unsigned i = 0; /* Index to the items table. */ + unsigned prev_step = 0; + double prev_db = 0; + + pa_assert(filename); + pa_assert(section); + pa_assert(lvalue); + pa_assert(rvalue); + pa_assert(ps); + + if (!(db_fix = decibel_fix_get(ps, section))) { + pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section); + return -1; + } + + if (!(items = pa_split_spaces_strv(rvalue))) { + pa_log("[%s:%u] Value missing", pa_strnull(filename), line); + return -1; + } + + db_values = pa_xnew(long, n); + + while ((item = items[i++])) { + char *s = item; /* Step value string. */ + char *d = item; /* dB value string. */ + uint32_t step; + double db; + + /* Move d forward until it points to a colon or to the end of the item. */ + for (; *d && *d != ':'; ++d); + + if (d == s) { + /* item started with colon. */ + pa_log("[%s:%u] No step value found in %s", filename, line, item); + goto fail; + } + + if (!*d || !*(d + 1)) { + /* No colon found, or it was the last character in item. */ + pa_log("[%s:%u] No dB value found in %s", filename, line, item); + goto fail; + } + + /* pa_atou() needs a null-terminating string. Let's replace the colon + * with a zero byte. */ + *d++ = '\0'; + + if (pa_atou(s, &step) < 0) { + pa_log("[%s:%u] Invalid step value: %s", filename, line, s); + goto fail; + } + + if (pa_atod(d, &db) < 0) { + pa_log("[%s:%u] Invalid dB value: %s", filename, line, d); + goto fail; + } + + if (step <= prev_step && i != 1) { + pa_log("[%s:%u] Step value %u not greater than the previous value %u", filename, line, step, prev_step); + goto fail; + } + + if (db < prev_db && i != 1) { + pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", filename, line, db, prev_db); + goto fail; + } + + if (i == 1) { + min_step = step; + db_values[0] = (long) (db * 100.0); + prev_step = step; + prev_db = db; + } else { + /* Interpolate linearly. */ + double db_increment = (db - prev_db) / (step - prev_step); + + for (; prev_step < step; ++prev_step, prev_db += db_increment) { + + /* Reallocate the db_values table if it's about to overflow. */ + if (prev_step + 1 - min_step == n) { + n *= 2; + db_values = pa_xrenew(long, db_values, n); + } + + db_values[prev_step + 1 - min_step] = (long) ((prev_db + db_increment) * 100.0); + } + } + + max_step = step; + } + + db_fix->min_step = min_step; + db_fix->max_step = max_step; + pa_xfree(db_fix->db_values); + db_fix->db_values = db_values; + + pa_xstrfreev(items); + + return 0; + +fail: + pa_xstrfreev(items); + pa_xfree(db_values); + + return -1; +} + static int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) { static const struct description_map well_known_descriptions[] = { @@ -2931,7 +3401,7 @@ static int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) { if ((m->input_path_names && m->input_element) || (m->output_path_names && m->output_element)) { - pa_log("Mapping %s must have either mixer path or mixer elment, not both.", m->name); + pa_log("Mapping %s must have either mixer path or mixer element, not both.", m->name); return -1; } @@ -3176,10 +3646,52 @@ void pa_alsa_profile_dump(pa_alsa_profile *p) { pa_log_debug("Output %s", m->name); } +static int decibel_fix_verify(pa_alsa_decibel_fix *db_fix) { + pa_assert(db_fix); + + /* Check that the dB mapping has been configured. Since "db-values" is + * currently the only option in the DecibelFix section, and decibel fix + * objects don't get created if a DecibelFix section is empty, this is + * actually a redundant check. Having this may prevent future bugs, + * however. */ + if (!db_fix->db_values) { + pa_log("Decibel fix for element %s lacks the dB values.", db_fix->name); + return -1; + } + + return 0; +} + +void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix *db_fix) { + char *db_values = NULL; + + pa_assert(db_fix); + + if (db_fix->db_values) { + pa_strbuf *buf; + long i; + long max_i = db_fix->max_step - db_fix->min_step; + + buf = pa_strbuf_new(); + pa_strbuf_printf(buf, "[%li]:%0.2f", db_fix->min_step, db_fix->db_values[0] / 100.0); + + for (i = 1; i <= max_i; ++i) + pa_strbuf_printf(buf, " [%li]:%0.2f", i + db_fix->min_step, db_fix->db_values[i] / 100.0); + + db_values = pa_strbuf_tostring_free(buf); + } + + pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s", + db_fix->name, db_fix->min_step, db_fix->max_step, pa_strnull(db_values)); + + pa_xfree(db_values); +} + pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus) { pa_alsa_profile_set *ps; pa_alsa_profile *p; pa_alsa_mapping *m; + pa_alsa_decibel_fix *db_fix; char *fn; int r; void *state; @@ -3205,12 +3717,16 @@ pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel { "input-mappings", profile_parse_mappings, NULL, NULL }, { "output-mappings", profile_parse_mappings, NULL, NULL }, { "skip-probe", profile_parse_skip_probe, NULL, NULL }, + + /* [DecibelFix ...] */ + { "db-values", decibel_fix_parse_db_values, NULL, NULL }, { NULL, NULL, NULL, NULL } }; ps = pa_xnew0(pa_alsa_profile_set, 1); ps->mappings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); ps->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + ps->decibel_fixes = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); items[0].data = &ps->auto_profiles; @@ -3240,6 +3756,10 @@ pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel if (profile_verify(p) < 0) goto fail; + PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state) + if (decibel_fix_verify(db_fix) < 0) + goto fail; + return ps; fail: @@ -3420,23 +3940,28 @@ void pa_alsa_profile_set_probe( void pa_alsa_profile_set_dump(pa_alsa_profile_set *ps) { pa_alsa_profile *p; pa_alsa_mapping *m; + pa_alsa_decibel_fix *db_fix; void *state; pa_assert(ps); - pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u", + pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u", (void*) ps, pa_yes_no(ps->auto_profiles), pa_yes_no(ps->probed), pa_hashmap_size(ps->mappings), - pa_hashmap_size(ps->profiles)); + pa_hashmap_size(ps->profiles), + pa_hashmap_size(ps->decibel_fixes)); PA_HASHMAP_FOREACH(m, ps->mappings, state) pa_alsa_mapping_dump(m); PA_HASHMAP_FOREACH(p, ps->profiles, state) pa_alsa_profile_dump(p); + + PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state) + pa_alsa_decibel_fix_dump(db_fix); } void pa_alsa_add_ports(pa_hashmap **p, pa_alsa_path_set *ps) { diff --git a/src/modules/alsa/alsa-mixer.h b/src/modules/alsa/alsa-mixer.h index 7fb408a6..c24a8965 100644 --- a/src/modules/alsa/alsa-mixer.h +++ b/src/modules/alsa/alsa-mixer.h @@ -46,6 +46,7 @@ typedef struct pa_alsa_path pa_alsa_path; typedef struct pa_alsa_path_set pa_alsa_path_set; typedef struct pa_alsa_mapping pa_alsa_mapping; typedef struct pa_alsa_profile pa_alsa_profile; +typedef struct pa_alsa_decibel_fix pa_alsa_decibel_fix; typedef struct pa_alsa_profile_set pa_alsa_profile_set; typedef struct pa_alsa_port_data pa_alsa_port_data; @@ -112,11 +113,15 @@ struct pa_alsa_option { char *name; char *description; unsigned priority; + + pa_alsa_required_t required; + pa_alsa_required_t required_any; + pa_alsa_required_t required_absent; }; -/* And element wraps one specific ALSA element. A series of elements * -make up a path (see below). If the element is an enumeration or switch -* element it may includes a list of options. */ +/* An element wraps one specific ALSA element. A series of elements + * make up a path (see below). If the element is an enumeration or switch + * element it may include a list of options. */ struct pa_alsa_element { pa_alsa_path *path; PA_LLIST_FIELDS(pa_alsa_element); @@ -129,6 +134,7 @@ struct pa_alsa_element { pa_alsa_enumeration_use_t enumeration_use; pa_alsa_required_t required; + pa_alsa_required_t required_any; pa_alsa_required_t required_absent; pa_bool_t override_map:1; @@ -136,6 +142,7 @@ struct pa_alsa_element { pa_bool_t has_dB:1; long min_volume, max_volume; + long volume_limit; /* -1 for no configured limit */ double min_dB, max_dB; pa_channel_position_mask_t masks[SND_MIXER_SCHN_LAST][2]; @@ -144,6 +151,8 @@ struct pa_alsa_element { pa_channel_position_mask_t merged_mask; PA_LLIST_HEAD(pa_alsa_option, options); + + pa_alsa_decibel_fix *db_fix; }; /* A path wraps a series of elements into a single entity which can be @@ -164,6 +173,9 @@ struct pa_alsa_path { pa_bool_t has_mute:1; pa_bool_t has_volume:1; pa_bool_t has_dB:1; + /* These two are used during probing only */ + pa_bool_t has_req_any:1; + pa_bool_t req_any_present:1; long min_volume, max_volume; double min_dB, max_dB; @@ -258,9 +270,26 @@ struct pa_alsa_profile { pa_idxset *output_mappings; }; +struct pa_alsa_decibel_fix { + pa_alsa_profile_set *profile_set; + + char *name; /* Alsa volume element name. */ + long min_step; + long max_step; + + /* An array that maps alsa volume element steps to decibels. The steps can + * be used as indices to this array, after substracting min_step from the + * real value. + * + * The values are actually stored as integers representing millibels, + * because that's the format the alsa API uses. */ + long *db_values; +}; + struct pa_alsa_profile_set { pa_hashmap *mappings; pa_hashmap *profiles; + pa_hashmap *decibel_fixes; pa_bool_t auto_profiles; pa_bool_t probed:1; @@ -268,6 +297,7 @@ struct pa_alsa_profile_set { void pa_alsa_mapping_dump(pa_alsa_mapping *m); void pa_alsa_profile_dump(pa_alsa_profile *p); +void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix *db_fix); pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus); void pa_alsa_profile_set_probe(pa_alsa_profile_set *ps, const char *dev_id, const pa_sample_spec *ss, unsigned default_n_fragments, unsigned default_fragment_size_msec); diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index 45a7af39..6d18e607 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -796,7 +796,7 @@ static void update_smoother(struct userdata *u) { } static pa_usec_t source_get_latency(struct userdata *u) { - int64_t delay; + int64_t delay; pa_usec_t now1, now2; pa_assert(u); diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c index 786e664d..6435db00 100644 --- a/src/modules/alsa/alsa-util.c +++ b/src/modules/alsa/alsa-util.c @@ -362,7 +362,7 @@ int pa_alsa_set_hw_params( pa_log_debug("Set neither period nor buffer size."); /* Last chance, set nothing */ - if ((ret = snd_pcm_hw_params(pcm_handle, hwparams)) < 0) { + if ((ret = snd_pcm_hw_params(pcm_handle, hwparams)) < 0) { pa_log_info("snd_pcm_hw_params failed: %s", pa_alsa_strerror(ret)); goto finish; } diff --git a/src/modules/alsa/mixer/paths/analog-input-dock-mic.conf b/src/modules/alsa/mixer/paths/analog-input-dock-mic.conf new file mode 100644 index 00000000..74826a96 --- /dev/null +++ b/src/modules/alsa/mixer/paths/analog-input-dock-mic.conf @@ -0,0 +1,81 @@ +# This file is part of PulseAudio. +# +# PulseAudio is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation; either version 2.1 of the +# License, or (at your option) any later version. +# +# PulseAudio is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with PulseAudio; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +; For devices where a 'Dock Mic' or 'Dock Mic Boost' element exists +; +; See analog-output.conf.common for an explanation on the directives + +[General] +priority = 80 +name = analog-input-microphone-dock + +[Element Dock Mic Boost] +required-any = any +switch = select +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +[Option Dock Mic Boost:on] +name = input-boost-on + +[Option Dock Mic Boost:off] +name = input-boost-off + +[Element Dock Mic] +required-any = any +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +[Element Capture] +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +[Element Input Source] +enumeration = select + +[Option Input Source:Dock Mic] +name = analog-input-microphone-dock +required-any = any + +[Element Capture Source] +enumeration = select + +[Option Capture Source:Dock Mic] +name = analog-input-microphone-dock +required-any = any + +[Element Mic] +switch = off +volume = off + +[Element Internal Mic] +switch = off +volume = off + +[Element Front Mic] +switch = off +volume = off + +[Element Rear Mic] +switch = off +volume = off + +.include analog-input-mic.conf.common diff --git a/src/modules/alsa/mixer/paths/analog-input-front-mic.conf b/src/modules/alsa/mixer/paths/analog-input-front-mic.conf new file mode 100644 index 00000000..6c58ece1 --- /dev/null +++ b/src/modules/alsa/mixer/paths/analog-input-front-mic.conf @@ -0,0 +1,81 @@ +# This file is part of PulseAudio. +# +# PulseAudio is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation; either version 2.1 of the +# License, or (at your option) any later version. +# +# PulseAudio is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with PulseAudio; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +; For devices where a 'Front Mic' or 'Front Mic Boost' element exists +; +; See analog-output.conf.common for an explanation on the directives + +[General] +priority = 90 +name = analog-input-microphone-front + +[Element Front Mic Boost] +required-any = any +switch = select +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +[Option Front Mic Boost:on] +name = input-boost-on + +[Option Front Mic Boost:off] +name = input-boost-off + +[Element Front Mic] +required-any = any +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +[Element Capture] +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +[Element Input Source] +enumeration = select + +[Option Input Source:Front Mic] +name = analog-input-microphone-front +required-any = any + +[Element Capture Source] +enumeration = select + +[Option Capture Source:Front Mic] +name = analog-input-microphone-front +required-any = any + +[Element Mic] +switch = off +volume = off + +[Element Internal Mic] +switch = off +volume = off + +[Element Rear Mic] +switch = off +volume = off + +[Element Dock Mic] +switch = off +volume = off + +.include analog-input-mic.conf.common diff --git a/src/modules/alsa/mixer/paths/analog-input-internal-mic.conf b/src/modules/alsa/mixer/paths/analog-input-internal-mic.conf index 70cd5129..70a1cd12 100644 --- a/src/modules/alsa/mixer/paths/analog-input-internal-mic.conf +++ b/src/modules/alsa/mixer/paths/analog-input-internal-mic.conf @@ -14,54 +14,98 @@ # along with PulseAudio; if not, write to the Free Software Foundation, # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. -; For devices where a 'Internal Mic' element exists +; For devices where a 'Internal Mic' or 'Internal Mic Boost' element exists +; 'Int Mic' and 'Int Mic Boost' are for compatibility with kernels < 2.6.38 ; ; See analog-output.conf.common for an explanation on the directives [General] -priority = 90 -name = analog-input-microphone +priority = 89 +name = analog-input-microphone-internal -[Element Capture] -switch = mute +[Element Internal Mic Boost] +required-any = any +switch = select volume = merge override-map.1 = all override-map.2 = all-left,all-right -[Element Mic] -switch = off -volume = off +[Option Internal Mic Boost:on] +name = input-boost-on + +[Option Internal Mic Boost:off] +name = input-boost-off + +[Element Int Mic Boost] +required-any = any +switch = select +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +[Option Int Mic Boost:on] +name = input-boost-on + +[Option Int Mic Boost:off] +name = input-boost-off + [Element Internal Mic] -required = any +required-any = any switch = mute volume = merge override-map.1 = all override-map.2 = all-left,all-right -[Element Line] -switch = off -volume = off +[Element Int Mic] +required-any = any +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right -[Element Aux] -switch = off -volume = off +[Element Capture] +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +[Element Input Source] +enumeration = select + +[Option Input Source:Internal Mic] +name = analog-input-microphone-internal +required-any = any + +[Option Input Source:Int Mic] +name = analog-input-microphone-internal +required-any = any -[Element Video] +[Element Capture Source] +enumeration = select + +[Option Capture Source:Internal Mic] +name = analog-input-microphone-internal +required-any = any + +[Option Capture Source:Int Mic] +name = analog-input-microphone-internal +required-any = any + +[Element Mic] switch = off volume = off -[Element Mic/Line] +[Element Dock Mic] switch = off volume = off -[Element TV Tuner] +[Element Front Mic] switch = off volume = off -[Element FM] +[Element Rear Mic] switch = off volume = off -.include analog-input.conf.common .include analog-input-mic.conf.common diff --git a/src/modules/alsa/mixer/paths/analog-input-linein.conf b/src/modules/alsa/mixer/paths/analog-input-linein.conf index 57568ccd..461cebdb 100644 --- a/src/modules/alsa/mixer/paths/analog-input-linein.conf +++ b/src/modules/alsa/mixer/paths/analog-input-linein.conf @@ -35,13 +35,35 @@ volume = off switch = off volume = off +[Element Line Boost] +required-any = any +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + [Element Line] -required = any +required-any = any switch = mute volume = merge override-map.1 = all override-map.2 = all-left,all-right +[Element Input Source] +enumeration = select + +[Option Input Source:Line] +name = analog-input-linein +required-any = any + +[Element Capture Source] +enumeration = select + +[Option Capture Source:Line] +name = analog-input-linein +required-any = any + + [Element Aux] switch = off volume = off @@ -62,4 +84,10 @@ volume = off switch = off volume = off -.include analog-input.conf.common +[Element Mic Jack Mode] +enumeration = select + +[Option Mic Jack Mode:Line In] +priority = 19 +required-any = any +name = input-linein diff --git a/src/modules/alsa/mixer/paths/analog-input-mic.conf b/src/modules/alsa/mixer/paths/analog-input-mic.conf index 9b8b75a1..d88028bf 100644 --- a/src/modules/alsa/mixer/paths/analog-input-mic.conf +++ b/src/modules/alsa/mixer/paths/analog-input-mic.conf @@ -14,54 +14,91 @@ # along with PulseAudio; if not, write to the Free Software Foundation, # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. -; For devices where a 'Mic' element exists +; For devices where a 'Mic' or 'Mic Boost' element exists ; ; See analog-output.conf.common for an explanation on the directives [General] -priority = 100 +priority = 89 name = analog-input-microphone -[Element Capture] -switch = mute +[Element Mic Boost] +required-any = any +switch = select volume = merge override-map.1 = all override-map.2 = all-left,all-right +[Option Mic Boost:on] +name = input-boost-on + +[Option Mic Boost:off] +name = input-boost-off + [Element Mic] -required = any +required-any = any switch = mute volume = merge override-map.1 = all override-map.2 = all-left,all-right -[Element Internal Mic] -switch = off -volume = off +[Element Capture] +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right -[Element Line] -switch = off -volume = off +[Element Input Source] +enumeration = select -[Element Aux] -switch = off -volume = off +[Option Input Source:Mic] +name = analog-input-microphone +required-any = any + +[Element Capture Source] +enumeration = select + +[Option Capture Source:Mic] +name = analog-input-microphone +required-any = any + +;;; Some AC'97s have "Mic Select" and "Mic Boost (+20dB)" + +[Element Mic Select] +enumeration = select -[Element Video] +[Option Mic Select:Mic1] +name = input-microphone +priority = 20 + +[Option Mic Select:Mic2] +name = input-microphone +priority = 19 + +[Element Mic Boost (+20dB)] +switch = select +volume = merge + +[Option Mic Boost (+20dB):on] +name = input-boost-on + +[Option Mic Boost (+20dB):off] +name = input-boost-off + +[Element Front Mic] switch = off volume = off -[Element Mic/Line] +[Element Internal Mic] switch = off volume = off -[Element TV Tuner] +[Element Rear Mic] switch = off volume = off -[Element FM] +[Element Dock Mic] switch = off volume = off -.include analog-input.conf.common .include analog-input-mic.conf.common diff --git a/src/modules/alsa/mixer/paths/analog-input-mic.conf.common b/src/modules/alsa/mixer/paths/analog-input-mic.conf.common index 9bddd48c..2e4f0d81 100644 --- a/src/modules/alsa/mixer/paths/analog-input-mic.conf.common +++ b/src/modules/alsa/mixer/paths/analog-input-mic.conf.common @@ -18,64 +18,37 @@ ; ; See analog-output.conf.common for an explanation on the directives -;;; 'Mic Select' +[Element Line] +switch = off +volume = off -[Element Mic Select] -enumeration = select - -[Option Mic Select:Mic1] -name = input-microphone -priority = 20 - -[Option Mic Select:Mic2] -name = input-microphone -priority = 19 - -;;; Various Boosts - -[Element Mic Boost (+20dB)] -switch = select -volume = merge - -[Option Mic Boost (+20dB):on] -name = input-boost-on +[Element Line Boost] +switch = off +volume = off -[Option Mic Boost (+20dB):off] -name = input-boost-off +[Element Aux] +switch = off +volume = off -[Element Mic Boost] -switch = select -volume = merge +[Element Video] +switch = off +volume = off -[Option Mic Boost:on] -name = input-boost-on +[Element Mic/Line] +switch = off +volume = off -[Option Mic Boost:off] -name = input-boost-off +[Element TV Tuner] +switch = off +volume = off -[Element Front Mic Boost] -switch = select +[Element FM] +switch = off +volume = off -[Option Front Mic Boost:on] -name = input-boost-on - -[Option Front Mic Boost:off] -name = input-boost-off - -[Element Rear Mic Boost] -switch = select - -[Option Rear Mic Boost:on] -name = input-boost-on - -[Option Rear Mic Boost:off] -name = input-boost-off - -[Element Int Mic Boost] -switch = select - -[Option Int Mic Boost:on] -name = input-boost-on +[Element Mic Jack Mode] +enumeration = select -[Option Int Mic Boost:off] -name = input-boost-off +[Option Mic Jack Mode:Mic In] +priority = 19 +name = input-microphone diff --git a/src/modules/alsa/mixer/paths/analog-input-rear-mic.conf b/src/modules/alsa/mixer/paths/analog-input-rear-mic.conf new file mode 100644 index 00000000..75ed61b0 --- /dev/null +++ b/src/modules/alsa/mixer/paths/analog-input-rear-mic.conf @@ -0,0 +1,81 @@ +# This file is part of PulseAudio. +# +# PulseAudio is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation; either version 2.1 of the +# License, or (at your option) any later version. +# +# PulseAudio is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with PulseAudio; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +; For devices where a 'Rear Mic' or 'Rear Mic Boost' element exists +; +; See analog-output.conf.common for an explanation on the directives + +[General] +priority = 89 +name = analog-input-microphone-rear + +[Element Rear Mic Boost] +required-any = any +switch = select +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +[Option Rear Mic Boost:on] +name = input-boost-on + +[Option Rear Mic Boost:off] +name = input-boost-off + +[Element Rear Mic] +required-any = any +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +[Element Capture] +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +[Element Input Source] +enumeration = select + +[Option Input Source:Rear Mic] +name = analog-input-microphone-rear +required-any = any + +[Element Capture Source] +enumeration = select + +[Option Capture Source:Rear Mic] +name = analog-input-microphone-rear +required-any = any + +[Element Mic] +switch = off +volume = off + +[Element Internal Mic] +switch = off +volume = off + +[Element Front Mic] +switch = off +volume = off + +[Element Dock Mic] +switch = off +volume = off + +.include analog-input-mic.conf.common diff --git a/src/modules/alsa/mixer/paths/analog-input.conf b/src/modules/alsa/mixer/paths/analog-input.conf index 30507386..b86c3564 100644 --- a/src/modules/alsa/mixer/paths/analog-input.conf +++ b/src/modules/alsa/mixer/paths/analog-input.conf @@ -32,9 +32,36 @@ override-map.2 = all-left,all-right [Element Mic] required-absent = any +[Element Dock Mic] +required-absent = any + +[Element Dock Mic Boost] +required-absent = any + +[Element Front Mic] +required-absent = any + +[Element Front Mic Boost] +required-absent = any + +[Element Int Mic] +required-absent = any + +[Element Int Mic Boost] +required-absent = any + [Element Internal Mic] required-absent = any +[Element Internal Mic Boost] +required-absent = any + +[Element Rear Mic] +required-absent = any + +[Element Rear Mic Boost] +required-absent = any + [Element Line] required-absent = any @@ -54,4 +81,3 @@ required-absent = any required-absent = any .include analog-input.conf.common -.include analog-input-mic.conf.common diff --git a/src/modules/alsa/mixer/paths/analog-input.conf.common b/src/modules/alsa/mixer/paths/analog-input.conf.common index 0b2cfd94..94165776 100644 --- a/src/modules/alsa/mixer/paths/analog-input.conf.common +++ b/src/modules/alsa/mixer/paths/analog-input.conf.common @@ -66,42 +66,18 @@ enumeration = select name = input-microphone priority = 20 -[Option Input Source:Mic] -name = input-microphone -priority = 20 - [Option Input Source:Microphone] name = input-microphone priority = 20 -[Option Input Source:Front Mic] -name = input-microphone -priority = 19 - [Option Input Source:Front Microphone] name = input-microphone priority = 19 -[Option Input Source:Int Mic] -name = input-microphone -priority = 19 - -[Option Input Source:Internal Mic] -name = input-microphone -priority = 19 - -[Option Input Source:Rear Mic] -name = input-microphone -priority = 19 - [Option Input Source:Internal Mic 1] name = input-microphone priority = 19 -[Option Input Source:Line] -name = input-linein -priority = 18 - [Option Input Source:Line-In] name = input-linein priority = 18 @@ -135,21 +111,12 @@ name = input [Option Capture Source:Line/Mic] name = input -[Option Capture Source:Mic] -name = input-microphone - [Option Capture Source:Microphone] name = input-microphone -[Option Capture Source:Int Mic] -name = input-microphone-internal - [Option Capture Source:Int DMic] name = input-microphone-internal -[Option Capture Source:Internal Mic] -name = input-microphone-internal - [Option Capture Source:iMic] name = input-microphone-internal @@ -159,15 +126,9 @@ name = input-microphone-internal [Option Capture Source:Internal Microphone] name = input-microphone-internal -[Option Capture Source:Front Mic] -name = input-microphone - [Option Capture Source:Front Microphone] name = input-microphone -[Option Capture Source:Rear Mic] -name = input-microphone - [Option Capture Source:Mic1] name = input-microphone @@ -198,9 +159,6 @@ name = input-linein [Option Capture Source:Analog] name = input -[Option Capture Source:Line] -name = input-linein - [Option Capture Source:Line-In] name = input-linein @@ -261,9 +219,6 @@ name = input [Option Capture Source:Docking-Station] name = input-docking -[Option Capture Source:Dock Mic] -name = input-docking-microphone - ;;; 'Mic Jack Mode' [Element Mic Jack Mode] diff --git a/src/modules/alsa/mixer/paths/analog-output.conf.common b/src/modules/alsa/mixer/paths/analog-output.conf.common index 6131da5c..c7c44350 100644 --- a/src/modules/alsa/mixer/paths/analog-output.conf.common +++ b/src/modules/alsa/mixer/paths/analog-output.conf.common @@ -63,10 +63,15 @@ ; # by the option name, resp. on/off if the element is a switch. ; name = ... # Logical name to use in the path identifier ; priority = ... # Priority if this is made into a device port +; required = ignore | enumeration | any # In this element, this option must exist or the path will be invalid. ("any" is an alias for "enumeration".) +; required-any = ignore | enumeration | any # In this element, either this or another option must exist (or an element) +; required-absent = ignore | enumeration | any # In this element, this option must not exist or the path will be invalid ; ; [Element ...] # For each element that we shall control ; required = ignore | switch | volume | enumeration | any # If set, require this element to be of this kind and available, ; # otherwise don't consider this path valid for the card +; required-any = ignore | switch | volume | enumeration | any # If set, at least one of the elements with required-any in this +; # path must be present, otherwise this path is invalid for the card ; required-absent = ignore | switch | volume # If set, require this element to not be of this kind and not ; # available, otherwise don't consider this path valid for the card ; @@ -77,6 +82,7 @@ ; volume = ignore | merge | off | zero # What to do with this volume: ignore it, merge it into the device ; # volume slider, always set it to the lowest value possible, or always ; # set it to 0 dB (for whatever that means) +; volume-limit = <volume step> # Limit the maximum volume by disabling the volume steps above <volume step>. ; enumeration = ignore | select # What to do with this enumeration, ignore it or make it selectable ; # via device ports. If set to 'select' you need to define an Option section ; # for each of the items you want to expose diff --git a/src/modules/alsa/mixer/profile-sets/90-pulseaudio.rules b/src/modules/alsa/mixer/profile-sets/90-pulseaudio.rules index f964b005..03293409 100644 --- a/src/modules/alsa/mixer/profile-sets/90-pulseaudio.rules +++ b/src/modules/alsa/mixer/profile-sets/90-pulseaudio.rules @@ -24,6 +24,8 @@ SUBSYSTEMS=="usb", ATTRS{idVendor}=="17cc", ATTRS{idProduct}=="1978", ENV{PULSE_ SUBSYSTEMS=="usb", ATTRS{idVendor}=="17cc", ATTRS{idProduct}=="0839", ENV{PULSE_PROFILE_SET}="native-instruments-audio4dj.conf" SUBSYSTEMS=="usb", ATTRS{idVendor}=="17cc", ATTRS{idProduct}=="baff", ENV{PULSE_PROFILE_SET}="native-instruments-traktorkontrol-s4.conf" SUBSYSTEMS=="usb", ATTRS{idVendor}=="17cc", ATTRS{idProduct}=="4711", ENV{PULSE_PROFILE_SET}="native-instruments-korecontroller.conf" +SUBSYSTEMS=="usb", ATTRS{idVendor}=="17cc", ATTRS{idProduct}=="1011", ENV{PULSE_PROFILE_SET}="native-instruments-traktor-audio6.conf" +SUBSYSTEMS=="usb", ATTRS{idVendor}=="17cc", ATTRS{idProduct}=="1021", ENV{PULSE_PROFILE_SET}="native-instruments-traktor-audio10.conf" SUBSYSTEMS=="usb", ATTRS{idVendor}=="0763", ATTRS{idProduct}=="2012", ENV{PULSE_PROFILE_SET}="maudio-fasttrack-pro.conf" LABEL="pulseaudio_end" diff --git a/src/modules/alsa/mixer/profile-sets/default.conf b/src/modules/alsa/mixer/profile-sets/default.conf index f470d604..9f7b5f2b 100644 --- a/src/modules/alsa/mixer/profile-sets/default.conf +++ b/src/modules/alsa/mixer/profile-sets/default.conf @@ -16,17 +16,27 @@ ; Default profile definitions for the ALSA backend of PulseAudio. This ; is used as fallback for all cards that have no special mapping -; assigned. (and should be good enough for the vast majority of -; cards). Use the udev property PULSE_PROFILE_SET to assign a -; different profile set than this one to a device. So what is this -; about? Simply, what we do here is map ALSA devices to how they are -; exposed in PA. We say which ALSA device string to use to open a -; device, which channel mapping to use then, and which mixer path to -; use. This is encoded in a 'mapping'. Multiple of these mappings can -; be bound together in a 'profile' which is then directly exposed in -; the UI as a card profile. Each mapping assigned to a profile will -; result in one sink/source to be created if the profile is selected -; for the card. +; assigned (and should be good enough for the vast majority of +; cards). If you want to assign a different profile set than this one +; to a device, either set the udev property PULSE_PROFILE_SET for the +; card, or use the "profile_set" module argument when loading +; module-alsa-card. +; +; So what is this about? Simply, what we do here is map ALSA devices +; to how they are exposed in PA. We say which ALSA device string to +; use to open a device, which channel mapping to use then, and which +; mixer path to use. This is encoded in a 'mapping'. Multiple of these +; mappings can be bound together in a 'profile' which is then directly +; exposed in the UI as a card profile. Each mapping assigned to a +; profile will result in one sink/source to be created if the profile +; is selected for the card. +; +; Additionally, the path set configuration files can describe the +; decibel values assigned to the steps of the volume elements. This +; can be used to work around situations when the alsa driver doesn't +; provide any decibel information, or when the information is +; incorrect. + ; [General] ; auto-profiles = no | yes # Instead of defining all profiles manually, autogenerate @@ -55,6 +65,35 @@ ; skip-probe = no | yes # Skip probing for availability? If this is yes then this profile ; # will be assumed as working without probing. Makes initialization ; # a bit faster but only works if the card is really known well. +; +; [DecibelFix element] # Decibel fixes can be used to work around missing or incorrect dB +; # information from alsa. A decibel fix is a table that maps volume steps +; # to decibel values for one volume element. The "element" part in the +; # section title is the name of the volume element. +; # +; # NOTE: This feature is meant just as a help for figuring out the correct +; # decibel values. Pulseaudio is not the correct place to maintain the +; # decibel mappings! +; # +; # If you need this feature, then you should make sure that when you have +; # the correct values figured out, the alsa driver developers get informed +; # too, so that they can fix the driver. +; +; db-values = ... # The option value consists of pairs of step numbers and decibel values. +; # The pairs are separated with whitespace, and steps are separated from +; # the corresponding decibel values with a colon. The values must be in an +; # increasing order. Here's an example of a valid string: +; # +; # "0:-40.50 1:-38.70 3:-33.00 11:0" +; # +; # The lowest step imposes a lower limit for hardware volume and the +; # highest step correspondingly imposes a higher limit. That means that +; # that the mixer will never be set outside those values - the rest of the +; # volume scale is done using software volume. +; # +; # As can be seen in the example, you don't need to specify a dB value for +; # each step. The dB values for skipped steps will be linearly interpolated +; # using the nearest steps that are given. [General] auto-profiles = yes @@ -63,14 +102,14 @@ auto-profiles = yes device-strings = hw:%f channel-map = mono paths-output = analog-output analog-output-speaker analog-output-desktop-speaker analog-output-headphones analog-output-headphones-2 analog-output-mono analog-output-lfe-on-mono -paths-input = analog-input analog-input-mic analog-input-linein analog-input-aux analog-input-video analog-input-tvtuner analog-input-fm analog-input-mic-line +paths-input = analog-input-front-mic analog-input-rear-mic analog-input-internal-mic analog-input-dock-mic analog-input analog-input-mic analog-input-linein analog-input-aux analog-input-video analog-input-tvtuner analog-input-fm analog-input-mic-line priority = 1 [Mapping analog-stereo] device-strings = front:%f hw:%f channel-map = left,right paths-output = analog-output analog-output-speaker analog-output-desktop-speaker analog-output-headphones analog-output-headphones-2 analog-output-mono analog-output-lfe-on-mono -paths-input = analog-input analog-input-mic analog-input-linein analog-input-aux analog-input-video analog-input-tvtuner analog-input-fm analog-input-mic-line +paths-input = analog-input-front-mic analog-input-rear-mic analog-input-internal-mic analog-input-dock-mic analog-input analog-input-mic analog-input-linein analog-input-aux analog-input-video analog-input-tvtuner analog-input-fm analog-input-mic-line priority = 10 [Mapping analog-surround-40] diff --git a/src/modules/alsa/mixer/profile-sets/native-instruments-traktor-audio10.conf b/src/modules/alsa/mixer/profile-sets/native-instruments-traktor-audio10.conf new file mode 100644 index 00000000..4deb65da --- /dev/null +++ b/src/modules/alsa/mixer/profile-sets/native-instruments-traktor-audio10.conf @@ -0,0 +1,131 @@ +# This file is part of PulseAudio. +# +# PulseAudio is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation; either version 2.1 of the +# License, or (at your option) any later version. +# +# PulseAudio is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with PulseAudio; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +; Native Instruments Audio 10 DJ +; +; This card has five stereo pairs of input and five stereo pairs of +; output +; +; We knowingly only define a subset of the theoretically possible +; mapping combinations as profiles here. +; +; See default.conf for an explanation on the directives used here. + +[General] +auto-profiles = no + +[Mapping analog-stereo-out-main] +description = Analog Stereo Main +device-strings = hw:%f,0,0 +channel-map = left,right + +[Mapping analog-stereo-out-a] +description = Analog Stereo Channel A +device-strings = hw:%f,0,1 +channel-map = left,right +direction = output + +[Mapping analog-stereo-out-b] +description = Analog Stereo Channel B +device-strings = hw:%f,0,1 +channel-map = left,right +direction = output + +[Mapping analog-stereo-out-c] +description = Analog Stereo Channel C +device-strings = hw:%f,0,2 +channel-map = left,right +direction = output + +[Mapping analog-stereo-out-d] +description = Analog Stereo Channel D +device-strings = hw:%f,0,3 +channel-map = left,right +direction = output + +[Mapping analog-stereo-in-main] +description = Analog Stereo Main +device-strings = hw:%f,0,0 +channel-map = left,right + +[Mapping analog-stereo-in-a] +description = Analog Stereo Channel A +device-strings = hw:%f,0,1 +channel-map = left,right +direction = input + +[Mapping analog-stereo-in-b] +description = Analog Stereo Channel B +device-strings = hw:%f,0,1 +channel-map = left,right +direction = input + +[Mapping analog-stereo-in-c] +description = Analog Stereo Channel C +device-strings = hw:%f,0,2 +channel-map = left,right +direction = input + +[Mapping analog-stereo-in-d] +description = Analog Stereo Channel D +device-strings = hw:%f,0,3 +channel-map = left,right +direction = input + + + + +[Profile output:analog-stereo-all+input:analog-stereo-all] +description = Analog Stereo Duplex Channels Main, A, B, C, D +output-mappings = analog-stereo-out-main analog-stereo-out-a analog-stereo-out-b analog-stereo-out-c analog-stereo-out-d +input-mappings = analog-stereo-in-main analog-stereo-in-a analog-stereo-in-b analog-stereo-in-c analog-stereo-in-d +priority = 100 +skip-probe = yes + +[Profile output:analog-stereo-main+input:analog-stereo-main] +description = Analog Stereo Duplex Main +output-mappings = analog-stereo-out-main +input-mappings = analog-stereo-in-main +priority = 50 +skip-probe = yes + +[Profile output:analog-stereo-a+input:analog-stereo-a] +description = Analog Stereo Duplex Channel A +output-mappings = analog-stereo-out-a +input-mappings = analog-stereo-in-a +priority = 40 +skip-probe = yes + +[Profile output:analog-stereo-b+input:analog-stereo-b] +description = Analog Stereo Duplex Channel B +output-mappings = analog-stereo-out-b +input-mappings = analog-stereo-in-b +priority = 30 +skip-probe = yes + +[Profile output:analog-stereo-a+input:analog-stereo-c] +description = Analog Stereo Duplex Channel C +output-mappings = analog-stereo-out-c +input-mappings = analog-stereo-in-c +priority = 20 +skip-probe = yes + +[Profile output:analog-stereo-a+input:analog-stereo-d] +description = Analog Stereo Duplex Channel D +output-mappings = analog-stereo-out-d +input-mappings = analog-stereo-in-d +priority = 10 +skip-probe = yes diff --git a/src/modules/alsa/mixer/profile-sets/native-instruments-traktor-audio6.conf b/src/modules/alsa/mixer/profile-sets/native-instruments-traktor-audio6.conf new file mode 100644 index 00000000..48d9058b --- /dev/null +++ b/src/modules/alsa/mixer/profile-sets/native-instruments-traktor-audio6.conf @@ -0,0 +1,92 @@ +# This file is part of PulseAudio. +# +# PulseAudio is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation; either version 2.1 of the +# License, or (at your option) any later version. +# +# PulseAudio is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with PulseAudio; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +; Native Instruments Audio 6 DJ +; +; This card has three stereo pairs of input and three stereo pairs of +; output +; +; We knowingly only define a subset of the theoretically possible +; mapping combinations as profiles here. +; +; See default.conf for an explanation on the directives used here. + +[General] +auto-profiles = no + +[Mapping analog-stereo-out-main] +description = Analog Stereo Main +device-strings = hw:%f,0,0 +channel-map = left,right + +[Mapping analog-stereo-out-a] +description = Analog Stereo Channel A +device-strings = hw:%f,0,1 +channel-map = left,right +direction = output + +[Mapping analog-stereo-out-b] +description = Analog Stereo Channel B +device-strings = hw:%f,0,1 +channel-map = left,right +direction = output + +[Mapping analog-stereo-in-main] +description = Analog Stereo Main +device-strings = hw:%f,0,0 +channel-map = left,right + +[Mapping analog-stereo-in-a] +description = Analog Stereo Channel A +device-strings = hw:%f,0,1 +channel-map = left,right +direction = input + +[Mapping analog-stereo-in-b] +description = Analog Stereo Channel B +device-strings = hw:%f,0,1 +channel-map = left,right +direction = input + + + +[Profile output:analog-stereo-all+input:analog-stereo-all] +description = Analog Stereo Duplex Channels A, B (Headphones) +output-mappings = analog-stereo-out-main analog-stereo-out-a analog-stereo-out-b +input-mappings = analog-stereo-in-main analog-stereo-in-a analog-stereo-in-b +priority = 100 +skip-probe = yes + +[Profile output:analog-stereo-main+input:analog-stereo-main] +description = Analog Stereo Duplex Channel Main +output-mappings = analog-stereo-out-main +input-mappings = analog-stereo-in-main +priority = 50 +skip-probe = yes + +[Profile output:analog-stereo-a+input:analog-stereo-a] +description = Analog Stereo Duplex Channel A +output-mappings = analog-stereo-out-a +input-mappings = analog-stereo-in-a +priority = 40 +skip-probe = yes + +[Profile output:analog-stereo-b+input:analog-stereo-b] +description = Analog Stereo Duplex Channel B +output-mappings = analog-stereo-out-b +input-mappings = analog-stereo-in-b +priority = 30 +skip-probe = yes diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c index ebd2f8ae..e60aa5ef 100644 --- a/src/modules/alsa/module-alsa-card.c +++ b/src/modules/alsa/module-alsa-card.c @@ -65,7 +65,8 @@ PA_MODULE_USAGE( "tsched_buffer_watermark=<lower fill watermark> " "profile=<profile name> " "ignore_dB=<ignore dB information from the device?> " - "sync_volume=<syncronize sw and hw voluchanges in IO-thread?>"); + "sync_volume=<syncronize sw and hw voluchanges in IO-thread?> " + "profile_set=<profile set configuration file> "); static const char* const valid_modargs[] = { "name", @@ -88,6 +89,7 @@ static const char* const valid_modargs[] = { "profile", "ignore_dB", "sync_volume", + "profile_set", NULL }; @@ -328,6 +330,11 @@ int pa__init(pa_module *m) { fn = pa_udev_get_property(alsa_card_index, "PULSE_PROFILE_SET"); #endif + if (pa_modargs_get_value(ma, "profile_set", NULL)) { + pa_xfree(fn); + fn = pa_xstrdup(pa_modargs_get_value(ma, "profile_set", NULL)); + } + u->profile_set = pa_alsa_profile_set_new(fn, &u->core->default_channel_map); pa_xfree(fn); @@ -335,6 +342,7 @@ int pa__init(pa_module *m) { goto fail; pa_alsa_profile_set_probe(u->profile_set, u->device_id, &m->core->default_sample_spec, m->core->default_n_fragments, m->core->default_fragment_size_msec); + pa_alsa_profile_set_dump(u->profile_set); pa_card_new_data_init(&data); data.driver = __FILE__; @@ -393,6 +401,14 @@ int pa__init(pa_module *m) { if (reserve) pa_reserve_wrapper_unref(reserve); + if (!pa_hashmap_isempty(u->profile_set->decibel_fixes)) + pa_log_warn("Card %s uses decibel fixes (i.e. overrides the decibel information for some alsa volume elements). " + "Please note that this feature is meant just as a help for figuring out the correct decibel values. " + "Pulseaudio is not the correct place to maintain the decibel mappings! The fixed decibel values " + "should be sent to ALSA developers so that they can fix the driver. If it turns out that this feature " + "is abused (i.e. fixes are not pushed to ALSA), the decibel fix feature may be removed in some future " + "Pulseaudio version.", u->card->name); + return 0; fail: diff --git a/src/modules/alsa/module-alsa-sink.c b/src/modules/alsa/module-alsa-sink.c index 697fab45..465c8b9e 100644 --- a/src/modules/alsa/module-alsa-sink.c +++ b/src/modules/alsa/module-alsa-sink.c @@ -40,7 +40,8 @@ PA_MODULE_LOAD_ONCE(FALSE); PA_MODULE_USAGE( "name=<name of the sink, to be prefixed> " "sink_name=<name for the sink> " - "sink_properities=<properties for the sink> " + "sink_properties=<properties for the sink> " + "namereg_fail=<pa_namereg_register() fail parameter value> " "device=<ALSA device> " "device_id=<ALSA card index> " "format=<sample format> " @@ -64,6 +65,7 @@ static const char* const valid_modargs[] = { "name", "sink_name", "sink_properties", + "namereg_fail", "device", "device_id", "format", diff --git a/src/modules/alsa/module-alsa-source.c b/src/modules/alsa/module-alsa-source.c index 23da4185..90ffea57 100644 --- a/src/modules/alsa/module-alsa-source.c +++ b/src/modules/alsa/module-alsa-source.c @@ -65,6 +65,7 @@ PA_MODULE_USAGE( "name=<name for the source, to be prefixed> " "source_name=<name for the source> " "source_properties=<properties for the source> " + "namereg_fail=<pa_namereg_register() fail parameter value> " "device=<ALSA device> " "device_id=<ALSA card index> " "format=<sample format> " @@ -84,6 +85,7 @@ static const char* const valid_modargs[] = { "name", "source_name", "source_properties", + "namereg_fail", "device", "device_id", "format", diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c index 47e0fd50..293e0244 100644 --- a/src/modules/bluetooth/bluetooth-util.c +++ b/src/modules/bluetooth/bluetooth-util.c @@ -193,7 +193,7 @@ static int parse_device_property(pa_bluetooth_discovery *y, pa_bluetooth_device dbus_message_iter_get_basic(i, &key); - if (!dbus_message_iter_next(i)) { + if (!dbus_message_iter_next(i)) { pa_log("Property value missing"); return -1; } @@ -323,7 +323,7 @@ static int parse_audio_property(pa_bluetooth_discovery *u, int *state, DBusMessa dbus_message_iter_get_basic(i, &key); - if (!dbus_message_iter_next(i)) { + if (!dbus_message_iter_next(i)) { pa_log("Property value missing"); return -1; } @@ -590,7 +590,7 @@ static void list_devices_reply(DBusPendingCall *pending, void *userdata) { finish: if (paths) - dbus_free_string_array (paths); + dbus_free_string_array(paths); dbus_message_unref(r); @@ -598,8 +598,7 @@ finish: pa_dbus_pending_free(p); } -static void register_endpoint(pa_bluetooth_discovery *y, const char *path, const char *endpoint, const char *uuid) -{ +static void register_endpoint(pa_bluetooth_discovery *y, const char *path, const char *endpoint, const char *uuid) { DBusMessage *m; DBusMessageIter i, d; uint8_t codec = 0; @@ -699,7 +698,7 @@ static void list_adapters_reply(DBusPendingCall *pending, void *userdata) { finish: if (paths) - dbus_free_string_array (paths); + dbus_free_string_array(paths); dbus_message_unref(r); @@ -715,6 +714,47 @@ static void list_adapters(pa_bluetooth_discovery *y) { send_and_add_to_pending(y, NULL, m, list_adapters_reply); } +int pa_bluetooth_transport_parse_property(pa_bluetooth_transport *t, DBusMessageIter *i) +{ + const char *key; + DBusMessageIter variant_i; + + if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING) { + pa_log("Property name not a string."); + return -1; + } + + dbus_message_iter_get_basic(i, &key); + + if (!dbus_message_iter_next(i)) { + pa_log("Property value missing"); + return -1; + } + + if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_VARIANT) { + pa_log("Property value not a variant."); + return -1; + } + + dbus_message_iter_recurse(i, &variant_i); + + switch (dbus_message_iter_get_arg_type(&variant_i)) { + + case DBUS_TYPE_BOOLEAN: { + + pa_bool_t *value; + dbus_message_iter_get_basic(&variant_i, &value); + + if (pa_streq(key, "NREC")) + t->nrec = value; + + break; + } + } + + return 0; +} + static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *userdata) { DBusError err; pa_bluetooth_discovery *y; @@ -863,6 +903,28 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us } return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } else if (dbus_message_is_signal(m, "org.bluez.MediaTransport", "PropertyChanged")) { + pa_bluetooth_device *d; + pa_bluetooth_transport *t; + void *state = NULL; + DBusMessageIter arg_i; + + while ((d = pa_hashmap_iterate(y->devices, &state, NULL))) + if ((t = pa_hashmap_get(d->transports, dbus_message_get_path(m)))) + break; + + if (!t) + goto fail; + + if (!dbus_message_iter_init(m, &arg_i)) { + pa_log("Failed to parse PropertyChanged: %s", err.message); + goto fail; + } + + if (pa_bluetooth_transport_parse_property(t, &arg_i) < 0) + goto fail; + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } fail: @@ -935,10 +997,11 @@ const pa_bluetooth_transport* pa_bluetooth_device_get_transport(const pa_bluetoo return NULL; } -int pa_bluetooth_transport_acquire(const pa_bluetooth_transport *t, const char *accesstype) { +int pa_bluetooth_transport_acquire(const pa_bluetooth_transport *t, const char *accesstype, size_t *imtu, size_t *omtu) { DBusMessage *m, *r; DBusError err; int ret; + uint16_t i, o; pa_assert(t); pa_assert(t->y); @@ -956,7 +1019,7 @@ int pa_bluetooth_transport_acquire(const pa_bluetooth_transport *t, const char * } #ifdef DBUS_TYPE_UNIX_FD - if (!dbus_message_get_args(r, &err, DBUS_TYPE_UNIX_FD, &ret, DBUS_TYPE_INVALID)) { + if (!dbus_message_get_args(r, &err, DBUS_TYPE_UNIX_FD, &ret, DBUS_TYPE_UINT16, &i, DBUS_TYPE_UINT16, &o, DBUS_TYPE_INVALID)) { pa_log("Failed to parse org.bluez.MediaTransport.Acquire(): %s", err.message); ret = -1; dbus_error_free(&err); @@ -964,6 +1027,12 @@ int pa_bluetooth_transport_acquire(const pa_bluetooth_transport *t, const char * } #endif + if (imtu) + *imtu = i; + + if (omtu) + *omtu = o; + fail: dbus_message_unref(r); return ret; @@ -1029,6 +1098,7 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage const char *path, *dev_path = NULL, *uuid = NULL; uint8_t *config = NULL; int size = 0; + pa_bool_t nrec; enum profile p; DBusMessageIter args, props; DBusMessage *r; @@ -1064,6 +1134,10 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage if (var != DBUS_TYPE_OBJECT_PATH) goto fail; dbus_message_iter_get_basic(&value, &dev_path); + } else if (strcasecmp(key, "NREC") == 0) { + if (var != DBUS_TYPE_BOOLEAN) + goto fail; + dbus_message_iter_get_basic(&value, &nrec); } else if (strcasecmp(key, "Configuration") == 0) { DBusMessageIter array; if (var != DBUS_TYPE_ARRAY) @@ -1087,6 +1161,8 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage p = PROFILE_A2DP_SOURCE; t = transport_new(y, path, p, config, size); + if (nrec) + t->nrec = nrec; pa_hashmap_put(d->transports, t->path, t); pa_log_debug("Transport %s profile %d available", t->path, t->profile); @@ -1396,6 +1472,7 @@ pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c) { "type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged'", "type='signal',sender='org.bluez',interface='org.bluez.AudioSource',member='PropertyChanged'", "type='signal',sender='org.bluez',interface='org.bluez.HandsfreeGateway',member='PropertyChanged'", + "type='signal',sender='org.bluez',interface='org.bluez.MediaTransport',member='PropertyChanged'", NULL) < 0) { pa_log("Failed to add D-Bus matches: %s", err.message); goto fail; @@ -1463,6 +1540,7 @@ void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) { "type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged'", "type='signal',sender='org.bluez',interface='org.bluez.AudioSource',member='PropertyChanged'", "type='signal',sender='org.bluez',interface='org.bluez.HandsfreeGateway',member='PropertyChanged'", + "type='signal',sender='org.bluez',interface='org.bluez.MediaTransport',member='PropertyChanged'", NULL); if (y->filter_added) diff --git a/src/modules/bluetooth/bluetooth-util.h b/src/modules/bluetooth/bluetooth-util.h index f141209d..bb0cb24a 100644 --- a/src/modules/bluetooth/bluetooth-util.h +++ b/src/modules/bluetooth/bluetooth-util.h @@ -70,6 +70,7 @@ struct pa_bluetooth_transport { uint8_t codec; uint8_t *config; int config_size; + pa_bool_t nrec; }; /* This enum is shared among Audio, Headset, AudioSink, and AudioSource, although not all values are acceptable in all profiles */ @@ -126,8 +127,9 @@ const pa_bluetooth_device* pa_bluetooth_discovery_get_by_address(pa_bluetooth_di const pa_bluetooth_transport* pa_bluetooth_discovery_get_transport(pa_bluetooth_discovery *y, const char *path); const pa_bluetooth_transport* pa_bluetooth_device_get_transport(const pa_bluetooth_device *d, enum profile profile); -int pa_bluetooth_transport_acquire(const pa_bluetooth_transport *t, const char *accesstype); +int pa_bluetooth_transport_acquire(const pa_bluetooth_transport *t, const char *accesstype, size_t *imtu, size_t *omtu); void pa_bluetooth_transport_release(const pa_bluetooth_transport *t, const char *accesstype); +int pa_bluetooth_transport_parse_property(pa_bluetooth_transport *t, DBusMessageIter *i); pa_hook* pa_bluetooth_discovery_hook(pa_bluetooth_discovery *d); diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c index dc09ffca..55610546 100644 --- a/src/modules/bluetooth/module-bluetooth-device.c +++ b/src/modules/bluetooth/module-bluetooth-device.c @@ -58,6 +58,9 @@ #define MAX_BITPOOL 64 #define MIN_BITPOOL 2U +#define BITPOOL_DEC_LIMIT 32 +#define BITPOOL_DEC_STEP 5 + PA_MODULE_AUTHOR("Joao Paulo Rechi Vita"); PA_MODULE_DESCRIPTION("Bluetooth audio sink and source"); PA_MODULE_VERSION(PACKAGE_VERSION); @@ -117,6 +120,8 @@ struct a2dp_info { size_t buffer_size; /* Size of the buffer */ uint16_t seq_num; /* Cumulative packet sequence */ + uint8_t min_bitpool; + uint8_t max_bitpool; }; struct hsp_info { @@ -574,7 +579,7 @@ static int setup_a2dp(struct userdata *u) { } /* Run from main thread */ -static void setup_sbc(struct a2dp_info *a2dp) { +static void setup_sbc(struct a2dp_info *a2dp, enum profile p) { sbc_capabilities_t *active_capabilities; pa_assert(a2dp); @@ -660,7 +665,11 @@ static void setup_sbc(struct a2dp_info *a2dp) { pa_assert_not_reached(); } - a2dp->sbc.bitpool = active_capabilities->max_bitpool; + a2dp->min_bitpool = active_capabilities->min_bitpool; + a2dp->max_bitpool = active_capabilities->max_bitpool; + + /* Set minimum bitpool for source to get the maximum possible block_size */ + a2dp->sbc.bitpool = p == PROFILE_A2DP ? a2dp->max_bitpool : a2dp->min_bitpool; a2dp->codesize = sbc_get_codesize(&a2dp->sbc); a2dp->frame_length = sbc_get_frame_length(&a2dp->sbc); } @@ -728,7 +737,7 @@ static int set_conf(struct userdata *u) { /* setup SBC encoder now we agree on parameters */ if (u->profile == PROFILE_A2DP || u->profile == PROFILE_A2DP_SOURCE) { - setup_sbc(&u->a2dp); + setup_sbc(&u->a2dp, u->profile); u->block_size = ((u->link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload)) @@ -743,6 +752,39 @@ static int set_conf(struct userdata *u) { return 0; } +/* from IO thread */ +static void a2dp_set_bitpool(struct userdata *u, uint8_t bitpool) +{ + struct a2dp_info *a2dp; + + pa_assert(u); + + a2dp = &u->a2dp; + + if (a2dp->sbc.bitpool == bitpool) + return; + + if (bitpool > a2dp->max_bitpool) + bitpool = a2dp->max_bitpool; + else if (bitpool < a2dp->min_bitpool) + bitpool = a2dp->min_bitpool; + + a2dp->sbc.bitpool = bitpool; + + a2dp->codesize = sbc_get_codesize(&a2dp->sbc); + a2dp->frame_length = sbc_get_frame_length(&a2dp->sbc); + + pa_log_debug("Bitpool has changed to %u", a2dp->sbc.bitpool); + + u->block_size = + (u->link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload)) + / a2dp->frame_length * a2dp->codesize; + + pa_sink_set_max_request_within_thread(u->sink, u->block_size); + pa_sink_set_fixed_latency_within_thread(u->sink, + FIXED_LATENCY_PLAYBACK_A2DP + pa_bytes_to_usec(u->block_size, &u->sample_spec)); +} + /* from IO thread, except in SCO over PCM */ static int setup_stream(struct userdata *u) { @@ -758,6 +800,9 @@ static int setup_stream(struct userdata *u) { pa_log_debug("Stream properly set up, we're ready to roll!"); + if (u->profile == PROFILE_A2DP) + a2dp_set_bitpool(u, u->a2dp.max_bitpool); + u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1); pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); pollfd->fd = u->stream_fd; @@ -857,8 +902,7 @@ static int stop_stream_fd(struct userdata *u) { return r; } -static void bt_transport_release(struct userdata *u) -{ +static void bt_transport_release(struct userdata *u) { const char *accesstype = "rw"; const pa_bluetooth_transport *t; @@ -891,8 +935,7 @@ static void bt_transport_release(struct userdata *u) } } -static int bt_transport_acquire(struct userdata *u, pa_bool_t start) -{ +static int bt_transport_acquire(struct userdata *u, pa_bool_t start) { const char *accesstype = "rw"; const pa_bluetooth_transport *t; @@ -912,7 +955,8 @@ static int bt_transport_acquire(struct userdata *u, pa_bool_t start) return -1; } - u->stream_fd = pa_bluetooth_transport_acquire(t, accesstype); + /* FIXME: Handle in/out MTU properly when unix socket is not longer supported */ + u->stream_fd = pa_bluetooth_transport_acquire(t, accesstype, NULL, &u->link_mtu); if (u->stream_fd < 0) return -1; @@ -1351,7 +1395,7 @@ static int a2dp_process_render(struct userdata *u) { break; pa_log_error("Failed to write data to socket: %s", pa_cstrerror(errno)); - ret = -1; + ret = -1; break; } @@ -1443,7 +1487,7 @@ static int a2dp_process_push(struct userdata *u) { d = pa_memblock_acquire(memchunk.memblock); to_write = memchunk.length = pa_memblock_get_length(memchunk.memblock); - while (PA_LIKELY(to_decode > 0 && to_write > 0)) { + while (PA_LIKELY(to_decode > 0)) { size_t written; ssize_t decoded; @@ -1462,10 +1506,12 @@ static int a2dp_process_push(struct userdata *u) { /* pa_log_debug("SBC: decoded: %lu; written: %lu", (unsigned long) decoded, (unsigned long) written); */ /* pa_log_debug("SBC: frame_length: %lu; codesize: %lu", (unsigned long) a2dp->frame_length, (unsigned long) a2dp->codesize); */ + /* Reset frame length, it can be changed due to bitpool change */ + a2dp->frame_length = sbc_get_frame_length(&a2dp->sbc); + pa_assert_fp((size_t) decoded <= to_decode); pa_assert_fp((size_t) decoded == a2dp->frame_length); - pa_assert_fp((size_t) written <= to_write); pa_assert_fp((size_t) written == a2dp->codesize); p = (const uint8_t*) p + decoded; @@ -1477,6 +1523,8 @@ static int a2dp_process_push(struct userdata *u) { frame_count++; } + memchunk.length -= to_write; + pa_memblock_release(memchunk.memblock); pa_source_post(u->source, &memchunk); @@ -1490,6 +1538,27 @@ static int a2dp_process_push(struct userdata *u) { return ret; } +static void a2dp_reduce_bitpool(struct userdata *u) +{ + struct a2dp_info *a2dp; + uint8_t bitpool; + + pa_assert(u); + + a2dp = &u->a2dp; + + /* Check if bitpool is already at its limit */ + if (a2dp->sbc.bitpool <= BITPOOL_DEC_LIMIT) + return; + + bitpool = a2dp->sbc.bitpool - BITPOOL_DEC_STEP; + + if (bitpool < BITPOOL_DEC_LIMIT) + bitpool = BITPOOL_DEC_LIMIT; + + a2dp_set_bitpool(u, bitpool); +} + static void thread_func(void *userdata) { struct userdata *u = userdata; unsigned do_write = 0; @@ -1581,6 +1650,9 @@ static void thread_func(void *userdata) { pa_sink_render_full(u->sink, skip_bytes, &tmp); pa_memblock_unref(tmp.memblock); u->write_index += skip_bytes; + + if (u->profile == PROFILE_A2DP) + a2dp_reduce_bitpool(u); } } @@ -1679,8 +1751,8 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us dbus_message_get_path(m), dbus_message_get_member(m)); - if (!dbus_message_has_path(m, u->path)) - goto fail; + if (!dbus_message_has_path(m, u->path) && !dbus_message_has_path(m, u->transport)) + goto fail; if (dbus_message_is_signal(m, "org.bluez.Headset", "SpeakerGainChanged") || dbus_message_is_signal(m, "org.bluez.Headset", "MicrophoneGainChanged")) { @@ -1705,6 +1777,28 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us pa_source_volume_changed(u->source, &v); } } + } else if (dbus_message_is_signal(m, "org.bluez.MediaTransport", "PropertyChanged")) { + DBusMessageIter arg_i; + pa_bluetooth_transport *t; + pa_bool_t nrec; + + t = (pa_bluetooth_transport *) pa_bluetooth_discovery_get_transport(u->discovery, u->transport); + pa_assert(t); + + if (!dbus_message_iter_init(m, &arg_i)) { + pa_log("Failed to parse PropertyChanged: %s", err.message); + goto fail; + } + + nrec = t->nrec; + + if (pa_bluetooth_transport_parse_property(t, &arg_i) < 0) + goto fail; + + if (nrec != t->nrec) { + pa_log_debug("dbus: property 'NREC' changed to value '%s'", t->nrec ? "True" : "False"); + pa_proplist_sets(u->source->proplist, "bluetooth.nrec", t->nrec ? "1" : "0"); + } } fail: @@ -1946,6 +2040,7 @@ static int add_source(struct userdata *u) { pa_proplist_sets(data.proplist, "bluetooth.protocol", u->profile == PROFILE_A2DP_SOURCE ? "a2dp_source" : "hsp"); if ((u->profile == PROFILE_HSP) || (u->profile == PROFILE_HFGW)) pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone"); + data.card = u->card; data.name = get_name("source", u->modargs, u->address, &b); data.namereg_fail = b; @@ -1972,8 +2067,15 @@ static int add_source(struct userdata *u) { pa_bytes_to_usec(u->block_size, &u->sample_spec)); } - if (u->profile == PROFILE_HSP || u->profile == PROFILE_HFGW) - pa_proplist_sets(u->source->proplist, "bluetooth.nrec", (u->hsp.pcm_capabilities.flags & BT_PCM_FLAG_NREC) ? "1" : "0"); + if ((u->profile == PROFILE_HSP) || (u->profile == PROFILE_HFGW)) { + if (u->transport) { + const pa_bluetooth_transport *t; + t = pa_bluetooth_discovery_get_transport(u->discovery, u->transport); + pa_assert(t); + pa_proplist_sets(u->source->proplist, "bluetooth.nrec", t->nrec ? "1" : "0"); + } else + pa_proplist_sets(u->source->proplist, "bluetooth.nrec", (u->hsp.pcm_capabilities.flags & BT_PCM_FLAG_NREC) ? "1" : "0"); + } if (u->profile == PROFILE_HSP) { u->source->set_volume = source_set_volume_cb; @@ -2007,8 +2109,7 @@ static void shutdown_bt(struct userdata *u) { } } -static int bt_transport_config_a2dp(struct userdata *u) -{ +static int bt_transport_config_a2dp(struct userdata *u) { const pa_bluetooth_transport *t; struct a2dp_info *a2dp = &u->a2dp; sbc_capabilities_raw_t *config; @@ -2097,7 +2198,11 @@ static int bt_transport_config_a2dp(struct userdata *u) pa_assert_not_reached(); } - a2dp->sbc.bitpool = config->max_bitpool; + a2dp->min_bitpool = config->min_bitpool; + a2dp->max_bitpool = config->max_bitpool; + + /* Set minimum bitpool for source to get the maximum possible block_size */ + a2dp->sbc.bitpool = u->profile == PROFILE_A2DP ? a2dp->max_bitpool : a2dp->min_bitpool; a2dp->codesize = sbc_get_codesize(&a2dp->sbc); a2dp->frame_length = sbc_get_frame_length(&a2dp->sbc); @@ -2112,8 +2217,7 @@ static int bt_transport_config_a2dp(struct userdata *u) return 0; } -static int bt_transport_config(struct userdata *u) -{ +static int bt_transport_config(struct userdata *u) { if (u->profile == PROFILE_HSP || u->profile == PROFILE_HFGW) { u->block_size = u->link_mtu; return 0; @@ -2122,101 +2226,12 @@ static int bt_transport_config(struct userdata *u) return bt_transport_config_a2dp(u); } -static int parse_transport_property(struct userdata *u, DBusMessageIter *i) -{ - const char *key; - DBusMessageIter variant_i; - - pa_assert(u); - pa_assert(i); - - if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING) { - pa_log("Property name not a string."); - return -1; - } - - dbus_message_iter_get_basic(i, &key); - - if (!dbus_message_iter_next(i)) { - pa_log("Property value missing"); - return -1; - } - - if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_VARIANT) { - pa_log("Property value not a variant."); - return -1; - } - - dbus_message_iter_recurse(i, &variant_i); - - switch (dbus_message_iter_get_arg_type(&variant_i)) { - - case DBUS_TYPE_UINT16: { - - uint16_t value; - dbus_message_iter_get_basic(&variant_i, &value); - - if (pa_streq(key, "OMTU")) - u->link_mtu = value; - - break; - } - - } - - return 0; -} - /* Run from main thread */ -static int bt_transport_open(struct userdata *u) -{ - DBusMessage *m, *r; - DBusMessageIter arg_i, element_i; - DBusError err; - +static int bt_transport_open(struct userdata *u) { if (bt_transport_acquire(u, FALSE) < 0) return -1; - dbus_error_init(&err); - - pa_assert_se(m = dbus_message_new_method_call("org.bluez", u->transport, "org.bluez.MediaTransport", "GetProperties")); - r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->connection), m, -1, &err); - - if (dbus_error_is_set(&err) || !r) { - pa_log("Failed to get transport properties: %s", err.message); - goto fail; - } - - if (!dbus_message_iter_init(r, &arg_i)) { - pa_log("GetProperties reply has no arguments."); - goto fail; - } - - if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_ARRAY) { - pa_log("GetProperties argument is not an array."); - goto fail; - } - - dbus_message_iter_recurse(&arg_i, &element_i); - while (dbus_message_iter_get_arg_type(&element_i) != DBUS_TYPE_INVALID) { - - if (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) { - DBusMessageIter dict_i; - - dbus_message_iter_recurse(&element_i, &dict_i); - - parse_transport_property(u, &dict_i); - } - - if (!dbus_message_iter_next(&element_i)) - break; - } - return bt_transport_config(u); - -fail: - dbus_message_unref(r); - return -1; } /* Run from main thread */ @@ -2696,7 +2711,7 @@ int pa__init(pa_module* m) { struct userdata *u; const char *address, *path; DBusError err; - char *mike, *speaker; + char *mike, *speaker, *transport; const pa_bluetooth_device *device; pa_assert(m); @@ -2775,15 +2790,18 @@ int pa__init(pa_module* m) { speaker = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.Headset',member='SpeakerGainChanged',path='%s'", u->path); mike = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.Headset',member='MicrophoneGainChanged',path='%s'", u->path); + transport = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.MediaTransport',member='PropertyChanged'"); if (pa_dbus_add_matches( pa_dbus_connection_get(u->connection), &err, speaker, mike, + transport, NULL) < 0) { pa_xfree(speaker); pa_xfree(mike); + pa_xfree(transport); pa_log("Failed to add D-Bus matches: %s", err.message); goto fail; @@ -2791,6 +2809,7 @@ int pa__init(pa_module* m) { pa_xfree(speaker); pa_xfree(mike); + pa_xfree(transport); /* Connect to the BT service */ init_bt(u); @@ -2855,10 +2874,7 @@ void pa__done(pa_module *m) { speaker = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.Headset',member='SpeakerGainChanged',path='%s'", u->path); mike = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.Headset',member='MicrophoneGainChanged',path='%s'", u->path); - pa_dbus_remove_matches(pa_dbus_connection_get(u->connection), - speaker, - mike, - NULL); + pa_dbus_remove_matches(pa_dbus_connection_get(u->connection), speaker, mike, NULL); pa_xfree(speaker); pa_xfree(mike); diff --git a/src/modules/bluetooth/module-bluetooth-proximity.c b/src/modules/bluetooth/module-bluetooth-proximity.c index 3eed9cea..8c3a5b9f 100644 --- a/src/modules/bluetooth/module-bluetooth-proximity.c +++ b/src/modules/bluetooth/module-bluetooth-proximity.c @@ -59,7 +59,6 @@ PA_MODULE_USAGE( static const char* const valid_modargs[] = { "sink", - "rssi", "hci", NULL, }; diff --git a/src/modules/bluetooth/sbc.c b/src/modules/bluetooth/sbc/sbc.c index 5157c70f..98b236bd 100644 --- a/src/modules/bluetooth/sbc.c +++ b/src/modules/bluetooth/sbc/sbc.c @@ -77,7 +77,7 @@ struct sbc_frame { uint8_t joint; /* only the lower 4 bits of every element are to be used */ - uint32_t scale_factor[2][8]; + uint32_t SBC_ALIGNED scale_factor[2][8]; /* raw integer subband samples in the frame */ int32_t SBC_ALIGNED sb_sample_f[16][2][8]; @@ -159,7 +159,8 @@ static uint8_t sbc_crc8(const uint8_t *data, size_t len) * Takes a pointer to the frame in question, a pointer to the bits array and * the sampling frequency (as 2 bit integer) */ -static void sbc_calculate_bits(const struct sbc_frame *frame, int (*bits)[8]) +static SBC_ALWAYS_INLINE void sbc_calculate_bits_internal( + const struct sbc_frame *frame, int (*bits)[8], int subbands) { uint8_t sf = frame->frequency; @@ -170,17 +171,17 @@ static void sbc_calculate_bits(const struct sbc_frame *frame, int (*bits)[8]) for (ch = 0; ch < frame->channels; ch++) { max_bitneed = 0; if (frame->allocation == SNR) { - for (sb = 0; sb < frame->subbands; sb++) { + for (sb = 0; sb < subbands; sb++) { bitneed[ch][sb] = frame->scale_factor[ch][sb]; if (bitneed[ch][sb] > max_bitneed) max_bitneed = bitneed[ch][sb]; } } else { - for (sb = 0; sb < frame->subbands; sb++) { + for (sb = 0; sb < subbands; sb++) { if (frame->scale_factor[ch][sb] == 0) bitneed[ch][sb] = -5; else { - if (frame->subbands == 4) + if (subbands == 4) loudness = frame->scale_factor[ch][sb] - sbc_offset4[sf][sb]; else loudness = frame->scale_factor[ch][sb] - sbc_offset8[sf][sb]; @@ -201,7 +202,7 @@ static void sbc_calculate_bits(const struct sbc_frame *frame, int (*bits)[8]) bitslice--; bitcount += slicecount; slicecount = 0; - for (sb = 0; sb < frame->subbands; sb++) { + for (sb = 0; sb < subbands; sb++) { if ((bitneed[ch][sb] > bitslice + 1) && (bitneed[ch][sb] < bitslice + 16)) slicecount++; else if (bitneed[ch][sb] == bitslice + 1) @@ -214,7 +215,7 @@ static void sbc_calculate_bits(const struct sbc_frame *frame, int (*bits)[8]) bitslice--; } - for (sb = 0; sb < frame->subbands; sb++) { + for (sb = 0; sb < subbands; sb++) { if (bitneed[ch][sb] < bitslice + 2) bits[ch][sb] = 0; else { @@ -224,7 +225,8 @@ static void sbc_calculate_bits(const struct sbc_frame *frame, int (*bits)[8]) } } - for (sb = 0; bitcount < frame->bitpool && sb < frame->subbands; sb++) { + for (sb = 0; bitcount < frame->bitpool && + sb < subbands; sb++) { if ((bits[ch][sb] >= 2) && (bits[ch][sb] < 16)) { bits[ch][sb]++; bitcount++; @@ -234,7 +236,8 @@ static void sbc_calculate_bits(const struct sbc_frame *frame, int (*bits)[8]) } } - for (sb = 0; bitcount < frame->bitpool && sb < frame->subbands; sb++) { + for (sb = 0; bitcount < frame->bitpool && + sb < subbands; sb++) { if (bits[ch][sb] < 16) { bits[ch][sb]++; bitcount++; @@ -250,7 +253,7 @@ static void sbc_calculate_bits(const struct sbc_frame *frame, int (*bits)[8]) max_bitneed = 0; if (frame->allocation == SNR) { for (ch = 0; ch < 2; ch++) { - for (sb = 0; sb < frame->subbands; sb++) { + for (sb = 0; sb < subbands; sb++) { bitneed[ch][sb] = frame->scale_factor[ch][sb]; if (bitneed[ch][sb] > max_bitneed) max_bitneed = bitneed[ch][sb]; @@ -258,11 +261,11 @@ static void sbc_calculate_bits(const struct sbc_frame *frame, int (*bits)[8]) } } else { for (ch = 0; ch < 2; ch++) { - for (sb = 0; sb < frame->subbands; sb++) { + for (sb = 0; sb < subbands; sb++) { if (frame->scale_factor[ch][sb] == 0) bitneed[ch][sb] = -5; else { - if (frame->subbands == 4) + if (subbands == 4) loudness = frame->scale_factor[ch][sb] - sbc_offset4[sf][sb]; else loudness = frame->scale_factor[ch][sb] - sbc_offset8[sf][sb]; @@ -285,7 +288,7 @@ static void sbc_calculate_bits(const struct sbc_frame *frame, int (*bits)[8]) bitcount += slicecount; slicecount = 0; for (ch = 0; ch < 2; ch++) { - for (sb = 0; sb < frame->subbands; sb++) { + for (sb = 0; sb < subbands; sb++) { if ((bitneed[ch][sb] > bitslice + 1) && (bitneed[ch][sb] < bitslice + 16)) slicecount++; else if (bitneed[ch][sb] == bitslice + 1) @@ -300,7 +303,7 @@ static void sbc_calculate_bits(const struct sbc_frame *frame, int (*bits)[8]) } for (ch = 0; ch < 2; ch++) { - for (sb = 0; sb < frame->subbands; sb++) { + for (sb = 0; sb < subbands; sb++) { if (bitneed[ch][sb] < bitslice + 2) { bits[ch][sb] = 0; } else { @@ -324,7 +327,8 @@ static void sbc_calculate_bits(const struct sbc_frame *frame, int (*bits)[8]) if (ch == 1) { ch = 0; sb++; - if (sb >= frame->subbands) break; + if (sb >= subbands) + break; } else ch = 1; } @@ -339,7 +343,8 @@ static void sbc_calculate_bits(const struct sbc_frame *frame, int (*bits)[8]) if (ch == 1) { ch = 0; sb++; - if (sb >= frame->subbands) break; + if (sb >= subbands) + break; } else ch = 1; } @@ -348,6 +353,14 @@ static void sbc_calculate_bits(const struct sbc_frame *frame, int (*bits)[8]) } +static void sbc_calculate_bits(const struct sbc_frame *frame, int (*bits)[8]) +{ + if (frame->subbands == 4) + sbc_calculate_bits_internal(frame, bits, 4); + else + sbc_calculate_bits_internal(frame, bits, 8); +} + /* * Unpacks a SBC frame at the beginning of the stream in data, * which has at most len bytes into frame. @@ -534,6 +547,16 @@ static void sbc_decoder_init(struct sbc_decoder_state *state, state->offset[ch][i] = (10 * i + 10); } +static SBC_ALWAYS_INLINE int16_t sbc_clip16(int32_t s) +{ + if (s > 0x7FFF) + return 0x7FFF; + else if (s < -0x8000) + return -0x8000; + else + return s; +} + static inline void sbc_synthesize_four(struct sbc_decoder_state *state, struct sbc_frame *frame, int ch, int blk) { @@ -562,7 +585,7 @@ static inline void sbc_synthesize_four(struct sbc_decoder_state *state, k = (i + 4) & 0xf; /* Store in output, Q0 */ - frame->pcm_sample[ch][blk * 4 + i] = SCALE4_STAGED1( + frame->pcm_sample[ch][blk * 4 + i] = sbc_clip16(SCALE4_STAGED1( MULA(v[offset[i] + 0], sbc_proto_4_40m0[idx + 0], MULA(v[offset[k] + 1], sbc_proto_4_40m1[idx + 0], MULA(v[offset[i] + 2], sbc_proto_4_40m0[idx + 1], @@ -572,7 +595,7 @@ static inline void sbc_synthesize_four(struct sbc_decoder_state *state, MULA(v[offset[i] + 6], sbc_proto_4_40m0[idx + 3], MULA(v[offset[k] + 7], sbc_proto_4_40m1[idx + 3], MULA(v[offset[i] + 8], sbc_proto_4_40m0[idx + 4], - MUL( v[offset[k] + 9], sbc_proto_4_40m1[idx + 4]))))))))))); + MUL( v[offset[k] + 9], sbc_proto_4_40m1[idx + 4])))))))))))); } } @@ -607,8 +630,8 @@ static inline void sbc_synthesize_eight(struct sbc_decoder_state *state, for (idx = 0, i = 0; i < 8; i++, idx += 5) { k = (i + 8) & 0xf; - /* Store in output */ - frame->pcm_sample[ch][blk * 8 + i] = SCALE8_STAGED1( // Q0 + /* Store in output, Q0 */ + frame->pcm_sample[ch][blk * 8 + i] = sbc_clip16(SCALE8_STAGED1( MULA(state->V[ch][offset[i] + 0], sbc_proto_8_80m0[idx + 0], MULA(state->V[ch][offset[k] + 1], sbc_proto_8_80m1[idx + 0], MULA(state->V[ch][offset[i] + 2], sbc_proto_8_80m0[idx + 1], @@ -618,7 +641,7 @@ static inline void sbc_synthesize_eight(struct sbc_decoder_state *state, MULA(state->V[ch][offset[i] + 6], sbc_proto_8_80m0[idx + 3], MULA(state->V[ch][offset[k] + 7], sbc_proto_8_80m1[idx + 3], MULA(state->V[ch][offset[i] + 8], sbc_proto_8_80m0[idx + 4], - MUL( state->V[ch][offset[k] + 9], sbc_proto_8_80m1[idx + 4]))))))))))); + MUL( state->V[ch][offset[k] + 9], sbc_proto_8_80m1[idx + 4])))))))))))); } } @@ -732,9 +755,9 @@ static int sbc_analyze_audio(struct sbc_encoder_state *state, * -99 not implemented */ -static SBC_ALWAYS_INLINE int sbc_pack_frame_internal( +static SBC_ALWAYS_INLINE ssize_t sbc_pack_frame_internal( uint8_t *data, struct sbc_frame *frame, size_t len, - int frame_subbands, int frame_channels) + int frame_subbands, int frame_channels, int joint) { /* Bitstream writer starts from the fourth byte */ uint8_t *data_ptr = data + 4; @@ -791,63 +814,6 @@ static SBC_ALWAYS_INLINE int sbc_pack_frame_internal( crc_pos = 16; if (frame->mode == JOINT_STEREO) { - /* like frame->sb_sample but joint stereo */ - int32_t sb_sample_j[16][2]; - /* scalefactor and scale_factor in joint case */ - uint32_t scalefactor_j[2]; - uint8_t scale_factor_j[2]; - - uint8_t joint = 0; - frame->joint = 0; - - for (sb = 0; sb < frame_subbands - 1; sb++) { - scale_factor_j[0] = 0; - scalefactor_j[0] = 2 << SCALE_OUT_BITS; - scale_factor_j[1] = 0; - scalefactor_j[1] = 2 << SCALE_OUT_BITS; - - for (blk = 0; blk < frame->blocks; blk++) { - uint32_t tmp; - /* Calculate joint stereo signal */ - sb_sample_j[blk][0] = - ASR(frame->sb_sample_f[blk][0][sb], 1) + - ASR(frame->sb_sample_f[blk][1][sb], 1); - sb_sample_j[blk][1] = - ASR(frame->sb_sample_f[blk][0][sb], 1) - - ASR(frame->sb_sample_f[blk][1][sb], 1); - - /* calculate scale_factor_j and scalefactor_j for joint case */ - tmp = fabs(sb_sample_j[blk][0]); - while (scalefactor_j[0] < tmp) { - scale_factor_j[0]++; - scalefactor_j[0] *= 2; - } - tmp = fabs(sb_sample_j[blk][1]); - while (scalefactor_j[1] < tmp) { - scale_factor_j[1]++; - scalefactor_j[1] *= 2; - } - } - - /* decide whether to join this subband */ - if ((frame->scale_factor[0][sb] + - frame->scale_factor[1][sb]) > - (scale_factor_j[0] + - scale_factor_j[1])) { - /* use joint stereo for this subband */ - joint |= 1 << (frame_subbands - 1 - sb); - frame->joint |= 1 << sb; - frame->scale_factor[0][sb] = scale_factor_j[0]; - frame->scale_factor[1][sb] = scale_factor_j[1]; - for (blk = 0; blk < frame->blocks; blk++) { - frame->sb_sample_f[blk][0][sb] = - sb_sample_j[blk][0]; - frame->sb_sample_f[blk][1][sb] = - sb_sample_j[blk][1]; - } - } - } - PUT_BITS(data_ptr, bits_cache, bits_count, joint, frame_subbands); crc_header[crc_pos >> 3] = joint; @@ -905,18 +871,23 @@ static SBC_ALWAYS_INLINE int sbc_pack_frame_internal( return data_ptr - data; } -static int sbc_pack_frame(uint8_t *data, struct sbc_frame *frame, size_t len) +static ssize_t sbc_pack_frame(uint8_t *data, struct sbc_frame *frame, size_t len, + int joint) { if (frame->subbands == 4) { if (frame->channels == 1) - return sbc_pack_frame_internal(data, frame, len, 4, 1); + return sbc_pack_frame_internal( + data, frame, len, 4, 1, joint); else - return sbc_pack_frame_internal(data, frame, len, 4, 2); + return sbc_pack_frame_internal( + data, frame, len, 4, 2, joint); } else { if (frame->channels == 1) - return sbc_pack_frame_internal(data, frame, len, 8, 1); + return sbc_pack_frame_internal( + data, frame, len, 8, 1, joint); else - return sbc_pack_frame_internal(data, frame, len, 8, 2); + return sbc_pack_frame_internal( + data, frame, len, 8, 2, joint); } } @@ -924,7 +895,7 @@ static void sbc_encoder_init(struct sbc_encoder_state *state, const struct sbc_frame *frame) { memset(&state->X, 0, sizeof(state->X)); - state->position = SBC_X_BUFFER_SIZE - frame->subbands * 9; + state->position = (SBC_X_BUFFER_SIZE - frame->subbands * 9) & ~7; sbc_init_primitives(state); } @@ -1046,10 +1017,11 @@ ssize_t sbc_decode(sbc_t *sbc, const void *input, size_t input_len, } ssize_t sbc_encode(sbc_t *sbc, const void *input, size_t input_len, - void *output, size_t output_len, size_t *written) + void *output, size_t output_len, ssize_t *written) { struct sbc_priv *priv; - int framelen, samples; + int samples; + ssize_t framelen; int (*sbc_enc_process_input)(int position, const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE], int nsamples, int nchannels); @@ -1114,11 +1086,18 @@ ssize_t sbc_encode(sbc_t *sbc, const void *input, size_t input_len, samples = sbc_analyze_audio(&priv->enc_state, &priv->frame); - priv->enc_state.sbc_calc_scalefactors( - priv->frame.sb_sample_f, priv->frame.scale_factor, - priv->frame.blocks, priv->frame.channels, priv->frame.subbands); - - framelen = sbc_pack_frame(output, &priv->frame, output_len); + if (priv->frame.mode == JOINT_STEREO) { + int j = priv->enc_state.sbc_calc_scalefactors_j( + priv->frame.sb_sample_f, priv->frame.scale_factor, + priv->frame.blocks, priv->frame.subbands); + framelen = sbc_pack_frame(output, &priv->frame, output_len, j); + } else { + priv->enc_state.sbc_calc_scalefactors( + priv->frame.sb_sample_f, priv->frame.scale_factor, + priv->frame.blocks, priv->frame.channels, + priv->frame.subbands); + framelen = sbc_pack_frame(output, &priv->frame, output_len, 0); + } if (written) *written = framelen; @@ -1131,8 +1110,7 @@ void sbc_finish(sbc_t *sbc) if (!sbc) return; - if (sbc->priv_alloc_base) - free(sbc->priv_alloc_base); + free(sbc->priv_alloc_base); memset(sbc, 0, sizeof(sbc_t)); } diff --git a/src/modules/bluetooth/sbc.h b/src/modules/bluetooth/sbc/sbc.h index 65435884..c9c56d38 100644 --- a/src/modules/bluetooth/sbc.h +++ b/src/modules/bluetooth/sbc/sbc.h @@ -90,7 +90,7 @@ ssize_t sbc_decode(sbc_t *sbc, const void *input, size_t input_len, /* Encodes ONE input block into ONE output block */ ssize_t sbc_encode(sbc_t *sbc, const void *input, size_t input_len, - void *output, size_t output_len, size_t *written); + void *output, size_t output_len, ssize_t *written); /* Returns the output block size in bytes */ size_t sbc_get_frame_length(sbc_t *sbc); diff --git a/src/modules/bluetooth/sbc_math.h b/src/modules/bluetooth/sbc/sbc_math.h index b87bc81c..b87bc81c 100644 --- a/src/modules/bluetooth/sbc_math.h +++ b/src/modules/bluetooth/sbc/sbc_math.h diff --git a/src/modules/bluetooth/sbc_primitives.c b/src/modules/bluetooth/sbc/sbc_primitives.c index 6b0be3f5..3a76a7a0 100644 --- a/src/modules/bluetooth/sbc_primitives.c +++ b/src/modules/bluetooth/sbc/sbc_primitives.c @@ -32,7 +32,9 @@ #include "sbc_primitives.h" #include "sbc_primitives_mmx.h" +#include "sbc_primitives_iwmmxt.h" #include "sbc_primitives_neon.h" +#include "sbc_primitives_armv6.h" /* * A reference C code of analysis filter with SIMD-friendly tables @@ -231,12 +233,12 @@ static SBC_ALWAYS_INLINE int sbc_encoder_process_input_s4_internal( /* handle X buffer wraparound */ if (position < nsamples) { if (nchannels > 0) - memcpy(&X[0][SBC_X_BUFFER_SIZE - 36], &X[0][position], + memcpy(&X[0][SBC_X_BUFFER_SIZE - 40], &X[0][position], 36 * sizeof(int16_t)); if (nchannels > 1) - memcpy(&X[1][SBC_X_BUFFER_SIZE - 36], &X[1][position], + memcpy(&X[1][SBC_X_BUFFER_SIZE - 40], &X[1][position], 36 * sizeof(int16_t)); - position = SBC_X_BUFFER_SIZE - 36; + position = SBC_X_BUFFER_SIZE - 40; } #define PCM(i) (big_endian ? \ @@ -439,6 +441,80 @@ static void sbc_calc_scalefactors( } } +static int sbc_calc_scalefactors_j( + int32_t sb_sample_f[16][2][8], + uint32_t scale_factor[2][8], + int blocks, int subbands) +{ + int blk, joint = 0; + int32_t tmp0, tmp1; + uint32_t x, y; + + /* last subband does not use joint stereo */ + int sb = subbands - 1; + x = 1 << SCALE_OUT_BITS; + y = 1 << SCALE_OUT_BITS; + for (blk = 0; blk < blocks; blk++) { + tmp0 = fabs(sb_sample_f[blk][0][sb]); + tmp1 = fabs(sb_sample_f[blk][1][sb]); + if (tmp0 != 0) + x |= tmp0 - 1; + if (tmp1 != 0) + y |= tmp1 - 1; + } + scale_factor[0][sb] = (31 - SCALE_OUT_BITS) - sbc_clz(x); + scale_factor[1][sb] = (31 - SCALE_OUT_BITS) - sbc_clz(y); + + /* the rest of subbands can use joint stereo */ + while (--sb >= 0) { + int32_t sb_sample_j[16][2]; + x = 1 << SCALE_OUT_BITS; + y = 1 << SCALE_OUT_BITS; + for (blk = 0; blk < blocks; blk++) { + tmp0 = sb_sample_f[blk][0][sb]; + tmp1 = sb_sample_f[blk][1][sb]; + sb_sample_j[blk][0] = ASR(tmp0, 1) + ASR(tmp1, 1); + sb_sample_j[blk][1] = ASR(tmp0, 1) - ASR(tmp1, 1); + tmp0 = fabs(tmp0); + tmp1 = fabs(tmp1); + if (tmp0 != 0) + x |= tmp0 - 1; + if (tmp1 != 0) + y |= tmp1 - 1; + } + scale_factor[0][sb] = (31 - SCALE_OUT_BITS) - + sbc_clz(x); + scale_factor[1][sb] = (31 - SCALE_OUT_BITS) - + sbc_clz(y); + x = 1 << SCALE_OUT_BITS; + y = 1 << SCALE_OUT_BITS; + for (blk = 0; blk < blocks; blk++) { + tmp0 = fabs(sb_sample_j[blk][0]); + tmp1 = fabs(sb_sample_j[blk][1]); + if (tmp0 != 0) + x |= tmp0 - 1; + if (tmp1 != 0) + y |= tmp1 - 1; + } + x = (31 - SCALE_OUT_BITS) - sbc_clz(x); + y = (31 - SCALE_OUT_BITS) - sbc_clz(y); + + /* decide whether to use joint stereo for this subband */ + if ((scale_factor[0][sb] + scale_factor[1][sb]) > x + y) { + joint |= 1 << (subbands - 1 - sb); + scale_factor[0][sb] = x; + scale_factor[1][sb] = y; + for (blk = 0; blk < blocks; blk++) { + sb_sample_f[blk][0][sb] = sb_sample_j[blk][0]; + sb_sample_f[blk][1][sb] = sb_sample_j[blk][1]; + } + } + } + + /* bitmask with the information about subbands using joint stereo */ + return joint; +} + /* * Detect CPU features and setup function pointers */ @@ -456,6 +532,7 @@ void sbc_init_primitives(struct sbc_encoder_state *state) /* Default implementation for scale factors calculation */ state->sbc_calc_scalefactors = sbc_calc_scalefactors; + state->sbc_calc_scalefactors_j = sbc_calc_scalefactors_j; state->implementation_info = "Generic C"; /* X86/AMD64 optimizations */ @@ -464,6 +541,12 @@ void sbc_init_primitives(struct sbc_encoder_state *state) #endif /* ARM optimizations */ +#ifdef SBC_BUILD_WITH_ARMV6_SUPPORT + sbc_init_primitives_armv6(state); +#endif +#ifdef SBC_BUILD_WITH_IWMMXT_SUPPORT + sbc_init_primitives_iwmmxt(state); +#endif #ifdef SBC_BUILD_WITH_NEON_SUPPORT sbc_init_primitives_neon(state); #endif diff --git a/src/modules/bluetooth/sbc_primitives.h b/src/modules/bluetooth/sbc/sbc_primitives.h index 3d01c115..b4b9df2f 100644 --- a/src/modules/bluetooth/sbc_primitives.h +++ b/src/modules/bluetooth/sbc/sbc_primitives.h @@ -62,6 +62,10 @@ struct sbc_encoder_state { void (*sbc_calc_scalefactors)(int32_t sb_sample_f[16][2][8], uint32_t scale_factor[2][8], int blocks, int channels, int subbands); + /* Scale factors calculation with joint stereo support */ + int (*sbc_calc_scalefactors_j)(int32_t sb_sample_f[16][2][8], + uint32_t scale_factor[2][8], + int blocks, int subbands); const char *implementation_info; }; diff --git a/src/modules/bluetooth/sbc/sbc_primitives_armv6.c b/src/modules/bluetooth/sbc/sbc_primitives_armv6.c new file mode 100644 index 00000000..95860980 --- /dev/null +++ b/src/modules/bluetooth/sbc/sbc_primitives_armv6.c @@ -0,0 +1,299 @@ +/* + * + * Bluetooth low-complexity, subband codec (SBC) library + * + * Copyright (C) 2008-2010 Nokia Corporation + * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org> + * Copyright (C) 2004-2005 Henryk Ploetz <henryk@ploetzli.ch> + * Copyright (C) 2005-2006 Brad Midgley <bmidgley@xmission.com> + * + * + * This library 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. + * + * This library 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 this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include <stdint.h> +#include <limits.h> +#include "sbc.h" +#include "sbc_math.h" +#include "sbc_tables.h" + +#include "sbc_primitives_armv6.h" + +/* + * ARMv6 optimizations. The instructions are scheduled for ARM11 pipeline. + */ + +#ifdef SBC_BUILD_WITH_ARMV6_SUPPORT + +static void __attribute__((naked)) sbc_analyze_four_armv6() +{ + /* r0 = in, r1 = out, r2 = consts */ + asm volatile ( + "push {r1, r4-r7, lr}\n" + "push {r8-r11}\n" + "ldrd r4, r5, [r0, #0]\n" + "ldrd r6, r7, [r2, #0]\n" + "ldrd r8, r9, [r0, #16]\n" + "ldrd r10, r11, [r2, #16]\n" + "mov r14, #0x8000\n" + "smlad r3, r4, r6, r14\n" + "smlad r12, r5, r7, r14\n" + "ldrd r4, r5, [r0, #32]\n" + "ldrd r6, r7, [r2, #32]\n" + "smlad r3, r8, r10, r3\n" + "smlad r12, r9, r11, r12\n" + "ldrd r8, r9, [r0, #48]\n" + "ldrd r10, r11, [r2, #48]\n" + "smlad r3, r4, r6, r3\n" + "smlad r12, r5, r7, r12\n" + "ldrd r4, r5, [r0, #64]\n" + "ldrd r6, r7, [r2, #64]\n" + "smlad r3, r8, r10, r3\n" + "smlad r12, r9, r11, r12\n" + "ldrd r8, r9, [r0, #8]\n" + "ldrd r10, r11, [r2, #8]\n" + "smlad r3, r4, r6, r3\n" /* t1[0] is done */ + "smlad r12, r5, r7, r12\n" /* t1[1] is done */ + "ldrd r4, r5, [r0, #24]\n" + "ldrd r6, r7, [r2, #24]\n" + "pkhtb r3, r12, r3, asr #16\n" /* combine t1[0] and t1[1] */ + "smlad r12, r8, r10, r14\n" + "smlad r14, r9, r11, r14\n" + "ldrd r8, r9, [r0, #40]\n" + "ldrd r10, r11, [r2, #40]\n" + "smlad r12, r4, r6, r12\n" + "smlad r14, r5, r7, r14\n" + "ldrd r4, r5, [r0, #56]\n" + "ldrd r6, r7, [r2, #56]\n" + "smlad r12, r8, r10, r12\n" + "smlad r14, r9, r11, r14\n" + "ldrd r8, r9, [r0, #72]\n" + "ldrd r10, r11, [r2, #72]\n" + "smlad r12, r4, r6, r12\n" + "smlad r14, r5, r7, r14\n" + "ldrd r4, r5, [r2, #80]\n" /* start loading cos table */ + "smlad r12, r8, r10, r12\n" /* t1[2] is done */ + "smlad r14, r9, r11, r14\n" /* t1[3] is done */ + "ldrd r6, r7, [r2, #88]\n" + "ldrd r8, r9, [r2, #96]\n" + "ldrd r10, r11, [r2, #104]\n" /* cos table fully loaded */ + "pkhtb r12, r14, r12, asr #16\n" /* combine t1[2] and t1[3] */ + "smuad r4, r3, r4\n" + "smuad r5, r3, r5\n" + "smlad r4, r12, r8, r4\n" + "smlad r5, r12, r9, r5\n" + "smuad r6, r3, r6\n" + "smuad r7, r3, r7\n" + "smlad r6, r12, r10, r6\n" + "smlad r7, r12, r11, r7\n" + "pop {r8-r11}\n" + "stmia r1, {r4, r5, r6, r7}\n" + "pop {r1, r4-r7, pc}\n" + ); +} + +#define sbc_analyze_four(in, out, consts) \ + ((void (*)(int16_t *, int32_t *, const FIXED_T*)) \ + sbc_analyze_four_armv6)((in), (out), (consts)) + +static void __attribute__((naked)) sbc_analyze_eight_armv6() +{ + /* r0 = in, r1 = out, r2 = consts */ + asm volatile ( + "push {r1, r4-r7, lr}\n" + "push {r8-r11}\n" + "ldrd r4, r5, [r0, #24]\n" + "ldrd r6, r7, [r2, #24]\n" + "ldrd r8, r9, [r0, #56]\n" + "ldrd r10, r11, [r2, #56]\n" + "mov r14, #0x8000\n" + "smlad r3, r4, r6, r14\n" + "smlad r12, r5, r7, r14\n" + "ldrd r4, r5, [r0, #88]\n" + "ldrd r6, r7, [r2, #88]\n" + "smlad r3, r8, r10, r3\n" + "smlad r12, r9, r11, r12\n" + "ldrd r8, r9, [r0, #120]\n" + "ldrd r10, r11, [r2, #120]\n" + "smlad r3, r4, r6, r3\n" + "smlad r12, r5, r7, r12\n" + "ldrd r4, r5, [r0, #152]\n" + "ldrd r6, r7, [r2, #152]\n" + "smlad r3, r8, r10, r3\n" + "smlad r12, r9, r11, r12\n" + "ldrd r8, r9, [r0, #16]\n" + "ldrd r10, r11, [r2, #16]\n" + "smlad r3, r4, r6, r3\n" /* t1[6] is done */ + "smlad r12, r5, r7, r12\n" /* t1[7] is done */ + "ldrd r4, r5, [r0, #48]\n" + "ldrd r6, r7, [r2, #48]\n" + "pkhtb r3, r12, r3, asr #16\n" /* combine t1[6] and t1[7] */ + "str r3, [sp, #-4]!\n" /* save to stack */ + "smlad r3, r8, r10, r14\n" + "smlad r12, r9, r11, r14\n" + "ldrd r8, r9, [r0, #80]\n" + "ldrd r10, r11, [r2, #80]\n" + "smlad r3, r4, r6, r3\n" + "smlad r12, r5, r7, r12\n" + "ldrd r4, r5, [r0, #112]\n" + "ldrd r6, r7, [r2, #112]\n" + "smlad r3, r8, r10, r3\n" + "smlad r12, r9, r11, r12\n" + "ldrd r8, r9, [r0, #144]\n" + "ldrd r10, r11, [r2, #144]\n" + "smlad r3, r4, r6, r3\n" + "smlad r12, r5, r7, r12\n" + "ldrd r4, r5, [r0, #0]\n" + "ldrd r6, r7, [r2, #0]\n" + "smlad r3, r8, r10, r3\n" /* t1[4] is done */ + "smlad r12, r9, r11, r12\n" /* t1[5] is done */ + "ldrd r8, r9, [r0, #32]\n" + "ldrd r10, r11, [r2, #32]\n" + "pkhtb r3, r12, r3, asr #16\n" /* combine t1[4] and t1[5] */ + "str r3, [sp, #-4]!\n" /* save to stack */ + "smlad r3, r4, r6, r14\n" + "smlad r12, r5, r7, r14\n" + "ldrd r4, r5, [r0, #64]\n" + "ldrd r6, r7, [r2, #64]\n" + "smlad r3, r8, r10, r3\n" + "smlad r12, r9, r11, r12\n" + "ldrd r8, r9, [r0, #96]\n" + "ldrd r10, r11, [r2, #96]\n" + "smlad r3, r4, r6, r3\n" + "smlad r12, r5, r7, r12\n" + "ldrd r4, r5, [r0, #128]\n" + "ldrd r6, r7, [r2, #128]\n" + "smlad r3, r8, r10, r3\n" + "smlad r12, r9, r11, r12\n" + "ldrd r8, r9, [r0, #8]\n" + "ldrd r10, r11, [r2, #8]\n" + "smlad r3, r4, r6, r3\n" /* t1[0] is done */ + "smlad r12, r5, r7, r12\n" /* t1[1] is done */ + "ldrd r4, r5, [r0, #40]\n" + "ldrd r6, r7, [r2, #40]\n" + "pkhtb r3, r12, r3, asr #16\n" /* combine t1[0] and t1[1] */ + "smlad r12, r8, r10, r14\n" + "smlad r14, r9, r11, r14\n" + "ldrd r8, r9, [r0, #72]\n" + "ldrd r10, r11, [r2, #72]\n" + "smlad r12, r4, r6, r12\n" + "smlad r14, r5, r7, r14\n" + "ldrd r4, r5, [r0, #104]\n" + "ldrd r6, r7, [r2, #104]\n" + "smlad r12, r8, r10, r12\n" + "smlad r14, r9, r11, r14\n" + "ldrd r8, r9, [r0, #136]\n" + "ldrd r10, r11, [r2, #136]!\n" + "smlad r12, r4, r6, r12\n" + "smlad r14, r5, r7, r14\n" + "ldrd r4, r5, [r2, #(160 - 136 + 0)]\n" + "smlad r12, r8, r10, r12\n" /* t1[2] is done */ + "smlad r14, r9, r11, r14\n" /* t1[3] is done */ + "ldrd r6, r7, [r2, #(160 - 136 + 8)]\n" + "smuad r4, r3, r4\n" + "smuad r5, r3, r5\n" + "pkhtb r12, r14, r12, asr #16\n" /* combine t1[2] and t1[3] */ + /* r3 = t2[0:1] */ + /* r12 = t2[2:3] */ + "pop {r0, r14}\n" /* t2[4:5], t2[6:7] */ + "ldrd r8, r9, [r2, #(160 - 136 + 32)]\n" + "smuad r6, r3, r6\n" + "smuad r7, r3, r7\n" + "ldrd r10, r11, [r2, #(160 - 136 + 40)]\n" + "smlad r4, r12, r8, r4\n" + "smlad r5, r12, r9, r5\n" + "ldrd r8, r9, [r2, #(160 - 136 + 64)]\n" + "smlad r6, r12, r10, r6\n" + "smlad r7, r12, r11, r7\n" + "ldrd r10, r11, [r2, #(160 - 136 + 72)]\n" + "smlad r4, r0, r8, r4\n" + "smlad r5, r0, r9, r5\n" + "ldrd r8, r9, [r2, #(160 - 136 + 96)]\n" + "smlad r6, r0, r10, r6\n" + "smlad r7, r0, r11, r7\n" + "ldrd r10, r11, [r2, #(160 - 136 + 104)]\n" + "smlad r4, r14, r8, r4\n" + "smlad r5, r14, r9, r5\n" + "ldrd r8, r9, [r2, #(160 - 136 + 16 + 0)]\n" + "smlad r6, r14, r10, r6\n" + "smlad r7, r14, r11, r7\n" + "ldrd r10, r11, [r2, #(160 - 136 + 16 + 8)]\n" + "stmia r1!, {r4, r5}\n" + "smuad r4, r3, r8\n" + "smuad r5, r3, r9\n" + "ldrd r8, r9, [r2, #(160 - 136 + 16 + 32)]\n" + "stmia r1!, {r6, r7}\n" + "smuad r6, r3, r10\n" + "smuad r7, r3, r11\n" + "ldrd r10, r11, [r2, #(160 - 136 + 16 + 40)]\n" + "smlad r4, r12, r8, r4\n" + "smlad r5, r12, r9, r5\n" + "ldrd r8, r9, [r2, #(160 - 136 + 16 + 64)]\n" + "smlad r6, r12, r10, r6\n" + "smlad r7, r12, r11, r7\n" + "ldrd r10, r11, [r2, #(160 - 136 + 16 + 72)]\n" + "smlad r4, r0, r8, r4\n" + "smlad r5, r0, r9, r5\n" + "ldrd r8, r9, [r2, #(160 - 136 + 16 + 96)]\n" + "smlad r6, r0, r10, r6\n" + "smlad r7, r0, r11, r7\n" + "ldrd r10, r11, [r2, #(160 - 136 + 16 + 104)]\n" + "smlad r4, r14, r8, r4\n" + "smlad r5, r14, r9, r5\n" + "smlad r6, r14, r10, r6\n" + "smlad r7, r14, r11, r7\n" + "pop {r8-r11}\n" + "stmia r1!, {r4, r5, r6, r7}\n" + "pop {r1, r4-r7, pc}\n" + ); +} + +#define sbc_analyze_eight(in, out, consts) \ + ((void (*)(int16_t *, int32_t *, const FIXED_T*)) \ + sbc_analyze_eight_armv6)((in), (out), (consts)) + +static void sbc_analyze_4b_4s_armv6(int16_t *x, int32_t *out, int out_stride) +{ + /* Analyze blocks */ + sbc_analyze_four(x + 12, out, analysis_consts_fixed4_simd_odd); + out += out_stride; + sbc_analyze_four(x + 8, out, analysis_consts_fixed4_simd_even); + out += out_stride; + sbc_analyze_four(x + 4, out, analysis_consts_fixed4_simd_odd); + out += out_stride; + sbc_analyze_four(x + 0, out, analysis_consts_fixed4_simd_even); +} + +static void sbc_analyze_4b_8s_armv6(int16_t *x, int32_t *out, int out_stride) +{ + /* Analyze blocks */ + sbc_analyze_eight(x + 24, out, analysis_consts_fixed8_simd_odd); + out += out_stride; + sbc_analyze_eight(x + 16, out, analysis_consts_fixed8_simd_even); + out += out_stride; + sbc_analyze_eight(x + 8, out, analysis_consts_fixed8_simd_odd); + out += out_stride; + sbc_analyze_eight(x + 0, out, analysis_consts_fixed8_simd_even); +} + +void sbc_init_primitives_armv6(struct sbc_encoder_state *state) +{ + state->sbc_analyze_4b_4s = sbc_analyze_4b_4s_armv6; + state->sbc_analyze_4b_8s = sbc_analyze_4b_8s_armv6; + state->implementation_info = "ARMv6 SIMD"; +} + +#endif diff --git a/src/modules/bluetooth/sbc/sbc_primitives_armv6.h b/src/modules/bluetooth/sbc/sbc_primitives_armv6.h new file mode 100644 index 00000000..1862aede --- /dev/null +++ b/src/modules/bluetooth/sbc/sbc_primitives_armv6.h @@ -0,0 +1,52 @@ +/* + * + * Bluetooth low-complexity, subband codec (SBC) library + * + * Copyright (C) 2008-2010 Nokia Corporation + * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org> + * Copyright (C) 2004-2005 Henryk Ploetz <henryk@ploetzli.ch> + * Copyright (C) 2005-2006 Brad Midgley <bmidgley@xmission.com> + * + * + * This library 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. + * + * This library 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 this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __SBC_PRIMITIVES_ARMV6_H +#define __SBC_PRIMITIVES_ARMV6_H + +#include "sbc_primitives.h" + +#if defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || \ + defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) || \ + defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) || \ + defined(__ARM_ARCH_6M__) || defined(__ARM_ARCH_7__) || \ + defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) || \ + defined(__ARM_ARCH_7M__) +#define SBC_HAVE_ARMV6 1 +#endif + +#if !defined(SBC_HIGH_PRECISION) && (SCALE_OUT_BITS == 15) && \ + defined(__GNUC__) && defined(SBC_HAVE_ARMV6) && \ + defined(__ARM_EABI__) && !defined(__thumb__) && \ + !defined(__ARM_NEON__) + +#define SBC_BUILD_WITH_ARMV6_SUPPORT + +void sbc_init_primitives_armv6(struct sbc_encoder_state *encoder_state); + +#endif + +#endif diff --git a/src/modules/bluetooth/sbc/sbc_primitives_iwmmxt.c b/src/modules/bluetooth/sbc/sbc_primitives_iwmmxt.c new file mode 100644 index 00000000..213967ef --- /dev/null +++ b/src/modules/bluetooth/sbc/sbc_primitives_iwmmxt.c @@ -0,0 +1,304 @@ +/* + * + * Bluetooth low-complexity, subband codec (SBC) library + * + * Copyright (C) 2010 Keith Mok <ek9852@gmail.com> + * Copyright (C) 2008-2010 Nokia Corporation + * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org> + * Copyright (C) 2004-2005 Henryk Ploetz <henryk@ploetzli.ch> + * Copyright (C) 2005-2006 Brad Midgley <bmidgley@xmission.com> + * + * + * This library 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. + * + * This library 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 this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include <stdint.h> +#include <limits.h> +#include "sbc.h" +#include "sbc_math.h" +#include "sbc_tables.h" + +#include "sbc_primitives_iwmmxt.h" + +/* + * IWMMXT optimizations + */ + +#ifdef SBC_BUILD_WITH_IWMMXT_SUPPORT + +static inline void sbc_analyze_four_iwmmxt(const int16_t *in, int32_t *out, + const FIXED_T *consts) +{ + asm volatile ( + "wldrd wr0, [%0]\n" + "tbcstw wr4, %2\n" + "wldrd wr2, [%1]\n" + "wldrd wr1, [%0, #8]\n" + "wldrd wr3, [%1, #8]\n" + "wmadds wr0, wr2, wr0\n" + " wldrd wr6, [%0, #16]\n" + "wmadds wr1, wr3, wr1\n" + " wldrd wr7, [%0, #24]\n" + "waddwss wr0, wr0, wr4\n" + " wldrd wr8, [%1, #16]\n" + "waddwss wr1, wr1, wr4\n" + " wldrd wr9, [%1, #24]\n" + " wmadds wr6, wr8, wr6\n" + " wldrd wr2, [%0, #32]\n" + " wmadds wr7, wr9, wr7\n" + " wldrd wr3, [%0, #40]\n" + " waddwss wr0, wr6, wr0\n" + " wldrd wr4, [%1, #32]\n" + " waddwss wr1, wr7, wr1\n" + " wldrd wr5, [%1, #40]\n" + " wmadds wr2, wr4, wr2\n" + "wldrd wr6, [%0, #48]\n" + " wmadds wr3, wr5, wr3\n" + "wldrd wr7, [%0, #56]\n" + " waddwss wr0, wr2, wr0\n" + "wldrd wr8, [%1, #48]\n" + " waddwss wr1, wr3, wr1\n" + "wldrd wr9, [%1, #56]\n" + "wmadds wr6, wr8, wr6\n" + " wldrd wr2, [%0, #64]\n" + "wmadds wr7, wr9, wr7\n" + " wldrd wr3, [%0, #72]\n" + "waddwss wr0, wr6, wr0\n" + " wldrd wr4, [%1, #64]\n" + "waddwss wr1, wr7, wr1\n" + " wldrd wr5, [%1, #72]\n" + " wmadds wr2, wr4, wr2\n" + "tmcr wcgr0, %4\n" + " wmadds wr3, wr5, wr3\n" + " waddwss wr0, wr2, wr0\n" + " waddwss wr1, wr3, wr1\n" + "\n" + "wsrawg wr0, wr0, wcgr0\n" + " wldrd wr4, [%1, #80]\n" + "wsrawg wr1, wr1, wcgr0\n" + " wldrd wr5, [%1, #88]\n" + "wpackwss wr0, wr0, wr0\n" + " wldrd wr6, [%1, #96]\n" + "wpackwss wr1, wr1, wr1\n" + "wmadds wr2, wr5, wr0\n" + " wldrd wr7, [%1, #104]\n" + "wmadds wr0, wr4, wr0\n" + "\n" + " wmadds wr3, wr7, wr1\n" + " wmadds wr1, wr6, wr1\n" + " waddwss wr2, wr3, wr2\n" + " waddwss wr0, wr1, wr0\n" + "\n" + "wstrd wr0, [%3]\n" + "wstrd wr2, [%3, #8]\n" + : + : "r" (in), "r" (consts), + "r" (1 << (SBC_PROTO_FIXED4_SCALE - 1)), "r" (out), + "r" (SBC_PROTO_FIXED4_SCALE) + : "wr0", "wr1", "wr2", "wr3", "wr4", "wr5", "wr6", "wr7", + "wr8", "wr9", "wcgr0", "memory"); +} + +static inline void sbc_analyze_eight_iwmmxt(const int16_t *in, int32_t *out, + const FIXED_T *consts) +{ + asm volatile ( + "wldrd wr0, [%0]\n" + "tbcstw wr15, %2\n" + "wldrd wr1, [%0, #8]\n" + "wldrd wr2, [%0, #16]\n" + "wldrd wr3, [%0, #24]\n" + "wldrd wr4, [%1]\n" + "wldrd wr5, [%1, #8]\n" + "wldrd wr6, [%1, #16]\n" + "wldrd wr7, [%1, #24]\n" + "wmadds wr0, wr0, wr4\n" + " wldrd wr8, [%1, #32]\n" + "wmadds wr1, wr1, wr5\n" + " wldrd wr9, [%1, #40]\n" + "wmadds wr2, wr2, wr6\n" + " wldrd wr10, [%1, #48]\n" + "wmadds wr3, wr3, wr7\n" + " wldrd wr11, [%1, #56]\n" + "waddwss wr0, wr0, wr15\n" + " wldrd wr4, [%0, #32]\n" + "waddwss wr1, wr1, wr15\n" + " wldrd wr5, [%0, #40]\n" + "waddwss wr2, wr2, wr15\n" + " wldrd wr6, [%0, #48]\n" + "waddwss wr3, wr3, wr15\n" + " wldrd wr7, [%0, #56]\n" + " wmadds wr4, wr4, wr8\n" + " wldrd wr12, [%0, #64]\n" + " wmadds wr5, wr5, wr9\n" + " wldrd wr13, [%0, #72]\n" + " wmadds wr6, wr6, wr10\n" + " wldrd wr14, [%0, #80]\n" + " wmadds wr7, wr7, wr11\n" + " wldrd wr15, [%0, #88]\n" + " waddwss wr0, wr4, wr0\n" + " wldrd wr8, [%1, #64]\n" + " waddwss wr1, wr5, wr1\n" + " wldrd wr9, [%1, #72]\n" + " waddwss wr2, wr6, wr2\n" + " wldrd wr10, [%1, #80]\n" + " waddwss wr3, wr7, wr3\n" + " wldrd wr11, [%1, #88]\n" + " wmadds wr12, wr12, wr8\n" + "wldrd wr4, [%0, #96]\n" + " wmadds wr13, wr13, wr9\n" + "wldrd wr5, [%0, #104]\n" + " wmadds wr14, wr14, wr10\n" + "wldrd wr6, [%0, #112]\n" + " wmadds wr15, wr15, wr11\n" + "wldrd wr7, [%0, #120]\n" + " waddwss wr0, wr12, wr0\n" + "wldrd wr8, [%1, #96]\n" + " waddwss wr1, wr13, wr1\n" + "wldrd wr9, [%1, #104]\n" + " waddwss wr2, wr14, wr2\n" + "wldrd wr10, [%1, #112]\n" + " waddwss wr3, wr15, wr3\n" + "wldrd wr11, [%1, #120]\n" + "wmadds wr4, wr4, wr8\n" + " wldrd wr12, [%0, #128]\n" + "wmadds wr5, wr5, wr9\n" + " wldrd wr13, [%0, #136]\n" + "wmadds wr6, wr6, wr10\n" + " wldrd wr14, [%0, #144]\n" + "wmadds wr7, wr7, wr11\n" + " wldrd wr15, [%0, #152]\n" + "waddwss wr0, wr4, wr0\n" + " wldrd wr8, [%1, #128]\n" + "waddwss wr1, wr5, wr1\n" + " wldrd wr9, [%1, #136]\n" + "waddwss wr2, wr6, wr2\n" + " wldrd wr10, [%1, #144]\n" + " waddwss wr3, wr7, wr3\n" + " wldrd wr11, [%1, #152]\n" + " wmadds wr12, wr12, wr8\n" + "tmcr wcgr0, %4\n" + " wmadds wr13, wr13, wr9\n" + " wmadds wr14, wr14, wr10\n" + " wmadds wr15, wr15, wr11\n" + " waddwss wr0, wr12, wr0\n" + " waddwss wr1, wr13, wr1\n" + " waddwss wr2, wr14, wr2\n" + " waddwss wr3, wr15, wr3\n" + "\n" + "wsrawg wr0, wr0, wcgr0\n" + "wsrawg wr1, wr1, wcgr0\n" + "wsrawg wr2, wr2, wcgr0\n" + "wsrawg wr3, wr3, wcgr0\n" + "\n" + "wpackwss wr0, wr0, wr0\n" + "wpackwss wr1, wr1, wr1\n" + " wldrd wr4, [%1, #160]\n" + "wpackwss wr2, wr2, wr2\n" + " wldrd wr5, [%1, #168]\n" + "wpackwss wr3, wr3, wr3\n" + " wldrd wr6, [%1, #192]\n" + " wmadds wr4, wr4, wr0\n" + " wldrd wr7, [%1, #200]\n" + " wmadds wr5, wr5, wr0\n" + " wldrd wr8, [%1, #224]\n" + " wmadds wr6, wr6, wr1\n" + " wldrd wr9, [%1, #232]\n" + " wmadds wr7, wr7, wr1\n" + " waddwss wr4, wr6, wr4\n" + " waddwss wr5, wr7, wr5\n" + " wmadds wr8, wr8, wr2\n" + "wldrd wr6, [%1, #256]\n" + " wmadds wr9, wr9, wr2\n" + "wldrd wr7, [%1, #264]\n" + "waddwss wr4, wr8, wr4\n" + " waddwss wr5, wr9, wr5\n" + "wmadds wr6, wr6, wr3\n" + "wmadds wr7, wr7, wr3\n" + "waddwss wr4, wr6, wr4\n" + "waddwss wr5, wr7, wr5\n" + "\n" + "wstrd wr4, [%3]\n" + "wstrd wr5, [%3, #8]\n" + "\n" + "wldrd wr6, [%1, #176]\n" + "wldrd wr5, [%1, #184]\n" + "wmadds wr5, wr5, wr0\n" + "wldrd wr8, [%1, #208]\n" + "wmadds wr0, wr6, wr0\n" + "wldrd wr9, [%1, #216]\n" + "wmadds wr9, wr9, wr1\n" + "wldrd wr6, [%1, #240]\n" + "wmadds wr1, wr8, wr1\n" + "wldrd wr7, [%1, #248]\n" + "waddwss wr0, wr1, wr0\n" + "waddwss wr5, wr9, wr5\n" + "wmadds wr7, wr7, wr2\n" + "wldrd wr8, [%1, #272]\n" + "wmadds wr2, wr6, wr2\n" + "wldrd wr9, [%1, #280]\n" + "waddwss wr0, wr2, wr0\n" + "waddwss wr5, wr7, wr5\n" + "wmadds wr9, wr9, wr3\n" + "wmadds wr3, wr8, wr3\n" + "waddwss wr0, wr3, wr0\n" + "waddwss wr5, wr9, wr5\n" + "\n" + "wstrd wr0, [%3, #16]\n" + "wstrd wr5, [%3, #24]\n" + : + : "r" (in), "r" (consts), + "r" (1 << (SBC_PROTO_FIXED8_SCALE - 1)), "r" (out), + "r" (SBC_PROTO_FIXED8_SCALE) + : "wr0", "wr1", "wr2", "wr3", "wr4", "wr5", "wr6", "wr7", + "wr8", "wr9", "wr10", "wr11", "wr12", "wr13", "wr14", "wr15", + "wcgr0", "memory"); +} + +static inline void sbc_analyze_4b_4s_iwmmxt(int16_t *x, int32_t *out, + int out_stride) +{ + /* Analyze blocks */ + sbc_analyze_four_iwmmxt(x + 12, out, analysis_consts_fixed4_simd_odd); + out += out_stride; + sbc_analyze_four_iwmmxt(x + 8, out, analysis_consts_fixed4_simd_even); + out += out_stride; + sbc_analyze_four_iwmmxt(x + 4, out, analysis_consts_fixed4_simd_odd); + out += out_stride; + sbc_analyze_four_iwmmxt(x + 0, out, analysis_consts_fixed4_simd_even); +} + +static inline void sbc_analyze_4b_8s_iwmmxt(int16_t *x, int32_t *out, + int out_stride) +{ + /* Analyze blocks */ + sbc_analyze_eight_iwmmxt(x + 24, out, analysis_consts_fixed8_simd_odd); + out += out_stride; + sbc_analyze_eight_iwmmxt(x + 16, out, analysis_consts_fixed8_simd_even); + out += out_stride; + sbc_analyze_eight_iwmmxt(x + 8, out, analysis_consts_fixed8_simd_odd); + out += out_stride; + sbc_analyze_eight_iwmmxt(x + 0, out, analysis_consts_fixed8_simd_even); +} + +void sbc_init_primitives_iwmmxt(struct sbc_encoder_state *state) +{ + state->sbc_analyze_4b_4s = sbc_analyze_4b_4s_iwmmxt; + state->sbc_analyze_4b_8s = sbc_analyze_4b_8s_iwmmxt; + state->implementation_info = "IWMMXT"; +} + +#endif diff --git a/src/modules/bluetooth/sbc/sbc_primitives_iwmmxt.h b/src/modules/bluetooth/sbc/sbc_primitives_iwmmxt.h new file mode 100644 index 00000000..b535e686 --- /dev/null +++ b/src/modules/bluetooth/sbc/sbc_primitives_iwmmxt.h @@ -0,0 +1,42 @@ +/* + * + * Bluetooth low-complexity, subband codec (SBC) library + * + * Copyright (C) 2010 Keith Mok <ek9852@gmail.com> + * Copyright (C) 2008-2010 Nokia Corporation + * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org> + * Copyright (C) 2004-2005 Henryk Ploetz <henryk@ploetzli.ch> + * Copyright (C) 2005-2006 Brad Midgley <bmidgley@xmission.com> + * + * + * This library 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. + * + * This library 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 this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __SBC_PRIMITIVES_IWMMXT_H +#define __SBC_PRIMITIVES_IWMMXT_H + +#include "sbc_primitives.h" + +#if defined(__GNUC__) && defined(__IWMMXT__) && \ + !defined(SBC_HIGH_PRECISION) && (SCALE_OUT_BITS == 15) + +#define SBC_BUILD_WITH_IWMMXT_SUPPORT + +void sbc_init_primitives_iwmmxt(struct sbc_encoder_state *encoder_state); + +#endif + +#endif diff --git a/src/modules/bluetooth/sbc_primitives_mmx.c b/src/modules/bluetooth/sbc/sbc_primitives_mmx.c index 08e9ca28..ab89d074 100644 --- a/src/modules/bluetooth/sbc_primitives_mmx.c +++ b/src/modules/bluetooth/sbc/sbc_primitives_mmx.c @@ -100,7 +100,7 @@ static inline void sbc_analyze_four_mmx(const int16_t *in, int32_t *out, : : "r" (in), "r" (consts), "r" (&round_c), "r" (out), "i" (SBC_PROTO_FIXED4_SCALE) - : "memory"); + : "cc", "memory"); } static inline void sbc_analyze_eight_mmx(const int16_t *in, int32_t *out, @@ -242,7 +242,7 @@ static inline void sbc_analyze_eight_mmx(const int16_t *in, int32_t *out, : : "r" (in), "r" (consts), "r" (&round_c), "r" (out), "i" (SBC_PROTO_FIXED8_SCALE) - : "memory"); + : "cc", "memory"); } static inline void sbc_analyze_4b_4s_mmx(int16_t *x, int32_t *out, @@ -275,6 +275,59 @@ static inline void sbc_analyze_4b_8s_mmx(int16_t *x, int32_t *out, asm volatile ("emms\n"); } +static void sbc_calc_scalefactors_mmx( + int32_t sb_sample_f[16][2][8], + uint32_t scale_factor[2][8], + int blocks, int channels, int subbands) +{ + static const SBC_ALIGNED int32_t consts[2] = { + 1 << SCALE_OUT_BITS, + 1 << SCALE_OUT_BITS, + }; + int ch, sb; + intptr_t blk; + for (ch = 0; ch < channels; ch++) { + for (sb = 0; sb < subbands; sb += 2) { + blk = (blocks - 1) * (((char *) &sb_sample_f[1][0][0] - + (char *) &sb_sample_f[0][0][0])); + asm volatile ( + "movq (%4), %%mm0\n" + "1:\n" + "movq (%1, %0), %%mm1\n" + "pxor %%mm2, %%mm2\n" + "pcmpgtd %%mm2, %%mm1\n" + "paddd (%1, %0), %%mm1\n" + "pcmpgtd %%mm1, %%mm2\n" + "pxor %%mm2, %%mm1\n" + + "por %%mm1, %%mm0\n" + + "sub %2, %0\n" + "jns 1b\n" + + "movd %%mm0, %k0\n" + "psrlq $32, %%mm0\n" + "bsrl %k0, %k0\n" + "subl %5, %k0\n" + "movl %k0, (%3)\n" + + "movd %%mm0, %k0\n" + "bsrl %k0, %k0\n" + "subl %5, %k0\n" + "movl %k0, 4(%3)\n" + : "+r" (blk) + : "r" (&sb_sample_f[0][ch][sb]), + "i" ((char *) &sb_sample_f[1][0][0] - + (char *) &sb_sample_f[0][0][0]), + "r" (&scale_factor[ch][sb]), + "r" (&consts), + "i" (SCALE_OUT_BITS) + : "cc", "memory"); + } + } + asm volatile ("emms\n"); +} + static int check_mmx_support(void) { #ifdef __amd64__ @@ -313,6 +366,7 @@ void sbc_init_primitives_mmx(struct sbc_encoder_state *state) if (check_mmx_support()) { state->sbc_analyze_4b_4s = sbc_analyze_4b_4s_mmx; state->sbc_analyze_4b_8s = sbc_analyze_4b_8s_mmx; + state->sbc_calc_scalefactors = sbc_calc_scalefactors_mmx; state->implementation_info = "MMX"; } } diff --git a/src/modules/bluetooth/sbc_primitives_mmx.h b/src/modules/bluetooth/sbc/sbc_primitives_mmx.h index c1e44a5d..c1e44a5d 100644 --- a/src/modules/bluetooth/sbc_primitives_mmx.h +++ b/src/modules/bluetooth/sbc/sbc_primitives_mmx.h diff --git a/src/modules/bluetooth/sbc/sbc_primitives_neon.c b/src/modules/bluetooth/sbc/sbc_primitives_neon.c new file mode 100644 index 00000000..c233d3c6 --- /dev/null +++ b/src/modules/bluetooth/sbc/sbc_primitives_neon.c @@ -0,0 +1,892 @@ +/* + * + * Bluetooth low-complexity, subband codec (SBC) library + * + * Copyright (C) 2004-2009 Marcel Holtmann <marcel@holtmann.org> + * Copyright (C) 2004-2005 Henryk Ploetz <henryk@ploetzli.ch> + * Copyright (C) 2005-2006 Brad Midgley <bmidgley@xmission.com> + * + * + * This library 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. + * + * This library 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 this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include <stdint.h> +#include <limits.h> +#include "sbc.h" +#include "sbc_math.h" +#include "sbc_tables.h" + +#include "sbc_primitives_neon.h" + +/* + * ARM NEON optimizations + */ + +#ifdef SBC_BUILD_WITH_NEON_SUPPORT + +static inline void _sbc_analyze_four_neon(const int16_t *in, int32_t *out, + const FIXED_T *consts) +{ + /* TODO: merge even and odd cases (or even merge all four calls to this + * function) in order to have only aligned reads from 'in' array + * and reduce number of load instructions */ + asm volatile ( + "vld1.16 {d4, d5}, [%0, :64]!\n" + "vld1.16 {d8, d9}, [%1, :128]!\n" + + "vmull.s16 q0, d4, d8\n" + "vld1.16 {d6, d7}, [%0, :64]!\n" + "vmull.s16 q1, d5, d9\n" + "vld1.16 {d10, d11}, [%1, :128]!\n" + + "vmlal.s16 q0, d6, d10\n" + "vld1.16 {d4, d5}, [%0, :64]!\n" + "vmlal.s16 q1, d7, d11\n" + "vld1.16 {d8, d9}, [%1, :128]!\n" + + "vmlal.s16 q0, d4, d8\n" + "vld1.16 {d6, d7}, [%0, :64]!\n" + "vmlal.s16 q1, d5, d9\n" + "vld1.16 {d10, d11}, [%1, :128]!\n" + + "vmlal.s16 q0, d6, d10\n" + "vld1.16 {d4, d5}, [%0, :64]!\n" + "vmlal.s16 q1, d7, d11\n" + "vld1.16 {d8, d9}, [%1, :128]!\n" + + "vmlal.s16 q0, d4, d8\n" + "vmlal.s16 q1, d5, d9\n" + + "vpadd.s32 d0, d0, d1\n" + "vpadd.s32 d1, d2, d3\n" + + "vrshrn.s32 d0, q0, %3\n" + + "vld1.16 {d2, d3, d4, d5}, [%1, :128]!\n" + + "vdup.i32 d1, d0[1]\n" /* TODO: can be eliminated */ + "vdup.i32 d0, d0[0]\n" /* TODO: can be eliminated */ + + "vmull.s16 q3, d2, d0\n" + "vmull.s16 q4, d3, d0\n" + "vmlal.s16 q3, d4, d1\n" + "vmlal.s16 q4, d5, d1\n" + + "vpadd.s32 d0, d6, d7\n" /* TODO: can be eliminated */ + "vpadd.s32 d1, d8, d9\n" /* TODO: can be eliminated */ + + "vst1.32 {d0, d1}, [%2, :128]\n" + : "+r" (in), "+r" (consts) + : "r" (out), + "i" (SBC_PROTO_FIXED4_SCALE) + : "memory", + "d0", "d1", "d2", "d3", "d4", "d5", + "d6", "d7", "d8", "d9", "d10", "d11"); +} + +static inline void _sbc_analyze_eight_neon(const int16_t *in, int32_t *out, + const FIXED_T *consts) +{ + /* TODO: merge even and odd cases (or even merge all four calls to this + * function) in order to have only aligned reads from 'in' array + * and reduce number of load instructions */ + asm volatile ( + "vld1.16 {d4, d5}, [%0, :64]!\n" + "vld1.16 {d8, d9}, [%1, :128]!\n" + + "vmull.s16 q6, d4, d8\n" + "vld1.16 {d6, d7}, [%0, :64]!\n" + "vmull.s16 q7, d5, d9\n" + "vld1.16 {d10, d11}, [%1, :128]!\n" + "vmull.s16 q8, d6, d10\n" + "vld1.16 {d4, d5}, [%0, :64]!\n" + "vmull.s16 q9, d7, d11\n" + "vld1.16 {d8, d9}, [%1, :128]!\n" + + "vmlal.s16 q6, d4, d8\n" + "vld1.16 {d6, d7}, [%0, :64]!\n" + "vmlal.s16 q7, d5, d9\n" + "vld1.16 {d10, d11}, [%1, :128]!\n" + "vmlal.s16 q8, d6, d10\n" + "vld1.16 {d4, d5}, [%0, :64]!\n" + "vmlal.s16 q9, d7, d11\n" + "vld1.16 {d8, d9}, [%1, :128]!\n" + + "vmlal.s16 q6, d4, d8\n" + "vld1.16 {d6, d7}, [%0, :64]!\n" + "vmlal.s16 q7, d5, d9\n" + "vld1.16 {d10, d11}, [%1, :128]!\n" + "vmlal.s16 q8, d6, d10\n" + "vld1.16 {d4, d5}, [%0, :64]!\n" + "vmlal.s16 q9, d7, d11\n" + "vld1.16 {d8, d9}, [%1, :128]!\n" + + "vmlal.s16 q6, d4, d8\n" + "vld1.16 {d6, d7}, [%0, :64]!\n" + "vmlal.s16 q7, d5, d9\n" + "vld1.16 {d10, d11}, [%1, :128]!\n" + "vmlal.s16 q8, d6, d10\n" + "vld1.16 {d4, d5}, [%0, :64]!\n" + "vmlal.s16 q9, d7, d11\n" + "vld1.16 {d8, d9}, [%1, :128]!\n" + + "vmlal.s16 q6, d4, d8\n" + "vld1.16 {d6, d7}, [%0, :64]!\n" + "vmlal.s16 q7, d5, d9\n" + "vld1.16 {d10, d11}, [%1, :128]!\n" + + "vmlal.s16 q8, d6, d10\n" + "vmlal.s16 q9, d7, d11\n" + + "vpadd.s32 d0, d12, d13\n" + "vpadd.s32 d1, d14, d15\n" + "vpadd.s32 d2, d16, d17\n" + "vpadd.s32 d3, d18, d19\n" + + "vrshr.s32 q0, q0, %3\n" + "vrshr.s32 q1, q1, %3\n" + "vmovn.s32 d0, q0\n" + "vmovn.s32 d1, q1\n" + + "vdup.i32 d3, d1[1]\n" /* TODO: can be eliminated */ + "vdup.i32 d2, d1[0]\n" /* TODO: can be eliminated */ + "vdup.i32 d1, d0[1]\n" /* TODO: can be eliminated */ + "vdup.i32 d0, d0[0]\n" /* TODO: can be eliminated */ + + "vld1.16 {d4, d5}, [%1, :128]!\n" + "vmull.s16 q6, d4, d0\n" + "vld1.16 {d6, d7}, [%1, :128]!\n" + "vmull.s16 q7, d5, d0\n" + "vmull.s16 q8, d6, d0\n" + "vmull.s16 q9, d7, d0\n" + + "vld1.16 {d4, d5}, [%1, :128]!\n" + "vmlal.s16 q6, d4, d1\n" + "vld1.16 {d6, d7}, [%1, :128]!\n" + "vmlal.s16 q7, d5, d1\n" + "vmlal.s16 q8, d6, d1\n" + "vmlal.s16 q9, d7, d1\n" + + "vld1.16 {d4, d5}, [%1, :128]!\n" + "vmlal.s16 q6, d4, d2\n" + "vld1.16 {d6, d7}, [%1, :128]!\n" + "vmlal.s16 q7, d5, d2\n" + "vmlal.s16 q8, d6, d2\n" + "vmlal.s16 q9, d7, d2\n" + + "vld1.16 {d4, d5}, [%1, :128]!\n" + "vmlal.s16 q6, d4, d3\n" + "vld1.16 {d6, d7}, [%1, :128]!\n" + "vmlal.s16 q7, d5, d3\n" + "vmlal.s16 q8, d6, d3\n" + "vmlal.s16 q9, d7, d3\n" + + "vpadd.s32 d0, d12, d13\n" /* TODO: can be eliminated */ + "vpadd.s32 d1, d14, d15\n" /* TODO: can be eliminated */ + "vpadd.s32 d2, d16, d17\n" /* TODO: can be eliminated */ + "vpadd.s32 d3, d18, d19\n" /* TODO: can be eliminated */ + + "vst1.32 {d0, d1, d2, d3}, [%2, :128]\n" + : "+r" (in), "+r" (consts) + : "r" (out), + "i" (SBC_PROTO_FIXED8_SCALE) + : "memory", + "d0", "d1", "d2", "d3", "d4", "d5", + "d6", "d7", "d8", "d9", "d10", "d11", + "d12", "d13", "d14", "d15", "d16", "d17", + "d18", "d19"); +} + +static inline void sbc_analyze_4b_4s_neon(int16_t *x, + int32_t *out, int out_stride) +{ + /* Analyze blocks */ + _sbc_analyze_four_neon(x + 12, out, analysis_consts_fixed4_simd_odd); + out += out_stride; + _sbc_analyze_four_neon(x + 8, out, analysis_consts_fixed4_simd_even); + out += out_stride; + _sbc_analyze_four_neon(x + 4, out, analysis_consts_fixed4_simd_odd); + out += out_stride; + _sbc_analyze_four_neon(x + 0, out, analysis_consts_fixed4_simd_even); +} + +static inline void sbc_analyze_4b_8s_neon(int16_t *x, + int32_t *out, int out_stride) +{ + /* Analyze blocks */ + _sbc_analyze_eight_neon(x + 24, out, analysis_consts_fixed8_simd_odd); + out += out_stride; + _sbc_analyze_eight_neon(x + 16, out, analysis_consts_fixed8_simd_even); + out += out_stride; + _sbc_analyze_eight_neon(x + 8, out, analysis_consts_fixed8_simd_odd); + out += out_stride; + _sbc_analyze_eight_neon(x + 0, out, analysis_consts_fixed8_simd_even); +} + +static void sbc_calc_scalefactors_neon( + int32_t sb_sample_f[16][2][8], + uint32_t scale_factor[2][8], + int blocks, int channels, int subbands) +{ + int ch, sb; + for (ch = 0; ch < channels; ch++) { + for (sb = 0; sb < subbands; sb += 4) { + int blk = blocks; + int32_t *in = &sb_sample_f[0][ch][sb]; + asm volatile ( + "vmov.s32 q0, #0\n" + "vmov.s32 q1, %[c1]\n" + "vmov.s32 q14, #1\n" + "vmov.s32 q15, %[c2]\n" + "vadd.s32 q1, q1, q14\n" + "1:\n" + "vld1.32 {d16, d17}, [%[in], :128], %[inc]\n" + "vabs.s32 q8, q8\n" + "vld1.32 {d18, d19}, [%[in], :128], %[inc]\n" + "vabs.s32 q9, q9\n" + "vld1.32 {d20, d21}, [%[in], :128], %[inc]\n" + "vabs.s32 q10, q10\n" + "vld1.32 {d22, d23}, [%[in], :128], %[inc]\n" + "vabs.s32 q11, q11\n" + "vmax.s32 q0, q0, q8\n" + "vmax.s32 q1, q1, q9\n" + "vmax.s32 q0, q0, q10\n" + "vmax.s32 q1, q1, q11\n" + "subs %[blk], %[blk], #4\n" + "bgt 1b\n" + "vmax.s32 q0, q0, q1\n" + "vsub.s32 q0, q0, q14\n" + "vclz.s32 q0, q0\n" + "vsub.s32 q0, q15, q0\n" + "vst1.32 {d0, d1}, [%[out], :128]\n" + : + [blk] "+r" (blk), + [in] "+r" (in) + : + [inc] "r" ((char *) &sb_sample_f[1][0][0] - + (char *) &sb_sample_f[0][0][0]), + [out] "r" (&scale_factor[ch][sb]), + [c1] "i" (1 << SCALE_OUT_BITS), + [c2] "i" (31 - SCALE_OUT_BITS) + : "d0", "d1", "d2", "d3", "d16", "d17", "d18", "d19", + "d20", "d21", "d22", "d23", "d24", "d25", "d26", + "d27", "d28", "d29", "d30", "d31", "cc", "memory"); + } + } +} + +int sbc_calc_scalefactors_j_neon( + int32_t sb_sample_f[16][2][8], + uint32_t scale_factor[2][8], + int blocks, int subbands) +{ + static SBC_ALIGNED int32_t joint_bits_mask[8] = { + 8, 4, 2, 1, 128, 64, 32, 16 + }; + int joint, i; + int32_t *in0, *in1; + int32_t *in = &sb_sample_f[0][0][0]; + uint32_t *out0, *out1; + uint32_t *out = &scale_factor[0][0]; + int32_t *consts = joint_bits_mask; + + i = subbands; + + asm volatile ( + /* + * constants: q13 = (31 - SCALE_OUT_BITS), q14 = 1 + * input: q0 = ((1 << SCALE_OUT_BITS) + 1) + * %[in0] - samples for channel 0 + * %[in1] - samples for shannel 1 + * output: q0, q1 - scale factors without joint stereo + * q2, q3 - scale factors with joint stereo + * q15 - joint stereo selection mask + */ + ".macro calc_scalefactors\n" + "vmov.s32 q1, q0\n" + "vmov.s32 q2, q0\n" + "vmov.s32 q3, q0\n" + "mov %[i], %[blocks]\n" + "1:\n" + "vld1.32 {d18, d19}, [%[in1], :128], %[inc]\n" + "vbic.s32 q11, q9, q14\n" + "vld1.32 {d16, d17}, [%[in0], :128], %[inc]\n" + "vhadd.s32 q10, q8, q11\n" + "vhsub.s32 q11, q8, q11\n" + "vabs.s32 q8, q8\n" + "vabs.s32 q9, q9\n" + "vabs.s32 q10, q10\n" + "vabs.s32 q11, q11\n" + "vmax.s32 q0, q0, q8\n" + "vmax.s32 q1, q1, q9\n" + "vmax.s32 q2, q2, q10\n" + "vmax.s32 q3, q3, q11\n" + "subs %[i], %[i], #1\n" + "bgt 1b\n" + "vsub.s32 q0, q0, q14\n" + "vsub.s32 q1, q1, q14\n" + "vsub.s32 q2, q2, q14\n" + "vsub.s32 q3, q3, q14\n" + "vclz.s32 q0, q0\n" + "vclz.s32 q1, q1\n" + "vclz.s32 q2, q2\n" + "vclz.s32 q3, q3\n" + "vsub.s32 q0, q13, q0\n" + "vsub.s32 q1, q13, q1\n" + "vsub.s32 q2, q13, q2\n" + "vsub.s32 q3, q13, q3\n" + ".endm\n" + /* + * constants: q14 = 1 + * input: q15 - joint stereo selection mask + * %[in0] - value set by calc_scalefactors macro + * %[in1] - value set by calc_scalefactors macro + */ + ".macro update_joint_stereo_samples\n" + "sub %[out1], %[in1], %[inc]\n" + "sub %[out0], %[in0], %[inc]\n" + "sub %[in1], %[in1], %[inc], asl #1\n" + "sub %[in0], %[in0], %[inc], asl #1\n" + "vld1.32 {d18, d19}, [%[in1], :128]\n" + "vbic.s32 q11, q9, q14\n" + "vld1.32 {d16, d17}, [%[in0], :128]\n" + "vld1.32 {d2, d3}, [%[out1], :128]\n" + "vbic.s32 q3, q1, q14\n" + "vld1.32 {d0, d1}, [%[out0], :128]\n" + "vhsub.s32 q10, q8, q11\n" + "vhadd.s32 q11, q8, q11\n" + "vhsub.s32 q2, q0, q3\n" + "vhadd.s32 q3, q0, q3\n" + "vbif.s32 q10, q9, q15\n" + "vbif.s32 d22, d16, d30\n" + "sub %[inc], %[zero], %[inc], asl #1\n" + "sub %[i], %[blocks], #2\n" + "2:\n" + "vbif.s32 d23, d17, d31\n" + "vst1.32 {d20, d21}, [%[in1], :128], %[inc]\n" + "vbif.s32 d4, d2, d30\n" + "vld1.32 {d18, d19}, [%[in1], :128]\n" + "vbif.s32 d5, d3, d31\n" + "vst1.32 {d22, d23}, [%[in0], :128], %[inc]\n" + "vbif.s32 d6, d0, d30\n" + "vld1.32 {d16, d17}, [%[in0], :128]\n" + "vbif.s32 d7, d1, d31\n" + "vst1.32 {d4, d5}, [%[out1], :128], %[inc]\n" + "vbic.s32 q11, q9, q14\n" + "vld1.32 {d2, d3}, [%[out1], :128]\n" + "vst1.32 {d6, d7}, [%[out0], :128], %[inc]\n" + "vbic.s32 q3, q1, q14\n" + "vld1.32 {d0, d1}, [%[out0], :128]\n" + "vhsub.s32 q10, q8, q11\n" + "vhadd.s32 q11, q8, q11\n" + "vhsub.s32 q2, q0, q3\n" + "vhadd.s32 q3, q0, q3\n" + "vbif.s32 q10, q9, q15\n" + "vbif.s32 d22, d16, d30\n" + "subs %[i], %[i], #2\n" + "bgt 2b\n" + "sub %[inc], %[zero], %[inc], asr #1\n" + "vbif.s32 d23, d17, d31\n" + "vst1.32 {d20, d21}, [%[in1], :128]\n" + "vbif.s32 q2, q1, q15\n" + "vst1.32 {d22, d23}, [%[in0], :128]\n" + "vbif.s32 q3, q0, q15\n" + "vst1.32 {d4, d5}, [%[out1], :128]\n" + "vst1.32 {d6, d7}, [%[out0], :128]\n" + ".endm\n" + + "vmov.s32 q14, #1\n" + "vmov.s32 q13, %[c2]\n" + + "cmp %[i], #4\n" + "bne 8f\n" + + "4:\n" /* 4 subbands */ + "add %[in0], %[in], #0\n" + "add %[in1], %[in], #32\n" + "add %[out0], %[out], #0\n" + "add %[out1], %[out], #32\n" + "vmov.s32 q0, %[c1]\n" + "vadd.s32 q0, q0, q14\n" + + "calc_scalefactors\n" + + /* check whether to use joint stereo for subbands 0, 1, 2 */ + "vadd.s32 q15, q0, q1\n" + "vadd.s32 q9, q2, q3\n" + "vmov.s32 d31[1], %[zero]\n" /* last subband -> no joint */ + "vld1.32 {d16, d17}, [%[consts], :128]!\n" + "vcgt.s32 q15, q15, q9\n" + + /* calculate and save to memory 'joint' variable */ + /* update and save scale factors to memory */ + " vand.s32 q8, q8, q15\n" + "vbit.s32 q0, q2, q15\n" + " vpadd.s32 d16, d16, d17\n" + "vbit.s32 q1, q3, q15\n" + " vpadd.s32 d16, d16, d16\n" + "vst1.32 {d0, d1}, [%[out0], :128]\n" + "vst1.32 {d2, d3}, [%[out1], :128]\n" + " vst1.32 {d16[0]}, [%[joint]]\n" + + "update_joint_stereo_samples\n" + "b 9f\n" + + "8:\n" /* 8 subbands */ + "add %[in0], %[in], #16\n\n" + "add %[in1], %[in], #48\n" + "add %[out0], %[out], #16\n\n" + "add %[out1], %[out], #48\n" + "vmov.s32 q0, %[c1]\n" + "vadd.s32 q0, q0, q14\n" + + "calc_scalefactors\n" + + /* check whether to use joint stereo for subbands 4, 5, 6 */ + "vadd.s32 q15, q0, q1\n" + "vadd.s32 q9, q2, q3\n" + "vmov.s32 d31[1], %[zero]\n" /* last subband -> no joint */ + "vld1.32 {d16, d17}, [%[consts], :128]!\n" + "vcgt.s32 q15, q15, q9\n" + + /* calculate part of 'joint' variable and save it to d24 */ + /* update and save scale factors to memory */ + " vand.s32 q8, q8, q15\n" + "vbit.s32 q0, q2, q15\n" + " vpadd.s32 d16, d16, d17\n" + "vbit.s32 q1, q3, q15\n" + "vst1.32 {d0, d1}, [%[out0], :128]\n" + "vst1.32 {d2, d3}, [%[out1], :128]\n" + " vpadd.s32 d24, d16, d16\n" + + "update_joint_stereo_samples\n" + + "add %[in0], %[in], #0\n" + "add %[in1], %[in], #32\n" + "add %[out0], %[out], #0\n\n" + "add %[out1], %[out], #32\n" + "vmov.s32 q0, %[c1]\n" + "vadd.s32 q0, q0, q14\n" + + "calc_scalefactors\n" + + /* check whether to use joint stereo for subbands 0, 1, 2, 3 */ + "vadd.s32 q15, q0, q1\n" + "vadd.s32 q9, q2, q3\n" + "vld1.32 {d16, d17}, [%[consts], :128]!\n" + "vcgt.s32 q15, q15, q9\n" + + /* combine last part of 'joint' with d24 and save to memory */ + /* update and save scale factors to memory */ + " vand.s32 q8, q8, q15\n" + "vbit.s32 q0, q2, q15\n" + " vpadd.s32 d16, d16, d17\n" + "vbit.s32 q1, q3, q15\n" + " vpadd.s32 d16, d16, d16\n" + "vst1.32 {d0, d1}, [%[out0], :128]\n" + " vadd.s32 d16, d16, d24\n" + "vst1.32 {d2, d3}, [%[out1], :128]\n" + " vst1.32 {d16[0]}, [%[joint]]\n" + + "update_joint_stereo_samples\n" + "9:\n" + ".purgem calc_scalefactors\n" + ".purgem update_joint_stereo_samples\n" + : + [i] "+&r" (i), + [in] "+&r" (in), + [in0] "=&r" (in0), + [in1] "=&r" (in1), + [out] "+&r" (out), + [out0] "=&r" (out0), + [out1] "=&r" (out1), + [consts] "+&r" (consts) + : + [inc] "r" ((char *) &sb_sample_f[1][0][0] - + (char *) &sb_sample_f[0][0][0]), + [blocks] "r" (blocks), + [joint] "r" (&joint), + [c1] "i" (1 << SCALE_OUT_BITS), + [c2] "i" (31 - SCALE_OUT_BITS), + [zero] "r" (0) + : "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", + "d16", "d17", "d18", "d19", "d20", "d21", "d22", + "d23", "d24", "d25", "d26", "d27", "d28", "d29", + "d30", "d31", "cc", "memory"); + + return joint; +} + +#define PERM_BE(a, b, c, d) { \ + (a * 2) + 1, (a * 2) + 0, \ + (b * 2) + 1, (b * 2) + 0, \ + (c * 2) + 1, (c * 2) + 0, \ + (d * 2) + 1, (d * 2) + 0 \ + } +#define PERM_LE(a, b, c, d) { \ + (a * 2) + 0, (a * 2) + 1, \ + (b * 2) + 0, (b * 2) + 1, \ + (c * 2) + 0, (c * 2) + 1, \ + (d * 2) + 0, (d * 2) + 1 \ + } + +static SBC_ALWAYS_INLINE int sbc_enc_process_input_4s_neon_internal( + int position, + const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE], + int nsamples, int nchannels, int big_endian) +{ + static SBC_ALIGNED uint8_t perm_be[2][8] = { + PERM_BE(7, 3, 6, 4), + PERM_BE(0, 2, 1, 5) + }; + static SBC_ALIGNED uint8_t perm_le[2][8] = { + PERM_LE(7, 3, 6, 4), + PERM_LE(0, 2, 1, 5) + }; + /* handle X buffer wraparound */ + if (position < nsamples) { + int16_t *dst = &X[0][SBC_X_BUFFER_SIZE - 40]; + int16_t *src = &X[0][position]; + asm volatile ( + "vld1.16 {d0, d1, d2, d3}, [%[src], :128]!\n" + "vst1.16 {d0, d1, d2, d3}, [%[dst], :128]!\n" + "vld1.16 {d0, d1, d2, d3}, [%[src], :128]!\n" + "vst1.16 {d0, d1, d2, d3}, [%[dst], :128]!\n" + "vld1.16 {d0}, [%[src], :64]!\n" + "vst1.16 {d0}, [%[dst], :64]!\n" + : + [dst] "+r" (dst), + [src] "+r" (src) + : : "memory", "d0", "d1", "d2", "d3"); + if (nchannels > 1) { + dst = &X[1][SBC_X_BUFFER_SIZE - 40]; + src = &X[1][position]; + asm volatile ( + "vld1.16 {d0, d1, d2, d3}, [%[src], :128]!\n" + "vst1.16 {d0, d1, d2, d3}, [%[dst], :128]!\n" + "vld1.16 {d0, d1, d2, d3}, [%[src], :128]!\n" + "vst1.16 {d0, d1, d2, d3}, [%[dst], :128]!\n" + "vld1.16 {d0}, [%[src], :64]!\n" + "vst1.16 {d0}, [%[dst], :64]!\n" + : + [dst] "+r" (dst), + [src] "+r" (src) + : : "memory", "d0", "d1", "d2", "d3"); + } + position = SBC_X_BUFFER_SIZE - 40; + } + + if ((nchannels > 1) && ((uintptr_t)pcm & 1)) { + /* poor 'pcm' alignment */ + int16_t *x = &X[0][position]; + int16_t *y = &X[1][position]; + asm volatile ( + "vld1.8 {d0, d1}, [%[perm], :128]\n" + "1:\n" + "sub %[x], %[x], #16\n" + "sub %[y], %[y], #16\n" + "sub %[position], %[position], #8\n" + "vld1.8 {d4, d5}, [%[pcm]]!\n" + "vuzp.16 d4, d5\n" + "vld1.8 {d20, d21}, [%[pcm]]!\n" + "vuzp.16 d20, d21\n" + "vswp d5, d20\n" + "vtbl.8 d16, {d4, d5}, d0\n" + "vtbl.8 d17, {d4, d5}, d1\n" + "vtbl.8 d18, {d20, d21}, d0\n" + "vtbl.8 d19, {d20, d21}, d1\n" + "vst1.16 {d16, d17}, [%[x], :128]\n" + "vst1.16 {d18, d19}, [%[y], :128]\n" + "subs %[nsamples], %[nsamples], #8\n" + "bgt 1b\n" + : + [x] "+r" (x), + [y] "+r" (y), + [pcm] "+r" (pcm), + [nsamples] "+r" (nsamples), + [position] "+r" (position) + : + [perm] "r" (big_endian ? perm_be : perm_le) + : "cc", "memory", "d0", "d1", "d2", "d3", "d4", + "d5", "d6", "d7", "d16", "d17", "d18", "d19", + "d20", "d21", "d22", "d23"); + } else if (nchannels > 1) { + /* proper 'pcm' alignment */ + int16_t *x = &X[0][position]; + int16_t *y = &X[1][position]; + asm volatile ( + "vld1.8 {d0, d1}, [%[perm], :128]\n" + "1:\n" + "sub %[x], %[x], #16\n" + "sub %[y], %[y], #16\n" + "sub %[position], %[position], #8\n" + "vld2.16 {d4, d5}, [%[pcm]]!\n" + "vld2.16 {d20, d21}, [%[pcm]]!\n" + "vswp d5, d20\n" + "vtbl.8 d16, {d4, d5}, d0\n" + "vtbl.8 d17, {d4, d5}, d1\n" + "vtbl.8 d18, {d20, d21}, d0\n" + "vtbl.8 d19, {d20, d21}, d1\n" + "vst1.16 {d16, d17}, [%[x], :128]\n" + "vst1.16 {d18, d19}, [%[y], :128]\n" + "subs %[nsamples], %[nsamples], #8\n" + "bgt 1b\n" + : + [x] "+r" (x), + [y] "+r" (y), + [pcm] "+r" (pcm), + [nsamples] "+r" (nsamples), + [position] "+r" (position) + : + [perm] "r" (big_endian ? perm_be : perm_le) + : "cc", "memory", "d0", "d1", "d2", "d3", "d4", + "d5", "d6", "d7", "d16", "d17", "d18", "d19", + "d20", "d21", "d22", "d23"); + } else { + int16_t *x = &X[0][position]; + asm volatile ( + "vld1.8 {d0, d1}, [%[perm], :128]\n" + "1:\n" + "sub %[x], %[x], #16\n" + "sub %[position], %[position], #8\n" + "vld1.8 {d4, d5}, [%[pcm]]!\n" + "vtbl.8 d16, {d4, d5}, d0\n" + "vtbl.8 d17, {d4, d5}, d1\n" + "vst1.16 {d16, d17}, [%[x], :128]\n" + "subs %[nsamples], %[nsamples], #8\n" + "bgt 1b\n" + : + [x] "+r" (x), + [pcm] "+r" (pcm), + [nsamples] "+r" (nsamples), + [position] "+r" (position) + : + [perm] "r" (big_endian ? perm_be : perm_le) + : "cc", "memory", "d0", "d1", "d2", "d3", "d4", + "d5", "d6", "d7", "d16", "d17", "d18", "d19"); + } + return position; +} + +static SBC_ALWAYS_INLINE int sbc_enc_process_input_8s_neon_internal( + int position, + const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE], + int nsamples, int nchannels, int big_endian) +{ + static SBC_ALIGNED uint8_t perm_be[4][8] = { + PERM_BE(15, 7, 14, 8), + PERM_BE(13, 9, 12, 10), + PERM_BE(11, 3, 6, 0), + PERM_BE(5, 1, 4, 2) + }; + static SBC_ALIGNED uint8_t perm_le[4][8] = { + PERM_LE(15, 7, 14, 8), + PERM_LE(13, 9, 12, 10), + PERM_LE(11, 3, 6, 0), + PERM_LE(5, 1, 4, 2) + }; + /* handle X buffer wraparound */ + if (position < nsamples) { + int16_t *dst = &X[0][SBC_X_BUFFER_SIZE - 72]; + int16_t *src = &X[0][position]; + asm volatile ( + "vld1.16 {d0, d1, d2, d3}, [%[src], :128]!\n" + "vst1.16 {d0, d1, d2, d3}, [%[dst], :128]!\n" + "vld1.16 {d0, d1, d2, d3}, [%[src], :128]!\n" + "vst1.16 {d0, d1, d2, d3}, [%[dst], :128]!\n" + "vld1.16 {d0, d1, d2, d3}, [%[src], :128]!\n" + "vst1.16 {d0, d1, d2, d3}, [%[dst], :128]!\n" + "vld1.16 {d0, d1, d2, d3}, [%[src], :128]!\n" + "vst1.16 {d0, d1, d2, d3}, [%[dst], :128]!\n" + "vld1.16 {d0, d1}, [%[src], :128]!\n" + "vst1.16 {d0, d1}, [%[dst], :128]!\n" + : + [dst] "+r" (dst), + [src] "+r" (src) + : : "memory", "d0", "d1", "d2", "d3"); + if (nchannels > 1) { + dst = &X[1][SBC_X_BUFFER_SIZE - 72]; + src = &X[1][position]; + asm volatile ( + "vld1.16 {d0, d1, d2, d3}, [%[src], :128]!\n" + "vst1.16 {d0, d1, d2, d3}, [%[dst], :128]!\n" + "vld1.16 {d0, d1, d2, d3}, [%[src], :128]!\n" + "vst1.16 {d0, d1, d2, d3}, [%[dst], :128]!\n" + "vld1.16 {d0, d1, d2, d3}, [%[src], :128]!\n" + "vst1.16 {d0, d1, d2, d3}, [%[dst], :128]!\n" + "vld1.16 {d0, d1, d2, d3}, [%[src], :128]!\n" + "vst1.16 {d0, d1, d2, d3}, [%[dst], :128]!\n" + "vld1.16 {d0, d1}, [%[src], :128]!\n" + "vst1.16 {d0, d1}, [%[dst], :128]!\n" + : + [dst] "+r" (dst), + [src] "+r" (src) + : : "memory", "d0", "d1", "d2", "d3"); + } + position = SBC_X_BUFFER_SIZE - 72; + } + + if ((nchannels > 1) && ((uintptr_t)pcm & 1)) { + /* poor 'pcm' alignment */ + int16_t *x = &X[0][position]; + int16_t *y = &X[1][position]; + asm volatile ( + "vld1.8 {d0, d1, d2, d3}, [%[perm], :128]\n" + "1:\n" + "sub %[x], %[x], #32\n" + "sub %[y], %[y], #32\n" + "sub %[position], %[position], #16\n" + "vld1.8 {d4, d5, d6, d7}, [%[pcm]]!\n" + "vuzp.16 q2, q3\n" + "vld1.8 {d20, d21, d22, d23}, [%[pcm]]!\n" + "vuzp.16 q10, q11\n" + "vswp q3, q10\n" + "vtbl.8 d16, {d4, d5, d6, d7}, d0\n" + "vtbl.8 d17, {d4, d5, d6, d7}, d1\n" + "vtbl.8 d18, {d4, d5, d6, d7}, d2\n" + "vtbl.8 d19, {d4, d5, d6, d7}, d3\n" + "vst1.16 {d16, d17, d18, d19}, [%[x], :128]\n" + "vtbl.8 d16, {d20, d21, d22, d23}, d0\n" + "vtbl.8 d17, {d20, d21, d22, d23}, d1\n" + "vtbl.8 d18, {d20, d21, d22, d23}, d2\n" + "vtbl.8 d19, {d20, d21, d22, d23}, d3\n" + "vst1.16 {d16, d17, d18, d19}, [%[y], :128]\n" + "subs %[nsamples], %[nsamples], #16\n" + "bgt 1b\n" + : + [x] "+r" (x), + [y] "+r" (y), + [pcm] "+r" (pcm), + [nsamples] "+r" (nsamples), + [position] "+r" (position) + : + [perm] "r" (big_endian ? perm_be : perm_le) + : "cc", "memory", "d0", "d1", "d2", "d3", "d4", + "d5", "d6", "d7", "d16", "d17", "d18", "d19", + "d20", "d21", "d22", "d23"); + } else if (nchannels > 1) { + /* proper 'pcm' alignment */ + int16_t *x = &X[0][position]; + int16_t *y = &X[1][position]; + asm volatile ( + "vld1.8 {d0, d1, d2, d3}, [%[perm], :128]\n" + "1:\n" + "sub %[x], %[x], #32\n" + "sub %[y], %[y], #32\n" + "sub %[position], %[position], #16\n" + "vld2.16 {d4, d5, d6, d7}, [%[pcm]]!\n" + "vld2.16 {d20, d21, d22, d23}, [%[pcm]]!\n" + "vswp q3, q10\n" + "vtbl.8 d16, {d4, d5, d6, d7}, d0\n" + "vtbl.8 d17, {d4, d5, d6, d7}, d1\n" + "vtbl.8 d18, {d4, d5, d6, d7}, d2\n" + "vtbl.8 d19, {d4, d5, d6, d7}, d3\n" + "vst1.16 {d16, d17, d18, d19}, [%[x], :128]\n" + "vtbl.8 d16, {d20, d21, d22, d23}, d0\n" + "vtbl.8 d17, {d20, d21, d22, d23}, d1\n" + "vtbl.8 d18, {d20, d21, d22, d23}, d2\n" + "vtbl.8 d19, {d20, d21, d22, d23}, d3\n" + "vst1.16 {d16, d17, d18, d19}, [%[y], :128]\n" + "subs %[nsamples], %[nsamples], #16\n" + "bgt 1b\n" + : + [x] "+r" (x), + [y] "+r" (y), + [pcm] "+r" (pcm), + [nsamples] "+r" (nsamples), + [position] "+r" (position) + : + [perm] "r" (big_endian ? perm_be : perm_le) + : "cc", "memory", "d0", "d1", "d2", "d3", "d4", + "d5", "d6", "d7", "d16", "d17", "d18", "d19", + "d20", "d21", "d22", "d23"); + } else { + int16_t *x = &X[0][position]; + asm volatile ( + "vld1.8 {d0, d1, d2, d3}, [%[perm], :128]\n" + "1:\n" + "sub %[x], %[x], #32\n" + "sub %[position], %[position], #16\n" + "vld1.8 {d4, d5, d6, d7}, [%[pcm]]!\n" + "vtbl.8 d16, {d4, d5, d6, d7}, d0\n" + "vtbl.8 d17, {d4, d5, d6, d7}, d1\n" + "vtbl.8 d18, {d4, d5, d6, d7}, d2\n" + "vtbl.8 d19, {d4, d5, d6, d7}, d3\n" + "vst1.16 {d16, d17, d18, d19}, [%[x], :128]\n" + "subs %[nsamples], %[nsamples], #16\n" + "bgt 1b\n" + : + [x] "+r" (x), + [pcm] "+r" (pcm), + [nsamples] "+r" (nsamples), + [position] "+r" (position) + : + [perm] "r" (big_endian ? perm_be : perm_le) + : "cc", "memory", "d0", "d1", "d2", "d3", "d4", + "d5", "d6", "d7", "d16", "d17", "d18", "d19"); + } + return position; +} + +#undef PERM_BE +#undef PERM_LE + +static int sbc_enc_process_input_4s_be_neon(int position, const uint8_t *pcm, + int16_t X[2][SBC_X_BUFFER_SIZE], + int nsamples, int nchannels) +{ + return sbc_enc_process_input_4s_neon_internal( + position, pcm, X, nsamples, nchannels, 1); +} + +static int sbc_enc_process_input_4s_le_neon(int position, const uint8_t *pcm, + int16_t X[2][SBC_X_BUFFER_SIZE], + int nsamples, int nchannels) +{ + return sbc_enc_process_input_4s_neon_internal( + position, pcm, X, nsamples, nchannels, 0); +} + +static int sbc_enc_process_input_8s_be_neon(int position, const uint8_t *pcm, + int16_t X[2][SBC_X_BUFFER_SIZE], + int nsamples, int nchannels) +{ + return sbc_enc_process_input_8s_neon_internal( + position, pcm, X, nsamples, nchannels, 1); +} + +static int sbc_enc_process_input_8s_le_neon(int position, const uint8_t *pcm, + int16_t X[2][SBC_X_BUFFER_SIZE], + int nsamples, int nchannels) +{ + return sbc_enc_process_input_8s_neon_internal( + position, pcm, X, nsamples, nchannels, 0); +} + +void sbc_init_primitives_neon(struct sbc_encoder_state *state) +{ + state->sbc_analyze_4b_4s = sbc_analyze_4b_4s_neon; + state->sbc_analyze_4b_8s = sbc_analyze_4b_8s_neon; + state->sbc_calc_scalefactors = sbc_calc_scalefactors_neon; + state->sbc_calc_scalefactors_j = sbc_calc_scalefactors_j_neon; + state->sbc_enc_process_input_4s_le = sbc_enc_process_input_4s_le_neon; + state->sbc_enc_process_input_4s_be = sbc_enc_process_input_4s_be_neon; + state->sbc_enc_process_input_8s_le = sbc_enc_process_input_8s_le_neon; + state->sbc_enc_process_input_8s_be = sbc_enc_process_input_8s_be_neon; + state->implementation_info = "NEON"; +} + +#endif diff --git a/src/modules/bluetooth/sbc_primitives_neon.h b/src/modules/bluetooth/sbc/sbc_primitives_neon.h index 30766ed8..30766ed8 100644 --- a/src/modules/bluetooth/sbc_primitives_neon.h +++ b/src/modules/bluetooth/sbc/sbc_primitives_neon.h diff --git a/src/modules/bluetooth/sbc_tables.h b/src/modules/bluetooth/sbc/sbc_tables.h index 0057c73f..0057c73f 100644 --- a/src/modules/bluetooth/sbc_tables.h +++ b/src/modules/bluetooth/sbc/sbc_tables.h diff --git a/src/modules/bluetooth/sbc_primitives_neon.c b/src/modules/bluetooth/sbc_primitives_neon.c deleted file mode 100644 index f1bc7b48..00000000 --- a/src/modules/bluetooth/sbc_primitives_neon.c +++ /dev/null @@ -1,246 +0,0 @@ -/* - * - * Bluetooth low-complexity, subband codec (SBC) library - * - * Copyright (C) 2004-2009 Marcel Holtmann <marcel@holtmann.org> - * Copyright (C) 2004-2005 Henryk Ploetz <henryk@ploetzli.ch> - * Copyright (C) 2005-2006 Brad Midgley <bmidgley@xmission.com> - * - * - * This library 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. - * - * This library 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 this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#include <stdint.h> -#include <limits.h> -#include "sbc.h" -#include "sbc_math.h" -#include "sbc_tables.h" - -#include "sbc_primitives_neon.h" - -/* - * ARM NEON optimizations - */ - -#ifdef SBC_BUILD_WITH_NEON_SUPPORT - -static inline void _sbc_analyze_four_neon(const int16_t *in, int32_t *out, - const FIXED_T *consts) -{ - /* TODO: merge even and odd cases (or even merge all four calls to this - * function) in order to have only aligned reads from 'in' array - * and reduce number of load instructions */ - asm volatile ( - "vld1.16 {d4, d5}, [%0, :64]!\n" - "vld1.16 {d8, d9}, [%1, :128]!\n" - - "vmull.s16 q0, d4, d8\n" - "vld1.16 {d6, d7}, [%0, :64]!\n" - "vmull.s16 q1, d5, d9\n" - "vld1.16 {d10, d11}, [%1, :128]!\n" - - "vmlal.s16 q0, d6, d10\n" - "vld1.16 {d4, d5}, [%0, :64]!\n" - "vmlal.s16 q1, d7, d11\n" - "vld1.16 {d8, d9}, [%1, :128]!\n" - - "vmlal.s16 q0, d4, d8\n" - "vld1.16 {d6, d7}, [%0, :64]!\n" - "vmlal.s16 q1, d5, d9\n" - "vld1.16 {d10, d11}, [%1, :128]!\n" - - "vmlal.s16 q0, d6, d10\n" - "vld1.16 {d4, d5}, [%0, :64]!\n" - "vmlal.s16 q1, d7, d11\n" - "vld1.16 {d8, d9}, [%1, :128]!\n" - - "vmlal.s16 q0, d4, d8\n" - "vmlal.s16 q1, d5, d9\n" - - "vpadd.s32 d0, d0, d1\n" - "vpadd.s32 d1, d2, d3\n" - - "vrshrn.s32 d0, q0, %3\n" - - "vld1.16 {d2, d3, d4, d5}, [%1, :128]!\n" - - "vdup.i32 d1, d0[1]\n" /* TODO: can be eliminated */ - "vdup.i32 d0, d0[0]\n" /* TODO: can be eliminated */ - - "vmull.s16 q3, d2, d0\n" - "vmull.s16 q4, d3, d0\n" - "vmlal.s16 q3, d4, d1\n" - "vmlal.s16 q4, d5, d1\n" - - "vpadd.s32 d0, d6, d7\n" /* TODO: can be eliminated */ - "vpadd.s32 d1, d8, d9\n" /* TODO: can be eliminated */ - - "vst1.32 {d0, d1}, [%2, :128]\n" - : "+r" (in), "+r" (consts) - : "r" (out), - "i" (SBC_PROTO_FIXED4_SCALE) - : "memory", - "d0", "d1", "d2", "d3", "d4", "d5", - "d6", "d7", "d8", "d9", "d10", "d11"); -} - -static inline void _sbc_analyze_eight_neon(const int16_t *in, int32_t *out, - const FIXED_T *consts) -{ - /* TODO: merge even and odd cases (or even merge all four calls to this - * function) in order to have only aligned reads from 'in' array - * and reduce number of load instructions */ - asm volatile ( - "vld1.16 {d4, d5}, [%0, :64]!\n" - "vld1.16 {d8, d9}, [%1, :128]!\n" - - "vmull.s16 q6, d4, d8\n" - "vld1.16 {d6, d7}, [%0, :64]!\n" - "vmull.s16 q7, d5, d9\n" - "vld1.16 {d10, d11}, [%1, :128]!\n" - "vmull.s16 q8, d6, d10\n" - "vld1.16 {d4, d5}, [%0, :64]!\n" - "vmull.s16 q9, d7, d11\n" - "vld1.16 {d8, d9}, [%1, :128]!\n" - - "vmlal.s16 q6, d4, d8\n" - "vld1.16 {d6, d7}, [%0, :64]!\n" - "vmlal.s16 q7, d5, d9\n" - "vld1.16 {d10, d11}, [%1, :128]!\n" - "vmlal.s16 q8, d6, d10\n" - "vld1.16 {d4, d5}, [%0, :64]!\n" - "vmlal.s16 q9, d7, d11\n" - "vld1.16 {d8, d9}, [%1, :128]!\n" - - "vmlal.s16 q6, d4, d8\n" - "vld1.16 {d6, d7}, [%0, :64]!\n" - "vmlal.s16 q7, d5, d9\n" - "vld1.16 {d10, d11}, [%1, :128]!\n" - "vmlal.s16 q8, d6, d10\n" - "vld1.16 {d4, d5}, [%0, :64]!\n" - "vmlal.s16 q9, d7, d11\n" - "vld1.16 {d8, d9}, [%1, :128]!\n" - - "vmlal.s16 q6, d4, d8\n" - "vld1.16 {d6, d7}, [%0, :64]!\n" - "vmlal.s16 q7, d5, d9\n" - "vld1.16 {d10, d11}, [%1, :128]!\n" - "vmlal.s16 q8, d6, d10\n" - "vld1.16 {d4, d5}, [%0, :64]!\n" - "vmlal.s16 q9, d7, d11\n" - "vld1.16 {d8, d9}, [%1, :128]!\n" - - "vmlal.s16 q6, d4, d8\n" - "vld1.16 {d6, d7}, [%0, :64]!\n" - "vmlal.s16 q7, d5, d9\n" - "vld1.16 {d10, d11}, [%1, :128]!\n" - - "vmlal.s16 q8, d6, d10\n" - "vmlal.s16 q9, d7, d11\n" - - "vpadd.s32 d0, d12, d13\n" - "vpadd.s32 d1, d14, d15\n" - "vpadd.s32 d2, d16, d17\n" - "vpadd.s32 d3, d18, d19\n" - - "vrshr.s32 q0, q0, %3\n" - "vrshr.s32 q1, q1, %3\n" - "vmovn.s32 d0, q0\n" - "vmovn.s32 d1, q1\n" - - "vdup.i32 d3, d1[1]\n" /* TODO: can be eliminated */ - "vdup.i32 d2, d1[0]\n" /* TODO: can be eliminated */ - "vdup.i32 d1, d0[1]\n" /* TODO: can be eliminated */ - "vdup.i32 d0, d0[0]\n" /* TODO: can be eliminated */ - - "vld1.16 {d4, d5}, [%1, :128]!\n" - "vmull.s16 q6, d4, d0\n" - "vld1.16 {d6, d7}, [%1, :128]!\n" - "vmull.s16 q7, d5, d0\n" - "vmull.s16 q8, d6, d0\n" - "vmull.s16 q9, d7, d0\n" - - "vld1.16 {d4, d5}, [%1, :128]!\n" - "vmlal.s16 q6, d4, d1\n" - "vld1.16 {d6, d7}, [%1, :128]!\n" - "vmlal.s16 q7, d5, d1\n" - "vmlal.s16 q8, d6, d1\n" - "vmlal.s16 q9, d7, d1\n" - - "vld1.16 {d4, d5}, [%1, :128]!\n" - "vmlal.s16 q6, d4, d2\n" - "vld1.16 {d6, d7}, [%1, :128]!\n" - "vmlal.s16 q7, d5, d2\n" - "vmlal.s16 q8, d6, d2\n" - "vmlal.s16 q9, d7, d2\n" - - "vld1.16 {d4, d5}, [%1, :128]!\n" - "vmlal.s16 q6, d4, d3\n" - "vld1.16 {d6, d7}, [%1, :128]!\n" - "vmlal.s16 q7, d5, d3\n" - "vmlal.s16 q8, d6, d3\n" - "vmlal.s16 q9, d7, d3\n" - - "vpadd.s32 d0, d12, d13\n" /* TODO: can be eliminated */ - "vpadd.s32 d1, d14, d15\n" /* TODO: can be eliminated */ - "vpadd.s32 d2, d16, d17\n" /* TODO: can be eliminated */ - "vpadd.s32 d3, d18, d19\n" /* TODO: can be eliminated */ - - "vst1.32 {d0, d1, d2, d3}, [%2, :128]\n" - : "+r" (in), "+r" (consts) - : "r" (out), - "i" (SBC_PROTO_FIXED8_SCALE) - : "memory", - "d0", "d1", "d2", "d3", "d4", "d5", - "d6", "d7", "d8", "d9", "d10", "d11", - "d12", "d13", "d14", "d15", "d16", "d17", - "d18", "d19"); -} - -static inline void sbc_analyze_4b_4s_neon(int16_t *x, - int32_t *out, int out_stride) -{ - /* Analyze blocks */ - _sbc_analyze_four_neon(x + 12, out, analysis_consts_fixed4_simd_odd); - out += out_stride; - _sbc_analyze_four_neon(x + 8, out, analysis_consts_fixed4_simd_even); - out += out_stride; - _sbc_analyze_four_neon(x + 4, out, analysis_consts_fixed4_simd_odd); - out += out_stride; - _sbc_analyze_four_neon(x + 0, out, analysis_consts_fixed4_simd_even); -} - -static inline void sbc_analyze_4b_8s_neon(int16_t *x, - int32_t *out, int out_stride) -{ - /* Analyze blocks */ - _sbc_analyze_eight_neon(x + 24, out, analysis_consts_fixed8_simd_odd); - out += out_stride; - _sbc_analyze_eight_neon(x + 16, out, analysis_consts_fixed8_simd_even); - out += out_stride; - _sbc_analyze_eight_neon(x + 8, out, analysis_consts_fixed8_simd_odd); - out += out_stride; - _sbc_analyze_eight_neon(x + 0, out, analysis_consts_fixed8_simd_even); -} - -void sbc_init_primitives_neon(struct sbc_encoder_state *state) -{ - state->sbc_analyze_4b_4s = sbc_analyze_4b_4s_neon; - state->sbc_analyze_4b_8s = sbc_analyze_4b_8s_neon; - state->implementation_info = "NEON"; -} - -#endif diff --git a/src/modules/coreaudio/module-coreaudio-detect.c b/src/modules/coreaudio/module-coreaudio-detect.c index c23079a9..1d26b84e 100644 --- a/src/modules/coreaudio/module-coreaudio-detect.c +++ b/src/modules/coreaudio/module-coreaudio-detect.c @@ -47,7 +47,7 @@ typedef struct ca_device ca_device; struct ca_device { AudioObjectID id; - unsigned int module_index; + unsigned int module_index; PA_LLIST_FIELDS(ca_device); }; @@ -162,7 +162,7 @@ scan_removed: } if (!found) { - pa_log_debug("object id %d has been removed (module index %d) %p", (unsigned int) dev->id, dev->module_index, dev); + pa_log_debug("object id %d has been removed (module index %d) %p", (unsigned int) dev->id, dev->module_index, dev); pa_module_unload_request_by_index(m->core, dev->module_index, TRUE); PA_LLIST_REMOVE(ca_device, u->devices, dev); pa_xfree(dev); diff --git a/src/modules/coreaudio/module-coreaudio-device.c b/src/modules/coreaudio/module-coreaudio-device.c index 5e6d49c2..cc4600b5 100644 --- a/src/modules/coreaudio/module-coreaudio-device.c +++ b/src/modules/coreaudio/module-coreaudio-device.c @@ -747,7 +747,7 @@ int pa__init(pa_module *m) { ca_device_create_streams(m, TRUE); /* create the message thread */ - if (!(u->thread = pa_thread_new(thread_func, u))) { + if (!(u->thread = pa_thread_new("coreaudio", thread_func, u))) { pa_log("Failed to create thread."); goto fail; } diff --git a/src/modules/dbus/iface-device.c b/src/modules/dbus/iface-device.c index a8652df9..c5ba88e0 100644 --- a/src/modules/dbus/iface-device.c +++ b/src/modules/dbus/iface-device.c @@ -422,21 +422,20 @@ static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, DBusMessag pa_assert(iter); pa_assert(d); - pa_cvolume_init(&new_vol); - device_channels = (d->type == DEVICE_TYPE_SINK) ? d->sink->channel_map.channels : d->source->channel_map.channels; - new_vol.channels = device_channels; - dbus_message_iter_recurse(iter, &array_iter); dbus_message_iter_get_fixed_array(&array_iter, &volume, &n_volume_entries); - if (n_volume_entries != device_channels) { + if (n_volume_entries != device_channels && n_volume_entries != 1) { pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Expected %u volume entries, got %i.", device_channels, n_volume_entries); return; } + pa_cvolume_init(&new_vol); + new_vol.channels = n_volume_entries; + for (i = 0; i < n_volume_entries; ++i) { if (!PA_VOLUME_IS_VALID(volume[i])) { pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too large volume value: %u", volume[i]); diff --git a/src/modules/dbus/iface-stream.c b/src/modules/dbus/iface-stream.c index 681790b7..e3464fdf 100644 --- a/src/modules/dbus/iface-stream.c +++ b/src/modules/dbus/iface-stream.c @@ -56,6 +56,9 @@ struct pa_dbusiface_stream { dbus_bool_t mute; pa_proplist *proplist; + pa_bool_t has_volume; + pa_bool_t read_only_volume; + pa_dbus_protocol *dbus_protocol; pa_subscription *subscription; pa_hook_slot *send_event_slot; @@ -189,6 +192,14 @@ static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userd pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &idx); } +/* The returned string has to be freed with pa_xfree() by the caller. */ +static char *stream_to_string(pa_dbusiface_stream *s) { + if (s->type == STREAM_TYPE_PLAYBACK) + return pa_sprintf_malloc("Playback stream %u", (unsigned) s->sink_input->index); + else + return pa_sprintf_malloc("Record stream %u", (unsigned) s->source_output->index); +} + static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata) { pa_dbusiface_stream *s = userdata; const char *driver = NULL; @@ -200,12 +211,11 @@ static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *user driver = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->driver : s->source_output->driver; if (!driver) { - if (s->type == STREAM_TYPE_PLAYBACK) - pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, - "Playback stream %u doesn't have a driver.", s->sink_input->index); - else - pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, - "Record stream %u doesn't have a driver.", s->source_output->index); + char *str = stream_to_string(s); + + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s doesn't have a driver.", str); + pa_xfree(str); + return; } @@ -224,12 +234,11 @@ static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void owner_module = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->module : s->source_output->module; if (!owner_module) { - if (s->type == STREAM_TYPE_PLAYBACK) - pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, - "Playback stream %u doesn't have an owner module.", s->sink_input->index); - else - pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, - "Record stream %u doesn't have an owner module.", s->source_output->index); + char *str = stream_to_string(s); + + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s doesn't have an owner module.", str); + pa_xfree(str); + return; } @@ -250,12 +259,11 @@ static void handle_get_client(DBusConnection *conn, DBusMessage *msg, void *user client = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->client : s->source_output->client; if (!client) { - if (s->type == STREAM_TYPE_PLAYBACK) - pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, - "Playback stream %u isn't associated to any client.", s->sink_input->index); - else - pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, - "Record stream %u isn't associated to any client.", s->source_output->index); + char *str = stream_to_string(s); + + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s isn't associated to any client.", str); + pa_xfree(str); + return; } @@ -332,8 +340,12 @@ static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *user pa_assert(msg); pa_assert(s); - if (s->type == STREAM_TYPE_RECORD) { - pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Record streams don't have volume."); + if (!s->has_volume) { + char *str = stream_to_string(s); + + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s doesn't have volume.", str); + pa_xfree(str); + return; } @@ -357,26 +369,32 @@ static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, DBusMessag pa_assert(iter); pa_assert(s); - if (s->type == STREAM_TYPE_RECORD) { - pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Record streams don't have volume."); + if (!s->has_volume || s->read_only_volume) { + char *str = stream_to_string(s); + + if (!s->has_volume) + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s doesn't have volume.", str); + else if (s->read_only_volume) + pa_dbus_send_error(conn, msg, DBUS_ERROR_ACCESS_DENIED, "%s has read-only volume.", str); + pa_xfree(str); + return; } - pa_cvolume_init(&new_vol); - stream_channels = s->sink_input->channel_map.channels; - new_vol.channels = stream_channels; - dbus_message_iter_recurse(iter, &array_iter); dbus_message_iter_get_fixed_array(&array_iter, &volume, &n_volume_entries); - if (n_volume_entries != stream_channels) { + if (n_volume_entries != stream_channels && n_volume_entries != 1) { pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Expected %u volume entries, got %u.", stream_channels, n_volume_entries); return; } + pa_cvolume_init(&new_vol); + new_vol.channels = n_volume_entries; + for (i = 0; i < n_volume_entries; ++i) { if (!PA_VOLUME_IS_VALID(volume[i])) { pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid volume: %u", volume[i]); @@ -471,6 +489,9 @@ static void handle_get_resample_method(DBusConnection *conn, DBusMessage *msg, v else resample_method = pa_resample_method_to_string(s->source_output->actual_resample_method); + if (!resample_method) + resample_method = ""; + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &resample_method); } @@ -509,6 +530,11 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat pa_assert(msg); pa_assert(s); + if (s->has_volume) { + for (i = 0; i < s->volume.channels; ++i) + volume[i] = s->volume.values[i]; + } + if (s->type == STREAM_TYPE_PLAYBACK) { idx = s->sink_input->index; driver = s->sink_input->driver; @@ -517,8 +543,6 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat device = pa_dbusiface_core_get_sink_path(s->core, s->sink); sample_format = s->sink_input->sample_spec.format; channel_map = &s->sink_input->channel_map; - for (i = 0; i < s->volume.channels; ++i) - volume[i] = s->volume.values[i]; buffer_latency = pa_sink_input_get_latency(s->sink_input, &device_latency); resample_method = pa_resample_method_to_string(s->sink_input->actual_resample_method); } else { @@ -538,6 +562,8 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat client_path = pa_dbusiface_core_get_client_path(s->core, client); for (i = 0; i < channel_map->channels; ++i) channels[i] = channel_map->map[i]; + if (!resample_method) + resample_method = ""; pa_assert_se((reply = dbus_message_new_method_return(msg))); @@ -555,11 +581,12 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat if (client) pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CLIENT].property_name, DBUS_TYPE_OBJECT_PATH, &client_path); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DEVICE].property_name, DBUS_TYPE_OBJECT_PATH, &device); pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLE_FORMAT].property_name, DBUS_TYPE_UINT32, &sample_format); pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLE_RATE].property_name, DBUS_TYPE_UINT32, &s->sample_rate); pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CHANNELS].property_name, DBUS_TYPE_UINT32, channels, channel_map->channels); - if (s->type == STREAM_TYPE_PLAYBACK) { + if (s->has_volume) { pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_VOLUME].property_name, DBUS_TYPE_UINT32, volume, s->volume.channels); pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_MUTE].property_name, DBUS_TYPE_BOOLEAN, &s->mute); } @@ -707,30 +734,33 @@ static void subscription_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t } if (s->type == STREAM_TYPE_PLAYBACK) { - pa_cvolume new_volume; pa_bool_t new_mute = FALSE; - pa_sink_input_get_volume(s->sink_input, &new_volume, TRUE); + if (s->has_volume) { + pa_cvolume new_volume; - if (!pa_cvolume_equal(&s->volume, &new_volume)) { - dbus_uint32_t volume[PA_CHANNELS_MAX]; - dbus_uint32_t *volume_ptr = volume; + pa_sink_input_get_volume(s->sink_input, &new_volume, TRUE); - s->volume = new_volume; + if (!pa_cvolume_equal(&s->volume, &new_volume)) { + dbus_uint32_t volume[PA_CHANNELS_MAX]; + dbus_uint32_t *volume_ptr = volume; - for (i = 0; i < s->volume.channels; ++i) - volume[i] = s->volume.values[i]; + s->volume = new_volume; - pa_assert_se(signal_msg = dbus_message_new_signal(s->path, - PA_DBUSIFACE_STREAM_INTERFACE, - signals[SIGNAL_VOLUME_UPDATED].name)); - pa_assert_se(dbus_message_append_args(signal_msg, - DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &volume_ptr, s->volume.channels, - DBUS_TYPE_INVALID)); + for (i = 0; i < s->volume.channels; ++i) + volume[i] = s->volume.values[i]; - pa_dbus_protocol_send_signal(s->dbus_protocol, signal_msg); - dbus_message_unref(signal_msg); - signal_msg = NULL; + pa_assert_se(signal_msg = dbus_message_new_signal(s->path, + PA_DBUSIFACE_STREAM_INTERFACE, + signals[SIGNAL_VOLUME_UPDATED].name)); + pa_assert_se(dbus_message_append_args(signal_msg, + DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &volume_ptr, s->volume.channels, + DBUS_TYPE_INVALID)); + + pa_dbus_protocol_send_signal(s->dbus_protocol, signal_msg); + dbus_message_unref(signal_msg); + signal_msg = NULL; + } } new_mute = pa_sink_input_get_mute(s->sink_input); @@ -822,7 +852,14 @@ pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_dbusiface_core *core, p s->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, PLAYBACK_OBJECT_NAME, sink_input->index); s->sink = pa_sink_ref(sink_input->sink); s->sample_rate = sink_input->sample_spec.rate; - pa_sink_input_get_volume(sink_input, &s->volume, TRUE); + s->has_volume = pa_sink_input_is_volume_readable(sink_input); + s->read_only_volume = s->has_volume ? !pa_sink_input_is_volume_writable(sink_input) : FALSE; + + if (s->has_volume) + pa_sink_input_get_volume(sink_input, &s->volume, TRUE); + else + pa_cvolume_init(&s->volume); + s->mute = pa_sink_input_get_mute(sink_input); s->proplist = pa_proplist_copy(sink_input->proplist); s->dbus_protocol = pa_dbus_protocol_get(sink_input->core); @@ -853,6 +890,8 @@ pa_dbusiface_stream *pa_dbusiface_stream_new_record(pa_dbusiface_core *core, pa_ pa_cvolume_init(&s->volume); s->mute = FALSE; s->proplist = pa_proplist_copy(source_output->proplist); + s->has_volume = FALSE; + s->read_only_volume = FALSE; s->dbus_protocol = pa_dbus_protocol_get(source_output->core); s->subscription = pa_subscription_new(source_output->core, PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, subscription_cb, s); s->send_event_slot = pa_hook_connect(&source_output->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_SEND_EVENT], diff --git a/src/modules/echo-cancel/adrian.c b/src/modules/echo-cancel/adrian.c index 446966fb..08df2edd 100644 --- a/src/modules/echo-cancel/adrian.c +++ b/src/modules/echo-cancel/adrian.c @@ -42,7 +42,7 @@ static const char* const valid_modargs[] = { }; static void pa_adrian_ec_fixate_spec(pa_sample_spec *source_ss, pa_channel_map *source_map, - pa_sample_spec *sink_ss, pa_channel_map *sink_map) + pa_sample_spec *sink_ss, pa_channel_map *sink_map) { source_ss->format = PA_SAMPLE_S16NE; source_ss->channels = 1; @@ -87,19 +87,18 @@ pa_bool_t pa_adrian_ec_init(pa_core *c, pa_echo_canceller *ec, ec->params.priv.adrian.aec = AEC_init(rate, have_vector); if (!ec->params.priv.adrian.aec) - goto fail; + goto fail; pa_modargs_free(ma); return TRUE; fail: if (ma) - pa_modargs_free(ma); + pa_modargs_free(ma); return FALSE; } -void pa_adrian_ec_run(pa_echo_canceller *ec, const uint8_t *rec, const uint8_t *play, uint8_t *out) -{ +void pa_adrian_ec_run(pa_echo_canceller *ec, const uint8_t *rec, const uint8_t *play, uint8_t *out) { unsigned int i; for (i = 0; i < ec->params.priv.adrian.blocksize; i += 2) { @@ -110,8 +109,7 @@ void pa_adrian_ec_run(pa_echo_canceller *ec, const uint8_t *rec, const uint8_t * } } -void pa_adrian_ec_done(pa_echo_canceller *ec) -{ +void pa_adrian_ec_done(pa_echo_canceller *ec) { pa_xfree(ec->params.priv.adrian.aec); ec->params.priv.adrian.aec = NULL; } diff --git a/src/modules/echo-cancel/module-echo-cancel.c b/src/modules/echo-cancel/module-echo-cancel.c index 611ebb71..a3ad1698 100644 --- a/src/modules/echo-cancel/module-echo-cancel.c +++ b/src/modules/echo-cancel/module-echo-cancel.c @@ -290,7 +290,7 @@ static void time_callback(pa_mainloop_api *a, pa_time_event *e, const struct tim * canceler does not work in this case. */ pa_asyncmsgq_post(u->asyncmsgq, PA_MSGOBJECT(u->source_output), SOURCE_OUTPUT_MESSAGE_APPLY_DIFF_TIME, NULL, diff_time, NULL, NULL); - //new_rate = base_rate - ((pa_usec_to_bytes (-diff_time, &u->source_output->sample_spec) / fs) * PA_USEC_PER_SEC) / u->adjust_time; + //new_rate = base_rate - ((pa_usec_to_bytes(-diff_time, &u->source_output->sample_spec) / fs) * PA_USEC_PER_SEC) / u->adjust_time; new_rate = base_rate; } else { @@ -301,7 +301,7 @@ static void time_callback(pa_mainloop_api *a, pa_time_event *e, const struct tim } /* recording behind playback, we need to slowly adjust the rate to match */ - //new_rate = base_rate + ((pa_usec_to_bytes (diff_time, &u->source_output->sample_spec) / fs) * PA_USEC_PER_SEC) / u->adjust_time; + //new_rate = base_rate + ((pa_usec_to_bytes(diff_time, &u->source_output->sample_spec) / fs) * PA_USEC_PER_SEC) / u->adjust_time; /* assume equal samplerates for now */ new_rate = base_rate; @@ -404,7 +404,7 @@ static int source_set_state_cb(pa_source *s, pa_source_state_t state) { if (u->active_mask == 3) pa_core_rttime_restart(u->core, u->time_event, pa_rtclock_now() + u->adjust_time); - pa_atomic_store (&u->request_resync, 1); + pa_atomic_store(&u->request_resync, 1); pa_source_output_cork(u->source_output, FALSE); } else if (state == PA_SOURCE_SUSPENDED) { u->active_mask &= ~1; @@ -432,7 +432,7 @@ static int sink_set_state_cb(pa_sink *s, pa_sink_state_t state) { if (u->active_mask == 3) pa_core_rttime_restart(u->core, u->time_event, pa_rtclock_now() + u->adjust_time); - pa_atomic_store (&u->request_resync, 1); + pa_atomic_store(&u->request_resync, 1); pa_sink_input_cork(u->sink_input, FALSE); } else if (state == PA_SINK_SUSPENDED) { u->active_mask &= ~2; @@ -597,7 +597,7 @@ static void apply_diff_time(struct userdata *u, int64_t diff_time) { int64_t diff; if (diff_time < 0) { - diff = pa_usec_to_bytes (-diff_time, &u->source_output->sample_spec); + diff = pa_usec_to_bytes(-diff_time, &u->source_output->sample_spec); if (diff > 0) { /* add some extra safety samples to compensate for jitter in the @@ -610,7 +610,7 @@ static void apply_diff_time(struct userdata *u, int64_t diff_time) { u->source_skip = 0; } } else if (diff_time > 0) { - diff = pa_usec_to_bytes (diff_time, &u->source_output->sample_spec); + diff = pa_usec_to_bytes(diff_time, &u->source_output->sample_spec); if (diff > 0) { pa_log("playback too far ahead (%lld), drop source %lld", (long long) diff_time, (long long) diff); @@ -660,7 +660,7 @@ static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) u->in_push = FALSE; if (pa_atomic_cmpxchg (&u->request_resync, 1, 0)) { - do_resync (u); + do_resync(u); } pa_memblockq_push_align(u->source_memblockq, chunk); @@ -770,7 +770,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk if (i->thread_info.underrun_for > 0) { pa_log_debug("Handling end of underrun."); - pa_atomic_store (&u->request_resync, 1); + pa_atomic_store(&u->request_resync, 1); } /* let source thread handle the chunk. pass the sample count as well so that @@ -926,7 +926,7 @@ static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) { pa_log_debug("Sink input update max rewind %lld", (long long) nbytes); - pa_memblockq_set_maxrewind (u->sink_memblockq, nbytes); + pa_memblockq_set_maxrewind(u->sink_memblockq, nbytes); pa_sink_set_max_rewind_within_thread(u->sink, nbytes); } @@ -1300,8 +1300,7 @@ static void sink_input_mute_changed_cb(pa_sink_input *i) { pa_sink_mute_changed(u->sink, i->muted); } -static pa_echo_canceller_method_t get_ec_method_from_string(const char *method) -{ +static pa_echo_canceller_method_t get_ec_method_from_string(const char *method) { if (strcmp(method, "speex") == 0) return PA_ECHO_CANCELLER_SPEEX; else if (strcmp(method, "adrian") == 0) @@ -1502,6 +1501,7 @@ int pa__init(pa_module*m) { source_output_data.driver = __FILE__; source_output_data.module = m; source_output_data.source = source_master; + source_output_data.destination_source = u->source; /* FIXME source_output_data.flags = PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND; */ @@ -1531,11 +1531,14 @@ int pa__init(pa_module*m) { u->source_output->moving = source_output_moving_cb; u->source_output->userdata = u; + u->source->output_from_master = u->source_output; + /* Create sink input */ pa_sink_input_new_data_init(&sink_input_data); sink_input_data.driver = __FILE__; sink_input_data.module = m; sink_input_data.sink = sink_master; + sink_input_data.origin_sink = u->sink; pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Echo-Cancel Sink Stream"); pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter"); pa_sink_input_new_data_set_sample_spec(&sink_input_data, &sink_ss); @@ -1566,6 +1569,8 @@ int pa__init(pa_module*m) { u->sink_input->mute_changed = sink_input_mute_changed_cb; u->sink_input->userdata = u; + u->sink->input_to_master = u->sink_input; + pa_sink_input_get_silence(u->sink_input, &silence); u->source_memblockq = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, @@ -1609,7 +1614,7 @@ int pa__init(pa_module*m) { return 0; - fail: +fail: if (ma) pa_modargs_free(ma); @@ -1624,7 +1629,7 @@ int pa__get_n_used(pa_module *m) { pa_assert(m); pa_assert_se(u = m->userdata); - return pa_sink_linked_by(u->sink) + pa_source_linked_by(u->source); + return pa_sink_linked_by(u->sink) + pa_source_linked_by(u->source); } void pa__done(pa_module*m) { diff --git a/src/modules/echo-cancel/speex.c b/src/modules/echo-cancel/speex.c index 7851510b..ce361fc3 100644 --- a/src/modules/echo-cancel/speex.c +++ b/src/modules/echo-cancel/speex.c @@ -40,7 +40,7 @@ static const char* const valid_modargs[] = { }; static void pa_speex_ec_fixate_spec(pa_sample_spec *source_ss, pa_channel_map *source_map, - pa_sample_spec *sink_ss, pa_channel_map *sink_map) + pa_sample_spec *sink_ss, pa_channel_map *sink_map) { source_ss->format = PA_SAMPLE_S16NE; @@ -91,7 +91,7 @@ pa_bool_t pa_speex_ec_init(pa_core *c, pa_echo_canceller *ec, ec->params.priv.speex.state = speex_echo_state_init_mc (framelen, (rate * filter_size_ms) / 1000, source_ss->channels, source_ss->channels); if (!ec->params.priv.speex.state) - goto fail; + goto fail; speex_echo_ctl(ec->params.priv.speex.state, SPEEX_ECHO_SET_SAMPLING_RATE, &rate); @@ -100,17 +100,15 @@ pa_bool_t pa_speex_ec_init(pa_core *c, pa_echo_canceller *ec, fail: if (ma) - pa_modargs_free(ma); + pa_modargs_free(ma); return FALSE; } -void pa_speex_ec_run(pa_echo_canceller *ec, const uint8_t *rec, const uint8_t *play, uint8_t *out) -{ +void pa_speex_ec_run(pa_echo_canceller *ec, const uint8_t *rec, const uint8_t *play, uint8_t *out) { speex_echo_cancellation(ec->params.priv.speex.state, (const spx_int16_t *) rec, (const spx_int16_t *) play, (spx_int16_t *) out); } -void pa_speex_ec_done(pa_echo_canceller *ec) -{ - speex_echo_state_destroy (ec->params.priv.speex.state); +void pa_speex_ec_done(pa_echo_canceller *ec) { + speex_echo_state_destroy(ec->params.priv.speex.state); ec->params.priv.speex.state = NULL; } diff --git a/src/modules/jack/module-jack-sink.c b/src/modules/jack/module-jack-sink.c index 08a8befa..0b4e7ee4 100644 --- a/src/modules/jack/module-jack-sink.c +++ b/src/modules/jack/module-jack-sink.c @@ -68,7 +68,7 @@ PA_MODULE_LOAD_ONCE(TRUE); PA_MODULE_VERSION(PACKAGE_VERSION); PA_MODULE_USAGE( "sink_name=<name for the sink> " - "sink_properties=<properties for the card> " + "sink_properties=<properties for the card> " "server_name=<jack server name> " "client_name=<jack client name> " "channels=<number of channels> " diff --git a/src/modules/jack/module-jackdbus-detect.c b/src/modules/jack/module-jackdbus-detect.c index f635b233..c3dd7bb2 100644 --- a/src/modules/jack/module-jackdbus-detect.c +++ b/src/modules/jack/module-jackdbus-detect.c @@ -84,8 +84,7 @@ struct userdata { }; -static void ensure_ports_stopped(struct userdata* u) -{ +static void ensure_ports_stopped(struct userdata* u) { int i; pa_assert(u); @@ -97,8 +96,7 @@ static void ensure_ports_stopped(struct userdata* u) } } -static void ensure_ports_started(struct userdata* u) -{ +static void ensure_ports_started(struct userdata* u) { int i; pa_assert(u); @@ -120,8 +118,7 @@ static void ensure_ports_started(struct userdata* u) } -static pa_bool_t check_service_started(struct userdata* u) -{ +static pa_bool_t check_service_started(struct userdata* u) { DBusError error; DBusMessage *m = NULL, *reply = NULL; pa_bool_t new_status = FALSE; @@ -169,8 +166,7 @@ finish: return new_status; } -static DBusHandlerResult dbus_filter_handler(DBusConnection *c, DBusMessage *s, void *userdata) -{ +static DBusHandlerResult dbus_filter_handler(DBusConnection *c, DBusMessage *s, void *userdata) { struct userdata *u = NULL; DBusError error; @@ -182,13 +178,11 @@ static DBusHandlerResult dbus_filter_handler(DBusConnection *c, DBusMessage *s, if (dbus_message_is_signal(s, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) { const char *name, *old, *new; - if (!dbus_message_get_args( - s, - &error, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_STRING, &old, - DBUS_TYPE_STRING, &new, - DBUS_TYPE_INVALID)) + if (!dbus_message_get_args(s, &error, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_STRING, &old, + DBUS_TYPE_STRING, &new, + DBUS_TYPE_INVALID)) goto finish; if (strcmp(name, JACK_SERVICE_NAME)) goto finish; @@ -211,8 +205,7 @@ finish: return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } -int pa__init(pa_module *m) -{ +int pa__init(pa_module *m) { DBusError error; pa_dbus_connection *connection = NULL; struct userdata *u = NULL; @@ -275,8 +268,7 @@ fail: return -1; } -void pa__done(pa_module *m) -{ +void pa__done(pa_module *m) { struct userdata *u; pa_assert(m); diff --git a/src/modules/module-augment-properties.c b/src/modules/module-augment-properties.c index bdeee291..c3f5c088 100644 --- a/src/modules/module-augment-properties.c +++ b/src/modules/module-augment-properties.c @@ -25,6 +25,7 @@ #include <sys/stat.h> #include <dirent.h> +#include <time.h> #include <pulse/xmalloc.h> #include <pulse/volume.h> @@ -190,9 +191,7 @@ static void update_rule(struct rule *r) { continue; pa_xfree(fn); - fn = pa_sprintf_malloc(DESKTOPFILEDIR - PA_PATH_SEP "%s" PA_PATH_SEP "%s.desktop", - dir->d_name, r->process_name); + fn = pa_sprintf_malloc(DESKTOPFILEDIR PA_PATH_SEP "%s" PA_PATH_SEP "%s.desktop", dir->d_name, r->process_name); if (stat(fn, &st) == 0) { found = TRUE; @@ -357,7 +356,7 @@ fail: if (ma) pa_modargs_free(ma); - return -1; + return -1; } void pa__done(pa_module *m) { diff --git a/src/modules/module-card-restore.c b/src/modules/module-card-restore.c index 7c20ce31..ed0a31df 100644 --- a/src/modules/module-card-restore.c +++ b/src/modules/module-card-restore.c @@ -251,7 +251,7 @@ fail: if (ma) pa_modargs_free(ma); - return -1; + return -1; } void pa__done(pa_module*m) { diff --git a/src/modules/module-combine.c b/src/modules/module-combine.c index 3104ed68..ab93c05e 100644 --- a/src/modules/module-combine.c +++ b/src/modules/module-combine.c @@ -557,7 +557,7 @@ static int sink_input_process_msg(pa_msgobject *obj, int code, void *data, int64 switch (code) { case PA_SINK_INPUT_MESSAGE_GET_LATENCY: { - pa_usec_t *r = data; + pa_usec_t *r = data; *r = pa_bytes_to_usec(pa_memblockq_get_length(o->memblockq), &o->sink_input->sample_spec); diff --git a/src/modules/module-console-kit.c b/src/modules/module-console-kit.c index 875852f3..d52cc244 100644 --- a/src/modules/module-console-kit.c +++ b/src/modules/module-console-kit.c @@ -77,7 +77,7 @@ static void add_session(struct userdata *u, const char *id) { struct session *session; pa_client_new_data data; - dbus_error_init (&error); + dbus_error_init(&error); if (pa_hashmap_get(u->sessions, id)) { pa_log_warn("Duplicate session %s, ignoring.", id); diff --git a/src/modules/module-cork-music-on-phone.c b/src/modules/module-cork-music-on-phone.c index d3a2b00e..b629f06f 100644 --- a/src/modules/module-cork-music-on-phone.c +++ b/src/modules/module-cork-music-on-phone.c @@ -66,8 +66,10 @@ static pa_bool_t shall_cork(pa_sink *s, pa_sink_input *ignore) { if (!(role = pa_proplist_gets(j->proplist, PA_PROP_MEDIA_ROLE))) continue; - if (pa_streq(role, "phone")) + if (pa_streq(role, "phone")) { + pa_log_debug("Found a phone stream that will trigger the auto-cork."); return TRUE; + } } return FALSE; @@ -81,7 +83,7 @@ static void apply_cork(struct userdata *u, pa_sink *s, pa_sink_input *ignore, pa pa_sink_assert_ref(s); for (j = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); j; j = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) { - pa_bool_t corked; + pa_bool_t corked, muted, corked_here; const char *role; if (j == ignore) @@ -94,18 +96,25 @@ static void apply_cork(struct userdata *u, pa_sink *s, pa_sink_input *ignore, pa !pa_streq(role, "music")) continue; - corked = !!pa_hashmap_get(u->cork_state, j); + corked = (pa_sink_input_get_state(j) == PA_SINK_INPUT_CORKED); + muted = pa_sink_input_get_mute(j); + corked_here = !!pa_hashmap_get(u->cork_state, j); - if (cork && !corked) { - pa_hashmap_put(u->cork_state, j, PA_INT_TO_PTR(1)); + if (cork && !corked && !muted) { + pa_log_debug("Found a music/video stream that should be corked/muted."); + if (!corked_here) + pa_hashmap_put(u->cork_state, j, PA_INT_TO_PTR(1)); pa_sink_input_set_mute(j, TRUE, FALSE); pa_sink_input_send_event(j, PA_STREAM_EVENT_REQUEST_CORK, NULL); } else if (!cork) { pa_hashmap_remove(u->cork_state, j); - if (corked) { - pa_sink_input_set_mute(j, FALSE, FALSE); - pa_sink_input_send_event(j, PA_STREAM_EVENT_REQUEST_UNCORK, NULL); + if (corked_here && (corked || muted)) { + pa_log_debug("Found a music/video stream that should be uncorked/unmuted."); + if (muted) + pa_sink_input_set_mute(j, FALSE, FALSE); + if (corked) + pa_sink_input_send_event(j, PA_STREAM_EVENT_REQUEST_UNCORK, NULL); } } } @@ -193,7 +202,7 @@ fail: if (ma) pa_modargs_free(ma); - return -1; + return -1; } diff --git a/src/modules/module-detect.c b/src/modules/module-detect.c index 1fe8eb8b..2a90eb63 100644 --- a/src/modules/module-detect.c +++ b/src/modules/module-detect.c @@ -144,7 +144,7 @@ static int detect_oss(pa_core *c, int just_one) { line[strcspn(line, "\r\n")] = 0; if (!b) { - b = strcmp(line, "Audio devices:") == 0 || strcmp(line, "Installed devices:") == 0; + b = strcmp(line, "Audio devices:") == 0 || strcmp(line, "Installed devices:") == 0; continue; } diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c index eda6787e..47469b06 100644 --- a/src/modules/module-device-manager.c +++ b/src/modules/module-device-manager.c @@ -698,7 +698,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3 pa_strlcpy(entry.icon, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_ICON_NAME)), sizeof(entry.icon)); - } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE) { + } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE) { pa_source *source; pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE); @@ -1174,7 +1174,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio goto fail; if (PA_INVALID_INDEX == (role_index = get_role_index(role))) - goto fail; + goto fail; /* Cycle through the devices given and make sure they exist */ h = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); @@ -1209,9 +1209,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio if (first) { first = FALSE; sink_mode = (0 == strncmp("sink:", s, 5)); - } else if ((sink_mode && 0 != strncmp("sink:", s, 5)) - || (!sink_mode && 0 != strncmp("source:", s, 7))) - { + } else if ((sink_mode && 0 != strncmp("sink:", s, 5)) || (!sink_mode && 0 != strncmp("source:", s, 7))) { while ((device = pa_hashmap_steal_first(h))) { pa_xfree(device->device); pa_xfree(device); @@ -1542,7 +1540,7 @@ fail: if (ma) pa_modargs_free(ma); - return -1; + return -1; } void pa__done(pa_module*m) { diff --git a/src/modules/module-device-restore.c b/src/modules/module-device-restore.c index fcd3ccf6..4c31a985 100644 --- a/src/modules/module-device-restore.c +++ b/src/modules/module-device-restore.c @@ -514,7 +514,7 @@ fail: if (ma) pa_modargs_free(ma); - return -1; + return -1; } void pa__done(pa_module*m) { diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index bc349ce0..bb350c38 100644 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -77,7 +77,8 @@ PA_MODULE_DESCRIPTION(_("General Purpose Equalizer")); PA_MODULE_VERSION(PACKAGE_VERSION); PA_MODULE_LOAD_ONCE(FALSE); PA_MODULE_USAGE( - _("sink_name=<name of the sink>" + _("sink_name=<name of the sink> " + "sink_properties=<properties for the sink> " "master=<sink to connect to> " "format=<sample format> " "rate=<sample rate> " @@ -131,6 +132,7 @@ struct userdata { static const char* const valid_modargs[] = { "sink_name", + "sink_properties", "master", "format", "rate", @@ -1200,6 +1202,7 @@ int pa__init(pa_module*m) { sink_input_data.driver = __FILE__; sink_input_data.module = m; sink_input_data.sink = master; + sink_input_data.origin_sink = u->sink; pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Equalized Stream"); pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter"); pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss); @@ -1225,9 +1228,10 @@ int pa__init(pa_module*m) { u->sink_input->moving = sink_input_moving_cb; u->sink_input->volume_changed = sink_input_volume_changed_cb; u->sink_input->mute_changed = sink_input_mute_changed_cb; - u->sink_input->userdata = u; + u->sink->input_to_master = u->sink_input; + dbus_init(u); /* default filter to these */ diff --git a/src/modules/module-hal-detect.c b/src/modules/module-hal-detect.c index 23d40100..941ac3a4 100644 --- a/src/modules/module-hal-detect.c +++ b/src/modules/module-hal-detect.c @@ -57,13 +57,13 @@ PA_MODULE_VERSION(PACKAGE_VERSION); PA_MODULE_LOAD_ONCE(TRUE); #if defined(HAVE_ALSA) && defined(HAVE_OSS_OUTPUT) PA_MODULE_USAGE("api=<alsa or oss> " - "tsched=<enable system timer based scheduling mode?>" + "tsched=<enable system timer based scheduling mode?> " "subdevices=<init all subdevices>"); #elif defined(HAVE_ALSA) PA_MODULE_USAGE("api=<alsa> " "tsched=<enable system timer based scheduling mode?>"); #elif defined(HAVE_OSS_OUTPUT) -PA_MODULE_USAGE("api=<oss>" +PA_MODULE_USAGE("api=<oss> " "subdevices=<init all subdevices>"); #endif PA_MODULE_DEPRECATED("Please use module-udev-detect instead of module-hal-detect!"); diff --git a/src/modules/module-intended-roles.c b/src/modules/module-intended-roles.c index 78a2c49c..d19444d2 100644 --- a/src/modules/module-intended-roles.c +++ b/src/modules/module-intended-roles.c @@ -447,7 +447,7 @@ fail: if (ma) pa_modargs_free(ma); - return -1; + return -1; } void pa__done(pa_module*m) { diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c index 88df67ec..f6430f29 100644 --- a/src/modules/module-ladspa-sink.c +++ b/src/modules/module-ladspa-sink.c @@ -50,19 +50,24 @@ PA_MODULE_DESCRIPTION(_("Virtual LADSPA sink")); PA_MODULE_VERSION(PACKAGE_VERSION); PA_MODULE_LOAD_ONCE(FALSE); PA_MODULE_USAGE( - _("sink_name=<name for the sink> " - "sink_properties=<properties for the sink> " - "master=<name of sink to filter> " - "format=<sample format> " - "rate=<sample rate> " - "channels=<number of channels> " - "channel_map=<channel map> " - "plugin=<ladspa plugin name> " - "label=<ladspa plugin label> " - "control=<comma seperated list of input control values>")); + _("sink_name=<name for the sink> " + "sink_properties=<properties for the sink> " + "master=<name of sink to filter> " + "format=<sample format> " + "rate=<sample rate> " + "channels=<number of channels> " + "channel_map=<input channel map> " + "plugin=<ladspa plugin name> " + "label=<ladspa plugin label> " + "control=<comma seperated list of input control values> " + "input_ladspaport_map=<comma separated list of input LADSPA port names> " + "output_ladspaport_map=<comma separated list of output LADSPA port names> ")); #define MEMBLOCKQ_MAXLENGTH (16*1024*1024) +/* PLEASE NOTICE: The PortAudio ports and the LADSPA ports are two different concepts. +They are not related and where possible the names of the LADSPA port variables contains "ladspa" to avoid confusion */ + struct userdata { pa_module *module; @@ -70,15 +75,14 @@ struct userdata { pa_sink_input *sink_input; const LADSPA_Descriptor *descriptor; - unsigned channels; LADSPA_Handle handle[PA_CHANNELS_MAX]; - LADSPA_Data *input, *output; + unsigned long max_ladspaport_count, input_count, output_count, channels; + LADSPA_Data **input, **output; size_t block_size; - unsigned long input_port, output_port; LADSPA_Data *control; /* This is a dummy buffer. Every port must be connected, but we don't care - about control out ports. We connect them all to this single buffer. */ + about control out ports. We connect them all to this single buffer. */ LADSPA_Data control_out; pa_memblockq *memblockq; @@ -97,6 +101,8 @@ static const char* const valid_modargs[] = { "plugin", "label", "control", + "input_ladspaport_map", + "output_ladspaport_map", NULL }; @@ -106,26 +112,26 @@ static int sink_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t of switch (code) { - case PA_SINK_MESSAGE_GET_LATENCY: + case PA_SINK_MESSAGE_GET_LATENCY: - /* The sink is _put() before the sink input is, so let's - * make sure we don't access it in that time. Also, the - * sink input is first shut down, the sink second. */ - if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) || + /* The sink is _put() before the sink input is, so let's + * make sure we don't access it in that time. Also, the + * sink input is first shut down, the sink second. */ + if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) || !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state)) { - *((pa_usec_t*) data) = 0; - return 0; - } + *((pa_usec_t*) data) = 0; + return 0; + } - *((pa_usec_t*) data) = + *((pa_usec_t*) data) = - /* Get the latency of the master sink */ - pa_sink_get_latency_within_thread(u->sink_input->sink) + + /* Get the latency of the master sink */ + pa_sink_get_latency_within_thread(u->sink_input->sink) + - /* Add the latency internal to our sink input on top */ - pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->sink_input->sink->sample_spec); + /* Add the latency internal to our sink input on top */ + pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->sink_input->sink->sample_spec); - return 0; + return 0; } return pa_sink_process_msg(o, code, data, offset, chunk); @@ -139,7 +145,7 @@ static int sink_set_state_cb(pa_sink *s, pa_sink_state_t state) { pa_assert_se(u = s->userdata); if (!PA_SINK_IS_LINKED(state) || - !PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input))) + !PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input))) return 0; pa_sink_input_cork(u->sink_input, state == PA_SINK_SUSPENDED); @@ -154,7 +160,7 @@ static void sink_request_rewind_cb(pa_sink *s) { pa_assert_se(u = s->userdata); if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) || - !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state)) + !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state)) return; /* Just hand this one over to the master sink */ @@ -171,13 +177,13 @@ static void sink_update_requested_latency_cb(pa_sink *s) { pa_assert_se(u = s->userdata); if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) || - !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state)) + !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state)) return; /* Just hand this one over to the master sink */ pa_sink_input_set_requested_latency_within_thread( - u->sink_input, - pa_sink_get_requested_latency_within_thread(s)); + u->sink_input, + pa_sink_get_requested_latency_within_thread(s)); } /* Called from main context */ @@ -188,7 +194,7 @@ static void sink_set_volume_cb(pa_sink *s) { pa_assert_se(u = s->userdata); if (!PA_SINK_IS_LINKED(pa_sink_get_state(s)) || - !PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input))) + !PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input))) return; pa_sink_input_set_volume(u->sink_input, &s->real_volume, s->save_volume, TRUE); @@ -202,7 +208,7 @@ static void sink_set_mute_cb(pa_sink *s) { pa_assert_se(u = s->userdata); if (!PA_SINK_IS_LINKED(pa_sink_get_state(s)) || - !PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input))) + !PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input))) return; pa_sink_input_set_mute(u->sink_input, s->muted, s->save_muted); @@ -213,7 +219,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk struct userdata *u; float *src, *dst; size_t fs; - unsigned n, c; + unsigned n, h, c; pa_memchunk tchunk; pa_sink_input_assert_ref(i); @@ -248,10 +254,12 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk src = (float*) ((uint8_t*) pa_memblock_acquire(tchunk.memblock) + tchunk.index); dst = (float*) pa_memblock_acquire(chunk->memblock); - for (c = 0; c < u->channels; c++) { - pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input, sizeof(float), src+c, u->channels*sizeof(float), n); - u->descriptor->run(u->handle[c], n); - pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+c, u->channels*sizeof(float), u->output, sizeof(float), n); + for (h = 0; h < (u->channels / u->max_ladspaport_count); h++) { + for (c = 0; c < u->input_count; c++) + pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input[c], sizeof(float), src+ h*u->max_ladspaport_count + c, u->channels*sizeof(float), n); + u->descriptor->run(u->handle[h], n); + for (c = 0; c < u->output_count; c++) + pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst + h*u->max_ladspaport_count + c, u->channels*sizeof(float), u->output[c], sizeof(float), n); } pa_memblock_release(tchunk.memblock); @@ -286,10 +294,10 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { /* Reset the plugin */ if (u->descriptor->deactivate) - for (c = 0; c < u->channels; c++) + for (c = 0; c < (u->channels / u->max_ladspaport_count); c++) u->descriptor->deactivate(u->handle[c]); if (u->descriptor->activate) - for (c = 0; c < u->channels; c++) + for (c = 0; c < (u->channels / u->max_ladspaport_count); c++) u->descriptor->activate(u->handle[c]); } } @@ -399,7 +407,7 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s /* If we are added for the first time, ask for a rewinding so that * we are heard right-away. */ if (PA_SINK_INPUT_IS_LINKED(state) && - i->thread_info.state == PA_SINK_INPUT_INIT) { + i->thread_info.state == PA_SINK_INPUT_INIT) { pa_log_debug("Requesting rewind due to state change."); pa_sink_input_request_rewind(i, 0, FALSE, TRUE, TRUE); } @@ -471,14 +479,13 @@ int pa__init(pa_module*m) { pa_sink *master; pa_sink_input_new_data sink_input_data; pa_sink_new_data sink_data; - const char *plugin, *label; + const char *plugin, *label, *input_ladspaport_map, *output_ladspaport_map; LADSPA_Descriptor_Function descriptor_func; + unsigned long input_ladspaport[PA_CHANNELS_MAX], output_ladspaport[PA_CHANNELS_MAX]; const char *e, *cdata; const LADSPA_Descriptor *d; - unsigned long input_port, output_port, p, j, n_control; - unsigned c; + unsigned long p, h, j, n_control, c; pa_bool_t *use_default = NULL; - pa_memchunk silence; pa_assert(m); @@ -512,15 +519,22 @@ int pa__init(pa_module*m) { goto fail; } + if (!(input_ladspaport_map = pa_modargs_get_value(ma, "input_ladspaport_map", NULL))) + pa_log_debug("Using default input ladspa port mapping"); + + if (!(output_ladspaport_map = pa_modargs_get_value(ma, "output_ladspaport_map", NULL))) + pa_log_debug("Using default output ladspa port mapping"); + cdata = pa_modargs_get_value(ma, "control", NULL); u = pa_xnew0(struct userdata, 1); u->module = m; m->userdata = u; - - pa_silence_memchunk_get(&m->core->silence_cache, m->core->mempool, &silence, &ss, 0); - u->memblockq = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, pa_frame_size(&ss), 1, 1, 0, &silence); - pa_memblock_unref(silence.memblock); + u->memblockq = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, pa_frame_size(&ss), 1, 1, 0, NULL); + u->max_ladspaport_count = 1; /*to avoid division by zero etc. in pa__done when failing before this value has been set*/ + u->channels = 0; + u->input = NULL; + u->output = NULL; if (!(e = getenv("LADSPA_PATH"))) e = LADSPA_PATH; @@ -562,64 +576,127 @@ int pa__init(pa_module*m) { pa_log_debug("Maker: %s", d->Maker); pa_log_debug("Copyright: %s", d->Copyright); - input_port = output_port = (unsigned long) -1; n_control = 0; + u->channels = ss.channels; + /* + * Enumerate ladspa ports + * Default mapping is in order given by the plugin + */ for (p = 0; p < d->PortCount; p++) { + if (LADSPA_IS_PORT_AUDIO(d->PortDescriptors[p])) { + if (LADSPA_IS_PORT_INPUT(d->PortDescriptors[p])) { + pa_log_debug("Port %lu is input: %s", p, d->PortNames[p]); + input_ladspaport[u->input_count] = p; + u->input_count++; + } else if (LADSPA_IS_PORT_OUTPUT(d->PortDescriptors[p])) { + pa_log_debug("Port %lu is output: %s", p, d->PortNames[p]); + output_ladspaport[u->output_count] = p; + u->output_count++; + } + } else if (LADSPA_IS_PORT_CONTROL(d->PortDescriptors[p]) && LADSPA_IS_PORT_INPUT(d->PortDescriptors[p])) { + pa_log_debug("Port %lu is control: %s", p, d->PortNames[p]); + n_control++; + } else + pa_log_debug("Ignored port %s", d->PortNames[p]); + /* XXX: Has anyone ever seen an in-place plugin with non-equal number of input and output ports? */ + /* Could be if the plugin is for up-mixing stereo to 5.1 channels */ + /* Or if the plugin is down-mixing 5.1 to two channel stereo or binaural encoded signal */ + if (u->input_count > u->max_ladspaport_count) + u->max_ladspaport_count = u->input_count; + else + u->max_ladspaport_count = u->output_count; + } + + if (u->channels % u->max_ladspaport_count) { + pa_log("Cannot handle non-integral number of plugins required for given number of channels"); + goto fail; + } - if (LADSPA_IS_PORT_INPUT(d->PortDescriptors[p]) && LADSPA_IS_PORT_AUDIO(d->PortDescriptors[p])) { + pa_log_debug("Will run %lu plugin instances", u->channels / u->max_ladspaport_count); - if (strcmp(d->PortNames[p], "Input") == 0) { - pa_assert(input_port == (unsigned long) -1); - input_port = p; - } else { - pa_log("Found audio input port on plugin we cannot handle: %s", d->PortNames[p]); + /* Parse data for input ladspa port map */ + if (input_ladspaport_map) { + const char *state = NULL; + char *pname; + c = 0; + while ((pname = pa_split(input_ladspaport_map, ",", &state))) { + if (c == u->input_count) { + pa_log("Too many ports in input ladspa port map"); goto fail; } - } else if (LADSPA_IS_PORT_OUTPUT(d->PortDescriptors[p]) && LADSPA_IS_PORT_AUDIO(d->PortDescriptors[p])) { - if (strcmp(d->PortNames[p], "Output") == 0) { - pa_assert(output_port == (unsigned long) -1); - output_port = p; - } else { - pa_log("Found audio output port on plugin we cannot handle: %s", d->PortNames[p]); - goto fail; + for (p = 0; p < d->PortCount; p++) { + if (strcmp(d->PortNames[p], pname) == 0) { + if (LADSPA_IS_PORT_AUDIO(d->PortDescriptors[p]) && LADSPA_IS_PORT_INPUT(d->PortDescriptors[p])) { + input_ladspaport[c] = p; + } else { + pa_log("Port %s is not an audio input ladspa port", pname); + pa_xfree(pname); + goto fail; + } + } } - - } else if (LADSPA_IS_PORT_INPUT(d->PortDescriptors[p]) && LADSPA_IS_PORT_CONTROL(d->PortDescriptors[p])) - n_control++; - else { - pa_assert(LADSPA_IS_PORT_OUTPUT(d->PortDescriptors[p]) && LADSPA_IS_PORT_CONTROL(d->PortDescriptors[p])); - pa_log_debug("Ignored control output port \"%s\".", d->PortNames[p]); + c++; + pa_xfree(pname); } } - if ((input_port == (unsigned long) -1) || (output_port == (unsigned long) -1)) { - pa_log("Failed to identify input and output ports. " - "Right now this module can only deal with plugins which provide an 'Input' and an 'Output' audio port. " - "Patches welcome!"); - goto fail; + /* Parse data for output port map */ + if (output_ladspaport_map) { + const char *state = NULL; + char *pname; + c = 0; + while ((pname = pa_split(output_ladspaport_map, ",", &state))) { + if (c == u->output_count) { + pa_log("Too many ports in output ladspa port map"); + goto fail; + } + for (p = 0; p < d->PortCount; p++) { + if (strcmp(d->PortNames[p], pname) == 0) { + if (LADSPA_IS_PORT_AUDIO(d->PortDescriptors[p]) && LADSPA_IS_PORT_OUTPUT(d->PortDescriptors[p])) { + output_ladspaport[c] = p; + } else { + pa_log("Port %s is not an output ladspa port", pname); + pa_xfree(pname); + goto fail; + } + } + } + c++; + pa_xfree(pname); + } } + u->block_size = pa_frame_align(pa_mempool_block_size_max(m->core->mempool), &ss); - u->input = (LADSPA_Data*) pa_xnew(uint8_t, (unsigned) u->block_size); - if (LADSPA_IS_INPLACE_BROKEN(d->Properties)) - u->output = (LADSPA_Data*) pa_xnew(uint8_t, (unsigned) u->block_size); - else + /* Create buffers */ + if (LADSPA_IS_INPLACE_BROKEN(d->Properties)) { + u->input = (LADSPA_Data**) pa_xnew(LADSPA_Data*, (unsigned) u->input_count); + for (c = 0; c < u->input_count; c++) + u->input[c] = (LADSPA_Data*) pa_xnew(uint8_t, (unsigned) u->block_size); + u->output = (LADSPA_Data**) pa_xnew(LADSPA_Data*, (unsigned) u->output_count); + for (c = 0; c < u->output_count; c++) + u->output[c] = (LADSPA_Data*) pa_xnew(uint8_t, (unsigned) u->block_size); + } else { + u->input = (LADSPA_Data**) pa_xnew(LADSPA_Data*, (unsigned) u->max_ladspaport_count); + for (c = 0; c < u->max_ladspaport_count; c++) + u->input[c] = (LADSPA_Data*) pa_xnew(uint8_t, (unsigned) u->block_size); u->output = u->input; - - u->channels = ss.channels; - - for (c = 0; c < ss.channels; c++) { - if (!(u->handle[c] = d->instantiate(d, ss.rate))) { - pa_log("Failed to instantiate plugin %s with label %s for channel %i", plugin, d->Label, c); + } + /* Initialize plugin instances */ + for (h = 0; h < (u->channels / u->max_ladspaport_count); h++) { + if (!(u->handle[h] = d->instantiate(d, ss.rate))) { + pa_log("Failed to instantiate plugin %s with label %s", plugin, d->Label); goto fail; } - d->connect_port(u->handle[c], input_port, u->input); - d->connect_port(u->handle[c], output_port, u->output); + for (c = 0; c < u->input_count; c++) + d->connect_port(u->handle[h], input_ladspaport[c], u->input[c]); + for (c = 0; c < u->output_count; c++) + d->connect_port(u->handle[h], output_ladspaport[c], u->output[c]); } if (!cdata && n_control > 0) { @@ -630,7 +707,6 @@ int pa__init(pa_module*m) { if (n_control > 0) { const char *state = NULL; char *k; - unsigned long h; u->control = pa_xnew(LADSPA_Data, (unsigned) n_control); use_default = pa_xnew(pa_bool_t, (unsigned) n_control); @@ -658,7 +734,7 @@ int pa__init(pa_module*m) { } /* The previous loop doesn't take the last control value into account - if it is left empty, so we do it here. */ + if it is left empty, so we do it here. */ if (*cdata == 0 || cdata[strlen(cdata) - 1] == ',') { if (p < n_control) use_default[p] = TRUE; @@ -684,7 +760,7 @@ int pa__init(pa_module*m) { continue; if (LADSPA_IS_PORT_OUTPUT(d->PortDescriptors[p])) { - for (c = 0; c < ss.channels; c++) + for (c = 0; c < (u->channels / u->max_ladspaport_count); c++) d->connect_port(u->handle[c], p, &u->control_out); continue; } @@ -709,53 +785,53 @@ int pa__init(pa_module*m) { switch (hint & LADSPA_HINT_DEFAULT_MASK) { - case LADSPA_HINT_DEFAULT_MINIMUM: - u->control[h] = lower; - break; - - case LADSPA_HINT_DEFAULT_MAXIMUM: - u->control[h] = upper; - break; - - case LADSPA_HINT_DEFAULT_LOW: - if (LADSPA_IS_HINT_LOGARITHMIC(hint)) - u->control[h] = (LADSPA_Data) exp(log(lower) * 0.75 + log(upper) * 0.25); - else - u->control[h] = (LADSPA_Data) (lower * 0.75 + upper * 0.25); - break; - - case LADSPA_HINT_DEFAULT_MIDDLE: - if (LADSPA_IS_HINT_LOGARITHMIC(hint)) - u->control[h] = (LADSPA_Data) exp(log(lower) * 0.5 + log(upper) * 0.5); - else - u->control[h] = (LADSPA_Data) (lower * 0.5 + upper * 0.5); - break; - - case LADSPA_HINT_DEFAULT_HIGH: - if (LADSPA_IS_HINT_LOGARITHMIC(hint)) - u->control[h] = (LADSPA_Data) exp(log(lower) * 0.25 + log(upper) * 0.75); - else - u->control[h] = (LADSPA_Data) (lower * 0.25 + upper * 0.75); - break; - - case LADSPA_HINT_DEFAULT_0: - u->control[h] = 0; - break; - - case LADSPA_HINT_DEFAULT_1: - u->control[h] = 1; - break; - - case LADSPA_HINT_DEFAULT_100: - u->control[h] = 100; - break; - - case LADSPA_HINT_DEFAULT_440: - u->control[h] = 440; - break; - - default: - pa_assert_not_reached(); + case LADSPA_HINT_DEFAULT_MINIMUM: + u->control[h] = lower; + break; + + case LADSPA_HINT_DEFAULT_MAXIMUM: + u->control[h] = upper; + break; + + case LADSPA_HINT_DEFAULT_LOW: + if (LADSPA_IS_HINT_LOGARITHMIC(hint)) + u->control[h] = (LADSPA_Data) exp(log(lower) * 0.75 + log(upper) * 0.25); + else + u->control[h] = (LADSPA_Data) (lower * 0.75 + upper * 0.25); + break; + + case LADSPA_HINT_DEFAULT_MIDDLE: + if (LADSPA_IS_HINT_LOGARITHMIC(hint)) + u->control[h] = (LADSPA_Data) exp(log(lower) * 0.5 + log(upper) * 0.5); + else + u->control[h] = (LADSPA_Data) (lower * 0.5 + upper * 0.5); + break; + + case LADSPA_HINT_DEFAULT_HIGH: + if (LADSPA_IS_HINT_LOGARITHMIC(hint)) + u->control[h] = (LADSPA_Data) exp(log(lower) * 0.25 + log(upper) * 0.75); + else + u->control[h] = (LADSPA_Data) (lower * 0.25 + upper * 0.75); + break; + + case LADSPA_HINT_DEFAULT_0: + u->control[h] = 0; + break; + + case LADSPA_HINT_DEFAULT_1: + u->control[h] = 1; + break; + + case LADSPA_HINT_DEFAULT_100: + u->control[h] = 100; + break; + + case LADSPA_HINT_DEFAULT_440: + u->control[h] = 440; + break; + + default: + pa_assert_not_reached(); } } @@ -764,7 +840,7 @@ int pa__init(pa_module*m) { pa_log_debug("Binding %f to port %s", u->control[h], d->PortNames[p]); - for (c = 0; c < ss.channels; c++) + for (c = 0; c < (u->channels / u->max_ladspaport_count); c++) d->connect_port(u->handle[c], p, &u->control[h]); h++; @@ -774,7 +850,7 @@ int pa__init(pa_module*m) { } if (d->activate) - for (c = 0; c < u->channels; c++) + for (c = 0; c < (u->channels / u->max_ladspaport_count); c++) d->activate(u->handle[c]); /* Create sink */ @@ -832,6 +908,7 @@ int pa__init(pa_module*m) { sink_input_data.driver = __FILE__; sink_input_data.module = m; sink_input_data.sink = master; + sink_input_data.origin_sink = u->sink; pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "LADSPA Stream"); pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter"); pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss); @@ -859,11 +936,12 @@ int pa__init(pa_module*m) { u->sink_input->mute_changed = sink_input_mute_changed_cb; u->sink_input->userdata = u; + u->sink->input_to_master = u->sink_input; + pa_sink_put(u->sink); pa_sink_input_put(u->sink_input); pa_modargs_free(ma); - pa_xfree(use_default); return 0; @@ -898,7 +976,7 @@ void pa__done(pa_module*m) { return; /* See comments in sink_input_kill_cb() above regarding - * destruction order! */ + * destruction order! */ if (u->sink_input) pa_sink_input_unlink(u->sink_input); @@ -912,21 +990,36 @@ void pa__done(pa_module*m) { if (u->sink) pa_sink_unref(u->sink); - for (c = 0; c < u->channels; c++) + for (c = 0; c < (u->channels / u->max_ladspaport_count); c++) { if (u->handle[c]) { if (u->descriptor->deactivate) u->descriptor->deactivate(u->handle[c]); u->descriptor->cleanup(u->handle[c]); } + } - if (u->output != u->input) - pa_xfree(u->output); + if (u->output == u->input) { + if (u->input != NULL) { + for (c = 0; c < u->max_ladspaport_count; c++) + pa_xfree(u->input[c]); + pa_xfree(u->input); + } + } else { + if (u->input != NULL) { + for (c = 0; c < u->input_count; c++) + pa_xfree(u->input[c]); + pa_xfree(u->input); + } + if (u->output != NULL) { + for (c = 0; c < u->output_count; c++) + pa_xfree(u->output[c]); + pa_xfree(u->output); + } + } if (u->memblockq) pa_memblockq_free(u->memblockq); - pa_xfree(u->input); pa_xfree(u->control); - pa_xfree(u); } diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c index 8cabf71b..9a8640b1 100644 --- a/src/modules/module-loopback.c +++ b/src/modules/module-loopback.c @@ -437,9 +437,9 @@ static int sink_input_process_msg_cb(pa_msgobject *obj, int code, void *data, in switch (code) { case PA_SINK_INPUT_MESSAGE_GET_LATENCY: { - pa_usec_t *r = data; + pa_usec_t *r = data; - pa_sink_input_assert_io_context(u->sink_input); + pa_sink_input_assert_io_context(u->sink_input); *r = pa_bytes_to_usec(pa_memblockq_get_length(u->memblockq), &u->sink_input->sample_spec); diff --git a/src/modules/module-match.c b/src/modules/module-match.c index f9f36fdb..c8f27062 100644 --- a/src/modules/module-match.c +++ b/src/modules/module-match.c @@ -261,7 +261,7 @@ fail: if (ma) pa_modargs_free(ma); - return -1; + return -1; } void pa__done(pa_module*m) { diff --git a/src/modules/module-pipe-sink.c b/src/modules/module-pipe-sink.c index 4ed91aae..6623aef4 100644 --- a/src/modules/module-pipe-sink.c +++ b/src/modules/module-pipe-sink.c @@ -61,7 +61,7 @@ PA_MODULE_USAGE( "sink_properties=<properties for the sink> " "file=<path of the FIFO> " "format=<sample format> " - "rate=<sample rate>" + "rate=<sample rate> " "channels=<number of channels> " "channel_map=<channel map>"); @@ -362,7 +362,7 @@ void pa__done(pa_module*m) { pa_sink_unref(u->sink); if (u->memchunk.memblock) - pa_memblock_unref(u->memchunk.memblock); + pa_memblock_unref(u->memchunk.memblock); if (u->rtpoll_item) pa_rtpoll_item_free(u->rtpoll_item); diff --git a/src/modules/module-position-event-sounds.c b/src/modules/module-position-event-sounds.c index ee4c8c88..3cba0f37 100644 --- a/src/modules/module-position-event-sounds.c +++ b/src/modules/module-position-event-sounds.c @@ -164,7 +164,7 @@ fail: if (ma) pa_modargs_free(ma); - return -1; + return -1; } void pa__done(pa_module*m) { diff --git a/src/modules/module-protocol-stub.c b/src/modules/module-protocol-stub.c index 52506f95..29cb419d 100644 --- a/src/modules/module-protocol-stub.c +++ b/src/modules/module-protocol-stub.c @@ -135,7 +135,7 @@ PA_MODULE_DESCRIPTION("Native protocol "SOCKET_DESCRIPTION); PA_MODULE_USAGE("auth-anonymous=<don't check for cookies?> " "auth-cookie=<path to cookie file> " - "auth-cookie-enabled=<enable cookie authentification? " + "auth-cookie-enabled=<enable cookie authentification?> " AUTH_USAGE SOCKET_USAGE); #elif defined(USE_PROTOCOL_ESOUND) @@ -164,7 +164,7 @@ "source=<source to connect to> " "auth-anonymous=<don't verify cookies?> " "auth-cookie=<path to cookie file> " - "auth-cookie-enabled=<enable cookie authentification? " + "auth-cookie-enabled=<enable cookie authentification?> " AUTH_USAGE SOCKET_USAGE); #else diff --git a/src/modules/module-remap-sink.c b/src/modules/module-remap-sink.c index 43748bd0..7f64f306 100644 --- a/src/modules/module-remap-sink.c +++ b/src/modules/module-remap-sink.c @@ -420,6 +420,7 @@ int pa__init(pa_module*m) { sink_input_data.driver = __FILE__; sink_input_data.module = m; sink_input_data.sink = master; + sink_input_data.origin_sink = u->sink; pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Remapped Stream"); pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter"); pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss); @@ -446,6 +447,8 @@ int pa__init(pa_module*m) { u->sink_input->moving = sink_input_moving_cb; u->sink_input->userdata = u; + u->sink->input_to_master = u->sink_input; + pa_sink_put(u->sink); pa_sink_input_put(u->sink_input); diff --git a/src/modules/module-rygel-media-server.c b/src/modules/module-rygel-media-server.c index 76b485c3..9d23e8a4 100644 --- a/src/modules/module-rygel-media-server.c +++ b/src/modules/module-rygel-media-server.c @@ -723,10 +723,7 @@ static DBusHandlerResult root_handler(DBusConnection *c, DBusMessage *m, void *u const char *xml = ROOT_INTROSPECT_XML; pa_assert_se(r = dbus_message_new_method_return(m)); - pa_assert_se(dbus_message_append_args( - r, - DBUS_TYPE_STRING, &xml, - DBUS_TYPE_INVALID)); + pa_assert_se(dbus_message_append_args(r, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID)); } else return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; @@ -938,10 +935,7 @@ static DBusHandlerResult sinks_and_sources_handler(DBusConnection *c, DBusMessag xml = pa_strbuf_tostring_free(sb); pa_assert_se(r = dbus_message_new_method_return(m)); - pa_assert_se(dbus_message_append_args( - r, - DBUS_TYPE_STRING, &xml, - DBUS_TYPE_INVALID)); + pa_assert_se(dbus_message_append_args(r, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID)); pa_xfree(xml); } else @@ -1012,10 +1006,7 @@ static DBusHandlerResult sinks_and_sources_handler(DBusConnection *c, DBusMessag ITEM_INTROSPECT_XML; pa_assert_se(r = dbus_message_new_method_return(m)); - pa_assert_se(dbus_message_append_args( - r, - DBUS_TYPE_STRING, &xml, - DBUS_TYPE_INVALID)); + pa_assert_se(dbus_message_append_args(r, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID)); } else return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; diff --git a/src/modules/module-solaris.c b/src/modules/module-solaris.c index 396094ce..ee06b3be 100644 --- a/src/modules/module-solaris.c +++ b/src/modules/module-solaris.c @@ -307,8 +307,8 @@ static int auto_format(int fd, int mode, pa_sample_spec *ss) { info.record.encoding = AUDIO_ENCODING_LINEAR; break; default: - pa_log("AUDIO_SETINFO: Unsupported sample format."); - return -1; + pa_log("AUDIO_SETINFO: Unsupported sample format."); + return -1; } } @@ -1015,7 +1015,7 @@ int pa__init(pa_module *m) { else pa_log_warn("Could not register SIGPOLL handler"); - if (!(u->thread = pa_thread_new(thread_func, u))) { + if (!(u->thread = pa_thread_new("solaris", thread_func, u))) { pa_log("Failed to create thread."); goto fail; } diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c index f8fecd89..df48dce2 100644 --- a/src/modules/module-stream-restore.c +++ b/src/modules/module-stream-restore.c @@ -1168,6 +1168,8 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3 } if (sink_input->save_volume) { + pa_assert(pa_sink_input_is_volume_writable(sink_input)); + entry.channel_map = sink_input->channel_map; pa_sink_input_get_volume(sink_input, &entry.volume, FALSE); entry.volume_valid = TRUE; @@ -1327,8 +1329,11 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_inpu if ((e = read_entry(u, name))) { if (u->restore_volume && e->volume_valid) { - - if (!new_data->volume_is_set) { + if (!pa_sink_input_new_data_is_volume_writable(new_data)) + pa_log_debug("Not restoring volume for sink input %s, because its volume can't be changed.", name); + else if (new_data->volume_is_set) + pa_log_debug("Not restoring volume for sink input %s, because already set.", name); + else { pa_cvolume v; pa_log_info("Restoring volume for sink input %s.", name); @@ -1339,8 +1344,7 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_inpu new_data->volume_is_absolute = FALSE; new_data->save_volume = TRUE; - } else - pa_log_debug("Not restoring volume for sink input %s, because already set.", name); + } } if (u->restore_muted && e->muted_valid) { @@ -1615,7 +1619,7 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) { } pa_xfree(n); - if (u->restore_volume && e->volume_valid) { + if (u->restore_volume && e->volume_valid && pa_sink_input_is_volume_writable(si)) { pa_cvolume v; v = e->volume; @@ -2106,7 +2110,7 @@ fail: if (ma) pa_modargs_free(ma); - return -1; + return -1; } #ifdef HAVE_DBUS diff --git a/src/modules/module-suspend-on-idle.c b/src/modules/module-suspend-on-idle.c index cfb78792..2bc424b3 100644 --- a/src/modules/module-suspend-on-idle.c +++ b/src/modules/module-suspend-on-idle.c @@ -41,6 +41,7 @@ PA_MODULE_AUTHOR("Lennart Poettering"); PA_MODULE_DESCRIPTION("When a sink/source is idle for too long, suspend it"); PA_MODULE_VERSION(PACKAGE_VERSION); PA_MODULE_LOAD_ONCE(TRUE); +PA_MODULE_USAGE("timeout=<timeout>"); static const char* const valid_modargs[] = { "timeout", diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c index 176c2c00..af1bb955 100644 --- a/src/modules/module-tunnel.c +++ b/src/modules/module-tunnel.c @@ -379,7 +379,7 @@ static void command_stream_buffer_attr_changed(pa_pdispatch *pd, uint32_t comman /* Called from main context */ static void command_started(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { - struct userdata *u = userdata; + struct userdata *u = userdata; pa_assert(pd); pa_assert(t); @@ -393,7 +393,7 @@ static void command_started(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa #endif /* Called from IO thread context */ -static void check_smoother_status(struct userdata *u, pa_bool_t past) { +static void check_smoother_status(struct userdata *u, pa_bool_t past) { pa_usec_t x; pa_assert(u); @@ -973,8 +973,7 @@ static void server_info_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa pa_tagstruct_gets(t, &default_sink_name) < 0 || pa_tagstruct_gets(t, &default_source_name) < 0 || pa_tagstruct_getu32(t, &cookie) < 0 || - (u->version >= 15 && - pa_tagstruct_get_channel_map(t, &cm) < 0)) { + (u->version >= 15 && pa_tagstruct_get_channel_map(t, &cm) < 0)) { pa_log("Parse failure"); goto fail; @@ -2037,7 +2036,7 @@ fail: pa_xfree(dn); - return -1; + return -1; } void pa__done(pa_module*m) { diff --git a/src/modules/module-udev-detect.c b/src/modules/module-udev-detect.c index 1eaa84fd..63ad1952 100644 --- a/src/modules/module-udev-detect.c +++ b/src/modules/module-udev-detect.c @@ -442,8 +442,7 @@ static void process_device(struct userdata *u, struct udev_device *dev) { if (action && pa_streq(action, "remove")) remove_card(u, dev); - else if ((!action || pa_streq(action, "change")) && - udev_device_get_property_value(dev, "SOUND_INITIALIZED")) + else if ((!action || pa_streq(action, "change")) && udev_device_get_property_value(dev, "SOUND_INITIALIZED")) card_changed(u, dev); /* For an explanation why we don't look for 'add' events here diff --git a/src/modules/module-virtual-sink.c b/src/modules/module-virtual-sink.c index fac204d4..cc134900 100644 --- a/src/modules/module-virtual-sink.c +++ b/src/modules/module-virtual-sink.c @@ -57,6 +57,8 @@ PA_MODULE_USAGE( "rate=<sample rate> " "channels=<number of channels> " "channel_map=<channel map> " + "use_volume_sharing=<yes or no> " + "force_flat_volume=<yes or no> " )); #define MEMBLOCKQ_MAXLENGTH (16*1024*1024) @@ -81,6 +83,8 @@ static const char* const valid_modargs[] = { "rate", "channels", "channel_map", + "use_volume_sharing", + "force_flat_volume", NULL }; @@ -477,7 +481,9 @@ int pa__init(pa_module*m) { pa_sink *master=NULL; pa_sink_input_new_data sink_input_data; pa_sink_new_data sink_data; - pa_bool_t *use_default = NULL; + pa_bool_t use_volume_sharing = FALSE; + pa_bool_t force_flat_volume = FALSE; + pa_memchunk silence; pa_assert(m); @@ -501,6 +507,21 @@ int pa__init(pa_module*m) { goto fail; } + if (pa_modargs_get_value_boolean(ma, "use_volume_sharing", &use_volume_sharing) < 0) { + pa_log("use_volume_sharing= expects a boolean argument"); + goto fail; + } + + if (pa_modargs_get_value_boolean(ma, "force_flat_volume", &force_flat_volume) < 0) { + pa_log("force_flat_volume= expects a boolean argument"); + goto fail; + } + + if (use_volume_sharing && force_flat_volume) { + pa_log("Flat volume can't be forced when using volume sharing."); + goto fail; + } + u = pa_xnew0(struct userdata, 1); u->module = m; m->userdata = u; @@ -531,9 +552,9 @@ int pa__init(pa_module*m) { pa_proplist_setf(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Virtual Sink %s on %s", sink_data.name, z ? z : master->name); } - u->sink = pa_sink_new(m->core, &sink_data, - PA_SINK_HW_MUTE_CTRL|PA_SINK_HW_VOLUME_CTRL|PA_SINK_DECIBEL_VOLUME| - (master->flags & (PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY))); + u->sink = pa_sink_new(m->core, &sink_data, (master->flags & (PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY)) + | (use_volume_sharing ? PA_SINK_SHARE_VOLUME_WITH_MASTER : 0) + | (force_flat_volume ? PA_SINK_FLAT_VOLUME : 0)); pa_sink_new_data_done(&sink_data); if (!u->sink) { @@ -545,7 +566,7 @@ int pa__init(pa_module*m) { u->sink->set_state = sink_set_state_cb; u->sink->update_requested_latency = sink_update_requested_latency_cb; u->sink->request_rewind = sink_request_rewind_cb; - u->sink->set_volume = sink_set_volume_cb; + u->sink->set_volume = use_volume_sharing ? NULL : sink_set_volume_cb; u->sink->set_mute = sink_set_mute_cb; u->sink->userdata = u; @@ -556,7 +577,8 @@ int pa__init(pa_module*m) { sink_input_data.driver = __FILE__; sink_input_data.module = m; sink_input_data.sink = master; - pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Virtual Sink Stream"); + sink_input_data.origin_sink = u->sink; + pa_proplist_setf(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Virtual Sink Stream from %s", pa_proplist_gets(u->sink->proplist, PA_PROP_DEVICE_DESCRIPTION)); pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter"); pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss); pa_sink_input_new_data_set_channel_map(&sink_input_data, &map); @@ -579,32 +601,29 @@ int pa__init(pa_module*m) { u->sink_input->state_change = sink_input_state_change_cb; u->sink_input->may_move_to = sink_input_may_move_to_cb; u->sink_input->moving = sink_input_moving_cb; - u->sink_input->volume_changed = sink_input_volume_changed_cb; + u->sink_input->volume_changed = use_volume_sharing ? NULL : sink_input_volume_changed_cb; u->sink_input->mute_changed = sink_input_mute_changed_cb; u->sink_input->userdata = u; - /* (9) IF YOU REQUIRE A FIXED BLOCK SIZE MAKE SURE TO PASS A - * SILENCE MEMBLOCK AS LAST PARAMETER - * HERE. pa_sink_input_get_silence() IS USEFUL HERE. */ - u->memblockq = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, pa_frame_size(&ss), 1, 1, 0, NULL); + u->sink->input_to_master = u->sink_input; - /* (10) INITIALIZE ANYTHING ELSE YOU NEED HERE */ + pa_sink_input_get_silence(u->sink_input, &silence); + u->memblockq = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, pa_frame_size(&ss), 1, 1, 0, &silence); + pa_memblock_unref(silence.memblock); + + /* (9) INITIALIZE ANYTHING ELSE YOU NEED HERE */ pa_sink_put(u->sink); pa_sink_input_put(u->sink_input); pa_modargs_free(ma); - pa_xfree(use_default); - return 0; - fail: +fail: if (ma) pa_modargs_free(ma); - pa_xfree(use_default); - pa__done(m); return -1; diff --git a/src/modules/module-virtual-source.c b/src/modules/module-virtual-source.c index fdf89b02..b8f2ab06 100644 --- a/src/modules/module-virtual-source.c +++ b/src/modules/module-virtual-source.c @@ -535,7 +535,6 @@ int pa__init(pa_module*m) { pa_source *master=NULL; pa_source_output_new_data source_output_data; pa_source_new_data source_data; - pa_bool_t *use_default = NULL; /* optional for uplink_sink */ pa_sink_new_data sink_data; @@ -629,10 +628,11 @@ int pa__init(pa_module*m) { source_output_data.driver = __FILE__; source_output_data.module = m; source_output_data.source = master; + source_output_data.destination_source = u->source; /* FIXME source_output_data.flags = PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND; */ - pa_proplist_sets(source_output_data.proplist, PA_PROP_MEDIA_NAME, "Virtual Source Stream"); + pa_proplist_setf(source_output_data.proplist, PA_PROP_MEDIA_NAME, "Virtual Source Stream of %s", pa_proplist_gets(u->source->proplist, PA_PROP_DEVICE_DESCRIPTION)); pa_proplist_sets(source_output_data.proplist, PA_PROP_MEDIA_ROLE, "filter"); pa_source_output_new_data_set_sample_spec(&source_output_data, &ss); pa_source_output_new_data_set_channel_map(&source_output_data, &map); @@ -654,6 +654,8 @@ int pa__init(pa_module*m) { u->source_output->moving = source_output_moving_cb; u->source_output->userdata = u; + u->source->output_from_master = u->source_output; + pa_source_put(u->source); pa_source_output_put(u->source_output); @@ -711,16 +713,12 @@ int pa__init(pa_module*m) { pa_modargs_free(ma); - pa_xfree(use_default); - return 0; - fail: +fail: if (ma) pa_modargs_free(ma); - pa_xfree(use_default); - pa__done(m); return -1; @@ -765,7 +763,7 @@ void pa__done(pa_module*m) { pa_memblockq_free(u->memblockq); if (u->sink_memblockq) - pa_memblockq_free(u->sink_memblockq); + pa_memblockq_free(u->sink_memblockq); pa_xfree(u); } diff --git a/src/modules/module-volume-restore.c b/src/modules/module-volume-restore.c index 6e484eae..a344c5eb 100644 --- a/src/modules/module-volume-restore.c +++ b/src/modules/module-volume-restore.c @@ -81,5 +81,5 @@ fail: if (ma) pa_modargs_free(ma); - return -1; + return -1; } diff --git a/src/modules/module-waveout.c b/src/modules/module-waveout.c index 6c969212..9f119c59 100644 --- a/src/modules/module-waveout.c +++ b/src/modules/module-waveout.c @@ -56,11 +56,11 @@ PA_MODULE_USAGE( "record=<enable source?> " "playback=<enable sink?> " "format=<sample format> " - "channels=<number of channels> " "rate=<sample rate> " + "channels=<number of channels> " + "channel_map=<channel map> " "fragments=<number of fragments> " - "fragment_size=<fragment size> " - "channel_map=<channel map>"); + "fragment_size=<fragment size>"); #define DEFAULT_SINK_NAME "wave_output" #define DEFAULT_SOURCE_NAME "wave_input" diff --git a/src/modules/oss/module-oss.c b/src/modules/oss/module-oss.c index c9dc4e7a..84dbbdaf 100644 --- a/src/modules/oss/module-oss.c +++ b/src/modules/oss/module-oss.c @@ -169,7 +169,7 @@ static void trigger(struct userdata *u, pa_bool_t quick) { if (u->fd < 0) return; - pa_log_debug("trigger"); + pa_log_debug("trigger"); if (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state)) enable_bits |= PCM_ENABLE_INPUT; diff --git a/src/modules/raop/base64.c b/src/modules/raop/base64.c index 5b061034..37e47628 100644 --- a/src/modules/raop/base64.c +++ b/src/modules/raop/base64.c @@ -38,8 +38,7 @@ static const char base64_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; -static int pos(char c) -{ +static int pos(char c) { if (c >= 'A' && c <= 'Z') return c - 'A' + 0; if (c >= 'a' && c <= 'z') return c - 'a' + 26; if (c >= '0' && c <= '9') return c - '0' + 52; @@ -48,8 +47,7 @@ static int pos(char c) return -1; } -int pa_base64_encode(const void *data, int size, char **str) -{ +int pa_base64_encode(const void *data, int size, char **str) { char *s, *p; int i; int c; @@ -84,8 +82,7 @@ int pa_base64_encode(const void *data, int size, char **str) #define DECODE_ERROR 0xffffffff -static unsigned int token_decode(const char *token) -{ +static unsigned int token_decode(const char *token) { int i; unsigned int val = 0; int marker = 0; @@ -109,8 +106,7 @@ static unsigned int token_decode(const char *token) return (marker << 24) | val; } -int pa_base64_decode(const char *str, void *data) -{ +int pa_base64_decode(const char *str, void *data) { const char *p; unsigned char *q; diff --git a/src/modules/raop/raop_client.c b/src/modules/raop/raop_client.c index 7dd96839..05c7b169 100644 --- a/src/modules/raop/raop_client.c +++ b/src/modules/raop/raop_client.c @@ -173,8 +173,7 @@ static int rsa_encrypt(uint8_t *text, int len, uint8_t *res) { return size; } -static int aes_encrypt(pa_raop_client* c, uint8_t *data, int size) -{ +static int aes_encrypt(pa_raop_client* c, uint8_t *data, int size) { uint8_t *buf; int i=0, j; @@ -193,8 +192,7 @@ static int aes_encrypt(pa_raop_client* c, uint8_t *data, int size) return i; } -static inline void rtrimchar(char *str, char rc) -{ +static inline void rtrimchar(char *str, char rc) { char *sp = str + strlen(str) - 1; while (sp >= str && *sp == rc) { *sp = '\0'; @@ -231,8 +229,7 @@ static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata c->callback(c->fd, c->userdata); } -static void rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_headerlist* headers, void *userdata) -{ +static void rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_headerlist* headers, void *userdata) { pa_raop_client* c = userdata; pa_assert(c); pa_assert(rtsp); @@ -367,8 +364,7 @@ static void rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_headerlist* he } } -pa_raop_client* pa_raop_client_new(pa_core *core, const char* host) -{ +pa_raop_client* pa_raop_client_new(pa_core *core, const char* host) { pa_raop_client* c = pa_xnew0(pa_raop_client, 1); pa_assert(core); @@ -386,8 +382,7 @@ pa_raop_client* pa_raop_client_new(pa_core *core, const char* host) } -void pa_raop_client_free(pa_raop_client* c) -{ +void pa_raop_client_free(pa_raop_client* c) { pa_assert(c); if (c->rtsp) @@ -397,8 +392,7 @@ void pa_raop_client_free(pa_raop_client* c) } -int pa_raop_connect(pa_raop_client* c) -{ +int pa_raop_connect(pa_raop_client* c) { char *sci; struct { uint32_t a; @@ -432,8 +426,7 @@ int pa_raop_connect(pa_raop_client* c) } -int pa_raop_flush(pa_raop_client* c) -{ +int pa_raop_flush(pa_raop_client* c) { pa_assert(c); pa_rtsp_flush(c->rtsp, c->seq, c->rtptime); @@ -441,8 +434,7 @@ int pa_raop_flush(pa_raop_client* c) } -int pa_raop_client_set_volume(pa_raop_client* c, pa_volume_t volume) -{ +int pa_raop_client_set_volume(pa_raop_client* c, pa_volume_t volume) { int rv; double db; char *param; @@ -464,8 +456,7 @@ int pa_raop_client_set_volume(pa_raop_client* c, pa_volume_t volume) } -int pa_raop_client_encode_sample(pa_raop_client* c, pa_memchunk* raw, pa_memchunk* encoded) -{ +int pa_raop_client_encode_sample(pa_raop_client* c, pa_memchunk* raw, pa_memchunk* encoded) { uint16_t len; size_t bufmax; uint8_t *bp, bpos; @@ -547,16 +538,14 @@ int pa_raop_client_encode_sample(pa_raop_client* c, pa_memchunk* raw, pa_memchun } -void pa_raop_client_set_callback(pa_raop_client* c, pa_raop_client_cb_t callback, void *userdata) -{ +void pa_raop_client_set_callback(pa_raop_client* c, pa_raop_client_cb_t callback, void *userdata) { pa_assert(c); c->callback = callback; c->userdata = userdata; } -void pa_raop_client_set_closed_callback(pa_raop_client* c, pa_raop_client_closed_cb_t callback, void *userdata) -{ +void pa_raop_client_set_closed_callback(pa_raop_client* c, pa_raop_client_closed_cb_t callback, void *userdata) { pa_assert(c); c->closed_callback = callback; diff --git a/src/modules/rtp/rtp.h b/src/modules/rtp/rtp.h index b197e82f..e975e750 100644 --- a/src/modules/rtp/rtp.h +++ b/src/modules/rtp/rtp.h @@ -40,6 +40,9 @@ typedef struct pa_rtp_context { } pa_rtp_context; pa_rtp_context* pa_rtp_context_init_send(pa_rtp_context *c, int fd, uint32_t ssrc, uint8_t payload, size_t frame_size); + +/* If the memblockq doesn't have a silence memchunk set, then the caller must + * guarantee that the current read index doesn't point to a hole. */ int pa_rtp_send(pa_rtp_context *c, size_t size, pa_memblockq *q); pa_rtp_context* pa_rtp_context_init_recv(pa_rtp_context *c, int fd, size_t frame_size); diff --git a/src/modules/rtp/rtsp_client.c b/src/modules/rtp/rtsp_client.c index fd3b1de3..8a5a1d75 100644 --- a/src/modules/rtp/rtsp_client.c +++ b/src/modules/rtp/rtsp_client.c @@ -372,8 +372,7 @@ void pa_rtsp_set_url(pa_rtsp_client* c, const char* url) { c->url = pa_xstrdup(url); } -void pa_rtsp_add_header(pa_rtsp_client *c, const char* key, const char* value) -{ +void pa_rtsp_add_header(pa_rtsp_client *c, const char* key, const char* value) { pa_assert(c); pa_assert(key); pa_assert(value); @@ -381,8 +380,7 @@ void pa_rtsp_add_header(pa_rtsp_client *c, const char* key, const char* value) pa_headerlist_puts(c->headers, key, value); } -void pa_rtsp_remove_header(pa_rtsp_client *c, const char* key) -{ +void pa_rtsp_remove_header(pa_rtsp_client *c, const char* key) { pa_assert(c); pa_assert(key); diff --git a/src/modules/udev-util.c b/src/modules/udev-util.c index 52df1039..356f7736 100644 --- a/src/modules/udev-util.c +++ b/src/modules/udev-util.c @@ -74,7 +74,7 @@ static int dehex(char x) { static void proplist_sets_unescape(pa_proplist *p, const char *prop, const char *s) { const char *f; char *t, *r; - int c; + int c = 0; enum { TEXT, diff --git a/src/pulse/browser.c b/src/pulse/browser.c index 4cf5d0c3..d0592284 100644 --- a/src/pulse/browser.c +++ b/src/pulse/browser.c @@ -20,7 +20,7 @@ ***/ #ifdef HAVE_CONFIG_H -#include "config.h" +#include <config.h> #endif #include <string.h> diff --git a/src/pulse/context.c b/src/pulse/context.c index 8f632b5d..1480af53 100644 --- a/src/pulse/context.c +++ b/src/pulse/context.c @@ -53,6 +53,11 @@ #include <pulse/i18n.h> #include <pulse/mainloop.h> #include <pulse/timeval.h> +#include <pulse/fork-detect.h> +#include <pulse/client-conf.h> +#ifdef HAVE_X11 +#include <pulse/client-conf-x11.h> +#endif #include <pulsecore/core-error.h> #include <pulsecore/native-common.h> @@ -71,14 +76,6 @@ #include <pulsecore/proplist-util.h> #include "internal.h" - -#include "client-conf.h" -#include "fork-detect.h" - -#ifdef HAVE_X11 -#include "client-conf-x11.h" -#endif - #include "context.h" void pa_command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); diff --git a/src/pulse/def.h b/src/pulse/def.h index ac4ae538..a3b86223 100644 --- a/src/pulse/def.h +++ b/src/pulse/def.h @@ -748,6 +748,16 @@ typedef enum pa_sink_flags { /**< The HW volume changes are syncronized with SW volume. * \since 1.0 */ +/** \cond fulldocs */ + /* PRIVATE: Server-side values -- do not try to use these at client-side. + * The server will filter out these flags anyway, so you should never see + * these flags in sinks. */ + + PA_SINK_SHARE_VOLUME_WITH_MASTER = 0x0400U, + /**< This sink shares the volume with the master sink (used by some filter + * sinks). */ +/** \endcond */ + } pa_sink_flags_t; /** \cond fulldocs */ @@ -761,6 +771,7 @@ typedef enum pa_sink_flags { #define PA_SINK_DYNAMIC_LATENCY PA_SINK_DYNAMIC_LATENCY #define PA_SINK_PASSTHROUGH PA_SINK_PASSTHROUGH #define PA_SINK_SYNC_VOLUME PA_SINK_SYNC_VOLUME +#define PA_SINK_SHARE_VOLUME_WITH_MASTER PA_SINK_SHARE_VOLUME_WITH_MASTER /** \endcond */ diff --git a/src/pulse/ext-device-manager.c b/src/pulse/ext-device-manager.c index 57cb57c8..7b78c24e 100644 --- a/src/pulse/ext-device-manager.c +++ b/src/pulse/ext-device-manager.c @@ -27,14 +27,13 @@ #include <pulse/context.h> #include <pulse/gccmacro.h> #include <pulse/xmalloc.h> +#include <pulse/fork-detect.h> +#include <pulse/operation.h> #include <pulsecore/macro.h> #include <pulsecore/pstream-util.h> #include "internal.h" -#include "operation.h" -#include "fork-detect.h" - #include "ext-device-manager.h" enum { diff --git a/src/pulse/ext-stream-restore.c b/src/pulse/ext-stream-restore.c index 10e9fd5d..7bc1a612 100644 --- a/src/pulse/ext-stream-restore.c +++ b/src/pulse/ext-stream-restore.c @@ -25,14 +25,13 @@ #include <pulse/context.h> #include <pulse/gccmacro.h> +#include <pulse/fork-detect.h> +#include <pulse/operation.h> #include <pulsecore/macro.h> #include <pulsecore/pstream-util.h> #include "internal.h" -#include "operation.h" -#include "fork-detect.h" - #include "ext-stream-restore.h" enum { diff --git a/src/pulse/glib-mainloop.c b/src/pulse/glib-mainloop.c index 6afb7a2d..2e5f2f92 100644 --- a/src/pulse/glib-mainloop.c +++ b/src/pulse/glib-mainloop.c @@ -34,7 +34,7 @@ #include <glib.h> #include "glib-mainloop.h" -struct pa_io_event { +struct pa_io_event { pa_glib_mainloop *mainloop; int dead; @@ -336,7 +336,7 @@ static void glib_time_restart(pa_time_event*e, const struct timeval *tv) { e->mainloop->cached_next_time_event = e; } else if (e->mainloop->cached_next_time_event == e) e->mainloop->cached_next_time_event = NULL; - } +} static void glib_time_free(pa_time_event *e) { g_assert(e); diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c index 2a817881..ec27b928 100644 --- a/src/pulse/introspect.c +++ b/src/pulse/introspect.c @@ -29,14 +29,13 @@ #include <pulse/context.h> #include <pulse/gccmacro.h> #include <pulse/xmalloc.h> +#include <pulse/fork-detect.h> #include <pulsecore/macro.h> #include <pulsecore/core-util.h> #include <pulsecore/pstream-util.h> #include "internal.h" -#include "fork-detect.h" - #include "introspect.h" /*** Statistics ***/ @@ -996,7 +995,7 @@ static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t comm while (!pa_tagstruct_eof(t)) { pa_sink_input_info i; - pa_bool_t mute = FALSE, corked = FALSE; + pa_bool_t mute = FALSE, corked = FALSE, has_volume = FALSE, read_only_volume = FALSE; pa_zero(i); i.proplist = pa_proplist_new(); @@ -1015,7 +1014,9 @@ static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t comm pa_tagstruct_gets(t, &i.driver) < 0 || (o->context->version >= 11 && pa_tagstruct_get_boolean(t, &mute) < 0) || (o->context->version >= 13 && pa_tagstruct_get_proplist(t, i.proplist) < 0) || - (o->context->version >= 19 && pa_tagstruct_get_boolean(t, &corked) < 0)) { + (o->context->version >= 19 && pa_tagstruct_get_boolean(t, &corked) < 0) || + (o->context->version >= 20 && (pa_tagstruct_get_boolean(t, &has_volume) < 0 || + pa_tagstruct_get_boolean(t, &read_only_volume) < 0))) { pa_context_fail(o->context, PA_ERR_PROTOCOL); pa_proplist_free(i.proplist); @@ -1024,6 +1025,8 @@ static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t comm i.mute = (int) mute; i.corked = (int) corked; + i.has_volume = (int) has_volume; + i.read_only_volume = (int) read_only_volume; if (o->callback) { pa_sink_input_info_cb_t cb = (pa_sink_input_info_cb_t) o->callback; diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h index bc50611e..1cadee52 100644 --- a/src/pulse/introspect.h +++ b/src/pulse/introspect.h @@ -503,6 +503,8 @@ typedef struct pa_sink_input_info { int mute; /**< Stream muted \since 0.9.7 */ pa_proplist *proplist; /**< Property list \since 0.9.11 */ int corked; /**< Stream corked \since 1.0 */ + int has_volume; /**< Stream has volume. If not set, then the meaning of this struct's volume member is unspecified. \since 1.0 */ + int read_only_volume; /**< Stream volume can only be read. Although volume control is disabled, the stream volume is still not necessarily constant. \since 1.0 */ } pa_sink_input_info; /** Callback prototype for pa_context_get_sink_input_info() and friends*/ diff --git a/src/pulse/mainloop-api.h b/src/pulse/mainloop-api.h index 0ce2219b..212ff3cc 100644 --- a/src/pulse/mainloop-api.h +++ b/src/pulse/mainloop-api.h @@ -80,7 +80,7 @@ typedef void (*pa_defer_event_cb_t)(pa_mainloop_api*a, pa_defer_event* e, void * typedef void (*pa_defer_event_destroy_cb_t)(pa_mainloop_api*a, pa_defer_event *e, void *userdata); /** An abstract mainloop API vtable */ -struct pa_mainloop_api { +struct pa_mainloop_api { /** A pointer to some private, arbitrary data of the main loop implementation */ void *userdata; diff --git a/src/pulse/scache.c b/src/pulse/scache.c index cb8d7c59..6ed80a68 100644 --- a/src/pulse/scache.c +++ b/src/pulse/scache.c @@ -28,14 +28,14 @@ #include <string.h> #include <pulse/utf8.h> -#include <pulse/scache.h> +#include <pulse/fork-detect.h> #include <pulsecore/pstream-util.h> #include <pulsecore/macro.h> #include <pulsecore/proplist-util.h> -#include "fork-detect.h" #include "internal.h" +#include "scache.h" int pa_stream_connect_upload(pa_stream *s, size_t length) { pa_tagstruct *t; diff --git a/src/pulse/stream.c b/src/pulse/stream.c index addc36ae..aac18a31 100644 --- a/src/pulse/stream.c +++ b/src/pulse/stream.c @@ -29,10 +29,10 @@ #include <string.h> #include <pulse/def.h> -#include <pulse/stream.h> #include <pulse/timeval.h> #include <pulse/rtclock.h> #include <pulse/xmalloc.h> +#include <pulse/fork-detect.h> #include <pulsecore/pstream-util.h> #include <pulsecore/log.h> @@ -41,8 +41,8 @@ #include <pulsecore/core-rtclock.h> #include <pulsecore/core-util.h> -#include "fork-detect.h" #include "internal.h" +#include "stream.h" #define AUTO_TIMING_INTERVAL_START_USEC (10*PA_USEC_PER_MSEC) #define AUTO_TIMING_INTERVAL_END_USEC (1500*PA_USEC_PER_MSEC) @@ -790,7 +790,7 @@ void pa_command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, uint32 s->underflow_callback(s, s->underflow_userdata); } - finish: +finish: pa_context_unref(c); } @@ -925,7 +925,7 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, if (pa_tagstruct_getu32(t, &s->channel) < 0 || s->channel == PA_INVALID_INDEX || - ((s->direction != PA_STREAM_UPLOAD) && (pa_tagstruct_getu32(t, &s->stream_index) < 0 || s->stream_index == PA_INVALID_INDEX)) || + ((s->direction != PA_STREAM_UPLOAD) && (pa_tagstruct_getu32(t, &s->stream_index) < 0 || s->stream_index == PA_INVALID_INDEX)) || ((s->direction != PA_STREAM_RECORD) && pa_tagstruct_getu32(t, &requested_bytes) < 0)) { pa_context_fail(s->context, PA_ERR_PROTOCOL); goto finish; diff --git a/src/pulse/subscribe.c b/src/pulse/subscribe.c index 203bc928..52d0af35 100644 --- a/src/pulse/subscribe.c +++ b/src/pulse/subscribe.c @@ -31,7 +31,6 @@ #include <pulsecore/pstream-util.h> #include "internal.h" - #include "subscribe.h" void pa_command_subscribe_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { diff --git a/src/pulse/volume.c b/src/pulse/volume.c index f74d7202..134c007d 100644 --- a/src/pulse/volume.c +++ b/src/pulse/volume.c @@ -263,7 +263,7 @@ pa_volume_t pa_sw_volume_from_linear(double v) { * same volume value! That's why we need the lround() below! */ - return (pa_volume_t) PA_CLAMP_VOLUME(lround(cbrt(v) * PA_VOLUME_NORM)); + return (pa_volume_t) PA_CLAMP_VOLUME((uint64_t) lround(cbrt(v) * PA_VOLUME_NORM)); } double pa_sw_volume_to_linear(pa_volume_t v) { @@ -375,8 +375,7 @@ char *pa_sw_volume_snprint_dB(char *s, size_t l, pa_volume_t v) { } f = pa_sw_volume_to_dB(v); - pa_snprintf(s, l, "%0.2f dB", - isinf(f) < 0 || f <= PA_DECIBEL_MININFTY ? -INFINITY : f); + pa_snprintf(s, l, "%0.2f dB", isinf(f) < 0 || f <= PA_DECIBEL_MININFTY ? -INFINITY : f); return s; } @@ -657,9 +656,9 @@ pa_cvolume* pa_cvolume_set_balance(pa_cvolume *v, const pa_channel_map *map, flo m = PA_MAX(left, right); if (new_balance <= 0) { - nright = (new_balance + 1.0f) * m; + nright = (new_balance + 1.0f) * m; nleft = m; - } else { + } else { nleft = (1.0f - new_balance) * m; nright = m; } @@ -720,7 +719,7 @@ pa_cvolume* pa_cvolume_scale_mask(pa_cvolume *v, pa_volume_t max, pa_channel_map return pa_cvolume_set(v, v->channels, max); for (c = 0; c < v->channels; c++) - v->values[c] = (pa_volume_t) PA_CLAMP_VOLUME(((uint64_t) v->values[c] * (uint64_t) max) / (uint64_t) t); + v->values[c] = (pa_volume_t) PA_CLAMP_VOLUME(((uint64_t) v->values[c] * (uint64_t) max) / (uint64_t) t); return v; } @@ -798,9 +797,9 @@ pa_cvolume* pa_cvolume_set_fade(pa_cvolume *v, const pa_channel_map *map, float m = PA_MAX(front, rear); if (new_fade <= 0) { - nfront = (new_fade + 1.0f) * m; + nfront = (new_fade + 1.0f) * m; nrear = m; - } else { + } else { nrear = (1.0f - new_fade) * m; nfront = m; } diff --git a/src/pulse/volume.h b/src/pulse/volume.h index 91eef749..abf930ea 100644 --- a/src/pulse/volume.h +++ b/src/pulse/volume.h @@ -115,6 +115,13 @@ typedef uint32_t pa_volume_t; /** Maximum valid volume we can store. \since 0.9.15 */ #define PA_VOLUME_MAX ((pa_volume_t) UINT32_MAX/2) +/** Recommended maximum volume to show in user facing UIs. + * Note: UIs should deal gracefully with volumes greater than this value + * and not cause feedback loops etc. - i.e. if the volume is more than + * this, the UI should not limit it and push the limited value back to + * the server. \since 0.9.23 */ +#define PA_VOLUME_UI_MAX (pa_sw_volume_from_dB(+11.0)) + /** Special 'invalid' volume. \since 0.9.16 */ #define PA_VOLUME_INVALID ((pa_volume_t) UINT32_MAX) 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; diff --git a/src/tests/cpulimit-test.c b/src/tests/cpulimit-test.c index 9d0f4eef..5b11bc44 100644 --- a/src/tests/cpulimit-test.c +++ b/src/tests/cpulimit-test.c @@ -22,7 +22,7 @@ #endif #include <assert.h> -#include <sys/time.h> +#include <time.h> #include <stdlib.h> #include <stdio.h> #include <signal.h> @@ -34,7 +34,7 @@ #include <pulse/mainloop-signal.h> #endif -#include "../daemon/cpulimit.h" +#include <daemon/cpulimit.h> /* A simple example for testing the cpulimit subsystem */ diff --git a/src/tests/envelope-test.c b/src/tests/envelope-test.c deleted file mode 100644 index 9382040b..00000000 --- a/src/tests/envelope-test.c +++ /dev/null @@ -1,243 +0,0 @@ -/*** - This file is part of PulseAudio. - - PulseAudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2.1 of the License, - or (at your option) any later version. - - PulseAudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PulseAudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <stdio.h> -#include <stdlib.h> - -#include <pulse/sample.h> -#include <pulse/volume.h> -#include <pulse/timeval.h> - -#include <pulsecore/envelope.h> -#include <pulsecore/macro.h> -#include <pulsecore/endianmacros.h> -#include <pulsecore/memblock.h> -#include <pulsecore/sample-util.h> - -const pa_envelope_def ramp_down = { - .n_points = 2, - .points_x = { 100*PA_USEC_PER_MSEC, 300*PA_USEC_PER_MSEC }, - .points_y = { - .f = { 1.0f, 0.2f }, - .i = { 0x10000, 0x10000/5 } - } -}; - -const pa_envelope_def ramp_up = { - .n_points = 2, - .points_x = { 100*PA_USEC_PER_MSEC, 300*PA_USEC_PER_MSEC }, - .points_y = { - .f = { 0.2f, 1.0f }, - .i = { 0x10000/5, 0x10000 } - } -}; - -const pa_envelope_def ramp_down2 = { - .n_points = 2, - .points_x = { 50*PA_USEC_PER_MSEC, 900*PA_USEC_PER_MSEC }, - .points_y = { - .f = { 0.8f, 0.7f }, - .i = { 0x10000*4/5, 0x10000*7/10 } - } -}; - -const pa_envelope_def ramp_up2 = { - .n_points = 2, - .points_x = { 50*PA_USEC_PER_MSEC, 900*PA_USEC_PER_MSEC }, - .points_y = { - .f = { 0.7f, 0.9f }, - .i = { 0x10000*7/10, 0x10000*9/10 } - } -}; - -static void dump_block(const pa_sample_spec *ss, const pa_memchunk *chunk) { - void *d; - unsigned i; - - static unsigned j = 0; - - d = pa_memblock_acquire(chunk->memblock); - - switch (ss->format) { - - case PA_SAMPLE_U8: - case PA_SAMPLE_ULAW: - case PA_SAMPLE_ALAW: { - uint8_t *u = d; - - for (i = 0; i < chunk->length / pa_frame_size(ss); i++) - printf("0x%02x ", *(u++)); - - break; - } - - case PA_SAMPLE_S16NE: - case PA_SAMPLE_S16RE: { - int16_t *u = d; - - for (i = 0; i < chunk->length / pa_frame_size(ss); i++) - printf("%i\t%i\n", j++, *(u++)); - - break; - } - - case PA_SAMPLE_S32NE: - case PA_SAMPLE_S32RE: { - int32_t *u = d; - - for (i = 0; i < chunk->length / pa_frame_size(ss); i++) - printf("%i\t%i\n", j++, *(u++)); - - break; - } - - case PA_SAMPLE_FLOAT32NE: - case PA_SAMPLE_FLOAT32RE: { - float *u = d; - - for (i = 0; i < chunk->length / pa_frame_size(ss); i++) { - printf("%i\t%1.3g\n", j++, PA_MAYBE_FLOAT32_SWAP(ss->format == PA_SAMPLE_FLOAT32RE, *u)); - u++; - } - - break; - } - - default: - pa_assert_not_reached(); - } - - printf("\n"); - - pa_memblock_release(chunk->memblock); -} - -static pa_memblock * generate_block(pa_mempool *pool, const pa_sample_spec *ss) { - pa_memblock *block; - void *d; - unsigned n_samples; - - block = pa_memblock_new(pool, pa_bytes_per_second(ss)); - n_samples = (unsigned) (pa_memblock_get_length(block) / pa_sample_size(ss)); - - d = pa_memblock_acquire(block); - - switch (ss->format) { - - case PA_SAMPLE_S16NE: - case PA_SAMPLE_S16RE: { - int16_t *i; - - for (i = d; n_samples > 0; n_samples--, i++) - *i = 0x7FFF; - - break; - } - - case PA_SAMPLE_S32NE: - case PA_SAMPLE_S32RE: { - int32_t *i; - - for (i = d; n_samples > 0; n_samples--, i++) - *i = 0x7FFFFFFF; - - break; - } - - case PA_SAMPLE_FLOAT32RE: - case PA_SAMPLE_FLOAT32NE: { - float *f; - - for (f = d; n_samples > 0; n_samples--, f++) - *f = PA_MAYBE_FLOAT32_SWAP(ss->format == PA_SAMPLE_FLOAT32RE, 1.0f); - - break; - } - - default: - pa_assert_not_reached(); - } - - pa_memblock_release(block); - return block; -} - -int main(int argc, char *argv[]) { - pa_mempool *pool; - pa_memblock *block; - pa_memchunk chunk; - pa_envelope *envelope; - pa_envelope_item *item1, *item2; - - const pa_sample_spec ss = { - .format = PA_SAMPLE_S16NE, - .channels = 1, - .rate = 200 - }; - - const pa_cvolume v = { - .channels = 1, - .values = { PA_VOLUME_NORM, PA_VOLUME_NORM/2 } - }; - - pa_log_set_level(PA_LOG_DEBUG); - - pa_assert_se(pool = pa_mempool_new(FALSE, 0)); - pa_assert_se(envelope = pa_envelope_new(&ss)); - - block = generate_block(pool, &ss); - - chunk.memblock = pa_memblock_ref(block); - chunk.length = pa_memblock_get_length(block); - chunk.index = 0; - - pa_volume_memchunk(&chunk, &ss, &v); - - item1 = pa_envelope_add(envelope, &ramp_down); - item2 = pa_envelope_add(envelope, &ramp_down2); - pa_envelope_apply(envelope, &chunk); - dump_block(&ss, &chunk); - - pa_memblock_unref(chunk.memblock); - - chunk.memblock = pa_memblock_ref(block); - chunk.length = pa_memblock_get_length(block); - chunk.index = 0; - - item1 = pa_envelope_replace(envelope, item1, &ramp_up); - item2 = pa_envelope_replace(envelope, item2, &ramp_up2); - pa_envelope_apply(envelope, &chunk); - dump_block(&ss, &chunk); - - pa_memblock_unref(chunk.memblock); - - pa_envelope_remove(envelope, item1); - pa_envelope_remove(envelope, item2); - pa_envelope_free(envelope); - - pa_memblock_unref(block); - - pa_mempool_free(pool); - - return 0; -} diff --git a/src/tests/mainloop-test.c b/src/tests/mainloop-test.c index 3ec6d115..cd54bcfe 100644 --- a/src/tests/mainloop-test.c +++ b/src/tests/mainloop-test.c @@ -48,7 +48,7 @@ static pa_defer_event *de; static void iocb(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) { unsigned char c; - (void) read(fd, &c, sizeof(c)); + pa_assert_se(read(fd, &c, sizeof(c)) >= 0); fprintf(stderr, "IO EVENT: %c\n", c < 32 ? '.' : c); a->defer_enable(de, 1); } diff --git a/src/tests/memblockq-test.c b/src/tests/memblockq-test.c index e401bb62..b6c60039 100644 --- a/src/tests/memblockq-test.c +++ b/src/tests/memblockq-test.c @@ -81,7 +81,7 @@ int main(int argc, char *argv[]) { p = pa_mempool_new(FALSE, 0); - silence.memblock = pa_memblock_new_fixed(p, (char*) "__", 2, 1); + silence.memblock = pa_memblock_new_fixed(p, (char*) "__", 2, 1); assert(silence.memblock); silence.index = 0; silence.length = pa_memblock_get_length(silence.memblock); diff --git a/src/tests/mix-test.c b/src/tests/mix-test.c index 457c4acd..55844e7f 100644 --- a/src/tests/mix-test.c +++ b/src/tests/mix-test.c @@ -32,12 +32,6 @@ #include <pulsecore/memblock.h> #include <pulsecore/sample-util.h> -static float swap_float(float a) { - uint32_t *b = (uint32_t*) &a; - *b = PA_UINT32_SWAP(*b); - return a; -} - static void dump_block(const pa_sample_spec *ss, const pa_memchunk *chunk) { void *d; unsigned i; @@ -96,7 +90,7 @@ static void dump_block(const pa_sample_spec *ss, const pa_memchunk *chunk) { float *u = d; for (i = 0; i < chunk->length / pa_frame_size(ss); i++) { - printf("%1.5f ", ss->format == PA_SAMPLE_FLOAT32NE ? *u : swap_float(*u)); + printf("%1.5f ", ss->format == PA_SAMPLE_FLOAT32NE ? *u : PA_FLOAT32_SWAP(*u)); u++; } @@ -188,7 +182,7 @@ static pa_memblock* generate_block(pa_mempool *pool, const pa_sample_spec *ss) { if (ss->format == PA_SAMPLE_FLOAT32RE) { for (i = 0; i < 10; i++) - u[i] = swap_float(float_samples[i]); + u[i] = PA_FLOAT32_SWAP(float_samples[i]); } else memcpy(d, float_samples, sizeof(float_samples)); diff --git a/src/tests/rtstutter.c b/src/tests/rtstutter.c index 9ef835c3..402a8c09 100644 --- a/src/tests/rtstutter.c +++ b/src/tests/rtstutter.c @@ -106,7 +106,7 @@ int main(int argc, char*argv[]) { pa_assert(msec_upper > 0); pa_assert(msec_upper >= msec_lower); - pa_log_notice("Creating random latencies in the range of %ims to %ims.", msec_lower, msec_upper); + pa_log_notice("Creating random latencies in the range of %ims to %ims.", msec_lower, msec_upper); for (n = 1; n < pa_ncpus(); n++) { pa_assert_se(pa_thread_new("rtstutter", work, PA_UINT_TO_PTR(n))); diff --git a/src/tests/usergroup-test.c b/src/tests/usergroup-test.c index a48b016d..3948e0f8 100644 --- a/src/tests/usergroup-test.c +++ b/src/tests/usergroup-test.c @@ -47,28 +47,24 @@ static int compare_group(const struct group *a, const struct group *b) { char **amem, **bmem; if (strcmp(a->gr_name, b->gr_name)) { - fprintf(stderr, "Group name mismatch: [%s] [%s]\n", - a->gr_name, b->gr_name); + fprintf(stderr, "Group name mismatch: [%s] [%s]\n", a->gr_name, b->gr_name); return 1; } if (strcmp(a->gr_passwd, b->gr_passwd)) { - fprintf(stderr, "Group password mismatch: [%s] [%s]\n", - a->gr_passwd, b->gr_passwd); + fprintf(stderr, "Group password mismatch: [%s] [%s]\n", a->gr_passwd, b->gr_passwd); return 1; } if (a->gr_gid != b->gr_gid) { - fprintf(stderr, "Gid mismatch: [%lu] [%lu]\n", - (unsigned long) a->gr_gid, (unsigned long) b->gr_gid); + fprintf(stderr, "Gid mismatch: [%lu] [%lu]\n", (unsigned long) a->gr_gid, (unsigned long) b->gr_gid); return 1; } /* XXX: Assuming the group ordering is identical. */ for (amem = a->gr_mem, bmem = b->gr_mem; *amem && *bmem; ++amem, ++bmem) { if (strcmp(*amem, *bmem)) { - fprintf(stderr, "Group member mismatch: [%s] [%s]\n", - *amem, *bmem); + fprintf(stderr, "Group member mismatch: [%s] [%s]\n", *amem, *bmem); return 1; } } @@ -93,14 +89,12 @@ static int compare_passwd(const struct passwd *a, const struct passwd *b) { } if (a->pw_uid != b->pw_uid) { - fprintf(stderr, "pw_uid mismatch: [%lu] [%lu]\n", - (unsigned long) a->pw_uid, (unsigned long) b->pw_uid); + fprintf(stderr, "pw_uid mismatch: [%lu] [%lu]\n", (unsigned long) a->pw_uid, (unsigned long) b->pw_uid); return 1; } if (a->pw_gid != b->pw_gid) { - fprintf(stderr, "pw_gid mismatch: [%lu] [%lu]\n", - (unsigned long) a->pw_gid, (unsigned long) b->pw_gid); + fprintf(stderr, "pw_gid mismatch: [%lu] [%lu]\n", (unsigned long) a->pw_gid, (unsigned long) b->pw_gid); return 1; } diff --git a/src/utils/pacat.c b/src/utils/pacat.c index 79936fd7..749593ea 100644 --- a/src/utils/pacat.c +++ b/src/utils/pacat.c @@ -105,6 +105,7 @@ static void context_drain_complete(pa_context*c, void *userdata) { /* Stream draining complete */ static void stream_drain_complete(pa_stream*s, int success, void *userdata) { + pa_operation *o = NULL; if (!success) { pa_log(_("Failed to drain stream: %s"), pa_strerror(pa_context_errno(context))); @@ -118,9 +119,10 @@ static void stream_drain_complete(pa_stream*s, int success, void *userdata) { pa_stream_unref(stream); stream = NULL; - if (!pa_context_drain(context, context_drain_complete, NULL)) + if (!(o = pa_context_drain(context, context_drain_complete, NULL))) pa_context_disconnect(context); else { + pa_operation_unref(o); if (verbose) pa_log(_("Draining connection to server.")); } @@ -604,6 +606,7 @@ static void stream_update_timing_callback(pa_stream *s, int success, void *userd fprintf(stderr, " \r"); } +#ifdef SIGUSR1 /* Someone requested that the latency is shown */ static void sigusr1_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) { @@ -612,6 +615,7 @@ static void sigusr1_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int s pa_operation_unref(pa_stream_update_timing_info(stream, stream_update_timing_callback, NULL)); } +#endif static void time_event_callback(pa_mainloop_api *m, pa_time_event *e, const struct timeval *t, void *userdata) { if (stream && pa_stream_get_state(stream) == PA_STREAM_READY) { diff --git a/src/utils/pacmd.c b/src/utils/pacmd.c index 143a948d..4d07c4c2 100644 --- a/src/utils/pacmd.c +++ b/src/utils/pacmd.c @@ -44,8 +44,7 @@ #include <pulsecore/pid.h> int main(int argc, char*argv[]) { - - pid_t pid ; + pid_t pid; int fd = -1; int ret = 1, i; struct sockaddr_un sa; diff --git a/src/utils/pactl.c b/src/utils/pactl.c index 98c4d455..ad5c0b81 100644 --- a/src/utils/pactl.c +++ b/src/utils/pactl.c @@ -723,7 +723,7 @@ static void stream_write_callback(pa_stream *s, size_t length, void *userdata) { sample_length -= length; - if (sample_length <= 0) { + if (sample_length <= 0) { pa_stream_set_write_callback(sample_stream, NULL, NULL); pa_stream_finish_upload(sample_stream); } diff --git a/src/utils/padsp.c b/src/utils/padsp.c index ec0c46d9..ab9d18a3 100644 --- a/src/utils/padsp.c +++ b/src/utils/padsp.c @@ -282,7 +282,7 @@ static void debug(int level, const char *format, ...) PA_GCC_PRINTF_ATTR(2,3); #define DEBUG_LEVEL_ALWAYS 0 #define DEBUG_LEVEL_NORMAL 1 -#define DEBUG_LEVEL_VERBOSE 2 +#define DEBUG_LEVEL_VERBOSE 2 static void debug(int level, const char *format, ...) { va_list ap; @@ -1596,7 +1596,7 @@ static int mixer_ioctl(fd_info *i, unsigned long request, void*argp, int *_errno *(int*) argp = ((v->values[0]*100/PA_VOLUME_NORM)) | - ((v->values[v->channels > 1 ? 1 : 0]*100/PA_VOLUME_NORM) << 8); + ((v->values[v->channels > 1 ? 1 : 0]*100/PA_VOLUME_NORM) << 8); pa_threaded_mainloop_unlock(i->mainloop); @@ -2077,7 +2077,7 @@ static int dsp_ioctl(fd_info *i, unsigned long request, void*argp, int *_errno) case SNDCTL_DSP_GETCAPS: debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_CAPS\n"); - *(int*) argp = DSP_CAP_DUPLEX | DSP_CAP_TRIGGER + *(int*) argp = DSP_CAP_DUPLEX | DSP_CAP_TRIGGER #ifdef DSP_CAP_MULTI | DSP_CAP_MULTI #endif diff --git a/src/utils/pax11publish.c b/src/utils/pax11publish.c index e155888c..6600363b 100644 --- a/src/utils/pax11publish.c +++ b/src/utils/pax11publish.c @@ -33,6 +33,7 @@ #include <pulse/util.h> #include <pulse/i18n.h> +#include <pulse/client-conf.h> #include <pulsecore/core-util.h> #include <pulsecore/log.h> @@ -40,7 +41,6 @@ #include <pulsecore/native-common.h> #include <pulsecore/x11prop.h> -#include "../pulse/client-conf.h" int main(int argc, char *argv[]) { const char *dname = NULL, *sink = NULL, *source = NULL, *server = NULL, *cookie_file = PA_NATIVE_COOKIE_FILE; @@ -209,6 +209,7 @@ int main(int argc, char *argv[]) { pa_x11_del_prop(xcb, screen, "PULSE_SOURCE"); pa_x11_del_prop(xcb, screen, "PULSE_ID"); pa_x11_del_prop(xcb, screen, "PULSE_COOKIE"); + pa_x11_del_prop(xcb, screen, "PULSE_SESSION_ID"); break; default: |