summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am249
-rw-r--r--src/daemon/caps.c13
-rw-r--r--src/daemon/cmdline.c37
-rw-r--r--src/daemon/cpulimit.c52
-rw-r--r--src/daemon/daemon-conf.c257
-rw-r--r--src/daemon/daemon-conf.h7
-rw-r--r--src/daemon/daemon.conf.in13
-rwxr-xr-xsrc/daemon/default.pa.in63
-rw-r--r--src/daemon/dumpmodules.c18
-rw-r--r--src/daemon/ltdl-bind-now.c160
-rw-r--r--src/daemon/ltdl-bind-now.h32
-rw-r--r--src/daemon/main.c164
-rw-r--r--src/daemon/pulseaudio-module-xsmp.desktop10
-rw-r--r--src/modules/alsa-util.c202
-rw-r--r--src/modules/alsa-util.h9
-rw-r--r--src/modules/dbus-util.c285
-rw-r--r--src/modules/gconf/gconf-helper.c10
-rw-r--r--src/modules/gconf/module-gconf.c99
-rw-r--r--src/modules/ladspa.h603
-rw-r--r--src/modules/module-alsa-sink.c954
-rw-r--r--src/modules/module-alsa-source.c913
-rw-r--r--src/modules/module-cli.c30
-rw-r--r--src/modules/module-combine.c1064
-rw-r--r--src/modules/module-default-device-restore.c103
-rw-r--r--src/modules/module-defs.h.m44
-rw-r--r--src/modules/module-detect.c47
-rw-r--r--src/modules/module-esound-compat-spawnfd.c17
-rw-r--r--src/modules/module-esound-compat-spawnpid.c14
-rw-r--r--src/modules/module-esound-sink.c477
-rw-r--r--src/modules/module-hal-detect.c858
-rw-r--r--src/modules/module-jack-sink.c1025
-rw-r--r--src/modules/module-jack-source.c315
-rw-r--r--src/modules/module-ladspa-sink.c673
-rw-r--r--src/modules/module-lirc.c50
-rw-r--r--src/modules/module-match.c27
-rw-r--r--src/modules/module-mmkbd-evdev.c44
-rw-r--r--src/modules/module-native-protocol-fd.c19
-rw-r--r--src/modules/module-null-sink.c167
-rw-r--r--src/modules/module-oss-mmap.c637
-rw-r--r--src/modules/module-oss.c1469
-rw-r--r--src/modules/module-pipe-sink.c281
-rw-r--r--src/modules/module-pipe-source.c244
-rw-r--r--src/modules/module-protocol-stub.c49
-rw-r--r--src/modules/module-remap-sink.c334
-rw-r--r--src/modules/module-rescue-streams.c54
-rw-r--r--src/modules/module-sine.c81
-rw-r--r--src/modules/module-solaris.c690
-rw-r--r--src/modules/module-suspend-on-idle.c473
-rw-r--r--src/modules/module-tunnel.c4
-rw-r--r--src/modules/module-volume-restore.c47
-rw-r--r--src/modules/module-x11-bell.c54
-rw-r--r--src/modules/module-x11-publish.c24
-rw-r--r--src/modules/module-x11-xsmp.c195
-rw-r--r--src/modules/module-zeroconf-publish.c569
-rw-r--r--src/modules/oss-util.c136
-rw-r--r--src/modules/oss-util.h9
-rw-r--r--src/modules/rtp/module-rtp-recv.c327
-rw-r--r--src/modules/rtp/module-rtp-send.c122
-rw-r--r--src/modules/rtp/rtp.c101
-rw-r--r--src/modules/rtp/sap.c40
-rw-r--r--src/modules/rtp/sdp.c36
-rw-r--r--src/pulse/browser.c62
-rw-r--r--src/pulse/cdecl.h18
-rw-r--r--src/pulse/channelmap.c108
-rw-r--r--src/pulse/channelmap.h9
-rw-r--r--src/pulse/client-conf-x11.c6
-rw-r--r--src/pulse/client-conf.c19
-rw-r--r--src/pulse/context.c198
-rw-r--r--src/pulse/glib-mainloop.c2
-rw-r--r--src/pulse/internal.h9
-rw-r--r--src/pulse/introspect.c372
-rw-r--r--src/pulse/introspect.h16
-rw-r--r--src/pulse/mainloop-api.c22
-rw-r--r--src/pulse/mainloop-signal.c55
-rw-r--r--src/pulse/mainloop.c173
-rw-r--r--src/pulse/operation.c41
-rw-r--r--src/pulse/sample.c79
-rw-r--r--src/pulse/sample.h18
-rw-r--r--src/pulse/scache.c17
-rw-r--r--src/pulse/simple.c49
-rw-r--r--src/pulse/simple.h2
-rw-r--r--src/pulse/stream.c319
-rw-r--r--src/pulse/subscribe.c19
-rw-r--r--src/pulse/thread-mainloop.c56
-rw-r--r--src/pulse/thread-mainloop.h3
-rw-r--r--src/pulse/timeval.c59
-rw-r--r--src/pulse/timeval.h17
-rw-r--r--src/pulse/utf8.c32
-rw-r--r--src/pulse/utf8.h2
-rw-r--r--src/pulse/util.c42
-rw-r--r--src/pulse/util.h2
-rw-r--r--src/pulse/volume.c34
-rw-r--r--src/pulse/volume.h20
-rw-r--r--src/pulse/xmalloc.c14
-rw-r--r--src/pulse/xmalloc.h9
-rw-r--r--src/pulsecore/anotify.c145
-rw-r--r--src/pulsecore/asyncmsgq.c303
-rw-r--r--src/pulsecore/asyncmsgq.h75
-rw-r--r--src/pulsecore/asyncq.c213
-rw-r--r--src/pulsecore/asyncq.h56
-rw-r--r--src/pulsecore/atomic.h193
-rw-r--r--src/pulsecore/authkey-prop.c49
-rw-r--r--src/pulsecore/authkey.c80
-rw-r--r--src/pulsecore/autoload.c43
-rw-r--r--src/pulsecore/avahi-wrap.c52
-rw-r--r--src/pulsecore/cli-command.c497
-rw-r--r--src/pulsecore/cli-text.c122
-rw-r--r--src/pulsecore/cli.c30
-rw-r--r--src/pulsecore/client.c25
-rw-r--r--src/pulsecore/conf-parser.c35
-rw-r--r--src/pulsecore/core-def.h5
-rw-r--r--src/pulsecore/core-error.c178
-rw-r--r--src/pulsecore/core-scache.c94
-rw-r--r--src/pulsecore/core-scache.h1
-rw-r--r--src/pulsecore/core-subscribe.c40
-rw-r--r--src/pulsecore/core-util.c639
-rw-r--r--src/pulsecore/core-util.h44
-rw-r--r--src/pulsecore/core.c73
-rw-r--r--src/pulsecore/core.h55
-rw-r--r--src/pulsecore/creds.h4
-rw-r--r--src/pulsecore/dynarray.c20
-rw-r--r--src/pulsecore/endianmacros.h78
-rw-r--r--src/pulsecore/esound.h2
-rw-r--r--src/pulsecore/fdsem.c276
-rw-r--r--src/pulsecore/fdsem.h49
-rw-r--r--src/pulsecore/ffmpeg/Makefile13
-rw-r--r--src/pulsecore/ffmpeg/avcodec.h82
-rw-r--r--src/pulsecore/ffmpeg/dsputil.h1
-rw-r--r--src/pulsecore/ffmpeg/resample2.c324
-rw-r--r--src/pulsecore/flist.c62
-rw-r--r--src/pulsecore/flist.h27
-rw-r--r--src/pulsecore/g711.c392
-rw-r--r--src/pulsecore/gccmacro.h25
-rw-r--r--src/pulsecore/hashmap.c39
-rw-r--r--src/pulsecore/hashmap.h4
-rw-r--r--src/pulsecore/hook-list.c26
-rw-r--r--src/pulsecore/idxset.c130
-rw-r--r--src/pulsecore/idxset.h5
-rw-r--r--src/pulsecore/inet_ntop.c4
-rw-r--r--src/pulsecore/iochannel.c168
-rw-r--r--src/pulsecore/iochannel.h18
-rw-r--r--src/pulsecore/ioline.c95
-rw-r--r--src/pulsecore/ipacl.c24
-rw-r--r--src/pulsecore/llist.h123
-rw-r--r--src/pulsecore/log.c29
-rw-r--r--src/pulsecore/ltdl-helper.c64
-rw-r--r--src/pulsecore/ltdl-helper.h34
-rw-r--r--src/pulsecore/macro.h149
-rw-r--r--src/pulsecore/mcalign.c49
-rw-r--r--src/pulsecore/memblock.c685
-rw-r--r--src/pulsecore/memblock.h65
-rw-r--r--src/pulsecore/memblockq.c331
-rw-r--r--src/pulsecore/memblockq.h22
-rw-r--r--src/pulsecore/memchunk.c52
-rw-r--r--src/pulsecore/memchunk.h11
-rw-r--r--src/pulsecore/modargs.c50
-rw-r--r--src/pulsecore/modargs.h4
-rw-r--r--src/pulsecore/modinfo.c31
-rw-r--r--src/pulsecore/modinfo.h2
-rw-r--r--src/pulsecore/module.c122
-rw-r--r--src/pulsecore/module.h10
-rw-r--r--src/pulsecore/msgobject.c49
-rw-r--r--src/pulsecore/msgobject.h54
-rw-r--r--src/pulsecore/mutex-posix.c70
-rw-r--r--src/pulsecore/mutex-win32.c2
-rw-r--r--src/pulsecore/mutex.h10
-rw-r--r--src/pulsecore/namereg.c35
-rw-r--r--src/pulsecore/native-common.h5
-rw-r--r--src/pulsecore/object.c72
-rw-r--r--src/pulsecore/object.h106
-rw-r--r--src/pulsecore/once-posix.c71
-rw-r--r--src/pulsecore/once-win32.c69
-rw-r--r--src/pulsecore/once.c96
-rw-r--r--src/pulsecore/once.h48
-rw-r--r--src/pulsecore/packet.c28
-rw-r--r--src/pulsecore/packet.h4
-rw-r--r--src/pulsecore/parseaddr.c14
-rw-r--r--src/pulsecore/pdispatch.c73
-rw-r--r--src/pulsecore/pid.c74
-rw-r--r--src/pulsecore/pipe.c16
-rw-r--r--src/pulsecore/play-memblockq.c194
-rw-r--r--src/pulsecore/play-memblockq.h10
-rw-r--r--src/pulsecore/play-memchunk.c160
-rw-r--r--src/pulsecore/poll.c2
-rw-r--r--src/pulsecore/props.c43
-rw-r--r--src/pulsecore/protocol-cli.c22
-rw-r--r--src/pulsecore/protocol-esound.c640
-rw-r--r--src/pulsecore/protocol-http.c39
-rw-r--r--src/pulsecore/protocol-native.c1651
-rw-r--r--src/pulsecore/protocol-simple.c368
-rw-r--r--src/pulsecore/pstream-util.c24
-rw-r--r--src/pulsecore/pstream.c367
-rw-r--r--src/pulsecore/pstream.h8
-rw-r--r--src/pulsecore/queue.c52
-rw-r--r--src/pulsecore/random.c21
-rw-r--r--src/pulsecore/refcnt.h10
-rw-r--r--src/pulsecore/resampler.c1099
-rw-r--r--src/pulsecore/resampler.h31
-rw-r--r--src/pulsecore/rtclock.c98
-rw-r--r--src/pulsecore/rtclock.h43
-rw-r--r--src/pulsecore/rtpoll.c751
-rw-r--r--src/pulsecore/rtpoll.h116
-rw-r--r--src/pulsecore/rtsig.c133
-rw-r--r--src/pulsecore/rtsig.h41
-rw-r--r--src/pulsecore/sample-util.c288
-rw-r--r--src/pulsecore/sample-util.h10
-rw-r--r--src/pulsecore/sconv-s16be.c7
-rw-r--r--src/pulsecore/sconv-s16be.h15
-rw-r--r--src/pulsecore/sconv-s16le.c65
-rw-r--r--src/pulsecore/sconv-s16le.h15
-rw-r--r--src/pulsecore/sconv.c273
-rw-r--r--src/pulsecore/sconv.h10
-rw-r--r--src/pulsecore/semaphore-posix.c69
-rw-r--r--src/pulsecore/semaphore-win32.c65
-rw-r--r--src/pulsecore/semaphore.h (renamed from src/pulsecore/anotify.h)19
-rw-r--r--src/pulsecore/shm.c247
-rw-r--r--src/pulsecore/shm.h2
-rw-r--r--src/pulsecore/sink-input.c832
-rw-r--r--src/pulsecore/sink-input.h163
-rw-r--r--src/pulsecore/sink.c984
-rw-r--r--src/pulsecore/sink.h161
-rw-r--r--src/pulsecore/sioman.c14
-rw-r--r--src/pulsecore/socket-client.c180
-rw-r--r--src/pulsecore/socket-server.c121
-rw-r--r--src/pulsecore/socket-util.c118
-rw-r--r--src/pulsecore/socket-util.h5
-rw-r--r--src/pulsecore/sound-file-stream.c249
-rw-r--r--src/pulsecore/sound-file.c71
-rw-r--r--src/pulsecore/source-output.c376
-rw-r--r--src/pulsecore/source-output.h97
-rw-r--r--src/pulsecore/source.c560
-rw-r--r--src/pulsecore/source.h140
-rw-r--r--src/pulsecore/speex/Makefile13
-rw-r--r--src/pulsecore/speex/arch.h197
-rw-r--r--src/pulsecore/speex/fixed_generic.h106
-rw-r--r--src/pulsecore/speex/resample.c1114
-rw-r--r--src/pulsecore/speex/speex_resampler.h328
-rw-r--r--src/pulsecore/speexwrap.h48
-rw-r--r--src/pulsecore/strbuf.c49
-rw-r--r--src/pulsecore/strlist.c39
-rw-r--r--src/pulsecore/tagstruct.c125
-rw-r--r--src/pulsecore/thread-mq.c112
-rw-r--r--src/pulsecore/thread-mq.h49
-rw-r--r--src/pulsecore/thread-posix.c59
-rw-r--r--src/pulsecore/thread-win32.c19
-rw-r--r--src/pulsecore/thread.h61
-rw-r--r--src/pulsecore/time-smoother.c378
-rw-r--r--src/pulsecore/time-smoother.h43
-rw-r--r--src/pulsecore/tokenizer.c34
-rw-r--r--src/pulsecore/winsock.h2
-rw-r--r--src/pulsecore/x11prop.c1
-rw-r--r--src/pulsecore/x11wrap.c92
-rw-r--r--src/tests/asyncmsgq-test.c110
-rw-r--r--src/tests/asyncq-test.c87
-rw-r--r--src/tests/hook-list-test.c4
-rw-r--r--src/tests/interpol-test.c2
-rw-r--r--src/tests/mcalign-test.c17
-rw-r--r--src/tests/memblock-test.c42
-rw-r--r--src/tests/memblockq-test.c20
-rw-r--r--src/tests/queue-test.c69
-rw-r--r--src/tests/resampler-test.c227
-rw-r--r--src/tests/rtpoll-test.c91
-rw-r--r--src/tests/sig2str-test.c39
-rw-r--r--src/tests/smoother-test.c80
-rw-r--r--src/tests/thread-mainloop-test.c13
-rw-r--r--src/tests/thread-test.c6
-rw-r--r--src/utils/pactl.c123
-rw-r--r--src/utils/padsp.c71
-rw-r--r--src/utils/paplay.c2
-rw-r--r--src/utils/pasuspender.c316
270 files changed, 28673 insertions, 11201 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 93f331d6..e17e5ece 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -30,6 +30,7 @@ pulseincludedir=$(includedir)/pulse
pulsecoreincludedir=$(includedir)/pulsecore
pulseconfdir=$(sysconfdir)/pulse
pulselibexecdir=$(libexecdir)/pulse
+xdgautostartdir=$(sysconfdir)/xdg/autostart
###################################
# Defines #
@@ -76,15 +77,14 @@ endif
if OS_IS_WIN32
PA_THREAD_OBJS = \
- pulsecore/once-win32.c pulsecore/once.h \
pulsecore/mutex-win32.c pulsecore/mutex.h \
- pulsecore/thread-win32.c pulsecore/thread.h
+ pulsecore/thread-win32.c pulsecore/thread.h \
+ pulsecore/semaphore-win32.c pulsecore/semaphore.h
else
PA_THREAD_OBJS = \
- pulsecore/atomic.h \
- pulsecore/once-posix.c pulsecore/once.h \
pulsecore/mutex-posix.c pulsecore/mutex.h \
- pulsecore/thread-posix.c pulsecore/thread.h
+ pulsecore/thread-posix.c pulsecore/thread.h \
+ pulsecore/semaphore-posix.c pulsecore/semaphore.h
endif
###################################
@@ -100,13 +100,19 @@ EXTRA_DIST = \
depmod.py \
daemon/esdcompat.in \
utils/padsp \
- modules/module-defs.h.m4
+ modules/module-defs.h.m4 \
+ daemon/pulseaudio-module-xsmp.desktop
pulseconf_DATA = \
default.pa \
daemon.conf \
client.conf
+if HAVE_X11
+xdgautostart_DATA = \
+ daemon/pulseaudio-module-xsmp.desktop
+endif
+
BUILT_SOURCES = \
pulse/version.h
@@ -122,13 +128,13 @@ pulseaudio_SOURCES = \
daemon/cpulimit.c daemon/cpulimit.h \
daemon/daemon-conf.c daemon/daemon-conf.h \
daemon/dumpmodules.c daemon/dumpmodules.h \
+ daemon/ltdl-bind-now.c daemon/ltdl-bind-now.h \
daemon/main.c \
pulsecore/gccmacro.h
-pulseaudio_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
+pulseaudio_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS) $(LIBSAMPLERATE_CFLAGS) $(LIBSNDFILE_CFLAGS) $(CAP_CFLAGS) $(LIBOIL_CFLAGS) $(DBUS_CFLAGS)
pulseaudio_CPPFLAGS = $(AM_CPPFLAGS)
-pulseaudio_LDADD = $(AM_LDADD) libpulsecore.la $(LIBLTDL) \
- $(LIBSAMPLERATE_LIBS) $(LIBSNDFILE_LIBS) $(CAP_LIBS) $(LIBOIL_LIBS)
+pulseaudio_LDADD = $(AM_LDADD) libpulsecore.la $(LIBLTDL) $(LIBSAMPLERATE_LIBS) $(LIBSNDFILE_LIBS) $(CAP_LIBS) $(LIBOIL_LIBS) $(DBUS_LIBS)
# This is needed because automake doesn't properly expand the foreach below
pulseaudio_DEPENDENCIES = libpulsecore.la $(PREOPEN_LIBS)
@@ -151,7 +157,8 @@ endif
bin_PROGRAMS += \
pacat \
pactl \
- paplay
+ paplay \
+ pasuspender
if HAVE_AF_UNIX
bin_PROGRAMS += pacmd
@@ -182,6 +189,11 @@ pactl_LDADD = $(AM_LDADD) libpulse.la $(LIBSNDFILE_LIBS)
pactl_CFLAGS = $(AM_CFLAGS) $(LIBSNDFILE_CFLAGS)
pactl_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+pasuspender_SOURCES = utils/pasuspender.c
+pasuspender_LDADD = $(AM_LDADD) libpulse.la $(LIBSNDFILE_LIBS)
+pasuspender_CFLAGS = $(AM_CFLAGS) $(LIBSNDFILE_CFLAGS)
+pasuspender_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
pacmd_SOURCES = utils/pacmd.c pulsecore/pid.c pulsecore/pid.h
pacmd_CFLAGS = $(AM_CFLAGS)
pacmd_LDADD = $(AM_LDADD) libpulse.la
@@ -219,7 +231,14 @@ noinst_PROGRAMS = \
hook-list-test \
memblock-test \
thread-test \
- flist-test
+ flist-test \
+ asyncq-test \
+ asyncmsgq-test \
+ queue-test \
+ rtpoll-test \
+ sig2str-test \
+ resampler-test \
+ smoother-test
if HAVE_SIGXCPU
noinst_PROGRAMS += \
@@ -274,13 +293,31 @@ thread_test_CFLAGS = $(AM_CFLAGS)
thread_test_LDADD = $(AM_LDADD) libpulsecore.la
thread_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
-flist_test_SOURCES = tests/flist-test.c \
- pulsecore/atomic.h \
- pulsecore/flist.c pulsecore/flist.h
+flist_test_SOURCES = tests/flist-test.c
flist_test_CFLAGS = $(AM_CFLAGS)
flist_test_LDADD = $(AM_LDADD) libpulsecore.la
flist_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+asyncq_test_SOURCES = tests/asyncq-test.c
+asyncq_test_CFLAGS = $(AM_CFLAGS)
+asyncq_test_LDADD = $(AM_LDADD) libpulsecore.la
+asyncq_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+asyncmsgq_test_SOURCES = tests/asyncmsgq-test.c
+asyncmsgq_test_CFLAGS = $(AM_CFLAGS)
+asyncmsgq_test_LDADD = $(AM_LDADD) libpulsecore.la
+asyncmsgq_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+queue_test_SOURCES = tests/queue-test.c
+queue_test_CFLAGS = $(AM_CFLAGS)
+queue_test_LDADD = $(AM_LDADD) libpulsecore.la
+queue_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+rtpoll_test_SOURCES = tests/rtpoll-test.c
+rtpoll_test_CFLAGS = $(AM_CFLAGS)
+rtpoll_test_LDADD = $(AM_LDADD) libpulsecore.la
+rtpoll_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
mcalign_test_SOURCES = tests/mcalign-test.c
mcalign_test_CFLAGS = $(AM_CFLAGS)
mcalign_test_LDADD = $(AM_LDADD) $(WINSOCK_LIBS) libpulsecore.la
@@ -341,6 +378,21 @@ interpol_test_LDADD = $(AM_LDADD) libpulse.la
interpol_test_CFLAGS = $(AM_CFLAGS)
interpol_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+sig2str_test_SOURCES = tests/sig2str-test.c
+sig2str_test_LDADD = $(AM_LDADD) libpulsecore.la
+sig2str_test_CFLAGS = $(AM_CFLAGS)
+sig2str_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+resampler_test_SOURCES = tests/resampler-test.c
+resampler_test_LDADD = $(AM_LDADD) libpulsecore.la
+resampler_test_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
+resampler_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)
+
+smoother_test_SOURCES = tests/smoother-test.c
+smoother_test_LDADD = $(AM_LDADD) libpulsecore.la
+smoother_test_CFLAGS = $(AM_CFLAGS)
+smoother_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
###################################
# Client library #
###################################
@@ -455,6 +507,10 @@ libpulse_la_SOURCES += \
pulsecore/core-error.c pulsecore/core-error.h \
pulsecore/winsock.h pulsecore/creds.h \
pulsecore/shm.c pulsecore/shm.h \
+ pulsecore/flist.c pulsecore/flist.h \
+ pulsecore/object.c pulsecore/object.h \
+ pulsecore/msgobject.c pulsecore/msgobject.h \
+ pulsecore/once.c pulsecore/once.h \
$(PA_THREAD_OBJS)
if OS_IS_WIN32
@@ -515,10 +571,26 @@ libpulsedsp_la_LIBADD = $(AM_LIBADD) libpulse.la
libpulsedsp_la_LDFLAGS = -avoid-version
###################################
+# Speex Resampler #
+###################################
+
+noinst_LTLIBRARIES = libspeex-resampler-fixed.la libspeex-resampler-float.la libffmpeg-resampler.la
+
+libspeex_resampler_fixed_la_CPPFLAGS = $(AM_CPPFLAGS) -DRANDOM_PREFIX=paspfx -DOUTSIDE_SPEEX -DFIXED_POINT
+libspeex_resampler_fixed_la_SOURCES = pulsecore/speex/resample.c pulsecore/speex/speex_resampler.h pulsecore/speex/arch.h pulsecore/speex/fixed_generic.h pulsecore/speexwrap.h
+
+libspeex_resampler_float_la_CPPFLAGS = $(AM_CPPFLAGS) -DRANDOM_PREFIX=paspfl -DOUTSIDE_SPEEX
+libspeex_resampler_float_la_SOURCES = pulsecore/speex/resample.c pulsecore/speex/speex_resampler.h pulsecore/speex/arch.h
+
+libffmpeg_resampler_la_CPPFLAGS = $(AM_CPPFLAGS)
+libffmpeg_resampler_la_SOURCES = pulsecore/ffmpeg/resample2.c pulsecore/ffmpeg/avcodec.h pulsecore/ffmpeg/dsputil.h
+
+###################################
# Daemon core library #
###################################
-pulsecoreinclude_HEADERS = \
+#pulsecoreinclude_HEADERS =
+noinst_HEADERS = \
pulsecore/autoload.h \
pulsecore/atomic.h \
pulsecore/cli-command.h \
@@ -567,6 +639,7 @@ pulsecoreinclude_HEADERS = \
pulsecore/refcnt.h \
pulsecore/mutex.h \
pulsecore/thread.h \
+ pulsecore/semaphore.h \
pulsecore/once.h
lib_LTLIBRARIES += libpulsecore.la
@@ -608,6 +681,7 @@ libpulsecore_la_SOURCES += \
pulsecore/memchunk.c pulsecore/memchunk.h \
pulsecore/modargs.c pulsecore/modargs.h \
pulsecore/modinfo.c pulsecore/modinfo.h \
+ pulsecore/ltdl-helper.c pulsecore/ltdl-helper.h \
pulsecore/module.c pulsecore/module.h \
pulsecore/namereg.c pulsecore/namereg.h \
pulsecore/pid.c pulsecore/pid.h \
@@ -636,6 +710,19 @@ libpulsecore_la_SOURCES += \
pulsecore/core-error.c pulsecore/core-error.h \
pulsecore/hook-list.c pulsecore/hook-list.h \
pulsecore/shm.c pulsecore/shm.h \
+ pulsecore/flist.c pulsecore/flist.h \
+ pulsecore/asyncmsgq.c pulsecore/asyncmsgq.h \
+ pulsecore/asyncq.c pulsecore/asyncq.h \
+ pulsecore/thread-mq.c pulsecore/thread-mq.h \
+ pulsecore/fdsem.c pulsecore/fdsem.h \
+ pulsecore/object.c pulsecore/object.h \
+ pulsecore/msgobject.c pulsecore/msgobject.h \
+ pulsecore/rtsig.c pulsecore/rtsig.h \
+ pulsecore/rtpoll.c pulsecore/rtpoll.h \
+ pulsecore/rtclock.c pulsecore/rtclock.h \
+ pulsecore/macro.h \
+ pulsecore/once.c pulsecore/once.h \
+ pulsecore/time-smoother.c pulsecore/time-smoother.h \
$(PA_THREAD_OBJS)
if OS_IS_WIN32
@@ -645,13 +732,14 @@ endif
libpulsecore_la_CPPFLAGS = $(AM_CPPFLAGS) $(LIBOIL_CFLAGS)
libpulsecore_la_LDFLAGS = -version-info $(LIBPULSECORE_VERSION_INFO)
-libpulsecore_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) $(LIBSAMPLERATE_LIBS) $(LIBSNDFILE_LIBS) $(WINSOCK_LIBS) $(LIBOIL_LIBS) $(LIBICONV)
+libpulsecore_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) $(LIBSAMPLERATE_LIBS) $(LIBSNDFILE_LIBS) $(WINSOCK_LIBS) $(LIBOIL_LIBS) $(LIBICONV) libspeex-resampler-fixed.la libspeex-resampler-float.la libffmpeg-resampler.la
###################################
# Plug-in support libraries #
###################################
-pulsecoreinclude_HEADERS += \
+#pulsecoreinclude_HEADERS +=
+noinst_HEADERS += \
pulsecore/socket-util.h \
pulsecore/iochannel.h \
pulsecore/socket-server.h \
@@ -700,9 +788,9 @@ modlibexec_LTLIBRARIES = \
libauthkey-prop.la \
libstrlist.la \
libprotocol-simple.la \
- libprotocol-esound.la \
+ libprotocol-http.la \
libprotocol-native.la \
- libprotocol-http.la
+ libprotocol-esound.la
# We need to emulate sendmsg/recvmsg to support this on Win32
if !OS_IS_WIN32
@@ -711,7 +799,8 @@ modlibexec_LTLIBRARIES += \
endif
if HAVE_X11
-pulsecoreinclude_HEADERS += \
+#pulsecoreinclude_HEADERS +=
+noinst_HEADERS += \
pulsecore/x11wrap.h \
pulsecore/x11prop.h
@@ -721,7 +810,8 @@ modlibexec_LTLIBRARIES += \
endif
if HAVE_AVAHI
-pulsecoreinclude_HEADERS += \
+#pulsecoreinclude_HEADERS +=
+noinst_HEADERS += \
pulsecore/avahi-wrap.h
modlibexec_LTLIBRARIES += \
@@ -851,19 +941,23 @@ modlibexec_LTLIBRARIES += \
module-cli.la \
module-cli-protocol-tcp.la \
module-simple-protocol-tcp.la \
- module-esound-protocol-tcp.la \
- module-native-protocol-tcp.la \
- module-native-protocol-fd.la \
- module-sine.la \
- module-combine.la \
- module-tunnel-sink.la \
- module-tunnel-source.la \
module-null-sink.la \
- module-esound-sink.la \
- module-http-protocol-tcp.la \
module-detect.la \
module-volume-restore.la \
- module-rescue-streams.la
+ module-default-device-restore.la \
+ module-rescue-streams.la \
+ module-suspend-on-idle.la \
+ module-http-protocol-tcp.la \
+ module-sine.la \
+ module-native-protocol-tcp.la \
+ module-native-protocol-fd.la \
+ module-esound-protocol-tcp.la \
+ module-combine.la \
+ module-remap-sink.la \
+ module-ladspa-sink.la \
+ module-esound-sink.la
+# module-tunnel-sink.la
+# module-tunnel-source.la
# See comment at librtp.la above
if !OS_IS_WIN32
@@ -876,9 +970,9 @@ if HAVE_AF_UNIX
modlibexec_LTLIBRARIES += \
module-cli-protocol-unix.la \
module-simple-protocol-unix.la \
- module-esound-protocol-unix.la \
+ module-http-protocol-unix.la \
module-native-protocol-unix.la \
- module-http-protocol-unix.la
+ module-esound-protocol-unix.la
endif
if HAVE_MKFIFO
@@ -901,14 +995,14 @@ endif
if HAVE_X11
modlibexec_LTLIBRARIES += \
module-x11-bell.la \
- module-x11-publish.la
+ module-x11-publish.la \
+ module-x11-xsmp.la
endif
if HAVE_OSS
modlibexec_LTLIBRARIES += \
liboss-util.la \
- module-oss.la \
- module-oss-mmap.la
+ module-oss.la
endif
if HAVE_ALSA
@@ -952,10 +1046,10 @@ pulselibexec_PROGRAMS = \
gconf-helper
endif
-if OS_IS_WIN32
-modlibexec_LTLIBRARIES += \
- module-waveout.la
-endif
+#if OS_IS_WIN32
+#modlibexec_LTLIBRARIES += \
+# module-waveout.la
+#endif
if HAVE_HAL
modlibexec_LTLIBRARIES += \
@@ -980,6 +1074,8 @@ SYMDEF_FILES = \
modules/module-native-protocol-fd-symdef.h \
modules/module-sine-symdef.h \
modules/module-combine-symdef.h \
+ modules/module-remap-sink-symdef.h \
+ modules/module-ladspa-sink-symdef.h \
modules/module-esound-compat-spawnfd-symdef.h \
modules/module-esound-compat-spawnpid-symdef.h \
modules/module-match-symdef.h \
@@ -994,8 +1090,8 @@ SYMDEF_FILES = \
modules/module-http-protocol-unix-symdef.h \
modules/module-x11-bell-symdef.h \
modules/module-x11-publish-symdef.h \
+ modules/module-x11-xsmp-symdef.h \
modules/module-oss-symdef.h \
- modules/module-oss-mmap-symdef.h \
modules/module-alsa-sink-symdef.h \
modules/module-alsa-source-symdef.h \
modules/module-solaris-symdef.h \
@@ -1006,7 +1102,9 @@ SYMDEF_FILES = \
modules/module-jack-sink-symdef.h \
modules/module-jack-source-symdef.h \
modules/module-volume-restore-symdef.h \
+ modules/module-default-device-restore-symdef.h \
modules/module-rescue-streams-symdef.h \
+ modules/module-suspend-on-idle-symdef.h \
modules/module-hal-detect-symdef.h \
modules/gconf/module-gconf-symdef.h
@@ -1126,18 +1224,27 @@ module_combine_la_SOURCES = modules/module-combine.c
module_combine_la_LDFLAGS = -module -avoid-version
module_combine_la_LIBADD = $(AM_LIBADD) libpulsecore.la
+module_remap_sink_la_SOURCES = modules/module-remap-sink.c
+module_remap_sink_la_LDFLAGS = -module -avoid-version
+module_remap_sink_la_LIBADD = $(AM_LIBADD) libpulsecore.la
+
+module_ladspa_sink_la_SOURCES = modules/module-ladspa-sink.c modules/ladspa.h
+module_ladspa_sink_la_CFLAGS = -DLADSPA_PATH=\"$(libdir)/ladspa:/usr/local/lib/ladspa:/usr/lib/ladspa\" $(AM_CFLAGS)
+module_ladspa_sink_la_LDFLAGS = -module -avoid-version
+module_ladspa_sink_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) libpulsecore.la
+
module_match_la_SOURCES = modules/module-match.c
module_match_la_LDFLAGS = -module -avoid-version
module_match_la_LIBADD = $(AM_LIBADD) libpulsecore.la
-module_tunnel_sink_la_SOURCES = modules/module-tunnel.c
-module_tunnel_sink_la_CFLAGS = -DTUNNEL_SINK=1 $(AM_CFLAGS)
-module_tunnel_sink_la_LDFLAGS = -module -avoid-version
-module_tunnel_sink_la_LIBADD = $(AM_LIBADD) libpulsecore.la libsocket-client.la libpstream.la libpstream-util.la libpdispatch.la libtagstruct.la libauthkey.la libauthkey-prop.la libsocket-util.la libiochannel.la
+#module_tunnel_sink_la_SOURCES = modules/module-tunnel.c
+#module_tunnel_sink_la_CFLAGS = -DTUNNEL_SINK=1 $(AM_CFLAGS)
+#module_tunnel_sink_la_LDFLAGS = -module -avoid-version
+#module_tunnel_sink_la_LIBADD = $(AM_LIBADD) libpulsecore.la libsocket-client.la libpstream.la libpstream-util.la libpdispatch.la libtagstruct.la libauthkey.la libauthkey-prop.la libsocket-util.la libiochannel.la
-module_tunnel_source_la_SOURCES = modules/module-tunnel.c
-module_tunnel_source_la_LDFLAGS = -module -avoid-version
-module_tunnel_source_la_LIBADD = $(AM_LIBADD) libpulsecore.la libsocket-client.la libpstream.la libpstream-util.la libpdispatch.la libtagstruct.la libauthkey.la libauthkey-prop.la libsocket-util.la libiochannel.la
+#module_tunnel_source_la_SOURCES = modules/module-tunnel.c
+#module_tunnel_source_la_LDFLAGS = -module -avoid-version
+#module_tunnel_source_la_LIBADD = $(AM_LIBADD) libpulsecore.la libsocket-client.la libpstream.la libpstream-util.la libpdispatch.la libtagstruct.la libauthkey.la libauthkey-prop.la libsocket-util.la libiochannel.la
# X11
@@ -1151,6 +1258,11 @@ module_x11_publish_la_CFLAGS = $(AM_CFLAGS) $(X_CFLAGS)
module_x11_publish_la_LDFLAGS = -module -avoid-version
module_x11_publish_la_LIBADD = $(AM_LIBADD) $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS) libx11wrap.la libauthkey.la libauthkey-prop.la libx11prop.la libstrlist.la libpulsecore.la
+module_x11_xsmp_la_SOURCES = modules/module-x11-xsmp.c
+module_x11_xsmp_la_CFLAGS = $(AM_CFLAGS) $(X_CFLAGS)
+module_x11_xsmp_la_LDFLAGS = -module -avoid-version
+module_x11_xsmp_la_LIBADD = $(AM_LIBADD) $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS) libpulsecore.la
+
# OSS
liboss_util_la_SOURCES = modules/oss-util.c modules/oss-util.h
@@ -1161,10 +1273,6 @@ module_oss_la_SOURCES = modules/module-oss.c
module_oss_la_LDFLAGS = -module -avoid-version
module_oss_la_LIBADD = $(AM_LIBADD) libiochannel.la liboss-util.la libpulsecore.la
-module_oss_mmap_la_SOURCES = modules/module-oss-mmap.c
-module_oss_mmap_la_LDFLAGS = -module -avoid-version
-module_oss_mmap_la_LIBADD = $(AM_LIBADD) liboss-util.la libpulsecore.la
-
# ALSA
libalsa_util_la_SOURCES = modules/alsa-util.c modules/alsa-util.h
@@ -1211,10 +1319,10 @@ module_mmkbd_evdev_la_CFLAGS = $(AM_CFLAGS)
# Windows waveout
-module_waveout_la_SOURCES = modules/module-waveout.c
-module_waveout_la_LDFLAGS = -module -avoid-version
-module_waveout_la_LIBADD = $(AM_LIBADD) libpulsecore.la -lwinmm
-module_waveout_la_CFLAGS = $(AM_CFLAGS)
+#module_waveout_la_SOURCES = modules/module-waveout.c
+#module_waveout_la_LDFLAGS = -module -avoid-version
+#module_waveout_la_LIBADD = $(AM_LIBADD) libpulsecore.la -lwinmm
+#module_waveout_la_CFLAGS = $(AM_CFLAGS)
# Hardware autodetection module
module_detect_la_SOURCES = modules/module-detect.c
@@ -1228,16 +1336,28 @@ module_volume_restore_la_LDFLAGS = -module -avoid-version
module_volume_restore_la_LIBADD = $(AM_LIBADD) libpulsecore.la
module_volume_restore_la_CFLAGS = $(AM_CFLAGS)
+# Default sink/source restore module
+module_default_device_restore_la_SOURCES = modules/module-default-device-restore.c
+module_default_device_restore_la_LDFLAGS = -module -avoid-version
+module_default_device_restore_la_LIBADD = $(AM_LIBADD) libpulsecore.la
+module_default_device_restore_la_CFLAGS = $(AM_CFLAGS)
+
# Rescue streams module
module_rescue_streams_la_SOURCES = modules/module-rescue-streams.c
module_rescue_streams_la_LDFLAGS = -module -avoid-version
module_rescue_streams_la_LIBADD = $(AM_LIBADD) libpulsecore.la
module_rescue_streams_la_CFLAGS = $(AM_CFLAGS)
+# Suspend-on-idle module
+module_suspend_on_idle_la_SOURCES = modules/module-suspend-on-idle.c
+module_suspend_on_idle_la_LDFLAGS = -module -avoid-version
+module_suspend_on_idle_la_LIBADD = $(AM_LIBADD) libpulsecore.la
+module_suspend_on_idle_la_CFLAGS = $(AM_CFLAGS)
+
# RTP modules
module_rtp_send_la_SOURCES = modules/rtp/module-rtp-send.c
module_rtp_send_la_LDFLAGS = -module -avoid-version
-module_rtp_send_la_LIBADD = $(AM_LIBADD) libpulsecore.la librtp.la
+module_rtp_send_la_LIBADD = $(AM_LIBADD) libpulsecore.la librtp.la libsocket-util.la
module_rtp_send_la_CFLAGS = $(AM_CFLAGS)
module_rtp_recv_la_SOURCES = modules/rtp/module-rtp-recv.c
@@ -1275,7 +1395,7 @@ module_gconf_la_LIBADD = $(AM_LIBADD) libpulsecore.la
module_gconf_la_CFLAGS = $(AM_CFLAGS) -DPA_GCONF_HELPER=\"$(pulselibexecdir)/gconf-helper\"
gconf_helper_SOURCES = modules/gconf/gconf-helper.c
-gconf_helper_LDADD = $(AM_LDADD) $(GCONF_LIBS)
+gconf_helper_LDADD = $(AM_LDADD) $(GCONF_LIBS) libpulsecore.la
gconf_helper_CFLAGS = $(AM_CFLAGS) $(GCONF_CFLAGS)
gconf_helper_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
@@ -1304,8 +1424,8 @@ default.pa: daemon/default.pa.win32
else
default.pa: daemon/default.pa.in Makefile
sed -e 's,@PA_BINARY\@,$(PA_BINARY),g' \
- -e 's,@HAVE_HAL_TRUE\@,@HAVE_HAL_TRUE@,g' \
- -e 's,@HAVE_HAL_FALSE\@,@HAVE_HAL_FALSE@,g' < $< > $@
+ -e 's,@PA_DLSEARCHPATH\@,$(modlibexecdir),g' \
+ -e 's,@PA_SOEXT\@,.so,g' < $< > $@
endif
daemon.conf: daemon/daemon.conf.in Makefile
@@ -1323,4 +1443,13 @@ install-exec-hook:
massif: pulseaudio
libtool --mode=execute valgrind --tool=massif --depth=6 --alloc-fn=pa_xmalloc --alloc-fn=pa_xmalloc0 --alloc-fn=pa_xrealloc --alloc-fn=dbus_realloc --alloc-fn=pa_xnew0_internal --alloc-fn=pa_xnew_internal ./pulseaudio
+update-speex:
+ wget -O pulsecore/speex/speex_resampler.h http://svn.xiph.org/trunk/speex/include/speex/speex_resampler.h
+ wget -O pulsecore/speex/resample.c http://svn.xiph.org/trunk/speex/libspeex/resample.c
+ wget -O pulsecore/speex/arch.h http://svn.xiph.org/trunk/speex/libspeex/arch.h
+ wget -O pulsecore/speex/fixed_generic.h http://svn.xiph.org/trunk/speex/libspeex/fixed_generic.h
+
+update-ffmpeg:
+ wget -O pulsecore/ffmpeg/resample2.c http://svn.mplayerhq.hu/ffmpeg/trunk/libavcodec/resample2.c?view=co
+
.PHONY: utils/padsp
diff --git a/src/daemon/caps.c b/src/daemon/caps.c
index f92db743..5b4008a5 100644
--- a/src/daemon/caps.c
+++ b/src/daemon/caps.c
@@ -26,11 +26,11 @@
#include <config.h>
#endif
-#include <assert.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
+#include <pulsecore/macro.h>
#ifdef HAVE_SYS_CAPABILITY_H
#include <sys/capability.h>
@@ -60,7 +60,7 @@ void pa_drop_root(void) {
if (uid == 0 || geteuid() != 0)
return;
- pa_log_info("dropping root rights.");
+ pa_log_info("Dropping root priviliges.");
#if defined(HAVE_SETRESUID)
setresuid(uid, uid, uid);
@@ -88,8 +88,9 @@ int pa_limit_caps(void) {
cap_value_t nice_cap = CAP_SYS_NICE;
caps = cap_init();
- assert(caps);
+ pa_assert(caps);
cap_clear(caps);
+ cap_set_flag(caps, CAP_EFFECTIVE, 1, &nice_cap, CAP_SET);
cap_set_flag(caps, CAP_PERMITTED, 1, &nice_cap, CAP_SET);
if (cap_set_proc(caps) < 0)
@@ -98,7 +99,7 @@ int pa_limit_caps(void) {
if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0)
goto fail;
- pa_log_info("dropped capabilities successfully.");
+ pa_log_info("Dropped capabilities successfully.");
r = 1;
@@ -114,14 +115,14 @@ int pa_drop_caps(void) {
int r = -1;
caps = cap_init();
- assert(caps);
+ pa_assert(caps);
cap_clear(caps);
prctl(PR_SET_KEEPCAPS, 0, 0, 0, 0);
if (cap_set_proc(caps) < 0) {
- pa_log("failed to drop capabilities: %s", pa_cstrerror(errno));
+ pa_log("Failed to drop capabilities: %s", pa_cstrerror(errno));
goto fail;
}
diff --git a/src/daemon/cmdline.c b/src/daemon/cmdline.c
index dc757c9c..6b7b2671 100644
--- a/src/daemon/cmdline.c
+++ b/src/daemon/cmdline.c
@@ -26,7 +26,6 @@
#endif
#include <string.h>
-#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <getopt.h>
@@ -36,6 +35,7 @@
#include <pulsecore/core-util.h>
#include <pulsecore/strbuf.h>
+#include <pulsecore/macro.h>
#include "cmdline.h"
@@ -63,7 +63,9 @@ enum {
ARG_CHECK,
ARG_NO_CPU_LIMIT,
ARG_DISABLE_SHM,
- ARG_SYSTEM
+ ARG_DUMP_RESAMPLE_METHODS,
+ ARG_SYSTEM,
+ ARG_CLEANUP_SHM
};
/* Tabel for getopt_long() */
@@ -92,12 +94,16 @@ static struct option long_options[] = {
{"system", 2, 0, ARG_SYSTEM},
{"no-cpu-limit", 2, 0, ARG_NO_CPU_LIMIT},
{"disable-shm", 2, 0, ARG_DISABLE_SHM},
+ {"dump-resample-methods", 2, 0, ARG_DUMP_RESAMPLE_METHODS},
+ {"cleanup-shm", 2, 0, ARG_CLEANUP_SHM},
{NULL, 0, 0, 0}
};
void pa_cmdline_help(const char *argv0) {
const char *e;
+ pa_assert(argv0);
+
if ((e = strrchr(argv0, '/')))
e++;
else
@@ -109,6 +115,8 @@ void pa_cmdline_help(const char *argv0) {
" --version Show version\n"
" --dump-conf Dump default configuration\n"
" --dump-modules Dump list of available modules\n"
+ " --dump-resample-methods Dump available resample methods\n"
+ " --cleanup-shm Cleanup stale shared memory segments\n"
" -k --kill Kill a running daemon\n"
" --check Check for a running daemon\n\n"
@@ -131,9 +139,8 @@ void pa_cmdline_help(const char *argv0) {
" -p, --dl-search-path=PATH Set the search path for dynamic shared\n"
" objects (plugins)\n"
" --resample-method=[METHOD] Use the specified resampling method\n"
- " (one of src-sinc-medium-quality,\n"
- " src-sinc-best-quality,src-sinc-fastest\n"
- " src-zero-order-hold,src-linear,trivial)\n"
+ " (See --dump-resample-methods for\n"
+ " possible values)\n"
" --use-pid-file[=BOOL] Create a PID file\n"
" --no-cpu-limit[=BOOL] Do not install CPU load limiter on\n"
" platforms that support it.\n"
@@ -152,7 +159,10 @@ void pa_cmdline_help(const char *argv0) {
int pa_cmdline_parse(pa_daemon_conf *conf, int argc, char *const argv [], int *d) {
pa_strbuf *buf = NULL;
int c;
- assert(conf && argc && argv);
+
+ pa_assert(conf);
+ pa_assert(argc > 0);
+ pa_assert(argv);
buf = pa_strbuf_new();
@@ -178,6 +188,14 @@ int pa_cmdline_parse(pa_daemon_conf *conf, int argc, char *const argv [], int *d
conf->cmd = PA_CMD_DUMP_MODULES;
break;
+ case ARG_DUMP_RESAMPLE_METHODS:
+ conf->cmd = PA_CMD_DUMP_RESAMPLE_METHODS;
+ break;
+
+ case ARG_CLEANUP_SHM:
+ conf->cmd = PA_CMD_CLEANUP_SHM;
+ break;
+
case 'k':
case ARG_KILL:
conf->cmd = PA_CMD_KILL;
@@ -193,9 +211,12 @@ int pa_cmdline_parse(pa_daemon_conf *conf, int argc, char *const argv [], int *d
break;
case ARG_FILE:
- case 'F':
- pa_strbuf_printf(buf, ".include %s\n", optarg);
+ case 'F': {
+ char *p;
+ pa_strbuf_printf(buf, ".include %s\n", p = pa_make_path_absolute(optarg));
+ pa_xfree(p);
break;
+ }
case 'C':
pa_strbuf_puts(buf, "load-module module-cli exit_on_eof=1\n");
diff --git a/src/daemon/cpulimit.c b/src/daemon/cpulimit.c
index d4ac1d86..ab212129 100644
--- a/src/daemon/cpulimit.c
+++ b/src/daemon/cpulimit.c
@@ -30,6 +30,7 @@
#include <pulsecore/core-util.h>
#include <pulsecore/core-error.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
#include "cpulimit.h"
@@ -38,7 +39,6 @@
#include <errno.h>
#include <stdio.h>
#include <string.h>
-#include <assert.h>
#include <sys/time.h>
#include <unistd.h>
#include <signal.h>
@@ -92,23 +92,18 @@ static enum {
/* Reset the SIGXCPU timer to the next t seconds */
static void reset_cpu_time(int t) {
- int r;
long n;
struct rlimit rl;
struct rusage ru;
/* Get the current CPU time of the current process */
- r = getrusage(RUSAGE_SELF, &ru);
- assert(r >= 0);
+ pa_assert_se(getrusage(RUSAGE_SELF, &ru) >= 0);
n = ru.ru_utime.tv_sec + ru.ru_stime.tv_sec + t;
-
- r = getrlimit(RLIMIT_CPU, &rl);
- assert(r >= 0);
+ pa_assert_se(getrlimit(RLIMIT_CPU, &rl) >= 0);
rl.rlim_cur = n;
- r = setrlimit(RLIMIT_CPU, &rl);
- assert(r >= 0);
+ pa_assert_se(setrlimit(RLIMIT_CPU, &rl) >= 0);
}
/* A simple, thread-safe puts() work-alike */
@@ -118,7 +113,7 @@ static void write_err(const char *p) {
/* The signal handler, called on every SIGXCPU */
static void signal_handler(int sig) {
- assert(sig == SIGXCPU);
+ pa_assert(sig == SIGXCPU);
if (phase == PHASE_IDLE) {
time_t now;
@@ -130,7 +125,7 @@ static void signal_handler(int sig) {
time(&now);
#ifdef PRINT_CPU_LOAD
- snprintf(t, sizeof(t), "Using %0.1f%% CPU\n", (double)CPUTIME_INTERVAL_SOFT/(now-last_time)*100);
+ pa_snprintf(t, sizeof(t), "Using %0.1f%% CPU\n", (double)CPUTIME_INTERVAL_SOFT/(now-last_time)*100);
write_err(t);
#endif
@@ -160,7 +155,12 @@ static void signal_handler(int sig) {
/* Callback for IO events on the FIFO */
static void callback(pa_mainloop_api*m, pa_io_event*e, int fd, pa_io_event_flags_t f, void *userdata) {
char c;
- assert(m && e && f == PA_IO_EVENT_INPUT && e == io_event && fd == the_pipe[0]);
+ pa_assert(m);
+ pa_assert(e);
+ pa_assert(f == PA_IO_EVENT_INPUT);
+ pa_assert(e == io_event);
+ pa_assert(fd == the_pipe[0]);
+
pa_read(the_pipe[0], &c, sizeof(c), NULL);
m->quit(m, 1); /* Quit the main loop */
}
@@ -168,7 +168,13 @@ static void callback(pa_mainloop_api*m, pa_io_event*e, int fd, pa_io_event_flags
/* Initializes CPU load limiter */
int pa_cpu_limit_init(pa_mainloop_api *m) {
struct sigaction sa;
- assert(m && !api && !io_event && the_pipe[0] == -1 && the_pipe[1] == -1 && !installed);
+
+ pa_assert(m);
+ pa_assert(!api);
+ pa_assert(!io_event);
+ pa_assert(the_pipe[0] == -1);
+ pa_assert(the_pipe[1] == -1);
+ pa_assert(!installed);
time(&last_time);
@@ -178,10 +184,10 @@ int pa_cpu_limit_init(pa_mainloop_api *m) {
return -1;
}
- pa_make_nonblock_fd(the_pipe[0]);
- pa_make_nonblock_fd(the_pipe[1]);
- pa_fd_set_cloexec(the_pipe[0], 1);
- pa_fd_set_cloexec(the_pipe[1], 1);
+ pa_make_fd_nonblock(the_pipe[0]);
+ pa_make_fd_nonblock(the_pipe[1]);
+ pa_make_fd_cloexec(the_pipe[0]);
+ pa_make_fd_cloexec(the_pipe[1]);
api = m;
io_event = api->io_new(m, the_pipe[0], PA_IO_EVENT_INPUT, callback, NULL);
@@ -208,24 +214,18 @@ int pa_cpu_limit_init(pa_mainloop_api *m) {
/* Shutdown CPU load limiter */
void pa_cpu_limit_done(void) {
- int r;
if (io_event) {
- assert(api);
+ pa_assert(api);
api->io_free(io_event);
io_event = NULL;
api = NULL;
}
- if (the_pipe[0] >= 0)
- close(the_pipe[0]);
- if (the_pipe[1] >= 0)
- close(the_pipe[1]);
- the_pipe[0] = the_pipe[1] = -1;
+ pa_close_pipe(the_pipe);
if (installed) {
- r = sigaction(SIGXCPU, &sigaction_prev, NULL);
- assert(r >= 0);
+ pa_assert_se(sigaction(SIGXCPU, &sigaction_prev, NULL) >= 0);
installed = 0;
}
}
diff --git a/src/daemon/daemon-conf.c b/src/daemon/daemon-conf.c
index 8cab327f..920e3717 100644
--- a/src/daemon/daemon-conf.c
+++ b/src/daemon/daemon-conf.c
@@ -29,7 +29,6 @@
#include <errno.h>
#include <stdio.h>
#include <string.h>
-#include <assert.h>
#include <unistd.h>
#include <pulse/xmalloc.h>
@@ -39,19 +38,14 @@
#include <pulsecore/strbuf.h>
#include <pulsecore/conf-parser.h>
#include <pulsecore/resampler.h>
+#include <pulsecore/macro.h>
#include "daemon-conf.h"
-#ifndef OS_IS_WIN32
-# define PATH_SEP "/"
-#else
-# define PATH_SEP "\\"
-#endif
-
-#define DEFAULT_SCRIPT_FILE PA_DEFAULT_CONFIG_DIR PATH_SEP "default.pa"
-#define DEFAULT_SCRIPT_FILE_USER PATH_SEP "default.pa"
-#define DEFAULT_CONFIG_FILE PA_DEFAULT_CONFIG_DIR PATH_SEP "daemon.conf"
-#define DEFAULT_CONFIG_FILE_USER PATH_SEP "daemon.conf"
+#define DEFAULT_SCRIPT_FILE PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "default.pa"
+#define DEFAULT_SCRIPT_FILE_USER PA_PATH_SEP "default.pa"
+#define DEFAULT_CONFIG_FILE PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "daemon.conf"
+#define DEFAULT_CONFIG_FILE_USER PA_PATH_SEP "daemon.conf"
#define ENV_SCRIPT_FILE "PULSE_SCRIPT"
#define ENV_CONFIG_FILE "PULSE_CONFIG"
@@ -72,31 +66,34 @@ static const pa_daemon_conf default_conf = {
.default_script_file = NULL,
.log_target = PA_LOG_SYSLOG,
.log_level = PA_LOG_NOTICE,
- .resample_method = PA_RESAMPLER_SRC_SINC_FASTEST,
+ .resample_method = PA_RESAMPLER_AUTO,
.config_file = NULL,
.use_pid_file = 1,
.system_instance = 0,
.no_cpu_limit = 0,
- .disable_shm = 0
+ .disable_shm = 0,
+ .default_n_fragments = 4,
+ .default_fragment_size_msec = 25,
+ .default_sample_spec = { .format = PA_SAMPLE_S16NE, .rate = 44100, .channels = 2 }
#ifdef HAVE_SYS_RESOURCE_H
, .rlimit_as = { .value = 0, .is_set = 0 },
.rlimit_core = { .value = 0, .is_set = 0 },
.rlimit_data = { .value = 0, .is_set = 0 },
.rlimit_fsize = { .value = 0, .is_set = 0 },
- .rlimit_nofile = { .value = 200, .is_set = 1 },
+ .rlimit_nofile = { .value = 256, .is_set = 1 },
.rlimit_stack = { .value = 0, .is_set = 0 }
#ifdef RLIMIT_NPROC
, .rlimit_nproc = { .value = 0, .is_set = 0 }
#endif
#ifdef RLIMIT_MEMLOCK
- , .rlimit_memlock = { .value = 0, .is_set = 1 }
+ , .rlimit_memlock = { .value = 16384, .is_set = 1 }
#endif
#endif
};
pa_daemon_conf* pa_daemon_conf_new(void) {
FILE *f;
- pa_daemon_conf *c = pa_xmemdup(&default_conf, sizeof(default_conf));
+ pa_daemon_conf *c = pa_xnewdup(pa_daemon_conf, &default_conf, 1);
if ((f = pa_open_config_file(DEFAULT_SCRIPT_FILE, DEFAULT_SCRIPT_FILE_USER, ENV_SCRIPT_FILE, &c->default_script_file, "r")))
fclose(f);
@@ -106,7 +103,7 @@ pa_daemon_conf* pa_daemon_conf_new(void) {
}
void pa_daemon_conf_free(pa_daemon_conf *c) {
- assert(c);
+ pa_assert(c);
pa_xfree(c->script_commands);
pa_xfree(c->dl_search_path);
pa_xfree(c->default_script_file);
@@ -115,7 +112,8 @@ void pa_daemon_conf_free(pa_daemon_conf *c) {
}
int pa_daemon_conf_set_log_target(pa_daemon_conf *c, const char *string) {
- assert(c && string);
+ pa_assert(c);
+ pa_assert(string);
if (!strcmp(string, "auto"))
c->auto_log_target = 1;
@@ -133,7 +131,8 @@ int pa_daemon_conf_set_log_target(pa_daemon_conf *c, const char *string) {
int pa_daemon_conf_set_log_level(pa_daemon_conf *c, const char *string) {
uint32_t u;
- assert(c && string);
+ pa_assert(c);
+ pa_assert(string);
if (pa_atou(string, &u) >= 0) {
if (u >= PA_LOG_LEVEL_MAX)
@@ -158,7 +157,8 @@ int pa_daemon_conf_set_log_level(pa_daemon_conf *c, const char *string) {
int pa_daemon_conf_set_resample_method(pa_daemon_conf *c, const char *string) {
int m;
- assert(c && string);
+ pa_assert(c);
+ pa_assert(string);
if ((m = pa_parse_resample_method(string)) < 0)
return -1;
@@ -169,7 +169,11 @@ int pa_daemon_conf_set_resample_method(pa_daemon_conf *c, const char *string) {
static int parse_log_target(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
pa_daemon_conf *c = data;
- assert(filename && lvalue && rvalue && data);
+
+ pa_assert(filename);
+ pa_assert(lvalue);
+ pa_assert(rvalue);
+ pa_assert(data);
if (pa_daemon_conf_set_log_target(c, rvalue) < 0) {
pa_log("[%s:%u] Invalid log target '%s'.", filename, line, rvalue);
@@ -181,7 +185,11 @@ static int parse_log_target(const char *filename, unsigned line, const char *lva
static int parse_log_level(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
pa_daemon_conf *c = data;
- assert(filename && lvalue && rvalue && data);
+
+ pa_assert(filename);
+ pa_assert(lvalue);
+ pa_assert(rvalue);
+ pa_assert(data);
if (pa_daemon_conf_set_log_level(c, rvalue) < 0) {
pa_log("[%s:%u] Invalid log level '%s'.", filename, line, rvalue);
@@ -193,10 +201,14 @@ static int parse_log_level(const char *filename, unsigned line, const char *lval
static int parse_resample_method(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
pa_daemon_conf *c = data;
- assert(filename && lvalue && rvalue && data);
+
+ pa_assert(filename);
+ pa_assert(lvalue);
+ pa_assert(rvalue);
+ pa_assert(data);
if (pa_daemon_conf_set_resample_method(c, rvalue) < 0) {
- pa_log("[%s:%u] Inavalid resample method '%s'.", filename, line, rvalue);
+ pa_log("[%s:%u] Invalid resample method '%s'.", filename, line, rvalue);
return -1;
}
@@ -206,10 +218,11 @@ static int parse_resample_method(const char *filename, unsigned line, const char
static int parse_rlimit(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
#ifdef HAVE_SYS_RESOURCE_H
struct pa_rlimit *r = data;
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(r);
+
+ pa_assert(filename);
+ pa_assert(lvalue);
+ pa_assert(rvalue);
+ pa_assert(r);
if (rvalue[strspn(rvalue, "\t ")] == 0) {
/* Empty string */
@@ -218,7 +231,7 @@ static int parse_rlimit(const char *filename, unsigned line, const char *lvalue,
} else {
int32_t k;
if (pa_atoi(rvalue, &k) < 0) {
- pa_log("[%s:%u] Inavalid rlimit '%s'.", filename, line, rvalue);
+ pa_log("[%s:%u] Invalid rlimit '%s'.", filename, line, rvalue);
return -1;
}
r->is_set = k >= 0;
@@ -231,43 +244,138 @@ static int parse_rlimit(const char *filename, unsigned line, const char *lvalue,
return 0;
}
+static int parse_sample_format(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
+ pa_daemon_conf *c = data;
+ pa_sample_format_t f;
+
+ pa_assert(filename);
+ pa_assert(lvalue);
+ pa_assert(rvalue);
+ pa_assert(data);
+
+ if ((f = pa_parse_sample_format(rvalue)) < 0) {
+ pa_log("[%s:%u] Invalid sample format '%s'.", filename, line, rvalue);
+ return -1;
+ }
+
+ c->default_sample_spec.format = f;
+ return 0;
+}
+
+static int parse_sample_rate(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
+ pa_daemon_conf *c = data;
+ int32_t r;
+
+ pa_assert(filename);
+ pa_assert(lvalue);
+ pa_assert(rvalue);
+ pa_assert(data);
+
+ if (pa_atoi(rvalue, &r) < 0 || r > PA_RATE_MAX || r <= 0) {
+ pa_log("[%s:%u] Invalid sample rate '%s'.", filename, line, rvalue);
+ return -1;
+ }
+
+ c->default_sample_spec.rate = r;
+ return 0;
+}
+
+static int parse_sample_channels(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
+ pa_daemon_conf *c = data;
+ int32_t n;
+
+ pa_assert(filename);
+ pa_assert(lvalue);
+ pa_assert(rvalue);
+ pa_assert(data);
+
+ if (pa_atoi(rvalue, &n) < 0 || n > PA_CHANNELS_MAX || n <= 0) {
+ pa_log("[%s:%u] Invalid sample channels '%s'.", filename, line, rvalue);
+ return -1;
+ }
+
+ c->default_sample_spec.channels = (uint8_t) n;
+ return 0;
+}
+
+static int parse_fragments(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
+ pa_daemon_conf *c = data;
+ int32_t n;
+
+ pa_assert(filename);
+ pa_assert(lvalue);
+ pa_assert(rvalue);
+ pa_assert(data);
+
+ if (pa_atoi(rvalue, &n) < 0 || n < 2) {
+ pa_log("[%s:%u] Invalid number of fragments '%s'.", filename, line, rvalue);
+ return -1;
+ }
+
+ c->default_n_fragments = (unsigned) n;
+ return 0;
+}
+
+static int parse_fragment_size_msec(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
+ pa_daemon_conf *c = data;
+ int32_t n;
+
+ pa_assert(filename);
+ pa_assert(lvalue);
+ pa_assert(rvalue);
+ pa_assert(data);
+
+ if (pa_atoi(rvalue, &n) < 0 || n < 1) {
+ pa_log("[%s:%u] Invalid fragment size '%s'.", filename, line, rvalue);
+ return -1;
+ }
+
+ c->default_fragment_size_msec = (unsigned) n;
+ return 0;
+}
+
int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
int r = -1;
FILE *f = NULL;
pa_config_item table[] = {
- { "daemonize", pa_config_parse_bool, NULL },
- { "fail", pa_config_parse_bool, NULL },
- { "high-priority", pa_config_parse_bool, NULL },
- { "disallow-module-loading", pa_config_parse_bool, NULL },
- { "exit-idle-time", pa_config_parse_int, NULL },
- { "module-idle-time", pa_config_parse_int, NULL },
- { "scache-idle-time", pa_config_parse_int, NULL },
- { "dl-search-path", pa_config_parse_string, NULL },
- { "default-script-file", pa_config_parse_string, NULL },
- { "log-target", parse_log_target, NULL },
- { "log-level", parse_log_level, NULL },
- { "verbose", parse_log_level, NULL },
- { "resample-method", parse_resample_method, NULL },
- { "use-pid-file", pa_config_parse_bool, NULL },
- { "system-instance", pa_config_parse_bool, NULL },
- { "no-cpu-limit", pa_config_parse_bool, NULL },
- { "disable-shm", pa_config_parse_bool, NULL },
+ { "daemonize", pa_config_parse_bool, NULL },
+ { "fail", pa_config_parse_bool, NULL },
+ { "high-priority", pa_config_parse_bool, NULL },
+ { "disallow-module-loading", pa_config_parse_bool, NULL },
+ { "exit-idle-time", pa_config_parse_int, NULL },
+ { "module-idle-time", pa_config_parse_int, NULL },
+ { "scache-idle-time", pa_config_parse_int, NULL },
+ { "dl-search-path", pa_config_parse_string, NULL },
+ { "default-script-file", pa_config_parse_string, NULL },
+ { "log-target", parse_log_target, NULL },
+ { "log-level", parse_log_level, NULL },
+ { "verbose", parse_log_level, NULL },
+ { "resample-method", parse_resample_method, NULL },
+ { "use-pid-file", pa_config_parse_bool, NULL },
+ { "system-instance", pa_config_parse_bool, NULL },
+ { "no-cpu-limit", pa_config_parse_bool, NULL },
+ { "disable-shm", pa_config_parse_bool, NULL },
+ { "default-sample-format", parse_sample_format, NULL },
+ { "default-sample-rate", parse_sample_rate, NULL },
+ { "default-sample-channels", parse_sample_channels, NULL },
+ { "default-fragments", parse_fragments, NULL },
+ { "default-fragment-size-msec", parse_fragment_size_msec, NULL },
#ifdef HAVE_SYS_RESOURCE_H
- { "rlimit-as", parse_rlimit, NULL },
- { "rlimit-core", parse_rlimit, NULL },
- { "rlimit-data", parse_rlimit, NULL },
- { "rlimit-fsize", parse_rlimit, NULL },
- { "rlimit-nofile", parse_rlimit, NULL },
- { "rlimit-stack", parse_rlimit, NULL },
+ { "rlimit-as", parse_rlimit, NULL },
+ { "rlimit-core", parse_rlimit, NULL },
+ { "rlimit-data", parse_rlimit, NULL },
+ { "rlimit-fsize", parse_rlimit, NULL },
+ { "rlimit-nofile", parse_rlimit, NULL },
+ { "rlimit-stack", parse_rlimit, NULL },
#ifdef RLIMIT_NPROC
- { "rlimit-nproc", parse_rlimit, NULL },
+ { "rlimit-nproc", parse_rlimit, NULL },
#endif
#ifdef RLIMIT_MEMLOCK
- { "rlimit-memlock", parse_rlimit, NULL },
+ { "rlimit-memlock", parse_rlimit, NULL },
#endif
#endif
- { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
};
table[0].data = &c->daemonize;
@@ -287,25 +395,29 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
table[14].data = &c->system_instance;
table[15].data = &c->no_cpu_limit;
table[16].data = &c->disable_shm;
+ table[17].data = c;
+ table[18].data = c;
+ table[19].data = c;
+ table[20].data = c;
+ table[21].data = c;
#ifdef HAVE_SYS_RESOURCE_H
- table[17].data = &c->rlimit_as;
- table[18].data = &c->rlimit_core;
- table[19].data = &c->rlimit_data;
- table[20].data = &c->rlimit_fsize;
- table[21].data = &c->rlimit_nofile;
- table[22].data = &c->rlimit_stack;
+ table[22].data = &c->rlimit_as;
+ table[23].data = &c->rlimit_core;
+ table[24].data = &c->rlimit_data;
+ table[25].data = &c->rlimit_fsize;
+ table[26].data = &c->rlimit_nofile;
+ table[27].data = &c->rlimit_stack;
#ifdef RLIMIT_NPROC
- table[23].data = &c->rlimit_nproc;
+ table[28].data = &c->rlimit_nproc;
#endif
#ifdef RLIMIT_MEMLOCK
#ifndef RLIMIT_NPROC
#error "Houston, we have a numbering problem!"
#endif
- table[24].data = &c->rlimit_memlock;
+ table[29].data = &c->rlimit_memlock;
#endif
#endif
-
pa_xfree(c->config_file);
c->config_file = NULL;
@@ -314,7 +426,7 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
pa_open_config_file(DEFAULT_CONFIG_FILE, DEFAULT_CONFIG_FILE_USER, ENV_CONFIG_FILE, &c->config_file, "r");
if (!f && errno != ENOENT) {
- pa_log("WARNING: failed to open configuration file '%s': %s", c->config_file, pa_cstrerror(errno));
+ pa_log_warn("Failed to open configuration file '%s': %s", c->config_file, pa_cstrerror(errno));
goto finish;
}
@@ -351,12 +463,16 @@ static const char* const log_level_to_string[] = {
};
char *pa_daemon_conf_dump(pa_daemon_conf *c) {
- pa_strbuf *s = pa_strbuf_new();
+ pa_strbuf *s;
+
+ pa_assert(c);
+
+ s = pa_strbuf_new();
if (c->config_file)
pa_strbuf_printf(s, "### Read from configuration file: %s ###\n", c->config_file);
- assert(c->log_level <= PA_LOG_LEVEL_MAX);
+ pa_assert(c->log_level <= PA_LOG_LEVEL_MAX);
pa_strbuf_printf(s, "daemonize = %i\n", !!c->daemonize);
pa_strbuf_printf(s, "fail = %i\n", !!c->fail);
@@ -373,7 +489,12 @@ char *pa_daemon_conf_dump(pa_daemon_conf *c) {
pa_strbuf_printf(s, "use-pid-file = %i\n", c->use_pid_file);
pa_strbuf_printf(s, "system-instance = %i\n", !!c->system_instance);
pa_strbuf_printf(s, "no-cpu-limit = %i\n", !!c->no_cpu_limit);
- pa_strbuf_printf(s, "disable_shm = %i\n", !!c->disable_shm);
+ pa_strbuf_printf(s, "disable-shm = %i\n", !!c->disable_shm);
+ pa_strbuf_printf(s, "default-sample-format = %s\n", pa_sample_format_to_string(c->default_sample_spec.format));
+ pa_strbuf_printf(s, "default-sample-rate = %u\n", c->default_sample_spec.rate);
+ pa_strbuf_printf(s, "default-sample-channels = %u\n", c->default_sample_spec.channels);
+ pa_strbuf_printf(s, "default-fragments = %u\n", c->default_n_fragments);
+ pa_strbuf_printf(s, "default-fragment-size-msec = %u\n", c->default_fragment_size_msec);
#ifdef HAVE_SYS_RESOURCE_H
pa_strbuf_printf(s, "rlimit-as = %li\n", c->rlimit_as.is_set ? (long int) c->rlimit_as.value : -1);
pa_strbuf_printf(s, "rlimit-core = %li\n", c->rlimit_core.is_set ? (long int) c->rlimit_core.value : -1);
diff --git a/src/daemon/daemon-conf.h b/src/daemon/daemon-conf.h
index 4843a610..4d37861d 100644
--- a/src/daemon/daemon-conf.h
+++ b/src/daemon/daemon-conf.h
@@ -26,6 +26,7 @@
***/
#include <pulsecore/log.h>
+#include <pulse/sample.h>
#ifdef HAVE_SYS_RESOURCE_H
#include <sys/resource.h>
@@ -39,7 +40,9 @@ typedef enum pa_daemon_conf_cmd {
PA_CMD_DUMP_CONF,
PA_CMD_DUMP_MODULES,
PA_CMD_KILL,
- PA_CMD_CHECK
+ PA_CMD_CHECK,
+ PA_CMD_DUMP_RESAMPLE_METHODS,
+ PA_CMD_CLEANUP_SHM
} pa_daemon_conf_cmd_t;
#ifdef HAVE_SYS_RESOURCE_H
@@ -80,6 +83,8 @@ typedef struct pa_daemon_conf {
#endif
#endif
+ unsigned default_n_fragments, default_fragment_size_msec;
+ pa_sample_spec default_sample_spec;
} pa_daemon_conf;
/* Allocate a new structure and fill it with sane defaults */
diff --git a/src/daemon/daemon.conf.in b/src/daemon/daemon.conf.in
index 29b22a42..2132bf3d 100644
--- a/src/daemon/daemon.conf.in
+++ b/src/daemon/daemon.conf.in
@@ -92,10 +92,19 @@
; rlimit-core = -1
; rlimit-data = -1
; rlimit-fsize = -1
-; rlimit-nofile = 200
+; rlimit-nofile = 256
; rlimit-stack = -1
; rlimit-nproc = -1
-; rlimit-memlock = 25
+; rlimit-memlock = 16384
## Disable shared memory data transfer
; disable-shm = 0
+
+## Default sample format
+; default-sample-format = s16le
+; default-sample-rate = 44100
+; default-sample-channels = 2
+
+## Default fragment settings, for device drivers that need this
+; default-fragments = 4
+; default-fragment-size-msec = 25
diff --git a/src/daemon/default.pa.in b/src/daemon/default.pa.in
index af2a6789..597993c4 100755
--- a/src/daemon/default.pa.in
+++ b/src/daemon/default.pa.in
@@ -1,5 +1,4 @@
-#!@PA_BINARY@ -nF
-
+#!@PA_BINARY@ -nF
#
# This file is part of PulseAudio.
#
@@ -17,8 +16,19 @@
# along with PulseAudio; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+.nofail
+
+### Load something into the sample cache
+#load-sample-lazy x11-bell /usr/share/sounds/gtk-events/activate.wav
+load-sample-lazy pulse-hotplug /usr/share/sounds/startup3.wav
+#load-sample-lazy pulse-coldplug /usr/share/sounds/startup3.wav
+#load-sample-lazy pulse-access /usr/share/sounds/generic.wav
-### Load audio drivers statically
+.fail
+
+### Load audio drivers statically (it's probably better to not load
+### these drivers manually, but instead use module-hal-detect --
+### see below -- for doing this automatically)
#load-module module-alsa-sink
#load-module module-alsa-source device=hw:1,0
#load-module module-oss device="/dev/dsp" sink_name=output source_name=input
@@ -27,19 +37,13 @@
#load-module module-pipe-sink
### Automatically load driver modules depending on the hardware available
-@HAVE_HAL_TRUE@load-module module-hal-detect
-
+.ifexists @PA_DLSEARCHPATH@/module-hal-detect@PA_SOEXT@
+load-module module-hal-detect
+.else
### Alternatively use the static hardware detection module (for systems that
-### lack HAL support
-@HAVE_HAL_FALSE@load-module module-detect
-
-### Load audio drivers automatically on access
-#add-autoload-sink output module-oss device="/dev/dsp" sink_name=output source_name=input
-#add-autoload-source input module-oss device="/dev/dsp" sink_name=output source_name=input
-#add-autoload-sink output module-oss-mmap device="/dev/dsp" sink_name=output source_name=input
-#add-autoload-source input module-oss-mmap device="/dev/dsp" sink_name=output source_name=input
-#add-autoload-sink output module-alsa-sink sink_name=output
-#add-autoload-source input module-alsa-source source_name=input
+### lack HAL support)
+load-module module-detect
+.endif
### Load several protocols
load-module module-esound-protocol-unix
@@ -61,27 +65,36 @@ load-module module-native-protocol-unix
### Automatically restore the volume of playback streams
load-module module-volume-restore
+### Automatically restore the default sink/source when changed by the user during runtime
+load-module module-default-device-restore
+
### Automatically move streams to the default sink if the sink they are
### connected to dies, similar for sources
load-module module-rescue-streams
-### Make some devices default
-#set-default-sink output
-#set-default-source input
-
-.nofail
-
-### Load something to the sample cache
-load-sample x11-bell /usr/share/sounds/gtk-events/activate.wav
-#load-sample-dir-lazy /usr/share/sounds/*.wav
+### Automatically suspend sinks/sources that become idle for too long
+load-module module-suspend-on-idle
### Load X11 bell module
-load-module module-x11-bell sample=x11-bell
+#load-module module-x11-bell sample=x11-bell
### Publish connection data in the X11 root window
+.ifexists @PA_DLSEARCHPATH@/module-x11-publish@PA_SOEXT@
load-module module-x11-publish
+.endif
+
+### Register ourselves in the X11 session manager
+# Deactivated by default, to avoid deadlock when PA is started as esd from gnome-session
+# Instead we load this via /etc/xdg/autostart/ and "pactl load-module" now
+# load-module module-x11-xsmp
### Load additional modules from GConf settings. This can be configured with the paprefs tool.
### Please keep in mind that the modules configured by paprefs might conflict with manually
### loaded modules.
+.ifexists @PA_DLSEARCHPATH@/module-gconf@PA_SOEXT@
load-module module-gconf
+.endif
+
+### Make some devices default
+#set-default-sink output
+#set-default-source input
diff --git a/src/daemon/dumpmodules.c b/src/daemon/dumpmodules.c
index 6743622a..ad7fab20 100644
--- a/src/daemon/dumpmodules.c
+++ b/src/daemon/dumpmodules.c
@@ -28,26 +28,30 @@
#include <string.h>
#include <getopt.h>
-#include <assert.h>
#include <stdio.h>
#include <ltdl.h>
#include <pulse/util.h>
#include <pulsecore/modinfo.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
#include "dumpmodules.h"
#define PREFIX "module-"
static void short_info(const char *name, PA_GCC_UNUSED const char *path, pa_modinfo *i) {
- assert(name && i);
+ pa_assert(name);
+ pa_assert(i);
+
printf("%-40s%s\n", name, i->description ? i->description : "n/a");
}
static void long_info(const char *name, const char *path, pa_modinfo *i) {
static int nl = 0;
- assert(name && i);
+ pa_assert(name);
+ pa_assert(i);
if (nl)
printf("\n");
@@ -76,6 +80,8 @@ static void long_info(const char *name, const char *path, pa_modinfo *i) {
static void show_info(const char *name, const char *path, void (*info)(const char *name, const char *path, pa_modinfo*i)) {
pa_modinfo *i;
+ pa_assert(name);
+
if ((i = pa_modinfo_get_by_name(path ? path : name))) {
info(name, path, i);
pa_modinfo_free(i);
@@ -93,7 +99,7 @@ static int is_preloaded(const char *name) {
if (l->address)
continue;
- snprintf(buf, sizeof(buf), "%s", l->name);
+ pa_snprintf(buf, sizeof(buf), "%s", l->name);
if ((e = strrchr(buf, '.')))
*e = 0;
@@ -121,6 +127,8 @@ static int callback(const char *path, lt_ptr data) {
}
void pa_dump_modules(pa_daemon_conf *c, int argc, char * const argv[]) {
+ pa_assert(c);
+
if (argc > 0) {
int i;
for (i = 0; i < argc; i++)
@@ -137,7 +145,7 @@ void pa_dump_modules(pa_daemon_conf *c, int argc, char * const argv[]) {
if (strlen(l->name) <= sizeof(PREFIX)-1 || strncmp(l->name, PREFIX, sizeof(PREFIX)-1))
continue;
- snprintf(buf, sizeof(buf), "%s", l->name);
+ pa_snprintf(buf, sizeof(buf), "%s", l->name);
if ((e = strrchr(buf, '.')))
*e = 0;
diff --git a/src/daemon/ltdl-bind-now.c b/src/daemon/ltdl-bind-now.c
new file mode 100644
index 00000000..6025c6e3
--- /dev/null
+++ b/src/daemon/ltdl-bind-now.c
@@ -0,0 +1,160 @@
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+ Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 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
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#if HAVE_SYS_DL_H
+#include <sys/dl.h>
+#endif
+
+#include <ltdl.h>
+
+#include <pulsecore/macro.h>
+#include <pulsecore/mutex.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/log.h>
+
+#include "ltdl-bind-now.h"
+
+#ifdef RTLD_NOW
+#define PA_BIND_NOW RTLD_NOW
+#elif defined(DL_NOW)
+#define PA_BIND_NOW DL_NOW
+#else
+#undef PA_BIND_NOW
+#endif
+
+static pa_mutex *libtool_mutex = NULL;
+
+PA_STATIC_TLS_DECLARE_NO_FREE(libtool_tls);
+
+static void libtool_lock(void) {
+ pa_mutex_lock(libtool_mutex);
+}
+
+static void libtool_unlock(void) {
+ pa_mutex_unlock(libtool_mutex);
+}
+
+static void libtool_set_error(const char *error) {
+ PA_STATIC_TLS_SET(libtool_tls, (char*) error);
+}
+
+static const char *libtool_get_error(void) {
+ return PA_STATIC_TLS_GET(libtool_tls);
+}
+
+#ifdef PA_BIND_NOW
+
+/*
+ To avoid lazy relocations during runtime in our RT threads we add
+ our own shared object loader with uses RTLD_NOW if it is
+ available. The standard ltdl loader prefers RTLD_LAZY.
+
+ Please note that this loader doesn't have any influence on
+ relocations on any libraries that are already loaded into our
+ process, i.e. because the pulseaudio binary links directly to
+ them. To disable lazy relocations for those libraries it is possible
+ to set $LT_BIND_NOW before starting the pulsaudio binary.
+*/
+
+static lt_module bind_now_open(lt_user_data d, const char *fname) {
+ lt_module m;
+
+ pa_assert(fname);
+
+ if (!(m = dlopen(fname, PA_BIND_NOW))) {
+ libtool_set_error(dlerror());
+ return NULL;
+ }
+
+ return m;
+}
+
+static int bind_now_close(lt_user_data d, lt_module m) {
+
+ pa_assert(m);
+
+ if (dlclose(m) != 0){
+ libtool_set_error(dlerror());
+ return 1;
+ }
+
+ return 0;
+}
+
+static lt_ptr bind_now_find_sym(lt_user_data d, lt_module m, const char *symbol) {
+ lt_ptr ptr;
+
+ pa_assert(m);
+ pa_assert(symbol);
+
+ if (!(ptr = dlsym(m, symbol))) {
+ libtool_set_error(dlerror());
+ return NULL;
+ }
+
+ return ptr;
+}
+
+#endif
+
+void pa_ltdl_init(void) {
+
+#ifdef PA_BIND_NOW
+ lt_dlloader *place;
+ static const struct lt_user_dlloader loader = {
+ .module_open = bind_now_open,
+ .module_close = bind_now_close,
+ .find_sym = bind_now_find_sym
+ };
+#endif
+
+ pa_assert_se(lt_dlinit() == 0);
+ pa_assert_se(libtool_mutex = pa_mutex_new(TRUE, FALSE));
+ pa_assert_se(lt_dlmutex_register(libtool_lock, libtool_unlock, libtool_set_error, libtool_get_error) == 0);
+
+#ifdef PA_BIND_NOW
+
+ if (!(place = lt_dlloader_find("dlopen")))
+ place = lt_dlloader_next(NULL);
+
+ /* Add our BIND_NOW loader as the default module loader. */
+ if (lt_dlloader_add(place, &loader, "bind-now-loader") != 0)
+ pa_log_warn("Failed to add bind-now-loader.");
+#endif
+}
+
+void pa_ltdl_done(void) {
+ pa_assert_se(lt_dlexit() == 0);
+ pa_mutex_free(libtool_mutex);
+ libtool_mutex = NULL;
+}
+
diff --git a/src/daemon/ltdl-bind-now.h b/src/daemon/ltdl-bind-now.h
new file mode 100644
index 00000000..e19c7bc1
--- /dev/null
+++ b/src/daemon/ltdl-bind-now.h
@@ -0,0 +1,32 @@
+#ifndef foopulsecoreltdlbindnowhfoo
+#define foopulsecoreltdlbindnowhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 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 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.
+***/
+
+
+void pa_ltdl_init(void);
+void pa_ltdl_done(void);
+
+#endif
+
diff --git a/src/daemon/main.c b/src/daemon/main.c
index 91cc3a2f..6c9f6627 100644
--- a/src/daemon/main.c
+++ b/src/daemon/main.c
@@ -33,7 +33,6 @@
#include <stdio.h>
#include <signal.h>
#include <stddef.h>
-#include <assert.h>
#include <ltdl.h>
#include <limits.h>
#include <fcntl.h>
@@ -59,13 +58,16 @@
#include <tcpd.h>
#endif
-#include "../pulsecore/winsock.h"
+#ifdef HAVE_DBUS
+#include <dbus/dbus.h>
+#endif
#include <pulse/mainloop.h>
#include <pulse/mainloop-signal.h>
#include <pulse/timeval.h>
#include <pulse/xmalloc.h>
+#include <pulsecore/winsock.h>
#include <pulsecore/core-error.h>
#include <pulsecore/core.h>
#include <pulsecore/memblock.h>
@@ -78,12 +80,20 @@
#include <pulsecore/pid.h>
#include <pulsecore/namereg.h>
#include <pulsecore/random.h>
+#include <pulsecore/rtsig.h>
+#include <pulsecore/rtclock.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/mutex.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/once.h>
+#include <pulsecore/shm.h>
#include "cmdline.h"
#include "cpulimit.h"
#include "daemon-conf.h"
#include "dumpmodules.h"
#include "caps.h"
+#include "ltdl-bind-now.h"
#ifdef HAVE_LIBWRAP
/* Only one instance of these variables */
@@ -120,7 +130,7 @@ static void message_cb(pa_mainloop_api*a, pa_time_event*e, PA_GCC_UNUSED const s
#endif
static void signal_callback(pa_mainloop_api*m, PA_GCC_UNUSED pa_signal_event *e, int sig, void *userdata) {
- pa_log_info("Got signal %s.", pa_strsignal(sig));
+ pa_log_info("Got signal %s.", pa_sig2str(sig));
switch (sig) {
#ifdef SIGUSR1
@@ -153,14 +163,6 @@ static void signal_callback(pa_mainloop_api*m, PA_GCC_UNUSED pa_signal_event *e,
}
}
-static void close_pipe(int p[2]) {
- if (p[0] != -1)
- close(p[0]);
- if (p[1] != -1)
- close(p[1]);
- p[0] = p[1] = -1;
-}
-
#define set_env(key, value) putenv(pa_sprintf_malloc("%s=%s", (key), (value)))
#if defined(HAVE_PWD_H) && defined(HAVE_GRP_H)
@@ -281,7 +283,7 @@ static int create_runtime_dir(void) {
static void set_one_rlimit(const pa_rlimit *r, int resource, const char *name) {
struct rlimit rl;
- assert(r);
+ pa_assert(r);
if (!r->is_set)
return;
@@ -313,13 +315,11 @@ int main(int argc, char *argv[]) {
pa_strbuf *buf = NULL;
pa_daemon_conf *conf = NULL;
pa_mainloop *mainloop = NULL;
-
char *s;
- int r, retval = 1, d = 0;
+ int r = 0, retval = 1, d = 0;
int daemon_pipe[2] = { -1, -1 };
int suid_root, real_root;
int valid_pid_file = 0;
-
gid_t gid = (gid_t) -1;
#ifdef OS_IS_WIN32
@@ -327,6 +327,23 @@ int main(int argc, char *argv[]) {
struct timeval tv;
#endif
+
+#if defined(__linux__) && defined(__OPTIMIZE__)
+ /*
+ Disable lazy relocations to make usage of external libraries
+ more deterministic for our RT threads. We abuse __OPTIMIZE__ as
+ a check whether we are a debug build or not.
+ */
+
+ if (!getenv("LD_BIND_NOW")) {
+ putenv(pa_xstrdup("LD_BIND_NOW=1"));
+
+ /* We have to execute ourselves, because the libc caches the
+ * value of $LD_BIND_NOW on initialization. */
+ pa_assert_se(execv("/proc/self/exe", argv) == 0);
+ }
+#endif
+
#ifdef HAVE_GETUID
real_root = getuid() == 0;
suid_root = !real_root && geteuid() == 0;
@@ -336,16 +353,26 @@ int main(int argc, char *argv[]) {
#endif
if (suid_root) {
- if (pa_limit_caps() > 0)
- /* We managed to drop capabilities except the needed
- * ones. Hence we can drop the uid. */
- pa_drop_root();
+ /* Drop all capabilities except CAP_SYS_NICE */
+ pa_limit_caps();
+
+ /* Drop priviliges, but keep CAP_SYS_NICE */
+ pa_drop_root();
+
+ /* After dropping root, the effective set is reset, hence,
+ * let's raise it again */
+ pa_limit_caps();
+
+ /* When capabilities are not supported we will not be able to
+ * aquire RT sched anymore. But yes, that's the way it is. It
+ * is just too risky tun let PA run as root all the time. */
}
setlocale(LC_ALL, "");
- if (suid_root && (pa_own_uid_in_group(PA_REALTIME_GROUP, &gid) <= 0 || gid >= 1000)) {
- pa_log_warn("WARNING: called SUID root, but not in group '"PA_REALTIME_GROUP"'.");
+ if (suid_root && (pa_own_uid_in_group(PA_REALTIME_GROUP, &gid) <= 0)) {
+ pa_log_info("Warning: Called SUID root, but not in group '"PA_REALTIME_GROUP"'. "
+ "For enabling real-time scheduling please become a member of '"PA_REALTIME_GROUP"' , or increase the RLIMIT_RTPRIO user limit.");
pa_drop_caps();
pa_drop_root();
suid_root = real_root = 0;
@@ -353,8 +380,7 @@ int main(int argc, char *argv[]) {
LTDL_SET_PRELOADED_SYMBOLS();
- r = lt_dlinit();
- assert(r == 0);
+ pa_ltdl_init();
#ifdef OS_IS_WIN32
{
@@ -386,7 +412,7 @@ int main(int argc, char *argv[]) {
if (conf->high_priority && conf->cmd == PA_CMD_DAEMON)
pa_raise_priority();
- if (suid_root) {
+ if (suid_root && (conf->cmd != PA_CMD_DAEMON || !conf->high_priority)) {
pa_drop_caps();
pa_drop_root();
}
@@ -408,6 +434,16 @@ int main(int argc, char *argv[]) {
goto finish;
}
+ case PA_CMD_DUMP_RESAMPLE_METHODS: {
+ int i;
+
+ for (i = 0; i < PA_RESAMPLER_MAX; i++)
+ if (pa_resample_method_supported(i))
+ printf("%s\n", pa_resample_method_to_string(i));
+
+ goto finish;
+ }
+
case PA_CMD_HELP :
pa_cmdline_help(argv[0]);
retval = 0;
@@ -440,8 +476,15 @@ int main(int argc, char *argv[]) {
goto finish;
+ case PA_CMD_CLEANUP_SHM:
+
+ if (pa_shm_cleanup() >= 0)
+ retval = 0;
+
+ goto finish;
+
default:
- assert(conf->cmd == PA_CMD_DAEMON);
+ pa_assert(conf->cmd == PA_CMD_DAEMON);
}
if (real_root && !conf->system_instance) {
@@ -474,7 +517,7 @@ int main(int argc, char *argv[]) {
if (child != 0) {
/* Father */
- close(daemon_pipe[1]);
+ pa_assert_se(pa_close(daemon_pipe[1]) == 0);
daemon_pipe[1] = -1;
if (pa_loop_read(daemon_pipe[0], &retval, sizeof(retval), NULL) != sizeof(retval)) {
@@ -490,7 +533,7 @@ int main(int argc, char *argv[]) {
goto finish;
}
- close(daemon_pipe[0]);
+ pa_assert_se(pa_close(daemon_pipe[0]) == 0);
daemon_pipe[0] = -1;
#endif
@@ -505,9 +548,9 @@ int main(int argc, char *argv[]) {
#endif
#ifndef OS_IS_WIN32
- close(0);
- close(1);
- close(2);
+ pa_close(0);
+ pa_close(1);
+ pa_close(2);
open("/dev/null", O_RDONLY);
open("/dev/null", O_WRONLY);
@@ -529,12 +572,12 @@ int main(int argc, char *argv[]) {
#ifdef TIOCNOTTY
if ((tty_fd = open("/dev/tty", O_RDWR)) >= 0) {
ioctl(tty_fd, TIOCNOTTY, (char*) 0);
- close(tty_fd);
+ pa_assert_se(pa_close(tty_fd) == 0);
}
#endif
}
- chdir("/");
+ pa_assert_se(chdir("/") == 0);
umask(0022);
if (conf->system_instance) {
@@ -564,18 +607,37 @@ int main(int argc, char *argv[]) {
signal(SIGPIPE, SIG_IGN);
#endif
- mainloop = pa_mainloop_new();
- assert(mainloop);
+ pa_log_info("Page size is %lu bytes", (unsigned long) PA_PAGE_SIZE);
+
+ if (pa_rtclock_hrtimer())
+ pa_log_info("Fresh high-resolution timers available! Bon appetit!");
+ else
+ pa_log_info("Dude, your kernel stinks! The chef's recommendation today is Linux with high-resolution timers enabled!");
+
+#ifdef SIGRTMIN
+ /* Valgrind uses SIGRTMAX. To easy debugging we don't use it here */
+ pa_rtsig_configure(SIGRTMIN, SIGRTMAX-1);
+#endif
+
+ pa_assert_se(mainloop = pa_mainloop_new());
if (!(c = pa_core_new(pa_mainloop_get_api(mainloop), !conf->disable_shm))) {
- pa_log("pa_core_new() failed.");
+ pa_log("pa_core_new() failed.");
goto finish;
}
c->is_system_instance = !!conf->system_instance;
-
- r = pa_signal_init(pa_mainloop_get_api(mainloop));
- assert(r == 0);
+ c->high_priority = !!conf->high_priority;
+ c->default_sample_spec = conf->default_sample_spec;
+ c->default_n_fragments = conf->default_n_fragments;
+ c->default_fragment_size_msec = conf->default_fragment_size_msec;
+ c->disallow_module_loading = conf->disallow_module_loading;
+ c->exit_idle_time = conf->exit_idle_time;
+ c->module_idle_time = conf->module_idle_time;
+ c->scache_idle_time = conf->scache_idle_time;
+ c->resample_method = conf->resample_method;
+
+ pa_assert_se(pa_signal_init(pa_mainloop_get_api(mainloop)) == 0);
pa_signal_new(SIGINT, signal_callback, c);
pa_signal_new(SIGTERM, signal_callback, c);
@@ -590,9 +652,7 @@ int main(int argc, char *argv[]) {
#endif
#ifdef OS_IS_WIN32
- timer = pa_mainloop_get_api(mainloop)->time_new(
- pa_mainloop_get_api(mainloop), pa_gettimeofday(&tv), message_cb, NULL);
- assert(timer);
+ pa_assert_se(timer = pa_mainloop_get_api(mainloop)->time_new(pa_mainloop_get_api(mainloop), pa_gettimeofday(&tv), message_cb, NULL));
#endif
if (conf->daemonize)
@@ -600,10 +660,8 @@ int main(int argc, char *argv[]) {
oil_init();
- if (!conf->no_cpu_limit) {
- r = pa_cpu_limit_init(pa_mainloop_get_api(mainloop));
- assert(r == 0);
- }
+ if (!conf->no_cpu_limit)
+ pa_assert_se(pa_cpu_limit_init(pa_mainloop_get_api(mainloop)) == 0);
buf = pa_strbuf_new();
if (conf->default_script_file)
@@ -634,12 +692,6 @@ int main(int argc, char *argv[]) {
pa_loop_write(daemon_pipe[1], &retval, sizeof(retval), NULL);
#endif
- c->disallow_module_loading = conf->disallow_module_loading;
- c->exit_idle_time = conf->exit_idle_time;
- c->module_idle_time = conf->module_idle_time;
- c->scache_idle_time = conf->scache_idle_time;
- c->resample_method = conf->resample_method;
-
if (c->default_sink_name &&
pa_namereg_get(c, c->default_sink_name, PA_NAMEREG_SINK, 1) == NULL) {
pa_log_error("%s : Fatal error. Default sink name (%s) does not exist in name register.", __FILE__, c->default_sink_name);
@@ -656,7 +708,7 @@ int main(int argc, char *argv[]) {
pa_mainloop_get_api(mainloop)->time_free(timer);
#endif
- pa_core_free(c);
+ pa_core_unref(c);
if (!conf->no_cpu_limit)
pa_cpu_limit_done();
@@ -676,13 +728,17 @@ finish:
if (valid_pid_file)
pa_pid_file_remove();
- close_pipe(daemon_pipe);
+ pa_close_pipe(daemon_pipe);
#ifdef OS_IS_WIN32
WSACleanup();
#endif
- lt_dlexit();
+ pa_ltdl_done();
+
+#ifdef HAVE_DBUS
+ dbus_shutdown();
+#endif
return retval;
}
diff --git a/src/daemon/pulseaudio-module-xsmp.desktop b/src/daemon/pulseaudio-module-xsmp.desktop
new file mode 100644
index 00000000..fa719a73
--- /dev/null
+++ b/src/daemon/pulseaudio-module-xsmp.desktop
@@ -0,0 +1,10 @@
+[Desktop Entry]
+Version=1.0
+Encoding=UTF-8
+Name=PulseAudio Session Management
+Comment=Load module-x11-xsmp into PulseAudio
+Exec=pactl load-module module-x11-xsmp
+Terminal=false
+Type=Application
+Categories=
+GenericName=
diff --git a/src/modules/alsa-util.c b/src/modules/alsa-util.c
index 40be5311..906de58d 100644
--- a/src/modules/alsa-util.c
+++ b/src/modules/alsa-util.c
@@ -33,6 +33,7 @@
#include <pulse/xmalloc.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
#include "alsa-util.h"
@@ -42,7 +43,6 @@ struct pa_alsa_fdlist {
/* This is a temporary buffer used to avoid lots of mallocs */
struct pollfd *work_fds;
- snd_pcm_t *pcm;
snd_mixer_t *mixer;
pa_mainloop_api *m;
@@ -56,11 +56,16 @@ struct pa_alsa_fdlist {
};
static void io_cb(pa_mainloop_api*a, pa_io_event* e, PA_GCC_UNUSED int fd, pa_io_event_flags_t events, void *userdata) {
- struct pa_alsa_fdlist *fdl = (struct pa_alsa_fdlist*)userdata;
+
+ struct pa_alsa_fdlist *fdl = userdata;
int err, i;
unsigned short revents;
- assert(a && fdl && (fdl->pcm || fdl->mixer) && fdl->fds && fdl->work_fds);
+ pa_assert(a);
+ pa_assert(fdl);
+ pa_assert(fdl->mixer);
+ pa_assert(fdl->fds);
+ pa_assert(fdl->work_fds);
if (fdl->polled)
return;
@@ -69,7 +74,7 @@ static void io_cb(pa_mainloop_api*a, pa_io_event* e, PA_GCC_UNUSED int fd, pa_io
memcpy(fdl->work_fds, fdl->fds, sizeof(struct pollfd) * fdl->num_fds);
- for (i = 0;i < fdl->num_fds;i++) {
+ for (i = 0;i < fdl->num_fds; i++) {
if (e == fdl->ios[i]) {
if (events & PA_IO_EVENT_INPUT)
fdl->work_fds[i].revents |= POLLIN;
@@ -83,63 +88,46 @@ static void io_cb(pa_mainloop_api*a, pa_io_event* e, PA_GCC_UNUSED int fd, pa_io
}
}
- assert(i != fdl->num_fds);
-
- if (fdl->pcm)
- err = snd_pcm_poll_descriptors_revents(fdl->pcm, fdl->work_fds, fdl->num_fds, &revents);
- else
- err = snd_mixer_poll_descriptors_revents(fdl->mixer, fdl->work_fds, fdl->num_fds, &revents);
+ pa_assert(i != fdl->num_fds);
- if (err < 0) {
- pa_log_error("Unable to get poll revent: %s",
- snd_strerror(err));
+ if ((err = snd_mixer_poll_descriptors_revents(fdl->mixer, fdl->work_fds, fdl->num_fds, &revents)) < 0) {
+ pa_log_error("Unable to get poll revent: %s", snd_strerror(err));
return;
}
a->defer_enable(fdl->defer, 1);
- if (revents) {
- if (fdl->pcm)
- fdl->cb(fdl->userdata);
- else
- snd_mixer_handle_events(fdl->mixer);
- }
+ if (revents)
+ snd_mixer_handle_events(fdl->mixer);
}
static void defer_cb(pa_mainloop_api*a, PA_GCC_UNUSED pa_defer_event* e, void *userdata) {
- struct pa_alsa_fdlist *fdl = (struct pa_alsa_fdlist*)userdata;
+ struct pa_alsa_fdlist *fdl = userdata;
int num_fds, i, err;
struct pollfd *temp;
- assert(a && fdl && (fdl->pcm || fdl->mixer));
+ pa_assert(a);
+ pa_assert(fdl);
+ pa_assert(fdl->mixer);
a->defer_enable(fdl->defer, 0);
- if (fdl->pcm)
- num_fds = snd_pcm_poll_descriptors_count(fdl->pcm);
- else
- num_fds = snd_mixer_poll_descriptors_count(fdl->mixer);
- assert(num_fds > 0);
+ num_fds = snd_mixer_poll_descriptors_count(fdl->mixer);
+ pa_assert(num_fds > 0);
if (num_fds != fdl->num_fds) {
if (fdl->fds)
pa_xfree(fdl->fds);
if (fdl->work_fds)
pa_xfree(fdl->work_fds);
- fdl->fds = pa_xmalloc0(sizeof(struct pollfd) * num_fds);
- fdl->work_fds = pa_xmalloc(sizeof(struct pollfd) * num_fds);
+ fdl->fds = pa_xnew0(struct pollfd, num_fds);
+ fdl->work_fds = pa_xnew(struct pollfd, num_fds);
}
memset(fdl->work_fds, 0, sizeof(struct pollfd) * num_fds);
- if (fdl->pcm)
- err = snd_pcm_poll_descriptors(fdl->pcm, fdl->work_fds, num_fds);
- else
- err = snd_mixer_poll_descriptors(fdl->mixer, fdl->work_fds, num_fds);
-
- if (err < 0) {
- pa_log_error("Unable to get poll descriptors: %s",
- snd_strerror(err));
+ if ((err = snd_mixer_poll_descriptors(fdl->mixer, fdl->work_fds, num_fds)) < 0) {
+ pa_log_error("Unable to get poll descriptors: %s", snd_strerror(err));
return;
}
@@ -149,18 +137,18 @@ static void defer_cb(pa_mainloop_api*a, PA_GCC_UNUSED pa_defer_event* e, void *u
return;
if (fdl->ios) {
- for (i = 0;i < fdl->num_fds;i++)
+ for (i = 0; i < fdl->num_fds; i++)
a->io_free(fdl->ios[i]);
+
if (num_fds != fdl->num_fds) {
pa_xfree(fdl->ios);
- fdl->ios = pa_xmalloc(sizeof(pa_io_event*) * num_fds);
- assert(fdl->ios);
+ fdl->ios = NULL;
}
- } else {
- fdl->ios = pa_xmalloc(sizeof(pa_io_event*) * num_fds);
- assert(fdl->ios);
}
+ if (!fdl->ios)
+ fdl->ios = pa_xnew(pa_io_event*, num_fds);
+
/* Swap pointers */
temp = fdl->work_fds;
fdl->work_fds = fdl->fds;
@@ -168,47 +156,41 @@ static void defer_cb(pa_mainloop_api*a, PA_GCC_UNUSED pa_defer_event* e, void *u
fdl->num_fds = num_fds;
- for (i = 0;i < num_fds;i++) {
+ for (i = 0;i < num_fds;i++)
fdl->ios[i] = a->io_new(a, fdl->fds[i].fd,
((fdl->fds[i].events & POLLIN) ? PA_IO_EVENT_INPUT : 0) |
((fdl->fds[i].events & POLLOUT) ? PA_IO_EVENT_OUTPUT : 0),
io_cb, fdl);
- assert(fdl->ios[i]);
- }
}
struct pa_alsa_fdlist *pa_alsa_fdlist_new(void) {
struct pa_alsa_fdlist *fdl;
- fdl = pa_xmalloc(sizeof(struct pa_alsa_fdlist));
+ fdl = pa_xnew0(struct pa_alsa_fdlist, 1);
fdl->num_fds = 0;
fdl->fds = NULL;
fdl->work_fds = NULL;
-
- fdl->pcm = NULL;
fdl->mixer = NULL;
-
fdl->m = NULL;
fdl->defer = NULL;
fdl->ios = NULL;
-
fdl->polled = 0;
return fdl;
}
void pa_alsa_fdlist_free(struct pa_alsa_fdlist *fdl) {
- assert(fdl);
+ pa_assert(fdl);
if (fdl->defer) {
- assert(fdl->m);
+ pa_assert(fdl->m);
fdl->m->defer_free(fdl->defer);
}
if (fdl->ios) {
int i;
- assert(fdl->m);
+ pa_assert(fdl->m);
for (i = 0;i < fdl->num_fds;i++)
fdl->m->io_free(fdl->ios[i]);
pa_xfree(fdl->ios);
@@ -222,29 +204,15 @@ void pa_alsa_fdlist_free(struct pa_alsa_fdlist *fdl) {
pa_xfree(fdl);
}
-int pa_alsa_fdlist_init_pcm(struct pa_alsa_fdlist *fdl, snd_pcm_t *pcm_handle, pa_mainloop_api* m, void (*cb)(void *userdata), void *userdata) {
- assert(fdl && pcm_handle && m && !fdl->m && cb);
-
- fdl->pcm = pcm_handle;
- fdl->m = m;
-
- fdl->defer = m->defer_new(m, defer_cb, fdl);
- assert(fdl->defer);
-
- fdl->cb = cb;
- fdl->userdata = userdata;
-
- return 0;
-}
-
-int pa_alsa_fdlist_init_mixer(struct pa_alsa_fdlist *fdl, snd_mixer_t *mixer_handle, pa_mainloop_api* m) {
- assert(fdl && mixer_handle && m && !fdl->m);
+int pa_alsa_fdlist_set_mixer(struct pa_alsa_fdlist *fdl, snd_mixer_t *mixer_handle, pa_mainloop_api* m) {
+ pa_assert(fdl);
+ pa_assert(mixer_handle);
+ pa_assert(m);
+ pa_assert(!fdl->m);
fdl->mixer = mixer_handle;
fdl->m = m;
-
fdl->defer = m->defer_new(m, defer_cb, fdl);
- assert(fdl->defer);
return 0;
}
@@ -274,8 +242,8 @@ static int set_format(snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hwparams, pa_s
int i, ret;
- assert(pcm_handle);
- assert(f);
+ pa_assert(pcm_handle);
+ pa_assert(f);
if ((ret = snd_pcm_hw_params_set_format(pcm_handle, hwparams, format_trans[*f])) >= 0)
return ret;
@@ -308,7 +276,7 @@ try_auto:
/* Set the hardware parameters of the given ALSA device. Returns the
* selected fragment settings in *period and *period_size */
-int pa_alsa_set_hw_params(snd_pcm_t *pcm_handle, pa_sample_spec *ss, uint32_t *periods, snd_pcm_uframes_t *period_size) {
+int pa_alsa_set_hw_params(snd_pcm_t *pcm_handle, pa_sample_spec *ss, uint32_t *periods, snd_pcm_uframes_t *period_size, int *use_mmap) {
int ret = -1;
snd_pcm_uframes_t buffer_size;
unsigned int r = ss->rate;
@@ -316,17 +284,32 @@ int pa_alsa_set_hw_params(snd_pcm_t *pcm_handle, pa_sample_spec *ss, uint32_t *p
pa_sample_format_t f = ss->format;
snd_pcm_hw_params_t *hwparams;
- assert(pcm_handle);
- assert(ss);
- assert(periods);
- assert(period_size);
+ pa_assert(pcm_handle);
+ pa_assert(ss);
+ pa_assert(periods);
+ pa_assert(period_size);
+
+ snd_pcm_hw_params_alloca(&hwparams);
buffer_size = *periods * *period_size;
- if ((ret = snd_pcm_hw_params_malloc(&hwparams)) < 0 ||
- (ret = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0 ||
- (ret = snd_pcm_hw_params_set_rate_resample(pcm_handle, hwparams, 0)) < 0 ||
- (ret = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
+ if ((ret = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0 ||
+ (ret = snd_pcm_hw_params_set_rate_resample(pcm_handle, hwparams, 0)) < 0)
+ goto finish;
+
+ if (use_mmap && *use_mmap) {
+ if ((ret = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_MMAP_INTERLEAVED)) < 0) {
+
+ /* mmap() didn't work, fall back to interleaved */
+
+ if ((ret = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
+ goto finish;
+
+ if (use_mmap)
+ *use_mmap = 0;
+ }
+
+ } else if ((ret = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
goto finish;
if ((ret = set_format(pcm_handle, hwparams, &f)) < 0)
@@ -346,7 +329,7 @@ int pa_alsa_set_hw_params(snd_pcm_t *pcm_handle, pa_sample_spec *ss, uint32_t *p
goto finish;
if (ss->rate != r) {
- pa_log_warn("device doesn't support %u Hz, changed to %u Hz.", ss->rate, r);
+ pa_log_warn("Device %s doesn't support %u Hz, changed to %u Hz.", snd_pcm_name(pcm_handle), ss->rate, r);
/* If the sample rate deviates too much, we need to resample */
if (r < ss->rate*.95 || r > ss->rate*1.05)
@@ -354,12 +337,12 @@ int pa_alsa_set_hw_params(snd_pcm_t *pcm_handle, pa_sample_spec *ss, uint32_t *p
}
if (ss->channels != c) {
- pa_log_warn("device doesn't support %u channels, changed to %u.", ss->channels, c);
+ pa_log_warn("Device %s doesn't support %u channels, changed to %u.", snd_pcm_name(pcm_handle), ss->channels, c);
ss->channels = c;
}
if (ss->format != f) {
- pa_log_warn("device doesn't support sample format %s, changed to %s.", pa_sample_format_to_string(ss->format), pa_sample_format_to_string(f));
+ pa_log_warn("Device %s doesn't support sample format %s, changed to %s.", snd_pcm_name(pcm_handle), pa_sample_format_to_string(ss->format), pa_sample_format_to_string(f));
ss->format = f;
}
@@ -370,24 +353,54 @@ int pa_alsa_set_hw_params(snd_pcm_t *pcm_handle, pa_sample_spec *ss, uint32_t *p
(ret = snd_pcm_hw_params_get_period_size(hwparams, period_size, NULL)) < 0)
goto finish;
- assert(buffer_size > 0);
- assert(*period_size > 0);
+ pa_assert(buffer_size > 0);
+ pa_assert(*period_size > 0);
*periods = buffer_size / *period_size;
- assert(*periods > 0);
+ pa_assert(*periods > 0);
ret = 0;
finish:
- if (hwparams)
- snd_pcm_hw_params_free(hwparams);
return ret;
}
+int pa_alsa_set_sw_params(snd_pcm_t *pcm) {
+ snd_pcm_sw_params_t *swparams;
+ int err;
+
+ pa_assert(pcm);
+
+ snd_pcm_sw_params_alloca(&swparams);
+
+ if ((err = snd_pcm_sw_params_current(pcm, swparams) < 0)) {
+ pa_log_warn("Unable to determine current swparams: %s\n", snd_strerror(err));
+ return err;
+ }
+
+ if ((err = snd_pcm_sw_params_set_stop_threshold(pcm, swparams, (snd_pcm_uframes_t) -1)) < 0) {
+ pa_log_warn("Unable to set stop threshold: %s\n", snd_strerror(err));
+ return err;
+ }
+
+ if ((err = snd_pcm_sw_params_set_start_threshold(pcm, swparams, (snd_pcm_uframes_t) -1)) < 0) {
+ pa_log_warn("Unable to set start threshold: %s\n", snd_strerror(err));
+ return err;
+ }
+
+ if ((err = snd_pcm_sw_params(pcm, swparams)) < 0) {
+ pa_log_warn("Unable to set sw params: %s\n", snd_strerror(err));
+ return err;
+ }
+
+ return 0;
+}
+
int pa_alsa_prepare_mixer(snd_mixer_t *mixer, const char *dev) {
int err;
- assert(mixer && dev);
+ pa_assert(mixer);
+ pa_assert(dev);
if ((err = snd_mixer_attach(mixer, dev)) < 0) {
pa_log_warn("Unable to attach to mixer %s: %s", dev, snd_strerror(err));
@@ -410,10 +423,11 @@ int pa_alsa_prepare_mixer(snd_mixer_t *mixer, const char *dev) {
snd_mixer_elem_t *pa_alsa_find_elem(snd_mixer_t *mixer, const char *name, const char *fallback) {
snd_mixer_elem_t *elem;
snd_mixer_selem_id_t *sid = NULL;
+
snd_mixer_selem_id_alloca(&sid);
- assert(mixer);
- assert(name);
+ pa_assert(mixer);
+ pa_assert(name);
snd_mixer_selem_id_set_name(sid, name);
diff --git a/src/modules/alsa-util.h b/src/modules/alsa-util.h
index ea6c7e1d..6f1f927e 100644
--- a/src/modules/alsa-util.h
+++ b/src/modules/alsa-util.h
@@ -32,15 +32,14 @@
#include <pulse/channelmap.h>
-struct pa_alsa_fdlist;
+typedef struct pa_alsa_fdlist pa_alsa_fdlist;
struct pa_alsa_fdlist *pa_alsa_fdlist_new(void);
void pa_alsa_fdlist_free(struct pa_alsa_fdlist *fdl);
+int pa_alsa_fdlist_set_mixer(struct pa_alsa_fdlist *fdl, snd_mixer_t *mixer_handle, pa_mainloop_api* m);
-int pa_alsa_fdlist_init_pcm(struct pa_alsa_fdlist *fdl, snd_pcm_t *pcm_handle, pa_mainloop_api* m, void (*cb)(void *userdata), void *userdata);
-int pa_alsa_fdlist_init_mixer(struct pa_alsa_fdlist *fdl, snd_mixer_t *mixer_handle, pa_mainloop_api* m);
-
-int pa_alsa_set_hw_params(snd_pcm_t *pcm_handle, pa_sample_spec *ss, uint32_t *periods, snd_pcm_uframes_t *period_size);
+int pa_alsa_set_hw_params(snd_pcm_t *pcm_handle, pa_sample_spec *ss, uint32_t *periods, snd_pcm_uframes_t *period_size, int *use_mmap);
+int pa_alsa_set_sw_params(snd_pcm_t *pcm);
int pa_alsa_prepare_mixer(snd_mixer_t *mixer, const char *dev);
snd_mixer_elem_t *pa_alsa_find_elem(snd_mixer_t *mixer, const char *name, const char *fallback);
diff --git a/src/modules/dbus-util.c b/src/modules/dbus-util.c
index 48a45174..fc1e91ea 100644
--- a/src/modules/dbus-util.c
+++ b/src/modules/dbus-util.c
@@ -26,25 +26,25 @@
#include <config.h>
#endif
-#include <assert.h>
-#include <pulsecore/log.h>
-#include <pulsecore/props.h>
#include <pulse/xmalloc.h>
#include <pulse/timeval.h>
+#include <pulsecore/log.h>
+#include <pulsecore/props.h>
#include "dbus-util.h"
struct pa_dbus_connection {
- int refcount;
+ PA_REFCNT_DECLARE;
+
pa_core *core;
DBusConnection *connection;
const char *property_name;
pa_defer_event* dispatch_event;
};
-static void dispatch_cb(pa_mainloop_api *ea, pa_defer_event *ev, void *userdata)
-{
- DBusConnection *conn = (DBusConnection *) userdata;
+static void dispatch_cb(pa_mainloop_api *ea, pa_defer_event *ev, void *userdata) {
+ DBusConnection *conn = userdata;
+
if (dbus_connection_dispatch(conn) == DBUS_DISPATCH_COMPLETE) {
/* no more data to process, disable the deferred */
ea->defer_enable(ev, 0);
@@ -52,14 +52,17 @@ static void dispatch_cb(pa_mainloop_api *ea, pa_defer_event *ev, void *userdata)
}
/* DBusDispatchStatusFunction callback for the pa mainloop */
-static void dispatch_status(DBusConnection *conn, DBusDispatchStatus status,
- void *userdata)
-{
- pa_dbus_connection *c = (pa_dbus_connection*) userdata;
+static void dispatch_status(DBusConnection *conn, DBusDispatchStatus status, void *userdata) {
+ pa_dbus_connection *c = userdata;
+
+ pa_assert(c);
+
switch(status) {
+
case DBUS_DISPATCH_COMPLETE:
c->core->mainloop->defer_enable(c->dispatch_event, 0);
break;
+
case DBUS_DISPATCH_DATA_REMAINS:
case DBUS_DISPATCH_NEED_MEMORY:
default:
@@ -68,11 +71,13 @@ static void dispatch_status(DBusConnection *conn, DBusDispatchStatus status,
}
}
-static pa_io_event_flags_t
-get_watch_flags(DBusWatch *watch)
-{
- unsigned int flags = dbus_watch_get_flags(watch);
- pa_io_event_flags_t events = PA_IO_EVENT_HANGUP | PA_IO_EVENT_ERROR;
+static pa_io_event_flags_t get_watch_flags(DBusWatch *watch) {
+ unsigned int flags;
+ pa_io_event_flags_t events = 0;
+
+ pa_assert(watch);
+
+ flags = dbus_watch_get_flags(watch);
/* no watch flags for disabled watches */
if (!dbus_watch_get_enabled(watch))
@@ -83,21 +88,22 @@ get_watch_flags(DBusWatch *watch)
if (flags & DBUS_WATCH_WRITABLE)
events |= PA_IO_EVENT_OUTPUT;
- return events;
+ return events | PA_IO_EVENT_HANGUP | PA_IO_EVENT_ERROR;
}
/* pa_io_event_cb_t IO event handler */
-static void handle_io_event(PA_GCC_UNUSED pa_mainloop_api *ea, pa_io_event *e,
- int fd, pa_io_event_flags_t events, void *userdata)
-{
+static void handle_io_event(PA_GCC_UNUSED pa_mainloop_api *ea, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) {
unsigned int flags = 0;
- DBusWatch *watch = (DBusWatch*) userdata;
+ DBusWatch *watch = userdata;
- assert(fd == dbus_watch_get_fd(watch));
+#if HAVE_DBUS_WATCH_GET_UNIX_FD
+ pa_assert(fd == dbus_watch_get_unix_fd(watch));
+#else
+ pa_assert(fd == dbus_watch_get_fd(watch));
+#endif
if (!dbus_watch_get_enabled(watch)) {
- pa_log_warn("Asked to handle disabled watch: %p %i",
- (void *) watch, fd);
+ pa_log_warn("Asked to handle disabled watch: %p %i", (void*) watch, fd);
return;
}
@@ -114,10 +120,8 @@ static void handle_io_event(PA_GCC_UNUSED pa_mainloop_api *ea, pa_io_event *e,
}
/* pa_time_event_cb_t timer event handler */
-static void handle_time_event(pa_mainloop_api *ea, pa_time_event* e,
- const struct timeval *tv, void *userdata)
-{
- DBusTimeout *timeout = (DBusTimeout*) userdata;
+static void handle_time_event(pa_mainloop_api *ea, pa_time_event* e, const struct timeval *tv, void *userdata) {
+ DBusTimeout *timeout = userdata;
if (dbus_timeout_get_enabled(timeout)) {
struct timeval next = *tv;
@@ -130,218 +134,195 @@ static void handle_time_event(pa_mainloop_api *ea, pa_time_event* e,
}
/* DBusAddWatchFunction callback for pa mainloop */
-static dbus_bool_t add_watch(DBusWatch *watch, void *data)
-{
+static dbus_bool_t add_watch(DBusWatch *watch, void *data) {
+ pa_core *c = PA_CORE(data);
pa_io_event *ev;
- pa_core *c = (pa_core*) data;
- ev = c->mainloop->io_new(c->mainloop, dbus_watch_get_fd(watch),
- get_watch_flags(watch),
- handle_io_event, (void*) watch);
- if (NULL == ev)
- return FALSE;
+ pa_assert(watch);
+ pa_assert(c);
- /* dbus_watch_set_data(watch, (void*) ev, c->mainloop->io_free); */
- dbus_watch_set_data(watch, (void*) ev, NULL);
+ ev = c->mainloop->io_new(
+ c->mainloop,
+#if HAVE_DBUS_WATCH_GET_UNIX_FD
+ dbus_watch_get_unix_fd(watch),
+#else
+ dbus_watch_get_fd(watch),
+#endif
+ get_watch_flags(watch), handle_io_event, watch);
+
+ dbus_watch_set_data(watch, ev, NULL);
return TRUE;
}
/* DBusRemoveWatchFunction callback for pa mainloop */
-static void remove_watch(DBusWatch *watch, void *data)
-{
- pa_core *c = (pa_core*) data;
- pa_io_event *ev = (pa_io_event*) dbus_watch_get_data(watch);
+static void remove_watch(DBusWatch *watch, void *data) {
+ pa_core *c = PA_CORE(data);
+ pa_io_event *ev;
- /* free the event */
- if (NULL != ev)
+ pa_assert(watch);
+ pa_assert(c);
+
+ if ((ev = dbus_watch_get_data(watch)))
c->mainloop->io_free(ev);
}
/* DBusWatchToggledFunction callback for pa mainloop */
-static void toggle_watch(DBusWatch *watch, void *data)
-{
- pa_core *c = (pa_core*) data;
- pa_io_event *ev = (pa_io_event*) dbus_watch_get_data(watch);
+static void toggle_watch(DBusWatch *watch, void *data) {
+ pa_core *c = PA_CORE(data);
+ pa_io_event *ev;
+
+ pa_assert(watch);
+ pa_core_assert_ref(c);
+
+ pa_assert_se(ev = dbus_watch_get_data(watch));
/* get_watch_flags() checks if the watch is enabled */
c->mainloop->io_enable(ev, get_watch_flags(watch));
}
/* DBusAddTimeoutFunction callback for pa mainloop */
-static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data)
-{
- struct timeval tv;
+static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data) {
+ pa_core *c = PA_CORE(data);
pa_time_event *ev;
- pa_core *c = (pa_core*) data;
+ struct timeval tv;
+
+ pa_assert(timeout);
+ pa_assert(c);
if (!dbus_timeout_get_enabled(timeout))
return FALSE;
- if (!pa_gettimeofday(&tv))
- return -1;
-
+ pa_gettimeofday(&tv);
pa_timeval_add(&tv, dbus_timeout_get_interval(timeout) * 1000);
- ev = c->mainloop->time_new(c->mainloop, &tv, handle_time_event,
- (void*) timeout);
- if (NULL == ev)
- return FALSE;
+ ev = c->mainloop->time_new(c->mainloop, &tv, handle_time_event, timeout);
- /* dbus_timeout_set_data(timeout, (void*) ev, c->mainloop->time_free); */
- dbus_timeout_set_data(timeout, (void*) ev, NULL);
+ dbus_timeout_set_data(timeout, ev, NULL);
return TRUE;
}
/* DBusRemoveTimeoutFunction callback for pa mainloop */
-static void remove_timeout(DBusTimeout *timeout, void *data)
-{
- pa_core *c = (pa_core*) data;
- pa_time_event *ev = (pa_time_event*) dbus_timeout_get_data(timeout);
+static void remove_timeout(DBusTimeout *timeout, void *data) {
+ pa_core *c = PA_CORE(data);
+ pa_time_event *ev;
+
+ pa_assert(timeout);
+ pa_assert(c);
- /* free the event */
- if (NULL != ev)
+ if ((ev = dbus_timeout_get_data(timeout)))
c->mainloop->time_free(ev);
}
/* DBusTimeoutToggledFunction callback for pa mainloop */
-static void toggle_timeout(DBusTimeout *timeout, void *data)
-{
- struct timeval tv;
- pa_core *c = (pa_core*) data;
- pa_time_event *ev = (pa_time_event*) dbus_timeout_get_data(timeout);
+static void toggle_timeout(DBusTimeout *timeout, void *data) {
+ pa_core *c = PA_CORE(data);
+ pa_time_event *ev;
+
+ pa_assert(timeout);
+ pa_assert(c);
+
+ pa_assert_se(ev = dbus_timeout_get_data(timeout));
if (dbus_timeout_get_enabled(timeout)) {
+ struct timeval tv;
+
pa_gettimeofday(&tv);
pa_timeval_add(&tv, dbus_timeout_get_interval(timeout) * 1000);
+
c->mainloop->time_restart(ev, &tv);
- } else {
- /* disable the timeout */
+ } else
c->mainloop->time_restart(ev, NULL);
- }
}
-static void
-pa_dbus_connection_free(pa_dbus_connection *c)
-{
- assert(c);
- assert(!dbus_connection_get_is_connected(c->connection));
+static void wakeup_main(void *userdata) {
+ pa_dbus_connection *c = userdata;
- /* already disconnected, just free */
- pa_property_remove(c->core, c->property_name);
- c->core->mainloop->defer_free(c->dispatch_event);
- dbus_connection_unref(c->connection);
- pa_xfree(c);
-}
+ pa_assert(c);
-static void
-wakeup_main(void *userdata)
-{
- pa_dbus_connection *c = (pa_dbus_connection*) userdata;
/* this will wakeup the mainloop and dispatch events, although
* it may not be the cleanest way of accomplishing it */
c->core->mainloop->defer_enable(c->dispatch_event, 1);
}
-static pa_dbus_connection* pa_dbus_connection_new(pa_core* c, DBusConnection *conn, const char* name)
-{
- pa_dbus_connection *pconn = pa_xnew(pa_dbus_connection, 1);
+static pa_dbus_connection* pa_dbus_connection_new(pa_core* c, DBusConnection *conn, const char* name) {
+ pa_dbus_connection *pconn;
- pconn->refcount = 1;
+ pconn = pa_xnew(pa_dbus_connection, 1);
+ PA_REFCNT_INIT(pconn);
pconn->core = c;
pconn->property_name = name;
pconn->connection = conn;
- pconn->dispatch_event = c->mainloop->defer_new(c->mainloop, dispatch_cb,
- (void*) conn);
+ pconn->dispatch_event = c->mainloop->defer_new(c->mainloop, dispatch_cb, conn);
pa_property_set(c, name, pconn);
return pconn;
}
-DBusConnection* pa_dbus_connection_get(pa_dbus_connection *c)
-{
- assert(c && c->connection);
+DBusConnection* pa_dbus_connection_get(pa_dbus_connection *c){
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) > 0);
+ pa_assert(c->connection);
+
return c->connection;
}
-void pa_dbus_connection_unref(pa_dbus_connection *c)
-{
- assert(c);
+void pa_dbus_connection_unref(pa_dbus_connection *c) {
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) > 0);
- /* non-zero refcount, still outstanding refs */
- if (--(c->refcount))
+ if (PA_REFCNT_DEC(c) > 0)
return;
- /* refcount is zero */
if (dbus_connection_get_is_connected(c->connection)) {
- /* disconnect as we have no more internal references */
dbus_connection_close(c->connection);
- /* must process remaining messages, bit of a kludge to
- * handle both unload and shutdown */
- while(dbus_connection_read_write_dispatch(c->connection, -1));
+ /* must process remaining messages, bit of a kludge to handle
+ * both unload and shutdown */
+ while (dbus_connection_read_write_dispatch(c->connection, -1));
}
- pa_dbus_connection_free(c);
+
+ /* already disconnected, just free */
+ pa_property_remove(c->core, c->property_name);
+ c->core->mainloop->defer_free(c->dispatch_event);
+ dbus_connection_unref(c->connection);
+ pa_xfree(c);
}
-pa_dbus_connection* pa_dbus_connection_ref(pa_dbus_connection *c)
-{
- assert(c);
+pa_dbus_connection* pa_dbus_connection_ref(pa_dbus_connection *c) {
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) > 0);
- ++(c->refcount);
+ PA_REFCNT_INC(c);
return c;
}
-pa_dbus_connection* pa_dbus_bus_get(pa_core *c, DBusBusType type,
- DBusError *error)
-{
- const char* name;
+pa_dbus_connection* pa_dbus_bus_get(pa_core *c, DBusBusType type, DBusError *error) {
+
+ static const char *const prop_name[] = {
+ [DBUS_BUS_SESSION] = "dbus-connection-session",
+ [DBUS_BUS_SYSTEM] = "dbus-connection-system",
+ [DBUS_BUS_STARTER] = "dbus-connection-starter"
+ };
DBusConnection *conn;
pa_dbus_connection *pconn;
- switch (type) {
- case DBUS_BUS_SYSTEM:
- name = "dbus-connection-system";
- break;
- case DBUS_BUS_SESSION:
- name = "dbus-connection-session";
- break;
- case DBUS_BUS_STARTER:
- name = "dbus-connection-starter";
- break;
- default:
- assert(0); /* never reached */
- break;
- }
+ pa_assert(type == DBUS_BUS_SYSTEM || type == DBUS_BUS_SESSION || type == DBUS_BUS_STARTER);
- if ((pconn = pa_property_get(c, name)))
+ if ((pconn = pa_property_get(c, prop_name[type])))
return pa_dbus_connection_ref(pconn);
- /* else */
- conn = dbus_bus_get_private(type, error);
- if (conn == NULL || dbus_error_is_set(error)) {
+ if (!(conn = dbus_bus_get_private(type, error)))
return NULL;
- }
- pconn = pa_dbus_connection_new(c, conn, name);
+ pconn = pa_dbus_connection_new(c, conn, prop_name[type]);
- /* don't exit on disconnect */
dbus_connection_set_exit_on_disconnect(conn, FALSE);
- /* set up the DBUS call backs */
- dbus_connection_set_dispatch_status_function(conn, dispatch_status,
- (void*) pconn, NULL);
- dbus_connection_set_watch_functions(conn,
- add_watch,
- remove_watch,
- toggle_watch,
- (void*) c, NULL);
- dbus_connection_set_timeout_functions(conn,
- add_timeout,
- remove_timeout,
- toggle_timeout,
- (void*) c, NULL);
+ dbus_connection_set_dispatch_status_function(conn, dispatch_status, pconn, NULL);
+ dbus_connection_set_watch_functions(conn, add_watch, remove_watch, toggle_watch, c, NULL);
+ dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout, toggle_timeout, c, NULL);
dbus_connection_set_wakeup_main_function(conn, wakeup_main, pconn, NULL);
return pconn;
diff --git a/src/modules/gconf/gconf-helper.c b/src/modules/gconf/gconf-helper.c
index 3483b845..abd13287 100644
--- a/src/modules/gconf/gconf-helper.c
+++ b/src/modules/gconf/gconf-helper.c
@@ -32,6 +32,8 @@
#include <gconf/gconf-client.h>
#include <glib.h>
+#include <pulsecore/core-util.h>
+
#define PA_GCONF_ROOT "/system/pulseaudio"
#define PA_GCONF_PATH_MODULES PA_GCONF_ROOT"/modules"
@@ -40,13 +42,13 @@ static void handle_module(GConfClient *client, const char *name) {
gboolean enabled, locked;
int i;
- snprintf(p, sizeof(p), PA_GCONF_PATH_MODULES"/%s/locked", name);
+ pa_snprintf(p, sizeof(p), PA_GCONF_PATH_MODULES"/%s/locked", name);
locked = gconf_client_get_bool(client, p, FALSE);
if (locked)
return;
- snprintf(p, sizeof(p), PA_GCONF_PATH_MODULES"/%s/enabled", name);
+ pa_snprintf(p, sizeof(p), PA_GCONF_PATH_MODULES"/%s/enabled", name);
enabled = gconf_client_get_bool(client, p, FALSE);
printf("%c%s%c", enabled ? '+' : '-', name, 0);
@@ -56,11 +58,11 @@ static void handle_module(GConfClient *client, const char *name) {
for (i = 0; i < 10; i++) {
gchar *n, *a;
- snprintf(p, sizeof(p), PA_GCONF_PATH_MODULES"/%s/name%i", name, i);
+ pa_snprintf(p, sizeof(p), PA_GCONF_PATH_MODULES"/%s/name%i", name, i);
if (!(n = gconf_client_get_string(client, p, NULL)) || !*n)
break;
- snprintf(p, sizeof(p), PA_GCONF_PATH_MODULES"/%s/args%i", name, i);
+ pa_snprintf(p, sizeof(p), PA_GCONF_PATH_MODULES"/%s/args%i", name, i);
a = gconf_client_get_string(client, p, NULL);
printf("%s%c%s%c", n, 0, a ? a : "", 0);
diff --git a/src/modules/gconf/module-gconf.c b/src/modules/gconf/module-gconf.c
index cbe17d20..1c8866de 100644
--- a/src/modules/gconf/module-gconf.c
+++ b/src/modules/gconf/module-gconf.c
@@ -25,7 +25,6 @@
#include <config.h>
#endif
-#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
@@ -34,6 +33,7 @@
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
+#include <dirent.h>
#ifdef HAVE_SYS_PRCTL_H
#include <sys/prctl.h>
@@ -95,7 +95,7 @@ struct userdata {
static int fill_buf(struct userdata *u) {
ssize_t r;
- assert(u);
+ pa_assert(u);
if (u->buf_fill >= BUF_MAX) {
pa_log("read buffer overflow");
@@ -111,21 +111,21 @@ static int fill_buf(struct userdata *u) {
static int read_byte(struct userdata *u) {
int ret;
- assert(u);
+ pa_assert(u);
if (u->buf_fill < 1)
if (fill_buf(u) < 0)
return -1;
ret = u->buf[0];
- assert(u->buf_fill > 0);
+ pa_assert(u->buf_fill > 0);
u->buf_fill--;
memmove(u->buf, u->buf+1, u->buf_fill);
return ret;
}
static char *read_string(struct userdata *u) {
- assert(u);
+ pa_assert(u);
for (;;) {
char *e;
@@ -143,9 +143,9 @@ static char *read_string(struct userdata *u) {
}
static void unload_one_module(struct userdata *u, struct module_info*m, unsigned i) {
- assert(u);
- assert(m);
- assert(i < m->n_items);
+ pa_assert(u);
+ pa_assert(m);
+ pa_assert(i < m->n_items);
if (m->items[i].index == PA_INVALID_INDEX)
return;
@@ -161,8 +161,8 @@ static void unload_one_module(struct userdata *u, struct module_info*m, unsigned
static void unload_all_modules(struct userdata *u, struct module_info*m) {
unsigned i;
- assert(u);
- assert(m);
+ pa_assert(u);
+ pa_assert(m);
for (i = 0; i < m->n_items; i++)
unload_one_module(u, m, i);
@@ -180,10 +180,10 @@ static void load_module(
pa_module *mod;
- assert(u);
- assert(m);
- assert(name);
- assert(args);
+ pa_assert(u);
+ pa_assert(m);
+ pa_assert(name);
+ pa_assert(args);
if (!is_new) {
if (m->items[i].index != PA_INVALID_INDEX &&
@@ -212,8 +212,8 @@ static void module_info_free(void *p, void *userdata) {
struct module_info *m = p;
struct userdata *u = userdata;
- assert(m);
- assert(u);
+ pa_assert(m);
+ pa_assert(u);
unload_all_modules(u, m);
pa_xfree(m->name);
@@ -356,8 +356,10 @@ static int start_client(const char *n, pid_t *pid) {
return pipe_fds[0];
} else {
+#ifdef __linux__
+ DIR* d;
+#endif
int max_fd, i;
-
/* child */
close(pipe_fds[0]);
@@ -372,18 +374,48 @@ static int start_client(const char *n, pid_t *pid) {
close(2);
open("/dev/null", O_WRONLY);
- max_fd = 1024;
+#ifdef __linux__
+
+ if ((d = opendir("/proc/self/fd/"))) {
+
+ struct dirent *de;
+
+ while ((de = readdir(d))) {
+ char *e = NULL;
+ int fd;
+
+ if (de->d_name[0] == '.')
+ continue;
+
+ errno = 0;
+ fd = strtol(de->d_name, &e, 10);
+ pa_assert(errno == 0 && e && *e == 0);
+
+ if (fd >= 3 && dirfd(d) != fd)
+ close(fd);
+ }
+
+ closedir(d);
+ } else {
+
+#endif
+
+ max_fd = 1024;
#ifdef HAVE_SYS_RESOURCE_H
- {
- struct rlimit r;
- if (getrlimit(RLIMIT_NOFILE, &r) == 0)
- max_fd = r.rlim_max;
- }
+ {
+ struct rlimit r;
+ if (getrlimit(RLIMIT_NOFILE, &r) == 0)
+ max_fd = r.rlim_max;
+ }
#endif
- for (i = 3; i < max_fd; i++)
- close(i);
+ for (i = 3; i < max_fd; i++)
+ close(i);
+#
+#ifdef __linux__
+ }
+#endif
#ifdef PR_SET_PDEATHSIG
/* On Linux we can use PR_SET_PDEATHSIG to have the helper
@@ -413,12 +445,12 @@ fail:
return -1;
}
-int pa__init(pa_core *c, pa_module*m) {
+int pa__init(pa_module*m) {
struct userdata *u;
int r;
u = pa_xnew(struct userdata, 1);
- u->core = c;
+ u->core = m->core;
u->module = m;
m->userdata = u;
u->module_infos = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
@@ -431,8 +463,8 @@ int pa__init(pa_core *c, pa_module*m) {
if ((u->fd = start_client(PA_GCONF_HELPER, &u->pid)) < 0)
goto fail;
- u->io_event = c->mainloop->io_new(
- c->mainloop,
+ u->io_event = m->core->mainloop->io_new(
+ m->core->mainloop,
u->fd,
PA_IO_EVENT_INPUT,
io_event_cb,
@@ -449,21 +481,20 @@ int pa__init(pa_core *c, pa_module*m) {
return 0;
fail:
- pa__done(c, m);
+ pa__done(m);
return -1;
}
-void pa__done(pa_core *c, pa_module*m) {
+void pa__done(pa_module*m) {
struct userdata *u;
- assert(c);
- assert(m);
+ pa_assert(m);
if (!(u = m->userdata))
return;
if (u->io_event)
- c->mainloop->io_free(u->io_event);
+ m->core->mainloop->io_free(u->io_event);
if (u->fd >= 0)
close(u->fd);
diff --git a/src/modules/ladspa.h b/src/modules/ladspa.h
new file mode 100644
index 00000000..b1a9c4e5
--- /dev/null
+++ b/src/modules/ladspa.h
@@ -0,0 +1,603 @@
+/* ladspa.h
+
+ Linux Audio Developer's Simple Plugin API Version 1.1[LGPL].
+ Copyright (C) 2000-2002 Richard W.E. Furse, Paul Barton-Davis,
+ Stefan Westerfeld.
+
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA. */
+
+#ifndef LADSPA_INCLUDED
+#define LADSPA_INCLUDED
+
+#define LADSPA_VERSION "1.1"
+#define LADSPA_VERSION_MAJOR 1
+#define LADSPA_VERSION_MINOR 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*****************************************************************************/
+
+/* Overview:
+
+ There is a large number of synthesis packages in use or development
+ on the Linux platform at this time. This API (`The Linux Audio
+ Developer's Simple Plugin API') attempts to give programmers the
+ ability to write simple `plugin' audio processors in C/C++ and link
+ them dynamically (`plug') into a range of these packages (`hosts').
+ It should be possible for any host and any plugin to communicate
+ completely through this interface.
+
+ This API is deliberately short and simple. To achieve compatibility
+ with a range of promising Linux sound synthesis packages it
+ attempts to find the `greatest common divisor' in their logical
+ behaviour. Having said this, certain limiting decisions are
+ implicit, notably the use of a fixed type (LADSPA_Data) for all
+ data transfer and absence of a parameterised `initialisation'
+ phase. See below for the LADSPA_Data typedef.
+
+ Plugins are expected to distinguish between control and audio
+ data. Plugins have `ports' that are inputs or outputs for audio or
+ control data and each plugin is `run' for a `block' corresponding
+ to a short time interval measured in samples. Audio data is
+ communicated using arrays of LADSPA_Data, allowing a block of audio
+ to be processed by the plugin in a single pass. Control data is
+ communicated using single LADSPA_Data values. Control data has a
+ single value at the start of a call to the `run()' or `run_adding()'
+ function, and may be considered to remain this value for its
+ duration. The plugin may assume that all its input and output ports
+ have been connected to the relevant data location (see the
+ `connect_port()' function below) before it is asked to run.
+
+ Plugins will reside in shared object files suitable for dynamic
+ linking by dlopen() and family. The file will provide a number of
+ `plugin types' that can be used to instantiate actual plugins
+ (sometimes known as `plugin instances') that can be connected
+ together to perform tasks.
+
+ This API contains very limited error-handling. */
+
+/*****************************************************************************/
+
+/* Fundamental data type passed in and out of plugin. This data type
+ is used to communicate audio samples and control values. It is
+ assumed that the plugin will work sensibly given any numeric input
+ value although it may have a preferred range (see hints below).
+
+ For audio it is generally assumed that 1.0f is the `0dB' reference
+ amplitude and is a `normal' signal level. */
+
+typedef float LADSPA_Data;
+
+/*****************************************************************************/
+
+/* Special Plugin Properties:
+
+ Optional features of the plugin type are encapsulated in the
+ LADSPA_Properties type. This is assembled by ORing individual
+ properties together. */
+
+typedef int LADSPA_Properties;
+
+/* Property LADSPA_PROPERTY_REALTIME indicates that the plugin has a
+ real-time dependency (e.g. listens to a MIDI device) and so its
+ output must not be cached or subject to significant latency. */
+#define LADSPA_PROPERTY_REALTIME 0x1
+
+/* Property LADSPA_PROPERTY_INPLACE_BROKEN indicates that the plugin
+ may cease to work correctly if the host elects to use the same data
+ location for both input and output (see connect_port()). This
+ should be avoided as enabling this flag makes it impossible for
+ hosts to use the plugin to process audio `in-place.' */
+#define LADSPA_PROPERTY_INPLACE_BROKEN 0x2
+
+/* Property LADSPA_PROPERTY_HARD_RT_CAPABLE indicates that the plugin
+ is capable of running not only in a conventional host but also in a
+ `hard real-time' environment. To qualify for this the plugin must
+ satisfy all of the following:
+
+ (1) The plugin must not use malloc(), free() or other heap memory
+ management within its run() or run_adding() functions. All new
+ memory used in run() must be managed via the stack. These
+ restrictions only apply to the run() function.
+
+ (2) The plugin will not attempt to make use of any library
+ functions with the exceptions of functions in the ANSI standard C
+ and C maths libraries, which the host is expected to provide.
+
+ (3) The plugin will not access files, devices, pipes, sockets, IPC
+ or any other mechanism that might result in process or thread
+ blocking.
+
+ (4) The plugin will take an amount of time to execute a run() or
+ run_adding() call approximately of form (A+B*SampleCount) where A
+ and B depend on the machine and host in use. This amount of time
+ may not depend on input signals or plugin state. The host is left
+ the responsibility to perform timings to estimate upper bounds for
+ A and B. */
+#define LADSPA_PROPERTY_HARD_RT_CAPABLE 0x4
+
+#define LADSPA_IS_REALTIME(x) ((x) & LADSPA_PROPERTY_REALTIME)
+#define LADSPA_IS_INPLACE_BROKEN(x) ((x) & LADSPA_PROPERTY_INPLACE_BROKEN)
+#define LADSPA_IS_HARD_RT_CAPABLE(x) ((x) & LADSPA_PROPERTY_HARD_RT_CAPABLE)
+
+/*****************************************************************************/
+
+/* Plugin Ports:
+
+ Plugins have `ports' that are inputs or outputs for audio or
+ data. Ports can communicate arrays of LADSPA_Data (for audio
+ inputs/outputs) or single LADSPA_Data values (for control
+ input/outputs). This information is encapsulated in the
+ LADSPA_PortDescriptor type which is assembled by ORing individual
+ properties together.
+
+ Note that a port must be an input or an output port but not both
+ and that a port must be a control or audio port but not both. */
+
+typedef int LADSPA_PortDescriptor;
+
+/* Property LADSPA_PORT_INPUT indicates that the port is an input. */
+#define LADSPA_PORT_INPUT 0x1
+
+/* Property LADSPA_PORT_OUTPUT indicates that the port is an output. */
+#define LADSPA_PORT_OUTPUT 0x2
+
+/* Property LADSPA_PORT_CONTROL indicates that the port is a control
+ port. */
+#define LADSPA_PORT_CONTROL 0x4
+
+/* Property LADSPA_PORT_AUDIO indicates that the port is a audio
+ port. */
+#define LADSPA_PORT_AUDIO 0x8
+
+#define LADSPA_IS_PORT_INPUT(x) ((x) & LADSPA_PORT_INPUT)
+#define LADSPA_IS_PORT_OUTPUT(x) ((x) & LADSPA_PORT_OUTPUT)
+#define LADSPA_IS_PORT_CONTROL(x) ((x) & LADSPA_PORT_CONTROL)
+#define LADSPA_IS_PORT_AUDIO(x) ((x) & LADSPA_PORT_AUDIO)
+
+/*****************************************************************************/
+
+/* Plugin Port Range Hints:
+
+ The host may wish to provide a representation of data entering or
+ leaving a plugin (e.g. to generate a GUI automatically). To make
+ this more meaningful, the plugin should provide `hints' to the host
+ describing the usual values taken by the data.
+
+ Note that these are only hints. The host may ignore them and the
+ plugin must not assume that data supplied to it is meaningful. If
+ the plugin receives invalid input data it is expected to continue
+ to run without failure and, where possible, produce a sensible
+ output (e.g. a high-pass filter given a negative cutoff frequency
+ might switch to an all-pass mode).
+
+ Hints are meaningful for all input and output ports but hints for
+ input control ports are expected to be particularly useful.
+
+ More hint information is encapsulated in the
+ LADSPA_PortRangeHintDescriptor type which is assembled by ORing
+ individual hint types together. Hints may require further
+ LowerBound and UpperBound information.
+
+ All the hint information for a particular port is aggregated in the
+ LADSPA_PortRangeHint structure. */
+
+typedef int LADSPA_PortRangeHintDescriptor;
+
+/* Hint LADSPA_HINT_BOUNDED_BELOW indicates that the LowerBound field
+ of the LADSPA_PortRangeHint should be considered meaningful. The
+ value in this field should be considered the (inclusive) lower
+ bound of the valid range. If LADSPA_HINT_SAMPLE_RATE is also
+ specified then the value of LowerBound should be multiplied by the
+ sample rate. */
+#define LADSPA_HINT_BOUNDED_BELOW 0x1
+
+/* Hint LADSPA_HINT_BOUNDED_ABOVE indicates that the UpperBound field
+ of the LADSPA_PortRangeHint should be considered meaningful. The
+ value in this field should be considered the (inclusive) upper
+ bound of the valid range. If LADSPA_HINT_SAMPLE_RATE is also
+ specified then the value of UpperBound should be multiplied by the
+ sample rate. */
+#define LADSPA_HINT_BOUNDED_ABOVE 0x2
+
+/* Hint LADSPA_HINT_TOGGLED indicates that the data item should be
+ considered a Boolean toggle. Data less than or equal to zero should
+ be considered `off' or `false,' and data above zero should be
+ considered `on' or `true.' LADSPA_HINT_TOGGLED may not be used in
+ conjunction with any other hint except LADSPA_HINT_DEFAULT_0 or
+ LADSPA_HINT_DEFAULT_1. */
+#define LADSPA_HINT_TOGGLED 0x4
+
+/* Hint LADSPA_HINT_SAMPLE_RATE indicates that any bounds specified
+ should be interpreted as multiples of the sample rate. For
+ instance, a frequency range from 0Hz to the Nyquist frequency (half
+ the sample rate) could be requested by this hint in conjunction
+ with LowerBound = 0 and UpperBound = 0.5. Hosts that support bounds
+ at all must support this hint to retain meaning. */
+#define LADSPA_HINT_SAMPLE_RATE 0x8
+
+/* Hint LADSPA_HINT_LOGARITHMIC indicates that it is likely that the
+ user will find it more intuitive to view values using a logarithmic
+ scale. This is particularly useful for frequencies and gains. */
+#define LADSPA_HINT_LOGARITHMIC 0x10
+
+/* Hint LADSPA_HINT_INTEGER indicates that a user interface would
+ probably wish to provide a stepped control taking only integer
+ values. Any bounds set should be slightly wider than the actual
+ integer range required to avoid floating point rounding errors. For
+ instance, the integer set {0,1,2,3} might be described as [-0.1,
+ 3.1]. */
+#define LADSPA_HINT_INTEGER 0x20
+
+/* The various LADSPA_HINT_HAS_DEFAULT_* hints indicate a `normal'
+ value for the port that is sensible as a default. For instance,
+ this value is suitable for use as an initial value in a user
+ interface or as a value the host might assign to a control port
+ when the user has not provided one. Defaults are encoded using a
+ mask so only one default may be specified for a port. Some of the
+ hints make use of lower and upper bounds, in which case the
+ relevant bound or bounds must be available and
+ LADSPA_HINT_SAMPLE_RATE must be applied as usual. The resulting
+ default must be rounded if LADSPA_HINT_INTEGER is present. Default
+ values were introduced in LADSPA v1.1. */
+#define LADSPA_HINT_DEFAULT_MASK 0x3C0
+
+/* This default values indicates that no default is provided. */
+#define LADSPA_HINT_DEFAULT_NONE 0x0
+
+/* This default hint indicates that the suggested lower bound for the
+ port should be used. */
+#define LADSPA_HINT_DEFAULT_MINIMUM 0x40
+
+/* This default hint indicates that a low value between the suggested
+ lower and upper bounds should be chosen. For ports with
+ LADSPA_HINT_LOGARITHMIC, this should be exp(log(lower) * 0.75 +
+ log(upper) * 0.25). Otherwise, this should be (lower * 0.75 + upper
+ * 0.25). */
+#define LADSPA_HINT_DEFAULT_LOW 0x80
+
+/* This default hint indicates that a middle value between the
+ suggested lower and upper bounds should be chosen. For ports with
+ LADSPA_HINT_LOGARITHMIC, this should be exp(log(lower) * 0.5 +
+ log(upper) * 0.5). Otherwise, this should be (lower * 0.5 + upper *
+ 0.5). */
+#define LADSPA_HINT_DEFAULT_MIDDLE 0xC0
+
+/* This default hint indicates that a high value between the suggested
+ lower and upper bounds should be chosen. For ports with
+ LADSPA_HINT_LOGARITHMIC, this should be exp(log(lower) * 0.25 +
+ log(upper) * 0.75). Otherwise, this should be (lower * 0.25 + upper
+ * 0.75). */
+#define LADSPA_HINT_DEFAULT_HIGH 0x100
+
+/* This default hint indicates that the suggested upper bound for the
+ port should be used. */
+#define LADSPA_HINT_DEFAULT_MAXIMUM 0x140
+
+/* This default hint indicates that the number 0 should be used. Note
+ that this default may be used in conjunction with
+ LADSPA_HINT_TOGGLED. */
+#define LADSPA_HINT_DEFAULT_0 0x200
+
+/* This default hint indicates that the number 1 should be used. Note
+ that this default may be used in conjunction with
+ LADSPA_HINT_TOGGLED. */
+#define LADSPA_HINT_DEFAULT_1 0x240
+
+/* This default hint indicates that the number 100 should be used. */
+#define LADSPA_HINT_DEFAULT_100 0x280
+
+/* This default hint indicates that the Hz frequency of `concert A'
+ should be used. This will be 440 unless the host uses an unusual
+ tuning convention, in which case it may be within a few Hz. */
+#define LADSPA_HINT_DEFAULT_440 0x2C0
+
+#define LADSPA_IS_HINT_BOUNDED_BELOW(x) ((x) & LADSPA_HINT_BOUNDED_BELOW)
+#define LADSPA_IS_HINT_BOUNDED_ABOVE(x) ((x) & LADSPA_HINT_BOUNDED_ABOVE)
+#define LADSPA_IS_HINT_TOGGLED(x) ((x) & LADSPA_HINT_TOGGLED)
+#define LADSPA_IS_HINT_SAMPLE_RATE(x) ((x) & LADSPA_HINT_SAMPLE_RATE)
+#define LADSPA_IS_HINT_LOGARITHMIC(x) ((x) & LADSPA_HINT_LOGARITHMIC)
+#define LADSPA_IS_HINT_INTEGER(x) ((x) & LADSPA_HINT_INTEGER)
+
+#define LADSPA_IS_HINT_HAS_DEFAULT(x) ((x) & LADSPA_HINT_DEFAULT_MASK)
+#define LADSPA_IS_HINT_DEFAULT_MINIMUM(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \
+ == LADSPA_HINT_DEFAULT_MINIMUM)
+#define LADSPA_IS_HINT_DEFAULT_LOW(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \
+ == LADSPA_HINT_DEFAULT_LOW)
+#define LADSPA_IS_HINT_DEFAULT_MIDDLE(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \
+ == LADSPA_HINT_DEFAULT_MIDDLE)
+#define LADSPA_IS_HINT_DEFAULT_HIGH(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \
+ == LADSPA_HINT_DEFAULT_HIGH)
+#define LADSPA_IS_HINT_DEFAULT_MAXIMUM(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \
+ == LADSPA_HINT_DEFAULT_MAXIMUM)
+#define LADSPA_IS_HINT_DEFAULT_0(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \
+ == LADSPA_HINT_DEFAULT_0)
+#define LADSPA_IS_HINT_DEFAULT_1(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \
+ == LADSPA_HINT_DEFAULT_1)
+#define LADSPA_IS_HINT_DEFAULT_100(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \
+ == LADSPA_HINT_DEFAULT_100)
+#define LADSPA_IS_HINT_DEFAULT_440(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \
+ == LADSPA_HINT_DEFAULT_440)
+
+typedef struct _LADSPA_PortRangeHint {
+
+ /* Hints about the port. */
+ LADSPA_PortRangeHintDescriptor HintDescriptor;
+
+ /* Meaningful when hint LADSPA_HINT_BOUNDED_BELOW is active. When
+ LADSPA_HINT_SAMPLE_RATE is also active then this value should be
+ multiplied by the relevant sample rate. */
+ LADSPA_Data LowerBound;
+
+ /* Meaningful when hint LADSPA_HINT_BOUNDED_ABOVE is active. When
+ LADSPA_HINT_SAMPLE_RATE is also active then this value should be
+ multiplied by the relevant sample rate. */
+ LADSPA_Data UpperBound;
+
+} LADSPA_PortRangeHint;
+
+/*****************************************************************************/
+
+/* Plugin Handles:
+
+ This plugin handle indicates a particular instance of the plugin
+ concerned. It is valid to compare this to NULL (0 for C++) but
+ otherwise the host should not attempt to interpret it. The plugin
+ may use it to reference internal instance data. */
+
+typedef void * LADSPA_Handle;
+
+/*****************************************************************************/
+
+/* Descriptor for a Type of Plugin:
+
+ This structure is used to describe a plugin type. It provides a
+ number of functions to examine the type, instantiate it, link it to
+ buffers and workspaces and to run it. */
+
+typedef struct _LADSPA_Descriptor {
+
+ /* This numeric identifier indicates the plugin type
+ uniquely. Plugin programmers may reserve ranges of IDs from a
+ central body to avoid clashes. Hosts may assume that IDs are
+ below 0x1000000. */
+ unsigned long UniqueID;
+
+ /* This identifier can be used as a unique, case-sensitive
+ identifier for the plugin type within the plugin file. Plugin
+ types should be identified by file and label rather than by index
+ or plugin name, which may be changed in new plugin
+ versions. Labels must not contain white-space characters. */
+ const char * Label;
+
+ /* This indicates a number of properties of the plugin. */
+ LADSPA_Properties Properties;
+
+ /* This member points to the null-terminated name of the plugin
+ (e.g. "Sine Oscillator"). */
+ const char * Name;
+
+ /* This member points to the null-terminated string indicating the
+ maker of the plugin. This can be an empty string but not NULL. */
+ const char * Maker;
+
+ /* This member points to the null-terminated string indicating any
+ copyright applying to the plugin. If no Copyright applies the
+ string "None" should be used. */
+ const char * Copyright;
+
+ /* This indicates the number of ports (input AND output) present on
+ the plugin. */
+ unsigned long PortCount;
+
+ /* This member indicates an array of port descriptors. Valid indices
+ vary from 0 to PortCount-1. */
+ const LADSPA_PortDescriptor * PortDescriptors;
+
+ /* This member indicates an array of null-terminated strings
+ describing ports (e.g. "Frequency (Hz)"). Valid indices vary from
+ 0 to PortCount-1. */
+ const char * const * PortNames;
+
+ /* This member indicates an array of range hints for each port (see
+ above). Valid indices vary from 0 to PortCount-1. */
+ const LADSPA_PortRangeHint * PortRangeHints;
+
+ /* This may be used by the plugin developer to pass any custom
+ implementation data into an instantiate call. It must not be used
+ or interpreted by the host. It is expected that most plugin
+ writers will not use this facility as LADSPA_Handle should be
+ used to hold instance data. */
+ void * ImplementationData;
+
+ /* This member is a function pointer that instantiates a plugin. A
+ handle is returned indicating the new plugin instance. The
+ instantiation function accepts a sample rate as a parameter. The
+ plugin descriptor from which this instantiate function was found
+ must also be passed. This function must return NULL if
+ instantiation fails.
+
+ Note that instance initialisation should generally occur in
+ activate() rather than here. */
+ LADSPA_Handle (*instantiate)(const struct _LADSPA_Descriptor * Descriptor,
+ unsigned long SampleRate);
+
+ /* This member is a function pointer that connects a port on an
+ instantiated plugin to a memory location at which a block of data
+ for the port will be read/written. The data location is expected
+ to be an array of LADSPA_Data for audio ports or a single
+ LADSPA_Data value for control ports. Memory issues will be
+ managed by the host. The plugin must read/write the data at these
+ locations every time run() or run_adding() is called and the data
+ present at the time of this connection call should not be
+ considered meaningful.
+
+ connect_port() may be called more than once for a plugin instance
+ to allow the host to change the buffers that the plugin is
+ reading or writing. These calls may be made before or after
+ activate() or deactivate() calls.
+
+ connect_port() must be called at least once for each port before
+ run() or run_adding() is called. When working with blocks of
+ LADSPA_Data the plugin should pay careful attention to the block
+ size passed to the run function as the block allocated may only
+ just be large enough to contain the block of samples.
+
+ Plugin writers should be aware that the host may elect to use the
+ same buffer for more than one port and even use the same buffer
+ for both input and output (see LADSPA_PROPERTY_INPLACE_BROKEN).
+ However, overlapped buffers or use of a single buffer for both
+ audio and control data may result in unexpected behaviour. */
+ void (*connect_port)(LADSPA_Handle Instance,
+ unsigned long Port,
+ LADSPA_Data * DataLocation);
+
+ /* This member is a function pointer that initialises a plugin
+ instance and activates it for use. This is separated from
+ instantiate() to aid real-time support and so that hosts can
+ reinitialise a plugin instance by calling deactivate() and then
+ activate(). In this case the plugin instance must reset all state
+ information dependent on the history of the plugin instance
+ except for any data locations provided by connect_port() and any
+ gain set by set_run_adding_gain(). If there is nothing for
+ activate() to do then the plugin writer may provide a NULL rather
+ than an empty function.
+
+ When present, hosts must call this function once before run() (or
+ run_adding()) is called for the first time. This call should be
+ made as close to the run() call as possible and indicates to
+ real-time plugins that they are now live. Plugins should not rely
+ on a prompt call to run() after activate(). activate() may not be
+ called again unless deactivate() is called first. Note that
+ connect_port() may be called before or after a call to
+ activate(). */
+ void (*activate)(LADSPA_Handle Instance);
+
+ /* This method is a function pointer that runs an instance of a
+ plugin for a block. Two parameters are required: the first is a
+ handle to the particular instance to be run and the second
+ indicates the block size (in samples) for which the plugin
+ instance may run.
+
+ Note that if an activate() function exists then it must be called
+ before run() or run_adding(). If deactivate() is called for a
+ plugin instance then the plugin instance may not be reused until
+ activate() has been called again.
+
+ If the plugin has the property LADSPA_PROPERTY_HARD_RT_CAPABLE
+ then there are various things that the plugin should not do
+ within the run() or run_adding() functions (see above). */
+ void (*run)(LADSPA_Handle Instance,
+ unsigned long SampleCount);
+
+ /* This method is a function pointer that runs an instance of a
+ plugin for a block. This has identical behaviour to run() except
+ in the way data is output from the plugin. When run() is used,
+ values are written directly to the memory areas associated with
+ the output ports. However when run_adding() is called, values
+ must be added to the values already present in the memory
+ areas. Furthermore, output values written must be scaled by the
+ current gain set by set_run_adding_gain() (see below) before
+ addition.
+
+ run_adding() is optional. When it is not provided by a plugin,
+ this function pointer must be set to NULL. When it is provided,
+ the function set_run_adding_gain() must be provided also. */
+ void (*run_adding)(LADSPA_Handle Instance,
+ unsigned long SampleCount);
+
+ /* This method is a function pointer that sets the output gain for
+ use when run_adding() is called (see above). If this function is
+ never called the gain is assumed to default to 1. Gain
+ information should be retained when activate() or deactivate()
+ are called.
+
+ This function should be provided by the plugin if and only if the
+ run_adding() function is provided. When it is absent this
+ function pointer must be set to NULL. */
+ void (*set_run_adding_gain)(LADSPA_Handle Instance,
+ LADSPA_Data Gain);
+
+ /* This is the counterpart to activate() (see above). If there is
+ nothing for deactivate() to do then the plugin writer may provide
+ a NULL rather than an empty function.
+
+ Hosts must deactivate all activated units after they have been
+ run() (or run_adding()) for the last time. This call should be
+ made as close to the last run() call as possible and indicates to
+ real-time plugins that they are no longer live. Plugins should
+ not rely on prompt deactivation. Note that connect_port() may be
+ called before or after a call to deactivate().
+
+ Deactivation is not similar to pausing as the plugin instance
+ will be reinitialised when activate() is called to reuse it. */
+ void (*deactivate)(LADSPA_Handle Instance);
+
+ /* Once an instance of a plugin has been finished with it can be
+ deleted using the following function. The instance handle passed
+ ceases to be valid after this call.
+
+ If activate() was called for a plugin instance then a
+ corresponding call to deactivate() must be made before cleanup()
+ is called. */
+ void (*cleanup)(LADSPA_Handle Instance);
+
+} LADSPA_Descriptor;
+
+/**********************************************************************/
+
+/* Accessing a Plugin: */
+
+/* The exact mechanism by which plugins are loaded is host-dependent,
+ however all most hosts will need to know is the name of shared
+ object file containing the plugin types. To allow multiple hosts to
+ share plugin types, hosts may wish to check for environment
+ variable LADSPA_PATH. If present, this should contain a
+ colon-separated path indicating directories that should be searched
+ (in order) when loading plugin types.
+
+ A plugin programmer must include a function called
+ "ladspa_descriptor" with the following function prototype within
+ the shared object file. This function will have C-style linkage (if
+ you are using C++ this is taken care of by the `extern "C"' clause
+ at the top of the file).
+
+ A host will find the plugin shared object file by one means or
+ another, find the ladspa_descriptor() function, call it, and
+ proceed from there.
+
+ Plugin types are accessed by index (not ID) using values from 0
+ upwards. Out of range indexes must result in this function
+ returning NULL, so the plugin count can be determined by checking
+ for the least index that results in NULL being returned. */
+
+const LADSPA_Descriptor * ladspa_descriptor(unsigned long Index);
+
+/* Datatype corresponding to the ladspa_descriptor() function. */
+typedef const LADSPA_Descriptor *
+(*LADSPA_Descriptor_Function)(unsigned long Index);
+
+/**********************************************************************/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LADSPA_INCLUDED */
+
+/* EOF */
diff --git a/src/modules/module-alsa-sink.c b/src/modules/module-alsa-sink.c
index 3d9f7577..a09247fe 100644
--- a/src/modules/module-alsa-sink.c
+++ b/src/modules/module-alsa-sink.c
@@ -26,18 +26,12 @@
#include <config.h>
#endif
-#include <assert.h>
#include <stdio.h>
-#ifdef HAVE_SYS_POLL_H
-#include <sys/poll.h>
-#else
-#include "poll.h"
-#endif
-
#include <asoundlib.h>
#include <pulse/xmalloc.h>
+#include <pulse/util.h>
#include <pulsecore/core.h>
#include <pulsecore/module.h>
@@ -47,6 +41,11 @@
#include <pulsecore/core-util.h>
#include <pulsecore/sample-util.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/rtpoll.h>
#include "alsa-util.h"
#include "module-alsa-sink-symdef.h"
@@ -62,20 +61,38 @@ PA_MODULE_USAGE(
"rate=<sample rate> "
"fragments=<number of fragments> "
"fragment_size=<fragment size> "
- "channel_map=<channel map>")
+ "channel_map=<channel map> "
+ "mmap=<enable memory mapping?>")
+
+#define DEFAULT_DEVICE "default"
struct userdata {
+ pa_core *core;
+ pa_module *module;
+ pa_sink *sink;
+
+ pa_thread *thread;
+ pa_thread_mq thread_mq;
+ pa_rtpoll *rtpoll;
+
snd_pcm_t *pcm_handle;
+
+ pa_alsa_fdlist *mixer_fdl;
snd_mixer_t *mixer_handle;
snd_mixer_elem_t *mixer_elem;
- pa_sink *sink;
- struct pa_alsa_fdlist *pcm_fdl;
- struct pa_alsa_fdlist *mixer_fdl;
long hw_volume_max, hw_volume_min;
- size_t frame_size, fragment_size;
- pa_memchunk memchunk, silence;
- pa_module *module;
+ size_t frame_size, fragment_size, hwbuf_size;
+ unsigned nfragments;
+ pa_memchunk memchunk;
+
+ char *device_name;
+
+ int use_mmap;
+
+ int first;
+
+ pa_rtpoll_item *alsa_rtpoll_item;
};
static const char* const valid_modargs[] = {
@@ -87,260 +104,440 @@ static const char* const valid_modargs[] = {
"fragments",
"fragment_size",
"channel_map",
+ "mmap",
NULL
};
-#define DEFAULT_DEVICE "default"
+static int mmap_write(struct userdata *u) {
+ int work_done = 0;
-static void update_usage(struct userdata *u) {
- pa_module_set_used(u->module, u->sink ? pa_sink_used_by(u->sink) : 0);
-}
+ pa_assert(u);
+ pa_sink_assert_ref(u->sink);
-static void clear_up(struct userdata *u) {
- assert(u);
+ for (;;) {
+ pa_memchunk chunk;
+ void *p;
+ snd_pcm_sframes_t n;
+ int err;
+ const snd_pcm_channel_area_t *areas;
+ snd_pcm_uframes_t offset, frames;
+
+ if ((n = snd_pcm_avail_update(u->pcm_handle)) < 0) {
+
+ if (n == -EPIPE) {
+ pa_log_debug("snd_pcm_avail_update: Buffer underrun!");
+ u->first = 1;
+ }
- if (u->sink) {
- pa_sink_disconnect(u->sink);
- pa_sink_unref(u->sink);
- u->sink = NULL;
- }
+ if ((err = snd_pcm_recover(u->pcm_handle, n, 1)) == 0)
+ continue;
- if (u->pcm_fdl)
- pa_alsa_fdlist_free(u->pcm_fdl);
- if (u->mixer_fdl)
- pa_alsa_fdlist_free(u->mixer_fdl);
+ if (err == -EAGAIN)
+ return work_done;
- u->pcm_fdl = u->mixer_fdl = NULL;
+ pa_log("snd_pcm_avail_update: %s", snd_strerror(err));
+ return -1;
+ }
- if (u->mixer_handle) {
- snd_mixer_close(u->mixer_handle);
- u->mixer_handle = NULL;
- }
+/* pa_log("Got request for %i samples", (int) n); */
- if (u->pcm_handle) {
- snd_pcm_drop(u->pcm_handle);
- snd_pcm_close(u->pcm_handle);
- u->pcm_handle = NULL;
- }
-}
+ if (n <= 0)
+ return work_done;
-static int xrun_recovery(struct userdata *u) {
- int ret;
- assert(u);
+ frames = n;
- pa_log_info("*** ALSA-XRUN (playback) ***");
+ if ((err = snd_pcm_mmap_begin(u->pcm_handle, &areas, &offset, &frames)) < 0) {
- if ((ret = snd_pcm_prepare(u->pcm_handle)) < 0) {
- pa_log("snd_pcm_prepare() failed: %s", snd_strerror(-ret));
+ if (err == -EPIPE) {
+ pa_log_debug("snd_pcm_mmap_begin: Buffer underrun!");
+ u->first = 1;
+ }
- clear_up(u);
- pa_module_unload_request(u->module);
- return -1;
- }
+ if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) == 0)
+ continue;
- return ret;
-}
+ if (err == -EAGAIN)
+ return work_done;
-static int suspend_recovery(struct userdata *u) {
- int ret;
- assert(u);
+ pa_log("Failed to write data to DSP: %s", snd_strerror(err));
+ return -1;
+ }
- pa_log_info("*** ALSA-SUSPEND (playback) ***");
+ /* Check these are multiples of 8 bit */
+ pa_assert((areas[0].first & 7) == 0);
+ pa_assert((areas[0].step & 7)== 0);
- if ((ret = snd_pcm_resume(u->pcm_handle)) < 0) {
- if (ret == -EAGAIN)
- return -1;
+ /* We assume a single interleaved memory buffer */
+ pa_assert((areas[0].first >> 3) == 0);
+ pa_assert((areas[0].step >> 3) == u->frame_size);
- if (ret != -ENOSYS)
- pa_log("snd_pcm_resume() failed: %s", snd_strerror(-ret));
- else {
- if ((ret = snd_pcm_prepare(u->pcm_handle)) < 0)
- pa_log("snd_pcm_prepare() failed: %s", snd_strerror(-ret));
- }
+ p = (uint8_t*) areas[0].addr + (offset * u->frame_size);
+
+ chunk.memblock = pa_memblock_new_fixed(u->core->mempool, p, frames * u->frame_size, 1);
+ chunk.length = pa_memblock_get_length(chunk.memblock);
+ chunk.index = 0;
+
+ pa_sink_render_into_full(u->sink, &chunk);
+
+ /* FIXME: Maybe we can do something to keep this memory block
+ * a little bit longer around? */
+ pa_memblock_unref_fixed(chunk.memblock);
+
+ if ((err = snd_pcm_mmap_commit(u->pcm_handle, offset, frames)) < 0) {
+
+ if (err == -EPIPE) {
+ pa_log_debug("snd_pcm_mmap_commit: Buffer underrun!");
+ u->first = 1;
+ }
+
+ if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) == 0)
+ continue;
- if (ret < 0) {
- clear_up(u);
- pa_module_unload_request(u->module);
+ if (err == -EAGAIN)
+ return work_done;
+
+ pa_log("Failed to write data to DSP: %s", snd_strerror(err));
return -1;
}
- }
- return ret;
+ work_done = 1;
+
+ if (frames >= (snd_pcm_uframes_t) n)
+ return work_done;
+
+/* pa_log("wrote %i samples", (int) frames); */
+ }
}
-static void do_write(struct userdata *u) {
- assert(u);
+static int unix_write(struct userdata *u) {
+ snd_pcm_status_t *status;
+ int work_done = 0;
- update_usage(u);
+ snd_pcm_status_alloca(&status);
+
+ pa_assert(u);
+ pa_sink_assert_ref(u->sink);
for (;;) {
- pa_memchunk *memchunk = NULL;
- snd_pcm_sframes_t frames;
-
- if (u->memchunk.memblock)
- memchunk = &u->memchunk;
- else {
- if (pa_sink_render(u->sink, u->fragment_size, &u->memchunk) < 0)
- memchunk = &u->silence;
- else
- memchunk = &u->memchunk;
+ void *p;
+ snd_pcm_sframes_t t;
+ ssize_t l;
+ int err;
+
+ if ((err = snd_pcm_status(u->pcm_handle, status)) < 0) {
+ pa_log("Failed to query DSP status data: %s", snd_strerror(err));
+ return -1;
}
- assert(memchunk->memblock);
- assert(memchunk->memblock->data);
- assert(memchunk->length);
- assert(memchunk->memblock->length);
- assert((memchunk->length % u->frame_size) == 0);
+ if (snd_pcm_status_get_avail_max(status)*u->frame_size >= u->hwbuf_size)
+ pa_log_debug("Buffer underrun!");
- if ((frames = snd_pcm_writei(u->pcm_handle, (uint8_t*) memchunk->memblock->data + memchunk->index, memchunk->length / u->frame_size)) < 0) {
- if (frames == -EAGAIN)
- return;
+ l = snd_pcm_status_get_avail(status) * u->frame_size;
- if (frames == -EPIPE) {
- if (xrun_recovery(u) < 0)
- return;
+/* pa_log("%u bytes to write", l); */
- continue;
- }
+ if (l <= 0)
+ return work_done;
+
+ if (u->memchunk.length <= 0)
+ pa_sink_render(u->sink, l, &u->memchunk);
+
+ pa_assert(u->memchunk.length > 0);
- if (frames == -ESTRPIPE) {
- if (suspend_recovery(u) < 0)
- return;
+ p = pa_memblock_acquire(u->memchunk.memblock);
+ t = snd_pcm_writei(u->pcm_handle, (const uint8_t*) p + u->memchunk.index, u->memchunk.length / u->frame_size);
+ pa_memblock_release(u->memchunk.memblock);
+/* pa_log("wrote %i bytes of %u (%u)", t*u->frame_size, u->memchunk.length, l); */
+
+ pa_assert(t != 0);
+
+ if (t < 0) {
+
+ if ((t = snd_pcm_recover(u->pcm_handle, t, 1)) == 0)
continue;
+
+ if (t == -EAGAIN) {
+ pa_log_debug("EAGAIN");
+ return work_done;
+ } else {
+ pa_log("Failed to write data to DSP: %s", snd_strerror(t));
+ return -1;
}
+ }
- pa_log("snd_pcm_writei() failed: %s", snd_strerror(-frames));
+ u->memchunk.index += t * u->frame_size;
+ u->memchunk.length -= t * u->frame_size;
- clear_up(u);
- pa_module_unload_request(u->module);
- return;
+ if (u->memchunk.length <= 0) {
+ pa_memblock_unref(u->memchunk.memblock);
+ pa_memchunk_reset(&u->memchunk);
}
- if (memchunk == &u->memchunk) {
- size_t l = frames * u->frame_size;
- memchunk->index += l;
- memchunk->length -= l;
+ work_done = 1;
- if (memchunk->length == 0) {
- pa_memblock_unref(memchunk->memblock);
- memchunk->memblock = NULL;
- memchunk->index = memchunk->length = 0;
- }
- }
+ if (t * u->frame_size >= (unsigned) l)
+ return work_done;
+ }
+}
- break;
+static pa_usec_t sink_get_latency(struct userdata *u) {
+ pa_usec_t r = 0;
+ snd_pcm_status_t *status;
+ snd_pcm_sframes_t frames = 0;
+ int err;
+
+ snd_pcm_status_alloca(&status);
+
+ pa_assert(u);
+ pa_assert(u->pcm_handle);
+
+ if ((err = snd_pcm_status(u->pcm_handle, status)) < 0)
+ pa_log("Failed to get delay: %s", snd_strerror(err));
+ else
+ frames = snd_pcm_status_get_delay(status);
+
+ if (frames > 0)
+ r = pa_bytes_to_usec(frames * u->frame_size, &u->sink->sample_spec);
+
+ if (u->memchunk.memblock)
+ r += pa_bytes_to_usec(u->memchunk.length, &u->sink->sample_spec);
+
+ return r;
+}
+
+static int build_pollfd(struct userdata *u) {
+ int err;
+ struct pollfd *pollfd;
+ int n;
+
+ pa_assert(u);
+ pa_assert(u->pcm_handle);
+
+ if ((n = snd_pcm_poll_descriptors_count(u->pcm_handle)) < 0) {
+ pa_log("snd_pcm_poll_descriptors_count() failed: %s", snd_strerror(n));
+ return -1;
+ }
+
+ if (u->alsa_rtpoll_item)
+ pa_rtpoll_item_free(u->alsa_rtpoll_item);
+
+ u->alsa_rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, n);
+ pollfd = pa_rtpoll_item_get_pollfd(u->alsa_rtpoll_item, NULL);
+
+ if ((err = snd_pcm_poll_descriptors(u->pcm_handle, pollfd, n)) < 0) {
+ pa_log("snd_pcm_poll_descriptors() failed: %s", snd_strerror(err));
+ return -1;
}
+
+ return 0;
}
-static void fdl_callback(void *userdata) {
- struct userdata *u = userdata;
- assert(u);
+static int suspend(struct userdata *u) {
+ pa_assert(u);
+ pa_assert(u->pcm_handle);
- if (snd_pcm_state(u->pcm_handle) == SND_PCM_STATE_XRUN)
- if (xrun_recovery(u) < 0)
- return;
+ /* Let's suspend */
+ snd_pcm_drain(u->pcm_handle);
+ snd_pcm_close(u->pcm_handle);
+ u->pcm_handle = NULL;
- if (snd_pcm_state(u->pcm_handle) == SND_PCM_STATE_SUSPENDED)
- if (suspend_recovery(u) < 0)
- return;
+ if (u->alsa_rtpoll_item) {
+ pa_rtpoll_item_free(u->alsa_rtpoll_item);
+ u->alsa_rtpoll_item = NULL;
+ }
+
+ pa_log_info("Device suspended...");
- do_write(u);
+ return 0;
}
-static int mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {
- struct userdata *u = snd_mixer_elem_get_callback_private(elem);
+static int unsuspend(struct userdata *u) {
+ pa_sample_spec ss;
+ int err, b;
+ unsigned nfrags;
+ snd_pcm_uframes_t period_size;
- assert(u && u->mixer_handle);
+ pa_assert(u);
+ pa_assert(!u->pcm_handle);
- if (mask == SND_CTL_EVENT_MASK_REMOVE)
- return 0;
+ pa_log_info("Trying resume...");
- if (mask & SND_CTL_EVENT_MASK_VALUE) {
- if (u->sink->get_hw_volume)
- u->sink->get_hw_volume(u->sink);
- if (u->sink->get_hw_mute)
- u->sink->get_hw_mute(u->sink);
- pa_subscription_post(u->sink->core,
- PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE,
- u->sink->index);
+ snd_config_update_free_global();
+ if ((err = snd_pcm_open(&u->pcm_handle, u->device_name, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0) {
+ pa_log("Error opening PCM device %s: %s", u->device_name, snd_strerror(err));
+ goto fail;
+ }
+
+ ss = u->sink->sample_spec;
+ nfrags = u->nfragments;
+ period_size = u->fragment_size / u->frame_size;
+ b = u->use_mmap;
+
+ if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &nfrags, &period_size, &b)) < 0) {
+ pa_log("Failed to set hardware parameters: %s", snd_strerror(err));
+ goto fail;
+ }
+
+ if (b != u->use_mmap) {
+ pa_log_warn("Resume failed, couldn't get original access mode.");
+ goto fail;
+ }
+
+ if (!pa_sample_spec_equal(&ss, &u->sink->sample_spec)) {
+ pa_log_warn("Resume failed, couldn't restore original sample settings.");
+ goto fail;
+ }
+
+ if (nfrags != u->nfragments || period_size*u->frame_size != u->fragment_size) {
+ pa_log_warn("Resume failed, couldn't restore original fragment settings.");
+ goto fail;
+ }
+
+ if ((err = pa_alsa_set_sw_params(u->pcm_handle)) < 0) {
+ pa_log("Failed to set software parameters: %s", snd_strerror(err));
+ goto fail;
}
+ if (build_pollfd(u) < 0)
+ goto fail;
+
+ /* FIXME: We need to reload the volume somehow */
+
+ u->first = 1;
+
+ pa_log_info("Resumed successfully...");
+
return 0;
+
+fail:
+ if (u->pcm_handle) {
+ snd_pcm_close(u->pcm_handle);
+ u->pcm_handle = NULL;
+ }
+
+ return -1;
}
-static pa_usec_t sink_get_latency_cb(pa_sink *s) {
- pa_usec_t r = 0;
- struct userdata *u = s->userdata;
- snd_pcm_sframes_t frames;
- int err;
+static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+ struct userdata *u = PA_SINK(o)->userdata;
- assert(s && u && u->sink);
+ switch (code) {
- if ((err = snd_pcm_delay(u->pcm_handle, &frames)) < 0) {
- pa_log("failed to get delay: %s", snd_strerror(err));
- s->get_latency = NULL;
- return 0;
+ case PA_SINK_MESSAGE_GET_LATENCY: {
+ pa_usec_t r = 0;
+
+ if (u->pcm_handle)
+ r = sink_get_latency(u);
+
+ *((pa_usec_t*) data) = r;
+
+ return 0;
+ }
+
+ case PA_SINK_MESSAGE_SET_STATE:
+
+ switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
+
+ case PA_SINK_SUSPENDED:
+ pa_assert(PA_SINK_OPENED(u->sink->thread_info.state));
+
+ if (suspend(u) < 0)
+ return -1;
+
+ break;
+
+ case PA_SINK_IDLE:
+ case PA_SINK_RUNNING:
+
+ if (u->sink->thread_info.state == PA_SINK_INIT) {
+ if (build_pollfd(u) < 0)
+ return -1;
+ }
+
+ if (u->sink->thread_info.state == PA_SINK_SUSPENDED) {
+ if (unsuspend(u) < 0)
+ return -1;
+ }
+
+ break;
+
+ case PA_SINK_UNLINKED:
+ case PA_SINK_INIT:
+ ;
+ }
+
+ break;
}
- if (frames < 0)
- frames = 0;
+ return pa_sink_process_msg(o, code, data, offset, chunk);
+}
- r += pa_bytes_to_usec(frames * u->frame_size, &s->sample_spec);
+static int mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {
+ struct userdata *u = snd_mixer_elem_get_callback_private(elem);
- if (u->memchunk.memblock)
- r += pa_bytes_to_usec(u->memchunk.length, &s->sample_spec);
+ pa_assert(u);
+ pa_assert(u->mixer_handle);
- return r;
+ if (mask == SND_CTL_EVENT_MASK_REMOVE)
+ return 0;
+
+ if (mask & SND_CTL_EVENT_MASK_VALUE) {
+ pa_sink_get_volume(u->sink);
+ pa_sink_get_mute(u->sink);
+ }
+
+ return 0;
}
-static int sink_get_hw_volume_cb(pa_sink *s) {
+static int sink_get_volume_cb(pa_sink *s) {
struct userdata *u = s->userdata;
int err;
int i;
- assert(u);
- assert(u->mixer_elem);
+ pa_assert(u);
+ pa_assert(u->mixer_elem);
- for (i = 0; i < s->hw_volume.channels; i++) {
+ for (i = 0; i < s->sample_spec.channels; i++) {
long set_vol, vol;
- assert(snd_mixer_selem_has_playback_channel(u->mixer_elem, i));
+ pa_assert(snd_mixer_selem_has_playback_channel(u->mixer_elem, i));
if ((err = snd_mixer_selem_get_playback_volume(u->mixer_elem, i, &vol)) < 0)
goto fail;
- set_vol = (long) roundf(((float) s->hw_volume.values[i] * (u->hw_volume_max - u->hw_volume_min)) / PA_VOLUME_NORM) + u->hw_volume_min;
+ set_vol = (long) roundf(((float) s->volume.values[i] * (u->hw_volume_max - u->hw_volume_min)) / PA_VOLUME_NORM) + u->hw_volume_min;
/* Try to avoid superfluous volume changes */
if (set_vol != vol)
- s->hw_volume.values[i] = (pa_volume_t) roundf(((float) (vol - u->hw_volume_min) * PA_VOLUME_NORM) / (u->hw_volume_max - u->hw_volume_min));
+ s->volume.values[i] = (pa_volume_t) roundf(((float) (vol - u->hw_volume_min) * PA_VOLUME_NORM) / (u->hw_volume_max - u->hw_volume_min));
}
return 0;
fail:
pa_log_error("Unable to read volume: %s", snd_strerror(err));
- s->get_hw_volume = NULL;
- s->set_hw_volume = NULL;
+
+ s->get_volume = NULL;
+ s->set_volume = NULL;
return -1;
}
-static int sink_set_hw_volume_cb(pa_sink *s) {
+static int sink_set_volume_cb(pa_sink *s) {
struct userdata *u = s->userdata;
int err;
int i;
- pa_volume_t vol;
- assert(u);
- assert(u->mixer_elem);
+ pa_assert(u);
+ pa_assert(u->mixer_elem);
- for (i = 0; i < s->hw_volume.channels; i++) {
+ for (i = 0; i < s->sample_spec.channels; i++) {
long alsa_vol;
+ pa_volume_t vol;
- assert(snd_mixer_selem_has_playback_channel(u->mixer_elem, i));
+ pa_assert(snd_mixer_selem_has_playback_channel(u->mixer_elem, i));
- vol = s->hw_volume.values[i];
+ vol = s->volume.values[i];
if (vol > PA_VOLUME_NORM)
vol = PA_VOLUME_NORM;
@@ -355,55 +552,166 @@ static int sink_set_hw_volume_cb(pa_sink *s) {
fail:
pa_log_error("Unable to set volume: %s", snd_strerror(err));
- s->get_hw_volume = NULL;
- s->set_hw_volume = NULL;
+
+ s->get_volume = NULL;
+ s->set_volume = NULL;
return -1;
}
-static int sink_get_hw_mute_cb(pa_sink *s) {
+static int sink_get_mute_cb(pa_sink *s) {
struct userdata *u = s->userdata;
int err, sw;
- assert(u && u->mixer_elem);
+ pa_assert(u);
+ pa_assert(u->mixer_elem);
- err = snd_mixer_selem_get_playback_switch(u->mixer_elem, 0, &sw);
- if (err) {
+ if ((err = snd_mixer_selem_get_playback_switch(u->mixer_elem, 0, &sw)) < 0) {
pa_log_error("Unable to get switch: %s", snd_strerror(err));
- s->get_hw_mute = NULL;
- s->set_hw_mute = NULL;
+
+ s->get_mute = NULL;
+ s->set_mute = NULL;
return -1;
}
- s->hw_muted = !sw;
+ s->muted = !sw;
return 0;
}
-static int sink_set_hw_mute_cb(pa_sink *s) {
+static int sink_set_mute_cb(pa_sink *s) {
struct userdata *u = s->userdata;
int err;
- assert(u && u->mixer_elem);
+ pa_assert(u);
+ pa_assert(u->mixer_elem);
- err = snd_mixer_selem_set_playback_switch_all(u->mixer_elem, !s->hw_muted);
- if (err) {
+ if ((err = snd_mixer_selem_set_playback_switch_all(u->mixer_elem, !s->muted)) < 0) {
pa_log_error("Unable to set switch: %s", snd_strerror(err));
- s->get_hw_mute = NULL;
- s->set_hw_mute = NULL;
+
+ s->get_mute = NULL;
+ s->set_mute = NULL;
return -1;
}
return 0;
}
-int pa__init(pa_core *c, pa_module*m) {
+static void thread_func(void *userdata) {
+ struct userdata *u = userdata;
+
+ pa_assert(u);
+
+ pa_log_debug("Thread starting up");
+
+ if (u->core->high_priority)
+ pa_make_realtime();
+
+ pa_thread_mq_install(&u->thread_mq);
+ pa_rtpoll_install(u->rtpoll);
+
+ for (;;) {
+ int ret;
+
+ /* Render some data and write it to the dsp */
+ if (PA_SINK_OPENED(u->sink->thread_info.state)) {
+ int work_done = 0;
+
+ if (u->use_mmap) {
+ if ((work_done = mmap_write(u)) < 0)
+ goto fail;
+ } else {
+ if ((work_done = unix_write(u)) < 0)
+ goto fail;
+ }
+
+ if (work_done && u->first) {
+ pa_log_info("Starting playback.");
+ snd_pcm_start(u->pcm_handle);
+ u->first = 0;
+ continue;
+ }
+ }
+
+ /* Hmm, nothing to do. Let's sleep */
+ if ((ret = pa_rtpoll_run(u->rtpoll, 1)) < 0)
+ goto fail;
+
+ if (ret == 0)
+ goto finish;
+
+ /* Tell ALSA about this and process its response */
+ if (PA_SINK_OPENED(u->sink->thread_info.state)) {
+ struct pollfd *pollfd;
+ unsigned short revents = 0;
+ int err;
+ unsigned n;
+
+ pollfd = pa_rtpoll_item_get_pollfd(u->alsa_rtpoll_item, &n);
+
+ if ((err = snd_pcm_poll_descriptors_revents(u->pcm_handle, pollfd, n, &revents)) < 0) {
+ pa_log("snd_pcm_poll_descriptors_revents() failed: %s", snd_strerror(err));
+ goto fail;
+ }
+
+ if (revents & (POLLERR|POLLNVAL|POLLHUP)) {
+
+ if (revents & POLLERR)
+ pa_log_warn("Got POLLERR from ALSA");
+ if (revents & POLLNVAL)
+ pa_log_warn("Got POLLNVAL from ALSA");
+ if (revents & POLLHUP)
+ pa_log_warn("Got POLLHUP from ALSA");
+
+ /* Try to recover from this error */
+
+ switch (snd_pcm_state(u->pcm_handle)) {
+
+ case SND_PCM_STATE_XRUN:
+ if ((err = snd_pcm_recover(u->pcm_handle, -EPIPE, 1)) != 0) {
+ pa_log_warn("Could not recover from POLLERR|POLLNVAL|POLLHUP and XRUN: %s", snd_strerror(err));
+ goto fail;
+ }
+ break;
+
+ case SND_PCM_STATE_SUSPENDED:
+ if ((err = snd_pcm_recover(u->pcm_handle, -ESTRPIPE, 1)) != 0) {
+ pa_log_warn("Could not recover from POLLERR|POLLNVAL|POLLHUP and SUSPENDED: %s", snd_strerror(err));
+ goto fail;
+ }
+ break;
+
+ default:
+
+ snd_pcm_drop(u->pcm_handle);
+
+ if ((err = snd_pcm_prepare(u->pcm_handle)) < 0) {
+ pa_log_warn("Could not recover from POLLERR|POLLNVAL|POLLHUP with snd_pcm_prepare(): %s", snd_strerror(err));
+ goto fail;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+fail:
+ /* If this was no regular exit from the loop we have to continue
+ * processing messages until we received PA_MESSAGE_SHUTDOWN */
+ pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+ pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+ pa_log_debug("Thread shutting down");
+}
+
+int pa__init(pa_module*m) {
+
pa_modargs *ma = NULL;
- int ret = -1;
struct userdata *u = NULL;
- const char *dev;
+ char *dev;
pa_sample_spec ss;
pa_channel_map map;
- uint32_t periods, fragsize;
+ uint32_t nfrags, frag_size;
snd_pcm_uframes_t period_size;
size_t frame_size;
snd_pcm_info_t *pcm_info = NULL;
@@ -412,48 +720,107 @@ int pa__init(pa_core *c, pa_module*m) {
const char *name;
char *name_buf = NULL;
int namereg_fail;
+ int use_mmap = 1, b;
+
+ snd_pcm_info_alloca(&pcm_info);
+
+ pa_assert(m);
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
- pa_log("failed to parse module arguments");
+ pa_log("Failed to parse module arguments");
goto fail;
}
- ss = c->default_sample_spec;
+ ss = m->core->default_sample_spec;
if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_ALSA) < 0) {
- pa_log("failed to parse sample specification and channel map");
+ pa_log("Failed to parse sample specification and channel map");
goto fail;
}
frame_size = pa_frame_size(&ss);
- /* Fix latency to 100ms */
- periods = 8;
- fragsize = pa_bytes_per_second(&ss)/128;
+ nfrags = m->core->default_n_fragments;
+ frag_size = pa_usec_to_bytes(m->core->default_fragment_size_msec*1000, &ss);
+ if (frag_size <= 0)
+ frag_size = frame_size;
+
+ if (pa_modargs_get_value_u32(ma, "fragments", &nfrags) < 0 || pa_modargs_get_value_u32(ma, "fragment_size", &frag_size) < 0) {
+ pa_log("Failed to parse buffer metrics");
+ goto fail;
+ }
+ period_size = frag_size/frame_size;
- if (pa_modargs_get_value_u32(ma, "fragments", &periods) < 0 || pa_modargs_get_value_u32(ma, "fragment_size", &fragsize) < 0) {
- pa_log("failed to parse buffer metrics");
+ if (pa_modargs_get_value_boolean(ma, "mmap", &use_mmap) < 0) {
+ pa_log("Failed to parse mmap argument.");
goto fail;
}
- period_size = fragsize/frame_size;
u = pa_xnew0(struct userdata, 1);
- m->userdata = u;
+ u->core = m->core;
u->module = m;
+ m->userdata = u;
+ u->use_mmap = use_mmap;
+ u->first = 1;
+ pa_thread_mq_init(&u->thread_mq, m->core->mainloop);
+ u->rtpoll = pa_rtpoll_new();
+ u->alsa_rtpoll_item = NULL;
+ pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq);
snd_config_update_free_global();
- if ((err = snd_pcm_open(&u->pcm_handle, dev = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0) {
- pa_log("Error opening PCM device %s: %s", dev, snd_strerror(err));
- goto fail;
+
+ dev = pa_xstrdup(pa_modargs_get_value(ma, "device", DEFAULT_DEVICE));
+
+ for (;;) {
+
+ if ((err = snd_pcm_open(&u->pcm_handle, dev, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0) {
+ pa_log("Error opening PCM device %s: %s", dev, snd_strerror(err));
+ pa_xfree(dev);
+ goto fail;
+ }
+
+ b = use_mmap;
+ if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &nfrags, &period_size, &b)) < 0) {
+
+ if (err == -EPERM) {
+ /* Hmm, some hw is very exotic, so we retry with plughw, if hw didn't work */
+
+ if (pa_startswith(dev, "hw:")) {
+ char *d = pa_sprintf_malloc("plughw:%s", dev+3);
+ pa_log_debug("Opening the device as '%s' didn't work, retrying with '%s'.", dev, d);
+ pa_xfree(dev);
+ dev = d;
+
+ snd_pcm_close(u->pcm_handle);
+ u->pcm_handle = NULL;
+ continue;
+ }
+ }
+
+ pa_log("Failed to set hardware parameters: %s", snd_strerror(err));
+ pa_xfree(dev);
+ goto fail;
+ }
+
+ break;
}
- if ((err = snd_pcm_info_malloc(&pcm_info)) < 0 ||
- (err = snd_pcm_info(u->pcm_handle, pcm_info)) < 0) {
+ u->device_name = dev;
+
+ if (use_mmap && !b) {
+ pa_log_info("Device doesn't support mmap(), falling back to UNIX read/write mode.");
+ u->use_mmap = use_mmap = b;
+ }
+
+ if (u->use_mmap)
+ pa_log_info("Successfully enabled mmap() mode.");
+
+ if ((err = snd_pcm_info(u->pcm_handle, pcm_info)) < 0) {
pa_log("Error fetching PCM info: %s", snd_strerror(err));
goto fail;
}
- if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &periods, &period_size)) < 0) {
- pa_log("Failed to set hardware parameters: %s", snd_strerror(err));
+ if ((err = pa_alsa_set_sw_params(u->pcm_handle)) < 0) {
+ pa_log("Failed to set software parameters: %s", snd_strerror(err));
goto fail;
}
@@ -464,15 +831,16 @@ int pa__init(pa_core *c, pa_module*m) {
/* Seems ALSA didn't like the channel number, so let's fix the channel map */
pa_channel_map_init_auto(&map, ss.channels, PA_CHANNEL_MAP_ALSA);
- if ((err = snd_mixer_open(&u->mixer_handle, 0)) < 0) {
- pa_log("Error opening mixer: %s", snd_strerror(err));
- goto fail;
- }
+ if ((err = snd_mixer_open(&u->mixer_handle, 0)) < 0)
+ pa_log_warn("Error opening mixer: %s", snd_strerror(err));
+ else {
- if ((pa_alsa_prepare_mixer(u->mixer_handle, dev) < 0) ||
- !(u->mixer_elem = pa_alsa_find_elem(u->mixer_handle, "PCM", "Master"))) {
- snd_mixer_close(u->mixer_handle);
- u->mixer_handle = NULL;
+ if ((pa_alsa_prepare_mixer(u->mixer_handle, dev) < 0) ||
+ !(u->mixer_elem = pa_alsa_find_elem(u->mixer_handle, "Master", "PCM"))) {
+
+ snd_mixer_close(u->mixer_handle);
+ u->mixer_handle = NULL;
+ }
}
if ((name = pa_modargs_get_value(ma, "sink_name", NULL)))
@@ -482,114 +850,146 @@ int pa__init(pa_core *c, pa_module*m) {
namereg_fail = 0;
}
- if (!(u->sink = pa_sink_new(c, __FILE__, name, namereg_fail, &ss, &map))) {
+ u->sink = pa_sink_new(m->core, __FILE__, name, namereg_fail, &ss, &map);
+ pa_xfree(name_buf);
+
+ if (!u->sink) {
pa_log("Failed to create sink object");
goto fail;
}
- u->sink->is_hardware = 1;
- u->sink->get_latency = sink_get_latency_cb;
+ u->sink->parent.process_msg = sink_process_msg;
+ u->sink->userdata = u;
+
+ pa_sink_set_module(u->sink, m);
+ pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
+ pa_sink_set_rtpoll(u->sink, u->rtpoll);
+ pa_sink_set_description(u->sink, t = pa_sprintf_malloc(
+ "ALSA PCM on %s (%s)%s",
+ dev,
+ snd_pcm_info_get_name(pcm_info),
+ use_mmap ? " via DMA" : ""));
+ pa_xfree(t);
+
+ u->sink->flags = PA_SINK_HARDWARE|PA_SINK_HW_VOLUME_CTRL|PA_SINK_LATENCY;
+
+ u->frame_size = frame_size;
+ u->fragment_size = frag_size = period_size * frame_size;
+ u->nfragments = nfrags;
+ u->hwbuf_size = u->fragment_size * nfrags;
+
+ pa_log_info("Using %u fragments of size %lu bytes.", nfrags, (long unsigned) u->fragment_size);
+
+ pa_memchunk_reset(&u->memchunk);
+
if (u->mixer_handle) {
- assert(u->mixer_elem);
+ /* Initialize mixer code */
+
+ pa_assert(u->mixer_elem);
+
if (snd_mixer_selem_has_playback_volume(u->mixer_elem)) {
int i;
- for (i = 0;i < ss.channels;i++) {
+ for (i = 0; i < ss.channels; i++)
if (!snd_mixer_selem_has_playback_channel(u->mixer_elem, i))
break;
- }
if (i == ss.channels) {
- u->sink->get_hw_volume = sink_get_hw_volume_cb;
- u->sink->set_hw_volume = sink_set_hw_volume_cb;
- snd_mixer_selem_get_playback_volume_range(
- u->mixer_elem, &u->hw_volume_min, &u->hw_volume_max);
- }
+ pa_log_debug("ALSA device has separate volumes controls for all %u channels.", ss.channels);
+ u->sink->get_volume = sink_get_volume_cb;
+ u->sink->set_volume = sink_set_volume_cb;
+ snd_mixer_selem_get_playback_volume_range(u->mixer_elem, &u->hw_volume_min, &u->hw_volume_max);
+ } else
+ pa_log_info("ALSA device lacks separate volumes controls for all %u channels (%u available), falling back to software volume control.", ss.channels, i+1);
}
+
if (snd_mixer_selem_has_playback_switch(u->mixer_elem)) {
- u->sink->get_hw_mute = sink_get_hw_mute_cb;
- u->sink->set_hw_mute = sink_set_hw_mute_cb;
+ u->sink->get_mute = sink_get_mute_cb;
+ u->sink->set_mute = sink_set_mute_cb;
}
- }
- u->sink->userdata = u;
- pa_sink_set_owner(u->sink, m);
- pa_sink_set_description(u->sink, t = pa_sprintf_malloc("ALSA PCM on %s (%s)", dev, snd_pcm_info_get_name(pcm_info)));
- pa_xfree(t);
-
- u->pcm_fdl = pa_alsa_fdlist_new();
- assert(u->pcm_fdl);
- if (pa_alsa_fdlist_init_pcm(u->pcm_fdl, u->pcm_handle, c->mainloop, fdl_callback, u) < 0) {
- pa_log("failed to initialise file descriptor monitoring");
- goto fail;
- }
- if (u->mixer_handle) {
u->mixer_fdl = pa_alsa_fdlist_new();
- assert(u->mixer_fdl);
- if (pa_alsa_fdlist_init_mixer(u->mixer_fdl, u->mixer_handle, c->mainloop) < 0) {
+
+ if (pa_alsa_fdlist_set_mixer(u->mixer_fdl, u->mixer_handle, m->core->mainloop) < 0) {
pa_log("failed to initialise file descriptor monitoring");
goto fail;
}
+
snd_mixer_elem_set_callback(u->mixer_elem, mixer_callback);
snd_mixer_elem_set_callback_private(u->mixer_elem, u);
} else
u->mixer_fdl = NULL;
- u->frame_size = frame_size;
- u->fragment_size = period_size * frame_size;
-
- pa_log_info("using %u fragments of size %lu bytes.", periods, (long unsigned)u->fragment_size);
-
- u->silence.memblock = pa_memblock_new(c->mempool, u->silence.length = u->fragment_size);
- assert(u->silence.memblock);
- pa_silence_memblock(u->silence.memblock, &ss);
- u->silence.index = 0;
-
- u->memchunk.memblock = NULL;
- u->memchunk.index = u->memchunk.length = 0;
-
- ret = 0;
+ if (!(u->thread = pa_thread_new(thread_func, u))) {
+ pa_log("Failed to create thread.");
+ goto fail;
+ }
/* Get initial mixer settings */
- if (u->sink->get_hw_volume)
- u->sink->get_hw_volume(u->sink);
- if (u->sink->get_hw_mute)
- u->sink->get_hw_mute(u->sink);
+ if (u->sink->get_volume)
+ u->sink->get_volume(u->sink);
+ if (u->sink->get_mute)
+ u->sink->get_mute(u->sink);
-finish:
-
- pa_xfree(name_buf);
+ pa_sink_put(u->sink);
- if (ma)
- pa_modargs_free(ma);
+ pa_modargs_free(ma);
- if (pcm_info)
- snd_pcm_info_free(pcm_info);
-
- return ret;
+ return 0;
fail:
- if (u)
- pa__done(c, m);
+ if (ma)
+ pa_modargs_free(ma);
- goto finish;
+ pa__done(m);
+
+ return -1;
}
-void pa__done(pa_core *c, pa_module*m) {
+void pa__done(pa_module*m) {
struct userdata *u;
- assert(c && m);
+
+ pa_assert(m);
if (!(u = m->userdata))
return;
- clear_up(u);
+ if (u->sink)
+ pa_sink_unlink(u->sink);
+
+ if (u->thread) {
+ pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+ pa_thread_free(u->thread);
+ }
+
+ pa_thread_mq_done(&u->thread_mq);
+
+ if (u->sink)
+ pa_sink_unref(u->sink);
if (u->memchunk.memblock)
pa_memblock_unref(u->memchunk.memblock);
- if (u->silence.memblock)
- pa_memblock_unref(u->silence.memblock);
+ if (u->alsa_rtpoll_item)
+ pa_rtpoll_item_free(u->alsa_rtpoll_item);
+
+ if (u->rtpoll)
+ pa_rtpoll_free(u->rtpoll);
+
+ if (u->mixer_fdl)
+ pa_alsa_fdlist_free(u->mixer_fdl);
+
+ if (u->mixer_handle)
+ snd_mixer_close(u->mixer_handle);
+
+ if (u->pcm_handle) {
+ snd_pcm_drop(u->pcm_handle);
+ snd_pcm_close(u->pcm_handle);
+ }
+
+ pa_xfree(u->device_name);
pa_xfree(u);
-}
+ snd_config_update_free_global();
+}
diff --git a/src/modules/module-alsa-source.c b/src/modules/module-alsa-source.c
index 4061d668..d840cac3 100644
--- a/src/modules/module-alsa-source.c
+++ b/src/modules/module-alsa-source.c
@@ -26,18 +26,12 @@
#include <config.h>
#endif
-#include <assert.h>
#include <stdio.h>
-#ifdef HAVE_SYS_POLL_H
-#include <sys/poll.h>
-#else
-#include "poll.h"
-#endif
-
#include <asoundlib.h>
#include <pulse/xmalloc.h>
+#include <pulse/util.h>
#include <pulsecore/core-error.h>
#include <pulsecore/core.h>
@@ -48,6 +42,11 @@
#include <pulsecore/core-util.h>
#include <pulsecore/sample-util.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/rtpoll.h>
#include "alsa-util.h"
#include "module-alsa-source-symdef.h"
@@ -63,20 +62,35 @@ PA_MODULE_USAGE(
"rate=<sample rate> "
"fragments=<number of fragments> "
"fragment_size=<fragment size> "
- "channel_map=<channel map>")
+ "channel_map=<channel map> "
+ "mmap=<enable memory mapping?>")
+
+#define DEFAULT_DEVICE "default"
struct userdata {
+ pa_core *core;
+ pa_module *module;
+ pa_source *source;
+
+ pa_thread *thread;
+ pa_thread_mq thread_mq;
+ pa_rtpoll *rtpoll;
+
snd_pcm_t *pcm_handle;
+
+ pa_alsa_fdlist *mixer_fdl;
snd_mixer_t *mixer_handle;
snd_mixer_elem_t *mixer_elem;
- pa_source *source;
- struct pa_alsa_fdlist *pcm_fdl;
- struct pa_alsa_fdlist *mixer_fdl;
long hw_volume_max, hw_volume_min;
- size_t frame_size, fragment_size;
- pa_memchunk memchunk;
- pa_module *module;
+ size_t frame_size, fragment_size, hwbuf_size;
+ unsigned nfragments;
+
+ char *device_name;
+
+ int use_mmap;
+
+ pa_rtpoll_item *alsa_rtpoll_item;
};
static const char* const valid_modargs[] = {
@@ -88,257 +102,438 @@ static const char* const valid_modargs[] = {
"fragments",
"fragment_size",
"channel_map",
+ "mmap",
NULL
};
-#define DEFAULT_DEVICE "default"
+static int mmap_read(struct userdata *u) {
+ int work_done = 0;
-static void update_usage(struct userdata *u) {
- pa_module_set_used(u->module, u->source ? pa_source_used_by(u->source) : 0);
-}
+ pa_assert(u);
+ pa_source_assert_ref(u->source);
-static void clear_up(struct userdata *u) {
- assert(u);
+ for (;;) {
+ snd_pcm_sframes_t n;
+ int err;
+ const snd_pcm_channel_area_t *areas;
+ snd_pcm_uframes_t offset, frames;
+ pa_memchunk chunk;
+ void *p;
- if (u->source) {
- pa_source_disconnect(u->source);
- pa_source_unref(u->source);
- u->source = NULL;
- }
+ if ((n = snd_pcm_avail_update(u->pcm_handle)) < 0) {
- if (u->pcm_fdl)
- pa_alsa_fdlist_free(u->pcm_fdl);
- if (u->mixer_fdl)
- pa_alsa_fdlist_free(u->mixer_fdl);
+ if (n == -EPIPE)
+ pa_log_debug("snd_pcm_avail_update: Buffer underrun!");
- u->pcm_fdl = u->mixer_fdl = NULL;
+ if ((err = snd_pcm_recover(u->pcm_handle, n, 1)) == 0)
+ continue;
- if (u->mixer_handle) {
- snd_mixer_close(u->mixer_handle);
- u->mixer_handle = NULL;
- }
+ if (err == -EAGAIN)
+ return work_done;
- if (u->pcm_handle) {
- snd_pcm_drop(u->pcm_handle);
- snd_pcm_close(u->pcm_handle);
- u->pcm_handle = NULL;
- }
-}
+ pa_log("snd_pcm_avail_update: %s", snd_strerror(err));
+ return -1;
+ }
-static int xrun_recovery(struct userdata *u) {
- int ret;
- assert(u);
+/* pa_log("Got request for %i samples", (int) n); */
- pa_log_info("*** ALSA-XRUN (capture) ***");
+ if (n <= 0)
+ return work_done;
- if ((ret = snd_pcm_prepare(u->pcm_handle)) < 0) {
- pa_log("snd_pcm_prepare() failed: %s", snd_strerror(-ret));
+ frames = n;
- clear_up(u);
- pa_module_unload_request(u->module);
+ if ((err = snd_pcm_mmap_begin(u->pcm_handle, &areas, &offset, &frames)) < 0) {
- return -1;
- }
+ if (err == -EPIPE)
+ pa_log_debug("snd_pcm_mmap_begin: Buffer underrun!");
- return 0;
-}
+ if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) == 0)
+ continue;
+ if (err == -EAGAIN)
+ return work_done;
-static int suspend_recovery(struct userdata *u) {
- int ret;
- assert(u);
+ pa_log("Failed to write data to DSP: %s", snd_strerror(err));
+ return -1;
+ }
- pa_log_info("*** ALSA-SUSPEND (capture) ***");
+ /* Check these are multiples of 8 bit */
+ pa_assert((areas[0].first & 7) == 0);
+ pa_assert((areas[0].step & 7)== 0);
- if ((ret = snd_pcm_resume(u->pcm_handle)) < 0) {
- if (ret == -EAGAIN)
- return -1;
+ /* We assume a single interleaved memory buffer */
+ pa_assert((areas[0].first >> 3) == 0);
+ pa_assert((areas[0].step >> 3) == u->frame_size);
- if (ret != -ENOSYS)
- pa_log("snd_pcm_resume() failed: %s", snd_strerror(-ret));
- else {
- if ((ret = snd_pcm_prepare(u->pcm_handle)) < 0)
- pa_log("snd_pcm_prepare() failed: %s", snd_strerror(-ret));
- }
+ p = (uint8_t*) areas[0].addr + (offset * u->frame_size);
+
+ chunk.memblock = pa_memblock_new_fixed(u->core->mempool, p, frames * u->frame_size, 1);
+ chunk.length = pa_memblock_get_length(chunk.memblock);
+ chunk.index = 0;
+
+ pa_source_post(u->source, &chunk);
+
+ /* FIXME: Maybe we can do something to keep this memory block
+ * a little bit longer around? */
+ pa_memblock_unref_fixed(chunk.memblock);
+
+ if ((err = snd_pcm_mmap_commit(u->pcm_handle, offset, frames)) < 0) {
+
+ if (err == -EPIPE)
+ pa_log_debug("snd_pcm_mmap_commit: Buffer underrun!");
- if (ret < 0) {
- clear_up(u);
- pa_module_unload_request(u->module);
+ if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) == 0)
+ continue;
+
+ if (err == -EAGAIN)
+ return work_done;
+
+ pa_log("Failed to write data to DSP: %s", snd_strerror(err));
return -1;
}
- }
- return ret;
+ work_done = 1;
+
+/* pa_log("wrote %i samples", (int) frames); */
+ }
}
-static void do_read(struct userdata *u) {
- assert(u);
+static int unix_read(struct userdata *u) {
+ snd_pcm_status_t *status;
+ int work_done = 0;
- update_usage(u);
+ snd_pcm_status_alloca(&status);
- for (;;) {
- pa_memchunk post_memchunk;
- snd_pcm_sframes_t frames;
- size_t l;
+ pa_assert(u);
+ pa_source_assert_ref(u->source);
- if (!u->memchunk.memblock) {
- u->memchunk.memblock = pa_memblock_new(u->source->core->mempool, u->memchunk.length = u->fragment_size);
- u->memchunk.index = 0;
+ for (;;) {
+ void *p;
+ snd_pcm_sframes_t t, k;
+ ssize_t l;
+ int err;
+ pa_memchunk chunk;
+
+ if ((err = snd_pcm_status(u->pcm_handle, status)) < 0) {
+ pa_log("Failed to query DSP status data: %s", snd_strerror(err));
+ return -1;
}
- assert(u->memchunk.memblock);
- assert(u->memchunk.length);
- assert(u->memchunk.memblock->data);
- assert(u->memchunk.memblock->length);
- assert(u->memchunk.length % u->frame_size == 0);
+ if (snd_pcm_status_get_avail_max(status)*u->frame_size >= u->hwbuf_size)
+ pa_log_debug("Buffer overrun!");
- if ((frames = snd_pcm_readi(u->pcm_handle, (uint8_t*) u->memchunk.memblock->data + u->memchunk.index, u->memchunk.length / u->frame_size)) < 0) {
- if (frames == -EAGAIN)
- return;
+ l = snd_pcm_status_get_avail(status) * u->frame_size;
- if (frames == -EPIPE) {
- if (xrun_recovery(u) < 0)
- return;
+ if (l <= 0)
+ return work_done;
- continue;
- }
+ chunk.memblock = pa_memblock_new(u->core->mempool, (size_t) -1);
+
+ k = pa_memblock_get_length(chunk.memblock);
+
+ if (k > l)
+ k = l;
+
+ k = (k/u->frame_size)*u->frame_size;
- if (frames == -ESTRPIPE) {
- if (suspend_recovery(u) < 0)
- return;
+ p = pa_memblock_acquire(chunk.memblock);
+ t = snd_pcm_readi(u->pcm_handle, (uint8_t*) p, k / u->frame_size);
+ pa_memblock_release(chunk.memblock);
+/* pa_log("wrote %i bytes of %u (%u)", t*u->frame_size, u->memchunk.length, l); */
+
+ pa_assert(t != 0);
+
+ if (t < 0) {
+ pa_memblock_unref(chunk.memblock);
+
+ if ((t = snd_pcm_recover(u->pcm_handle, t, 1)) == 0)
continue;
+
+ if (t == -EAGAIN) {
+ pa_log_debug("EAGAIN");
+ return work_done;
+ } else {
+ pa_log("Failed to read data from DSP: %s", snd_strerror(t));
+ return -1;
}
+ }
- pa_log("snd_pcm_readi() failed: %s", snd_strerror(-frames));
+ chunk.index = 0;
+ chunk.length = t * u->frame_size;
- clear_up(u);
- pa_module_unload_request(u->module);
- return;
- }
+ pa_source_post(u->source, &chunk);
+ pa_memblock_unref(chunk.memblock);
- l = frames * u->frame_size;
+ work_done = 1;
- post_memchunk = u->memchunk;
- post_memchunk.length = l;
+ if (t * u->frame_size >= (unsigned) l)
+ return work_done;
+ }
+}
- pa_source_post(u->source, &post_memchunk);
+static pa_usec_t source_get_latency(struct userdata *u) {
+ pa_usec_t r = 0;
+ snd_pcm_status_t *status;
+ snd_pcm_sframes_t frames = 0;
+ int err;
- u->memchunk.index += l;
- u->memchunk.length -= l;
+ snd_pcm_status_alloca(&status);
- if (u->memchunk.length == 0) {
- pa_memblock_unref(u->memchunk.memblock);
- u->memchunk.memblock = NULL;
- u->memchunk.index = u->memchunk.length = 0;
- }
+ pa_assert(u);
+ pa_assert(u->pcm_handle);
- break;
+ if ((err = snd_pcm_status(u->pcm_handle, status)) < 0)
+ pa_log("Failed to get delay: %s", snd_strerror(err));
+ else
+ frames = snd_pcm_status_get_delay(status);
+
+ if (frames > 0)
+ r = pa_bytes_to_usec(frames * u->frame_size, &u->source->sample_spec);
+
+ return r;
+}
+
+static int build_pollfd(struct userdata *u) {
+ int err;
+ struct pollfd *pollfd;
+ int n;
+
+ pa_assert(u);
+ pa_assert(u->pcm_handle);
+
+ if ((n = snd_pcm_poll_descriptors_count(u->pcm_handle)) < 0) {
+ pa_log("snd_pcm_poll_descriptors_count() failed: %s", snd_strerror(n));
+ return -1;
}
+
+ if (u->alsa_rtpoll_item)
+ pa_rtpoll_item_free(u->alsa_rtpoll_item);
+
+ u->alsa_rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, n);
+ pollfd = pa_rtpoll_item_get_pollfd(u->alsa_rtpoll_item, NULL);
+
+ if ((err = snd_pcm_poll_descriptors(u->pcm_handle, pollfd, n)) < 0) {
+ pa_log("snd_pcm_poll_descriptors() failed: %s", snd_strerror(err));
+ return -1;
+ }
+
+ return 0;
}
-static void fdl_callback(void *userdata) {
- struct userdata *u = userdata;
- assert(u);
+static int suspend(struct userdata *u) {
+ pa_assert(u);
+ pa_assert(u->pcm_handle);
- if (snd_pcm_state(u->pcm_handle) == SND_PCM_STATE_XRUN)
- if (xrun_recovery(u) < 0)
- return;
+ /* Let's suspend */
+ snd_pcm_close(u->pcm_handle);
+ u->pcm_handle = NULL;
- if (snd_pcm_state(u->pcm_handle) == SND_PCM_STATE_SUSPENDED)
- if (suspend_recovery(u) < 0)
- return;
+ if (u->alsa_rtpoll_item) {
+ pa_rtpoll_item_free(u->alsa_rtpoll_item);
+ u->alsa_rtpoll_item = NULL;
+ }
- do_read(u);
+ pa_log_info("Device suspended...");
+
+ return 0;
}
-static int mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {
- struct userdata *u = snd_mixer_elem_get_callback_private(elem);
+static int unsuspend(struct userdata *u) {
+ pa_sample_spec ss;
+ int err, b;
+ unsigned nfrags;
+ snd_pcm_uframes_t period_size;
- assert(u && u->mixer_handle);
+ pa_assert(u);
+ pa_assert(!u->pcm_handle);
- if (mask == SND_CTL_EVENT_MASK_REMOVE)
- return 0;
+ pa_log_info("Trying resume...");
- if (mask & SND_CTL_EVENT_MASK_VALUE) {
- if (u->source->get_hw_volume)
- u->source->get_hw_volume(u->source);
- if (u->source->get_hw_mute)
- u->source->get_hw_mute(u->source);
+ snd_config_update_free_global();
+ if ((err = snd_pcm_open(&u->pcm_handle, u->device_name, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)) < 0) {
+ pa_log("Error opening PCM device %s: %s", u->device_name, snd_strerror(err));
+ goto fail;
+ }
- pa_subscription_post(u->source->core,
- PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE,
- u->source->index);
+ ss = u->source->sample_spec;
+ nfrags = u->nfragments;
+ period_size = u->fragment_size / u->frame_size;
+ b = u->use_mmap;
+
+ if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &nfrags, &period_size, &b)) < 0) {
+ pa_log("Failed to set hardware parameters: %s", snd_strerror(err));
+ goto fail;
}
+ if (b != u->use_mmap) {
+ pa_log_warn("Resume failed, couldn't get original access mode.");
+ goto fail;
+ }
+
+ if (!pa_sample_spec_equal(&ss, &u->source->sample_spec)) {
+ pa_log_warn("Resume failed, couldn't restore original sample settings.");
+ goto fail;
+ }
+
+ if (nfrags != u->nfragments || period_size*u->frame_size != u->fragment_size) {
+ pa_log_warn("Resume failed, couldn't restore original fragment settings.");
+ goto fail;
+ }
+
+ if ((err = pa_alsa_set_sw_params(u->pcm_handle)) < 0) {
+ pa_log("Failed to set software parameters: %s", snd_strerror(err));
+ goto fail;
+ }
+
+ if (build_pollfd(u) < 0)
+ goto fail;
+
+ snd_pcm_start(u->pcm_handle);
+
+ /* FIXME: We need to reload the volume somehow */
+
+ pa_log_info("Resumed successfully...");
+
return 0;
+
+fail:
+ if (u->pcm_handle) {
+ snd_pcm_close(u->pcm_handle);
+ u->pcm_handle = NULL;
+ }
+
+ return -1;
}
-static pa_usec_t source_get_latency_cb(pa_source *s) {
- struct userdata *u = s->userdata;
- snd_pcm_sframes_t frames;
- assert(s && u && u->source);
+static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+ struct userdata *u = PA_SOURCE(o)->userdata;
+
+ switch (code) {
+
+ case PA_SOURCE_MESSAGE_GET_LATENCY: {
+ pa_usec_t r = 0;
+
+ if (u->pcm_handle)
+ r = source_get_latency(u);
+
+ *((pa_usec_t*) data) = r;
+
+ return 0;
+ }
+
+ case PA_SOURCE_MESSAGE_SET_STATE:
+
+ switch ((pa_source_state_t) PA_PTR_TO_UINT(data)) {
+
+ case PA_SOURCE_SUSPENDED:
+ pa_assert(PA_SOURCE_OPENED(u->source->thread_info.state));
+
+ if (suspend(u) < 0)
+ return -1;
+
+ break;
+
+ case PA_SOURCE_IDLE:
+ case PA_SOURCE_RUNNING:
+
+ if (u->source->thread_info.state == PA_SOURCE_INIT) {
+ if (build_pollfd(u) < 0)
+ return -1;
+
+ snd_pcm_start(u->pcm_handle);
+ }
+
+ if (u->source->thread_info.state == PA_SOURCE_SUSPENDED) {
+ if (unsuspend(u) < 0)
+ return -1;
+ }
+
+ break;
- if (snd_pcm_delay(u->pcm_handle, &frames) < 0) {
- pa_log("failed to get delay");
- s->get_latency = NULL;
+ case PA_SOURCE_UNLINKED:
+ case PA_SOURCE_INIT:
+ ;
+ }
+
+ break;
+ }
+
+ return pa_source_process_msg(o, code, data, offset, chunk);
+}
+
+static int mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {
+ struct userdata *u = snd_mixer_elem_get_callback_private(elem);
+
+ pa_assert(u);
+ pa_assert(u->mixer_handle);
+
+ if (mask == SND_CTL_EVENT_MASK_REMOVE)
return 0;
+
+ if (mask & SND_CTL_EVENT_MASK_VALUE) {
+ pa_source_get_volume(u->source);
+ pa_source_get_mute(u->source);
}
- return pa_bytes_to_usec(frames * u->frame_size, &s->sample_spec);
+ return 0;
}
-static int source_get_hw_volume_cb(pa_source *s) {
+static int source_get_volume_cb(pa_source *s) {
struct userdata *u = s->userdata;
- long vol;
int err;
int i;
- assert(u && u->mixer_elem);
+ pa_assert(u);
+ pa_assert(u->mixer_elem);
- for (i = 0;i < s->hw_volume.channels;i++) {
- long set_vol;
+ for (i = 0; i < s->sample_spec.channels; i++) {
+ long set_vol, vol;
- assert(snd_mixer_selem_has_capture_channel(u->mixer_elem, i));
+ pa_assert(snd_mixer_selem_has_capture_channel(u->mixer_elem, i));
if ((err = snd_mixer_selem_get_capture_volume(u->mixer_elem, i, &vol)) < 0)
goto fail;
- set_vol = (long) roundf(((float) s->hw_volume.values[i] * (u->hw_volume_max - u->hw_volume_min)) / PA_VOLUME_NORM) + u->hw_volume_min;
+ set_vol = (long) roundf(((float) s->volume.values[i] * (u->hw_volume_max - u->hw_volume_min)) / PA_VOLUME_NORM) + u->hw_volume_min;
/* Try to avoid superfluous volume changes */
if (set_vol != vol)
- s->hw_volume.values[i] = (pa_volume_t) roundf(((float) (vol - u->hw_volume_min) * PA_VOLUME_NORM) / (u->hw_volume_max - u->hw_volume_min));
+ s->volume.values[i] = (pa_volume_t) roundf(((float) (vol - u->hw_volume_min) * PA_VOLUME_NORM) / (u->hw_volume_max - u->hw_volume_min));
}
return 0;
fail:
pa_log_error("Unable to read volume: %s", snd_strerror(err));
- s->get_hw_volume = NULL;
- s->set_hw_volume = NULL;
+
+ s->get_volume = NULL;
+ s->set_volume = NULL;
return -1;
}
-static int source_set_hw_volume_cb(pa_source *s) {
+static int source_set_volume_cb(pa_source *s) {
struct userdata *u = s->userdata;
int err;
- pa_volume_t vol;
int i;
- assert(u && u->mixer_elem);
+ pa_assert(u);
+ pa_assert(u->mixer_elem);
- for (i = 0;i < s->hw_volume.channels;i++) {
- assert(snd_mixer_selem_has_capture_channel(u->mixer_elem, i));
+ for (i = 0; i < s->sample_spec.channels; i++) {
+ long alsa_vol;
+ pa_volume_t vol;
- vol = s->hw_volume.values[i];
+ pa_assert(snd_mixer_selem_has_capture_channel(u->mixer_elem, i));
+
+ vol = s->volume.values[i];
if (vol > PA_VOLUME_NORM)
vol = PA_VOLUME_NORM;
- vol = (long) roundf(((float) vol * (u->hw_volume_max - u->hw_volume_min)) / PA_VOLUME_NORM) + u->hw_volume_min;
+ alsa_vol = (long) roundf(((float) vol * (u->hw_volume_max - u->hw_volume_min)) / PA_VOLUME_NORM) + u->hw_volume_min;
- if ((err = snd_mixer_selem_set_capture_volume(u->mixer_elem, i, vol)) < 0)
+ if ((err = snd_mixer_selem_set_capture_volume(u->mixer_elem, i, alsa_vol)) < 0)
goto fail;
}
@@ -346,55 +541,159 @@ static int source_set_hw_volume_cb(pa_source *s) {
fail:
pa_log_error("Unable to set volume: %s", snd_strerror(err));
- s->get_hw_volume = NULL;
- s->set_hw_volume = NULL;
+
+ s->get_volume = NULL;
+ s->set_volume = NULL;
return -1;
}
-static int source_get_hw_mute_cb(pa_source *s) {
+static int source_get_mute_cb(pa_source *s) {
struct userdata *u = s->userdata;
int err, sw;
- assert(u && u->mixer_elem);
+ pa_assert(u);
+ pa_assert(u->mixer_elem);
- err = snd_mixer_selem_get_capture_switch(u->mixer_elem, 0, &sw);
- if (err) {
+ if ((err = snd_mixer_selem_get_capture_switch(u->mixer_elem, 0, &sw)) < 0) {
pa_log_error("Unable to get switch: %s", snd_strerror(err));
- s->get_hw_mute = NULL;
- s->set_hw_mute = NULL;
+
+ s->get_mute = NULL;
+ s->set_mute = NULL;
return -1;
}
- s->hw_muted = !sw;
+ s->muted = !sw;
return 0;
}
-static int source_set_hw_mute_cb(pa_source *s) {
+static int source_set_mute_cb(pa_source *s) {
struct userdata *u = s->userdata;
int err;
- assert(u && u->mixer_elem);
+ pa_assert(u);
+ pa_assert(u->mixer_elem);
- err = snd_mixer_selem_set_capture_switch_all(u->mixer_elem, !s->hw_muted);
- if (err) {
+ if ((err = snd_mixer_selem_set_capture_switch_all(u->mixer_elem, !s->muted)) < 0) {
pa_log_error("Unable to set switch: %s", snd_strerror(err));
- s->get_hw_mute = NULL;
- s->set_hw_mute = NULL;
+
+ s->get_mute = NULL;
+ s->set_mute = NULL;
return -1;
}
return 0;
}
-int pa__init(pa_core *c, pa_module*m) {
+static void thread_func(void *userdata) {
+ struct userdata *u = userdata;
+
+ pa_assert(u);
+
+ pa_log_debug("Thread starting up");
+
+ if (u->core->high_priority)
+ pa_make_realtime();
+
+ pa_thread_mq_install(&u->thread_mq);
+ pa_rtpoll_install(u->rtpoll);
+
+ for (;;) {
+ int ret;
+
+ /* Read some data and pass it to the sources */
+ if (PA_SOURCE_OPENED(u->source->thread_info.state)) {
+
+ if (u->use_mmap) {
+ if (mmap_read(u) < 0)
+ goto fail;
+
+ } else {
+ if (unix_read(u) < 0)
+ goto fail;
+ }
+ }
+
+ /* Hmm, nothing to do. Let's sleep */
+ if ((ret = pa_rtpoll_run(u->rtpoll, 1)) < 0)
+ goto fail;
+
+ if (ret == 0)
+ goto finish;
+
+ /* Tell ALSA about this and process its response */
+ if (PA_SOURCE_OPENED(u->source->thread_info.state)) {
+ struct pollfd *pollfd;
+ unsigned short revents = 0;
+ int err;
+ unsigned n;
+
+ pollfd = pa_rtpoll_item_get_pollfd(u->alsa_rtpoll_item, &n);
+
+ if ((err = snd_pcm_poll_descriptors_revents(u->pcm_handle, pollfd, n, &revents)) < 0) {
+ pa_log("snd_pcm_poll_descriptors_revents() failed: %s", snd_strerror(err));
+ goto fail;
+ }
+
+ if (revents & (POLLERR|POLLNVAL|POLLHUP)) {
+
+ if (revents & POLLERR)
+ pa_log_warn("Got POLLERR from ALSA");
+ if (revents & POLLNVAL)
+ pa_log_warn("Got POLLNVAL from ALSA");
+ if (revents & POLLHUP)
+ pa_log_warn("Got POLLHUP from ALSA");
+
+ /* Try to recover from this error */
+
+ switch (snd_pcm_state(u->pcm_handle)) {
+
+ case SND_PCM_STATE_XRUN:
+ if ((err = snd_pcm_recover(u->pcm_handle, -EPIPE, 1)) != 0) {
+ pa_log_warn("Could not recover from POLLERR|POLLNVAL|POLLHUP and XRUN: %s", snd_strerror(err));
+ goto fail;
+ }
+ break;
+
+ case SND_PCM_STATE_SUSPENDED:
+ if ((err = snd_pcm_recover(u->pcm_handle, -ESTRPIPE, 1)) != 0) {
+ pa_log_warn("Could not recover from POLLERR|POLLNVAL|POLLHUP and SUSPENDED: %s", snd_strerror(err));
+ goto fail;
+ }
+ break;
+
+ default:
+
+ snd_pcm_drop(u->pcm_handle);
+
+ if ((err = snd_pcm_prepare(u->pcm_handle)) < 0) {
+ pa_log_warn("Could not recover from POLLERR|POLLNVAL|POLLHUP with snd_pcm_prepare(): %s", snd_strerror(err));
+ goto fail;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+fail:
+ /* If this was no regular exit from the loop we have to continue
+ * processing messages until we received PA_MESSAGE_SHUTDOWN */
+ pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+ pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+ pa_log_debug("Thread shutting down");
+}
+
+int pa__init(pa_module*m) {
+
pa_modargs *ma = NULL;
- int ret = -1;
struct userdata *u = NULL;
- const char *dev;
+ char *dev;
pa_sample_spec ss;
pa_channel_map map;
- unsigned periods, fragsize;
+ unsigned nfrags, frag_size;
snd_pcm_uframes_t period_size;
size_t frame_size;
snd_pcm_info_t *pcm_info = NULL;
@@ -403,64 +702,125 @@ int pa__init(pa_core *c, pa_module*m) {
const char *name;
char *name_buf = NULL;
int namereg_fail;
+ int use_mmap = 1, b;
+
+ snd_pcm_info_alloca(&pcm_info);
+
+ pa_assert(m);
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
- pa_log("failed to parse module arguments");
+ pa_log("Failed to parse module arguments");
goto fail;
}
- ss = c->default_sample_spec;
+ ss = m->core->default_sample_spec;
if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_ALSA) < 0) {
- pa_log("failed to parse sample specification");
+ pa_log("Failed to parse sample specification");
goto fail;
}
frame_size = pa_frame_size(&ss);
- /* Fix latency to 100ms */
- periods = 12;
- fragsize = pa_bytes_per_second(&ss)/128;
+ nfrags = m->core->default_n_fragments;
+ frag_size = pa_usec_to_bytes(m->core->default_fragment_size_msec*1000, &ss);
+ if (frag_size <= 0)
+ frag_size = frame_size;
+
+ if (pa_modargs_get_value_u32(ma, "fragments", &nfrags) < 0 || pa_modargs_get_value_u32(ma, "fragment_size", &frag_size) < 0) {
+ pa_log("Failed to parse buffer metrics");
+ goto fail;
+ }
+ period_size = frag_size/frame_size;
- if (pa_modargs_get_value_u32(ma, "fragments", &periods) < 0 || pa_modargs_get_value_u32(ma, "fragment_size", &fragsize) < 0) {
- pa_log("failed to parse buffer metrics");
+ if (pa_modargs_get_value_boolean(ma, "mmap", &use_mmap) < 0) {
+ pa_log("Failed to parse mmap argument.");
goto fail;
}
- period_size = fragsize/frame_size;
u = pa_xnew0(struct userdata, 1);
- m->userdata = u;
+ u->core = m->core;
u->module = m;
+ m->userdata = u;
+ u->use_mmap = use_mmap;
+ pa_thread_mq_init(&u->thread_mq, m->core->mainloop);
+ u->rtpoll = pa_rtpoll_new();
+ u->alsa_rtpoll_item = NULL;
+ pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq);
snd_config_update_free_global();
- if ((err = snd_pcm_open(&u->pcm_handle, dev = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)) < 0) {
- pa_log("Error opening PCM device %s: %s", dev, snd_strerror(err));
- goto fail;
+
+ dev = pa_xstrdup(pa_modargs_get_value(ma, "device", DEFAULT_DEVICE));
+
+ for (;;) {
+
+ if ((err = snd_pcm_open(&u->pcm_handle, dev, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)) < 0) {
+ pa_log("Error opening PCM device %s: %s", dev, snd_strerror(err));
+ pa_xfree(dev);
+ goto fail;
+ }
+
+ b = use_mmap;
+ if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &nfrags, &period_size, &b)) < 0) {
+
+ if (err == -EPERM) {
+ /* Hmm, some hw is very exotic, so we retry with plughw, if hw didn't work */
+
+ if (pa_startswith(dev, "hw:")) {
+ char *d = pa_sprintf_malloc("plughw:%s", dev+3);
+ pa_log_debug("Opening the device as '%s' didn't work, retrying with '%s'.", dev, d);
+ pa_xfree(dev);
+ dev = d;
+
+ snd_pcm_close(u->pcm_handle);
+ u->pcm_handle = NULL;
+ continue;
+ }
+ }
+
+ pa_log("Failed to set hardware parameters: %s", snd_strerror(err));
+ pa_xfree(dev);
+ goto fail;
+ }
+
+ break;
}
- if ((err = snd_pcm_info_malloc(&pcm_info)) < 0 ||
- (err = snd_pcm_info(u->pcm_handle, pcm_info)) < 0) {
+ u->device_name = dev;
+
+ if (use_mmap && !b) {
+ pa_log_info("Device doesn't support mmap(), falling back to UNIX read/write mode.");
+ u->use_mmap = use_mmap = b;
+ }
+
+ if (u->use_mmap)
+ pa_log_info("Successfully enabled mmap() mode.");
+
+ if ((err = snd_pcm_info(u->pcm_handle, pcm_info)) < 0) {
pa_log("Error fetching PCM info: %s", snd_strerror(err));
goto fail;
}
- if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &periods, &period_size)) < 0) {
- pa_log("Failed to set hardware parameters: %s", snd_strerror(err));
+ if ((err = pa_alsa_set_sw_params(u->pcm_handle)) < 0) {
+ pa_log("Failed to set software parameters: %s", snd_strerror(err));
goto fail;
}
+ /* ALSA might tweak the sample spec, so recalculate the frame size */
+ frame_size = pa_frame_size(&ss);
+
if (ss.channels != map.channels)
/* Seems ALSA didn't like the channel number, so let's fix the channel map */
pa_channel_map_init_auto(&map, ss.channels, PA_CHANNEL_MAP_ALSA);
- if ((err = snd_mixer_open(&u->mixer_handle, 0)) < 0) {
+ if ((err = snd_mixer_open(&u->mixer_handle, 0)) < 0)
pa_log("Error opening mixer: %s", snd_strerror(err));
- goto fail;
- }
+ else {
- if ((pa_alsa_prepare_mixer(u->mixer_handle, dev) < 0) ||
- !(u->mixer_elem = pa_alsa_find_elem(u->mixer_handle, "Capture", "Mic"))) {
- snd_mixer_close(u->mixer_handle);
- u->mixer_handle = NULL;
+ if ((pa_alsa_prepare_mixer(u->mixer_handle, dev) < 0) ||
+ !(u->mixer_elem = pa_alsa_find_elem(u->mixer_handle, "Capture", NULL))) {
+ snd_mixer_close(u->mixer_handle);
+ u->mixer_handle = NULL;
+ }
}
if ((name = pa_modargs_get_value(ma, "source_name", NULL)))
@@ -470,16 +830,39 @@ int pa__init(pa_core *c, pa_module*m) {
namereg_fail = 0;
}
- if (!(u->source = pa_source_new(c, __FILE__, name, namereg_fail, &ss, &map))) {
+ u->source = pa_source_new(m->core, __FILE__, name, namereg_fail, &ss, &map);
+ pa_xfree(name_buf);
+
+ if (!u->source) {
pa_log("Failed to create source object");
goto fail;
}
- u->source->is_hardware = 1;
+ u->source->parent.process_msg = source_process_msg;
u->source->userdata = u;
- u->source->get_latency = source_get_latency_cb;
+
+ pa_source_set_module(u->source, m);
+ pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
+ pa_source_set_rtpoll(u->source, u->rtpoll);
+ pa_source_set_description(u->source, t = pa_sprintf_malloc(
+ "ALSA PCM on %s (%s)%s",
+ dev,
+ snd_pcm_info_get_name(pcm_info),
+ use_mmap ? " via DMA" : ""));
+ pa_xfree(t);
+
+ u->source->flags = PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY|PA_SOURCE_HW_VOLUME_CTRL;
+
+ u->frame_size = frame_size;
+ u->fragment_size = frag_size = period_size * frame_size;
+ u->nfragments = nfrags;
+ u->hwbuf_size = u->fragment_size * nfrags;
+
+ pa_log_info("Using %u fragments of size %lu bytes.", nfrags, (long unsigned) u->fragment_size);
+
if (u->mixer_handle) {
- assert(u->mixer_elem);
+ pa_assert(u->mixer_elem);
+
if (snd_mixer_selem_has_capture_volume(u->mixer_elem)) {
int i;
@@ -489,89 +872,95 @@ int pa__init(pa_core *c, pa_module*m) {
}
if (i == ss.channels) {
- u->source->get_hw_volume = source_get_hw_volume_cb;
- u->source->set_hw_volume = source_set_hw_volume_cb;
- snd_mixer_selem_get_capture_volume_range(
- u->mixer_elem, &u->hw_volume_min, &u->hw_volume_max);
+ u->source->get_volume = source_get_volume_cb;
+ u->source->set_volume = source_set_volume_cb;
+ snd_mixer_selem_get_capture_volume_range(u->mixer_elem, &u->hw_volume_min, &u->hw_volume_max);
}
}
+
if (snd_mixer_selem_has_capture_switch(u->mixer_elem)) {
- u->source->get_hw_mute = source_get_hw_mute_cb;
- u->source->set_hw_mute = source_set_hw_mute_cb;
+ u->source->get_mute = source_get_mute_cb;
+ u->source->set_mute = source_set_mute_cb;
}
- }
- pa_source_set_owner(u->source, m);
- pa_source_set_description(u->source, t = pa_sprintf_malloc("ALSA PCM on %s (%s)", dev, snd_pcm_info_get_name(pcm_info)));
- pa_xfree(t);
- u->pcm_fdl = pa_alsa_fdlist_new();
- assert(u->pcm_fdl);
- if (pa_alsa_fdlist_init_pcm(u->pcm_fdl, u->pcm_handle, c->mainloop, fdl_callback, u) < 0) {
- pa_log("failed to initialise file descriptor monitoring");
- goto fail;
- }
-
- if (u->mixer_handle) {
u->mixer_fdl = pa_alsa_fdlist_new();
- assert(u->mixer_fdl);
- if (pa_alsa_fdlist_init_mixer(u->mixer_fdl, u->mixer_handle, c->mainloop) < 0) {
+
+ if (pa_alsa_fdlist_set_mixer(u->mixer_fdl, u->mixer_handle, m->core->mainloop) < 0) {
pa_log("failed to initialise file descriptor monitoring");
goto fail;
}
+
snd_mixer_elem_set_callback(u->mixer_elem, mixer_callback);
snd_mixer_elem_set_callback_private(u->mixer_elem, u);
} else
u->mixer_fdl = NULL;
- u->frame_size = frame_size;
- u->fragment_size = period_size * frame_size;
-
- pa_log_info("using %u fragments of size %lu bytes.", periods, (long unsigned) u->fragment_size);
-
- u->memchunk.memblock = NULL;
- u->memchunk.index = u->memchunk.length = 0;
-
- snd_pcm_start(u->pcm_handle);
-
- ret = 0;
-
+ if (!(u->thread = pa_thread_new(thread_func, u))) {
+ pa_log("Failed to create thread.");
+ goto fail;
+ }
/* Get initial mixer settings */
- if (u->source->get_hw_volume)
- u->source->get_hw_volume(u->source);
- if (u->source->get_hw_mute)
- u->source->get_hw_mute(u->source);
+ if (u->source->get_volume)
+ u->source->get_volume(u->source);
+ if (u->source->get_mute)
+ u->source->get_mute(u->source);
-finish:
- pa_xfree(name_buf);
-
- if (ma)
- pa_modargs_free(ma);
+ pa_source_put(u->source);
- if (pcm_info)
- snd_pcm_info_free(pcm_info);
+ pa_modargs_free(ma);
- return ret;
+ return 0;
fail:
- if (u)
- pa__done(c, m);
+ if (ma)
+ pa_modargs_free(ma);
- goto finish;
+ pa__done(m);
+
+ return -1;
}
-void pa__done(pa_core *c, pa_module*m) {
+void pa__done(pa_module*m) {
struct userdata *u;
- assert(c && m);
+
+ pa_assert(m);
if (!(u = m->userdata))
return;
- clear_up(u);
+ if (u->source)
+ pa_source_unlink(u->source);
- if (u->memchunk.memblock)
- pa_memblock_unref(u->memchunk.memblock);
+ if (u->thread) {
+ pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+ pa_thread_free(u->thread);
+ }
+ pa_thread_mq_done(&u->thread_mq);
+
+ if (u->source)
+ pa_source_unref(u->source);
+
+ if (u->alsa_rtpoll_item)
+ pa_rtpoll_item_free(u->alsa_rtpoll_item);
+
+ if (u->rtpoll)
+ pa_rtpoll_free(u->rtpoll);
+
+ if (u->mixer_fdl)
+ pa_alsa_fdlist_free(u->mixer_fdl);
+
+ if (u->mixer_handle)
+ snd_mixer_close(u->mixer_handle);
+
+ if (u->pcm_handle) {
+ snd_pcm_drop(u->pcm_handle);
+ snd_pcm_close(u->pcm_handle);
+ }
+
+ pa_xfree(u->device_name);
pa_xfree(u);
-}
+ snd_config_update_free_global();
+}
diff --git a/src/modules/module-cli.c b/src/modules/module-cli.c
index 19ac0c26..84125214 100644
--- a/src/modules/module-cli.c
+++ b/src/modules/module-cli.c
@@ -26,7 +26,6 @@
#endif
#include <stdio.h>
-#include <assert.h>
#include <unistd.h>
#include <pulsecore/module.h>
@@ -35,6 +34,7 @@
#include <pulsecore/sioman.h>
#include <pulsecore/log.h>
#include <pulsecore/modargs.h>
+#include <pulsecore/macro.h>
#include "module-cli-symdef.h"
@@ -51,8 +51,8 @@ static const char* const valid_modargs[] = {
static void eof_and_unload_cb(pa_cli*c, void *userdata) {
pa_module *m = userdata;
- assert(c);
- assert(m);
+ pa_assert(c);
+ pa_assert(m);
pa_module_unload_request(m);
}
@@ -60,21 +60,20 @@ static void eof_and_unload_cb(pa_cli*c, void *userdata) {
static void eof_and_exit_cb(pa_cli*c, void *userdata) {
pa_module *m = userdata;
- assert(c);
- assert(m);
+ pa_assert(c);
+ pa_assert(m);
m->core->mainloop->quit(m->core->mainloop, 0);
}
-int pa__init(pa_core *c, pa_module*m) {
+int pa__init(pa_module*m) {
pa_iochannel *io;
pa_modargs *ma;
int exit_on_eof = 0;
- assert(c);
- assert(m);
+ pa_assert(m);
- if (c->running_as_daemon) {
+ if (m->core->running_as_daemon) {
pa_log_info("Running as daemon, refusing to load this module.");
return 0;
}
@@ -94,12 +93,10 @@ int pa__init(pa_core *c, pa_module*m) {
goto fail;
}
- io = pa_iochannel_new(c->mainloop, STDIN_FILENO, STDOUT_FILENO);
- assert(io);
+ io = pa_iochannel_new(m->core->mainloop, STDIN_FILENO, STDOUT_FILENO);
pa_iochannel_set_noclose(io, 1);
- m->userdata = pa_cli_new(c, io, m);
- assert(m->userdata);
+ m->userdata = pa_cli_new(m->core, io, m);
pa_cli_set_eof_callback(m->userdata, exit_on_eof ? eof_and_exit_cb : eof_and_unload_cb, m);
@@ -115,11 +112,10 @@ fail:
return -1;
}
-void pa__done(pa_core *c, pa_module*m) {
- assert(c);
- assert(m);
+void pa__done(pa_module*m) {
+ pa_assert(m);
- if (c->running_as_daemon == 0) {
+ if (m->core->running_as_daemon == 0) {
pa_cli_free(m->userdata);
pa_stdio_release();
}
diff --git a/src/modules/module-combine.c b/src/modules/module-combine.c
index 716c20b2..665bf9dd 100644
--- a/src/modules/module-combine.c
+++ b/src/modules/module-combine.c
@@ -25,12 +25,13 @@
#include <config.h>
#endif
-#include <assert.h>
#include <stdio.h>
+#include <errno.h>
#include <pulse/timeval.h>
#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
#include <pulsecore/module.h>
#include <pulsecore/llist.h>
#include <pulsecore/sink.h>
@@ -40,6 +41,12 @@
#include <pulsecore/core-util.h>
#include <pulsecore/modargs.h>
#include <pulsecore/namereg.h>
+#include <pulsecore/mutex.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/rtclock.h>
+#include <pulsecore/core-error.h>
#include "module-combine-symdef.h"
@@ -55,13 +62,12 @@ PA_MODULE_USAGE(
"format=<sample format> "
"channels=<number of channels> "
"rate=<sample rate> "
- "channel_map=<channel map> ")
+ "channel_map=<channel map>")
#define DEFAULT_SINK_NAME "combined"
#define MEMBLOCKQ_MAXLENGTH (1024*170)
-#define RENDER_SIZE (1024*10)
-#define DEFAULT_ADJUST_TIME 20
+#define DEFAULT_ADJUST_TIME 10
static const char* const valid_modargs[] = {
"sink_name",
@@ -78,170 +84,689 @@ static const char* const valid_modargs[] = {
struct output {
struct userdata *userdata;
+
+ pa_sink *sink;
pa_sink_input *sink_input;
- size_t counter;
+
+ pa_asyncmsgq *inq, /* Message queue from the sink thread to this sink input */
+ *outq; /* Message queue from this sink input to the sink thread */
+ pa_rtpoll_item *inq_rtpoll_item, *outq_rtpoll_item;
+
pa_memblockq *memblockq;
+
pa_usec_t total_latency;
+
PA_LLIST_FIELDS(struct output);
};
struct userdata {
- pa_module *module;
pa_core *core;
+ pa_module *module;
pa_sink *sink;
- unsigned n_outputs;
- struct output *master;
+
+ pa_thread *thread;
+ pa_thread_mq thread_mq;
+ pa_rtpoll *rtpoll;
+
pa_time_event *time_event;
uint32_t adjust_time;
- PA_LLIST_HEAD(struct output, outputs);
+ pa_bool_t automatic;
+ size_t block_size;
+
+ pa_hook_slot *sink_new_slot, *sink_unlink_slot, *sink_state_changed_slot;
+
+ pa_resample_method_t resample_method;
+
+ struct timeval adjust_timestamp;
+
+ struct output *master;
+ pa_idxset* outputs; /* managed in main context */
+
+ struct {
+ PA_LLIST_HEAD(struct output, active_outputs); /* managed in IO thread context */
+ pa_atomic_t running; /* we cache that value here, so that every thread can query it cheaply */
+ struct timeval timestamp;
+ pa_bool_t in_null_mode;
+ } thread_info;
};
-static void output_free(struct output *o);
-static void clear_up(struct userdata *u);
+enum {
+ SINK_MESSAGE_ADD_OUTPUT = PA_SINK_MESSAGE_MAX,
+ SINK_MESSAGE_REMOVE_OUTPUT,
+ SINK_MESSAGE_NEED
+};
-static void update_usage(struct userdata *u) {
- pa_module_set_used(u->module, u->sink ? pa_sink_used_by(u->sink) : 0);
-}
+enum {
+ SINK_INPUT_MESSAGE_POST = PA_SINK_INPUT_MESSAGE_MAX
+};
+
+static void output_free(struct output *o);
+static int output_create_sink_input(struct output *o);
+static void update_master(struct userdata *u, struct output *o);
+static void pick_master(struct userdata *u, struct output *except);
static void adjust_rates(struct userdata *u) {
struct output *o;
pa_usec_t max_sink_latency = 0, min_total_latency = (pa_usec_t) -1, target_latency;
uint32_t base_rate;
- assert(u && u->sink);
+ uint32_t idx;
- for (o = u->outputs; o; o = o->next) {
- uint32_t sink_latency = o->sink_input->sink ? pa_sink_get_latency(o->sink_input->sink) : 0;
+ pa_assert(u);
+ pa_sink_assert_ref(u->sink);
+ if (pa_idxset_size(u->outputs) <= 0)
+ return;
+
+ if (!u->master)
+ return;
+
+ if (!PA_SINK_OPENED(pa_sink_get_state(u->sink)))
+ return;
+
+ for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) {
+ pa_usec_t sink_latency;
+
+ if (!o->sink_input || !PA_SINK_OPENED(pa_sink_get_state(o->sink)))
+ continue;
+
+ sink_latency = pa_sink_get_latency(o->sink);
o->total_latency = sink_latency + pa_sink_input_get_latency(o->sink_input);
if (sink_latency > max_sink_latency)
max_sink_latency = sink_latency;
- if (o->total_latency < min_total_latency)
+ if (min_total_latency == (pa_usec_t) -1 || o->total_latency < min_total_latency)
min_total_latency = o->total_latency;
}
- assert(min_total_latency != (pa_usec_t) -1);
+ if (min_total_latency == (pa_usec_t) -1)
+ return;
target_latency = max_sink_latency > min_total_latency ? max_sink_latency : min_total_latency;
pa_log_info("[%s] target latency is %0.0f usec.", u->sink->name, (float) target_latency);
+ pa_log_info("[%s] master %s latency %0.0f usec.", u->sink->name, u->master->sink->name, (float) u->master->total_latency);
base_rate = u->sink->sample_spec.rate;
- for (o = u->outputs; o; o = o->next) {
+ for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) {
uint32_t r = base_rate;
+ if (!o->sink_input || !PA_SINK_OPENED(pa_sink_get_state(o->sink)))
+ continue;
+
if (o->total_latency < target_latency)
- r -= (uint32_t) (((((double) target_latency - o->total_latency))/u->adjust_time)*r/ 1000000);
+ r -= (uint32_t) (((((double) target_latency - o->total_latency))/u->adjust_time)*r/PA_USEC_PER_SEC);
else if (o->total_latency > target_latency)
- r += (uint32_t) (((((double) o->total_latency - target_latency))/u->adjust_time)*r/ 1000000);
+ r += (uint32_t) (((((double) o->total_latency - target_latency))/u->adjust_time)*r/PA_USEC_PER_SEC);
- if (r < (uint32_t) (base_rate*0.9) || r > (uint32_t) (base_rate*1.1))
+ if (r < (uint32_t) (base_rate*0.9) || r > (uint32_t) (base_rate*1.1)) {
pa_log_warn("[%s] sample rates too different, not adjusting (%u vs. %u).", o->sink_input->name, base_rate, r);
- else {
+ pa_sink_input_set_rate(o->sink_input, base_rate);
+ } else {
pa_log_info("[%s] new rate is %u Hz; ratio is %0.3f; latency is %0.0f usec.", o->sink_input->name, r, (double) r / base_rate, (float) o->total_latency);
pa_sink_input_set_rate(o->sink_input, r);
}
}
}
-static void request_memblock(struct userdata *u) {
- pa_memchunk chunk;
- struct output *o;
- assert(u && u->sink);
+static void time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) {
+ struct userdata *u = userdata;
+ struct timeval n;
+
+ pa_assert(u);
+ pa_assert(a);
+ pa_assert(u->time_event == e);
- update_usage(u);
+ adjust_rates(u);
+
+ pa_gettimeofday(&n);
+ n.tv_sec += u->adjust_time;
+ u->sink->core->mainloop->time_restart(e, &n);
+}
+
+static void thread_func(void *userdata) {
+ struct userdata *u = userdata;
- if (pa_sink_render(u->sink, RENDER_SIZE, &chunk) < 0)
+ pa_assert(u);
+
+ pa_log_debug("Thread starting up");
+
+ if (u->core->high_priority)
+ pa_make_realtime();
+
+ pa_thread_mq_install(&u->thread_mq);
+ pa_rtpoll_install(u->rtpoll);
+
+ pa_rtclock_get(&u->thread_info.timestamp);
+ u->thread_info.in_null_mode = FALSE;
+
+ for (;;) {
+ int ret;
+
+ /* If no outputs are connected, render some data and drop it immediately. */
+ if (u->sink->thread_info.state == PA_SINK_RUNNING && !u->thread_info.active_outputs) {
+ struct timeval now;
+
+ pa_rtclock_get(&now);
+
+ if (!u->thread_info.in_null_mode || pa_timeval_cmp(&u->thread_info.timestamp, &now) <= 0) {
+ pa_sink_skip(u->sink, u->block_size);
+
+ if (!u->thread_info.in_null_mode)
+ u->thread_info.timestamp = now;
+
+ pa_timeval_add(&u->thread_info.timestamp, pa_bytes_to_usec(u->block_size, &u->sink->sample_spec));
+ }
+
+ pa_rtpoll_set_timer_absolute(u->rtpoll, &u->thread_info.timestamp);
+ u->thread_info.in_null_mode = TRUE;
+
+ } else {
+ pa_rtpoll_set_timer_disabled(u->rtpoll);
+ u->thread_info.in_null_mode = FALSE;
+ }
+
+ /* Hmm, nothing to do. Let's sleep */
+ if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0) {
+ pa_log_info("pa_rtpoll_run() = %i", ret);
+ goto fail;
+ }
+
+ if (ret == 0)
+ goto finish;
+ }
+
+fail:
+ /* If this was no regular exit from the loop we have to continue
+ * processing messages until we received PA_MESSAGE_SHUTDOWN */
+ pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+ pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+ pa_log_debug("Thread shutting down");
+}
+
+/* Called from I/O thread context */
+static void render_memblock(struct userdata *u, struct output *o, size_t length) {
+ pa_assert(u);
+ pa_assert(o);
+
+ /* We are run by the sink thread, on behalf of an output (o). The
+ * other output is waiting for us, hence it is safe to access its
+ * mainblockq and asyncmsgq directly. */
+
+ /* If we are not running, we cannot produce any data */
+ if (!pa_atomic_load(&u->thread_info.running))
return;
- for (o = u->outputs; o; o = o->next)
- pa_memblockq_push_align(o->memblockq, &chunk);
+ /* Maybe there's some data in the requesting output's queue
+ * now? */
+ while (pa_asyncmsgq_process_one(o->inq) > 0)
+ ;
+
+ /* Ok, now let's prepare some data if we really have to */
+ while (!pa_memblockq_is_readable(o->memblockq)) {
+ struct output *j;
+ pa_memchunk chunk;
+
+ /* Render data! */
+ pa_sink_render(u->sink, length, &chunk);
+
+ /* OK, let's send this data to the other threads */
+ for (j = u->thread_info.active_outputs; j; j = j->next)
- pa_memblock_unref(chunk.memblock);
+ /* Send to other outputs, which are not the requesting
+ * one */
+
+ if (j != o)
+ pa_asyncmsgq_post(j->inq, PA_MSGOBJECT(j->sink_input), SINK_INPUT_MESSAGE_POST, NULL, 0, &chunk, NULL);
+
+ /* And place it directly into the requesting output's queue */
+ if (o)
+ pa_memblockq_push_align(o->memblockq, &chunk);
+
+ pa_memblock_unref(chunk.memblock);
+ }
}
-static void time_callback(pa_mainloop_api*a, pa_time_event* e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
- struct userdata *u = userdata;
- struct timeval n;
- assert(u && a && u->time_event == e);
+/* Called from I/O thread context */
+static void request_memblock(struct output *o, size_t length) {
+ pa_assert(o);
+ pa_sink_input_assert_ref(o->sink_input);
+ pa_sink_assert_ref(o->userdata->sink);
- adjust_rates(u);
+ /* If another thread already prepared some data we received
+ * the data over the asyncmsgq, hence let's first process
+ * it. */
+ while (pa_asyncmsgq_process_one(o->inq) > 0)
+ ;
- pa_gettimeofday(&n);
- n.tv_sec += u->adjust_time;
- u->sink->core->mainloop->time_restart(e, &n);
+ /* Check whether we're now readable */
+ if (pa_memblockq_is_readable(o->memblockq))
+ return;
+
+ /* OK, we need to prepare new data, but only if the sink is actually running */
+ if (pa_atomic_load(&o->userdata->thread_info.running))
+ pa_asyncmsgq_send(o->outq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_NEED, o, length, NULL);
}
-static int sink_input_peek_cb(pa_sink_input *i, pa_memchunk *chunk) {
- struct output *o = i->userdata;
- assert(i && o && o->sink_input && chunk);
+/* Called from I/O thread context */
+static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
+ struct output *o;
- if (pa_memblockq_peek(o->memblockq, chunk) >= 0)
- return 0;
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(o = i->userdata);
- /* Try harder */
- request_memblock(o->userdata);
+ /* If necessary, get some new data */
+ request_memblock(o, length);
return pa_memblockq_peek(o->memblockq, chunk);
}
-static void sink_input_drop_cb(pa_sink_input *i, const pa_memchunk *chunk, size_t length) {
- struct output *o = i->userdata;
- assert(i && o && o->sink_input && chunk && length);
+/* Called from I/O thread context */
+static void sink_input_drop_cb(pa_sink_input *i, size_t length) {
+ struct output *o;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert(length > 0);
+ pa_assert_se(o = i->userdata);
+
+ pa_memblockq_drop(o->memblockq, length);
+}
+
+/* Called from I/O thread context */
+static void sink_input_attach_cb(pa_sink_input *i) {
+ struct output *o;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(o = i->userdata);
+
+ /* Set up the queue from the sink thread to us */
+ pa_assert(!o->inq_rtpoll_item);
+ o->inq_rtpoll_item = pa_rtpoll_item_new_asyncmsgq(
+ i->sink->rtpoll,
+ PA_RTPOLL_LATE, /* This one is not that important, since we check for data in _peek() anyway. */
+ o->inq);
+}
+
+/* Called from I/O thread context */
+static void sink_input_detach_cb(pa_sink_input *i) {
+ struct output *o;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(o = i->userdata);
- pa_memblockq_drop(o->memblockq, chunk, length);
- o->counter += length;
+ /* Shut down the queue from the sink thread to us */
+ pa_assert(o->inq_rtpoll_item);
+ pa_rtpoll_item_free(o->inq_rtpoll_item);
+ o->inq_rtpoll_item = NULL;
}
+/* Called from main context */
static void sink_input_kill_cb(pa_sink_input *i) {
- struct output *o = i->userdata;
- assert(i && o && o->sink_input);
+ struct output *o;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert(o = i->userdata);
+
pa_module_unload_request(o->userdata->module);
- clear_up(o->userdata);
+ output_free(o);
+}
+
+/* Called from thread context */
+static int sink_input_process_msg(pa_msgobject *obj, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+ struct output *o = PA_SINK_INPUT(obj)->userdata;
+
+ switch (code) {
+
+ case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
+ pa_usec_t *r = data;
+
+ *r = pa_bytes_to_usec(pa_memblockq_get_length(o->memblockq), &o->sink_input->sample_spec);
+
+ /* Fall through, the default handler will add in the extra
+ * latency added by the resampler */
+ break;
+ }
+
+ case SINK_INPUT_MESSAGE_POST:
+
+ if (PA_SINK_OPENED(o->sink_input->sink->thread_info.state))
+ pa_memblockq_push_align(o->memblockq, chunk);
+ else
+ pa_memblockq_flush(o->memblockq);
+
+ break;
+ }
+
+ return pa_sink_input_process_msg(obj, code, data, offset, chunk);
}
-static pa_usec_t sink_input_get_latency_cb(pa_sink_input *i) {
- struct output *o = i->userdata;
- assert(i && o && o->sink_input);
+/* Called from main context */
+static void disable_output(struct output *o) {
+ pa_assert(o);
+
+ if (!o->sink_input)
+ return;
+
+ pa_asyncmsgq_send(o->userdata->sink->asyncmsgq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_REMOVE_OUTPUT, o, 0, NULL);
+ pa_sink_input_unlink(o->sink_input);
+ pa_sink_input_unref(o->sink_input);
+ o->sink_input = NULL;
- return pa_bytes_to_usec(pa_memblockq_get_length(o->memblockq), &i->sample_spec);
}
-static pa_usec_t sink_get_latency_cb(pa_sink *s) {
- struct userdata *u = s->userdata;
- assert(s && u && u->sink && u->master);
+/* Called from main context */
+static void enable_output(struct output *o) {
+ pa_assert(o);
+
+ if (o->sink_input)
+ return;
+
+ if (output_create_sink_input(o) >= 0) {
+
+ pa_memblockq_flush(o->memblockq);
+
+ pa_sink_input_put(o->sink_input);
+
+ if (o->userdata->sink && PA_SINK_LINKED(pa_sink_get_state(o->userdata->sink)))
+ pa_asyncmsgq_send(o->userdata->sink->asyncmsgq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_ADD_OUTPUT, o, 0, NULL);
+ }
+}
+
+/* Called from main context */
+static void suspend(struct userdata *u) {
+ struct output *o;
+ uint32_t idx;
+
+ pa_assert(u);
+
+ /* Let's suspend by unlinking all streams */
+ for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx))
+ disable_output(o);
+
+ pick_master(u, NULL);
+
+ pa_log_info("Device suspended...");
+}
+
+/* Called from main context */
+static void unsuspend(struct userdata *u) {
+ struct output *o;
+ uint32_t idx;
+
+ pa_assert(u);
+
+ /* Let's resume */
+ for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) {
+
+ pa_sink_suspend(o->sink, FALSE);
+
+ if (PA_SINK_OPENED(pa_sink_get_state(o->sink)))
+ enable_output(o);
+ }
+
+ pick_master(u, NULL);
+
+ pa_log_info("Resumed successfully...");
+}
+
+/* Called from main context */
+static int sink_set_state(pa_sink *sink, pa_sink_state_t state) {
+ struct userdata *u;
+
+ pa_sink_assert_ref(sink);
+ pa_assert_se(u = sink->userdata);
+
+ /* Please note that in contrast to the ALSA modules we call
+ * suspend/unsuspend from main context here! */
+
+ switch (state) {
+ case PA_SINK_SUSPENDED:
+ pa_assert(PA_SINK_OPENED(pa_sink_get_state(u->sink)));
+
+ suspend(u);
+ break;
+
+ case PA_SINK_IDLE:
+ case PA_SINK_RUNNING:
+
+ if (pa_sink_get_state(u->sink) == PA_SINK_SUSPENDED)
+ unsuspend(u);
+
+ break;
+
+ case PA_SINK_UNLINKED:
+ case PA_SINK_INIT:
+ ;
+ }
+
+ return 0;
+}
+
+/* Called from thread context of the master */
+static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+ struct userdata *u = PA_SINK(o)->userdata;
+
+ switch (code) {
+
+ case PA_SINK_MESSAGE_SET_STATE:
+ pa_atomic_store(&u->thread_info.running, PA_PTR_TO_UINT(data) == PA_SINK_RUNNING);
+ break;
+
+ case PA_SINK_MESSAGE_GET_LATENCY:
+
+ /* This code will only be called when running in NULL
+ * mode, i.e. when no output is attached. See
+ * sink_get_latency_cb() below */
+
+ if (u->thread_info.in_null_mode) {
+ struct timeval now;
+
+ if (pa_timeval_cmp(&u->thread_info.timestamp, pa_rtclock_get(&now)) > 0) {
+ *((pa_usec_t*) data) = pa_timeval_diff(&u->thread_info.timestamp, &now);
+ break;
+ }
+ }
+
+ *((pa_usec_t*) data) = 0;
+
+ break;
+
+ case SINK_MESSAGE_ADD_OUTPUT: {
+ struct output *op = data;
+
+ PA_LLIST_PREPEND(struct output, u->thread_info.active_outputs, op);
+
+ pa_assert(!op->outq_rtpoll_item);
+
+ /* Create pa_asyncmsgq to the sink thread */
+
+ op->outq_rtpoll_item = pa_rtpoll_item_new_asyncmsgq(
+ u->rtpoll,
+ PA_RTPOLL_EARLY-1, /* This item is very important */
+ op->outq);
+
+ return 0;
+ }
+
+ case SINK_MESSAGE_REMOVE_OUTPUT: {
+ struct output *op = data;
- return
- pa_sink_input_get_latency(u->master->sink_input) +
- pa_sink_get_latency(u->master->sink_input->sink);
+ PA_LLIST_REMOVE(struct output, u->thread_info.active_outputs, op);
+
+ /* Remove the q that leads from this output to the sink thread */
+
+ pa_assert(op->outq_rtpoll_item);
+ pa_rtpoll_item_free(op->outq_rtpoll_item);
+ op->outq_rtpoll_item = NULL;
+
+ return 0;
+ }
+
+ case SINK_MESSAGE_NEED:
+ render_memblock(u, data, (size_t) offset);
+ return 0;
+ }
+
+ return pa_sink_process_msg(o, code, data, offset, chunk);
}
-static void sink_notify(pa_sink *s) {
+/* Called from main context */
+static pa_usec_t sink_get_latency_cb(pa_sink *s) {
struct userdata *u;
+
+ pa_sink_assert_ref(s);
+ pa_assert_se(u = s->userdata);
+
+ if (u->master) {
+ /* If we have a master sink, we just return the latency of it
+ * and add our own buffering on top */
+
+ if (!u->master->sink_input)
+ return 0;
+
+ return
+ pa_sink_input_get_latency(u->master->sink_input) +
+ pa_sink_get_latency(u->master->sink);
+
+ } else {
+ pa_usec_t usec = 0;
+
+ /* We have no master, hence let's ask our own thread which
+ * implements the NULL sink */
+
+ if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
+ return 0;
+
+ return usec;
+ }
+}
+
+static void update_description(struct userdata *u) {
+ int first = 1;
+ char *t;
+ struct output *o;
+ uint32_t idx;
+
+ pa_assert(u);
+
+ if (pa_idxset_isempty(u->outputs)) {
+ pa_sink_set_description(u->sink, "Simultaneous output");
+ return;
+ }
+
+ t = pa_xstrdup("Simultaneous output to");
+
+ for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) {
+ char *e;
+
+ if (first) {
+ e = pa_sprintf_malloc("%s %s", t, o->sink->description);
+ first = 0;
+ } else
+ e = pa_sprintf_malloc("%s, %s", t, o->sink->description);
+
+ pa_xfree(t);
+ t = e;
+ }
+
+ pa_sink_set_description(u->sink, t);
+ pa_xfree(t);
+}
+
+static void update_master(struct userdata *u, struct output *o) {
+ pa_assert(u);
+
+ if (u->master == o)
+ return;
+
+ if ((u->master = o))
+ pa_log_info("Master sink is now '%s'", o->sink_input->sink->name);
+ else
+ pa_log_info("No master selected, lacking suitable outputs.");
+}
+
+static void pick_master(struct userdata *u, struct output *except) {
struct output *o;
+ uint32_t idx;
+ pa_assert(u);
+
+ if (u->master &&
+ u->master != except &&
+ u->master->sink_input &&
+ PA_SINK_OPENED(pa_sink_get_state(u->master->sink))) {
+ update_master(u, u->master);
+ return;
+ }
- assert(s);
- u = s->userdata;
- assert(u);
+ for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx))
+ if (o != except &&
+ o->sink_input &&
+ PA_SINK_OPENED(pa_sink_get_state(o->sink))) {
+ update_master(u, o);
+ return;
+ }
- for (o = u->outputs; o; o = o->next)
- pa_sink_notify(o->sink_input->sink);
+ update_master(u, NULL);
}
-static struct output *output_new(struct userdata *u, pa_sink *sink, int resample_method) {
- struct output *o = NULL;
- char t[256];
+static int output_create_sink_input(struct output *o) {
pa_sink_input_new_data data;
+ char *t;
- assert(u && sink && u->sink);
+ pa_assert(o);
- o = pa_xmalloc(sizeof(struct output));
- o->userdata = u;
+ if (o->sink_input)
+ return 0;
+
+ t = pa_sprintf_malloc("Simultaneous output on %s", o->sink->description);
- o->counter = 0;
+ pa_sink_input_new_data_init(&data);
+ data.sink = o->sink;
+ data.driver = __FILE__;
+ data.name = t;
+ pa_sink_input_new_data_set_sample_spec(&data, &o->userdata->sink->sample_spec);
+ pa_sink_input_new_data_set_channel_map(&data, &o->userdata->sink->channel_map);
+ data.module = o->userdata->module;
+ data.resample_method = o->userdata->resample_method;
+
+ o->sink_input = pa_sink_input_new(o->userdata->core, &data, PA_SINK_INPUT_VARIABLE_RATE|PA_SINK_INPUT_DONT_MOVE);
+
+ pa_xfree(t);
+
+ if (!o->sink_input)
+ return -1;
+
+ o->sink_input->parent.process_msg = sink_input_process_msg;
+ o->sink_input->peek = sink_input_peek_cb;
+ o->sink_input->drop = sink_input_drop_cb;
+ o->sink_input->attach = sink_input_attach_cb;
+ o->sink_input->detach = sink_input_detach_cb;
+ o->sink_input->kill = sink_input_kill_cb;
+ o->sink_input->userdata = o;
+
+
+ return 0;
+}
+
+static struct output *output_new(struct userdata *u, pa_sink *sink) {
+ struct output *o;
+
+ pa_assert(u);
+ pa_assert(sink);
+ pa_assert(u->sink);
+
+ o = pa_xnew(struct output, 1);
+ o->userdata = u;
+ o->inq = pa_asyncmsgq_new(0);
+ o->outq = pa_asyncmsgq_new(0);
+ o->inq_rtpoll_item = NULL;
+ o->outq_rtpoll_item = NULL;
+ o->sink = sink;
+ o->sink_input = NULL;
o->memblockq = pa_memblockq_new(
0,
MEMBLOCKQ_MAXLENGTH,
@@ -251,90 +776,151 @@ static struct output *output_new(struct userdata *u, pa_sink *sink, int resample
0,
NULL);
- snprintf(t, sizeof(t), "Output stream #%u of sink %s", u->n_outputs+1, u->sink->name);
+ pa_assert_se(pa_idxset_put(u->outputs, o, NULL) == 0);
- pa_sink_input_new_data_init(&data);
- data.sink = sink;
- data.driver = __FILE__;
- data.name = t;
- pa_sink_input_new_data_set_sample_spec(&data, &u->sink->sample_spec);
- pa_sink_input_new_data_set_channel_map(&data, &u->sink->channel_map);
- data.module = u->module;
+ if (u->sink && PA_SINK_LINKED(pa_sink_get_state(u->sink)))
+ pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_ADD_OUTPUT, o, 0, NULL);
+ else {
+ /* If the sink is not yet started, we need to do the activation ourselves */
+ PA_LLIST_PREPEND(struct output, u->thread_info.active_outputs, o);
- if (!(o->sink_input = pa_sink_input_new(u->core, &data, PA_SINK_INPUT_VARIABLE_RATE)))
- goto fail;
+ o->outq_rtpoll_item = pa_rtpoll_item_new_asyncmsgq(
+ u->rtpoll,
+ PA_RTPOLL_EARLY-1, /* This item is very important */
+ o->outq);
+ }
- o->sink_input->get_latency = sink_input_get_latency_cb;
- o->sink_input->peek = sink_input_peek_cb;
- o->sink_input->drop = sink_input_drop_cb;
- o->sink_input->kill = sink_input_kill_cb;
- o->sink_input->userdata = o;
+ if (PA_SINK_OPENED(pa_sink_get_state(u->sink)) || pa_sink_get_state(u->sink) == PA_SINK_INIT) {
+ pa_sink_suspend(sink, FALSE);
+
+ if (PA_SINK_OPENED(pa_sink_get_state(sink)))
+ if (output_create_sink_input(o) < 0)
+ goto fail;
+ }
+
+
+ update_description(u);
- PA_LLIST_PREPEND(struct output, u->outputs, o);
- u->n_outputs++;
return o;
fail:
if (o) {
+ pa_idxset_remove_by_data(u->outputs, o, NULL);
+
if (o->sink_input) {
- pa_sink_input_disconnect(o->sink_input);
+ pa_sink_input_unlink(o->sink_input);
pa_sink_input_unref(o->sink_input);
}
if (o->memblockq)
pa_memblockq_free(o->memblockq);
+ if (o->inq)
+ pa_asyncmsgq_unref(o->inq);
+
+ if (o->outq)
+ pa_asyncmsgq_unref(o->outq);
+
pa_xfree(o);
}
return NULL;
}
-static void output_free(struct output *o) {
- assert(o);
- PA_LLIST_REMOVE(struct output, o->userdata->outputs, o);
- o->userdata->n_outputs--;
- pa_memblockq_free(o->memblockq);
- pa_sink_input_disconnect(o->sink_input);
- pa_sink_input_unref(o->sink_input);
- pa_xfree(o);
+static pa_hook_result_t sink_new_hook_cb(pa_core *c, pa_sink *s, struct userdata* u) {
+ struct output *o;
+
+ pa_core_assert_ref(c);
+ pa_sink_assert_ref(s);
+ pa_assert(u);
+ pa_assert(u->automatic);
+
+ if (!(s->flags & PA_SINK_HARDWARE) || s == u->sink)
+ return PA_HOOK_OK;
+
+ pa_log_info("Configuring new sink: %s", s->name);
+
+ if (!(o = output_new(u, s))) {
+ pa_log("Failed to create sink input on sink '%s'.", s->name);
+ return PA_HOOK_OK;
+ }
+
+ if (o->sink_input)
+ pa_sink_input_put(o->sink_input);
+
+ pick_master(u, NULL);
+
+ return PA_HOOK_OK;
}
-static void clear_up(struct userdata *u) {
+static pa_hook_result_t sink_unlink_hook_cb(pa_core *c, pa_sink *s, struct userdata* u) {
struct output *o;
- assert(u);
+ uint32_t idx;
- if (u->time_event) {
- u->core->mainloop->time_free(u->time_event);
- u->time_event = NULL;
- }
+ pa_assert(c);
+ pa_sink_assert_ref(s);
+ pa_assert(u);
- while ((o = u->outputs))
- output_free(o);
+ if (s == u->sink)
+ return PA_HOOK_OK;
- u->master = NULL;
+ for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx))
+ if (o->sink == s)
+ break;
- if (u->sink) {
- pa_sink_disconnect(u->sink);
- pa_sink_unref(u->sink);
- u->sink = NULL;
+ if (!o)
+ return PA_HOOK_OK;
+
+ pa_log_info("Unconfiguring sink: %s", s->name);
+
+ output_free(o);
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_state_changed_hook_cb(pa_core *c, pa_sink *s, struct userdata* u) {
+ struct output *o;
+ uint32_t idx;
+ pa_sink_state_t state;
+
+ if (s == u->sink)
+ return PA_HOOK_OK;
+
+ for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx))
+ if (o->sink == s)
+ break;
+
+ if (!o)
+ return PA_HOOK_OK;
+
+ state = pa_sink_get_state(s);
+
+ if (PA_SINK_OPENED(state) && PA_SINK_OPENED(pa_sink_get_state(u->sink)) && !o->sink_input) {
+ enable_output(o);
+ pick_master(u, NULL);
+ }
+
+ if (state == PA_SINK_SUSPENDED && o->sink_input) {
+ disable_output(o);
+ pick_master(u, o);
}
+
+ return PA_HOOK_OK;
}
-int pa__init(pa_core *c, pa_module*m) {
+int pa__init(pa_module*m) {
struct userdata *u;
pa_modargs *ma = NULL;
const char *master_name, *slaves, *rm;
- pa_sink *master_sink;
- char *n = NULL;
- const char*split_state;
- struct timeval tv;
- int resample_method = -1;
+ pa_sink *master_sink = NULL;
+ int resample_method = PA_RESAMPLER_TRIVIAL;
pa_sample_spec ss;
pa_channel_map map;
+ struct output *o;
+ uint32_t idx;
- assert(c && m);
+ pa_assert(m);
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
pa_log("failed to parse module arguments");
@@ -349,116 +935,256 @@ int pa__init(pa_core *c, pa_module*m) {
}
u = pa_xnew(struct userdata, 1);
+ u->core = m->core;
+ u->module = m;
m->userdata = u;
u->sink = NULL;
- u->n_outputs = 0;
u->master = NULL;
- u->module = m;
- u->core = c;
u->time_event = NULL;
u->adjust_time = DEFAULT_ADJUST_TIME;
- PA_LLIST_HEAD_INIT(struct output, u->outputs);
+ pa_thread_mq_init(&u->thread_mq, m->core->mainloop);
+ u->rtpoll = pa_rtpoll_new();
+ u->thread = NULL;
+ u->resample_method = resample_method;
+ u->outputs = pa_idxset_new(NULL, NULL);
+ memset(&u->adjust_timestamp, 0, sizeof(u->adjust_timestamp));
+ u->sink_new_slot = u->sink_unlink_slot = u->sink_state_changed_slot = NULL;
+ PA_LLIST_HEAD_INIT(struct output, u->thread_info.active_outputs);
+ pa_atomic_store(&u->thread_info.running, FALSE);
+ u->thread_info.in_null_mode = FALSE;
+ pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq);
if (pa_modargs_get_value_u32(ma, "adjust_time", &u->adjust_time) < 0) {
- pa_log("failed to parse adjust_time value");
+ pa_log("Failed to parse adjust_time value");
goto fail;
}
- if (!(master_name = pa_modargs_get_value(ma, "master", NULL)) || !(slaves = pa_modargs_get_value(ma, "slaves", NULL))) {
- pa_log("no master or slave sinks specified");
+ master_name = pa_modargs_get_value(ma, "master", NULL);
+ slaves = pa_modargs_get_value(ma, "slaves", NULL);
+ if (!master_name != !slaves) {
+ pa_log("No master or slave sinks specified");
goto fail;
}
- if (!(master_sink = pa_namereg_get(c, master_name, PA_NAMEREG_SINK, 1))) {
- pa_log("invalid master sink '%s'", master_name);
- goto fail;
+ if (master_name) {
+ if (!(master_sink = pa_namereg_get(m->core, master_name, PA_NAMEREG_SINK, 1))) {
+ pa_log("Invalid master sink '%s'", master_name);
+ goto fail;
+ }
+
+ ss = master_sink->sample_spec;
+ u->automatic = FALSE;
+ } else {
+ master_sink = NULL;
+ ss = m->core->default_sample_spec;
+ u->automatic = TRUE;
}
- ss = master_sink->sample_spec;
if ((pa_modargs_get_sample_spec(ma, &ss) < 0)) {
- pa_log("invalid sample specification.");
+ pa_log("Invalid sample specification.");
goto fail;
}
- if (ss.channels == master_sink->sample_spec.channels)
+ if (master_sink && ss.channels == master_sink->sample_spec.channels)
map = master_sink->channel_map;
else
pa_channel_map_init_auto(&map, ss.channels, PA_CHANNEL_MAP_DEFAULT);
- if ((pa_modargs_get_channel_map(ma, &map) < 0)) {
- pa_log("invalid channel map.");
+ if ((pa_modargs_get_channel_map(ma, NULL, &map) < 0)) {
+ pa_log("Invalid channel map.");
goto fail;
}
if (ss.channels != map.channels) {
- pa_log("channel map and sample specification don't match.");
+ pa_log("Channel map and sample specification don't match.");
goto fail;
}
- if (!(u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, &map))) {
- pa_log("failed to create sink");
+ if (!(u->sink = pa_sink_new(m->core, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, &map))) {
+ pa_log("Failed to create sink");
goto fail;
}
- pa_sink_set_owner(u->sink, m);
- pa_sink_set_description(u->sink, "Combined Sink");
+ u->sink->parent.process_msg = sink_process_msg;
u->sink->get_latency = sink_get_latency_cb;
- u->sink->notify = sink_notify;
+ u->sink->set_state = sink_set_state;
u->sink->userdata = u;
- if (!(u->master = output_new(u, master_sink, resample_method))) {
- pa_log("failed to create master sink input on sink '%s'.", u->sink->name);
- goto fail;
- }
+ u->sink->flags = PA_SINK_LATENCY;
+ pa_sink_set_module(u->sink, m);
+ pa_sink_set_description(u->sink, "Simultaneous output");
+ pa_sink_set_rtpoll(u->sink, u->rtpoll);
+ pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
+
+ u->block_size = pa_bytes_per_second(&ss) / 20; /* 50 ms */
+ if (u->block_size <= 0)
+ u->block_size = pa_frame_size(&ss);
- split_state = NULL;
- while ((n = pa_split(slaves, ",", &split_state))) {
- pa_sink *slave_sink;
+ if (!u->automatic) {
+ const char*split_state;
+ char *n = NULL;
+ pa_assert(slaves);
- if (!(slave_sink = pa_namereg_get(c, n, PA_NAMEREG_SINK, 1))) {
- pa_log("invalid slave sink '%s'", n);
+ /* The master and slaves have been specified manually */
+
+ if (!(u->master = output_new(u, master_sink))) {
+ pa_log("Failed to create master sink input on sink '%s'.", master_sink->name);
goto fail;
}
- pa_xfree(n);
+ split_state = NULL;
+ while ((n = pa_split(slaves, ",", &split_state))) {
+ pa_sink *slave_sink;
- if (!output_new(u, slave_sink, resample_method)) {
- pa_log("failed to create slave sink input on sink '%s'.", slave_sink->name);
- goto fail;
+ if (!(slave_sink = pa_namereg_get(m->core, n, PA_NAMEREG_SINK, 1)) || slave_sink == u->sink) {
+ pa_log("Invalid slave sink '%s'", n);
+ pa_xfree(n);
+ goto fail;
+ }
+
+ pa_xfree(n);
+
+ if (!output_new(u, slave_sink)) {
+ pa_log("Failed to create slave sink input on sink '%s'.", slave_sink->name);
+ goto fail;
+ }
}
+
+ if (pa_idxset_size(u->outputs) <= 1)
+ pa_log_warn("No slave sinks specified.");
+
+ u->sink_new_slot = NULL;
+
+ } else {
+ pa_sink *s;
+
+ /* We're in automatic mode, we elect one hw sink to the master
+ * and attach all other hw sinks as slaves to it */
+
+ for (s = pa_idxset_first(m->core->sinks, &idx); s; s = pa_idxset_next(m->core->sinks, &idx)) {
+
+ if (!(s->flags & PA_SINK_HARDWARE) || s == u->sink)
+ continue;
+
+ if (!output_new(u, s)) {
+ pa_log("Failed to create sink input on sink '%s'.", s->name);
+ goto fail;
+ }
+ }
+
+ u->sink_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_NEW_POST], (pa_hook_cb_t) sink_new_hook_cb, u);
}
- if (u->n_outputs <= 1)
- pa_log_warn("WARNING: no slave sinks specified.");
+ u->sink_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], (pa_hook_cb_t) sink_unlink_hook_cb, u);
+ u->sink_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED], (pa_hook_cb_t) sink_state_changed_hook_cb, u);
+
+ pick_master(u, NULL);
+
+ if (!(u->thread = pa_thread_new(thread_func, u))) {
+ pa_log("Failed to create thread.");
+ goto fail;
+ }
+
+ /* Activate the sink and the sink inputs */
+ pa_sink_put(u->sink);
+
+ for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx))
+ if (o->sink_input)
+ pa_sink_input_put(o->sink_input);
if (u->adjust_time > 0) {
+ struct timeval tv;
pa_gettimeofday(&tv);
tv.tv_sec += u->adjust_time;
- u->time_event = c->mainloop->time_new(c->mainloop, &tv, time_callback, u);
+ u->time_event = m->core->mainloop->time_new(m->core->mainloop, &tv, time_callback, u);
}
pa_modargs_free(ma);
+
return 0;
fail:
- pa_xfree(n);
if (ma)
pa_modargs_free(ma);
- pa__done(c, m);
+ pa__done(m);
+
return -1;
}
-void pa__done(pa_core *c, pa_module*m) {
+static void output_free(struct output *o) {
+ pa_assert(o);
+
+ pick_master(o->userdata, o);
+
+ disable_output(o);
+
+ pa_assert_se(pa_idxset_remove_by_data(o->userdata->outputs, o, NULL));
+
+ update_description(o->userdata);
+
+ if (o->inq_rtpoll_item)
+ pa_rtpoll_item_free(o->inq_rtpoll_item);
+
+ if (o->outq_rtpoll_item)
+ pa_rtpoll_item_free(o->outq_rtpoll_item);
+
+ if (o->inq)
+ pa_asyncmsgq_unref(o->inq);
+
+ if (o->outq)
+ pa_asyncmsgq_unref(o->outq);
+
+ if (o->memblockq)
+ pa_memblockq_free(o->memblockq);
+
+ pa_xfree(o);
+}
+
+void pa__done(pa_module*m) {
struct userdata *u;
- assert(c && m);
+ struct output *o;
+
+ pa_assert(m);
if (!(u = m->userdata))
return;
- clear_up(u);
- pa_xfree(u);
-}
+ if (u->sink_new_slot)
+ pa_hook_slot_free(u->sink_new_slot);
+
+ if (u->sink_unlink_slot)
+ pa_hook_slot_free(u->sink_unlink_slot);
+
+ if (u->sink_state_changed_slot)
+ pa_hook_slot_free(u->sink_state_changed_slot);
+
+ if (u->outputs) {
+ while ((o = pa_idxset_first(u->outputs, NULL)))
+ output_free(o);
+
+ pa_idxset_free(u->outputs, NULL, NULL);
+ }
+ if (u->sink)
+ pa_sink_unlink(u->sink);
+ if (u->thread) {
+ pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+ pa_thread_free(u->thread);
+ }
+
+ pa_thread_mq_done(&u->thread_mq);
+
+ if (u->sink)
+ pa_sink_unref(u->sink);
+
+ if (u->rtpoll)
+ pa_rtpoll_free(u->rtpoll);
+
+ if (u->time_event)
+ u->core->mainloop->time_free(u->time_event);
+
+ pa_xfree(u);
+}
diff --git a/src/modules/module-default-device-restore.c b/src/modules/module-default-device-restore.c
new file mode 100644
index 00000000..a816eae8
--- /dev/null
+++ b/src/modules/module-default-device-restore.c
@@ -0,0 +1,103 @@
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2006 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 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/core-util.h>
+#include <pulsecore/module.h>
+#include <pulsecore/log.h>
+#include <pulsecore/namereg.h>
+
+#include "module-default-device-restore-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering")
+PA_MODULE_DESCRIPTION("Automatically restore the default sink and source")
+PA_MODULE_VERSION(PACKAGE_VERSION)
+
+#define DEFAULT_SINK_FILE "default-sink"
+#define DEFAULT_SOURCE_FILE "default-source"
+
+int pa__init(pa_module *m) {
+ FILE *f;
+
+ /* We never overwrite manually configured settings */
+
+ if (m->core->default_sink_name)
+ pa_log_info("Manually configured default sink, not overwriting.");
+ else if ((f = pa_open_config_file(NULL, DEFAULT_SINK_FILE, NULL, NULL, "r"))) {
+ char ln[256] = "";
+
+ fgets(ln, sizeof(ln)-1, f);
+ pa_strip_nl(ln);
+ fclose(f);
+
+ if (!ln[0])
+ pa_log_debug("No previous default sink setting, ignoring.");
+ else if (pa_namereg_get(m->core, ln, PA_NAMEREG_SINK, 1)) {
+ pa_namereg_set_default(m->core, ln, PA_NAMEREG_SINK);
+ pa_log_debug("Restored default sink '%s'.", ln);
+ } else
+ pa_log_info("Saved default sink '%s' not existant, not restoring default sink setting.", ln);
+ }
+
+ if (m->core->default_source_name)
+ pa_log_info("Manually configured default source, not overwriting.");
+ else if ((f = pa_open_config_file(NULL, DEFAULT_SOURCE_FILE, NULL, NULL, "r"))) {
+ char ln[256] = "";
+
+ fgets(ln, sizeof(ln)-1, f);
+ pa_strip_nl(ln);
+ fclose(f);
+
+ if (!ln[0])
+ pa_log_debug("No previous default source setting, ignoring.");
+ else if (pa_namereg_get(m->core, ln, PA_NAMEREG_SOURCE, 1)) {
+ pa_namereg_set_default(m->core, ln, PA_NAMEREG_SOURCE);
+ pa_log_debug("Restored default source '%s'.", ln);
+ } else
+ pa_log_info("Saved default source '%s' not existant, not restoring default source setting.", ln);
+ }
+
+ return 0;
+}
+
+void pa__done(pa_module*m) {
+ FILE *f;
+
+ if ((f = pa_open_config_file(NULL, DEFAULT_SINK_FILE, NULL, NULL, "w"))) {
+ const char *n = pa_namereg_get_default_sink_name(m->core);
+ fprintf(f, "%s\n", n ? n : "");
+ fclose(f);
+ }
+
+ if ((f = pa_open_config_file(NULL, DEFAULT_SOURCE_FILE, NULL, NULL, "w"))) {
+ const char *n = pa_namereg_get_default_source_name(m->core);
+ fprintf(f, "%s\n", n ? n : "");
+ fclose(f);
+ }
+}
+
+
+
diff --git a/src/modules/module-defs.h.m4 b/src/modules/module-defs.h.m4
index c961412d..5bff748e 100644
--- a/src/modules/module-defs.h.m4
+++ b/src/modules/module-defs.h.m4
@@ -18,8 +18,8 @@ gen_symbol(pa__get_description)
gen_symbol(pa__get_usage)
gen_symbol(pa__get_version)
-int pa__init(struct pa_core *c, struct pa_module*m);
-void pa__done(struct pa_core *c, struct pa_module*m);
+int pa__init(pa_module*m);
+void pa__done(pa_module*m);
const char* pa__get_author(void);
const char* pa__get_description(void);
diff --git a/src/modules/module-detect.c b/src/modules/module-detect.c
index 41b68ac3..7dc25243 100644
--- a/src/modules/module-detect.c
+++ b/src/modules/module-detect.c
@@ -28,7 +28,6 @@
#endif
#include <stdio.h>
-#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
@@ -44,6 +43,7 @@
#include <pulsecore/modargs.h>
#include <pulsecore/log.h>
#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
#include "module-detect-symdef.h"
@@ -52,6 +52,11 @@ PA_MODULE_DESCRIPTION("Detect available audio hardware and load matching drivers
PA_MODULE_VERSION(PACKAGE_VERSION)
PA_MODULE_USAGE("just-one=<boolean>")
+static const char* const valid_modargs[] = {
+ "just-one",
+ NULL
+};
+
#ifdef HAVE_ALSA
static int detect_alsa(pa_core *c, int just_one) {
@@ -96,7 +101,7 @@ static int detect_alsa(pa_core *c, int just_one) {
if (subdevice != 0)
continue;
- snprintf(args, sizeof(args), "device=hw:%u", device);
+ pa_snprintf(args, sizeof(args), "device=hw:%u", device);
if (!pa_module_load(c, is_sink ? "module-alsa-sink" : "module-alsa-source", args))
continue;
@@ -139,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;
}
@@ -148,20 +153,20 @@ static int detect_oss(pa_core *c, int just_one) {
if (sscanf(line, "%u: ", &device) == 1) {
if (device == 0)
- snprintf(args, sizeof(args), "device=/dev/dsp");
+ pa_snprintf(args, sizeof(args), "device=/dev/dsp");
else
- snprintf(args, sizeof(args), "device=/dev/dsp%u", device);
+ pa_snprintf(args, sizeof(args), "device=/dev/dsp%u", device);
if (!pa_module_load(c, "module-oss", args))
continue;
- } else if (sscanf(line, "pcm%u: ", &device) == 1) {
+ } else if (sscanf(line, "pcm%u: ", &device) == 1) {
/* FreeBSD support, the devices are named /dev/dsp0.0, dsp0.1 and so on */
- snprintf(args, sizeof(args), "device=/dev/dsp%u.0", device);
+ pa_snprintf(args, sizeof(args), "device=/dev/dsp%u.0", device);
if (!pa_module_load(c, "module-oss", args))
continue;
- }
+ }
n++;
@@ -193,7 +198,7 @@ static int detect_solaris(pa_core *c, int just_one) {
if (!S_ISCHR(s.st_mode))
return 0;
- snprintf(args, sizeof(args), "device=%s", dev);
+ pa_snprintf(args, sizeof(args), "device=%s", dev);
if (!pa_module_load(c, "module-solaris", args))
return 0;
@@ -215,17 +220,11 @@ static int detect_waveout(pa_core *c, int just_one) {
}
#endif
-int pa__init(pa_core *c, pa_module*m) {
+int pa__init(pa_module*m) {
int just_one = 0, n = 0;
pa_modargs *ma;
- static const char* const valid_modargs[] = {
- "just-one",
- NULL
- };
-
- assert(c);
- assert(m);
+ pa_assert(m);
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
pa_log("Failed to parse module arguments");
@@ -238,16 +237,16 @@ int pa__init(pa_core *c, pa_module*m) {
}
#if HAVE_ALSA
- if ((n = detect_alsa(c, just_one)) <= 0)
+ if ((n = detect_alsa(m->core, just_one)) <= 0)
#endif
#if HAVE_OSS
- if ((n = detect_oss(c, just_one)) <= 0)
+ if ((n = detect_oss(m->core, just_one)) <= 0)
#endif
#if HAVE_SOLARIS
- if ((n = detect_solaris(c, just_one)) <= 0)
+ if ((n = detect_solaris(m->core, just_one)) <= 0)
#endif
#if OS_IS_WIN32
- if ((n = detect_waveout(c, just_one)) <= 0)
+ if ((n = detect_waveout(m->core, just_one)) <= 0)
#endif
{
pa_log_warn("failed to detect any sound hardware.");
@@ -269,9 +268,3 @@ fail:
return -1;
}
-
-
-void pa__done(PA_GCC_UNUSED pa_core *c, PA_GCC_UNUSED pa_module*m) {
- /* NOP */
-}
-
diff --git a/src/modules/module-esound-compat-spawnfd.c b/src/modules/module-esound-compat-spawnfd.c
index 1aecade5..f0f73fcf 100644
--- a/src/modules/module-esound-compat-spawnfd.c
+++ b/src/modules/module-esound-compat-spawnfd.c
@@ -26,7 +26,6 @@
#endif
#include <unistd.h>
-#include <assert.h>
#include <string.h>
#include <errno.h>
@@ -48,23 +47,25 @@ static const char* const valid_modargs[] = {
NULL,
};
-int pa__init(pa_core *c, pa_module*m) {
+int pa__init(pa_module*m) {
pa_modargs *ma = NULL;
int ret = -1, fd = -1;
char x = 1;
- assert(c && m);
+
+ pa_assert(m);
if (!(ma = pa_modargs_new(m->argument, valid_modargs)) ||
pa_modargs_get_value_s32(ma, "fd", &fd) < 0 ||
fd < 0) {
+
pa_log("Failed to parse module arguments");
goto finish;
}
if (pa_loop_write(fd, &x, sizeof(x), NULL) != sizeof(x))
- pa_log("WARNING: write(%u, 1, 1) failed: %s", fd, pa_cstrerror(errno));
+ pa_log_warn("write(%u, 1, 1) failed: %s", fd, pa_cstrerror(errno));
- close(fd);
+ pa_assert_se(pa_close(fd) == 0);
pa_module_unload_request(m);
@@ -76,9 +77,3 @@ finish:
return ret;
}
-
-void pa__done(pa_core *c, pa_module*m) {
- assert(c && m);
-}
-
-
diff --git a/src/modules/module-esound-compat-spawnpid.c b/src/modules/module-esound-compat-spawnpid.c
index a9fd166d..6562fe28 100644
--- a/src/modules/module-esound-compat-spawnpid.c
+++ b/src/modules/module-esound-compat-spawnpid.c
@@ -25,7 +25,6 @@
#endif
#include <unistd.h>
-#include <assert.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
@@ -48,11 +47,12 @@ static const char* const valid_modargs[] = {
NULL,
};
-int pa__init(pa_core *c, pa_module*m) {
+int pa__init(pa_module*m) {
pa_modargs *ma = NULL;
int ret = -1;
uint32_t pid = 0;
- assert(c && m);
+
+ pa_assert(m);
if (!(ma = pa_modargs_new(m->argument, valid_modargs)) ||
pa_modargs_get_value_u32(ma, "pid", &pid) < 0 ||
@@ -62,7 +62,7 @@ int pa__init(pa_core *c, pa_module*m) {
}
if (kill(pid, SIGUSR1) < 0)
- pa_log("WARNING: kill(%u) failed: %s", pid, pa_cstrerror(errno));
+ pa_log_warn("kill(%u) failed: %s", pid, pa_cstrerror(errno));
pa_module_unload_request(m);
@@ -74,9 +74,3 @@ finish:
return ret;
}
-
-void pa__done(pa_core *c, pa_module*m) {
- assert(c && m);
-}
-
-
diff --git a/src/modules/module-esound-sink.c b/src/modules/module-esound-sink.c
index 26638d9d..8b46637e 100644
--- a/src/modules/module-esound-sink.c
+++ b/src/modules/module-esound-sink.c
@@ -28,14 +28,23 @@
#include <stdlib.h>
#include <sys/stat.h>
#include <stdio.h>
-#include <assert.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>
+#include <poll.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <sys/ioctl.h>
+
+#ifdef HAVE_LINUX_SOCKIOS_H
+#include <linux/sockios.h>
+#endif
#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
#include <pulsecore/core-error.h>
#include <pulsecore/iochannel.h>
@@ -47,27 +56,37 @@
#include <pulsecore/socket-client.h>
#include <pulsecore/esound.h>
#include <pulsecore/authkey.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/time-smoother.h>
+#include <pulsecore/rtclock.h>
+#include <pulsecore/socket-util.h>
#include "module-esound-sink-symdef.h"
PA_MODULE_AUTHOR("Lennart Poettering")
PA_MODULE_DESCRIPTION("ESOUND Sink")
PA_MODULE_VERSION(PACKAGE_VERSION)
-PA_MODULE_USAGE("sink_name=<name for the sink> server=<address> cookie=<filename> format=<sample format> channels=<number of channels> rate=<sample rate>")
+PA_MODULE_USAGE(
+ "sink_name=<name for the sink> "
+ "server=<address> cookie=<filename> "
+ "format=<sample format> "
+ "channels=<number of channels> "
+ "rate=<sample rate>")
-#define DEFAULT_SINK_NAME "esound_output"
+#define DEFAULT_SINK_NAME "esound_out"
struct userdata {
pa_core *core;
-
+ pa_module *module;
pa_sink *sink;
- pa_iochannel *io;
- pa_socket_client *client;
- pa_defer_event *defer_event;
+ pa_thread_mq thread_mq;
+ pa_rtpoll *rtpoll;
+ pa_rtpoll_item *rtpoll_item;
+ pa_thread *thread;
pa_memchunk memchunk;
- pa_module *module;
void *write_data;
size_t write_length, write_index;
@@ -75,12 +94,28 @@ struct userdata {
void *read_data;
size_t read_length, read_index;
- enum { STATE_AUTH, STATE_LATENCY, STATE_RUNNING, STATE_DEAD } state;
+ enum {
+ STATE_AUTH,
+ STATE_LATENCY,
+ STATE_PREPARE,
+ STATE_RUNNING,
+ STATE_DEAD
+ } state;
pa_usec_t latency;
esd_format_t format;
int32_t rate;
+
+ pa_smoother *smoother;
+ int fd;
+
+ int64_t offset;
+
+ pa_iochannel *io;
+ pa_socket_client *client;
+
+ size_t block_size;
};
static const char* const valid_modargs[] = {
@@ -93,42 +128,211 @@ static const char* const valid_modargs[] = {
NULL
};
-static void cancel(struct userdata *u) {
- assert(u);
+enum {
+ SINK_MESSAGE_PASS_SOCKET = PA_SINK_MESSAGE_MAX
+};
- u->state = STATE_DEAD;
+static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+ struct userdata *u = PA_SINK(o)->userdata;
- if (u->io) {
- pa_iochannel_free(u->io);
- u->io = NULL;
- }
+ switch (code) {
- if (u->defer_event) {
- u->core->mainloop->defer_free(u->defer_event);
- u->defer_event = NULL;
- }
+ case PA_SINK_MESSAGE_SET_STATE:
- if (u->sink) {
- pa_sink_disconnect(u->sink);
- pa_sink_unref(u->sink);
- u->sink = NULL;
+ switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
+
+ case PA_SINK_SUSPENDED:
+ pa_assert(PA_SINK_OPENED(u->sink->thread_info.state));
+
+ pa_smoother_pause(u->smoother, pa_rtclock_usec());
+ break;
+
+ case PA_SINK_IDLE:
+ case PA_SINK_RUNNING:
+
+ if (u->sink->thread_info.state == PA_SINK_SUSPENDED)
+ pa_smoother_resume(u->smoother, pa_rtclock_usec());
+
+ break;
+
+ case PA_SINK_UNLINKED:
+ case PA_SINK_INIT:
+ ;
+ }
+
+ break;
+
+ case PA_SINK_MESSAGE_GET_LATENCY: {
+ pa_usec_t w, r;
+
+ r = pa_smoother_get(u->smoother, pa_rtclock_usec());
+ w = pa_bytes_to_usec(u->offset + u->memchunk.length, &u->sink->sample_spec);
+
+ *((pa_usec_t*) data) = w > r ? w - r : 0;
+ break;
+ }
+
+ case SINK_MESSAGE_PASS_SOCKET: {
+ struct pollfd *pollfd;
+
+ pa_assert(!u->rtpoll_item);
+
+ 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->fd;
+ pollfd->events = pollfd->revents = 0;
+
+ return 0;
+ }
}
- if (u->module) {
- pa_module_unload_request(u->module);
- u->module = NULL;
+ return pa_sink_process_msg(o, code, data, offset, chunk);
+}
+
+static void thread_func(void *userdata) {
+ struct userdata *u = userdata;
+ int write_type = 0;
+
+ pa_assert(u);
+
+ pa_log_debug("Thread starting up");
+
+ pa_thread_mq_install(&u->thread_mq);
+ pa_rtpoll_install(u->rtpoll);
+
+ pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec());
+
+ for (;;) {
+ int ret;
+
+ if (u->rtpoll_item) {
+ struct pollfd *pollfd;
+ pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+
+ /* Render some data and write it to the fifo */
+ if (PA_SINK_OPENED(u->sink->thread_info.state) && pollfd->revents) {
+ pa_usec_t usec;
+ int64_t n;
+
+ for (;;) {
+ ssize_t l;
+ void *p;
+
+ if (u->memchunk.length <= 0)
+ pa_sink_render(u->sink, u->block_size, &u->memchunk);
+
+ pa_assert(u->memchunk.length > 0);
+
+ p = pa_memblock_acquire(u->memchunk.memblock);
+ l = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length, &write_type);
+ pa_memblock_release(u->memchunk.memblock);
+
+ pa_assert(l != 0);
+
+ if (l < 0) {
+
+ if (errno == EINTR)
+ continue;
+ else if (errno == EAGAIN) {
+
+ /* OK, we filled all socket buffers up
+ * now. */
+ goto filled_up;
+
+ } else {
+ pa_log("Failed to write data to FIFO: %s", pa_cstrerror(errno));
+ goto fail;
+ }
+
+ } else {
+ u->offset += l;
+
+ u->memchunk.index += l;
+ u->memchunk.length -= l;
+
+ if (u->memchunk.length <= 0) {
+ pa_memblock_unref(u->memchunk.memblock);
+ pa_memchunk_reset(&u->memchunk);
+ }
+
+ pollfd->revents = 0;
+
+ if (u->memchunk.length > 0)
+
+ /* OK, we wrote less that we asked for,
+ * hence we can assume that the socket
+ * buffers are full now */
+ goto filled_up;
+ }
+ }
+
+ filled_up:
+
+ /* At this spot we know that the socket buffers are
+ * fully filled up. This is the best time to estimate
+ * the playback position of the server */
+
+ n = u->offset;
+
+#ifdef SIOCOUTQ
+ {
+ int l;
+ if (ioctl(u->fd, SIOCOUTQ, &l) >= 0 && l > 0)
+ n -= l;
+ }
+#endif
+
+ usec = pa_bytes_to_usec(n, &u->sink->sample_spec);
+
+ if (usec > u->latency)
+ usec -= u->latency;
+ else
+ usec = 0;
+
+ pa_smoother_put(u->smoother, pa_rtclock_usec(), usec);
+ }
+
+ /* Hmm, nothing to do. Let's sleep */
+ pollfd->events = PA_SINK_OPENED(u->sink->thread_info.state) ? POLLOUT : 0;
+ }
+
+ if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
+ goto fail;
+
+ if (ret == 0)
+ goto finish;
+
+ if (u->rtpoll_item) {
+ struct pollfd* pollfd;
+
+ pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+
+ if (pollfd->revents & ~POLLOUT) {
+ pa_log("FIFO shutdown.");
+ goto fail;
+ }
+ }
}
+
+fail:
+ /* If this was no regular exit from the loop we have to continue
+ * processing messages until we received PA_MESSAGE_SHUTDOWN */
+ pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+ pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+ pa_log_debug("Thread shutting down");
}
static int do_write(struct userdata *u) {
ssize_t r;
- assert(u);
+ pa_assert(u);
if (!pa_iochannel_is_writable(u->io))
return 0;
if (u->write_data) {
- assert(u->write_index < u->write_length);
+ pa_assert(u->write_index < u->write_length);
if ((r = pa_iochannel_write(u->io, (uint8_t*) u->write_data + u->write_index, u->write_length - u->write_index)) <= 0) {
pa_log("write() failed: %s", pa_cstrerror(errno));
@@ -136,45 +340,44 @@ static int do_write(struct userdata *u) {
}
u->write_index += r;
- assert(u->write_index <= u->write_length);
+ pa_assert(u->write_index <= u->write_length);
if (u->write_index == u->write_length) {
- free(u->write_data);
+ pa_xfree(u->write_data);
u->write_data = NULL;
u->write_index = u->write_length = 0;
}
- } else if (u->state == STATE_RUNNING) {
- pa_module_set_used(u->module, pa_sink_used_by(u->sink));
+ }
- if (!u->memchunk.length)
- if (pa_sink_render(u->sink, 8192, &u->memchunk) < 0)
- return 0;
+ if (!u->write_data && u->state == STATE_PREPARE) {
+ /* OK, we're done with sending all control data we need to, so
+ * let's hand the socket over to the IO thread now */
- assert(u->memchunk.memblock && u->memchunk.length);
+ pa_assert(u->fd < 0);
+ u->fd = pa_iochannel_get_send_fd(u->io);
- if ((r = pa_iochannel_write(u->io, (uint8_t*) u->memchunk.memblock->data + u->memchunk.index, u->memchunk.length)) < 0) {
- pa_log("write() failed: %s", pa_cstrerror(errno));
- return -1;
- }
+ pa_iochannel_set_noclose(u->io, TRUE);
+ pa_iochannel_free(u->io);
+ u->io = NULL;
- u->memchunk.index += r;
- u->memchunk.length -= r;
+ pa_make_tcp_socket_low_delay(u->fd);
- if (u->memchunk.length <= 0) {
- pa_memblock_unref(u->memchunk.memblock);
- u->memchunk.memblock = NULL;
- }
+ pa_log_info("Connection authenticated, handing fd to IO thread...");
+
+ pa_asyncmsgq_post(u->thread_mq.inq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_PASS_SOCKET, NULL, 0, NULL, NULL);
+ u->state = STATE_RUNNING;
}
return 0;
}
static int handle_response(struct userdata *u) {
- assert(u);
+ pa_assert(u);
switch (u->state) {
+
case STATE_AUTH:
- assert(u->read_length == sizeof(int32_t));
+ pa_assert(u->read_length == sizeof(int32_t));
/* Process auth data */
if (!*(int32_t*) u->read_data) {
@@ -183,14 +386,14 @@ static int handle_response(struct userdata *u) {
}
/* Request latency data */
- assert(!u->write_data);
+ pa_assert(!u->write_data);
*(int32_t*) (u->write_data = pa_xmalloc(u->write_length = sizeof(int32_t))) = ESD_PROTO_LATENCY;
u->write_index = 0;
u->state = STATE_LATENCY;
/* Space for next response */
- assert(u->read_length >= sizeof(int32_t));
+ pa_assert(u->read_length >= sizeof(int32_t));
u->read_index = 0;
u->read_length = sizeof(int32_t);
@@ -198,17 +401,17 @@ static int handle_response(struct userdata *u) {
case STATE_LATENCY: {
int32_t *p;
- assert(u->read_length == sizeof(int32_t));
+ pa_assert(u->read_length == sizeof(int32_t));
/* Process latency info */
u->latency = (pa_usec_t) ((double) (*(int32_t*) u->read_data) * 1000000 / 44100);
if (u->latency > 10000000) {
- pa_log("WARNING! Invalid latency information received from server");
+ pa_log_warn("Invalid latency information received from server");
u->latency = 0;
}
/* Create stream */
- assert(!u->write_data);
+ pa_assert(!u->write_data);
p = u->write_data = pa_xmalloc0(u->write_length = sizeof(int32_t)*3+ESD_NAME_MAX);
*(p++) = ESD_PROTO_STREAM_PLAY;
*(p++) = u->format;
@@ -216,7 +419,7 @@ static int handle_response(struct userdata *u) {
pa_strlcpy((char*) p, "PulseAudio Tunnel", ESD_NAME_MAX);
u->write_index = 0;
- u->state = STATE_RUNNING;
+ u->state = STATE_PREPARE;
/* Don't read any further */
pa_xfree(u->read_data);
@@ -227,14 +430,14 @@ static int handle_response(struct userdata *u) {
}
default:
- abort();
+ pa_assert_not_reached();
}
return 0;
}
static int do_read(struct userdata *u) {
- assert(u);
+ pa_assert(u);
if (!pa_iochannel_is_readable(u->io))
return 0;
@@ -245,16 +448,15 @@ static int do_read(struct userdata *u) {
if (!u->read_data)
return 0;
- assert(u->read_index < u->read_length);
+ pa_assert(u->read_index < u->read_length);
if ((r = pa_iochannel_read(u->io, (uint8_t*) u->read_data + u->read_index, u->read_length - u->read_index)) <= 0) {
pa_log("read() failed: %s", r < 0 ? pa_cstrerror(errno) : "EOF");
- cancel(u);
return -1;
}
u->read_index += r;
- assert(u->read_index <= u->read_length);
+ pa_assert(u->read_index <= u->read_length);
if (u->read_index == u->read_length)
return handle_response(u);
@@ -263,42 +465,19 @@ static int do_read(struct userdata *u) {
return 0;
}
-static void do_work(struct userdata *u) {
- assert(u);
-
- u->core->mainloop->defer_enable(u->defer_event, 0);
-
- if (do_read(u) < 0 || do_write(u) < 0)
- cancel(u);
-}
-
-static void notify_cb(pa_sink*s) {
- struct userdata *u = s->userdata;
- assert(s && u);
-
- if (pa_iochannel_is_writable(u->io))
- u->core->mainloop->defer_enable(u->defer_event, 1);
-}
-
-static pa_usec_t get_latency_cb(pa_sink *s) {
- struct userdata *u = s->userdata;
- assert(s && u);
+static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void*userdata) {
+ struct userdata *u = userdata;
+ pa_assert(u);
- return
- u->latency +
- (u->memchunk.memblock ? pa_bytes_to_usec(u->memchunk.length, &s->sample_spec) : 0);
-}
+ if (do_read(u) < 0 || do_write(u) < 0) {
-static void defer_callback(PA_GCC_UNUSED pa_mainloop_api *m, PA_GCC_UNUSED pa_defer_event*e, void *userdata) {
- struct userdata *u = userdata;
- assert(u);
- do_work(u);
-}
+ if (u->io) {
+ pa_iochannel_free(u->io);
+ u->io = NULL;
+ }
-static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void*userdata) {
- struct userdata *u = userdata;
- assert(u);
- do_work(u);
+ pa_module_unload_request(u->module);
+ }
}
static void on_connection(PA_GCC_UNUSED pa_socket_client *c, pa_iochannel*io, void *userdata) {
@@ -308,30 +487,34 @@ static void on_connection(PA_GCC_UNUSED pa_socket_client *c, pa_iochannel*io, vo
u->client = NULL;
if (!io) {
- pa_log("connection failed: %s", pa_cstrerror(errno));
- cancel(u);
+ pa_log("Connection failed: %s", pa_cstrerror(errno));
+ pa_module_unload_request(u->module);
return;
}
+ pa_assert(!u->io);
u->io = io;
pa_iochannel_set_callback(u->io, io_callback, u);
+
+ pa_log_info("Connection established, authenticating ...");
}
-int pa__init(pa_core *c, pa_module*m) {
+int pa__init(pa_module*m) {
struct userdata *u = NULL;
const char *p;
pa_sample_spec ss;
pa_modargs *ma = NULL;
char *t;
+ const char *espeaker;
- assert(c && m);
+ pa_assert(m);
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
pa_log("failed to parse module arguments");
goto fail;
}
- ss = c->default_sample_spec;
+ ss = m->core->default_sample_spec;
if (pa_modargs_get_sample_spec(ma, &ss) < 0) {
pa_log("invalid sample format specification");
goto fail;
@@ -343,37 +526,62 @@ int pa__init(pa_core *c, pa_module*m) {
goto fail;
}
- u = pa_xmalloc0(sizeof(struct userdata));
- u->core = c;
+ u = pa_xnew0(struct userdata, 1);
+ u->core = m->core;
u->module = m;
m->userdata = u;
+ u->fd = -1;
+ u->smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC*2, TRUE);
+ pa_memchunk_reset(&u->memchunk);
+ u->offset = 0;
+
+ pa_thread_mq_init(&u->thread_mq, m->core->mainloop);
+ u->rtpoll = pa_rtpoll_new();
+ pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq);
+ u->rtpoll_item = NULL;
+
u->format =
(ss.format == PA_SAMPLE_U8 ? ESD_BITS8 : ESD_BITS16) |
(ss.channels == 2 ? ESD_STEREO : ESD_MONO);
u->rate = ss.rate;
- u->sink = NULL;
- u->client = NULL;
- u->io = NULL;
+ u->block_size = pa_usec_to_bytes(PA_USEC_PER_SEC/20, &ss);
+
u->read_data = u->write_data = NULL;
u->read_index = u->write_index = u->read_length = u->write_length = 0;
+
u->state = STATE_AUTH;
u->latency = 0;
- if (!(u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, NULL))) {
+ if (!(u->sink = pa_sink_new(m->core, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, NULL))) {
pa_log("failed to create sink.");
goto fail;
}
- if (!(u->client = pa_socket_client_new_string(u->core->mainloop, p = pa_modargs_get_value(ma, "server", ESD_UNIX_SOCKET_NAME), ESD_DEFAULT_PORT))) {
- pa_log("failed to connect to server.");
+ u->sink->parent.process_msg = sink_process_msg;
+ u->sink->userdata = u;
+ u->sink->flags = PA_SINK_LATENCY;
+
+ pa_sink_set_module(u->sink, m);
+ pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
+ pa_sink_set_rtpoll(u->sink, u->rtpoll);
+
+ if (!(espeaker = getenv("ESPEAKER")))
+ espeaker = ESD_UNIX_SOCKET_NAME;
+
+ if (!(u->client = pa_socket_client_new_string(u->core->mainloop, p = pa_modargs_get_value(ma, "server", espeaker), ESD_DEFAULT_PORT))) {
+ pa_log("Failed to connect to server.");
goto fail;
}
+
+ pa_sink_set_description(u->sink, t = pa_sprintf_malloc("Esound sink '%s'", p));
+ pa_xfree(t);
+
pa_socket_client_set_callback(u->client, on_connection, u);
/* Prepare the initial request */
u->write_data = pa_xmalloc(u->write_length = ESD_KEY_LEN + sizeof(int32_t));
if (pa_authkey_load_auto(pa_modargs_get_value(ma, "cookie", ".esd_auth"), u->write_data, ESD_KEY_LEN) < 0) {
- pa_log("failed to load cookie");
+ pa_log("Failed to load cookie");
goto fail;
}
*(int32_t*) ((uint8_t*) u->write_data + ESD_KEY_LEN) = ESD_ENDIAN_KEY;
@@ -381,19 +589,12 @@ int pa__init(pa_core *c, pa_module*m) {
/* Reserve space for the response */
u->read_data = pa_xmalloc(u->read_length = sizeof(int32_t));
- u->sink->notify = notify_cb;
- u->sink->get_latency = get_latency_cb;
- u->sink->userdata = u;
- pa_sink_set_owner(u->sink, m);
- pa_sink_set_description(u->sink, t = pa_sprintf_malloc("Esound sink '%s'", p));
- pa_xfree(t);
-
- u->memchunk.memblock = NULL;
- u->memchunk.length = 0;
-
- u->defer_event = c->mainloop->defer_new(c->mainloop, defer_callback, u);
- c->mainloop->defer_enable(u->defer_event, 0);
+ if (!(u->thread = pa_thread_new(thread_func, u))) {
+ pa_log("Failed to create thread.");
+ goto fail;
+ }
+ pa_sink_put(u->sink);
pa_modargs_free(ma);
@@ -403,20 +604,39 @@ fail:
if (ma)
pa_modargs_free(ma);
- pa__done(c, m);
+ pa__done(m);
return -1;
}
-void pa__done(pa_core *c, pa_module*m) {
+void pa__done(pa_module*m) {
struct userdata *u;
- assert(c && m);
+ pa_assert(m);
if (!(u = m->userdata))
return;
- u->module = NULL;
- cancel(u);
+ if (u->sink)
+ pa_sink_unlink(u->sink);
+
+ if (u->thread) {
+ pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+ pa_thread_free(u->thread);
+ }
+
+ pa_thread_mq_done(&u->thread_mq);
+
+ if (u->sink)
+ pa_sink_unref(u->sink);
+
+ if (u->io)
+ pa_iochannel_free(u->io);
+
+ if (u->rtpoll_item)
+ pa_rtpoll_item_free(u->rtpoll_item);
+
+ if (u->rtpoll)
+ pa_rtpoll_free(u->rtpoll);
if (u->memchunk.memblock)
pa_memblock_unref(u->memchunk.memblock);
@@ -427,8 +647,11 @@ void pa__done(pa_core *c, pa_module*m) {
pa_xfree(u->read_data);
pa_xfree(u->write_data);
- pa_xfree(u);
-}
-
+ if (u->smoother)
+ pa_smoother_free(u->smoother);
+ if (u->fd >= 0)
+ pa_close(u->fd);
+ pa_xfree(u);
+}
diff --git a/src/modules/module-hal-detect.c b/src/modules/module-hal-detect.c
index 1f48a452..a8ca7df3 100644
--- a/src/modules/module-hal-detect.c
+++ b/src/modules/module-hal-detect.c
@@ -1,25 +1,25 @@
/* $Id$ */
/***
- This file is part of PulseAudio.
+ This file is part of PulseAudio.
- Copyright 2006 Lennart Poettering
- Copyright 2006 Shams E. King
+ Copyright 2006 Lennart Poettering
+ Copyright 2006 Shams E. King
- 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 of the License,
- or (at your option) any later version.
+ 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 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.
+ 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.
+ 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
@@ -27,7 +27,6 @@
#endif
#include <stdio.h>
-#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
@@ -45,6 +44,9 @@
#include <pulsecore/hashmap.h>
#include <pulsecore/idxset.h>
#include <pulsecore/core-util.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/core-scache.h>
+#include <pulsecore/modargs.h>
#include <hal/libhal.h>
@@ -54,40 +56,27 @@
PA_MODULE_AUTHOR("Shahms King")
PA_MODULE_DESCRIPTION("Detect available audio hardware and load matching drivers")
PA_MODULE_VERSION(PACKAGE_VERSION)
-
-typedef enum {
-#ifdef HAVE_ALSA
- CAP_ALSA,
-#endif
-#ifdef HAVE_OSS
- CAP_OSS,
-#endif
- CAP_MAX
-} capability_t;
-
-static const char* const capabilities[CAP_MAX] = {
-#ifdef HAVE_ALSA
- [CAP_ALSA] = "alsa",
-#endif
-#ifdef HAVE_OSS
- [CAP_OSS] = "oss",
+#if defined(HAVE_ALSA) && defined(HAVE_OSS)
+PA_MODULE_USAGE("api=<alsa or oss>")
+#elif defined(HAVE_ALSA)
+PA_MODULE_USAGE("api=<alsa>")
+#elif defined(HAVE_OSS)
+PA_MODULE_USAGE("api=<oss>")
#endif
-};
struct device {
uint32_t index;
char *udi;
+ char *sink_name, *source_name;
+ int acl_race_fix;
};
struct userdata {
pa_core *core;
- LibHalContext *ctx;
- capability_t capability;
- pa_dbus_connection *conn;
+ LibHalContext *context;
+ pa_dbus_connection *connection;
pa_hashmap *devices;
-#if defined(HAVE_ALSA) && defined(HAVE_OSS)
- int use_oss;
-#endif
+ const char *capability;
};
struct timerdata {
@@ -95,23 +84,30 @@ struct timerdata {
char *udi;
};
-static const char* get_capability_name(capability_t cap) {
- if (cap >= CAP_MAX)
- return NULL;
- return capabilities[cap];
-}
+#define CAPABILITY_ALSA "alsa"
+#define CAPABILITY_OSS "oss"
+
+static const char* const valid_modargs[] = {
+ "api",
+ NULL
+};
static void hal_device_free(struct device* d) {
+ pa_assert(d);
+
pa_xfree(d->udi);
+ pa_xfree(d->sink_name);
+ pa_xfree(d->source_name);
pa_xfree(d);
}
static void hal_device_free_cb(void *d, PA_GCC_UNUSED void *data) {
- hal_device_free((struct device*) d);
+ hal_device_free(d);
}
static const char *strip_udi(const char *udi) {
const char *slash;
+
if ((slash = strrchr(udi, '/')))
return slash+1;
@@ -119,6 +115,7 @@ static const char *strip_udi(const char *udi) {
}
#ifdef HAVE_ALSA
+
typedef enum {
ALSA_TYPE_SINK,
ALSA_TYPE_SOURCE,
@@ -126,234 +123,297 @@ typedef enum {
ALSA_TYPE_MAX
} alsa_type_t;
-static alsa_type_t hal_device_get_alsa_type(LibHalContext *ctx, const char *udi,
- DBusError *error)
-{
+static alsa_type_t hal_alsa_device_get_type(LibHalContext *context, const char *udi, DBusError *error) {
char *type;
alsa_type_t t;
- type = libhal_device_get_property_string(ctx, udi, "alsa.type", error);
- if (!type || dbus_error_is_set(error))
- return FALSE;
+ if (!(type = libhal_device_get_property_string(context, udi, "alsa.type", error)))
+ return ALSA_TYPE_OTHER;
- if (!strcmp(type, "playback")) {
+ if (!strcmp(type, "playback"))
t = ALSA_TYPE_SINK;
- } else if (!strcmp(type, "capture")) {
+ else if (!strcmp(type, "capture"))
t = ALSA_TYPE_SOURCE;
- } else {
+ else
t = ALSA_TYPE_OTHER;
- }
+
libhal_free_string(type);
return t;
}
-static int hal_device_get_alsa_card(LibHalContext *ctx, const char *udi,
- DBusError *error)
-{
- return libhal_device_get_property_int(ctx, udi, "alsa.card", error);
-}
+static int hal_alsa_device_is_modem(LibHalContext *context, const char *udi, DBusError *error) {
+ char *class;
+ int r;
+
+ if (!(class = libhal_device_get_property_string(context, udi, "alsa.pcm_class", error)))
+ return 0;
+
+ r = strcmp(class, "modem") == 0;
+ pa_xfree(class);
-static int hal_device_get_alsa_device(LibHalContext *ctx, const char *udi,
- DBusError *error)
-{
- return libhal_device_get_property_int(ctx, udi, "alsa.device", error);
+ return r;
}
-static pa_module* hal_device_load_alsa(struct userdata *u, const char *udi,
- DBusError *error)
-{
- char args[128];
+static pa_module* hal_device_load_alsa(struct userdata *u, const char *udi, char **sink_name, char **source_name) {
+ char *args;
alsa_type_t type;
int device, card;
const char *module_name;
+ DBusError error;
+ pa_module *m;
- type = hal_device_get_alsa_type(u->ctx, udi, error);
- if (dbus_error_is_set(error) || type == ALSA_TYPE_OTHER)
- return NULL;
+ dbus_error_init(&error);
- device = hal_device_get_alsa_device(u->ctx, udi, error);
- if (dbus_error_is_set(error) || device != 0)
- return NULL;
+ pa_assert(u);
+ pa_assert(sink_name);
+ pa_assert(source_name);
- card = hal_device_get_alsa_card(u->ctx, udi, error);
- if (dbus_error_is_set(error))
- return NULL;
+ *sink_name = *source_name = NULL;
+
+ type = hal_alsa_device_get_type(u->context, udi, &error);
+ if (dbus_error_is_set(&error) || type == ALSA_TYPE_OTHER)
+ goto fail;
+
+ device = libhal_device_get_property_int(u->context, udi, "alsa.device", &error);
+ if (dbus_error_is_set(&error) || device != 0)
+ goto fail;
+
+ card = libhal_device_get_property_int(u->context, udi, "alsa.card", &error);
+ if (dbus_error_is_set(&error))
+ goto fail;
+
+ if (hal_alsa_device_is_modem(u->context, udi, &error))
+ goto fail;
if (type == ALSA_TYPE_SINK) {
+ *sink_name = pa_sprintf_malloc("alsa_output.%s", strip_udi(udi));
+
module_name = "module-alsa-sink";
- snprintf(args, sizeof(args), "device=hw:%u sink_name=alsa_output.%s", card, strip_udi(udi));
+ args = pa_sprintf_malloc("device=hw:%u sink_name=%s", card, *sink_name);
} else {
+ *source_name = pa_sprintf_malloc("alsa_input.%s", strip_udi(udi));
+
module_name = "module-alsa-source";
- snprintf(args, sizeof(args), "device=hw:%u source_name=alsa_input.%s", card, strip_udi(udi));
+ args = pa_sprintf_malloc("device=hw:%u source_name=%s", card, *source_name);
}
pa_log_debug("Loading %s with arguments '%s'", module_name, args);
- return pa_module_load(u->core, module_name, args);
+ m = pa_module_load(u->core, module_name, args);
+
+ pa_xfree(args);
+
+ if (!m) {
+ pa_xfree(*sink_name);
+ pa_xfree(*source_name);
+ *sink_name = *source_name = NULL;
+ }
+
+ return m;
+
+fail:
+ if (dbus_error_is_set(&error)) {
+ pa_log_error("D-Bus error while parsing ALSA data: %s: %s", error.name, error.message);
+ dbus_error_free(&error);
+ }
+
+ return NULL;
}
#endif
#ifdef HAVE_OSS
-static dbus_bool_t hal_device_is_oss_pcm(LibHalContext *ctx, const char *udi,
- DBusError *error)
-{
- dbus_bool_t rv = FALSE;
- char* type, *device_file = NULL;
+
+static int hal_oss_device_is_pcm(LibHalContext *context, const char *udi, DBusError *error) {
+ char *class = NULL, *dev = NULL, *e;
int device;
+ int r = 0;
- type = libhal_device_get_property_string(ctx, udi, "oss.type", error);
- if (!type || dbus_error_is_set(error))
- return FALSE;
+ class = libhal_device_get_property_string(context, udi, "oss.type", error);
+ if (dbus_error_is_set(error) || !class)
+ goto finish;
- if (!strcmp(type, "pcm")) {
- char *e;
+ if (strcmp(class, "pcm"))
+ goto finish;
- device = libhal_device_get_property_int(ctx, udi, "oss.device", error);
- if (dbus_error_is_set(error) || device != 0)
- goto exit;
+ dev = libhal_device_get_property_string(context, udi, "oss.device_file", error);
+ if (dbus_error_is_set(error) || !dev)
+ goto finish;
- device_file = libhal_device_get_property_string(ctx, udi, "oss.device_file",
- error);
- if (!device_file || dbus_error_is_set(error))
- goto exit;
+ if ((e = strrchr(dev, '/')))
+ if (pa_startswith(e + 1, "audio"))
+ goto finish;
- /* hack to ignore /dev/audio style devices */
- if ((e = strrchr(device_file, '/')))
- rv = !pa_startswith(e + 1, "audio");
- }
+ device = libhal_device_get_property_int(context, udi, "oss.device", error);
+ if (dbus_error_is_set(error) || device != 0)
+ goto finish;
-exit:
- libhal_free_string(type);
- libhal_free_string(device_file);
- return rv;
+ r = 1;
+
+finish:
+
+ libhal_free_string(class);
+ libhal_free_string(dev);
+
+ return r;
}
-static pa_module* hal_device_load_oss(struct userdata *u, const char *udi,
- DBusError *error)
-{
- char args[256];
+static pa_module* hal_device_load_oss(struct userdata *u, const char *udi, char **sink_name, char **source_name) {
+ char* args;
char* device;
+ DBusError error;
+ pa_module *m;
- if (!hal_device_is_oss_pcm(u->ctx, udi, error) || dbus_error_is_set(error))
- return NULL;
+ dbus_error_init(&error);
- device = libhal_device_get_property_string(u->ctx, udi, "oss.device_file",
- error);
- if (!device || dbus_error_is_set(error))
- return NULL;
+ pa_assert(u);
+ pa_assert(sink_name);
+ pa_assert(source_name);
+
+ *sink_name = *source_name = NULL;
- snprintf(args, sizeof(args), "device=%s sink_name=oss_output.%s source_name=oss_input.%s", device, strip_udi(udi), strip_udi(udi));
+ if (!hal_oss_device_is_pcm(u->context, udi, &error) || dbus_error_is_set(&error))
+ goto fail;
+
+ device = libhal_device_get_property_string(u->context, udi, "oss.device_file", &error);
+ if (!device || dbus_error_is_set(&error))
+ goto fail;
+
+ *sink_name = pa_sprintf_malloc("oss_output.%s", strip_udi(udi));
+ *source_name = pa_sprintf_malloc("oss_input.%s", strip_udi(udi));
+
+ args = pa_sprintf_malloc("device=%s sink_name=%s source_name=%s", device, *sink_name, *source_name);
libhal_free_string(device);
pa_log_debug("Loading module-oss with arguments '%s'", args);
+ m = pa_module_load(u->core, "module-oss", args);
+ pa_xfree(args);
+
+ if (!m) {
+ pa_xfree(*sink_name);
+ pa_xfree(*source_name);
+ *sink_name = *source_name = NULL;
+ }
+
+ return m;
+
+fail:
+ if (dbus_error_is_set(&error)) {
+ pa_log_error("D-Bus error while parsing OSS data: %s: %s", error.name, error.message);
+ dbus_error_free(&error);
+ }
- return pa_module_load(u->core, "module-oss", args);
+ return NULL;
}
#endif
-static dbus_bool_t hal_device_add(struct userdata *u, const char *udi,
- DBusError *error)
-{
+static struct device* hal_device_add(struct userdata *u, const char *udi) {
pa_module* m = NULL;
struct device *d;
+ char *sink_name = NULL, *source_name = NULL;
+
+ pa_assert(u);
+ pa_assert(u->capability);
+ pa_assert(!pa_hashmap_get(u->devices, udi));
- switch(u->capability) {
#ifdef HAVE_ALSA
- case CAP_ALSA:
- m = hal_device_load_alsa(u, udi, error);
- break;
+ if (strcmp(u->capability, CAPABILITY_ALSA) == 0)
+ m = hal_device_load_alsa(u, udi, &sink_name, &source_name);
#endif
#ifdef HAVE_OSS
- case CAP_OSS:
-#ifdef HAVE_ALSA
- if (u->use_oss)
-#endif
- m = hal_device_load_oss(u, udi, error);
- break;
+ if (strcmp(u->capability, CAPABILITY_OSS) == 0)
+ m = hal_device_load_oss(u, udi, &sink_name, &source_name);
#endif
- default:
- assert(FALSE); /* never reached */
- break;
- }
- if (!m || dbus_error_is_set(error))
- return FALSE;
+ if (!m)
+ return NULL;
d = pa_xnew(struct device, 1);
+ d->acl_race_fix = 0;
d->udi = pa_xstrdup(udi);
d->index = m->index;
-
+ d->sink_name = sink_name;
+ d->source_name = source_name;
pa_hashmap_put(u->devices, d->udi, d);
- return TRUE;
+ return d;
}
-static int hal_device_add_all(struct userdata *u, capability_t capability)
-{
+static int hal_device_add_all(struct userdata *u, const char *capability) {
DBusError error;
- int i,n,count;
- dbus_bool_t r;
+ int i, n, count = 0;
char** udis;
- const char* cap = get_capability_name(capability);
- assert(capability < CAP_MAX);
+ pa_assert(u);
- pa_log_info("Trying capability %u (%s)", capability, cap);
dbus_error_init(&error);
- udis = libhal_find_device_by_capability(u->ctx, cap, &n, &error);
+
+ if (u->capability && strcmp(u->capability, capability) != 0)
+ return 0;
+
+ pa_log_info("Trying capability %s", capability);
+
+ udis = libhal_find_device_by_capability(u->context, capability, &n, &error);
if (dbus_error_is_set(&error)) {
- pa_log_error("Error finding devices: %s: %s", error.name,
- error.message);
+ pa_log_error("Error finding devices: %s: %s", error.name, error.message);
dbus_error_free(&error);
return -1;
}
- count = 0;
- u->capability = capability;
- for (i = 0; i < n; ++i) {
- r = hal_device_add(u, udis[i], &error);
- if (dbus_error_is_set(&error)) {
- pa_log_error("Error adding device: %s: %s", error.name,
- error.message);
- dbus_error_free(&error);
- count = -1;
- break;
+
+ if (n > 0) {
+ u->capability = capability;
+
+ for (i = 0; i < n; i++) {
+ struct device *d;
+
+ if (!(d = hal_device_add(u, udis[i])))
+ pa_log_debug("Not loaded device %s", udis[i]);
+ else {
+ if (d->sink_name)
+ pa_scache_play_item_by_name(u->core, "pulse-coldplug", d->sink_name, PA_VOLUME_NORM, 0);
+ count++;
+ }
}
- if (r)
- ++count;
}
libhal_free_string_array(udis);
return count;
}
-static dbus_bool_t device_has_capability(LibHalContext *ctx, const char *udi,
- const char* cap, DBusError *error)
-{
+static dbus_bool_t device_has_capability(LibHalContext *context, const char *udi, const char* cap, DBusError *error){
dbus_bool_t has_prop;
- has_prop = libhal_device_property_exists(ctx, udi, "info.capabilities",
- error);
+
+ has_prop = libhal_device_property_exists(context, udi, "info.capabilities", error);
if (!has_prop || dbus_error_is_set(error))
return FALSE;
- return libhal_device_query_capability(ctx, udi, cap, error);
+ return libhal_device_query_capability(context, udi, cap, error);
}
-static void device_added_time_cb(pa_mainloop_api *ea, pa_time_event *ev,
- const struct timeval *tv, void *userdata)
-{
+static void device_added_time_cb(pa_mainloop_api *ea, pa_time_event *ev, const struct timeval *tv, void *userdata) {
DBusError error;
- struct timerdata *td = (struct timerdata*) userdata;
+ struct timerdata *td = userdata;
dbus_error_init(&error);
- if (libhal_device_exists(td->u->ctx, td->udi, &error))
- hal_device_add(td->u, td->udi, &error);
- if (dbus_error_is_set(&error)) {
- pa_log_error("Error adding device: %s: %s", error.name,
- error.message);
- dbus_error_free(&error);
+ if (!pa_hashmap_get(td->u->devices, td->udi)) {
+ int b;
+ struct device *d;
+
+ b = libhal_device_exists(td->u->context, td->udi, &error);
+
+ if (dbus_error_is_set(&error)) {
+ pa_log_error("Error adding device: %s: %s", error.name, error.message);
+ dbus_error_free(&error);
+ } else if (b) {
+ if (!(d = hal_device_add(td->u, td->udi)))
+ pa_log_debug("Not loaded device %s", td->udi);
+ else {
+ if (d->sink_name)
+ pa_scache_play_item_by_name(td->u->core, "pulse-hotplug", d->sink_name, PA_VOLUME_NORM, 0);
+ }
+ }
}
pa_xfree(td->udi);
@@ -361,28 +421,68 @@ static void device_added_time_cb(pa_mainloop_api *ea, pa_time_event *ev,
ea->time_free(ev);
}
-static void device_added_cb(LibHalContext *ctx, const char *udi)
-{
+static void device_added_cb(LibHalContext *context, const char *udi) {
DBusError error;
struct timeval tv;
- dbus_bool_t has_cap;
struct timerdata *t;
- struct userdata *u = (struct userdata*) libhal_ctx_get_user_data(ctx);
- const char* cap = get_capability_name(u->capability);
+ struct userdata *u;
+ int good = 0;
+
+ pa_assert_se(u = libhal_ctx_get_user_data(context));
+
+ if (pa_hashmap_get(u->devices, udi))
+ return;
pa_log_debug("HAL Device added: %s", udi);
dbus_error_init(&error);
- has_cap = device_has_capability(ctx, udi, cap, &error);
- if (dbus_error_is_set(&error)) {
- pa_log_error("Error getting capability: %s: %s", error.name,
- error.message);
- dbus_error_free(&error);
- return;
+
+ if (u->capability) {
+
+ good = device_has_capability(context, udi, u->capability, &error);
+
+ if (dbus_error_is_set(&error)) {
+ pa_log_error("Error getting capability: %s: %s", error.name, error.message);
+ dbus_error_free(&error);
+ return;
+ }
+
+ } else {
+
+#ifdef HAVE_ALSA
+ good = device_has_capability(context, udi, CAPABILITY_ALSA, &error);
+
+ if (dbus_error_is_set(&error)) {
+ pa_log_error("Error getting capability: %s: %s", error.name, error.message);
+ dbus_error_free(&error);
+ return;
+ }
+
+ if (good)
+ u->capability = CAPABILITY_ALSA;
+#endif
+#if defined(HAVE_OSS) && defined(HAVE_ALSA)
+ if (!good) {
+#endif
+#ifdef HAS_OSS
+ good = device_has_capability(context, udi, CAPABILITY_OSS, &error);
+
+ if (dbus_error_is_set(&error)) {
+ pa_log_error("Error getting capability: %s: %s", error.name, error.message);
+ dbus_error_free(&error);
+ return;
+ }
+
+ if (good)
+ u->capability = CAPABILITY_OSS;
+
+#endif
+#if defined(HAVE_OSS) && defined(HAVE_ALSA)
+ }
+#endif
}
- /* skip it */
- if (!has_cap)
+ if (!good)
return;
/* actually add the device 1/2 second later */
@@ -392,187 +492,359 @@ static void device_added_cb(LibHalContext *ctx, const char *udi)
pa_gettimeofday(&tv);
pa_timeval_add(&tv, 500000);
- u->core->mainloop->time_new(u->core->mainloop, &tv,
- device_added_time_cb, t);
+ u->core->mainloop->time_new(u->core->mainloop, &tv, device_added_time_cb, t);
}
-static void device_removed_cb(LibHalContext* ctx, const char *udi)
-{
+static void device_removed_cb(LibHalContext* context, const char *udi) {
struct device *d;
- struct userdata *u = (struct userdata*) libhal_ctx_get_user_data(ctx);
+ struct userdata *u;
+
+ pa_assert_se(u = libhal_ctx_get_user_data(context));
pa_log_debug("Device removed: %s", udi);
+
if ((d = pa_hashmap_remove(u->devices, udi))) {
pa_module_unload_by_index(u->core, d->index);
hal_device_free(d);
}
}
-static void new_capability_cb(LibHalContext *ctx, const char *udi,
- const char* capability)
-{
- struct userdata *u = (struct userdata*) libhal_ctx_get_user_data(ctx);
- const char* capname = get_capability_name(u->capability);
+static void new_capability_cb(LibHalContext *context, const char *udi, const char* capability) {
+ struct userdata *u;
+
+ pa_assert_se(u = libhal_ctx_get_user_data(context));
- if (capname && !strcmp(capname, capability)) {
+ if (!u->capability || strcmp(u->capability, capability) == 0)
/* capability we care about, pretend it's a new device */
- device_added_cb(ctx, udi);
- }
+ device_added_cb(context, udi);
}
-static void lost_capability_cb(LibHalContext *ctx, const char *udi,
- const char* capability)
-{
- struct userdata *u = (struct userdata*) libhal_ctx_get_user_data(ctx);
- const char* capname = get_capability_name(u->capability);
+static void lost_capability_cb(LibHalContext *context, const char *udi, const char* capability) {
+ struct userdata *u;
- if (capname && !strcmp(capname, capability)) {
- /* capability we care about, pretend it was removed */
- device_removed_cb(ctx, udi);
- }
-}
+ pa_assert_se(u = libhal_ctx_get_user_data(context));
-#if 0
-static void property_modified_cb(LibHalContext *ctx, const char *udi,
- const char* key,
- dbus_bool_t is_removed,
- dbus_bool_t is_added)
-{
+ if (u->capability && strcmp(u->capability, capability) == 0)
+ /* capability we care about, pretend it was removed */
+ device_removed_cb(context, udi);
}
-#endif
-static void pa_hal_context_free(LibHalContext* hal_ctx)
-{
+static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, void *userdata) {
+ struct userdata*u = userdata;
DBusError error;
+ pa_assert(bus);
+ pa_assert(message);
+ pa_assert(u);
+
dbus_error_init(&error);
- libhal_ctx_shutdown(hal_ctx, &error);
- libhal_ctx_free(hal_ctx);
- if (dbus_error_is_set(&error)) {
- dbus_error_free(&error);
+ pa_log_debug("dbus: interface=%s, path=%s, member=%s\n",
+ dbus_message_get_interface(message),
+ dbus_message_get_path(message),
+ dbus_message_get_member(message));
+
+ if (dbus_message_is_signal(message, "org.freedesktop.Hal.Device.AccessControl", "ACLAdded") ||
+ dbus_message_is_signal(message, "org.freedesktop.Hal.Device.AccessControl", "ACLRemoved")) {
+ uint32_t uid;
+ int suspend = strcmp(dbus_message_get_member(message), "ACLRemoved") == 0;
+
+ if (!dbus_message_get_args(message, &error, DBUS_TYPE_UINT32, &uid, DBUS_TYPE_INVALID) || dbus_error_is_set(&error)) {
+ pa_log_error("Failed to parse ACL message: %s: %s", error.name, error.message);
+ goto finish;
+ }
+
+ if (uid == getuid() || uid == geteuid()) {
+ struct device *d;
+ const char *udi;
+
+ udi = dbus_message_get_path(message);
+
+ if ((d = pa_hashmap_get(u->devices, udi))) {
+ int send_acl_race_fix_message = 0;
+
+ d->acl_race_fix = 0;
+
+ if (d->sink_name) {
+ pa_sink *sink;
+
+ if ((sink = pa_namereg_get(u->core, d->sink_name, PA_NAMEREG_SINK, 0))) {
+ int prev_suspended = pa_sink_get_state(sink) == PA_SINK_SUSPENDED;
+
+ if (prev_suspended && !suspend) {
+ /* resume */
+ if (pa_sink_suspend(sink, 0) >= 0)
+ pa_scache_play_item_by_name(u->core, "pulse-access", d->sink_name, PA_VOLUME_NORM, 0);
+ else
+ d->acl_race_fix = 1;
+
+ } else if (!prev_suspended && suspend) {
+ /* suspend */
+ if (pa_sink_suspend(sink, 1) >= 0)
+ send_acl_race_fix_message = 1;
+ }
+ }
+ }
+
+ if (d->source_name) {
+ pa_source *source;
+
+ if ((source = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_SOURCE, 0))) {
+ int prev_suspended = pa_source_get_state(source) == PA_SOURCE_SUSPENDED;
+
+ if (prev_suspended && !suspend) {
+ /* resume */
+ if (pa_source_suspend(source, 0) < 0)
+ d->acl_race_fix = 1;
+
+ } else if (!prev_suspended && suspend) {
+ /* suspend */
+ if (pa_source_suspend(source, 0) >= 0)
+ send_acl_race_fix_message = 1;
+ }
+ }
+ }
+
+ if (send_acl_race_fix_message) {
+ DBusMessage *msg;
+ msg = dbus_message_new_signal(udi, "org.pulseaudio.Server", "DirtyGiveUpMessage");
+ dbus_connection_send(pa_dbus_connection_get(u->connection), msg, NULL);
+ dbus_message_unref(msg);
+ }
+
+ } else if (!suspend)
+ device_added_cb(u->context, udi);
+ }
+
+ } else if (dbus_message_is_signal(message, "org.pulseaudio.Server", "DirtyGiveUpMessage")) {
+ /* We use this message to avoid a dirty race condition when we
+ get an ACLAdded message before the previously owning PA
+ sever has closed the device. We can remove this as
+ soon as HAL learns frevoke() */
+
+ const char *udi;
+ struct device *d;
+
+ udi = dbus_message_get_path(message);
+
+ if ((d = pa_hashmap_get(u->devices, udi)) && d->acl_race_fix) {
+ pa_log_debug("Got dirty give up message for '%s', trying resume ...", udi);
+
+ d->acl_race_fix = 0;
+
+ if (d->sink_name) {
+ pa_sink *sink;
+
+ if ((sink = pa_namereg_get(u->core, d->sink_name, PA_NAMEREG_SINK, 0))) {
+
+ int prev_suspended = pa_sink_get_state(sink) == PA_SINK_SUSPENDED;
+
+ if (prev_suspended) {
+ /* resume */
+ if (pa_sink_suspend(sink, 0) >= 0)
+ pa_scache_play_item_by_name(u->core, "pulse-access", d->sink_name, PA_VOLUME_NORM, 0);
+ }
+ }
+ }
+
+ if (d->source_name) {
+ pa_source *source;
+
+ if ((source = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_SOURCE, 0))) {
+
+ int prev_suspended = pa_source_get_state(source) == PA_SOURCE_SUSPENDED;
+
+ if (prev_suspended)
+ pa_source_suspend(source, 0);
+ }
+ }
+
+ } else
+ /* Yes, we don't check the UDI for validity, but hopefully HAL will */
+ device_added_cb(u->context, udi);
}
+
+finish:
+ dbus_error_free(&error);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
}
-static void userdata_free(struct userdata *u) {
- pa_hal_context_free(u->ctx);
- /* free the devices with the hashmap */
- pa_hashmap_free(u->devices, hal_device_free_cb, NULL);
- pa_dbus_connection_unref(u->conn);
- pa_xfree(u);
+static void hal_context_free(LibHalContext* hal_context) {
+ DBusError error;
+
+ dbus_error_init(&error);
+
+ libhal_ctx_shutdown(hal_context, &error);
+ libhal_ctx_free(hal_context);
+
+ dbus_error_free(&error);
}
-static LibHalContext* pa_hal_context_new(pa_core* c, DBusConnection *conn)
-{
+static LibHalContext* hal_context_new(pa_core* c, DBusConnection *conn) {
DBusError error;
- LibHalContext *hal_ctx = NULL;
+ LibHalContext *hal_context = NULL;
dbus_error_init(&error);
- if (!(hal_ctx = libhal_ctx_new())) {
+
+ if (!(hal_context = libhal_ctx_new())) {
pa_log_error("libhal_ctx_new() failed");
goto fail;
}
- if (!libhal_ctx_set_dbus_connection(hal_ctx, conn)) {
- pa_log_error("Error establishing DBUS connection: %s: %s",
- error.name, error.message);
+ if (!libhal_ctx_set_dbus_connection(hal_context, conn)) {
+ pa_log_error("Error establishing DBUS connection: %s: %s", error.name, error.message);
goto fail;
}
- if (!libhal_ctx_init(hal_ctx, &error)) {
- pa_log_error("Couldn't connect to hald: %s: %s",
- error.name, error.message);
+ if (!libhal_ctx_init(hal_context, &error)) {
+ pa_log_error("Couldn't connect to hald: %s: %s", error.name, error.message);
goto fail;
}
- return hal_ctx;
+ return hal_context;
fail:
- if (hal_ctx)
- pa_hal_context_free(hal_ctx);
+ if (hal_context)
+ hal_context_free(hal_context);
- if (dbus_error_is_set(&error))
- dbus_error_free(&error);
+ dbus_error_free(&error);
return NULL;
}
-int pa__init(pa_core *c, pa_module*m) {
+int pa__init(pa_module*m) {
DBusError error;
pa_dbus_connection *conn;
struct userdata *u = NULL;
- LibHalContext *hal_ctx = NULL;
+ LibHalContext *hal_context = NULL;
int n = 0;
+ pa_modargs *ma;
+ const char *api;
- assert(c);
- assert(m);
+ pa_assert(m);
dbus_error_init(&error);
- if (!(conn = pa_dbus_bus_get(c, DBUS_BUS_SYSTEM, &error))) {
- pa_log_error("Unable to contact DBUS system bus: %s: %s",
- error.name, error.message);
- dbus_error_free(&error);
- return -1;
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log("Failed to parse module arguments");
+ goto fail;
}
- if (!(hal_ctx = pa_hal_context_new(c, pa_dbus_connection_get(conn)))) {
+ if ((api = pa_modargs_get_value(ma, "api", NULL))) {
+ int good = 0;
+
+#ifdef HAVE_ALSA
+ if (strcmp(api, CAPABILITY_ALSA) == 0) {
+ good = 1;
+ api = CAPABILITY_ALSA;
+ }
+#endif
+#ifdef HAVE_OSS
+ if (strcmp(api, CAPABILITY_OSS) == 0) {
+ good = 1;
+ api = CAPABILITY_OSS;
+ }
+#endif
+
+ if (!good) {
+ pa_log_error("Invalid API specification.");
+ goto fail;
+ }
+ }
+
+ if (!(conn = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &error)) || dbus_error_is_set(&error)) {
+ if (conn)
+ pa_dbus_connection_unref(conn);
+ pa_log_error("Unable to contact DBUS system bus: %s: %s", error.name, error.message);
+ goto fail;
+ }
+
+ if (!(hal_context = hal_context_new(m->core, pa_dbus_connection_get(conn)))) {
/* pa_hal_context_new() logs appropriate errors */
- return -1;
+ pa_dbus_connection_unref(conn);
+ goto fail;
}
u = pa_xnew(struct userdata, 1);
- u->core = c;
- u->ctx = hal_ctx;
- u->conn = conn;
- u->devices = pa_hashmap_new(pa_idxset_string_hash_func,
- pa_idxset_string_compare_func);
- m->userdata = (void*) u;
+ u->core = m->core;
+ u->context = hal_context;
+ u->connection = conn;
+ u->devices = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+ u->capability = api;
+ m->userdata = u;
#ifdef HAVE_ALSA
- n = hal_device_add_all(u, CAP_ALSA);
+ n = hal_device_add_all(u, CAPABILITY_ALSA);
#endif
#if defined(HAVE_ALSA) && defined(HAVE_OSS)
- u->use_oss = 0;
-
- if (n <= 0) {
+ if (n <= 0)
#endif
#ifdef HAVE_OSS
- n += hal_device_add_all(u, CAP_OSS);
+ n += hal_device_add_all(u, CAPABILITY_OSS);
#endif
-#if defined(HAVE_ALSA) && defined(HAVE_OSS)
- /* We found something with OSS, but didn't find anything with
- * ALSA. Then let's use only OSS from now on. */
- if (n > 0)
- u->use_oss = 1;
+ libhal_ctx_set_user_data(hal_context, u);
+ libhal_ctx_set_device_added(hal_context, device_added_cb);
+ libhal_ctx_set_device_removed(hal_context, device_removed_cb);
+ libhal_ctx_set_device_new_capability(hal_context, new_capability_cb);
+ libhal_ctx_set_device_lost_capability(hal_context, lost_capability_cb);
+
+ if (!libhal_device_property_watch_all(hal_context, &error)) {
+ pa_log_error("Error monitoring device list: %s: %s", error.name, error.message);
+ goto fail;
}
-#endif
- libhal_ctx_set_user_data(hal_ctx, u);
- libhal_ctx_set_device_added(hal_ctx, device_added_cb);
- libhal_ctx_set_device_removed(hal_ctx, device_removed_cb);
- libhal_ctx_set_device_new_capability(hal_ctx, new_capability_cb);
- libhal_ctx_set_device_lost_capability(hal_ctx, lost_capability_cb);
- /*libhal_ctx_set_device_property_modified(hal_ctx, property_modified_cb);*/
+ if (!dbus_connection_add_filter(pa_dbus_connection_get(conn), filter_cb, u, NULL)) {
+ pa_log_error("Failed to add filter function");
+ goto fail;
+ }
- dbus_error_init(&error);
- if (!libhal_device_property_watch_all(hal_ctx, &error)) {
- pa_log_error("error monitoring device list: %s: %s",
- error.name, error.message);
- dbus_error_free(&error);
- userdata_free(u);
- return -1;
+ dbus_bus_add_match(pa_dbus_connection_get(conn), "type='signal',sender='org.freedesktop.Hal', interface='org.freedesktop.Hal.Device.AccessControl'", &error);
+ if (dbus_error_is_set(&error)) {
+ pa_log_error("Unable to subscribe to HAL ACL signals: %s: %s", error.name, error.message);
+ goto fail;
+ }
+
+ dbus_bus_add_match(pa_dbus_connection_get(conn), "type='signal',interface='org.pulseaudio.Server'", &error);
+ if (dbus_error_is_set(&error)) {
+ pa_log_error("Unable to subscribe to PulseAudio signals: %s: %s", error.name, error.message);
+ goto fail;
}
- pa_log_info("loaded %i modules.", n);
+ pa_log_info("Loaded %i modules.", n);
+
+ pa_modargs_free(ma);
return 0;
+
+fail:
+ if (ma)
+ pa_modargs_free(ma);
+
+ dbus_error_free(&error);
+ pa__done(m);
+
+ return -1;
}
-void pa__done(PA_GCC_UNUSED pa_core *c, pa_module *m) {
- assert (c && m);
+void pa__done(pa_module *m) {
+ struct userdata *u;
+
+ pa_assert(m);
+
+ if (!(u = m->userdata))
+ return;
+
+ if (u->context)
+ hal_context_free(u->context);
- /* free the user data */
- userdata_free(m->userdata);
+ if (u->devices)
+ pa_hashmap_free(u->devices, hal_device_free_cb, NULL);
+
+ if (u->connection)
+ pa_dbus_connection_unref(u->connection);
+
+ pa_xfree(u);
}
diff --git a/src/modules/module-jack-sink.c b/src/modules/module-jack-sink.c
index a40ebe29..5019d656 100644
--- a/src/modules/module-jack-sink.c
+++ b/src/modules/module-jack-sink.c
@@ -3,7 +3,7 @@
/***
This file is part of PulseAudio.
- Copyright 2006, 2007 Lennart Poettering and Tanu Kaskinen
+ Copyright 2006 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
@@ -25,858 +25,431 @@
#include <config.h>
#endif
-#include <pthread.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <stdio.h>
#include <assert.h>
#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
#include <jack/jack.h>
-#include <jack/ringbuffer.h>
-#include <jack/types.h>
-#include <pulse/mainloop-api.h>
-#include <pulse/sample.h>
-#include <pulse/channelmap.h>
#include <pulse/xmalloc.h>
+#include <pulsecore/core-error.h>
#include <pulsecore/sink.h>
#include <pulsecore/module.h>
-#include <pulsecore/core.h>
-#include <pulsecore/log.h>
#include <pulsecore/core-util.h>
-#include <pulsecore/core-error.h>
-#include <pulsecore/pipe.h>
#include <pulsecore/modargs.h>
-#include <pulsecore/strbuf.h>
+#include <pulsecore/log.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/sample-util.h>
#include "module-jack-sink-symdef.h"
-PA_MODULE_AUTHOR("Lennart Poettering & Tanu Kaskinen")
-PA_MODULE_DESCRIPTION("Jack Sink")
+/* General overview:
+ *
+ * Because JACK has a very unflexible event loop management, which
+ * doesn't allow us to add our own event sources to the event thread
+ * we cannot use the JACK real-time thread for dispatching our PA
+ * work. Instead, we run an additional RT thread which does most of
+ * the PA handling, and have the JACK RT thread request data from it
+ * via pa_asyncmsgq. The cost is an additional context switch which
+ * should hopefully not be that expensive if RT scheduling is
+ * enabled. A better fix would only be possible with additional event
+ * source support in JACK.
+ */
+
+PA_MODULE_AUTHOR("Lennart Poettering")
+PA_MODULE_DESCRIPTION("JACK Sink")
PA_MODULE_VERSION(PACKAGE_VERSION)
PA_MODULE_USAGE(
"sink_name=<name of sink> "
"server_name=<jack server name> "
"client_name=<jack client name> "
"channels=<number of channels> "
- "connect=<connect ports automatically?> "
- "buffersize=<intermediate buffering in frames> "
+ "connect=<connect ports?> "
"channel_map=<channel map>")
#define DEFAULT_SINK_NAME "jack_out"
-#define DEFAULT_CLIENT_NAME "PulseAudio(output)"
-#define DEFAULT_RINGBUFFER_SIZE 4096
-
struct userdata {
+ pa_core *core;
+ pa_module *module;
pa_sink *sink;
unsigned channels;
- unsigned frame_size;
- jack_port_t* j_ports[PA_CHANNELS_MAX];
- jack_client_t *j_client;
+ jack_port_t* port[PA_CHANNELS_MAX];
+ jack_client_t *client;
- jack_nframes_t j_buffersize;
+ void *buffer[PA_CHANNELS_MAX];
- /* For avoiding j_buffersize changes at a wrong moment. */
- pthread_mutex_t buffersize_mutex;
+ pa_thread_mq thread_mq;
+ pa_asyncmsgq *jack_msgq;
+ pa_rtpoll *rtpoll;
+ pa_rtpoll_item *rtpoll_item;
- /* The intermediate store where the pulse side writes to and the jack side
- reads from. */
- jack_ringbuffer_t* ringbuffer;
-
- /* For signaling when there's room in the ringbuffer. */
- pthread_mutex_t cond_mutex;
- pthread_cond_t ringbuffer_cond;
+ pa_thread *thread;
- pthread_t filler_thread; /* Keeps the ringbuffer filled. */
+ jack_nframes_t frames_in_buffer;
+ jack_nframes_t saved_frame_time;
+ pa_bool_t saved_frame_time_valid;
+};
- int ringbuffer_is_full;
- int filler_thread_is_running;
- int quit_requested;
+static const char* const valid_modargs[] = {
+ "sink_name",
+ "server_name",
+ "client_name",
+ "channels",
+ "connect",
+ "channel_map",
+ NULL
+};
- int pipe_fd_type;
- int pipe_fds[2];
- pa_io_event *io_event;
+enum {
+ SINK_MESSAGE_RENDER = PA_SINK_MESSAGE_MAX,
+ SINK_MESSAGE_ON_SHUTDOWN
};
+static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *memchunk) {
+ struct userdata *u = PA_SINK(o)->userdata;
-struct options {
- char* sink_name;
- int sink_name_given;
+ switch (code) {
- char* server_name; /* May be NULL */
- int server_name_given;
+ case SINK_MESSAGE_RENDER:
- char* client_name;
- int client_name_given;
+ /* Handle the request from the JACK thread */
- unsigned channels;
- int channels_given;
+ if (u->sink->thread_info.state == PA_SINK_RUNNING) {
+ pa_memchunk chunk;
+ size_t nbytes;
+ void *p;
- int connect;
- int connect_given;
+ pa_assert(offset > 0);
+ nbytes = offset * pa_frame_size(&u->sink->sample_spec);
- unsigned buffersize;
- int buffersize_given;
+ pa_sink_render_full(u->sink, nbytes, &chunk);
- pa_channel_map map;
- int map_given;
-};
+ p = (uint8_t*) pa_memblock_acquire(chunk.memblock) + chunk.index;
+ pa_deinterleave(p, u->buffer, u->channels, sizeof(float), offset);
+ pa_memblock_release(chunk.memblock);
+ pa_memblock_unref(chunk.memblock);
+ } else {
+ unsigned c;
+ pa_sample_spec ss;
-static const char* const valid_modargs[] = {
- "sink_name",
- "server_name",
- "client_name",
- "channels",
- "connect",
- "buffersize",
- "channel_map",
- NULL
-};
+ /* Humm, we're not RUNNING, hence let's write some silence */
+ ss = u->sink->sample_spec;
+ ss.channels = 1;
-/* Initialization functions. */
-static int parse_options(struct options* o, const char* argument);
-static void set_default_channels(pa_module* self, struct options* o);
-static int create_sink(pa_module* self, struct options *o);
-static void connect_ports(pa_module* self);
-static int start_filling_ringbuffer(pa_module* self);
+ for (c = 0; c < u->channels; c++)
+ pa_silence_memory(u->buffer[c], offset * pa_sample_size(&ss), &ss);
+ }
-/* Various callbacks. */
-static void jack_error_func(const char* t);
-static pa_usec_t sink_get_latency_cb(pa_sink* s);
-static int jack_process(jack_nframes_t nframes, void* arg);
-static int jack_blocksize_cb(jack_nframes_t nframes, void* arg);
-static void jack_shutdown(void* arg);
-static void io_event_cb(pa_mainloop_api* m, pa_io_event* e, int fd,
- pa_io_event_flags_t flags, void* userdata);
+ u->frames_in_buffer = offset;
+ u->saved_frame_time = * (jack_nframes_t*) data;
+ u->saved_frame_time_valid = TRUE;
-/* The ringbuffer filler thread runs in this function. */
-static void* fill_ringbuffer(void* arg);
+ return 0;
-/* request_render asks asynchronously the mainloop to call io_event_cb. */
-static void request_render(struct userdata* u);
+ case SINK_MESSAGE_ON_SHUTDOWN:
+ pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+ return 0;
+ case PA_SINK_MESSAGE_GET_LATENCY: {
+ jack_nframes_t l, ft, d;
+ size_t n;
-int pa__init(pa_core* c, pa_module* self) {
- struct userdata* u = NULL;
- struct options o;
- unsigned i;
-
- assert(c);
- assert(self);
-
- o.sink_name = NULL;
- o.server_name = NULL;
- o.client_name = NULL;
-
- self->userdata = pa_xnew0(struct userdata, 1);
- u = self->userdata;
-
- u->pipe_fds[0] = u->pipe_fds[1] = -1;
- u->pipe_fd_type = 0;
- u->ringbuffer_is_full = 0;
- u->filler_thread_is_running = 0;
- u->quit_requested = 0;
- pthread_mutex_init(&u->buffersize_mutex, NULL);
- pthread_mutex_init(&u->cond_mutex, NULL);
- pthread_cond_init(&u->ringbuffer_cond, NULL);
-
- if (parse_options(&o, self->argument) != 0)
- goto fail;
-
- jack_set_error_function(jack_error_func);
-
- if (!(u->j_client = jack_client_open(
- o.client_name,
- o.server_name ? JackServerName : JackNullOption,
- NULL, o.server_name))) {
- pa_log_error("jack_client_open() failed.");
- goto fail;
- }
- pa_log_info("Successfully connected as '%s'",
- jack_get_client_name(u->j_client));
-
- if (!o.channels_given)
- set_default_channels(self, &o);
-
- u->channels = o.channels;
-
- if (!o.map_given)
- pa_channel_map_init_auto(&o.map, u->channels, PA_CHANNEL_MAP_ALSA);
-
- for (i = 0; i < u->channels; i++) {
- char* port_name = pa_sprintf_malloc(
- "out_%i:%s", i+1,
- pa_channel_position_to_string(o.map.map[i]));
-
- if (!(u->j_ports[i] = jack_port_register(
- u->j_client, port_name,
- JACK_DEFAULT_AUDIO_TYPE,
- JackPortIsOutput|JackPortIsTerminal, 0))) {
- pa_log("jack_port_register() failed.");
- goto fail;
+ /* This is the "worst-case" latency */
+ l = jack_port_get_total_latency(u->client, u->port[0]) + u->frames_in_buffer;
+
+ if (u->saved_frame_time_valid) {
+ /* Adjust the worst case latency by the time that
+ * passed since we last handed data to JACK */
+
+ ft = jack_frame_time(u->client);
+ d = ft > u->saved_frame_time ? ft - u->saved_frame_time : 0;
+ l = l > d ? l - d : 0;
+ }
+
+ /* Convert it to usec */
+ n = l * pa_frame_size(&u->sink->sample_spec);
+ *((pa_usec_t*) data) = pa_bytes_to_usec(n, &u->sink->sample_spec);
+
+ return 0;
}
-
- pa_xfree(port_name);
}
-
- if (pipe(u->pipe_fds) < 0) {
- pa_log("pipe() failed: %s", pa_cstrerror(errno));
- goto fail;
- }
- pa_make_nonblock_fd(u->pipe_fds[1]);
-
- if (create_sink(self, &o) != 0)
- goto fail;
- u->frame_size = pa_frame_size(&u->sink->sample_spec);
- u->j_buffersize = jack_get_buffer_size(u->j_client);
-
- /* If the ringbuffer size were equal to the jack buffer size, a full block
- would never fit in the ringbuffer, because the ringbuffer can never be
- totally full: one slot is always wasted. */
- if (o.buffersize <= u->j_buffersize) {
- o.buffersize = u->j_buffersize + 1;
- }
- /* The actual ringbuffer size will be rounded up to the nearest power of
- two. */
- if (!(u->ringbuffer = jack_ringbuffer_create(
- o.buffersize * u->frame_size))) {
- pa_log("jack_ringbuffer_create() failed.");
- goto fail;
- }
- assert((u->ringbuffer->size % sizeof(float)) == 0);
- pa_log_info("buffersize is %u frames (%u samples, %u bytes).",
- u->ringbuffer->size / u->frame_size,
- u->ringbuffer->size / sizeof(float),
- u->ringbuffer->size);
-
- jack_set_process_callback(u->j_client, jack_process, u);
- jack_set_buffer_size_callback(u->j_client, jack_blocksize_cb, u);
- jack_on_shutdown(u->j_client, jack_shutdown, u);
-
- if (jack_activate(u->j_client)) {
- pa_log("jack_activate() failed.");
- goto fail;
- }
+ return pa_sink_process_msg(o, code, data, offset, memchunk);
+}
- if (o.connect)
- connect_ports(self);
+static int jack_process(jack_nframes_t nframes, void *arg) {
+ struct userdata *u = arg;
+ unsigned c;
+ jack_nframes_t frame_time;
+ pa_assert(u);
- u->io_event = c->mainloop->io_new(c->mainloop, u->pipe_fds[0],
- PA_IO_EVENT_INPUT, io_event_cb, self);
-
- if (start_filling_ringbuffer(self) != 0)
- goto fail;
+ /* We just forward the request to our other RT thread */
- pa_xfree(o.sink_name);
- pa_xfree(o.server_name);
- pa_xfree(o.client_name);
-
- return 0;
+ for (c = 0; c < u->channels; c++)
+ pa_assert_se(u->buffer[c] = jack_port_get_buffer(u->port[c], nframes));
-fail:
- pa_xfree(o.sink_name);
- pa_xfree(o.server_name);
- pa_xfree(o.client_name);
- pa__done(c, self);
+ frame_time = jack_frame_time(u->client);
- return -1;
+ pa_assert_se(pa_asyncmsgq_send(u->jack_msgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_RENDER, &frame_time, nframes, NULL) == 0);
+ return 0;
}
+static void thread_func(void *userdata) {
+ struct userdata *u = userdata;
-static int parse_options(struct options* o, const char* argument) {
- pa_modargs *ma = NULL;
- const char* arg_val;
- pa_strbuf* strbuf;
-
- assert(o);
+ pa_assert(u);
- if (!(ma = pa_modargs_new(argument, valid_modargs))) {
- pa_log_error("Failed to parse module arguments.");
- goto fail;
- }
+ pa_log_debug("Thread starting up");
- strbuf = pa_strbuf_new();
- if ((arg_val = pa_modargs_get_value(ma, "sink_name", NULL))) {
- pa_strbuf_puts(strbuf, arg_val);
- o->sink_name = pa_strbuf_tostring(strbuf);
- o->sink_name_given = 1;
- } else {
- pa_strbuf_puts(strbuf, DEFAULT_SINK_NAME);
- o->sink_name = pa_strbuf_tostring(strbuf);
- o->sink_name_given = 0;
- }
- pa_strbuf_free(strbuf);
-
- strbuf = pa_strbuf_new();
- if ((arg_val = pa_modargs_get_value(ma, "server_name", NULL))) {
- pa_strbuf_puts(strbuf, arg_val);
- o->server_name = pa_strbuf_tostring(strbuf);
- o->server_name_given = 1;
- } else {
- o->server_name = NULL;
- o->server_name_given = 0;
- }
- pa_strbuf_free(strbuf);
-
- strbuf = pa_strbuf_new();
- if ((arg_val = pa_modargs_get_value(ma, "client_name", NULL))) {
- pa_strbuf_puts(strbuf, arg_val);
- o->client_name = pa_strbuf_tostring(strbuf);
- o->client_name_given = 1;
- } else {
- pa_strbuf_puts(strbuf, DEFAULT_CLIENT_NAME);
- o->client_name = pa_strbuf_tostring(strbuf);
- o->client_name_given = 1;
- }
- pa_strbuf_free(strbuf);
-
- if (pa_modargs_get_value(ma, "channels", NULL)) {
- o->channels_given = 1;
- if (pa_modargs_get_value_u32(ma, "channels", &o->channels) < 0 ||
- o->channels == 0 ||
- o->channels >= PA_CHANNELS_MAX) {
- pa_log_error("Failed to parse the \"channels\" argument.");
- goto fail;
- }
- } else {
- o->channels = 0; /* The actual default value is the number of physical
- input ports in jack (unknown at the moment), or if
- that's zero, then the default_sample_spec.channels
- of the core. */
- o->channels_given = 0;
- }
+ if (u->core->high_priority)
+ pa_make_realtime();
- if (pa_modargs_get_value(ma, "connect", NULL)) {
- o->connect_given = 1;
- if (pa_modargs_get_value_boolean(ma, "connect", &o->connect) < 0) {
- pa_log_error("Failed to parse the \"connect\" argument.");
- goto fail;
- }
- } else {
- o->connect = 1;
- o->connect_given = 0;
- }
+ pa_thread_mq_install(&u->thread_mq);
+ pa_rtpoll_install(u->rtpoll);
- if (pa_modargs_get_value(ma, "buffersize", NULL)) {
- o->buffersize_given = 1;
- if (pa_modargs_get_value_u32(ma, "buffersize", &o->buffersize) < 0) {
- pa_log_error("Failed to parse the \"buffersize\" argument.");
- goto fail;
- }
- } else {
- o->buffersize = DEFAULT_RINGBUFFER_SIZE;
- o->buffersize_given = 0;
- }
+ for (;;) {
+ int ret;
- if (pa_modargs_get_value(ma, "channel_map", NULL)) {
- o->map_given = 1;
- if (pa_modargs_get_channel_map(ma, &o->map) < 0) {
- pa_log_error("Failed to parse the \"channel_map\" argument.");
+ if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
goto fail;
- }
- /* channel_map specifies the channel count too. */
- if (o->channels_given && (o->channels != o->map.channels)) {
- pa_log_error(
- "\"channels\" and \"channel_map\" arguments conficted. If you "
- "use the \"channel_map\" argument, you can omit the "
- "\"channels\" argument.");
- goto fail;
- } else {
- o->channels = o->map.channels;
- o->channels_given = 1;
- }
- } else {
- /* The actual default value is the default alsa mappings, but that
- can't be set until the channel count is known. Here we initialize
- the map to some valid value, although the value won't be used. */
- pa_channel_map_init_stereo(&o->map);
- o->map_given = 0;
+ if (ret == 0)
+ goto finish;
}
- pa_modargs_free(ma);
-
- return 0;
-
fail:
- if (ma)
- pa_modargs_free(ma);
+ /* If this was no regular exit from the loop we have to continue
+ * processing messages until we received PA_MESSAGE_SHUTDOWN */
+ pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+ pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
- return -1;
+finish:
+ pa_log_debug("Thread shutting down");
}
+static void jack_error_func(const char*t) {
+ char *s;
-static void set_default_channels(pa_module* self, struct options* o) {
- struct userdata* u;
- const char **ports, **p;
-
- assert(self);
- assert(o);
- assert(self->userdata);
-
- u = self->userdata;
-
- assert(u->j_client);
- assert(self->core);
-
- o->channels = 0;
-
- ports = jack_get_ports(u->j_client, NULL, JACK_DEFAULT_AUDIO_TYPE,
- JackPortIsPhysical|JackPortIsInput);
-
- for (p = ports; *p; p++)
- o->channels++;
-
- free(ports);
-
- if (o->channels >= PA_CHANNELS_MAX)
- o->channels = PA_CHANNELS_MAX - 1;
-
- if (o->channels == 0)
- o->channels = self->core->default_sample_spec.channels;
+ s = pa_xstrndup(t, strcspn(t, "\n\r"));
+ pa_log_warn("JACK error >%s<", s);
+ pa_xfree(s);
}
+static void jack_init(void *arg) {
+ struct userdata *u = arg;
-static int create_sink(pa_module* self, struct options* o) {
- struct userdata* u;
- pa_sample_spec ss;
- char *t;
-
- assert(self);
- assert(o);
- assert(self->userdata);
-
- u = self->userdata;
-
- assert(u->j_client);
-
- ss.channels = u->channels;
- ss.rate = jack_get_sample_rate(u->j_client);
- ss.format = PA_SAMPLE_FLOAT32NE;
- assert(pa_sample_spec_valid(&ss));
+ pa_log_info("JACK thread starting up.");
- if (!(u->sink = pa_sink_new(self->core, __FILE__, o->sink_name, 0, &ss,
- &o->map))) {
- pa_log("failed to create sink.");
- return -1;
- }
-
- u->sink->userdata = u;
- pa_sink_set_owner(u->sink, self);
-
- pa_sink_set_description(
- u->sink,
- t = pa_sprintf_malloc("Jack sink (%s)",
- jack_get_client_name(u->j_client)));
- pa_xfree(t);
-
- u->sink->get_latency = sink_get_latency_cb;
-
- return 0;
+ if (u->core->high_priority)
+ pa_make_realtime();
}
+static void jack_shutdown(void* arg) {
+ struct userdata *u = arg;
-static void connect_ports(pa_module* self) {
- struct userdata* u;
- unsigned i;
- const char **ports, **p;
-
- assert(self);
- assert(self->userdata);
-
- u = self->userdata;
-
- assert(u->j_client);
-
- ports = jack_get_ports(u->j_client, NULL, JACK_DEFAULT_AUDIO_TYPE,
- JackPortIsPhysical|JackPortIsInput);
-
- for (i = 0, p = ports; i < u->channels; i++, p++) {
- assert(u->j_ports[i]);
-
- if (!*p) {
- pa_log("Not enough physical output ports, leaving unconnected.");
- break;
- }
-
- pa_log_info("connecting %s to %s",
- jack_port_name(u->j_ports[i]), *p);
-
- if (jack_connect(u->j_client, jack_port_name(u->j_ports[i]), *p)) {
- pa_log("Failed to connect %s to %s, leaving unconnected.",
- jack_port_name(u->j_ports[i]), *p);
- break;
- }
- }
-
- free(ports);
+ pa_log_info("JACK thread shutting down..");
+ pa_asyncmsgq_post(u->jack_msgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_ON_SHUTDOWN, NULL, 0, NULL, NULL);
}
+int pa__init(pa_module*m) {
+ struct userdata *u = NULL;
+ pa_sample_spec ss;
+ pa_channel_map map;
+ pa_modargs *ma = NULL;
+ jack_status_t status;
+ const char *server_name, *client_name;
+ uint32_t channels = 0;
+ int do_connect = 1;
+ unsigned i;
+ const char **ports = NULL, **p;
+ char *t;
-static int start_filling_ringbuffer(pa_module* self) {
- struct userdata* u;
- pthread_attr_t thread_attributes;
+ pa_assert(m);
- assert(self);
- assert(self->userdata);
+ jack_set_error_function(jack_error_func);
- u = self->userdata;
-
- pthread_attr_init(&thread_attributes);
-
- if (pthread_attr_setinheritsched(&thread_attributes,
- PTHREAD_INHERIT_SCHED) != 0) {
- pa_log("pthread_attr_setinheritsched() failed.");
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log("Failed to parse module arguments.");
goto fail;
}
- else if (pthread_create(&u->filler_thread, &thread_attributes,
- fill_ringbuffer, u) != 0) {
- pa_log("pthread_create() failed.");
+
+ if (pa_modargs_get_value_boolean(ma, "connect", &do_connect) < 0) {
+ pa_log("Failed to parse connect= argument.");
goto fail;
}
-
- u->filler_thread_is_running = 1;
-
- pthread_attr_destroy(&thread_attributes);
-
- return 0;
-
-fail:
- pthread_attr_destroy(&thread_attributes);
- return -1;
-}
+ server_name = pa_modargs_get_value(ma, "server_name", NULL);
+ client_name = pa_modargs_get_value(ma, "client_name", "PulseAudio JACK Sink");
+
+ u = pa_xnew0(struct userdata, 1);
+ u->core = m->core;
+ u->module = m;
+ m->userdata = u;
+ u->saved_frame_time_valid = FALSE;
+ pa_thread_mq_init(&u->thread_mq, m->core->mainloop);
+ u->rtpoll = pa_rtpoll_new();
+ pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq);
+
+ /* The queue linking the JACK thread and our RT thread */
+ u->jack_msgq = pa_asyncmsgq_new(0);
+
+ /* The msgq from the JACK RT thread should have an even higher
+ * priority than the normal message queues, to match the guarantee
+ * all other drivers make: supplying the audio device with data is
+ * the top priority -- and as long as that is possible we don't do
+ * anything else */
+ u->rtpoll_item = pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY-1, u->jack_msgq);
+
+ if (!(u->client = jack_client_open(client_name, server_name ? JackServerName : JackNullOption, &status, server_name))) {
+ pa_log("jack_client_open() failed.");
+ goto fail;
+ }
-static void jack_error_func(const char* t) {
- pa_log_warn("JACK error >%s<", t);
-}
-
+ ports = jack_get_ports(u->client, NULL, NULL, JackPortIsPhysical|JackPortIsInput);
-static pa_usec_t sink_get_latency_cb(pa_sink* s) {
- /* The latency is approximately the sum of the first port's latency,
- buffersize of jack and the ringbuffer size. Maybe instead of using just
- the first port, the max of all ports' latencies should be used? */
- struct userdata* u;
- jack_nframes_t l;
-
- assert(s);
- assert(s->userdata);
-
- u = s->userdata;
-
- l = jack_port_get_total_latency(u->j_client, u->j_ports[0]) +
- u->j_buffersize + u->ringbuffer->size / u->frame_size;
-
- return pa_bytes_to_usec(l * u->frame_size, &s->sample_spec);
-}
+ channels = 0;
+ for (p = ports; *p; p++)
+ channels++;
+ if (!channels)
+ channels = m->core->default_sample_spec.channels;
-static int jack_process(jack_nframes_t nframes, void* arg) {
- struct userdata* u = arg;
- float* j_buffers[PA_CHANNELS_MAX];
- unsigned nsamples = u->channels * nframes;
- unsigned sample_idx_part1, sample_idx_part2;
- jack_nframes_t frame_idx;
- jack_ringbuffer_data_t data[2]; /* In case the readable area in the
- ringbuffer is non-continuous, the data
- will be split in two parts. */
- unsigned chan;
- unsigned samples_left_over;
-
- for (chan = 0; chan < u->channels; chan++) {
- j_buffers[chan] = jack_port_get_buffer(u->j_ports[chan], nframes);
- }
-
- jack_ringbuffer_get_read_vector(u->ringbuffer, data);
-
- /* We assume that the possible discontinuity doesn't happen in the middle
- * of a sample. Should be a safe assumption. */
- assert(((data[0].len % sizeof(float)) == 0) ||
- (data[1].len == 0));
-
- /* Copy from the first part of data until enough samples are copied or the
- first part ends. */
- sample_idx_part1 = 0;
- chan = 0;
- frame_idx = 0;
- while (sample_idx_part1 < nsamples &&
- ((sample_idx_part1 + 1) * sizeof(float)) <= data[0].len) {
- float *s = ((float*) data[0].buf) + sample_idx_part1;
- float *d = j_buffers[chan] + frame_idx;
- *d = *s;
-
- sample_idx_part1++;
- chan = (chan + 1) % u->channels;
- frame_idx = sample_idx_part1 / u->channels;
- }
-
- samples_left_over = nsamples - sample_idx_part1;
-
- /* Copy from the second part of data until enough samples are copied or the
- second part ends. */
- sample_idx_part2 = 0;
- while (sample_idx_part2 < samples_left_over &&
- ((sample_idx_part2 + 1) * sizeof(float)) <= data[1].len) {
- float *s = ((float*) data[1].buf) + sample_idx_part2;
- float *d = j_buffers[chan] + frame_idx;
- *d = *s;
-
- sample_idx_part2++;
- chan = (chan + 1) % u->channels;
- frame_idx = (sample_idx_part1 + sample_idx_part2) / u->channels;
+ if (pa_modargs_get_value_u32(ma, "channels", &channels) < 0 || channels <= 0 || channels >= PA_CHANNELS_MAX) {
+ pa_log("Failed to parse channels= argument.");
+ goto fail;
}
-
- samples_left_over -= sample_idx_part2;
-
- /* If there's still samples left, fill the buffers with zeros. */
- while (samples_left_over > 0) {
- float *d = j_buffers[chan] + frame_idx;
- *d = 0.0;
-
- samples_left_over--;
- chan = (chan + 1) % u->channels;
- frame_idx = (nsamples - samples_left_over) / u->channels;
+
+ pa_channel_map_init_auto(&map, channels, PA_CHANNEL_MAP_ALSA);
+ if (pa_modargs_get_channel_map(ma, NULL, &map) < 0 || map.channels != channels) {
+ pa_log("Failed to parse channel_map= argument.");
+ goto fail;
}
-
- jack_ringbuffer_read_advance(
- u->ringbuffer, (sample_idx_part1 + sample_idx_part2) * sizeof(float));
-
- /* Tell the rendering part that there is room in the ringbuffer. */
- u->ringbuffer_is_full = 0;
- pthread_cond_signal(&u->ringbuffer_cond);
-
- return 0;
-}
+ pa_log_info("Successfully connected as '%s'", jack_get_client_name(u->client));
+
+ ss.channels = u->channels = channels;
+ ss.rate = jack_get_sample_rate(u->client);
+ ss.format = PA_SAMPLE_FLOAT32NE;
-static int jack_blocksize_cb(jack_nframes_t nframes, void* arg) {
- /* This gets called in the processing thread, so do we have to be realtime
- safe? No, we can do whatever we want. User gets silence while we do it.
-
- In addition to just updating the j_buffersize field in userdata, we have
- to create a new ringbuffer, if the new buffer size is bigger or equal to
- the old ringbuffer size. */
- struct userdata* u = arg;
-
- assert(u);
-
- /* We don't want to change the blocksize and the ringbuffer while rendering
- is going on. */
- pthread_mutex_lock(&u->buffersize_mutex);
-
- u->j_buffersize = nframes;
-
- if ((u->ringbuffer->size / u->frame_size) <= nframes) {
- /* We have to create a new ringbuffer. What are we going to do with the
- old data in the old buffer? We throw it away, because we're lazy
- coders. The listening experience is likely to get ruined anyway
- during the blocksize change. */
- jack_ringbuffer_free(u->ringbuffer);
-
- /* The actual ringbuffer size will be rounded up to the nearest power
- of two. */
- if (!(u->ringbuffer =
- jack_ringbuffer_create((nframes + 1) * u->frame_size))) {
- pa_log_error(
- "jack_ringbuffer_create() failed while changing jack's buffer "
- "size, module exiting.");
- jack_client_close(u->j_client);
- u->quit_requested = 1;
+ pa_assert(pa_sample_spec_valid(&ss));
+
+ for (i = 0; i < ss.channels; i++) {
+ if (!(u->port[i] = jack_port_register(u->client, pa_channel_position_to_string(map.map[i]), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput|JackPortIsTerminal, 0))) {
+ pa_log("jack_port_register() failed.");
+ goto fail;
}
- assert((u->ringbuffer->size % sizeof(float)) == 0);
- pa_log_info("buffersize is %u frames (%u samples, %u bytes).",
- u->ringbuffer->size / u->frame_size,
- u->ringbuffer->size / sizeof(float),
- u->ringbuffer->size);
}
-
- pthread_mutex_unlock(&u->buffersize_mutex);
-
- return 0;
-}
+ if (!(u->sink = pa_sink_new(m->core, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, &map))) {
+ pa_log("failed to create sink.");
+ goto fail;
+ }
-static void jack_shutdown(void* arg) {
- struct userdata* u = arg;
- assert(u);
+ u->sink->parent.process_msg = sink_process_msg;
+ u->sink->userdata = u;
+ u->sink->flags = PA_SINK_LATENCY;
- u->quit_requested = 1;
- request_render(u);
-}
+ pa_sink_set_module(u->sink, m);
+ pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
+ pa_sink_set_rtpoll(u->sink, u->rtpoll);
+ pa_sink_set_description(u->sink, t = pa_sprintf_malloc("Jack sink (%s)", jack_get_client_name(u->client)));
+ pa_xfree(t);
+ jack_set_process_callback(u->client, jack_process, u);
+ jack_on_shutdown(u->client, jack_shutdown, u);
+ jack_set_thread_init_callback(u->client, jack_init, u);
-static void io_event_cb(pa_mainloop_api* m, pa_io_event* e, int fd,
- pa_io_event_flags_t flags, void* userdata) {
- pa_module* self = userdata;
- struct userdata* u;
- char x;
- jack_ringbuffer_data_t buffer[2]; /* The write area in the ringbuffer may
- be split in two parts. */
- pa_memchunk chunk; /* This is the data source. */
- unsigned part1_length, part2_length;
- unsigned sample_idx_part1, sample_idx_part2;
- unsigned chan;
- unsigned frame_size;
- int rem;
-
- assert(m);
- assert(e);
- assert(flags == PA_IO_EVENT_INPUT);
- assert(self);
- assert(self->userdata);
-
- u = self->userdata;
-
- assert(u->pipe_fds[0] == fd);
-
- pa_read(fd, &x, 1, &u->pipe_fd_type);
-
- if (u->quit_requested) {
- pa_module_unload_request(self);
- return;
+ if (!(u->thread = pa_thread_new(thread_func, u))) {
+ pa_log("Failed to create thread.");
+ goto fail;
}
- frame_size = u->frame_size;
-
- /* No blocksize changes during rendering, please. */
- pthread_mutex_lock(&u->buffersize_mutex);
-
- jack_ringbuffer_get_write_vector(u->ringbuffer, buffer);
- assert(((buffer[0].len % sizeof(float)) == 0) || (buffer[1].len == 0));
-
- part1_length = buffer[0].len / sizeof(float);
- part2_length = buffer[1].len / sizeof(float);
-
- /* If the amount of free space is not a multiple of the frame size, we have
- to truncate the lengths so that we process only complete frames. */
- if ((rem = (part1_length + part2_length) % u->channels) != 0) {
- if (part2_length >= rem) {
- part2_length -= rem;
- } else {
- part1_length -= rem - part2_length;
- part2_length = 0;
- }
+ if (jack_activate(u->client)) {
+ pa_log("jack_activate() failed");
+ goto fail;
}
-
- /* pa_sink_render_full doesn't accept zero length, so we have do the
- copying only if there's data to copy, which actually makes a kind of
- sense. */
- if (part1_length > 0 || part2_length > 0) {
- pa_sink_render_full(u->sink,
- (part1_length + part2_length) * sizeof(float),
- &chunk);
-
- /* Write to the first part of the buffer. */
- for (sample_idx_part1 = 0;
- sample_idx_part1 < part1_length;
- sample_idx_part1++) {
- float *s =
- ((float*) ((uint8_t*) chunk.memblock->data + chunk.index)) +
- sample_idx_part1;
- float *d = ((float*) buffer[0].buf) + sample_idx_part1;
- *d = *s;
- }
-
- /* Write to the second part of the buffer. */
- for (sample_idx_part2 = 0;
- sample_idx_part2 < part2_length;
- sample_idx_part2++) {
- float *s =
- ((float*) ((uint8_t*) chunk.memblock->data + chunk.index)) +
- sample_idx_part1 + sample_idx_part2;
- float *d = ((float*) buffer[1].buf) + sample_idx_part2;
- *d = *s;
+
+ if (do_connect) {
+ for (i = 0, p = ports; i < ss.channels; i++, p++) {
+
+ if (!*p) {
+ pa_log("Not enough physical output ports, leaving unconnected.");
+ break;
+ }
+
+ pa_log_info("Connecting %s to %s", jack_port_name(u->port[i]), *p);
+
+ if (jack_connect(u->client, jack_port_name(u->port[i]), *p)) {
+ pa_log("Failed to connect %s to %s, leaving unconnected.", jack_port_name(u->port[i]), *p);
+ break;
+ }
}
-
- pa_memblock_unref(chunk.memblock);
-
- jack_ringbuffer_write_advance(
- u->ringbuffer, (part1_length + part2_length) * sizeof(float));
}
-
- /* Blocksize can be changed again. */
- pthread_mutex_unlock(&u->buffersize_mutex);
-}
+ pa_sink_put(u->sink);
-static void* fill_ringbuffer(void* arg) {
- struct userdata* u = arg;
-
- assert(u);
-
- while (!u->quit_requested) {
- if (u->ringbuffer_is_full) {
- pthread_mutex_lock(&u->cond_mutex);
- pthread_cond_wait(&u->ringbuffer_cond,
- &u->cond_mutex);
- pthread_mutex_unlock(&u->cond_mutex);
- }
- /* No, it's not full yet, but this must be set to one as soon as
- possible, because if the jack thread manages to process another
- block before we set this to one, we may end up waiting without
- a reason. */
- u->ringbuffer_is_full = 1;
+ free(ports);
+ pa_modargs_free(ma);
- request_render(u);
- }
-
- return NULL;
-}
+ return 0;
+
+fail:
+ if (ma)
+ pa_modargs_free(ma);
+
+ free(ports);
+ pa__done(m);
-static void request_render(struct userdata* u) {
- char c = 'x';
-
- assert(u);
-
- assert(u->pipe_fds[1] >= 0);
- pa_write(u->pipe_fds[1], &c, 1, &u->pipe_fd_type);
+ return -1;
}
-void pa__done(pa_core* c, pa_module* self) {
- struct userdata* u;
-
- assert(c);
- assert(self);
+void pa__done(pa_module*m) {
+ struct userdata *u;
+
+ pa_assert(m);
- if (!self->userdata)
+ if (!(u = m->userdata))
return;
- u = self->userdata;
-
- if (u->filler_thread_is_running) {
- u->quit_requested = 1;
- pthread_cond_signal(&u->ringbuffer_cond);
- pthread_join(u->filler_thread, NULL);
+ if (u->client)
+ jack_client_close(u->client);
+
+ if (u->sink)
+ pa_sink_unlink(u->sink);
+
+ if (u->thread) {
+ pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+ pa_thread_free(u->thread);
}
-
- if (u->j_client)
- jack_client_close(u->j_client);
- if (u->io_event)
- c->mainloop->io_free(u->io_event);
+ pa_thread_mq_done(&u->thread_mq);
- if (u->sink) {
- pa_sink_disconnect(u->sink);
+ if (u->sink)
pa_sink_unref(u->sink);
- }
-
- if (u->ringbuffer)
- jack_ringbuffer_free(u->ringbuffer);
-
- if (u->pipe_fds[0] >= 0)
- pa_close(u->pipe_fds[0]);
- if (u->pipe_fds[1] >= 0)
- pa_close(u->pipe_fds[1]);
-
- pthread_mutex_destroy(&u->buffersize_mutex);
- pthread_cond_destroy(&u->ringbuffer_cond);
- pthread_mutex_destroy(&u->cond_mutex);
- pa_xfree(self->userdata);
- self->userdata = NULL;
+
+ if (u->rtpoll_item)
+ pa_rtpoll_item_free(u->rtpoll_item);
+
+ if (u->jack_msgq)
+ pa_asyncmsgq_unref(u->jack_msgq);
+
+ if (u->rtpoll)
+ pa_rtpoll_free(u->rtpoll);
+
+ pa_xfree(u);
}
diff --git a/src/modules/module-jack-source.c b/src/modules/module-jack-source.c
index 8ca24035..b62ebe7a 100644
--- a/src/modules/module-jack-source.c
+++ b/src/modules/module-jack-source.c
@@ -28,31 +28,34 @@
#include <stdlib.h>
#include <sys/stat.h>
#include <stdio.h>
-#include <assert.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>
-#include <pthread.h>
#include <jack/jack.h>
#include <pulse/xmalloc.h>
#include <pulsecore/core-error.h>
-#include <pulsecore/iochannel.h>
#include <pulsecore/source.h>
#include <pulsecore/module.h>
#include <pulsecore/core-util.h>
#include <pulsecore/modargs.h>
#include <pulsecore/log.h>
-#include <pulse/mainloop-api.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/sample-util.h>
#include "module-jack-source-symdef.h"
+/* See module-jack-sink for a few comments how this module basically
+ * works */
+
PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("Jack Source")
+PA_MODULE_DESCRIPTION("JACK Source")
PA_MODULE_VERSION(PACKAGE_VERSION)
PA_MODULE_USAGE(
"source_name=<name of source> "
@@ -67,7 +70,6 @@ PA_MODULE_USAGE(
struct userdata {
pa_core *core;
pa_module *module;
-
pa_source *source;
unsigned channels;
@@ -75,19 +77,15 @@ struct userdata {
jack_port_t* port[PA_CHANNELS_MAX];
jack_client_t *client;
- pthread_mutex_t mutex;
- pthread_cond_t cond;
+ pa_thread_mq thread_mq;
+ pa_asyncmsgq *jack_msgq;
+ pa_rtpoll *rtpoll;
+ pa_rtpoll_item *rtpoll_item;
- void * buffer[PA_CHANNELS_MAX];
- jack_nframes_t frames_posted;
- int quit_requested;
+ pa_thread *thread;
- int pipe_fds[2];
- int pipe_fd_type;
- pa_io_event *io_event;
-
- jack_nframes_t frames_in_buffer;
- jack_nframes_t timestamp;
+ jack_nframes_t saved_frame_time;
+ pa_bool_t saved_frame_time_valid;
};
static const char* const valid_modargs[] = {
@@ -100,141 +98,150 @@ static const char* const valid_modargs[] = {
NULL
};
-static void stop_source(struct userdata *u) {
- assert (u);
-
- jack_client_close(u->client);
- u->client = NULL;
- u->core->mainloop->io_free(u->io_event);
- u->io_event = NULL;
- pa_source_disconnect(u->source);
- pa_source_unref(u->source);
- u->source = NULL;
- pa_module_unload_request(u->module);
-}
+enum {
+ SOURCE_MESSAGE_POST = PA_SOURCE_MESSAGE_MAX,
+ SOURCE_MESSAGE_ON_SHUTDOWN
+};
-static void io_event_cb(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event_flags_t flags, void *userdata) {
- struct userdata *u = userdata;
- char x;
+static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+ struct userdata *u = PA_SOURCE(o)->userdata;
- assert(m);
- assert(flags == PA_IO_EVENT_INPUT);
- assert(u);
- assert(u->pipe_fds[0] == fd);
+ switch (code) {
- pa_read(fd, &x, 1, &u->pipe_fd_type);
+ case SOURCE_MESSAGE_POST:
- if (u->quit_requested) {
- stop_source(u);
- u->quit_requested = 0;
- return;
- }
+ /* Handle the new block from the JACK thread */
+ pa_assert(chunk);
+ pa_assert(chunk->length > 0);
- pthread_mutex_lock(&u->mutex);
+ if (u->source->thread_info.state == PA_SOURCE_RUNNING)
+ pa_source_post(u->source, chunk);
- if (u->frames_posted > 0) {
- unsigned fs;
- jack_nframes_t frame_idx;
- pa_memchunk chunk;
+ u->saved_frame_time = offset;
+ u->saved_frame_time_valid = TRUE;
- fs = pa_frame_size(&u->source->sample_spec);
+ return 0;
- chunk.memblock = pa_memblock_new(u->core->mempool, chunk.length = u->frames_posted * fs);
- chunk.index = 0;
+ case SOURCE_MESSAGE_ON_SHUTDOWN:
+ pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+ return 0;
- for (frame_idx = 0; frame_idx < u->frames_posted; frame_idx ++) {
- unsigned c;
+ case PA_SOURCE_MESSAGE_GET_LATENCY: {
+ jack_nframes_t l, ft, d;
+ size_t n;
- for (c = 0; c < u->channels; c++) {
- float *s = ((float*) u->buffer[c]) + frame_idx;
- float *d = ((float*) ((uint8_t*) chunk.memblock->data + chunk.index)) + (frame_idx * u->channels) + c;
+ /* This is the "worst-case" latency */
+ l = jack_port_get_total_latency(u->client, u->port[0]);
- *d = *s;
- }
- }
+ if (u->saved_frame_time_valid) {
+ /* Adjust the worst case latency by the time that
+ * passed since we last handed data to JACK */
- pa_source_post(u->source, &chunk);
- pa_memblock_unref(chunk.memblock);
+ ft = jack_frame_time(u->client);
+ d = ft > u->saved_frame_time ? ft - u->saved_frame_time : 0;
+ l += d;
+ }
- u->frames_posted = 0;
+ /* Convert it to usec */
+ n = l * pa_frame_size(&u->source->sample_spec);
+ *((pa_usec_t*) data) = pa_bytes_to_usec(n, &u->source->sample_spec);
- pthread_cond_signal(&u->cond);
+ return 0;
+ }
}
- pthread_mutex_unlock(&u->mutex);
-}
-
-static void request_post(struct userdata *u) {
- char c = 'x';
- assert(u);
-
- assert(u->pipe_fds[1] >= 0);
- pa_write(u->pipe_fds[1], &c, 1, &u->pipe_fd_type);
-}
-
-static void jack_shutdown(void *arg) {
- struct userdata *u = arg;
- assert(u);
-
- u->quit_requested = 1;
- request_post(u);
+ return pa_source_process_msg(o, code, data, offset, chunk);
}
static int jack_process(jack_nframes_t nframes, void *arg) {
+ unsigned c;
struct userdata *u = arg;
- assert(u);
-
- if (jack_transport_query(u->client, NULL) == JackTransportRolling) {
- unsigned c;
+ const void *buffer[PA_CHANNELS_MAX];
+ void *p;
+ jack_nframes_t frame_time;
+ pa_memchunk chunk;
- pthread_mutex_lock(&u->mutex);
+ pa_assert(u);
- u->frames_posted = nframes;
+ for (c = 0; c < u->channels; c++)
+ pa_assert(buffer[c] = jack_port_get_buffer(u->port[c], nframes));
- for (c = 0; c < u->channels; c++) {
- u->buffer[c] = jack_port_get_buffer(u->port[c], nframes);
- assert(u->buffer[c]);
- }
+ /* We interleave the data and pass it on to the other RT thread */
- request_post(u);
+ pa_memchunk_reset(&chunk);
+ chunk.length = nframes * pa_frame_size(&u->source->sample_spec);
+ chunk.memblock = pa_memblock_new(u->core->mempool, chunk.length);
+ p = pa_memblock_acquire(chunk.memblock);
+ pa_interleave(buffer, u->channels, p, sizeof(float), nframes);
+ pa_memblock_release(chunk.memblock);
- pthread_cond_wait(&u->cond, &u->mutex);
+ frame_time = jack_frame_time(u->client);
- u->frames_in_buffer = nframes;
- u->timestamp = jack_get_current_transport_frame(u->client);
+ pa_asyncmsgq_post(u->jack_msgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_POST, NULL, frame_time, &chunk, NULL);
- pthread_mutex_unlock(&u->mutex);
- }
+ pa_memblock_unref(chunk.memblock);
return 0;
}
-static pa_usec_t source_get_latency_cb(pa_source *s) {
- struct userdata *u;
- jack_nframes_t n, l, d;
+static void thread_func(void *userdata) {
+ struct userdata *u = userdata;
+
+ pa_assert(u);
- assert(s);
- u = s->userdata;
+ pa_log_debug("Thread starting up");
- if (jack_transport_query(u->client, NULL) != JackTransportRolling)
- return 0;
+ if (u->core->high_priority)
+ pa_make_realtime();
- n = jack_get_current_transport_frame(u->client);
+ pa_thread_mq_install(&u->thread_mq);
+ pa_rtpoll_install(u->rtpoll);
- if (n < u->timestamp)
- return 0;
+ for (;;) {
+ int ret;
- d = n - u->timestamp;
- l = jack_port_get_total_latency(u->client, u->port[0]);
+ if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
+ goto fail;
+
+ if (ret == 0)
+ goto finish;
+ }
- return pa_bytes_to_usec((l + d) * pa_frame_size(&s->sample_spec), &s->sample_spec);
+fail:
+ /* If this was no regular exit from the loop we have to continue
+ * processing messages until we received PA_MESSAGE_SHUTDOWN */
+ pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+ pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+ pa_log_debug("Thread shutting down");
}
static void jack_error_func(const char*t) {
- pa_log_warn("JACK error >%s<", t);
+ char *s;
+
+ s = pa_xstrndup(t, strcspn(t, "\n\r"));
+ pa_log_warn("JACK error >%s<", s);
+ pa_xfree(s);
+}
+
+static void jack_init(void *arg) {
+ struct userdata *u = arg;
+
+ pa_log_info("JACK thread starting up.");
+
+ if (u->core->high_priority)
+ pa_make_realtime();
}
-int pa__init(pa_core *c, pa_module*m) {
+static void jack_shutdown(void* arg) {
+ struct userdata *u = arg;
+
+ pa_log_info("JACK thread shutting down..");
+ pa_asyncmsgq_post(u->jack_msgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_ON_SHUTDOWN, NULL, 0, NULL, NULL);
+}
+
+int pa__init(pa_module*m) {
struct userdata *u = NULL;
pa_sample_spec ss;
pa_channel_map map;
@@ -247,40 +254,35 @@ int pa__init(pa_core *c, pa_module*m) {
const char **ports = NULL, **p;
char *t;
- assert(c);
- assert(m);
+ pa_assert(m);
jack_set_error_function(jack_error_func);
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
- pa_log("failed to parse module arguments.");
+ pa_log("Failed to parse module arguments.");
goto fail;
}
if (pa_modargs_get_value_boolean(ma, "connect", &do_connect) < 0) {
- pa_log("failed to parse connect= argument.");
+ pa_log("Failed to parse connect= argument.");
goto fail;
}
server_name = pa_modargs_get_value(ma, "server_name", NULL);
- client_name = pa_modargs_get_value(ma, "client_name", "PulseAudio");
+ client_name = pa_modargs_get_value(ma, "client_name", "PulseAudio JACK Source");
u = pa_xnew0(struct userdata, 1);
- m->userdata = u;
- u->core = c;
+ u->core = m->core;
u->module = m;
- u->pipe_fds[0] = u->pipe_fds[1] = -1;
- u->pipe_fd_type = 0;
-
- pthread_mutex_init(&u->mutex, NULL);
- pthread_cond_init(&u->cond, NULL);
+ m->userdata = u;
+ u->saved_frame_time_valid = FALSE;
- if (pipe(u->pipe_fds) < 0) {
- pa_log("pipe() failed: %s", pa_cstrerror(errno));
- goto fail;
- }
+ pa_thread_mq_init(&u->thread_mq, m->core->mainloop);
+ u->rtpoll = pa_rtpoll_new();
+ pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq);
- pa_make_nonblock_fd(u->pipe_fds[1]);
+ u->jack_msgq = pa_asyncmsgq_new(0);
+ u->rtpoll_item = pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY-1, u->jack_msgq);
if (!(u->client = jack_client_open(client_name, server_name ? JackServerName : JackNullOption, &status, server_name))) {
pa_log("jack_client_open() failed.");
@@ -294,7 +296,7 @@ int pa__init(pa_core *c, pa_module*m) {
channels++;
if (!channels)
- channels = c->default_sample_spec.channels;
+ channels = m->core->default_sample_spec.channels;
if (pa_modargs_get_value_u32(ma, "channels", &channels) < 0 || channels <= 0 || channels >= PA_CHANNELS_MAX) {
pa_log("failed to parse channels= argument.");
@@ -302,7 +304,7 @@ int pa__init(pa_core *c, pa_module*m) {
}
pa_channel_map_init_auto(&map, channels, PA_CHANNEL_MAP_ALSA);
- if (pa_modargs_get_channel_map(ma, &map) < 0 || map.channels != channels) {
+ if (pa_modargs_get_channel_map(ma, NULL, &map) < 0 || map.channels != channels) {
pa_log("failed to parse channel_map= argument.");
goto fail;
}
@@ -313,7 +315,7 @@ int pa__init(pa_core *c, pa_module*m) {
ss.rate = jack_get_sample_rate(u->client);
ss.format = PA_SAMPLE_FLOAT32NE;
- assert(pa_sample_spec_valid(&ss));
+ pa_assert(pa_sample_spec_valid(&ss));
for (i = 0; i < ss.channels; i++) {
if (!(u->port[i] = jack_port_register(u->client, pa_channel_position_to_string(map.map[i]), JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput|JackPortIsTerminal, 0))) {
@@ -322,19 +324,29 @@ int pa__init(pa_core *c, pa_module*m) {
}
}
- if (!(u->source = pa_source_new(c, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, &map))) {
+ if (!(u->source = pa_source_new(m->core, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, &map))) {
pa_log("failed to create source.");
goto fail;
}
+ u->source->parent.process_msg = source_process_msg;
u->source->userdata = u;
- pa_source_set_owner(u->source, m);
+ u->source->flags = PA_SOURCE_LATENCY;
+
+ pa_source_set_module(u->source, m);
+ pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
+ pa_source_set_rtpoll(u->source, u->rtpoll);
pa_source_set_description(u->source, t = pa_sprintf_malloc("Jack source (%s)", jack_get_client_name(u->client)));
pa_xfree(t);
- u->source->get_latency = source_get_latency_cb;
jack_set_process_callback(u->client, jack_process, u);
jack_on_shutdown(u->client, jack_shutdown, u);
+ jack_set_thread_init_callback(u->client, jack_init, u);
+
+ if (!(u->thread = pa_thread_new(thread_func, u))) {
+ pa_log("Failed to create thread.");
+ goto fail;
+ }
if (jack_activate(u->client)) {
pa_log("jack_activate() failed");
@@ -359,7 +371,7 @@ int pa__init(pa_core *c, pa_module*m) {
}
- u->io_event = c->mainloop->io_new(c->mainloop, u->pipe_fds[0], PA_IO_EVENT_INPUT, io_event_cb, u);
+ pa_source_put(u->source);
free(ports);
pa_modargs_free(ma);
@@ -372,14 +384,14 @@ fail:
free(ports);
- pa__done(c, m);
+ pa__done(m);
return -1;
}
-void pa__done(pa_core *c, pa_module*m) {
+void pa__done(pa_module*m) {
struct userdata *u;
- assert(c && m);
+ pa_assert(m);
if (!(u = m->userdata))
return;
@@ -387,20 +399,27 @@ void pa__done(pa_core *c, pa_module*m) {
if (u->client)
jack_client_close(u->client);
- if (u->io_event)
- c->mainloop->io_free(u->io_event);
+ if (u->source)
+ pa_source_unlink(u->source);
- if (u->source) {
- pa_source_disconnect(u->source);
- pa_source_unref(u->source);
+ if (u->thread) {
+ pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+ pa_thread_free(u->thread);
}
- if (u->pipe_fds[0] >= 0)
- close(u->pipe_fds[0]);
- if (u->pipe_fds[1] >= 0)
- close(u->pipe_fds[1]);
+ pa_thread_mq_done(&u->thread_mq);
+
+ if (u->source)
+ pa_source_unref(u->source);
+
+ if (u->rtpoll_item)
+ pa_rtpoll_item_free(u->rtpoll_item);
+
+ if (u->jack_msgq)
+ pa_asyncmsgq_unref(u->jack_msgq);
+
+ if (u->rtpoll)
+ pa_rtpoll_free(u->rtpoll);
- pthread_mutex_destroy(&u->mutex);
- pthread_cond_destroy(&u->cond);
pa_xfree(u);
}
diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c
new file mode 100644
index 00000000..0265d971
--- /dev/null
+++ b/src/modules/module-ladspa-sink.c
@@ -0,0 +1,673 @@
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 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 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.
+***/
+
+/* TODO: Some plugins cause latency, and some even report it by using a control
+ out port. We don't currently use the latency information. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/sample-util.h>
+
+#include "module-ladspa-sink-symdef.h"
+#include "ladspa.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering")
+PA_MODULE_DESCRIPTION("Virtual LADSPA sink")
+PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_USAGE(
+ "sink_name=<name for the sink> "
+ "master=<name of sink to remap> "
+ "format=<sample format> "
+ "channels=<number of channels> "
+ "rate=<sample rate> "
+ "channel_map=<channel map> "
+ "plugin=<ladspa plugin name> "
+ "label=<ladspa plugin label> "
+ "control=<comma seperated list of input control values>")
+
+struct userdata {
+ pa_core *core;
+ pa_module *module;
+
+ pa_sink *sink, *master;
+ pa_sink_input *sink_input;
+
+ const LADSPA_Descriptor *descriptor;
+ unsigned channels;
+ LADSPA_Handle handle[PA_CHANNELS_MAX];
+ 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. */
+ LADSPA_Data control_out;
+
+ pa_memchunk memchunk;
+};
+
+static const char* const valid_modargs[] = {
+ "sink_name",
+ "master",
+ "format",
+ "channels",
+ "rate",
+ "channel_map",
+ "plugin",
+ "label",
+ "control",
+ NULL
+};
+
+/* Called from I/O thread context */
+static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+ struct userdata *u = PA_SINK(o)->userdata;
+
+ switch (code) {
+
+ case PA_SINK_MESSAGE_GET_LATENCY: {
+ pa_usec_t usec = 0;
+
+ if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
+ usec = 0;
+
+ *((pa_usec_t*) data) = usec + pa_bytes_to_usec(u->memchunk.length, &u->sink->sample_spec);
+ return 0;
+ }
+ }
+
+ return pa_sink_process_msg(o, code, data, offset, chunk);
+}
+
+/* Called from main context */
+static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
+ struct userdata *u;
+
+ pa_sink_assert_ref(s);
+ pa_assert_se(u = s->userdata);
+
+ if (PA_SINK_LINKED(state) && u->sink_input && PA_SINK_INPUT_LINKED(pa_sink_input_get_state(u->sink_input)))
+ pa_sink_input_cork(u->sink_input, state == PA_SINK_SUSPENDED);
+
+ return 0;
+}
+
+/* Called from I/O thread context */
+static int sink_input_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+ struct userdata *u = PA_SINK_INPUT(o)->userdata;
+
+ switch (code) {
+ case PA_SINK_INPUT_MESSAGE_GET_LATENCY:
+ *((pa_usec_t*) data) = pa_bytes_to_usec(u->memchunk.length, &u->sink_input->sample_spec);
+
+ /* Fall through, the default handler will add in the extra
+ * latency added by the resampler */
+ break;
+ }
+
+ return pa_sink_input_process_msg(o, code, data, offset, chunk);
+}
+
+/* Called from I/O thread context */
+static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
+ struct userdata *u;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(u = i->userdata);
+
+ if (!u->memchunk.memblock) {
+ pa_memchunk tchunk;
+ float *src, *dst;
+ size_t fs;
+ unsigned n, c;
+
+ pa_sink_render(u->sink, length, &tchunk);
+
+ fs = pa_frame_size(&i->sample_spec);
+ n = tchunk.length / fs;
+
+ pa_assert(n > 0);
+
+ u->memchunk.memblock = pa_memblock_new(i->sink->core->mempool, tchunk.length);
+ u->memchunk.index = 0;
+ u->memchunk.length = tchunk.length;
+
+ src = (float*) ((uint8_t*) pa_memblock_acquire(tchunk.memblock) + tchunk.index);
+ dst = (float*) pa_memblock_acquire(u->memchunk.memblock);
+
+ for (c = 0; c < u->channels; c++) {
+ unsigned j;
+ float *p, *q;
+
+ p = src + c;
+ q = u->input;
+ for (j = 0; j < n; j++, p += u->channels, q++)
+ *q = CLAMP(*p, -1.0, 1.0);
+
+ u->descriptor->run(u->handle[c], n);
+
+ q = u->output;
+ p = dst + c;
+ for (j = 0; j < n; j++, q++, p += u->channels)
+ *p = CLAMP(*q, -1.0, 1.0);
+ }
+
+ pa_memblock_release(tchunk.memblock);
+ pa_memblock_release(u->memchunk.memblock);
+
+ pa_memblock_unref(tchunk.memblock);
+ }
+
+ pa_assert(u->memchunk.length > 0);
+ pa_assert(u->memchunk.memblock);
+
+ *chunk = u->memchunk;
+ pa_memblock_ref(chunk->memblock);
+
+ return 0;
+}
+
+/* Called from I/O thread context */
+static void sink_input_drop_cb(pa_sink_input *i, size_t length) {
+ struct userdata *u;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(u = i->userdata);
+ pa_assert(length > 0);
+
+ if (u->memchunk.memblock) {
+
+ if (length < u->memchunk.length) {
+ u->memchunk.index += length;
+ u->memchunk.length -= length;
+ return;
+ }
+
+ pa_memblock_unref(u->memchunk.memblock);
+ length -= u->memchunk.length;
+ pa_memchunk_reset(&u->memchunk);
+ }
+
+ if (length > 0)
+ pa_sink_skip(u->sink, length);
+}
+
+/* Called from I/O thread context */
+static void sink_input_detach_cb(pa_sink_input *i) {
+ struct userdata *u;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(u = i->userdata);
+
+ pa_sink_detach_within_thread(u->sink);
+}
+
+/* Called from I/O thread context */
+static void sink_input_attach_cb(pa_sink_input *i) {
+ struct userdata *u;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(u = i->userdata);
+
+ pa_sink_set_asyncmsgq(u->sink, i->sink->asyncmsgq);
+ pa_sink_set_rtpoll(u->sink, i->sink->rtpoll);
+
+ pa_sink_attach_within_thread(u->sink);
+}
+
+/* Called from main context */
+static void sink_input_kill_cb(pa_sink_input *i) {
+ struct userdata *u;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(u = i->userdata);
+
+ pa_sink_input_unlink(u->sink_input);
+ pa_sink_input_unref(u->sink_input);
+ u->sink_input = NULL;
+
+ pa_sink_unlink(u->sink);
+ pa_sink_unref(u->sink);
+ u->sink = NULL;
+
+ pa_module_unload_request(u->module);
+}
+
+int pa__init(pa_module*m) {
+ struct userdata *u;
+ pa_sample_spec ss;
+ pa_channel_map map;
+ pa_modargs *ma;
+ char *t;
+ pa_sink *master;
+ pa_sink_input_new_data data;
+ const char *plugin, *label;
+ LADSPA_Descriptor_Function descriptor_func;
+ const char *e, *cdata;
+ const LADSPA_Descriptor *d;
+ unsigned long input_port, output_port, p, j, n_control;
+ unsigned c;
+ pa_bool_t *use_default = NULL;
+ char *default_sink_name = NULL;
+
+ pa_assert(m);
+
+ pa_assert(sizeof(LADSPA_Data) == sizeof(float));
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log("Failed to parse module arguments.");
+ goto fail;
+ }
+
+ if (!(master = pa_namereg_get(m->core, pa_modargs_get_value(ma, "master", NULL), PA_NAMEREG_SINK, 1))) {
+ pa_log("Master sink not found");
+ goto fail;
+ }
+
+ ss = master->sample_spec;
+ ss.format = PA_SAMPLE_FLOAT32;
+ map = master->channel_map;
+ if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
+ pa_log("Invalid sample format specification or channel map");
+ goto fail;
+ }
+
+ if (!(plugin = pa_modargs_get_value(ma, "plugin", NULL))) {
+ pa_log("Missing LADSPA plugin name");
+ goto fail;
+ }
+
+ if (!(label = pa_modargs_get_value(ma, "label", NULL))) {
+ pa_log("Missing LADSPA plugin label");
+ goto fail;
+ }
+
+ cdata = pa_modargs_get_value(ma, "control", NULL);
+
+ u = pa_xnew0(struct userdata, 1);
+ u->core = m->core;
+ u->module = m;
+ m->userdata = u;
+ u->master = master;
+ pa_memchunk_reset(&u->memchunk);
+
+ if (!(e = getenv("LADSPA_PATH")))
+ e = LADSPA_PATH;
+
+ /* FIXME: This is not exactly thread safe */
+ t = pa_xstrdup(lt_dlgetsearchpath());
+ lt_dlsetsearchpath(e);
+ m->dl = lt_dlopenext(plugin);
+ lt_dlsetsearchpath(t);
+ pa_xfree(t);
+
+ if (!m->dl) {
+ pa_log("Failed to load LADSPA plugin: %s", lt_dlerror());
+ goto fail;
+ }
+
+ if (!(descriptor_func = (LADSPA_Descriptor_Function) lt_dlsym(m->dl, "ladspa_descriptor"))) {
+ pa_log("LADSPA module lacks ladspa_descriptor() symbol.");
+ goto fail;
+ }
+
+ for (j = 0;; j++) {
+
+ if (!(d = descriptor_func(j))) {
+ pa_log("Failed to find plugin label '%s' in plugin '%s'.", plugin, label);
+ goto fail;
+ }
+
+ if (strcmp(d->Label, label) == 0)
+ break;
+ }
+
+ u->descriptor = d;
+
+ pa_log_debug("Module: %s", plugin);
+ pa_log_debug("Label: %s", d->Label);
+ pa_log_debug("Unique ID: %lu", d->UniqueID);
+ pa_log_debug("Name: %s", d->Name);
+ pa_log_debug("Maker: %s", d->Maker);
+ pa_log_debug("Copyright: %s", d->Copyright);
+
+ input_port = output_port = (unsigned long) -1;
+ n_control = 0;
+
+ for (p = 0; p < d->PortCount; p++) {
+
+ if (LADSPA_IS_PORT_INPUT(d->PortDescriptors[p]) && LADSPA_IS_PORT_AUDIO(d->PortDescriptors[p])) {
+
+ 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]);
+ 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;
+ }
+
+ } 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_info("Ignored port \"%s\", because we ignore all control out ports.", d->PortNames[p]);
+ }
+ }
+
+ 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;
+ }
+
+ u->block_size = pa_frame_align(pa_mempool_block_size_max(m->core->mempool), &ss);
+
+ u->input = (LADSPA_Data*) pa_xnew(uint8_t, u->block_size);
+ if (LADSPA_IS_INPLACE_BROKEN(d->Properties))
+ u->output = (LADSPA_Data*) pa_xnew(uint8_t, u->block_size);
+ else
+ 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);
+ goto fail;
+ }
+
+ d->connect_port(u->handle[c], input_port, u->input);
+ d->connect_port(u->handle[c], output_port, u->output);
+ }
+
+ if (!cdata && n_control > 0) {
+ pa_log("This plugin requires specification of %lu control parameters.", n_control);
+ goto fail;
+ }
+
+ if (n_control > 0) {
+ const char *state = NULL;
+ char *k;
+ unsigned long h;
+
+ u->control = pa_xnew(LADSPA_Data, n_control);
+ use_default = pa_xnew(pa_bool_t, n_control);
+ p = 0;
+
+ while ((k = pa_split(cdata, ",", &state))) {
+ float f;
+
+ if (*k == 0) {
+ use_default[p++] = TRUE;
+ pa_xfree(k);
+ continue;
+ }
+
+ if (pa_atof(k, &f) < 0) {
+ pa_log("Failed to parse control value '%s'", k);
+ pa_xfree(k);
+ goto fail;
+ }
+
+ pa_xfree(k);
+
+ if (p >= n_control) {
+ pa_log("Too many control values passed, %lu expected.", n_control);
+ goto fail;
+ }
+
+ use_default[p] = FALSE;
+ u->control[p++] = f;
+ }
+
+ if (p < n_control) {
+ pa_log("Not enough control values passed, %lu expected, %lu passed.", n_control, p);
+ goto fail;
+ }
+
+ h = 0;
+ for (p = 0; p < d->PortCount; p++) {
+ LADSPA_PortRangeHintDescriptor hint = d->PortRangeHints[p].HintDescriptor;
+
+ if (!LADSPA_IS_PORT_CONTROL(d->PortDescriptors[p]))
+ continue;
+
+ if (LADSPA_IS_PORT_OUTPUT(d->PortDescriptors[p])) {
+ for (c = 0; c < ss.channels; c++)
+ d->connect_port(u->handle[c], p, &u->control_out);
+ continue;
+ }
+
+ pa_assert(h < n_control);
+
+ if (use_default[h]) {
+ LADSPA_Data lower, upper;
+
+ if (!LADSPA_IS_HINT_HAS_DEFAULT(hint)) {
+ pa_log("Control port value left empty but plugin defines no default.");
+ goto fail;
+ }
+
+ lower = d->PortRangeHints[p].LowerBound;
+ upper = d->PortRangeHints[p].UpperBound;
+
+ if (LADSPA_IS_HINT_SAMPLE_RATE(hint)) {
+ lower *= ss.rate;
+ upper *= ss.rate;
+ }
+
+ 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] = exp(log(lower) * 0.75 + log(upper) * 0.25);
+ else
+ u->control[h] = lower * 0.75 + upper * 0.25;
+ break;
+
+ case LADSPA_HINT_DEFAULT_MIDDLE:
+ if (LADSPA_IS_HINT_LOGARITHMIC(hint))
+ u->control[h] = exp(log(lower) * 0.5 + log(upper) * 0.5);
+ else
+ u->control[h] = lower * 0.5 + upper * 0.5;
+ break;
+
+ case LADSPA_HINT_DEFAULT_HIGH:
+ if (LADSPA_IS_HINT_LOGARITHMIC(hint))
+ u->control[h] = exp(log(lower) * 0.25 + log(upper) * 0.75);
+ else
+ u->control[h] = 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();
+ }
+ }
+
+ if (LADSPA_IS_HINT_INTEGER(hint))
+ u->control[h] = roundf(u->control[h]);
+
+ pa_log_debug("Binding %f to port %s", u->control[h], d->PortNames[p]);
+
+ for (c = 0; c < ss.channels; c++)
+ d->connect_port(u->handle[c], p, &u->control[h]);
+
+ h++;
+ }
+
+ pa_assert(h == n_control);
+ }
+
+ if (d->activate)
+ for (c = 0; c < u->channels; c++)
+ d->activate(u->handle[c]);
+
+ default_sink_name = pa_sprintf_malloc("%s.ladspa", master->name);
+
+ /* Create sink */
+ if (!(u->sink = pa_sink_new(m->core, __FILE__, pa_modargs_get_value(ma, "sink_name", default_sink_name), 0, &ss, &map))) {
+ pa_log("Failed to create sink.");
+ goto fail;
+ }
+
+ u->sink->parent.process_msg = sink_process_msg;
+ u->sink->set_state = sink_set_state;
+ u->sink->userdata = u;
+ u->sink->flags = PA_SINK_LATENCY;
+
+ pa_sink_set_module(u->sink, m);
+ pa_sink_set_description(u->sink, t = pa_sprintf_malloc("LADSPA plugin '%s' on '%s'", label, master->description));
+ pa_xfree(t);
+ pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq);
+ pa_sink_set_rtpoll(u->sink, master->rtpoll);
+
+ /* Create sink input */
+ pa_sink_input_new_data_init(&data);
+ data.sink = u->master;
+ data.driver = __FILE__;
+ data.name = "LADSPA Stream";
+ pa_sink_input_new_data_set_sample_spec(&data, &ss);
+ pa_sink_input_new_data_set_channel_map(&data, &map);
+ data.module = m;
+
+ if (!(u->sink_input = pa_sink_input_new(m->core, &data, PA_SINK_INPUT_DONT_MOVE)))
+ goto fail;
+
+ u->sink_input->parent.process_msg = sink_input_process_msg;
+ u->sink_input->peek = sink_input_peek_cb;
+ u->sink_input->drop = sink_input_drop_cb;
+ u->sink_input->kill = sink_input_kill_cb;
+ u->sink_input->attach = sink_input_attach_cb;
+ u->sink_input->detach = sink_input_detach_cb;
+ u->sink_input->userdata = u;
+
+ pa_sink_put(u->sink);
+ pa_sink_input_put(u->sink_input);
+
+ pa_modargs_free(ma);
+
+ pa_xfree(use_default);
+ pa_xfree(default_sink_name);
+
+ return 0;
+
+fail:
+ if (ma)
+ pa_modargs_free(ma);
+
+ pa_xfree(use_default);
+ pa_xfree(default_sink_name);
+
+ pa__done(m);
+
+ return -1;
+}
+
+void pa__done(pa_module*m) {
+ struct userdata *u;
+ unsigned c;
+
+ pa_assert(m);
+
+ if (!(u = m->userdata))
+ return;
+
+ if (u->sink_input) {
+ pa_sink_input_unlink(u->sink_input);
+ pa_sink_input_unref(u->sink_input);
+ }
+
+ if (u->sink) {
+ pa_sink_unlink(u->sink);
+ pa_sink_unref(u->sink);
+ }
+
+ if (u->memchunk.memblock)
+ pa_memblock_unref(u->memchunk.memblock);
+
+ for (c = 0; c < u->channels; 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);
+
+ pa_xfree(u->input);
+
+ pa_xfree(u->control);
+
+ pa_xfree(u);
+}
diff --git a/src/modules/module-lirc.c b/src/modules/module-lirc.c
index c8adbc8b..21d93837 100644
--- a/src/modules/module-lirc.c
+++ b/src/modules/module-lirc.c
@@ -26,12 +26,12 @@
#endif
#include <stdio.h>
-#include <assert.h>
#include <unistd.h>
#include <string.h>
-#include <lirc/lirc_client.h>
#include <stdlib.h>
+#include <lirc/lirc_client.h>
+
#include <pulse/xmalloc.h>
#include <pulsecore/module.h>
@@ -39,6 +39,7 @@
#include <pulsecore/namereg.h>
#include <pulsecore/sink.h>
#include <pulsecore/modargs.h>
+#include <pulsecore/macro.h>
#include "module-lirc-symdef.h"
@@ -68,11 +69,12 @@ static int lirc_in_use = 0;
static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GCC_UNUSED int fd, pa_io_event_flags_t events, void*userdata) {
struct userdata *u = userdata;
char *name = NULL, *code = NULL;
- assert(io);
- assert(u);
+
+ pa_assert(io);
+ pa_assert(u);
if (events & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) {
- pa_log("lost connection to LIRC daemon.");
+ pa_log("Lost connection to LIRC daemon.");
goto fail;
}
@@ -86,7 +88,7 @@ static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GC
c = pa_xstrdup(code);
c[strcspn(c, "\n\r")] = 0;
- pa_log_debug("raw IR code '%s'", c);
+ pa_log_debug("Raw IR code '%s'", c);
pa_xfree(c);
while (lirc_code2char(u->config, code, &name) == 0 && name) {
@@ -99,7 +101,7 @@ static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GC
MUTE_TOGGLE
} volchange = INVALID;
- pa_log_info("translated IR code '%s'", name);
+ pa_log_info("Translated IR code '%s'", name);
if (strcasecmp(name, "volume-up") == 0)
volchange = UP;
@@ -113,15 +115,15 @@ static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GC
volchange = RESET;
if (volchange == INVALID)
- pa_log_warn("recieved unknown IR code '%s'", name);
+ pa_log_warn("Recieved unknown IR code '%s'", name);
else {
pa_sink *s;
if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, 1)))
- pa_log("failed to get sink '%s'", u->sink_name);
+ pa_log("Failed to get sink '%s'", u->sink_name);
else {
int i;
- pa_cvolume cv = *pa_sink_get_volume(s, PA_MIXER_HARDWARE);
+ pa_cvolume cv = *pa_sink_get_volume(s);
#define DELTA (PA_VOLUME_NORM/20)
@@ -134,7 +136,7 @@ static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GC
cv.values[i] = PA_VOLUME_NORM;
}
- pa_sink_set_volume(s, PA_MIXER_HARDWARE, &cv);
+ pa_sink_set_volume(s, &cv);
break;
case DOWN:
@@ -145,20 +147,20 @@ static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GC
cv.values[i] = PA_VOLUME_MUTED;
}
- pa_sink_set_volume(s, PA_MIXER_HARDWARE, &cv);
+ pa_sink_set_volume(s, &cv);
break;
case MUTE:
- pa_sink_set_mute(s, PA_MIXER_HARDWARE, 0);
+ pa_sink_set_mute(s, 0);
break;
case RESET:
- pa_sink_set_mute(s, PA_MIXER_HARDWARE, 1);
+ pa_sink_set_mute(s, 1);
break;
case MUTE_TOGGLE:
- pa_sink_set_mute(s, PA_MIXER_HARDWARE, !pa_sink_get_mute(s, PA_MIXER_HARDWARE));
+ pa_sink_set_mute(s, !pa_sink_get_mute(s));
break;
case INVALID:
@@ -179,13 +181,14 @@ fail:
pa_module_unload_request(u->module);
- free(code);
+ pa_xfree(code);
}
-int pa__init(pa_core *c, pa_module*m) {
+int pa__init(pa_module*m) {
pa_modargs *ma = NULL;
struct userdata *u;
- assert(c && m);
+
+ pa_assert(m);
if (lirc_in_use) {
pa_log("module-lirc may no be loaded twice.");
@@ -197,7 +200,7 @@ int pa__init(pa_core *c, pa_module*m) {
goto fail;
}
- m->userdata = u = pa_xmalloc(sizeof(struct userdata));
+ m->userdata = u = pa_xnew(struct userdata, 1);
u->module = m;
u->io = NULL;
u->config = NULL;
@@ -215,7 +218,7 @@ int pa__init(pa_core *c, pa_module*m) {
goto fail;
}
- u->io = c->mainloop->io_new(c->mainloop, u->lirc_fd, PA_IO_EVENT_INPUT|PA_IO_EVENT_HANGUP, io_callback, u);
+ u->io = m->core->mainloop->io_new(m->core->mainloop, u->lirc_fd, PA_IO_EVENT_INPUT|PA_IO_EVENT_HANGUP, io_callback, u);
lirc_in_use = 1;
@@ -228,14 +231,13 @@ fail:
if (ma)
pa_modargs_free(ma);
- pa__done(c, m);
+ pa__done(m);
return -1;
}
-void pa__done(pa_core *c, pa_module*m) {
+void pa__done(pa_module*m) {
struct userdata *u;
- assert(c);
- assert(m);
+ pa_assert(m);
if (!(u = m->userdata))
return;
diff --git a/src/modules/module-match.c b/src/modules/module-match.c
index 0b051fac..0155b2af 100644
--- a/src/modules/module-match.c
+++ b/src/modules/module-match.c
@@ -26,7 +26,6 @@
#endif
#include <unistd.h>
-#include <assert.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
@@ -80,6 +79,8 @@ static int load_rules(struct userdata *u, const char *filename) {
struct rule *end = NULL;
char *fn = NULL;
+ pa_assert(u);
+
f = filename ?
fopen(fn = pa_xstrdup(filename), "r") :
pa_open_config_file(DEFAULT_MATCH_TABLE_FILE, DEFAULT_MATCH_TABLE_FILE_USER, NULL, &fn, "r");
@@ -132,7 +133,7 @@ static int load_rules(struct userdata *u, const char *filename) {
goto finish;
}
- rule = pa_xmalloc(sizeof(struct rule));
+ rule = pa_xnew(struct rule, 1);
rule->regex = regex;
rule->volume = volume;
rule->next = NULL;
@@ -164,7 +165,9 @@ static void callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, v
struct userdata *u = userdata;
pa_sink_input *si;
struct rule *r;
- assert(c && u);
+
+ pa_assert(c);
+ pa_assert(u);
if (t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW))
return;
@@ -179,23 +182,24 @@ static void callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, v
if (!regexec(&r->regex, si->name, 0, NULL, 0)) {
pa_cvolume cv;
pa_log_debug("changing volume of sink input '%s' to 0x%03x", si->name, r->volume);
- pa_cvolume_set(&cv, r->volume, si->sample_spec.channels);
+ pa_cvolume_set(&cv, si->sample_spec.channels, r->volume);
pa_sink_input_set_volume(si, &cv);
}
}
}
-int pa__init(pa_core *c, pa_module*m) {
+int pa__init(pa_module*m) {
pa_modargs *ma = NULL;
struct userdata *u;
- assert(c && m);
+
+ pa_assert(m);
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
pa_log("Failed to parse module arguments");
goto fail;
}
- u = pa_xmalloc(sizeof(struct userdata));
+ u = pa_xnew(struct userdata, 1);
u->rules = NULL;
u->subscription = NULL;
m->userdata = u;
@@ -203,23 +207,24 @@ int pa__init(pa_core *c, pa_module*m) {
if (load_rules(u, pa_modargs_get_value(ma, "table", NULL)) < 0)
goto fail;
- u->subscription = pa_subscription_new(c, PA_SUBSCRIPTION_MASK_SINK_INPUT, callback, u);
+ u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK_INPUT, callback, u);
pa_modargs_free(ma);
return 0;
fail:
- pa__done(c, m);
+ pa__done(m);
if (ma)
pa_modargs_free(ma);
return -1;
}
-void pa__done(pa_core *c, pa_module*m) {
+void pa__done(pa_module*m) {
struct userdata* u;
struct rule *r, *n;
- assert(c && m);
+
+ pa_assert(m);
if (!(u = m->userdata))
return;
diff --git a/src/modules/module-mmkbd-evdev.c b/src/modules/module-mmkbd-evdev.c
index b7433ac8..47a10645 100644
--- a/src/modules/module-mmkbd-evdev.c
+++ b/src/modules/module-mmkbd-evdev.c
@@ -26,7 +26,6 @@
#endif
#include <stdio.h>
-#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
@@ -80,11 +79,12 @@ struct userdata {
static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GCC_UNUSED int fd, pa_io_event_flags_t events, void*userdata) {
struct userdata *u = userdata;
- assert(io);
- assert(u);
+
+ pa_assert(io);
+ pa_assert(u);
if (events & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) {
- pa_log("lost connection to evdev device.");
+ pa_log("Lost connection to evdev device.");
goto fail;
}
@@ -92,14 +92,14 @@ static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GC
struct input_event ev;
if (pa_loop_read(u->fd, &ev, sizeof(ev), &u->fd_type) <= 0) {
- pa_log("failed to read from event device: %s", pa_cstrerror(errno));
+ pa_log("Failed to read from event device: %s", pa_cstrerror(errno));
goto fail;
}
if (ev.type == EV_KEY && (ev.value == 1 || ev.value == 2)) {
enum { INVALID, UP, DOWN, MUTE_TOGGLE } volchange = INVALID;
- pa_log_debug("key code=%u, value=%u", ev.code, ev.value);
+ pa_log_debug("Key code=%u, value=%u", ev.code, ev.value);
switch (ev.code) {
case KEY_VOLUMEDOWN: volchange = DOWN; break;
@@ -111,10 +111,10 @@ static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GC
pa_sink *s;
if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, 1)))
- pa_log("failed to get sink '%s'", u->sink_name);
+ pa_log("Failed to get sink '%s'", u->sink_name);
else {
int i;
- pa_cvolume cv = *pa_sink_get_volume(s, PA_MIXER_HARDWARE);
+ pa_cvolume cv = *pa_sink_get_volume(s);
#define DELTA (PA_VOLUME_NORM/20)
@@ -127,7 +127,7 @@ static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GC
cv.values[i] = PA_VOLUME_NORM;
}
- pa_sink_set_volume(s, PA_MIXER_HARDWARE, &cv);
+ pa_sink_set_volume(s, &cv);
break;
case DOWN:
@@ -138,12 +138,12 @@ static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GC
cv.values[i] = PA_VOLUME_MUTED;
}
- pa_sink_set_volume(s, PA_MIXER_HARDWARE, &cv);
+ pa_sink_set_volume(s, &cv);
break;
case MUTE_TOGGLE:
- pa_sink_set_mute(s, PA_MIXER_HARDWARE, !pa_sink_get_mute(s, PA_MIXER_HARDWARE));
+ pa_sink_set_mute(s, !pa_sink_get_mute(s));
break;
case INVALID:
@@ -165,21 +165,23 @@ fail:
#define test_bit(bit, array) (array[bit/8] & (1<<(bit%8)))
-int pa__init(pa_core *c, pa_module*m) {
+int pa__init(pa_module*m) {
+
pa_modargs *ma = NULL;
struct userdata *u;
int version;
struct _input_id input_id;
char name[256];
uint8_t evtype_bitmask[EV_MAX/8 + 1];
- assert(c && m);
+
+ pa_assert(m);
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
pa_log("Failed to parse module arguments");
goto fail;
}
- m->userdata = u = pa_xmalloc(sizeof(struct userdata));
+ m->userdata = u = pa_xnew(struct userdata,1);
u->module = m;
u->io = NULL;
u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
@@ -221,11 +223,11 @@ int pa__init(pa_core *c, pa_module*m) {
}
if (!test_bit(EV_KEY, evtype_bitmask)) {
- pa_log("device has no keys.");
+ pa_log("Device has no keys.");
goto fail;
}
- u->io = c->mainloop->io_new(c->mainloop, u->fd, PA_IO_EVENT_INPUT|PA_IO_EVENT_HANGUP, io_callback, u);
+ u->io = m->core->mainloop->io_new(m->core->mainloop, u->fd, PA_IO_EVENT_INPUT|PA_IO_EVENT_HANGUP, io_callback, u);
pa_modargs_free(ma);
@@ -236,14 +238,14 @@ fail:
if (ma)
pa_modargs_free(ma);
- pa__done(c, m);
+ pa__done(m);
return -1;
}
-void pa__done(pa_core *c, pa_module*m) {
+void pa__done(pa_module*m) {
struct userdata *u;
- assert(c);
- assert(m);
+
+ pa_assert(m);
if (!(u = m->userdata))
return;
@@ -252,7 +254,7 @@ void pa__done(pa_core *c, pa_module*m) {
m->core->mainloop->io_free(u->io);
if (u->fd >= 0)
- close(u->fd);
+ pa_assert_se(pa_close(u->fd) == 0);
pa_xfree(u->sink_name);
pa_xfree(u);
diff --git a/src/modules/module-native-protocol-fd.c b/src/modules/module-native-protocol-fd.c
index 3c1c2bca..2ef61c88 100644
--- a/src/modules/module-native-protocol-fd.c
+++ b/src/modules/module-native-protocol-fd.c
@@ -26,10 +26,10 @@
#endif
#include <stdio.h>
-#include <assert.h>
#include <unistd.h>
#include <pulsecore/module.h>
+#include <pulsecore/macro.h>
#include <pulsecore/iochannel.h>
#include <pulsecore/modargs.h>
#include <pulsecore/protocol-native.h>
@@ -48,25 +48,26 @@ static const char* const valid_modargs[] = {
NULL,
};
-int pa__init(pa_core *c, pa_module*m) {
+int pa__init(pa_module*m) {
pa_iochannel *io;
pa_modargs *ma;
int fd, r = -1;
- assert(c && m);
+
+ pa_assert(m);
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
- pa_log("failed to parse module arguments.");
+ pa_log("Failed to parse module arguments.");
goto finish;
}
if (pa_modargs_get_value_s32(ma, "fd", &fd) < 0) {
- pa_log("invalid file descriptor.");
+ pa_log("Invalid file descriptor.");
goto finish;
}
- io = pa_iochannel_new(c->mainloop, fd, fd);
+ io = pa_iochannel_new(m->core->mainloop, fd, fd);
- if (!(m->userdata = pa_protocol_native_new_iochannel(c, io, m, ma))) {
+ if (!(m->userdata = pa_protocol_native_new_iochannel(m->core, io, m, ma))) {
pa_iochannel_free(io);
goto finish;
}
@@ -80,8 +81,8 @@ finish:
return r;
}
-void pa__done(pa_core *c, pa_module*m) {
- assert(c && m);
+void pa__done(pa_module*m) {
+ pa_assert(m);
pa_protocol_native_free(m->userdata);
}
diff --git a/src/modules/module-null-sink.c b/src/modules/module-null-sink.c
index 54a8e890..3a4edec7 100644
--- a/src/modules/module-null-sink.c
+++ b/src/modules/module-null-sink.c
@@ -28,7 +28,6 @@
#include <stdlib.h>
#include <sys/stat.h>
#include <stdio.h>
-#include <assert.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
@@ -38,12 +37,17 @@
#include <pulse/timeval.h>
#include <pulse/xmalloc.h>
-#include <pulsecore/iochannel.h>
+#include <pulsecore/macro.h>
#include <pulsecore/sink.h>
#include <pulsecore/module.h>
#include <pulsecore/core-util.h>
+#include <pulsecore/core-error.h>
#include <pulsecore/modargs.h>
#include <pulsecore/log.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/rtclock.h>
#include "module-null-sink-symdef.h"
@@ -64,11 +68,14 @@ struct userdata {
pa_core *core;
pa_module *module;
pa_sink *sink;
- pa_time_event *time_event;
+
+ pa_thread *thread;
+ pa_thread_mq thread_mq;
+ pa_rtpoll *rtpoll;
+
size_t block_size;
- uint64_t n_bytes;
- struct timeval start_time;
+ struct timeval timestamp;
};
static const char* const valid_modargs[] = {
@@ -81,78 +88,132 @@ static const char* const valid_modargs[] = {
NULL
};
-static void time_callback(pa_mainloop_api *m, pa_time_event*e, const struct timeval *tv, void *userdata) {
- struct userdata *u = userdata;
- pa_memchunk chunk;
- struct timeval ntv = *tv;
- size_t l;
+static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+ struct userdata *u = PA_SINK(o)->userdata;
+
+ switch (code) {
+ case PA_SINK_MESSAGE_SET_STATE:
- assert(u);
+ if (PA_PTR_TO_UINT(data) == PA_SINK_RUNNING)
+ pa_rtclock_get(&u->timestamp);
- if (pa_sink_render(u->sink, u->block_size, &chunk) >= 0) {
- l = chunk.length;
- pa_memblock_unref(chunk.memblock);
- } else
- l = u->block_size;
+ break;
- pa_timeval_add(&ntv, pa_bytes_to_usec(l, &u->sink->sample_spec));
- m->time_restart(e, &ntv);
+ case PA_SINK_MESSAGE_GET_LATENCY: {
+ struct timeval now;
- u->n_bytes += l;
+ pa_rtclock_get(&now);
+
+ if (pa_timeval_cmp(&u->timestamp, &now) > 0)
+ *((pa_usec_t*) data) = 0;
+ else
+ *((pa_usec_t*) data) = pa_timeval_diff(&u->timestamp, &now);
+ break;
+ }
+ }
+
+ return pa_sink_process_msg(o, code, data, offset, chunk);
}
-static pa_usec_t get_latency(pa_sink *s) {
- struct userdata *u = s->userdata;
- pa_usec_t a, b;
- struct timeval now;
+static void thread_func(void *userdata) {
+ struct userdata *u = userdata;
+
+ pa_assert(u);
+
+ pa_log_debug("Thread starting up");
+
+ pa_thread_mq_install(&u->thread_mq);
+ pa_rtpoll_install(u->rtpoll);
+
+ pa_rtclock_get(&u->timestamp);
+
+ for (;;) {
+ int ret;
+
+ /* Render some data and drop it immediately */
+ if (u->sink->thread_info.state == PA_SINK_RUNNING) {
+ struct timeval now;
+
+ pa_rtclock_get(&now);
- a = pa_timeval_diff(pa_gettimeofday(&now), &u->start_time);
- b = pa_bytes_to_usec(u->n_bytes, &s->sample_spec);
+ if (pa_timeval_cmp(&u->timestamp, &now) <= 0) {
+ pa_sink_skip(u->sink, u->block_size);
+ pa_timeval_add(&u->timestamp, pa_bytes_to_usec(u->block_size, &u->sink->sample_spec));
+ }
- return b > a ? b - a : 0;
+ pa_rtpoll_set_timer_absolute(u->rtpoll, &u->timestamp);
+ } else
+ pa_rtpoll_set_timer_disabled(u->rtpoll);
+
+ /* Hmm, nothing to do. Let's sleep */
+ if ((ret = pa_rtpoll_run(u->rtpoll, 1)) < 0)
+ goto fail;
+
+ if (ret == 0)
+ goto finish;
+ }
+
+fail:
+ /* If this was no regular exit from the loop we have to continue
+ * processing messages until we received PA_MESSAGE_SHUTDOWN */
+ pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+ pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+ pa_log_debug("Thread shutting down");
}
-int pa__init(pa_core *c, pa_module*m) {
+int pa__init(pa_module*m) {
struct userdata *u = NULL;
pa_sample_spec ss;
pa_channel_map map;
pa_modargs *ma = NULL;
- assert(c);
- assert(m);
+ pa_assert(m);
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
- pa_log("failed to parse module arguments.");
+ pa_log("Failed to parse module arguments.");
goto fail;
}
- ss = c->default_sample_spec;
+ ss = m->core->default_sample_spec;
if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
- pa_log("invalid sample format specification or channel map.");
+ pa_log("Invalid sample format specification or channel map");
goto fail;
}
u = pa_xnew0(struct userdata, 1);
- u->core = c;
+ u->core = m->core;
u->module = m;
m->userdata = u;
+ pa_thread_mq_init(&u->thread_mq, m->core->mainloop);
+ u->rtpoll = pa_rtpoll_new();
+ pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq);
- if (!(u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, &map))) {
- pa_log("failed to create sink.");
+ if (!(u->sink = pa_sink_new(m->core, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, &map))) {
+ pa_log("Failed to create sink.");
goto fail;
}
- u->sink->get_latency = get_latency;
+ u->sink->parent.process_msg = sink_process_msg;
u->sink->userdata = u;
- pa_sink_set_owner(u->sink, m);
+ u->sink->flags = PA_SINK_LATENCY;
+
+ pa_sink_set_module(u->sink, m);
+ pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
+ pa_sink_set_rtpoll(u->sink, u->rtpoll);
pa_sink_set_description(u->sink, pa_modargs_get_value(ma, "description", "NULL sink"));
- u->n_bytes = 0;
- pa_gettimeofday(&u->start_time);
+ u->block_size = pa_bytes_per_second(&ss) / 20; /* 50 ms */
+ if (u->block_size <= 0)
+ u->block_size = pa_frame_size(&ss);
- u->time_event = c->mainloop->time_new(c->mainloop, &u->start_time, time_callback, u);
+ if (!(u->thread = pa_thread_new(thread_func, u))) {
+ pa_log("Failed to create thread.");
+ goto fail;
+ }
- u->block_size = pa_bytes_per_second(&ss) / 10;
+ pa_sink_put(u->sink);
pa_modargs_free(ma);
@@ -162,22 +223,34 @@ fail:
if (ma)
pa_modargs_free(ma);
- pa__done(c, m);
+ pa__done(m);
return -1;
}
-void pa__done(pa_core *c, pa_module*m) {
+void pa__done(pa_module*m) {
struct userdata *u;
- assert(c && m);
+
+ pa_assert(m);
if (!(u = m->userdata))
return;
- pa_sink_disconnect(u->sink);
- pa_sink_unref(u->sink);
+ if (u->sink)
+ pa_sink_unlink(u->sink);
+
+ if (u->thread) {
+ pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+ pa_thread_free(u->thread);
+ }
+
+ pa_thread_mq_done(&u->thread_mq);
+
+ if (u->sink)
+ pa_sink_unref(u->sink);
- u->core->mainloop->time_free(u->time_event);
+ if (u->rtpoll)
+ pa_rtpoll_free(u->rtpoll);
pa_xfree(u);
}
diff --git a/src/modules/module-oss-mmap.c b/src/modules/module-oss-mmap.c
deleted file mode 100644
index 16c9b311..00000000
--- a/src/modules/module-oss-mmap.c
+++ /dev/null
@@ -1,637 +0,0 @@
-/* $Id$ */
-
-/***
- This file is part of PulseAudio.
-
- Copyright 2004-2006 Lennart Poettering
- Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
-
- PulseAudio is free software; you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published
- by the Free Software Foundation; either version 2 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 <sys/soundcard.h>
-#include <sys/ioctl.h>
-#include <stdlib.h>
-#include <sys/stat.h>
-#include <stdio.h>
-#include <assert.h>
-#include <errno.h>
-#include <string.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <limits.h>
-
-#ifdef HAVE_SYS_MMAN_H
-#include <sys/mman.h>
-#endif
-
-#include <pulse/xmalloc.h>
-#include <pulse/util.h>
-
-#include <pulsecore/core-error.h>
-#include <pulsecore/iochannel.h>
-#include <pulsecore/sink.h>
-#include <pulsecore/source.h>
-#include <pulsecore/module.h>
-#include <pulsecore/sample-util.h>
-#include <pulsecore/core-util.h>
-#include <pulsecore/modargs.h>
-#include <pulsecore/log.h>
-
-#include "oss-util.h"
-#include "module-oss-mmap-symdef.h"
-
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("OSS Sink/Source (mmap)")
-PA_MODULE_VERSION(PACKAGE_VERSION)
-PA_MODULE_USAGE(
- "sink_name=<name for the sink> "
- "source_name=<name for the source> "
- "device=<OSS device> "
- "record=<enable source?> "
- "playback=<enable sink?> "
- "format=<sample format> "
- "channels=<number of channels> "
- "rate=<sample rate> "
- "fragments=<number of fragments> "
- "fragment_size=<fragment size> "
- "channel_map=<channel map>")
-
-struct userdata {
- pa_sink *sink;
- pa_source *source;
- pa_core *core;
- pa_sample_spec sample_spec;
-
- size_t in_fragment_size, out_fragment_size;
- unsigned in_fragments, out_fragments;
- unsigned out_blocks_saved, in_blocks_saved;
-
- int fd;
-
- void *in_mmap, *out_mmap;
- size_t in_mmap_length, out_mmap_length;
-
- pa_io_event *io_event;
-
- pa_memblock **in_memblocks, **out_memblocks;
- unsigned out_current, in_current;
- pa_module *module;
-};
-
-static const char* const valid_modargs[] = {
- "sink_name",
- "source_name",
- "device",
- "record",
- "playback",
- "fragments",
- "fragment_size",
- "format",
- "rate",
- "channels",
- "channel_map",
- NULL
-};
-
-#define DEFAULT_DEVICE "/dev/dsp"
-#define DEFAULT_NFRAGS 12
-#define DEFAULT_FRAGSIZE 1024
-
-static void update_usage(struct userdata *u) {
- pa_module_set_used(u->module,
- (u->sink ? pa_sink_used_by(u->sink) : 0) +
- (u->source ? pa_source_used_by(u->source) : 0));
-}
-
-static void clear_up(struct userdata *u) {
- assert(u);
-
- if (u->sink) {
- pa_sink_disconnect(u->sink);
- pa_sink_unref(u->sink);
- u->sink = NULL;
- }
-
- if (u->source) {
- pa_source_disconnect(u->source);
- pa_source_unref(u->source);
- u->source = NULL;
- }
-
- if (u->in_mmap && u->in_mmap != MAP_FAILED) {
- munmap(u->in_mmap, u->in_mmap_length);
- u->in_mmap = NULL;
- }
-
- if (u->out_mmap && u->out_mmap != MAP_FAILED) {
- munmap(u->out_mmap, u->out_mmap_length);
- u->out_mmap = NULL;
- }
-
- if (u->io_event) {
- u->core->mainloop->io_free(u->io_event);
- u->io_event = NULL;
- }
-
- if (u->fd >= 0) {
- close(u->fd);
- u->fd = -1;
- }
-}
-
-static void out_fill_memblocks(struct userdata *u, unsigned n) {
- assert(u && u->out_memblocks);
-
- while (n > 0) {
- pa_memchunk chunk;
-
- if (u->out_memblocks[u->out_current])
- pa_memblock_unref_fixed(u->out_memblocks[u->out_current]);
-
- chunk.memblock = u->out_memblocks[u->out_current] =
- pa_memblock_new_fixed(
- u->core->mempool,
- (uint8_t*) u->out_mmap+u->out_fragment_size*u->out_current,
- u->out_fragment_size,
- 1);
- assert(chunk.memblock);
- chunk.length = chunk.memblock->length;
- chunk.index = 0;
-
- pa_sink_render_into_full(u->sink, &chunk);
-
- u->out_current++;
- while (u->out_current >= u->out_fragments)
- u->out_current -= u->out_fragments;
-
- n--;
- }
-}
-
-static void do_write(struct userdata *u) {
- struct count_info info;
- assert(u && u->sink);
-
- update_usage(u);
-
- if (ioctl(u->fd, SNDCTL_DSP_GETOPTR, &info) < 0) {
- pa_log("SNDCTL_DSP_GETOPTR: %s", pa_cstrerror(errno));
-
- clear_up(u);
- pa_module_unload_request(u->module);
- return;
- }
-
- info.blocks += u->out_blocks_saved;
- u->out_blocks_saved = 0;
-
- if (!info.blocks)
- return;
-
- out_fill_memblocks(u, info.blocks);
-}
-
-static void in_post_memblocks(struct userdata *u, unsigned n) {
- assert(u && u->in_memblocks);
-
- while (n > 0) {
- pa_memchunk chunk;
-
- if (!u->in_memblocks[u->in_current]) {
- chunk.memblock = u->in_memblocks[u->in_current] = pa_memblock_new_fixed(u->core->mempool, (uint8_t*) u->in_mmap+u->in_fragment_size*u->in_current, u->in_fragment_size, 1);
- chunk.length = chunk.memblock->length;
- chunk.index = 0;
-
- pa_source_post(u->source, &chunk);
- }
-
- u->in_current++;
- while (u->in_current >= u->in_fragments)
- u->in_current -= u->in_fragments;
-
- n--;
- }
-}
-
-static void in_clear_memblocks(struct userdata*u, unsigned n) {
- unsigned i = u->in_current;
- assert(u && u->in_memblocks);
-
- if (n > u->in_fragments)
- n = u->in_fragments;
-
- while (n > 0) {
- if (u->in_memblocks[i]) {
- pa_memblock_unref_fixed(u->in_memblocks[i]);
- u->in_memblocks[i] = NULL;
- }
-
- i++;
- while (i >= u->in_fragments)
- i -= u->in_fragments;
-
- n--;
- }
-}
-
-static void do_read(struct userdata *u) {
- struct count_info info;
- assert(u && u->source);
-
- update_usage(u);
-
- if (ioctl(u->fd, SNDCTL_DSP_GETIPTR, &info) < 0) {
- pa_log("SNDCTL_DSP_GETIPTR: %s", pa_cstrerror(errno));
-
- clear_up(u);
- pa_module_unload_request(u->module);
- return;
- }
-
- info.blocks += u->in_blocks_saved;
- u->in_blocks_saved = 0;
-
- if (!info.blocks)
- return;
-
- in_post_memblocks(u, info.blocks);
- in_clear_memblocks(u, u->in_fragments/2);
-}
-
-static void io_callback(pa_mainloop_api *m, pa_io_event *e, PA_GCC_UNUSED int fd, pa_io_event_flags_t f, void *userdata) {
- struct userdata *u = userdata;
- assert (u && u->core->mainloop == m && u->io_event == e);
-
- if (f & PA_IO_EVENT_ERROR) {
- clear_up(u);
- pa_module_unload_request(u->module);
- return;
- }
-
- if (f & PA_IO_EVENT_INPUT)
- do_read(u);
- if (f & PA_IO_EVENT_OUTPUT)
- do_write(u);
-}
-
-static pa_usec_t sink_get_latency_cb(pa_sink *s) {
- struct userdata *u = s->userdata;
- struct count_info info;
- size_t bpos, n, total;
- assert(s && u);
-
- if (ioctl(u->fd, SNDCTL_DSP_GETOPTR, &info) < 0) {
- pa_log("SNDCTL_DSP_GETOPTR: %s", pa_cstrerror(errno));
- return 0;
- }
-
- u->out_blocks_saved += info.blocks;
-
- total = u->out_fragments * u->out_fragment_size;
- bpos = ((u->out_current + u->out_blocks_saved) * u->out_fragment_size) % total;
-
- if (bpos <= (size_t) info.ptr)
- n = total - (info.ptr - bpos);
- else
- n = bpos - info.ptr;
-
-/* pa_log("n = %u, bpos = %u, ptr = %u, total=%u, fragsize = %u, n_frags = %u\n", n, bpos, (unsigned) info.ptr, total, u->out_fragment_size, u->out_fragments); */
-
- return pa_bytes_to_usec(n, &s->sample_spec);
-}
-
-static pa_usec_t source_get_latency_cb(pa_source *s) {
- struct userdata *u = s->userdata;
- struct count_info info;
- size_t bpos, n, total;
- assert(s && u);
-
- if (ioctl(u->fd, SNDCTL_DSP_GETIPTR, &info) < 0) {
- pa_log("SNDCTL_DSP_GETIPTR: %s", pa_cstrerror(errno));
- return 0;
- }
-
- u->in_blocks_saved += info.blocks;
-
- total = u->in_fragments * u->in_fragment_size;
- bpos = ((u->in_current + u->in_blocks_saved) * u->in_fragment_size) % total;
-
- if (bpos <= (size_t) info.ptr)
- n = info.ptr - bpos;
- else
- n = (u->in_fragments * u->in_fragment_size) - bpos + info.ptr;
-
-/* pa_log("n = %u, bpos = %u, ptr = %u, total=%u, fragsize = %u, n_frags = %u\n", n, bpos, (unsigned) info.ptr, total, u->in_fragment_size, u->in_fragments); */
-
- return pa_bytes_to_usec(n, &s->sample_spec);
-}
-
-static int sink_get_hw_volume(pa_sink *s) {
- struct userdata *u = s->userdata;
-
- if (pa_oss_get_pcm_volume(u->fd, &s->sample_spec, &s->hw_volume) < 0) {
- pa_log_info("device doesn't support reading mixer settings: %s", pa_cstrerror(errno));
- s->get_hw_volume = NULL;
- return -1;
- }
-
- return 0;
-}
-
-static int sink_set_hw_volume(pa_sink *s) {
- struct userdata *u = s->userdata;
-
- if (pa_oss_set_pcm_volume(u->fd, &s->sample_spec, &s->hw_volume) < 0) {
- pa_log_info("device doesn't support writing mixer settings: %s", pa_cstrerror(errno));
- s->set_hw_volume = NULL;
- return -1;
- }
-
- return 0;
-}
-
-static int source_get_hw_volume(pa_source *s) {
- struct userdata *u = s->userdata;
-
- if (pa_oss_get_input_volume(u->fd, &s->sample_spec, &s->hw_volume) < 0) {
- pa_log_info("device doesn't support reading mixer settings: %s", pa_cstrerror(errno));
- s->get_hw_volume = NULL;
- return -1;
- }
-
- return 0;
-}
-
-static int source_set_hw_volume(pa_source *s) {
- struct userdata *u = s->userdata;
-
- if (pa_oss_set_input_volume(u->fd, &s->sample_spec, &s->hw_volume) < 0) {
- pa_log_info("device doesn't support writing mixer settings: %s", pa_cstrerror(errno));
- s->set_hw_volume = NULL;
- return -1;
- }
-
- return 0;
-}
-
-int pa__init(pa_core *c, pa_module*m) {
- struct audio_buf_info info;
- struct userdata *u = NULL;
- const char *p;
- int nfrags, frag_size;
- int mode, caps;
- int enable_bits = 0, zero = 0;
- int playback = 1, record = 1;
- pa_modargs *ma = NULL;
- char hwdesc[64], *t;
- pa_channel_map map;
- const char *name;
- char *name_buf = NULL;
- int namereg_fail;
-
- assert(c);
- assert(m);
-
- m->userdata = u = pa_xnew0(struct userdata, 1);
- u->module = m;
- u->fd = -1;
- u->core = c;
-
- if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
- pa_log("failed to parse module arguments.");
- goto fail;
- }
-
- if (pa_modargs_get_value_boolean(ma, "record", &record) < 0 || pa_modargs_get_value_boolean(ma, "playback", &playback) < 0) {
- pa_log("record= and playback= expect numeric arguments.");
- goto fail;
- }
-
- if (!playback && !record) {
- pa_log("neither playback nor record enabled for device.");
- goto fail;
- }
-
- mode = (playback&&record) ? O_RDWR : (playback ? O_WRONLY : (record ? O_RDONLY : 0));
-
- nfrags = DEFAULT_NFRAGS;
- frag_size = DEFAULT_FRAGSIZE;
- if (pa_modargs_get_value_s32(ma, "fragments", &nfrags) < 0 || pa_modargs_get_value_s32(ma, "fragment_size", &frag_size) < 0) {
- pa_log("failed to parse fragments arguments");
- goto fail;
- }
-
- u->sample_spec = c->default_sample_spec;
- if (pa_modargs_get_sample_spec_and_channel_map(ma, &u->sample_spec, &map, PA_CHANNEL_MAP_OSS) < 0) {
- pa_log("failed to parse sample specification or channel map");
- goto fail;
- }
-
- if ((u->fd = pa_oss_open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), &mode, &caps)) < 0)
- goto fail;
-
- if (!(caps & DSP_CAP_MMAP) || !(caps & DSP_CAP_TRIGGER)) {
- pa_log("OSS device not mmap capable.");
- goto fail;
- }
-
- pa_log_info("device opened in %s mode.", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR"));
-
- if (pa_oss_get_hw_description(p, hwdesc, sizeof(hwdesc)) >= 0)
- pa_log_info("hardware name is '%s'.", hwdesc);
- else
- hwdesc[0] = 0;
-
- if (nfrags >= 2 && frag_size >= 1)
- if (pa_oss_set_fragments(u->fd, nfrags, frag_size) < 0)
- goto fail;
-
- if (pa_oss_auto_format(u->fd, &u->sample_spec) < 0)
- goto fail;
-
- if (mode != O_WRONLY) {
- if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) < 0) {
- pa_log("SNDCTL_DSP_GETISPACE: %s", pa_cstrerror(errno));
- goto fail;
- }
-
- pa_log_info("input -- %u fragments of size %u.", info.fragstotal, info.fragsize);
- u->in_mmap_length = (u->in_fragment_size = info.fragsize) * (u->in_fragments = info.fragstotal);
-
- if ((u->in_mmap = mmap(NULL, u->in_mmap_length, PROT_READ, MAP_SHARED, u->fd, 0)) == MAP_FAILED) {
- if (mode == O_RDWR) {
- pa_log("mmap failed for input. Changing to O_WRONLY mode.");
- mode = O_WRONLY;
- } else {
- pa_log("mmap(): %s", pa_cstrerror(errno));
- goto fail;
- }
- } else {
- if ((name = pa_modargs_get_value(ma, "source_name", NULL)))
- namereg_fail = 1;
- else {
- name = name_buf = pa_sprintf_malloc("oss_input.%s", pa_path_get_filename(p));
- namereg_fail = 0;
- }
-
- if (!(u->source = pa_source_new(c, __FILE__, name, namereg_fail, &u->sample_spec, &map)))
- goto fail;
-
- u->source->userdata = u;
- u->source->get_latency = source_get_latency_cb;
- u->source->get_hw_volume = source_get_hw_volume;
- u->source->set_hw_volume = source_set_hw_volume;
- pa_source_set_owner(u->source, m);
- pa_source_set_description(u->source, t = pa_sprintf_malloc("OSS PCM/mmap() on %s%s%s%s",
- p,
- hwdesc[0] ? " (" : "",
- hwdesc[0] ? hwdesc : "",
- hwdesc[0] ? ")" : ""));
- pa_xfree(t);
- u->source->is_hardware = 1;
-
- u->in_memblocks = pa_xnew0(pa_memblock*, u->in_fragments);
-
- enable_bits |= PCM_ENABLE_INPUT;
- }
- }
-
- pa_xfree(name_buf);
- name_buf = NULL;
-
- if (mode != O_RDONLY) {
- if (ioctl(u->fd, SNDCTL_DSP_GETOSPACE, &info) < 0) {
- pa_log("SNDCTL_DSP_GETOSPACE: %s", pa_cstrerror(errno));
- goto fail;
- }
-
- pa_log_info("output -- %u fragments of size %u.", info.fragstotal, info.fragsize);
- u->out_mmap_length = (u->out_fragment_size = info.fragsize) * (u->out_fragments = info.fragstotal);
-
- if ((u->out_mmap = mmap(NULL, u->out_mmap_length, PROT_WRITE, MAP_SHARED, u->fd, 0)) == MAP_FAILED) {
- if (mode == O_RDWR) {
- pa_log("mmap filed for output. Changing to O_RDONLY mode.");
- mode = O_RDONLY;
- } else {
- pa_log("mmap(): %s", pa_cstrerror(errno));
- goto fail;
- }
- } else {
- pa_silence_memory(u->out_mmap, u->out_mmap_length, &u->sample_spec);
-
- if ((name = pa_modargs_get_value(ma, "sink_name", NULL)))
- namereg_fail = 1;
- else {
- name = name_buf = pa_sprintf_malloc("oss_output.%s", pa_path_get_filename(p));
- namereg_fail = 0;
- }
-
- if (!(u->sink = pa_sink_new(c, __FILE__, name, namereg_fail, &u->sample_spec, &map)))
- goto fail;
-
- u->sink->get_latency = sink_get_latency_cb;
- u->sink->get_hw_volume = sink_get_hw_volume;
- u->sink->set_hw_volume = sink_set_hw_volume;
- u->sink->userdata = u;
- pa_sink_set_owner(u->sink, m);
- pa_sink_set_description(u->sink, t = pa_sprintf_malloc("OSS PCM/mmap() on %s%s%s%s",
- p,
- hwdesc[0] ? " (" : "",
- hwdesc[0] ? hwdesc : "",
- hwdesc[0] ? ")" : ""));
- pa_xfree(t);
-
- u->sink->is_hardware = 1;
- u->out_memblocks = pa_xmalloc0(sizeof(struct memblock *)*u->out_fragments);
-
- enable_bits |= PCM_ENABLE_OUTPUT;
- }
- }
-
- pa_xfree(name_buf);
- name_buf = NULL;
-
- zero = 0;
- if (ioctl(u->fd, SNDCTL_DSP_SETTRIGGER, &zero) < 0) {
- pa_log("SNDCTL_DSP_SETTRIGGER: %s", pa_cstrerror(errno));
- goto fail;
- }
-
- if (ioctl(u->fd, SNDCTL_DSP_SETTRIGGER, &enable_bits) < 0) {
- pa_log("SNDCTL_DSP_SETTRIGGER: %s", pa_cstrerror(errno));
- goto fail;
- }
-
- assert(u->source || u->sink);
-
- u->io_event = c->mainloop->io_new(c->mainloop, u->fd, (u->source ? PA_IO_EVENT_INPUT : 0) | (u->sink ? PA_IO_EVENT_OUTPUT : 0), io_callback, u);
- assert(u->io_event);
-
- pa_modargs_free(ma);
-
- /* Read mixer settings */
- if (u->source)
- source_get_hw_volume(u->source);
- if (u->sink)
- sink_get_hw_volume(u->sink);
-
- return 0;
-
-fail:
- pa__done(c, m);
-
- if (ma)
- pa_modargs_free(ma);
-
- pa_xfree(name_buf);
-
- return -1;
-}
-
-void pa__done(pa_core *c, pa_module*m) {
- struct userdata *u;
-
- assert(c);
- assert(m);
-
- if (!(u = m->userdata))
- return;
-
- clear_up(u);
-
- if (u->out_memblocks) {
- unsigned i;
- for (i = 0; i < u->out_fragments; i++)
- if (u->out_memblocks[i])
- pa_memblock_unref_fixed(u->out_memblocks[i]);
- pa_xfree(u->out_memblocks);
- }
-
- if (u->in_memblocks) {
- unsigned i;
- for (i = 0; i < u->in_fragments; i++)
- if (u->in_memblocks[i])
- pa_memblock_unref_fixed(u->in_memblocks[i]);
- pa_xfree(u->in_memblocks);
- }
-
- pa_xfree(u);
-}
diff --git a/src/modules/module-oss.c b/src/modules/module-oss.c
index 9d4d0eac..19dceef2 100644
--- a/src/modules/module-oss.c
+++ b/src/modules/module-oss.c
@@ -22,27 +22,46 @@
USA.
***/
+/* General power management rules:
+ *
+ * When SUSPENDED we close the audio device.
+ *
+ * We make no difference between IDLE and RUNNING in our handling.
+ *
+ * As long as we are in RUNNING/IDLE state we will *always* write data to
+ * the device. If none is avilable from the inputs, we write silence
+ * instead.
+ *
+ * If power should be saved on IDLE module-suspend-on-idle should be used.
+ *
+ */
+
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+
#include <sys/soundcard.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <stdio.h>
-#include <assert.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>
+#include <signal.h>
+#include <poll.h>
#include <pulse/xmalloc.h>
#include <pulse/util.h>
#include <pulsecore/core-error.h>
-#include <pulsecore/iochannel.h>
+#include <pulsecore/thread.h>
#include <pulsecore/sink.h>
#include <pulsecore/source.h>
#include <pulsecore/module.h>
@@ -50,6 +69,9 @@
#include <pulsecore/core-util.h>
#include <pulsecore/modargs.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/rtpoll.h>
#include "oss-util.h"
#include "module-oss-symdef.h"
@@ -68,21 +90,48 @@ PA_MODULE_USAGE(
"rate=<sample rate> "
"fragments=<number of fragments> "
"fragment_size=<fragment size> "
- "channel_map=<channel map>")
+ "channel_map=<channel map> "
+ "mmap=<enable memory mapping?>")
+
+#define DEFAULT_DEVICE "/dev/dsp"
struct userdata {
+ pa_core *core;
+ pa_module *module;
pa_sink *sink;
pa_source *source;
- pa_iochannel *io;
- pa_core *core;
- pa_memchunk memchunk, silence;
+ pa_thread *thread;
+ pa_thread_mq thread_mq;
+ pa_rtpoll *rtpoll;
+
+ char *device_name;
+
+ pa_memchunk memchunk;
- uint32_t in_fragment_size, out_fragment_size, sample_size;
- int use_getospace, use_getispace;
+ size_t frame_size;
+ uint32_t in_fragment_size, out_fragment_size, in_nfrags, out_nfrags, in_hwbuf_size, out_hwbuf_size;
+ pa_bool_t use_getospace, use_getispace;
+ pa_bool_t use_getodelay;
+
+ pa_bool_t sink_suspended, source_suspended;
int fd;
- pa_module *module;
+ int mode;
+
+ int mixer_fd;
+ int mixer_devmask;
+
+ int nfrags, frag_size;
+
+ pa_bool_t use_mmap;
+ unsigned out_mmap_current, in_mmap_current;
+ void *in_mmap, *out_mmap;
+ pa_memblock **in_mmap_memblocks, **out_mmap_memblocks;
+
+ int in_mmap_saved_nfrags, out_mmap_saved_nfrags;
+
+ pa_rtpoll_item *rtpoll_item;
};
static const char* const valid_modargs[] = {
@@ -97,280 +146,1010 @@ static const char* const valid_modargs[] = {
"rate",
"channels",
"channel_map",
+ "mmap",
NULL
};
-#define DEFAULT_DEVICE "/dev/dsp"
+static void trigger(struct userdata *u, pa_bool_t quick) {
+ int enable_bits = 0, zero = 0;
-static void update_usage(struct userdata *u) {
- pa_module_set_used(u->module,
- (u->sink ? pa_sink_used_by(u->sink) : 0) +
- (u->source ? pa_source_used_by(u->source) : 0));
-}
+ pa_assert(u);
-static void clear_up(struct userdata *u) {
- assert(u);
+ if (u->fd < 0)
+ return;
- if (u->sink) {
- pa_sink_disconnect(u->sink);
- pa_sink_unref(u->sink);
- u->sink = NULL;
- }
+ pa_log_debug("trigger");
- if (u->source) {
- pa_source_disconnect(u->source);
- pa_source_unref(u->source);
- u->source = NULL;
- }
+ if (u->source && PA_SOURCE_OPENED(u->source->thread_info.state))
+ enable_bits |= PCM_ENABLE_INPUT;
- if (u->io) {
- pa_iochannel_free(u->io);
- u->io = NULL;
- }
-}
+ if (u->sink && PA_SINK_OPENED(u->sink->thread_info.state))
+ enable_bits |= PCM_ENABLE_OUTPUT;
-static void do_write(struct userdata *u) {
- pa_memchunk *memchunk;
- ssize_t r;
- size_t l;
- int loop = 0;
+ pa_log_debug("trigger: %i", enable_bits);
- assert(u);
- if (!u->sink || !pa_iochannel_is_writable(u->io))
- return;
+ if (u->use_mmap) {
- update_usage(u);
+ if (!quick)
+ ioctl(u->fd, SNDCTL_DSP_SETTRIGGER, &zero);
- l = u->out_fragment_size;
+#ifdef SNDCTL_DSP_HALT
+ if (enable_bits == 0)
+ if (ioctl(u->fd, SNDCTL_DSP_HALT, NULL) < 0)
+ pa_log_warn("SNDCTL_DSP_HALT: %s", pa_cstrerror(errno));
+#endif
- if (u->use_getospace) {
- audio_buf_info info;
+ if (ioctl(u->fd, SNDCTL_DSP_SETTRIGGER, &enable_bits) < 0)
+ pa_log_warn("SNDCTL_DSP_SETTRIGGER: %s", pa_cstrerror(errno));
- if (ioctl(u->fd, SNDCTL_DSP_GETOSPACE, &info) < 0)
- u->use_getospace = 0;
- else {
- if (info.bytes/l > 0) {
- l = (info.bytes/l)*l;
- loop = 1;
+ if (u->sink && !(enable_bits & PCM_ENABLE_OUTPUT)) {
+ pa_log_debug("clearing playback buffer");
+ pa_silence_memory(u->out_mmap, u->out_hwbuf_size, &u->sink->sample_spec);
+ }
+
+ } else {
+
+ if (enable_bits)
+ if (ioctl(u->fd, SNDCTL_DSP_POST, NULL) < 0)
+ pa_log_warn("SNDCTL_DSP_POST: %s", pa_cstrerror(errno));
+
+ if (!quick) {
+ /*
+ * Some crappy drivers do not start the recording until we
+ * read something. Without this snippet, poll will never
+ * register the fd as ready.
+ */
+
+ if (u->source && PA_SOURCE_OPENED(u->source->thread_info.state)) {
+ uint8_t *buf = pa_xnew(uint8_t, u->in_fragment_size);
+ pa_read(u->fd, buf, u->in_fragment_size, NULL);
+ pa_xfree(buf);
}
}
}
+}
- do {
- memchunk = &u->memchunk;
+static void mmap_fill_memblocks(struct userdata *u, unsigned n) {
+ pa_assert(u);
+ pa_assert(u->out_mmap_memblocks);
- if (!memchunk->length)
- if (pa_sink_render(u->sink, l, memchunk) < 0)
- memchunk = &u->silence;
+/* pa_log("Mmmap writing %u blocks", n); */
- assert(memchunk->memblock);
- assert(memchunk->memblock->data);
- assert(memchunk->length);
+ while (n > 0) {
+ pa_memchunk chunk;
- if ((r = pa_iochannel_write(u->io, (uint8_t*) memchunk->memblock->data + memchunk->index, memchunk->length)) < 0) {
+ if (u->out_mmap_memblocks[u->out_mmap_current])
+ pa_memblock_unref_fixed(u->out_mmap_memblocks[u->out_mmap_current]);
- if (errno != EAGAIN) {
- pa_log("write() failed: %s", pa_cstrerror(errno));
+ chunk.memblock = u->out_mmap_memblocks[u->out_mmap_current] =
+ pa_memblock_new_fixed(
+ u->core->mempool,
+ (uint8_t*) u->out_mmap + u->out_fragment_size * u->out_mmap_current,
+ u->out_fragment_size,
+ 1);
- clear_up(u);
- pa_module_unload_request(u->module);
- }
+ chunk.length = pa_memblock_get_length(chunk.memblock);
+ chunk.index = 0;
- break;
- }
+ pa_sink_render_into_full(u->sink, &chunk);
- if (memchunk == &u->silence)
- assert(r % u->sample_size == 0);
- else {
- u->memchunk.index += r;
- u->memchunk.length -= r;
+ u->out_mmap_current++;
+ while (u->out_mmap_current >= u->out_nfrags)
+ u->out_mmap_current -= u->out_nfrags;
- if (u->memchunk.length <= 0) {
- pa_memblock_unref(u->memchunk.memblock);
- u->memchunk.memblock = NULL;
- }
- }
+ n--;
+ }
+}
+
+static int mmap_write(struct userdata *u) {
+ struct count_info info;
+
+ pa_assert(u);
+ pa_assert(u->sink);
+
+/* pa_log("Mmmap writing..."); */
+
+ if (ioctl(u->fd, SNDCTL_DSP_GETOPTR, &info) < 0) {
+ pa_log("SNDCTL_DSP_GETOPTR: %s", pa_cstrerror(errno));
+ return -1;
+ }
+
+ info.blocks += u->out_mmap_saved_nfrags;
+ u->out_mmap_saved_nfrags = 0;
+
+ if (info.blocks > 0)
+ mmap_fill_memblocks(u, info.blocks);
- l = l > (size_t) r ? l - r : 0;
- } while (loop && l > 0);
+ return info.blocks;
}
-static void do_read(struct userdata *u) {
- pa_memchunk memchunk;
- ssize_t r;
- size_t l;
- int loop = 0;
- assert(u);
+static void mmap_post_memblocks(struct userdata *u, unsigned n) {
+ pa_assert(u);
+ pa_assert(u->in_mmap_memblocks);
- if (!u->source || !pa_iochannel_is_readable(u->io) || !pa_idxset_size(u->source->outputs))
- return;
+/* pa_log("Mmmap reading %u blocks", n); */
- update_usage(u);
+ while (n > 0) {
+ pa_memchunk chunk;
- l = u->in_fragment_size;
+ if (!u->in_mmap_memblocks[u->in_mmap_current]) {
- if (u->use_getispace) {
- audio_buf_info info;
+ chunk.memblock = u->in_mmap_memblocks[u->in_mmap_current] =
+ pa_memblock_new_fixed(
+ u->core->mempool,
+ (uint8_t*) u->in_mmap + u->in_fragment_size*u->in_mmap_current,
+ u->in_fragment_size,
+ 1);
- if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) < 0)
- u->use_getispace = 0;
- else {
- if (info.bytes/l > 0) {
- l = (info.bytes/l)*l;
- loop = 1;
- }
+ chunk.length = pa_memblock_get_length(chunk.memblock);
+ chunk.index = 0;
+
+ pa_source_post(u->source, &chunk);
}
+
+ u->in_mmap_current++;
+ while (u->in_mmap_current >= u->in_nfrags)
+ u->in_mmap_current -= u->in_nfrags;
+
+ n--;
}
+}
- do {
- memchunk.memblock = pa_memblock_new(u->core->mempool, l);
- assert(memchunk.memblock);
- if ((r = pa_iochannel_read(u->io, memchunk.memblock->data, memchunk.memblock->length)) < 0) {
- pa_memblock_unref(memchunk.memblock);
+static void mmap_clear_memblocks(struct userdata*u, unsigned n) {
+ unsigned i = u->in_mmap_current;
- if (errno != EAGAIN) {
- pa_log("read() failed: %s", pa_cstrerror(errno));
+ pa_assert(u);
+ pa_assert(u->in_mmap_memblocks);
- clear_up(u);
- pa_module_unload_request(u->module);
- }
+ if (n > u->in_nfrags)
+ n = u->in_nfrags;
- break;
+ while (n > 0) {
+ if (u->in_mmap_memblocks[i]) {
+ pa_memblock_unref_fixed(u->in_mmap_memblocks[i]);
+ u->in_mmap_memblocks[i] = NULL;
}
- assert(r <= (ssize_t) memchunk.memblock->length);
- memchunk.length = memchunk.memblock->length = r;
- memchunk.index = 0;
+ i++;
+ while (i >= u->in_nfrags)
+ i -= u->in_nfrags;
- pa_source_post(u->source, &memchunk);
- pa_memblock_unref(memchunk.memblock);
+ n--;
+ }
+}
- l = l > (size_t) r ? l - r : 0;
- } while (loop && l > 0);
+static int mmap_read(struct userdata *u) {
+ struct count_info info;
+ pa_assert(u);
+ pa_assert(u->source);
+
+/* pa_log("Mmmap reading..."); */
+
+ if (ioctl(u->fd, SNDCTL_DSP_GETIPTR, &info) < 0) {
+ pa_log("SNDCTL_DSP_GETIPTR: %s", pa_cstrerror(errno));
+ return -1;
+ }
+
+/* pa_log("... %i", info.blocks); */
+
+ info.blocks += u->in_mmap_saved_nfrags;
+ u->in_mmap_saved_nfrags = 0;
+
+ if (info.blocks > 0) {
+ mmap_post_memblocks(u, info.blocks);
+ mmap_clear_memblocks(u, u->in_nfrags/2);
+ }
+
+ return info.blocks;
}
-static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void*userdata) {
- struct userdata *u = userdata;
- assert(u);
- do_write(u);
- do_read(u);
+static pa_usec_t mmap_sink_get_latency(struct userdata *u) {
+ struct count_info info;
+ size_t bpos, n;
+
+ pa_assert(u);
+
+ if (ioctl(u->fd, SNDCTL_DSP_GETOPTR, &info) < 0) {
+ pa_log("SNDCTL_DSP_GETOPTR: %s", pa_cstrerror(errno));
+ return 0;
+ }
+
+ u->out_mmap_saved_nfrags += info.blocks;
+
+ bpos = ((u->out_mmap_current + u->out_mmap_saved_nfrags) * u->out_fragment_size) % u->out_hwbuf_size;
+
+ if (bpos <= (size_t) info.ptr)
+ n = u->out_hwbuf_size - (info.ptr - bpos);
+ else
+ n = bpos - info.ptr;
+
+/* pa_log("n = %u, bpos = %u, ptr = %u, total=%u, fragsize = %u, n_frags = %u\n", n, bpos, (unsigned) info.ptr, total, u->out_fragment_size, u->out_fragments); */
+
+ return pa_bytes_to_usec(n, &u->sink->sample_spec);
}
-static void source_notify_cb(pa_source *s) {
- struct userdata *u = s->userdata;
- assert(u);
- do_read(u);
+static pa_usec_t mmap_source_get_latency(struct userdata *u) {
+ struct count_info info;
+ size_t bpos, n;
+
+ pa_assert(u);
+
+ if (ioctl(u->fd, SNDCTL_DSP_GETIPTR, &info) < 0) {
+ pa_log("SNDCTL_DSP_GETIPTR: %s", pa_cstrerror(errno));
+ return 0;
+ }
+
+ u->in_mmap_saved_nfrags += info.blocks;
+ bpos = ((u->in_mmap_current + u->in_mmap_saved_nfrags) * u->in_fragment_size) % u->in_hwbuf_size;
+
+ if (bpos <= (size_t) info.ptr)
+ n = info.ptr - bpos;
+ else
+ n = u->in_hwbuf_size - bpos + info.ptr;
+
+/* pa_log("n = %u, bpos = %u, ptr = %u, total=%u, fragsize = %u, n_frags = %u\n", n, bpos, (unsigned) info.ptr, total, u->in_fragment_size, u->in_fragments); */
+
+ return pa_bytes_to_usec(n, &u->source->sample_spec);
}
-static pa_usec_t sink_get_latency_cb(pa_sink *s) {
+static pa_usec_t io_sink_get_latency(struct userdata *u) {
pa_usec_t r = 0;
- int arg;
- struct userdata *u = s->userdata;
- assert(s && u && u->sink);
- if (ioctl(u->fd, SNDCTL_DSP_GETODELAY, &arg) < 0) {
- pa_log_info("device doesn't support SNDCTL_DSP_GETODELAY: %s", pa_cstrerror(errno));
- s->get_latency = NULL;
- return 0;
+ pa_assert(u);
+
+ if (u->use_getodelay) {
+ int arg;
+
+ if (ioctl(u->fd, SNDCTL_DSP_GETODELAY, &arg) < 0) {
+ pa_log_info("Device doesn't support SNDCTL_DSP_GETODELAY: %s", pa_cstrerror(errno));
+ u->use_getodelay = 0;
+ } else
+ r = pa_bytes_to_usec(arg, &u->sink->sample_spec);
+
}
- r += pa_bytes_to_usec(arg, &s->sample_spec);
+ if (!u->use_getodelay && u->use_getospace) {
+ struct audio_buf_info info;
+
+ if (ioctl(u->fd, SNDCTL_DSP_GETOSPACE, &info) < 0) {
+ pa_log_info("Device doesn't support SNDCTL_DSP_GETOSPACE: %s", pa_cstrerror(errno));
+ u->use_getospace = 0;
+ } else
+ r = pa_bytes_to_usec(info.bytes, &u->sink->sample_spec);
+ }
if (u->memchunk.memblock)
- r += pa_bytes_to_usec(u->memchunk.length, &s->sample_spec);
+ r += pa_bytes_to_usec(u->memchunk.length, &u->sink->sample_spec);
return r;
}
-static pa_usec_t source_get_latency_cb(pa_source *s) {
- struct userdata *u = s->userdata;
- audio_buf_info info;
- assert(s && u && u->source);
- if (!u->use_getispace)
- return 0;
+static pa_usec_t io_source_get_latency(struct userdata *u) {
+ pa_usec_t r = 0;
- if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) < 0) {
- u->use_getispace = 0;
- return 0;
+ pa_assert(u);
+
+ if (u->use_getispace) {
+ struct audio_buf_info info;
+
+ if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) < 0) {
+ pa_log_info("Device doesn't support SNDCTL_DSP_GETISPACE: %s", pa_cstrerror(errno));
+ u->use_getispace = 0;
+ } else
+ r = pa_bytes_to_usec(info.bytes, &u->source->sample_spec);
}
- if (info.bytes <= 0)
- return 0;
+ return r;
+}
+
+static void build_pollfd(struct userdata *u) {
+ struct pollfd *pollfd;
- return pa_bytes_to_usec(info.bytes, &s->sample_spec);
+ pa_assert(u);
+ pa_assert(u->fd >= 0);
+
+ if (u->rtpoll_item)
+ pa_rtpoll_item_free(u->rtpoll_item);
+
+ 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->fd;
+ pollfd->events = 0;
+ pollfd->revents = 0;
}
-static int sink_get_hw_volume(pa_sink *s) {
- struct userdata *u = s->userdata;
+static int suspend(struct userdata *u) {
+ pa_assert(u);
+ pa_assert(u->fd >= 0);
- if (pa_oss_get_pcm_volume(u->fd, &s->sample_spec, &s->hw_volume) < 0) {
- pa_log_info("device doesn't support reading mixer settings: %s", pa_cstrerror(errno));
- s->get_hw_volume = NULL;
- return -1;
+ pa_log_info("Suspending...");
+
+ if (u->out_mmap_memblocks) {
+ unsigned i;
+ for (i = 0; i < u->out_nfrags; i++)
+ if (u->out_mmap_memblocks[i]) {
+ pa_memblock_unref_fixed(u->out_mmap_memblocks[i]);
+ u->out_mmap_memblocks[i] = NULL;
+ }
}
+ if (u->in_mmap_memblocks) {
+ unsigned i;
+ for (i = 0; i < u->in_nfrags; i++)
+ if (u->in_mmap_memblocks[i]) {
+ pa_memblock_unref_fixed(u->in_mmap_memblocks[i]);
+ u->in_mmap_memblocks[i] = NULL;
+ }
+ }
+
+ if (u->in_mmap && u->in_mmap != MAP_FAILED) {
+ munmap(u->in_mmap, u->in_hwbuf_size);
+ u->in_mmap = NULL;
+ }
+
+ if (u->out_mmap && u->out_mmap != MAP_FAILED) {
+ munmap(u->out_mmap, u->out_hwbuf_size);
+ u->out_mmap = NULL;
+ }
+
+ /* Let's suspend */
+ ioctl(u->fd, SNDCTL_DSP_SYNC, NULL);
+ pa_close(u->fd);
+ u->fd = -1;
+
+ if (u->rtpoll_item) {
+ pa_rtpoll_item_free(u->rtpoll_item);
+ u->rtpoll_item = NULL;
+ }
+
+ pa_log_info("Device suspended...");
+
return 0;
}
-static int sink_set_hw_volume(pa_sink *s) {
- struct userdata *u = s->userdata;
+static int unsuspend(struct userdata *u) {
+ int m;
+ pa_sample_spec ss, *ss_original;
+ int frag_size, in_frag_size, out_frag_size;
+ int in_nfrags, out_nfrags;
+ struct audio_buf_info info;
+
+ pa_assert(u);
+ pa_assert(u->fd < 0);
+
+ m = u->mode;
+
+ pa_log_info("Trying resume...");
- if (pa_oss_set_pcm_volume(u->fd, &s->sample_spec, &s->hw_volume) < 0) {
- pa_log_info("device doesn't support writing mixer settings: %s", pa_cstrerror(errno));
- s->set_hw_volume = NULL;
+ if ((u->fd = pa_oss_open(u->device_name, &m, NULL)) < 0) {
+ pa_log_warn("Resume failed, device busy (%s)", pa_cstrerror(errno));
return -1;
+
+ if (m != u->mode)
+ pa_log_warn("Resume failed, couldn't open device with original access mode.");
+ goto fail;
+ }
+
+ if (u->nfrags >= 2 && u->frag_size >= 1)
+ if (pa_oss_set_fragments(u->fd, u->nfrags, u->frag_size) < 0) {
+ pa_log_warn("Resume failed, couldn't set original fragment settings.");
+ goto fail;
+ }
+
+ ss = *(ss_original = u->sink ? &u->sink->sample_spec : &u->source->sample_spec);
+ if (pa_oss_auto_format(u->fd, &ss) < 0 || !pa_sample_spec_equal(&ss, ss_original)) {
+ pa_log_warn("Resume failed, couldn't set original sample format settings.");
+ goto fail;
+ }
+
+ if (ioctl(u->fd, SNDCTL_DSP_GETBLKSIZE, &frag_size) < 0) {
+ pa_log_warn("SNDCTL_DSP_GETBLKSIZE: %s", pa_cstrerror(errno));
+ goto fail;
+ }
+
+ in_frag_size = out_frag_size = frag_size;
+ in_nfrags = out_nfrags = u->nfrags;
+
+ if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) >= 0) {
+ in_frag_size = info.fragsize;
+ in_nfrags = info.fragstotal;
+ }
+
+ if (ioctl(u->fd, SNDCTL_DSP_GETOSPACE, &info) >= 0) {
+ out_frag_size = info.fragsize;
+ out_nfrags = info.fragstotal;
+ }
+
+ if ((u->source && (in_frag_size != (int) u->in_fragment_size || in_nfrags != (int) u->in_nfrags)) ||
+ (u->sink && (out_frag_size != (int) u->out_fragment_size || out_nfrags != (int) u->out_nfrags))) {
+ pa_log_warn("Resume failed, input fragment settings don't match.");
+ goto fail;
+ }
+
+ if (u->use_mmap) {
+ if (u->source) {
+ if ((u->in_mmap = mmap(NULL, u->in_hwbuf_size, PROT_READ, MAP_SHARED, u->fd, 0)) == MAP_FAILED) {
+ pa_log("Resume failed, mmap(): %s", pa_cstrerror(errno));
+ goto fail;
+ }
+ }
+
+ if (u->sink) {
+ if ((u->out_mmap = mmap(NULL, u->out_hwbuf_size, PROT_WRITE, MAP_SHARED, u->fd, 0)) == MAP_FAILED) {
+ pa_log("Resume failed, mmap(): %s", pa_cstrerror(errno));
+ if (u->in_mmap && u->in_mmap != MAP_FAILED) {
+ munmap(u->in_mmap, u->in_hwbuf_size);
+ u->in_mmap = NULL;
+ }
+
+ goto fail;
+ }
+
+ pa_silence_memory(u->out_mmap, u->out_hwbuf_size, &ss);
+ }
}
+ u->out_mmap_current = u->in_mmap_current = 0;
+ u->out_mmap_saved_nfrags = u->in_mmap_saved_nfrags = 0;
+
+ pa_assert(!u->rtpoll_item);
+
+ build_pollfd(u);
+
+ if (u->sink)
+ pa_sink_get_volume(u->sink);
+ if (u->source)
+ pa_source_get_volume(u->source);
+
+ pa_log_info("Resumed successfully...");
+
return 0;
+
+fail:
+ pa_close(u->fd);
+ u->fd = -1;
+ return -1;
}
-static int source_get_hw_volume(pa_source *s) {
- struct userdata *u = s->userdata;
+static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+ struct userdata *u = PA_SINK(o)->userdata;
+ int ret;
+ pa_bool_t do_trigger = FALSE, quick = TRUE;
+
+ switch (code) {
+
+ case PA_SINK_MESSAGE_GET_LATENCY: {
+ pa_usec_t r = 0;
+
+ if (u->fd >= 0) {
+ if (u->use_mmap)
+ r = mmap_sink_get_latency(u);
+ else
+ r = io_sink_get_latency(u);
+ }
+
+ *((pa_usec_t*) data) = r;
+
+ return 0;
+ }
+
+ case PA_SINK_MESSAGE_SET_STATE:
+
+ switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
+
+ case PA_SINK_SUSPENDED:
+ pa_assert(PA_SINK_OPENED(u->sink->thread_info.state));
+
+ if (!u->source || u->source_suspended) {
+ if (suspend(u) < 0)
+ return -1;
+ }
+
+ do_trigger = TRUE;
+
+ u->sink_suspended = TRUE;
+ break;
+
+ case PA_SINK_IDLE:
+ case PA_SINK_RUNNING:
+
+ if (u->sink->thread_info.state == PA_SINK_INIT) {
+ do_trigger = TRUE;
+ quick = u->source && PA_SOURCE_OPENED(u->source->thread_info.state);
+ }
+
+ if (u->sink->thread_info.state == PA_SINK_SUSPENDED) {
+
+ if (!u->source || u->source_suspended) {
+ if (unsuspend(u) < 0)
+ return -1;
+ quick = FALSE;
+ }
+
+ do_trigger = TRUE;
+
+ u->out_mmap_current = 0;
+ u->out_mmap_saved_nfrags = 0;
+
+ u->sink_suspended = FALSE;
+ }
+
+ break;
+
+ case PA_SINK_UNLINKED:
+ case PA_SINK_INIT:
+ ;
+ }
+
+ break;
- if (pa_oss_get_input_volume(u->fd, &s->sample_spec, &s->hw_volume) < 0) {
- pa_log_info("device doesn't support reading mixer settings: %s", pa_cstrerror(errno));
- s->get_hw_volume = NULL;
- return -1;
}
- return 0;
+ ret = pa_sink_process_msg(o, code, data, offset, chunk);
+
+ if (do_trigger)
+ trigger(u, quick);
+
+ return ret;
}
-static int source_set_hw_volume(pa_source *s) {
- struct userdata *u = s->userdata;
+static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+ struct userdata *u = PA_SOURCE(o)->userdata;
+ int ret;
+ int do_trigger = FALSE, quick = TRUE;
+
+ switch (code) {
+
+ case PA_SOURCE_MESSAGE_GET_LATENCY: {
+ pa_usec_t r = 0;
+
+ if (u->fd >= 0) {
+ if (u->use_mmap)
+ r = mmap_source_get_latency(u);
+ else
+ r = io_source_get_latency(u);
+ }
+
+ *((pa_usec_t*) data) = r;
+ return 0;
+ }
+
+ case PA_SOURCE_MESSAGE_SET_STATE:
+
+ switch ((pa_source_state_t) PA_PTR_TO_UINT(data)) {
+ case PA_SOURCE_SUSPENDED:
+ pa_assert(PA_SOURCE_OPENED(u->source->thread_info.state));
+
+ if (!u->sink || u->sink_suspended) {
+ if (suspend(u) < 0)
+ return -1;
+ }
+
+ do_trigger = TRUE;
+
+ u->source_suspended = TRUE;
+ break;
+
+ case PA_SOURCE_IDLE:
+ case PA_SOURCE_RUNNING:
+
+ if (u->source->thread_info.state == PA_SOURCE_INIT) {
+ do_trigger = TRUE;
+ quick = u->sink && PA_SINK_OPENED(u->sink->thread_info.state);
+ }
+
+ if (u->source->thread_info.state == PA_SOURCE_SUSPENDED) {
+
+ if (!u->sink || u->sink_suspended) {
+ if (unsuspend(u) < 0)
+ return -1;
+ quick = FALSE;
+ }
+
+ do_trigger = TRUE;
+
+ u->in_mmap_current = 0;
+ u->in_mmap_saved_nfrags = 0;
+
+ u->source_suspended = FALSE;
+ }
+ break;
+
+ case PA_SOURCE_UNLINKED:
+ case PA_SOURCE_INIT:
+ ;
+
+ }
+ break;
- if (pa_oss_set_input_volume(u->fd, &s->sample_spec, &s->hw_volume) < 0) {
- pa_log_info("device doesn't support writing mixer settings: %s", pa_cstrerror(errno));
- s->set_hw_volume = NULL;
- return -1;
}
- return 0;
+ ret = pa_source_process_msg(o, code, data, offset, chunk);
+
+ if (do_trigger)
+ trigger(u, quick);
+
+ return ret;
+}
+
+static int sink_get_volume(pa_sink *s) {
+ struct userdata *u;
+ int r;
+
+ pa_assert_se(u = s->userdata);
+
+ pa_assert(u->mixer_devmask & (SOUND_MASK_VOLUME|SOUND_MASK_PCM));
+
+ if (u->mixer_devmask & SOUND_MASK_VOLUME)
+ if ((r = pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_VOLUME, &s->sample_spec, &s->volume)) >= 0)
+ return r;
+
+ if (u->mixer_devmask & SOUND_MASK_PCM)
+ if ((r = pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_PCM, &s->sample_spec, &s->volume)) >= 0)
+ return r;
+
+ pa_log_info("Device doesn't support reading mixer settings: %s", pa_cstrerror(errno));
+ return -1;
+}
+
+static int sink_set_volume(pa_sink *s) {
+ struct userdata *u;
+ int r;
+
+ pa_assert_se(u = s->userdata);
+
+ pa_assert(u->mixer_devmask & (SOUND_MASK_VOLUME|SOUND_MASK_PCM));
+
+ if (u->mixer_devmask & SOUND_MASK_VOLUME)
+ if ((r = pa_oss_set_volume(u->mixer_fd, SOUND_MIXER_WRITE_VOLUME, &s->sample_spec, &s->volume)) >= 0)
+ return r;
+
+ if (u->mixer_devmask & SOUND_MASK_PCM)
+ if ((r = pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_WRITE_PCM, &s->sample_spec, &s->volume)) >= 0)
+ return r;
+
+ pa_log_info("Device doesn't support writing mixer settings: %s", pa_cstrerror(errno));
+ return -1;
}
-int pa__init(pa_core *c, pa_module*m) {
+static int source_get_volume(pa_source *s) {
+ struct userdata *u;
+ int r;
+
+ pa_assert_se(u = s->userdata);
+
+ pa_assert(u->mixer_devmask & (SOUND_MASK_IGAIN|SOUND_MASK_RECLEV));
+
+ if (u->mixer_devmask & SOUND_MASK_IGAIN)
+ if ((r = pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_IGAIN, &s->sample_spec, &s->volume)) >= 0)
+ return r;
+
+ if (u->mixer_devmask & SOUND_MASK_RECLEV)
+ if ((r = pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_RECLEV, &s->sample_spec, &s->volume)) >= 0)
+ return r;
+
+ pa_log_info("Device doesn't support reading mixer settings: %s", pa_cstrerror(errno));
+ return -1;
+}
+
+static int source_set_volume(pa_source *s) {
+ struct userdata *u;
+ int r;
+
+ pa_assert_se(u = s->userdata);
+
+ pa_assert(u->mixer_devmask & (SOUND_MASK_IGAIN|SOUND_MASK_RECLEV));
+
+ if (u->mixer_devmask & SOUND_MASK_IGAIN)
+ if ((r = pa_oss_set_volume(u->mixer_fd, SOUND_MIXER_WRITE_IGAIN, &s->sample_spec, &s->volume)) >= 0)
+ return r;
+
+ if (u->mixer_devmask & SOUND_MASK_RECLEV)
+ if ((r = pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_WRITE_RECLEV, &s->sample_spec, &s->volume)) >= 0)
+ return r;
+
+ pa_log_info("Device doesn't support writing mixer settings: %s", pa_cstrerror(errno));
+ return -1;
+}
+
+static void thread_func(void *userdata) {
+ struct userdata *u = userdata;
+ int write_type = 0, read_type = 0;
+ unsigned short revents = 0;
+
+ pa_assert(u);
+
+ pa_log_debug("Thread starting up");
+
+ if (u->core->high_priority)
+ pa_make_realtime();
+
+ pa_thread_mq_install(&u->thread_mq);
+ pa_rtpoll_install(u->rtpoll);
+
+ for (;;) {
+ int ret;
+
+/* pa_log("loop"); */
+
+ /* Render some data and write it to the dsp */
+
+ if (u->sink && PA_SINK_OPENED(u->sink->thread_info.state) && ((revents & POLLOUT) || u->use_mmap || u->use_getospace)) {
+
+ if (u->use_mmap) {
+
+ if ((ret = mmap_write(u)) < 0)
+ goto fail;
+
+ revents &= ~POLLOUT;
+
+ if (ret > 0)
+ continue;
+
+ } else {
+ ssize_t l;
+ pa_bool_t loop = FALSE, work_done = FALSE;
+
+ l = u->out_fragment_size;
+
+ if (u->use_getospace) {
+ audio_buf_info info;
+
+ if (ioctl(u->fd, SNDCTL_DSP_GETOSPACE, &info) < 0) {
+ pa_log_info("Device doesn't support SNDCTL_DSP_GETOSPACE: %s", pa_cstrerror(errno));
+ u->use_getospace = FALSE;
+ } else {
+ l = info.bytes;
+
+ /* We loop only if GETOSPACE worked and we
+ * actually *know* that we can write more than
+ * one fragment at a time */
+ loop = TRUE;
+ }
+ }
+
+ /* Round down to multiples of the fragment size,
+ * because OSS needs that (at least some versions
+ * do) */
+ l = (l/u->out_fragment_size) * u->out_fragment_size;
+
+ /* Hmm, so poll() signalled us that we can read
+ * something, but GETOSPACE told us there was nothing?
+ * Hmm, make the best of it, try to read some data, to
+ * avoid spinning forever. */
+ if (l <= 0 && (revents & POLLOUT)) {
+ l = u->out_fragment_size;
+ loop = FALSE;
+ }
+
+ while (l > 0) {
+ void *p;
+ ssize_t t;
+
+ if (u->memchunk.length <= 0)
+ pa_sink_render(u->sink, l, &u->memchunk);
+
+ pa_assert(u->memchunk.length > 0);
+
+ p = pa_memblock_acquire(u->memchunk.memblock);
+ t = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length, &write_type);
+ pa_memblock_release(u->memchunk.memblock);
+
+/* pa_log("wrote %i bytes of %u", t, l); */
+
+ pa_assert(t != 0);
+
+ if (t < 0) {
+
+ if (errno == EINTR)
+ continue;
+
+ else if (errno == EAGAIN) {
+ pa_log_debug("EAGAIN");
+
+ revents &= ~POLLOUT;
+ break;
+
+ } else {
+ pa_log("Failed to write data to DSP: %s", pa_cstrerror(errno));
+ goto fail;
+ }
+
+ } else {
+
+ u->memchunk.index += t;
+ u->memchunk.length -= t;
+
+ if (u->memchunk.length <= 0) {
+ pa_memblock_unref(u->memchunk.memblock);
+ pa_memchunk_reset(&u->memchunk);
+ }
+
+ l -= t;
+
+ revents &= ~POLLOUT;
+ work_done = TRUE;
+ }
+
+ if (!loop)
+ break;
+ }
+
+ if (work_done)
+ continue;
+ }
+ }
+
+ /* Try to read some data and pass it on to the source driver. */
+
+ if (u->source && PA_SOURCE_OPENED(u->source->thread_info.state) && ((revents & POLLIN) || u->use_mmap || u->use_getispace)) {
+
+ if (u->use_mmap) {
+
+ if ((ret = mmap_read(u)) < 0)
+ goto fail;
+
+ revents &= ~POLLIN;
+
+ if (ret > 0)
+ continue;
+
+ } else {
+
+ void *p;
+ ssize_t l;
+ pa_memchunk memchunk;
+ pa_bool_t loop = FALSE, work_done = FALSE;
+
+ l = u->in_fragment_size;
+
+ if (u->use_getispace) {
+ audio_buf_info info;
+
+ if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) < 0) {
+ pa_log_info("Device doesn't support SNDCTL_DSP_GETISPACE: %s", pa_cstrerror(errno));
+ u->use_getispace = FALSE;
+ } else {
+ l = info.bytes;
+ loop = TRUE;
+ }
+ }
+
+ l = (l/u->in_fragment_size) * u->in_fragment_size;
+
+ if (l <= 0 && (revents & POLLIN)) {
+ l = u->in_fragment_size;
+ loop = FALSE;
+ }
+
+ while (l > 0) {
+ ssize_t t, k;
+
+ pa_assert(l > 0);
+
+ memchunk.memblock = pa_memblock_new(u->core->mempool, (size_t) -1);
+
+ k = pa_memblock_get_length(memchunk.memblock);
+
+ if (k > l)
+ k = l;
+
+ k = (k/u->frame_size)*u->frame_size;
+
+ p = pa_memblock_acquire(memchunk.memblock);
+ t = pa_read(u->fd, p, k, &read_type);
+ pa_memblock_release(memchunk.memblock);
+
+ pa_assert(t != 0); /* EOF cannot happen */
+
+/* pa_log("read %i bytes of %u", t, l); */
+
+ if (t < 0) {
+ pa_memblock_unref(memchunk.memblock);
+
+ if (errno == EINTR)
+ continue;
+
+ else if (errno == EAGAIN) {
+ pa_log_debug("EAGAIN");
+
+ revents &= ~POLLIN;
+ break;
+
+ } else {
+ pa_log("Failed to read data from DSP: %s", pa_cstrerror(errno));
+ goto fail;
+ }
+
+ } else {
+ memchunk.index = 0;
+ memchunk.length = t;
+
+ pa_source_post(u->source, &memchunk);
+ pa_memblock_unref(memchunk.memblock);
+
+ l -= t;
+
+ revents &= ~POLLIN;
+ work_done = TRUE;
+ }
+
+ if (!loop)
+ break;
+ }
+
+ if (work_done)
+ continue;
+ }
+ }
+
+/* pa_log("loop2 revents=%i", revents); */
+
+ if (u->rtpoll_item) {
+ struct pollfd *pollfd;
+
+ pa_assert(u->fd >= 0);
+
+ pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+ pollfd->events =
+ ((u->source && PA_SOURCE_OPENED(u->source->thread_info.state)) ? POLLIN : 0) |
+ ((u->sink && PA_SINK_OPENED(u->sink->thread_info.state)) ? POLLOUT : 0);
+ }
+
+ /* Hmm, nothing to do. Let's sleep */
+ if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
+ goto fail;
+
+ if (ret == 0)
+ goto finish;
+
+ if (u->rtpoll_item) {
+ struct pollfd *pollfd;
+
+ pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+
+ if (pollfd->revents & ~(POLLOUT|POLLIN)) {
+ pa_log("DSP shutdown.");
+ goto fail;
+ }
+
+ revents = pollfd->revents;
+ } else
+ revents = 0;
+ }
+
+fail:
+ /* If this was no regular exit from the loop we have to continue
+ * processing messages until we received PA_MESSAGE_SHUTDOWN */
+ pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+ pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+ pa_log_debug("Thread shutting down");
+}
+
+int pa__init(pa_module*m) {
+
struct audio_buf_info info;
struct userdata *u = NULL;
- const char *p;
+ const char *dev;
int fd = -1;
- int nfrags, frag_size, in_frag_size, out_frag_size;
- int mode;
- int record = 1, playback = 1;
+ int nfrags, frag_size;
+ int mode, caps;
+ int record = 1, playback = 1, use_mmap = 1;
pa_sample_spec ss;
pa_channel_map map;
pa_modargs *ma = NULL;
char hwdesc[64], *t;
const char *name;
- char *name_buf = NULL;
int namereg_fail;
- assert(c);
- assert(m);
+ pa_assert(m);
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
- pa_log("failed to parse module arguments.");
+ pa_log("Failed to parse module arguments.");
goto fail;
}
@@ -380,36 +1159,52 @@ int pa__init(pa_core *c, pa_module*m) {
}
if (!playback && !record) {
- pa_log("neither playback nor record enabled for device.");
+ pa_log("Neither playback nor record enabled for device.");
goto fail;
}
- mode = (playback&&record) ? O_RDWR : (playback ? O_WRONLY : (record ? O_RDONLY : 0));
+ mode = (playback && record) ? O_RDWR : (playback ? O_WRONLY : (record ? O_RDONLY : 0));
- ss = c->default_sample_spec;
+ ss = m->core->default_sample_spec;
if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_OSS) < 0) {
- pa_log("failed to parse sample specification or channel map");
+ pa_log("Failed to parse sample specification or channel map");
goto fail;
}
- /* Fix latency to 100ms */
- nfrags = 12;
- frag_size = pa_bytes_per_second(&ss)/128;
+ nfrags = m->core->default_n_fragments;
+ frag_size = pa_usec_to_bytes(m->core->default_fragment_size_msec*1000, &ss);
+ if (frag_size <= 0)
+ frag_size = pa_frame_size(&ss);
if (pa_modargs_get_value_s32(ma, "fragments", &nfrags) < 0 || pa_modargs_get_value_s32(ma, "fragment_size", &frag_size) < 0) {
- pa_log("failed to parse fragments arguments");
+ pa_log("Failed to parse fragments arguments");
goto fail;
}
- if ((fd = pa_oss_open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), &mode, NULL)) < 0)
+ if (pa_modargs_get_value_boolean(ma, "mmap", &use_mmap) < 0) {
+ pa_log("Failed to parse mmap argument.");
goto fail;
+ }
- if (pa_oss_get_hw_description(p, hwdesc, sizeof(hwdesc)) >= 0)
- pa_log_info("hardware name is '%s'.", hwdesc);
+ if ((fd = pa_oss_open(dev = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), &mode, &caps)) < 0)
+ goto fail;
+
+ if (use_mmap && (!(caps & DSP_CAP_MMAP) || !(caps & DSP_CAP_TRIGGER))) {
+ pa_log_info("OSS device not mmap capable, falling back to UNIX read/write mode.");
+ use_mmap = 0;
+ }
+
+ if (use_mmap && mode == O_WRONLY) {
+ pa_log_info("Device opened for playback only, cannot do memory mapping, falling back to UNIX write() mode.");
+ use_mmap = 0;
+ }
+
+ if (pa_oss_get_hw_description(dev, hwdesc, sizeof(hwdesc)) >= 0)
+ pa_log_info("Hardware name is '%s'.", hwdesc);
else
hwdesc[0] = 0;
- pa_log_info("device opened in %s mode.", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR"));
+ pa_log_info("Device opened in %s mode.", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR"));
if (nfrags >= 2 && frag_size >= 1)
if (pa_oss_set_fragments(fd, nfrags, frag_size) < 0)
@@ -422,152 +1217,282 @@ int pa__init(pa_core *c, pa_module*m) {
pa_log("SNDCTL_DSP_GETBLKSIZE: %s", pa_cstrerror(errno));
goto fail;
}
- assert(frag_size);
- in_frag_size = out_frag_size = frag_size;
+ pa_assert(frag_size > 0);
- u = pa_xmalloc(sizeof(struct userdata));
- u->core = c;
- u->use_getospace = u->use_getispace = 0;
+ u = pa_xnew0(struct userdata, 1);
+ u->core = m->core;
+ u->module = m;
+ m->userdata = u;
+ u->fd = fd;
+ u->mixer_fd = -1;
+ u->use_getospace = u->use_getispace = 1;
+ u->use_getodelay = 1;
+ u->mode = mode;
+ u->frame_size = pa_frame_size(&ss);
+ u->device_name = pa_xstrdup(dev);
+ u->in_nfrags = u->out_nfrags = u->nfrags = nfrags;
+ u->out_fragment_size = u->in_fragment_size = u->frag_size = frag_size;
+ u->use_mmap = use_mmap;
+ pa_thread_mq_init(&u->thread_mq, m->core->mainloop);
+ u->rtpoll = pa_rtpoll_new();
+ pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq);
+ u->rtpoll_item = NULL;
+ build_pollfd(u);
if (ioctl(fd, SNDCTL_DSP_GETISPACE, &info) >= 0) {
- pa_log_info("input -- %u fragments of size %u.", info.fragstotal, info.fragsize);
- in_frag_size = info.fragsize;
+ pa_log_info("Input -- %u fragments of size %u.", info.fragstotal, info.fragsize);
+ u->in_fragment_size = info.fragsize;
+ u->in_nfrags = info.fragstotal;
u->use_getispace = 1;
}
if (ioctl(fd, SNDCTL_DSP_GETOSPACE, &info) >= 0) {
- pa_log_info("output -- %u fragments of size %u.", info.fragstotal, info.fragsize);
- out_frag_size = info.fragsize;
+ pa_log_info("Output -- %u fragments of size %u.", info.fragstotal, info.fragsize);
+ u->out_fragment_size = info.fragsize;
+ u->out_nfrags = info.fragstotal;
u->use_getospace = 1;
}
+ u->in_hwbuf_size = u->in_nfrags * u->in_fragment_size;
+ u->out_hwbuf_size = u->out_nfrags * u->out_fragment_size;
+
if (mode != O_WRONLY) {
+ char *name_buf = NULL;
+
+ if (use_mmap) {
+ if ((u->in_mmap = mmap(NULL, u->in_hwbuf_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
+ pa_log_warn("mmap(PROT_READ) failed, reverting to non-mmap mode: %s", pa_cstrerror(errno));
+ use_mmap = u->use_mmap = 0;
+ u->in_mmap = NULL;
+ } else
+ pa_log_debug("Successfully mmap()ed input buffer.");
+ }
+
if ((name = pa_modargs_get_value(ma, "source_name", NULL)))
namereg_fail = 1;
else {
- name = name_buf = pa_sprintf_malloc("oss_input.%s", pa_path_get_filename(p));
+ name = name_buf = pa_sprintf_malloc("oss_input.%s", pa_path_get_filename(dev));
namereg_fail = 0;
}
- if (!(u->source = pa_source_new(c, __FILE__, name, namereg_fail, &ss, &map)))
+ u->source = pa_source_new(m->core, __FILE__, name, namereg_fail, &ss, &map);
+ pa_xfree(name_buf);
+ if (!u->source) {
+ pa_log("Failed to create source object");
goto fail;
+ }
+ u->source->parent.process_msg = source_process_msg;
u->source->userdata = u;
- u->source->notify = source_notify_cb;
- u->source->get_latency = source_get_latency_cb;
- u->source->get_hw_volume = source_get_hw_volume;
- u->source->set_hw_volume = source_set_hw_volume;
- pa_source_set_owner(u->source, m);
- pa_source_set_description(u->source, t = pa_sprintf_malloc("OSS PCM on %s%s%s%s",
- p,
- hwdesc[0] ? " (" : "",
- hwdesc[0] ? hwdesc : "",
- hwdesc[0] ? ")" : ""));
+
+ pa_source_set_module(u->source, m);
+ pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
+ pa_source_set_rtpoll(u->source, u->rtpoll);
+ pa_source_set_description(u->source, t = pa_sprintf_malloc(
+ "OSS PCM on %s%s%s%s%s",
+ dev,
+ hwdesc[0] ? " (" : "",
+ hwdesc[0] ? hwdesc : "",
+ hwdesc[0] ? ")" : "",
+ use_mmap ? " via DMA" : ""));
pa_xfree(t);
- u->source->is_hardware = 1;
- } else
- u->source = NULL;
+ u->source->flags = PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY;
+ u->source->refresh_volume = TRUE;
- pa_xfree(name_buf);
- name_buf = NULL;
+ if (use_mmap)
+ u->in_mmap_memblocks = pa_xnew0(pa_memblock*, u->in_nfrags);
+ }
if (mode != O_RDONLY) {
+ char *name_buf = NULL;
+
+ if (use_mmap) {
+ if ((u->out_mmap = mmap(NULL, u->out_hwbuf_size, PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
+ if (mode == O_RDWR) {
+ pa_log_debug("mmap() failed for input. Changing to O_WRONLY mode.");
+ mode = O_WRONLY;
+ goto go_on;
+ } else {
+ pa_log_warn("mmap(PROT_WRITE) failed, reverting to non-mmap mode: %s", pa_cstrerror(errno));
+ u->use_mmap = (use_mmap = FALSE);
+ u->out_mmap = NULL;
+ }
+ } else {
+ pa_log_debug("Successfully mmap()ed output buffer.");
+ pa_silence_memory(u->out_mmap, u->out_hwbuf_size, &ss);
+ }
+ }
+
if ((name = pa_modargs_get_value(ma, "sink_name", NULL)))
namereg_fail = 1;
else {
- name = name_buf = pa_sprintf_malloc("oss_output.%s", pa_path_get_filename(p));
+ name = name_buf = pa_sprintf_malloc("oss_output.%s", pa_path_get_filename(dev));
namereg_fail = 0;
}
- if (!(u->sink = pa_sink_new(c, __FILE__, name, namereg_fail, &ss, &map)))
+ u->sink = pa_sink_new(m->core, __FILE__, name, namereg_fail, &ss, &map);
+ pa_xfree(name_buf);
+ if (!u->sink) {
+ pa_log("Failed to create sink object");
goto fail;
+ }
- u->sink->get_latency = sink_get_latency_cb;
- u->sink->get_hw_volume = sink_get_hw_volume;
- u->sink->set_hw_volume = sink_set_hw_volume;
+ u->sink->parent.process_msg = sink_process_msg;
u->sink->userdata = u;
- pa_sink_set_owner(u->sink, m);
- pa_sink_set_description(u->sink, t = pa_sprintf_malloc("OSS PCM on %s%s%s%s",
- p,
- hwdesc[0] ? " (" : "",
- hwdesc[0] ? hwdesc : "",
- hwdesc[0] ? ")" : ""));
+
+ pa_sink_set_module(u->sink, m);
+ pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
+ pa_sink_set_rtpoll(u->sink, u->rtpoll);
+ pa_sink_set_description(u->sink, t = pa_sprintf_malloc(
+ "OSS PCM on %s%s%s%s%s",
+ dev,
+ hwdesc[0] ? " (" : "",
+ hwdesc[0] ? hwdesc : "",
+ hwdesc[0] ? ")" : "",
+ use_mmap ? " via DMA" : ""));
pa_xfree(t);
- u->sink->is_hardware = 1;
- } else
- u->sink = NULL;
+ u->sink->flags = PA_SINK_HARDWARE|PA_SINK_LATENCY;
+ u->sink->refresh_volume = TRUE;
- pa_xfree(name_buf);
- name_buf = NULL;
+ if (use_mmap)
+ u->out_mmap_memblocks = pa_xnew0(pa_memblock*, u->out_nfrags);
+ }
- assert(u->source || u->sink);
+ if ((u->mixer_fd = pa_oss_open_mixer_for_device(u->device_name)) >= 0) {
+ int do_close = 1;
+ u->mixer_devmask = 0;
- u->io = pa_iochannel_new(c->mainloop, u->source ? fd : -1, u->sink ? fd : -1);
- assert(u->io);
- pa_iochannel_set_callback(u->io, io_callback, u);
- u->fd = fd;
+ if (ioctl(fd, SOUND_MIXER_READ_DEVMASK, &u->mixer_devmask) < 0)
+ pa_log_warn("SOUND_MIXER_READ_DEVMASK failed: %s", pa_cstrerror(errno));
- u->memchunk.memblock = NULL;
- u->memchunk.length = 0;
- u->sample_size = pa_frame_size(&ss);
+ else {
+ if (u->sink && (u->mixer_devmask & (SOUND_MASK_VOLUME|SOUND_MASK_PCM))) {
+ pa_log_debug("Found hardware mixer track for playback.");
+ u->sink->flags |= PA_SINK_HW_VOLUME_CTRL;
+ u->sink->get_volume = sink_get_volume;
+ u->sink->set_volume = sink_set_volume;
+ do_close = 0;
+ }
- u->out_fragment_size = out_frag_size;
- u->in_fragment_size = in_frag_size;
- u->silence.memblock = pa_memblock_new(u->core->mempool, u->silence.length = u->out_fragment_size);
- assert(u->silence.memblock);
- pa_silence_memblock(u->silence.memblock, &ss);
- u->silence.index = 0;
+ if (u->source && (u->mixer_devmask & (SOUND_MASK_RECLEV|SOUND_MASK_IGAIN))) {
+ pa_log_debug("Found hardware mixer track for recording.");
+ u->source->flags |= PA_SOURCE_HW_VOLUME_CTRL;
+ u->source->get_volume = source_get_volume;
+ u->source->set_volume = source_set_volume;
+ do_close = 0;
+ }
+ }
- u->module = m;
- m->userdata = u;
+ if (do_close) {
+ pa_close(u->mixer_fd);
+ u->mixer_fd = -1;
+ }
+ }
- pa_modargs_free(ma);
+go_on:
+
+ pa_assert(u->source || u->sink);
- /*
- * Some crappy drivers do not start the recording until we read something.
- * Without this snippet, poll will never register the fd as ready.
- */
- if (u->source) {
- char *buf = pa_xnew(char, u->sample_size);
- pa_read(u->fd, buf, u->sample_size, NULL);
- pa_xfree(buf);
+ pa_memchunk_reset(&u->memchunk);
+
+ if (!(u->thread = pa_thread_new(thread_func, u))) {
+ pa_log("Failed to create thread.");
+ goto fail;
}
/* Read mixer settings */
- if (u->source)
- source_get_hw_volume(u->source);
+ if (u->sink && u->sink->get_volume)
+ sink_get_volume(u->sink);
+ if (u->source && u->source->get_volume)
+ source_get_volume(u->source);
+
if (u->sink)
- sink_get_hw_volume(u->sink);
+ pa_sink_put(u->sink);
+ if (u->source)
+ pa_source_put(u->source);
+
+ pa_modargs_free(ma);
return 0;
fail:
- if (fd >= 0)
- close(fd);
+
+ if (u)
+ pa__done(m);
+ else if (fd >= 0)
+ pa_close(fd);
if (ma)
pa_modargs_free(ma);
- pa_xfree(name_buf);
-
return -1;
}
-void pa__done(pa_core *c, pa_module*m) {
+void pa__done(pa_module*m) {
struct userdata *u;
- assert(c);
- assert(m);
+ pa_assert(m);
if (!(u = m->userdata))
return;
- clear_up(u);
+ if (u->sink)
+ pa_sink_unlink(u->sink);
+
+ if (u->source)
+ pa_source_unlink(u->source);
+
+ if (u->thread) {
+ pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+ pa_thread_free(u->thread);
+ }
+
+ pa_thread_mq_done(&u->thread_mq);
+
+ if (u->sink)
+ pa_sink_unref(u->sink);
+
+ if (u->source)
+ pa_source_unref(u->source);
if (u->memchunk.memblock)
pa_memblock_unref(u->memchunk.memblock);
- if (u->silence.memblock)
- pa_memblock_unref(u->silence.memblock);
+
+ if (u->rtpoll_item)
+ pa_rtpoll_item_free(u->rtpoll_item);
+
+ if (u->rtpoll)
+ pa_rtpoll_free(u->rtpoll);
+
+ if (u->out_mmap_memblocks) {
+ unsigned i;
+ for (i = 0; i < u->out_nfrags; i++)
+ if (u->out_mmap_memblocks[i])
+ pa_memblock_unref_fixed(u->out_mmap_memblocks[i]);
+ pa_xfree(u->out_mmap_memblocks);
+ }
+
+ if (u->in_mmap_memblocks) {
+ unsigned i;
+ for (i = 0; i < u->in_nfrags; i++)
+ if (u->in_mmap_memblocks[i])
+ pa_memblock_unref_fixed(u->in_mmap_memblocks[i]);
+ pa_xfree(u->in_mmap_memblocks);
+ }
+
+ if (u->in_mmap && u->in_mmap != MAP_FAILED)
+ munmap(u->in_mmap, u->in_hwbuf_size);
+
+ if (u->out_mmap && u->out_mmap != MAP_FAILED)
+ munmap(u->out_mmap, u->out_hwbuf_size);
+
+ if (u->fd >= 0)
+ pa_close(u->fd);
+
+ if (u->mixer_fd >= 0)
+ pa_close(u->mixer_fd);
+
+ pa_xfree(u->device_name);
pa_xfree(u);
}
diff --git a/src/modules/module-pipe-sink.c b/src/modules/module-pipe-sink.c
index 170b046e..75748474 100644
--- a/src/modules/module-pipe-sink.c
+++ b/src/modules/module-pipe-sink.c
@@ -28,22 +28,25 @@
#include <stdlib.h>
#include <sys/stat.h>
#include <stdio.h>
-#include <assert.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>
+#include <sys/ioctl.h>
+#include <poll.h>
#include <pulse/xmalloc.h>
#include <pulsecore/core-error.h>
-#include <pulsecore/iochannel.h>
#include <pulsecore/sink.h>
#include <pulsecore/module.h>
#include <pulsecore/core-util.h>
#include <pulsecore/modargs.h>
#include <pulsecore/log.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/rtpoll.h>
#include "module-pipe-sink-symdef.h"
@@ -58,20 +61,24 @@ PA_MODULE_USAGE(
"rate=<sample rate>"
"channel_map=<channel map>")
-#define DEFAULT_FIFO_NAME "/tmp/music.output"
+#define DEFAULT_FILE_NAME "/tmp/music.output"
#define DEFAULT_SINK_NAME "fifo_output"
struct userdata {
pa_core *core;
+ pa_module *module;
+ pa_sink *sink;
- char *filename;
+ pa_thread *thread;
+ pa_thread_mq thread_mq;
+ pa_rtpoll *rtpoll;
- pa_sink *sink;
- pa_iochannel *io;
- pa_defer_event *defer_event;
+ char *filename;
+ int fd;
pa_memchunk memchunk;
- pa_module *module;
+
+ pa_rtpoll_item *rtpoll_item;
};
static const char* const valid_modargs[] = {
@@ -84,133 +91,191 @@ static const char* const valid_modargs[] = {
NULL
};
-static void do_write(struct userdata *u) {
- ssize_t r;
- assert(u);
+static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+ struct userdata *u = PA_SINK(o)->userdata;
- u->core->mainloop->defer_enable(u->defer_event, 0);
+ switch (code) {
- if (!pa_iochannel_is_writable(u->io))
- return;
+ case PA_SINK_MESSAGE_GET_LATENCY: {
+ size_t n = 0;
+ int l;
- pa_module_set_used(u->module, pa_sink_used_by(u->sink));
-
- if (!u->memchunk.length)
- if (pa_sink_render(u->sink, PIPE_BUF, &u->memchunk) < 0)
- return;
+#ifdef TIOCINQ
+ if (ioctl(u->fd, TIOCINQ, &l) >= 0 && l > 0)
+ n = (size_t) l;
+#endif
- assert(u->memchunk.memblock && u->memchunk.length);
+ n += u->memchunk.length;
- if ((r = pa_iochannel_write(u->io, (uint8_t*) u->memchunk.memblock->data + u->memchunk.index, u->memchunk.length)) < 0) {
- pa_log("write(): %s", pa_cstrerror(errno));
- return;
+ *((pa_usec_t*) data) = pa_bytes_to_usec(n, &u->sink->sample_spec);
+ break;
+ }
}
- u->memchunk.index += r;
- u->memchunk.length -= r;
-
- if (u->memchunk.length <= 0) {
- pa_memblock_unref(u->memchunk.memblock);
- u->memchunk.memblock = NULL;
- }
+ return pa_sink_process_msg(o, code, data, offset, chunk);
}
-static void notify_cb(pa_sink*s) {
- struct userdata *u = s->userdata;
- assert(s && u);
+static void thread_func(void *userdata) {
+ struct userdata *u = userdata;
+ int write_type = 0;
- if (pa_iochannel_is_writable(u->io))
- u->core->mainloop->defer_enable(u->defer_event, 1);
-}
+ pa_assert(u);
-static pa_usec_t get_latency_cb(pa_sink *s) {
- struct userdata *u = s->userdata;
- assert(s && u);
+ pa_log_debug("Thread starting up");
- return u->memchunk.memblock ? pa_bytes_to_usec(u->memchunk.length, &s->sample_spec) : 0;
-}
+ pa_thread_mq_install(&u->thread_mq);
+ pa_rtpoll_install(u->rtpoll);
-static void defer_callback(PA_GCC_UNUSED pa_mainloop_api *m, PA_GCC_UNUSED pa_defer_event*e, void *userdata) {
- struct userdata *u = userdata;
- assert(u);
- do_write(u);
-}
+ for (;;) {
+ struct pollfd *pollfd;
+ int ret;
-static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void*userdata) {
- struct userdata *u = userdata;
- assert(u);
- do_write(u);
+ pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+
+ /* Render some data and write it to the fifo */
+ if (u->sink->thread_info.state == PA_SINK_RUNNING && pollfd->revents) {
+ ssize_t l;
+ void *p;
+
+ if (u->memchunk.length <= 0)
+ pa_sink_render(u->sink, PIPE_BUF, &u->memchunk);
+
+ pa_assert(u->memchunk.length > 0);
+
+ p = pa_memblock_acquire(u->memchunk.memblock);
+ l = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length, &write_type);
+ pa_memblock_release(u->memchunk.memblock);
+
+ pa_assert(l != 0);
+
+ if (l < 0) {
+
+ if (errno == EINTR)
+ continue;
+ else if (errno != EAGAIN) {
+ pa_log("Failed to write data to FIFO: %s", pa_cstrerror(errno));
+ goto fail;
+ }
+
+ } else {
+
+ u->memchunk.index += l;
+ u->memchunk.length -= l;
+
+ if (u->memchunk.length <= 0) {
+ pa_memblock_unref(u->memchunk.memblock);
+ pa_memchunk_reset(&u->memchunk);
+ }
+
+ pollfd->revents = 0;
+ }
+ }
+
+ /* Hmm, nothing to do. Let's sleep */
+ pollfd->events = u->sink->thread_info.state == PA_SINK_RUNNING ? POLLOUT : 0;
+
+ if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
+ goto fail;
+
+ if (ret == 0)
+ goto finish;
+
+ pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+
+ if (pollfd->revents & ~POLLOUT) {
+ pa_log("FIFO shutdown.");
+ goto fail;
+ }
+ }
+
+fail:
+ /* If this was no regular exit from the loop we have to continue
+ * processing messages until we received PA_MESSAGE_SHUTDOWN */
+ pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+ pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+ pa_log_debug("Thread shutting down");
}
-int pa__init(pa_core *c, pa_module*m) {
- struct userdata *u = NULL;
+int pa__init(pa_module*m) {
+ struct userdata *u;
struct stat st;
- const char *p;
- int fd = -1;
pa_sample_spec ss;
pa_channel_map map;
- pa_modargs *ma = NULL;
+ pa_modargs *ma;
char *t;
+ struct pollfd *pollfd;
- assert(c && m);
+ pa_assert(m);
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
- pa_log("failed to parse module arguments");
+ pa_log("Failed to parse module arguments.");
goto fail;
}
- ss = c->default_sample_spec;
+ ss = m->core->default_sample_spec;
if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
- pa_log("invalid sample format specification");
+ pa_log("Invalid sample format specification or channel map");
goto fail;
}
- mkfifo(p = pa_modargs_get_value(ma, "file", DEFAULT_FIFO_NAME), 0777);
+ u = pa_xnew0(struct userdata, 1);
+ u->core = m->core;
+ u->module = m;
+ m->userdata = u;
+ pa_memchunk_reset(&u->memchunk);
+ pa_thread_mq_init(&u->thread_mq, m->core->mainloop);
+ u->rtpoll = pa_rtpoll_new();
+ pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq);
- if ((fd = open(p, O_RDWR)) < 0) {
- pa_log("open('%s'): %s", p, pa_cstrerror(errno));
+ u->filename = pa_xstrdup(pa_modargs_get_value(ma, "file", DEFAULT_FILE_NAME));
+
+ mkfifo(u->filename, 0666);
+ if ((u->fd = open(u->filename, O_RDWR|O_NOCTTY)) < 0) {
+ pa_log("open('%s'): %s", u->filename, pa_cstrerror(errno));
goto fail;
}
- pa_fd_set_cloexec(fd, 1);
+ pa_make_fd_cloexec(u->fd);
+ pa_make_fd_nonblock(u->fd);
- if (fstat(fd, &st) < 0) {
- pa_log("fstat('%s'): %s", p, pa_cstrerror(errno));
+ if (fstat(u->fd, &st) < 0) {
+ pa_log("fstat('%s'): %s", u->filename, pa_cstrerror(errno));
goto fail;
}
if (!S_ISFIFO(st.st_mode)) {
- pa_log("'%s' is not a FIFO.", p);
+ pa_log("'%s' is not a FIFO.", u->filename);
goto fail;
}
- u = pa_xmalloc0(sizeof(struct userdata));
- u->filename = pa_xstrdup(p);
- u->core = c;
- u->module = m;
- m->userdata = u;
-
- if (!(u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, &map))) {
- pa_log("failed to create sink.");
+ if (!(u->sink = pa_sink_new(m->core, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, &map))) {
+ pa_log("Failed to create sink.");
goto fail;
}
- u->sink->notify = notify_cb;
- u->sink->get_latency = get_latency_cb;
+
+ u->sink->parent.process_msg = sink_process_msg;
u->sink->userdata = u;
- pa_sink_set_owner(u->sink, m);
- pa_sink_set_description(u->sink, t = pa_sprintf_malloc("Unix FIFO sink '%s'", p));
+ u->sink->flags = PA_SINK_LATENCY;
+
+ pa_sink_set_module(u->sink, m);
+ pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
+ pa_sink_set_rtpoll(u->sink, u->rtpoll);
+ pa_sink_set_description(u->sink, t = pa_sprintf_malloc("Unix FIFO sink '%s'", u->filename));
pa_xfree(t);
- u->io = pa_iochannel_new(c->mainloop, -1, fd);
- assert(u->io);
- pa_iochannel_set_callback(u->io, io_callback, u);
+ 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->fd;
+ pollfd->events = pollfd->revents = 0;
- u->memchunk.memblock = NULL;
- u->memchunk.length = 0;
+ if (!(u->thread = pa_thread_new(thread_func, u))) {
+ pa_log("Failed to create thread.");
+ goto fail;
+ }
- u->defer_event = c->mainloop->defer_new(c->mainloop, defer_callback, u);
- assert(u->defer_event);
- c->mainloop->defer_enable(u->defer_event, 0);
+ pa_sink_put(u->sink);
pa_modargs_free(ma);
@@ -220,32 +285,48 @@ fail:
if (ma)
pa_modargs_free(ma);
- if (fd >= 0)
- close(fd);
-
- pa__done(c, m);
+ pa__done(m);
return -1;
}
-void pa__done(pa_core *c, pa_module*m) {
+void pa__done(pa_module*m) {
struct userdata *u;
- assert(c && m);
+
+ pa_assert(m);
if (!(u = m->userdata))
return;
+ if (u->sink)
+ pa_sink_unlink(u->sink);
+
+ if (u->thread) {
+ pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+ pa_thread_free(u->thread);
+ }
+
+ pa_thread_mq_done(&u->thread_mq);
+
+ if (u->sink)
+ pa_sink_unref(u->sink);
+
if (u->memchunk.memblock)
- pa_memblock_unref(u->memchunk.memblock);
+ pa_memblock_unref(u->memchunk.memblock);
- pa_sink_disconnect(u->sink);
- pa_sink_unref(u->sink);
- pa_iochannel_free(u->io);
- u->core->mainloop->defer_free(u->defer_event);
+ if (u->rtpoll_item)
+ pa_rtpoll_item_free(u->rtpoll_item);
+
+ if (u->rtpoll)
+ pa_rtpoll_free(u->rtpoll);
+
+ if (u->filename) {
+ unlink(u->filename);
+ pa_xfree(u->filename);
+ }
- assert(u->filename);
- unlink(u->filename);
- pa_xfree(u->filename);
+ if (u->fd >= 0)
+ pa_assert_se(pa_close(u->fd) == 0);
pa_xfree(u);
}
diff --git a/src/modules/module-pipe-source.c b/src/modules/module-pipe-source.c
index 56c721b0..45708c68 100644
--- a/src/modules/module-pipe-source.c
+++ b/src/modules/module-pipe-source.c
@@ -28,22 +28,24 @@
#include <stdlib.h>
#include <sys/stat.h>
#include <stdio.h>
-#include <assert.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>
+#include <sys/poll.h>
#include <pulse/xmalloc.h>
#include <pulsecore/core-error.h>
-#include <pulsecore/iochannel.h>
#include <pulsecore/source.h>
#include <pulsecore/module.h>
#include <pulsecore/core-util.h>
#include <pulsecore/modargs.h>
#include <pulsecore/log.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/rtpoll.h>
#include "module-pipe-source-symdef.h"
@@ -58,18 +60,24 @@ PA_MODULE_USAGE(
"rate=<sample rate> "
"channel_map=<channel map>")
-#define DEFAULT_FIFO_NAME "/tmp/music.input"
+#define DEFAULT_FILE_NAME "/tmp/music.input"
#define DEFAULT_SOURCE_NAME "fifo_input"
struct userdata {
pa_core *core;
+ pa_module *module;
+ pa_source *source;
+
+ pa_thread *thread;
+ pa_thread_mq thread_mq;
+ pa_rtpoll *rtpoll;
char *filename;
+ int fd;
- pa_source *source;
- pa_iochannel *io;
- pa_module *module;
- pa_memchunk chunk;
+ pa_memchunk memchunk;
+
+ pa_rtpoll_item *rtpoll_item;
};
static const char* const valid_modargs[] = {
@@ -82,109 +90,168 @@ static const char* const valid_modargs[] = {
NULL
};
-static void do_read(struct userdata *u) {
- ssize_t r;
- pa_memchunk chunk;
- assert(u);
+static void thread_func(void *userdata) {
+ struct userdata *u = userdata;
+ int read_type = 0;
- if (!pa_iochannel_is_readable(u->io))
- return;
+ pa_assert(u);
- pa_module_set_used(u->module, pa_idxset_size(u->source->outputs));
+ pa_log_debug("Thread starting up");
- if (!u->chunk.memblock) {
- u->chunk.memblock = pa_memblock_new(u->core->mempool, PIPE_BUF);
- u->chunk.index = chunk.length = 0;
- }
+ pa_thread_mq_install(&u->thread_mq);
+ pa_rtpoll_install(u->rtpoll);
- assert(u->chunk.memblock && u->chunk.memblock->length > u->chunk.index);
- if ((r = pa_iochannel_read(u->io, (uint8_t*) u->chunk.memblock->data + u->chunk.index, u->chunk.memblock->length - u->chunk.index)) <= 0) {
- pa_log("read(): %s", pa_cstrerror(errno));
- return;
- }
+ for (;;) {
+ int ret;
+ struct pollfd *pollfd;
+
+ pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+
+ /* Try to read some data and pass it on to the source driver */
+ if (u->source->thread_info.state == PA_SOURCE_RUNNING && pollfd->revents) {
+ ssize_t l;
+ void *p;
- u->chunk.length = r;
- pa_source_post(u->source, &u->chunk);
- u->chunk.index += r;
+ if (!u->memchunk.memblock) {
+ u->memchunk.memblock = pa_memblock_new(u->core->mempool, PIPE_BUF);
+ u->memchunk.index = u->memchunk.length = 0;
+ }
- if (u->chunk.index >= u->chunk.memblock->length) {
- u->chunk.index = u->chunk.length = 0;
- pa_memblock_unref(u->chunk.memblock);
- u->chunk.memblock = NULL;
+ pa_assert(pa_memblock_get_length(u->memchunk.memblock) > u->memchunk.index);
+
+ p = pa_memblock_acquire(u->memchunk.memblock);
+ l = pa_read(u->fd, (uint8_t*) p + u->memchunk.index, pa_memblock_get_length(u->memchunk.memblock) - u->memchunk.index, &read_type);
+ pa_memblock_release(u->memchunk.memblock);
+
+ pa_assert(l != 0); /* EOF cannot happen, since we opened the fifo for both reading and writing */
+
+ if (l < 0) {
+
+ if (errno == EINTR)
+ continue;
+ else if (errno != EAGAIN) {
+ pa_log("Faile to read data from FIFO: %s", pa_cstrerror(errno));
+ goto fail;
+ }
+
+ } else {
+
+ u->memchunk.length = l;
+ pa_source_post(u->source, &u->memchunk);
+ u->memchunk.index += l;
+
+ if (u->memchunk.index >= pa_memblock_get_length(u->memchunk.memblock)) {
+ pa_memblock_unref(u->memchunk.memblock);
+ pa_memchunk_reset(&u->memchunk);
+ }
+
+ pollfd->revents = 0;
+ }
+ }
+
+ /* Hmm, nothing to do. Let's sleep */
+ pollfd->events = u->source->thread_info.state == PA_SOURCE_RUNNING ? POLLIN : 0;
+
+ if ((ret = pa_rtpoll_run(u->rtpoll, 1)) < 0)
+ goto fail;
+
+ if (ret == 0)
+ goto finish;
+
+ pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+ if (pollfd->revents & ~POLLIN) {
+ pa_log("FIFO shutdown.");
+ goto fail;
+ }
}
-}
-static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void*userdata) {
- struct userdata *u = userdata;
- assert(u);
- do_read(u);
+fail:
+ /* If this was no regular exit from the loop we have to continue
+ * processing messages until we received PA_MESSAGE_SHUTDOWN */
+ pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+ pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+ pa_log_debug("Thread shutting down");
}
-int pa__init(pa_core *c, pa_module*m) {
- struct userdata *u = NULL;
+int pa__init(pa_module*m) {
+ struct userdata *u;
struct stat st;
- const char *p;
- int fd = -1;
pa_sample_spec ss;
pa_channel_map map;
- pa_modargs *ma = NULL;
+ pa_modargs *ma;
char *t;
+ struct pollfd *pollfd;
- assert(c && m);
+ pa_assert(m);
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
- pa_log("failed to parse module arguments");
+ pa_log("failed to parse module arguments.");
goto fail;
}
- ss = c->default_sample_spec;
+ ss = m->core->default_sample_spec;
if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
pa_log("invalid sample format specification or channel map");
goto fail;
}
- mkfifo(p = pa_modargs_get_value(ma, "file", DEFAULT_FIFO_NAME), 0777);
+ u = pa_xnew0(struct userdata, 1);
+ u->core = m->core;
+ u->module = m;
+ m->userdata = u;
+ pa_memchunk_reset(&u->memchunk);
+ pa_thread_mq_init(&u->thread_mq, m->core->mainloop);
+ u->rtpoll = pa_rtpoll_new();
+ pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq);
+
+ u->filename = pa_xstrdup(pa_modargs_get_value(ma, "file", DEFAULT_FILE_NAME));
- if ((fd = open(p, O_RDWR)) < 0) {
- pa_log("open('%s'): %s", p, pa_cstrerror(errno));
+ mkfifo(u->filename, 0666);
+ if ((u->fd = open(u->filename, O_RDWR|O_NOCTTY)) < 0) {
+ pa_log("open('%s'): %s", u->filename, pa_cstrerror(errno));
goto fail;
}
- pa_fd_set_cloexec(fd, 1);
+ pa_make_fd_cloexec(u->fd);
+ pa_make_fd_nonblock(u->fd);
- if (fstat(fd, &st) < 0) {
- pa_log("fstat('%s'): %s", p, pa_cstrerror(errno));
+ if (fstat(u->fd, &st) < 0) {
+ pa_log("fstat('%s'): %s",u->filename, pa_cstrerror(errno));
goto fail;
}
if (!S_ISFIFO(st.st_mode)) {
- pa_log("'%s' is not a FIFO.", p);
+ pa_log("'%s' is not a FIFO.", u->filename);
goto fail;
}
- u = pa_xmalloc0(sizeof(struct userdata));
-
- u->filename = pa_xstrdup(p);
- u->core = c;
-
- if (!(u->source = pa_source_new(c, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, &map))) {
- pa_log("failed to create source.");
+ if (!(u->source = pa_source_new(m->core, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, &map))) {
+ pa_log("Failed to create source.");
goto fail;
}
+
u->source->userdata = u;
- pa_source_set_owner(u->source, m);
- pa_source_set_description(u->source, t = pa_sprintf_malloc("Unix FIFO source '%s'", p));
+ u->source->flags = 0;
+
+ pa_source_set_module(u->source, m);
+ pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
+ pa_source_set_rtpoll(u->source, u->rtpoll);
+ pa_source_set_description(u->source, t = pa_sprintf_malloc("Unix FIFO source '%s'", u->filename));
pa_xfree(t);
- u->io = pa_iochannel_new(c->mainloop, fd, -1);
- assert(u->io);
- pa_iochannel_set_callback(u->io, io_callback, u);
+ 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->fd;
+ pollfd->events = pollfd->revents = 0;
- u->chunk.memblock = NULL;
- u->chunk.index = u->chunk.length = 0;
+ if (!(u->thread = pa_thread_new(thread_func, u))) {
+ pa_log("Failed to create thread.");
+ goto fail;
+ }
- u->module = m;
- m->userdata = u;
+ pa_source_put(u->source);
pa_modargs_free(ma);
@@ -194,31 +261,48 @@ fail:
if (ma)
pa_modargs_free(ma);
- if (fd >= 0)
- close(fd);
-
- pa__done(c, m);
+ pa__done(m);
return -1;
}
-void pa__done(pa_core *c, pa_module*m) {
+void pa__done(pa_module*m) {
struct userdata *u;
- assert(c && m);
+
+ pa_assert(m);
if (!(u = m->userdata))
return;
- if (u->chunk.memblock)
- pa_memblock_unref(u->chunk.memblock);
+ if (u->source)
+ pa_source_unlink(u->source);
+
+ if (u->thread) {
+ pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+ pa_thread_free(u->thread);
+ }
+
+ pa_thread_mq_done(&u->thread_mq);
+
+ if (u->source)
+ pa_source_unref(u->source);
+
+ if (u->memchunk.memblock)
+ pa_memblock_unref(u->memchunk.memblock);
- pa_source_disconnect(u->source);
- pa_source_unref(u->source);
- pa_iochannel_free(u->io);
+ if (u->rtpoll_item)
+ pa_rtpoll_item_free(u->rtpoll_item);
+
+ if (u->rtpoll)
+ pa_rtpoll_free(u->rtpoll);
+
+ if (u->filename) {
+ unlink(u->filename);
+ pa_xfree(u->filename);
+ }
- assert(u->filename);
- unlink(u->filename);
- pa_xfree(u->filename);
+ if (u->fd >= 0)
+ pa_assert_se(pa_close(u->fd) == 0);
pa_xfree(u);
}
diff --git a/src/modules/module-protocol-stub.c b/src/modules/module-protocol-stub.c
index 5c8733fb..6bd78079 100644
--- a/src/modules/module-protocol-stub.c
+++ b/src/modules/module-protocol-stub.c
@@ -29,7 +29,6 @@
#include <string.h>
#include <errno.h>
#include <stdio.h>
-#include <assert.h>
#include <unistd.h>
#include <limits.h>
@@ -43,10 +42,9 @@
#include <netinet/in.h>
#endif
-#include "../pulsecore/winsock.h"
-
#include <pulse/xmalloc.h>
+#include <pulsecore/winsock.h>
#include <pulsecore/core-error.h>
#include <pulsecore/module.h>
#include <pulsecore/socket-server.h>
@@ -154,7 +152,6 @@
#define protocol_free pa_protocol_esound_free
#define TCPWRAP_SERVICE "esound"
#define IPV4_PORT ESD_DEFAULT_PORT
- #define UNIX_SOCKET ESD_UNIX_SOCKET_NAME
#define MODULE_ARGUMENTS_COMMON "sink", "source", "auth-anonymous", "cookie",
#ifdef USE_TCP_SOCKETS
#include "module-esound-protocol-tcp-symdef.h"
@@ -205,10 +202,9 @@ struct userdata {
#endif
};
-int pa__init(pa_core *c, pa_module*m) {
+int pa__init(pa_module*m) {
pa_modargs *ma = NULL;
int ret = -1;
-
struct userdata *u = NULL;
#if defined(USE_TCP_SOCKETS)
@@ -219,9 +215,13 @@ int pa__init(pa_core *c, pa_module*m) {
pa_socket_server *s;
int r;
char tmp[PATH_MAX];
+
+#if defined(USE_PROTOCOL_ESOUND)
+ char tmp2[PATH_MAX];
+#endif
#endif
- assert(c && m);
+ pa_assert(m);
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
pa_log("Failed to parse module arguments");
@@ -239,22 +239,22 @@ int pa__init(pa_core *c, pa_module*m) {
listen_on = pa_modargs_get_value(ma, "listen", NULL);
if (listen_on) {
- s_ipv6 = pa_socket_server_new_ipv6_string(c->mainloop, listen_on, port, TCPWRAP_SERVICE);
- s_ipv4 = pa_socket_server_new_ipv4_string(c->mainloop, listen_on, port, TCPWRAP_SERVICE);
+ s_ipv6 = pa_socket_server_new_ipv6_string(m->core->mainloop, listen_on, port, TCPWRAP_SERVICE);
+ s_ipv4 = pa_socket_server_new_ipv4_string(m->core->mainloop, listen_on, port, TCPWRAP_SERVICE);
} else {
- s_ipv6 = pa_socket_server_new_ipv6_any(c->mainloop, port, TCPWRAP_SERVICE);
- s_ipv4 = pa_socket_server_new_ipv4_any(c->mainloop, port, TCPWRAP_SERVICE);
+ s_ipv6 = pa_socket_server_new_ipv6_any(m->core->mainloop, port, TCPWRAP_SERVICE);
+ s_ipv4 = pa_socket_server_new_ipv4_any(m->core->mainloop, port, TCPWRAP_SERVICE);
}
if (!s_ipv4 && !s_ipv6)
goto fail;
if (s_ipv4)
- if (!(u->protocol_ipv4 = protocol_new(c, s_ipv4, m, ma)))
+ if (!(u->protocol_ipv4 = protocol_new(m->core, s_ipv4, m, ma)))
pa_socket_server_unref(s_ipv4);
if (s_ipv6)
- if (!(u->protocol_ipv6 = protocol_new(c, s_ipv6, m, ma)))
+ if (!(u->protocol_ipv6 = protocol_new(m->core, s_ipv6, m, ma)))
pa_socket_server_unref(s_ipv6);
if (!u->protocol_ipv4 && !u->protocol_ipv6)
@@ -262,18 +262,23 @@ int pa__init(pa_core *c, pa_module*m) {
#else
- pa_runtime_path(pa_modargs_get_value(ma, "socket", UNIX_SOCKET), tmp, sizeof(tmp));
- u->socket_path = pa_xstrdup(tmp);
-
#if defined(USE_PROTOCOL_ESOUND)
+ snprintf(tmp2, sizeof(tmp2), "/tmp/.esd-%lu/socket", (unsigned long) getuid());
+ pa_runtime_path(pa_modargs_get_value(ma, "socket", tmp2), tmp, sizeof(tmp));
+ u->socket_path = pa_xstrdup(tmp);
+
/* This socket doesn't reside in our own runtime dir but in
* /tmp/.esd/, hence we have to create the dir first */
- if (pa_make_secure_parent_dir(u->socket_path, c->is_system_instance ? 0755 : 0700, (uid_t)-1, (gid_t)-1) < 0) {
+ if (pa_make_secure_parent_dir(u->socket_path, m->core->is_system_instance ? 0755 : 0700, (uid_t)-1, (gid_t)-1) < 0) {
pa_log("Failed to create socket directory '%s': %s\n", u->socket_path, pa_cstrerror(errno));
goto fail;
}
+
+#else
+ pa_runtime_path(pa_modargs_get_value(ma, "socket", UNIX_SOCKET), tmp, sizeof(tmp));
+ u->socket_path = pa_xstrdup(tmp);
#endif
if ((r = pa_unix_socket_remove_stale(tmp)) < 0) {
@@ -284,10 +289,10 @@ int pa__init(pa_core *c, pa_module*m) {
if (r)
pa_log("Removed stale UNIX socket '%s'.", tmp);
- if (!(s = pa_socket_server_new_unix(c->mainloop, tmp)))
+ if (!(s = pa_socket_server_new_unix(m->core->mainloop, tmp)))
goto fail;
- if (!(u->protocol_unix = protocol_new(c, s, m, ma)))
+ if (!(u->protocol_unix = protocol_new(m->core, s, m, ma)))
goto fail;
#endif
@@ -333,11 +338,10 @@ fail:
goto finish;
}
-void pa__done(pa_core *c, pa_module*m) {
+void pa__done(pa_module*m) {
struct userdata *u;
- assert(c);
- assert(m);
+ pa_assert(m);
u = m->userdata;
@@ -358,7 +362,6 @@ void pa__done(pa_core *c, pa_module*m) {
}
#endif
-
pa_xfree(u->socket_path);
#endif
diff --git a/src/modules/module-remap-sink.c b/src/modules/module-remap-sink.c
new file mode 100644
index 00000000..e863c0c3
--- /dev/null
+++ b/src/modules/module-remap-sink.c
@@ -0,0 +1,334 @@
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 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 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/rtpoll.h>
+
+#include "module-remap-sink-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering")
+PA_MODULE_DESCRIPTION("Virtual channel remapping sink")
+PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_USAGE(
+ "sink_name=<name for the sink> "
+ "master=<name of sink to remap> "
+ "master_channel_map=<channel map> "
+ "format=<sample format> "
+ "channels=<number of channels> "
+ "rate=<sample rate> "
+ "channel_map=<channel map>")
+
+struct userdata {
+ pa_core *core;
+ pa_module *module;
+
+ pa_sink *sink, *master;
+ pa_sink_input *sink_input;
+
+ pa_memchunk memchunk;
+};
+
+static const char* const valid_modargs[] = {
+ "sink_name",
+ "master",
+ "master_channel_map",
+ "rate",
+ "format",
+ "channels",
+ "channel_map",
+ NULL
+};
+
+/* Called from I/O thread context */
+static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+ struct userdata *u = PA_SINK(o)->userdata;
+
+ switch (code) {
+
+ case PA_SINK_MESSAGE_GET_LATENCY: {
+ pa_usec_t usec = 0;
+
+ if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
+ usec = 0;
+
+ *((pa_usec_t*) data) = usec + pa_bytes_to_usec(u->memchunk.length, &u->sink->sample_spec);
+ return 0;
+ }
+ }
+
+ return pa_sink_process_msg(o, code, data, offset, chunk);
+}
+
+/* Called from main context */
+static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
+ struct userdata *u;
+
+ pa_sink_assert_ref(s);
+ pa_assert_se(u = s->userdata);
+
+ if (PA_SINK_LINKED(state) && u->sink_input && PA_SINK_INPUT_LINKED(pa_sink_input_get_state(u->sink_input)))
+ pa_sink_input_cork(u->sink_input, state == PA_SINK_SUSPENDED);
+
+ return 0;
+}
+
+/* Called from I/O thread context */
+static int sink_input_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+ struct userdata *u = PA_SINK_INPUT(o)->userdata;
+
+ switch (code) {
+ case PA_SINK_INPUT_MESSAGE_GET_LATENCY:
+ *((pa_usec_t*) data) = pa_bytes_to_usec(u->memchunk.length, &u->sink_input->sample_spec);
+
+ /* Fall through, the default handler will add in the extra
+ * latency added by the resampler */
+ break;
+ }
+
+ return pa_sink_input_process_msg(o, code, data, offset, chunk);
+}
+
+/* Called from I/O thread context */
+static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
+ struct userdata *u;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(u = i->userdata);
+
+ if (!u->memchunk.memblock)
+ pa_sink_render(u->sink, length, &u->memchunk);
+
+ pa_assert(u->memchunk.memblock);
+ *chunk = u->memchunk;
+ pa_memblock_ref(chunk->memblock);
+ return 0;
+}
+
+/* Called from I/O thread context */
+static void sink_input_drop_cb(pa_sink_input *i, size_t length) {
+ struct userdata *u;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(u = i->userdata);
+ pa_assert(length > 0);
+
+ if (u->memchunk.memblock) {
+
+ if (length < u->memchunk.length) {
+ u->memchunk.index += length;
+ u->memchunk.length -= length;
+ return;
+ }
+
+ pa_memblock_unref(u->memchunk.memblock);
+ length -= u->memchunk.length;
+ pa_memchunk_reset(&u->memchunk);
+ }
+
+ if (length > 0)
+ pa_sink_skip(u->sink, length);
+}
+
+/* Called from I/O thread context */
+static void sink_input_detach_cb(pa_sink_input *i) {
+ struct userdata *u;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(u = i->userdata);
+
+ pa_sink_detach_within_thread(u->sink);
+}
+
+/* Called from I/O thread context */
+static void sink_input_attach_cb(pa_sink_input *i) {
+ struct userdata *u;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(u = i->userdata);
+
+ pa_sink_set_asyncmsgq(u->sink, i->sink->asyncmsgq);
+ pa_sink_set_rtpoll(u->sink, i->sink->rtpoll);
+
+ pa_sink_attach_within_thread(u->sink);
+}
+
+/* Called from main context */
+static void sink_input_kill_cb(pa_sink_input *i) {
+ struct userdata *u;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(u = i->userdata);
+
+ pa_sink_input_unlink(u->sink_input);
+ pa_sink_input_unref(u->sink_input);
+ u->sink_input = NULL;
+
+ pa_sink_unlink(u->sink);
+ pa_sink_unref(u->sink);
+ u->sink = NULL;
+
+ pa_module_unload_request(u->module);
+}
+
+int pa__init(pa_module*m) {
+ struct userdata *u;
+ pa_sample_spec ss;
+ pa_channel_map sink_map, stream_map;
+ pa_modargs *ma;
+ char *t;
+ pa_sink *master;
+ pa_sink_input_new_data data;
+ char *default_sink_name = NULL;
+
+ pa_assert(m);
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log("Failed to parse module arguments.");
+ goto fail;
+ }
+
+ if (!(master = pa_namereg_get(m->core, pa_modargs_get_value(ma, "master", NULL), PA_NAMEREG_SINK, 1))) {
+ pa_log("Master sink not found");
+ goto fail;
+ }
+
+ ss = master->sample_spec;
+ sink_map = master->channel_map;
+ if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &sink_map, PA_CHANNEL_MAP_DEFAULT) < 0) {
+ pa_log("Invalid sample format specification or channel map");
+ goto fail;
+ }
+
+ stream_map = sink_map;
+ if (pa_modargs_get_channel_map(ma, "master_channel_map", &stream_map) < 0) {
+ pa_log("Invalid master hannel map");
+ goto fail;
+ }
+
+ if (stream_map.channels != ss.channels) {
+ pa_log("Number of channels doesn't match");
+ goto fail;
+ }
+
+ u = pa_xnew0(struct userdata, 1);
+ u->core = m->core;
+ u->module = m;
+ m->userdata = u;
+ u->master = master;
+ pa_memchunk_reset(&u->memchunk);
+
+ default_sink_name = pa_sprintf_malloc("%s.remapped", master->name);
+
+ /* Create sink */
+ if (!(u->sink = pa_sink_new(m->core, __FILE__, pa_modargs_get_value(ma, "sink_name", default_sink_name), 0, &ss, &sink_map))) {
+ pa_log("Failed to create sink.");
+ goto fail;
+ }
+
+ u->sink->parent.process_msg = sink_process_msg;
+ u->sink->set_state = sink_set_state;
+ u->sink->userdata = u;
+ u->sink->flags = PA_SINK_LATENCY;
+
+ pa_sink_set_module(u->sink, m);
+ pa_sink_set_description(u->sink, t = pa_sprintf_malloc("Remapped %s", master->description));
+ pa_xfree(t);
+ pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq);
+ pa_sink_set_rtpoll(u->sink, master->rtpoll);
+
+ /* Create sink input */
+ pa_sink_input_new_data_init(&data);
+ data.sink = u->master;
+ data.driver = __FILE__;
+ data.name = "Remapped Stream";
+ pa_sink_input_new_data_set_sample_spec(&data, &ss);
+ pa_sink_input_new_data_set_channel_map(&data, &stream_map);
+ data.module = m;
+
+ if (!(u->sink_input = pa_sink_input_new(m->core, &data, PA_SINK_INPUT_DONT_MOVE)))
+ goto fail;
+
+ u->sink_input->parent.process_msg = sink_input_process_msg;
+ u->sink_input->peek = sink_input_peek_cb;
+ u->sink_input->drop = sink_input_drop_cb;
+ u->sink_input->kill = sink_input_kill_cb;
+ u->sink_input->attach = sink_input_attach_cb;
+ u->sink_input->detach = sink_input_detach_cb;
+ u->sink_input->userdata = u;
+
+ pa_sink_put(u->sink);
+ pa_sink_input_put(u->sink_input);
+
+ pa_modargs_free(ma);
+ pa_xfree(default_sink_name);
+
+ return 0;
+
+fail:
+ if (ma)
+ pa_modargs_free(ma);
+
+ pa__done(m);
+
+ pa_xfree(default_sink_name);
+
+ return -1;
+}
+
+void pa__done(pa_module*m) {
+ struct userdata *u;
+
+ pa_assert(m);
+
+ if (!(u = m->userdata))
+ return;
+
+ if (u->sink_input) {
+ pa_sink_input_unlink(u->sink_input);
+ pa_sink_input_unref(u->sink_input);
+ }
+
+ if (u->sink) {
+ pa_sink_unlink(u->sink);
+ pa_sink_unref(u->sink);
+ }
+
+ if (u->memchunk.memblock)
+ pa_memblock_unref(u->memchunk.memblock);
+
+ pa_xfree(u);
+}
diff --git a/src/modules/module-rescue-streams.c b/src/modules/module-rescue-streams.c
index 25005f25..5cabef76 100644
--- a/src/modules/module-rescue-streams.c
+++ b/src/modules/module-rescue-streams.c
@@ -52,20 +52,26 @@ static pa_hook_result_t sink_hook_callback(pa_core *c, pa_sink *sink, void* user
pa_sink_input *i;
pa_sink *target;
- assert(c);
- assert(sink);
+ pa_assert(c);
+ pa_assert(sink);
if (!pa_idxset_size(sink->inputs)) {
pa_log_debug("No sink inputs to move away.");
return PA_HOOK_OK;
}
- if (!(target = pa_namereg_get(c, NULL, PA_NAMEREG_SINK, 0))) {
- pa_log_info("No evacuation sink found.");
- return PA_HOOK_OK;
- }
+ if (!(target = pa_namereg_get(c, NULL, PA_NAMEREG_SINK, 0)) || target == sink) {
+ uint32_t idx;
+
+ for (target = pa_idxset_first(c->sinks, &idx); target; target = pa_idxset_next(c->sinks, &idx))
+ if (target != sink)
+ break;
- assert(target != sink);
+ if (!target) {
+ pa_log_info("No evacuation sink found.");
+ return PA_HOOK_OK;
+ }
+ }
while ((i = pa_idxset_first(sink->inputs, NULL))) {
if (pa_sink_input_move_to(i, target, 1) < 0) {
@@ -84,20 +90,28 @@ static pa_hook_result_t source_hook_callback(pa_core *c, pa_source *source, void
pa_source_output *o;
pa_source *target;
- assert(c);
- assert(source);
+ pa_assert(c);