summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/.gitignore56
-rw-r--r--src/Makefile.am496
-rw-r--r--src/daemon/caps.c114
-rw-r--r--src/daemon/caps.h17
-rw-r--r--src/daemon/cmdline.c125
-rw-r--r--src/daemon/cmdline.h10
-rw-r--r--src/daemon/cpulimit.c89
-rw-r--r--src/daemon/cpulimit.h10
-rw-r--r--src/daemon/daemon-conf.c549
-rw-r--r--src/daemon/daemon-conf.h63
-rw-r--r--src/daemon/daemon.conf.in104
-rwxr-xr-xsrc/daemon/default.pa.in81
-rw-r--r--src/daemon/dumpmodules.c46
-rw-r--r--src/daemon/dumpmodules.h4
-rwxr-xr-xsrc/daemon/esdcompat.in2
-rw-r--r--src/daemon/ltdl-bind-now.c195
-rw-r--r--src/daemon/ltdl-bind-now.h30
-rw-r--r--src/daemon/main.c664
-rw-r--r--src/daemon/org.pulseaudio.policy50
-rw-r--r--src/daemon/polkit.c165
-rw-r--r--src/daemon/polkit.h (renamed from src/pulsecore/core-def.h)19
-rw-r--r--src/daemon/pulseaudio-module-xsmp.desktop10
-rwxr-xr-xsrc/depmod.py3
-rw-r--r--src/map-file256
-rw-r--r--src/modules/.gitignore1
-rw-r--r--src/modules/alsa-util.c956
-rw-r--r--src/modules/alsa-util.h72
-rw-r--r--src/modules/bt-proximity-helper.c202
-rw-r--r--src/modules/dbus-util.c296
-rw-r--r--src/modules/dbus-util.h10
-rw-r--r--src/modules/gconf/gconf-helper.c44
-rw-r--r--src/modules/gconf/module-gconf.c218
-rw-r--r--src/modules/ladspa.h603
-rw-r--r--src/modules/module-alsa-sink.c1587
-rw-r--r--src/modules/module-alsa-source.c1401
-rw-r--r--src/modules/module-always-sink.c178
-rw-r--r--src/modules/module-bt-proximity.c490
-rw-r--r--src/modules/module-cli.c59
-rw-r--r--src/modules/module-combine.c1268
-rw-r--r--src/modules/module-console-kit.c334
-rw-r--r--src/modules/module-default-device-restore.c201
-rw-r--r--src/modules/module-defs.h.m48
-rw-r--r--src/modules/module-detect.c97
-rw-r--r--src/modules/module-device-restore.c348
-rw-r--r--src/modules/module-esound-compat-spawnfd.c36
-rw-r--r--src/modules/module-esound-compat-spawnpid.c31
-rw-r--r--src/modules/module-esound-sink.c568
-rw-r--r--src/modules/module-hal-detect.c882
-rw-r--r--src/modules/module-jack-sink.c466
-rw-r--r--src/modules/module-jack-source.c427
-rw-r--r--src/modules/module-ladspa-sink.c799
-rw-r--r--src/modules/module-lirc.c105
-rw-r--r--src/modules/module-match.c85
-rw-r--r--src/modules/module-mmkbd-evdev.c83
-rw-r--r--src/modules/module-native-protocol-fd.c40
-rw-r--r--src/modules/module-null-sink.c294
-rw-r--r--src/modules/module-oss-mmap.c634
-rw-r--r--src/modules/module-oss.c1619
-rw-r--r--src/modules/module-pipe-sink.c360
-rw-r--r--src/modules/module-pipe-source.c300
-rw-r--r--src/modules/module-position-event-sounds.c165
-rw-r--r--src/modules/module-protocol-stub.c156
-rw-r--r--src/modules/module-remap-sink.c429
-rw-r--r--src/modules/module-rescue-streams.c97
-rw-r--r--src/modules/module-sine.c137
-rw-r--r--src/modules/module-solaris.c726
-rw-r--r--src/modules/module-suspend-on-idle.c444
-rw-r--r--src/modules/module-tunnel.c1813
-rw-r--r--src/modules/module-volume-restore.c301
-rw-r--r--src/modules/module-waveout.c43
-rw-r--r--src/modules/module-x11-bell.c124
-rw-r--r--src/modules/module-x11-publish.c113
-rw-r--r--src/modules/module-x11-xsmp.c247
-rw-r--r--src/modules/module-zeroconf-discover.c438
-rw-r--r--src/modules/module-zeroconf-publish.c707
-rw-r--r--src/modules/oss-util.c193
-rw-r--r--src/modules/oss-util.h20
-rw-r--r--src/modules/rtp/module-rtp-recv.c533
-rw-r--r--src/modules/rtp/module-rtp-send.c189
-rw-r--r--src/modules/rtp/rtp.c173
-rw-r--r--src/modules/rtp/rtp.h12
-rw-r--r--src/modules/rtp/sap.c79
-rw-r--r--src/modules/rtp/sap.h14
-rw-r--r--src/modules/rtp/sdp.c78
-rw-r--r--src/modules/rtp/sdp.h10
-rw-r--r--src/pulse/.gitignore1
-rw-r--r--src/pulse/browser.c143
-rw-r--r--src/pulse/browser.h12
-rw-r--r--src/pulse/cdecl.h10
-rw-r--r--src/pulse/channelmap.c254
-rw-r--r--src/pulse/channelmap.h49
-rw-r--r--src/pulse/client-conf-x11.c21
-rw-r--r--src/pulse/client-conf-x11.h4
-rw-r--r--src/pulse/client-conf.c69
-rw-r--r--src/pulse/client-conf.h8
-rw-r--r--src/pulse/client.conf.in31
-rw-r--r--src/pulse/context.c789
-rw-r--r--src/pulse/context.h53
-rw-r--r--src/pulse/def.h264
-rw-r--r--src/pulse/error.c11
-rw-r--r--src/pulse/error.h11
-rw-r--r--src/pulse/gccmacro.h (renamed from src/pulsecore/gccmacro.h)53
-rw-r--r--src/pulse/glib-mainloop.c104
-rw-r--r--src/pulse/glib-mainloop.h11
-rw-r--r--src/pulse/internal.h103
-rw-r--r--src/pulse/introspect.c602
-rw-r--r--src/pulse/introspect.h308
-rw-r--r--src/pulse/mainloop-api.c35
-rw-r--r--src/pulse/mainloop-api.h13
-rw-r--r--src/pulse/mainloop-signal.c101
-rw-r--r--src/pulse/mainloop-signal.h25
-rw-r--r--src/pulse/mainloop.c274
-rw-r--r--src/pulse/mainloop.h13
-rw-r--r--src/pulse/operation.c103
-rw-r--r--src/pulse/operation.h10
-rw-r--r--src/pulse/proplist.c321
-rw-r--r--src/pulse/proplist.h217
-rw-r--r--src/pulse/pulseaudio.h17
-rw-r--r--src/pulse/sample.c119
-rw-r--r--src/pulse/sample.h64
-rw-r--r--src/pulse/scache.c174
-rw-r--r--src/pulse/scache.h42
-rw-r--r--src/pulse/simple.c119
-rw-r--r--src/pulse/simple.h21
-rw-r--r--src/pulse/stream.c1847
-rw-r--r--src/pulse/stream.h182
-rw-r--r--src/pulse/subscribe.c39
-rw-r--r--src/pulse/subscribe.h11
-rw-r--r--src/pulse/thread-mainloop.c77
-rw-r--r--src/pulse/thread-mainloop.h18
-rw-r--r--src/pulse/timeval.c94
-rw-r--r--src/pulse/timeval.h32
-rw-r--r--src/pulse/utf8.c56
-rw-r--r--src/pulse/utf8.h14
-rw-r--r--src/pulse/util.c95
-rw-r--r--src/pulse/util.h14
-rw-r--r--src/pulse/version.h.in18
-rw-r--r--src/pulse/volume.c56
-rw-r--r--src/pulse/volume.h49
-rw-r--r--src/pulse/xmalloc.c45
-rw-r--r--src/pulse/xmalloc.h19
-rw-r--r--src/pulsecore/anotify.c143
-rw-r--r--src/pulsecore/asyncmsgq.c319
-rw-r--r--src/pulsecore/asyncmsgq.h79
-rw-r--r--src/pulsecore/asyncq.c321
-rw-r--r--src/pulsecore/asyncq.h65
-rw-r--r--src/pulsecore/atomic.h435
-rw-r--r--src/pulsecore/authkey-prop.c63
-rw-r--r--src/pulsecore/authkey-prop.h10
-rw-r--r--src/pulsecore/authkey.c111
-rw-r--r--src/pulsecore/authkey.h10
-rw-r--r--src/pulsecore/autoload.c78
-rw-r--r--src/pulsecore/autoload.h14
-rw-r--r--src/pulsecore/avahi-wrap.c86
-rw-r--r--src/pulsecore/avahi-wrap.h10
-rw-r--r--src/pulsecore/cli-command.c868
-rw-r--r--src/pulsecore/cli-command.h22
-rw-r--r--src/pulsecore/cli-text.c359
-rw-r--r--src/pulsecore/cli-text.h10
-rw-r--r--src/pulsecore/cli.c58
-rw-r--r--src/pulsecore/cli.h10
-rw-r--r--src/pulsecore/client.c61
-rw-r--r--src/pulsecore/client.h18
-rw-r--r--src/pulsecore/conf-parser.c78
-rw-r--r--src/pulsecore/conf-parser.h4
-rw-r--r--src/pulsecore/core-error.c189
-rw-r--r--src/pulsecore/core-error.h11
-rw-r--r--src/pulsecore/core-scache.c208
-rw-r--r--src/pulsecore/core-scache.h31
-rw-r--r--src/pulsecore/core-subscribe.c76
-rw-r--r--src/pulsecore/core-subscribe.h10
-rw-r--r--src/pulsecore/core-util.c1441
-rw-r--r--src/pulsecore/core-util.h129
-rw-r--r--src/pulsecore/core.c110
-rw-r--r--src/pulsecore/core.h78
-rw-r--r--src/pulsecore/creds.h14
-rw-r--r--src/pulsecore/dllmain.c4
-rw-r--r--src/pulsecore/dynarray.c30
-rw-r--r--src/pulsecore/dynarray.h10
-rw-r--r--src/pulsecore/endianmacros.h115
-rw-r--r--src/pulsecore/envelope.c781
-rw-r--r--src/pulsecore/envelope.h53
-rw-r--r--src/pulsecore/esound.h12
-rw-r--r--src/pulsecore/fdsem.c324
-rw-r--r--src/pulsecore/fdsem.h55
-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.c88
-rw-r--r--src/pulsecore/flist.h37
-rw-r--r--src/pulsecore/g711.c5062
-rw-r--r--src/pulsecore/g711.h4
-rw-r--r--src/pulsecore/hashmap.c111
-rw-r--r--src/pulsecore/hashmap.h21
-rw-r--r--src/pulsecore/hook-list.c93
-rw-r--r--src/pulsecore/hook-list.h29
-rw-r--r--src/pulsecore/idxset.c185
-rw-r--r--src/pulsecore/idxset.h16
-rw-r--r--src/pulsecore/inet_ntop.c15
-rw-r--r--src/pulsecore/inet_pton.c11
-rw-r--r--src/pulsecore/iochannel.c299
-rw-r--r--src/pulsecore/iochannel.h31
-rw-r--r--src/pulsecore/ioline.c213
-rw-r--r--src/pulsecore/ioline.h14
-rw-r--r--src/pulsecore/ipacl.c65
-rw-r--r--src/pulsecore/ipacl.h11
-rw-r--r--src/pulsecore/llist.h133
-rw-r--r--src/pulsecore/log.c106
-rw-r--r--src/pulsecore/log.h13
-rw-r--r--src/pulsecore/ltdl-helper.c64
-rw-r--r--src/pulsecore/ltdl-helper.h32
-rw-r--r--src/pulsecore/macro.h224
-rw-r--r--src/pulsecore/mcalign.c90
-rw-r--r--src/pulsecore/mcalign.h17
-rw-r--r--src/pulsecore/memblock.c599
-rw-r--r--src/pulsecore/memblock.h59
-rw-r--r--src/pulsecore/memblockq.c814
-rw-r--r--src/pulsecore/memblockq.h87
-rw-r--r--src/pulsecore/memchunk.c89
-rw-r--r--src/pulsecore/memchunk.h24
-rw-r--r--src/pulsecore/modargs.c96
-rw-r--r--src/pulsecore/modargs.h17
-rw-r--r--src/pulsecore/modinfo.c46
-rw-r--r--src/pulsecore/modinfo.h14
-rw-r--r--src/pulsecore/module.c187
-rw-r--r--src/pulsecore/module.h43
-rw-r--r--src/pulsecore/msgobject.c47
-rw-r--r--src/pulsecore/msgobject.h52
-rw-r--r--src/pulsecore/mutex-posix.c92
-rw-r--r--src/pulsecore/mutex-win32.c12
-rw-r--r--src/pulsecore/mutex.h21
-rw-r--r--src/pulsecore/namereg.c115
-rw-r--r--src/pulsecore/namereg.h15
-rw-r--r--src/pulsecore/native-common.h70
-rw-r--r--src/pulsecore/object.c70
-rw-r--r--src/pulsecore/object.h104
-rw-r--r--src/pulsecore/once-posix.c69
-rw-r--r--src/pulsecore/once-win32.c67
-rw-r--r--src/pulsecore/once.c94
-rw-r--r--src/pulsecore/once.h58
-rw-r--r--src/pulsecore/packet.c46
-rw-r--r--src/pulsecore/packet.h14
-rw-r--r--src/pulsecore/parseaddr.c43
-rw-r--r--src/pulsecore/parseaddr.h10
-rw-r--r--src/pulsecore/pdispatch.c128
-rw-r--r--src/pulsecore/pdispatch.h11
-rw-r--r--src/pulsecore/pid.c221
-rw-r--r--src/pulsecore/pid.h16
-rw-r--r--src/pulsecore/pipe.c28
-rw-r--r--src/pulsecore/pipe.h10
-rw-r--r--src/pulsecore/play-memblockq.c276
-rw-r--r--src/pulsecore/play-memblockq.h25
-rw-r--r--src/pulsecore/play-memchunk.c106
-rw-r--r--src/pulsecore/play-memchunk.h15
-rw-r--r--src/pulsecore/poll.c23
-rw-r--r--src/pulsecore/poll.h23
-rw-r--r--src/pulsecore/proplist-util.c118
-rw-r--r--src/pulsecore/proplist-util.h29
-rw-r--r--src/pulsecore/props.c59
-rw-r--r--src/pulsecore/props.h10
-rw-r--r--src/pulsecore/protocol-cli.c38
-rw-r--r--src/pulsecore/protocol-cli.h10
-rw-r--r--src/pulsecore/protocol-esound.c928
-rw-r--r--src/pulsecore/protocol-esound.h11
-rw-r--r--src/pulsecore/protocol-http.c73
-rw-r--r--src/pulsecore/protocol-http.h10
-rw-r--r--src/pulsecore/protocol-native.c3274
-rw-r--r--src/pulsecore/protocol-native.h11
-rw-r--r--src/pulsecore/protocol-simple.c540
-rw-r--r--src/pulsecore/protocol-simple.h10
-rw-r--r--src/pulsecore/pstream-util.c34
-rw-r--r--src/pulsecore/pstream-util.h10
-rw-r--r--src/pulsecore/pstream.c484
-rw-r--r--src/pulsecore/pstream.h30
-rw-r--r--src/pulsecore/queue.c64
-rw-r--r--src/pulsecore/queue.h10
-rw-r--r--src/pulsecore/random.c38
-rw-r--r--src/pulsecore/random.h13
-rw-r--r--src/pulsecore/refcnt.h23
-rw-r--r--src/pulsecore/resampler.c1756
-rw-r--r--src/pulsecore/resampler.h55
-rw-r--r--src/pulsecore/rtclock.c117
-rw-r--r--src/pulsecore/rtclock.h43
-rw-r--r--src/pulsecore/rtpoll.c766
-rw-r--r--src/pulsecore/rtpoll.h114
-rw-r--r--src/pulsecore/rtsig.c131
-rw-r--r--src/pulsecore/rtsig.h39
-rw-r--r--src/pulsecore/sample-util.c1057
-rw-r--r--src/pulsecore/sample-util.h47
-rw-r--r--src/pulsecore/sconv-s16be.c32
-rw-r--r--src/pulsecore/sconv-s16be.h45
-rw-r--r--src/pulsecore/sconv-s16le.c206
-rw-r--r--src/pulsecore/sconv-s16le.h45
-rw-r--r--src/pulsecore/sconv.c292
-rw-r--r--src/pulsecore/sconv.h21
-rw-r--r--src/pulsecore/semaphore-posix.c67
-rw-r--r--src/pulsecore/semaphore-win32.c63
-rw-r--r--src/pulsecore/semaphore.h (renamed from src/pulsecore/anotify.h)29
-rw-r--r--src/pulsecore/shm.c302
-rw-r--r--src/pulsecore/shm.h20
-rw-r--r--src/pulsecore/shmasyncq.c220
-rw-r--r--src/pulsecore/shmasyncq.h60
-rw-r--r--src/pulsecore/sink-input.c1353
-rw-r--r--src/pulsecore/sink-input.h270
-rw-r--r--src/pulsecore/sink.c1635
-rw-r--r--src/pulsecore/sink.h268
-rw-r--r--src/pulsecore/sioman.c26
-rw-r--r--src/pulsecore/sioman.h10
-rw-r--r--src/pulsecore/socket-client.c305
-rw-r--r--src/pulsecore/socket-client.h19
-rw-r--r--src/pulsecore/socket-server.c217
-rw-r--r--src/pulsecore/socket-server.h15
-rw-r--r--src/pulsecore/socket-util.c183
-rw-r--r--src/pulsecore/socket-util.h22
-rw-r--r--src/pulsecore/sound-file-stream.c320
-rw-r--r--src/pulsecore/sound-file-stream.h10
-rw-r--r--src/pulsecore/sound-file.c92
-rw-r--r--src/pulsecore/sound-file.h10
-rw-r--r--src/pulsecore/source-output.c767
-rw-r--r--src/pulsecore/source-output.h192
-rw-r--r--src/pulsecore/source.c1045
-rw-r--r--src/pulsecore/source.h241
-rw-r--r--src/pulsecore/speex/Makefile13
-rw-r--r--src/pulsecore/speex/arch.h241
-rw-r--r--src/pulsecore/speex/fixed_generic.h106
-rw-r--r--src/pulsecore/speex/resample.c1121
-rw-r--r--src/pulsecore/speex/speex_resampler.h328
-rw-r--r--src/pulsecore/speexwrap.h48
-rw-r--r--src/pulsecore/start-child.c160
-rw-r--r--src/pulsecore/start-child.h30
-rw-r--r--src/pulsecore/strbuf.c71
-rw-r--r--src/pulsecore/strbuf.h12
-rw-r--r--src/pulsecore/strlist.c72
-rw-r--r--src/pulsecore/strlist.h13
-rw-r--r--src/pulsecore/tagstruct.c267
-rw-r--r--src/pulsecore/tagstruct.h23
-rw-r--r--src/pulsecore/thread-mq.c127
-rw-r--r--src/pulsecore/thread-mq.h48
-rw-r--r--src/pulsecore/thread-posix.c107
-rw-r--r--src/pulsecore/thread-win32.c29
-rw-r--r--src/pulsecore/thread.h72
-rw-r--r--src/pulsecore/time-smoother.c459
-rw-r--r--src/pulsecore/time-smoother.h49
-rw-r--r--src/pulsecore/tokenizer.c48
-rw-r--r--src/pulsecore/tokenizer.h10
-rw-r--r--src/pulsecore/winsock.h2
-rw-r--r--src/pulsecore/x11prop.c9
-rw-r--r--src/pulsecore/x11prop.h4
-rw-r--r--src/pulsecore/x11wrap.c161
-rw-r--r--src/pulsecore/x11wrap.h20
-rw-r--r--src/tests/asyncmsgq-test.c108
-rw-r--r--src/tests/asyncq-test.c85
-rw-r--r--src/tests/channelmap-test.c17
-rw-r--r--src/tests/close-test.c20
-rw-r--r--src/tests/cpulimit-test.c18
-rw-r--r--src/tests/envelope-test.c246
-rw-r--r--src/tests/flist-test.c12
-rw-r--r--src/tests/get-binary-name-test.c8
-rw-r--r--src/tests/hook-list-test.c18
-rw-r--r--src/tests/interpol-test.c60
-rw-r--r--src/tests/ipacl-test.c16
-rw-r--r--src/tests/mainloop-test.c12
-rw-r--r--src/tests/mcalign-test.c17
-rw-r--r--src/tests/memblock-test.c92
-rw-r--r--src/tests/memblockq-test.c103
-rw-r--r--src/tests/mix-test.c259
-rw-r--r--src/tests/pacat-simple.c20
-rw-r--r--src/tests/parec-simple.c14
-rw-r--r--src/tests/proplist-test.c60
-rw-r--r--src/tests/queue-test.c67
-rw-r--r--src/tests/remix-test.c89
-rw-r--r--src/tests/resampler-test.c252
-rw-r--r--src/tests/rtpoll-test.c91
-rw-r--r--src/tests/rtstutter.c117
-rw-r--r--src/tests/sig2str-test.c37
-rw-r--r--src/tests/smoother-test.c78
-rw-r--r--src/tests/stripnul.c70
-rw-r--r--src/tests/strlist-test.c9
-rw-r--r--src/tests/sync-playback.c30
-rw-r--r--src/tests/thread-mainloop-test.c31
-rw-r--r--src/tests/thread-test.c40
-rw-r--r--src/tests/utf8-test.c6
-rw-r--r--src/tests/voltest.c6
-rw-r--r--src/utils/pabrowse.c22
-rw-r--r--src/utils/pacat.c286
-rw-r--r--src/utils/pacmd.c59
-rw-r--r--src/utils/pactl.c311
-rwxr-xr-xsrc/utils/padsp5
-rw-r--r--src/utils/padsp.c754
-rw-r--r--src/utils/paplay.c59
-rw-r--r--src/utils/pasuspender.c316
-rw-r--r--src/utils/pax11publish.c28
393 files changed, 56550 insertions, 20421 deletions
diff --git a/src/.gitignore b/src/.gitignore
new file mode 100644
index 00000000..f3ed2e2e
--- /dev/null
+++ b/src/.gitignore
@@ -0,0 +1,56 @@
+*.lo
+*.o
+*.la
+.deps
+.libs
+Makefile
+Makefile.in
+asyncmsgq-test
+asyncq-test
+bt-proximity-helper
+channelmap-test
+client.conf
+close-test
+cpulimit-test
+cpulimit-test2
+daemon.conf
+default.pa
+envelope-test
+esdcompat
+flist-test
+gconf-helper
+get-binary-name-test
+hook-list-test
+interpol-test
+ipacl-test
+mainloop-test
+mainloop-test-glib
+mcalign-test
+memblock-test
+memblockq-test
+mix-test
+pabrowse
+pacat
+pacat-simple
+pacmd
+pactl
+paplay
+parec-simple
+pasuspender
+pax11publish
+proplist-test
+pulseaudio
+queue-test
+remix-test
+resampler-test
+rtpoll-test
+rtstutter
+sig2str-test
+smoother-test
+stripnul
+strlist-test
+sync-playback
+thread-mainloop-test
+thread-test
+utf8-test
+voltest
diff --git a/src/Makefile.am b/src/Makefile.am
index 0ce805ec..9cce6ed4 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,7 +1,9 @@
-# $Id$
-#
# This file is part of PulseAudio.
#
+# Copyright 2004-2006 Lennart Poettering
+# Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+# Copyright 2006 Diego Pettenò
+#
# 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
@@ -26,6 +28,7 @@ pulseincludedir=$(includedir)/pulse
pulsecoreincludedir=$(includedir)/pulsecore
pulseconfdir=$(sysconfdir)/pulse
pulselibexecdir=$(libexecdir)/pulse
+xdgautostartdir=$(sysconfdir)/xdg/autostart
###################################
# Defines #
@@ -50,6 +53,8 @@ AM_CFLAGS += -DPA_DLSEARCHPATH=\"$(modlibexecdir)\"
AM_CFLAGS += -DPA_DEFAULT_CONFIG_DIR=\"$(PA_DEFAULT_CONFIG_DIR)\"
AM_CFLAGS += -DPA_BINARY=\"$(PA_BINARY)\"
AM_CFLAGS += -DPA_SYSTEM_RUNTIME_PATH=\"$(PA_SYSTEM_RUNTIME_PATH)\"
+AM_CFLAGS += -DPA_SYSTEM_CONFIG_PATH=\"$(PA_SYSTEM_CONFIG_PATH)\"
+AM_CFLAGS += -DPA_SYSTEM_STATE_PATH=\"$(PA_SYSTEM_STATE_PATH)\"
AM_CFLAGS += -DAO_REQUIRE_CAS
# This cool debug trap works on i386/gcc only
@@ -59,7 +64,7 @@ AM_LIBADD = $(PTHREAD_LIBS)
AM_LDADD = $(PTHREAD_LIBS)
# Only required on some platforms but defined for all to avoid errors
-AM_LDFLAGS = -no-undefined
+AM_LDFLAGS = -Wl,-no-undefined -Wl,--gc-sections
if STATIC_BINS
BINLDFLAGS = -static
@@ -72,14 +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/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
###################################
@@ -88,19 +93,28 @@ endif
EXTRA_DIST = \
pulse/client.conf.in \
+ pulse/version.h.in \
daemon/daemon.conf.in \
daemon/default.pa.in \
daemon/default.pa.win32 \
depmod.py \
daemon/esdcompat.in \
utils/padsp \
- modules/module-defs.h.m4
+ modules/module-defs.h.m4 \
+ daemon/pulseaudio-module-xsmp.desktop \
+ map-file \
+ daemon/org.pulseaudio.policy
pulseconf_DATA = \
default.pa \
daemon.conf \
client.conf
+if HAVE_X11
+xdgautostart_DATA = \
+ daemon/pulseaudio-module-xsmp.desktop
+endif
+
BUILT_SOURCES = \
pulse/version.h
@@ -116,13 +130,12 @@ pulseaudio_SOURCES = \
daemon/cpulimit.c daemon/cpulimit.h \
daemon/daemon-conf.c daemon/daemon-conf.h \
daemon/dumpmodules.c daemon/dumpmodules.h \
- daemon/main.c \
- pulsecore/gccmacro.h
+ daemon/ltdl-bind-now.c daemon/ltdl-bind-now.h \
+ daemon/main.c
-pulseaudio_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
-pulseaudio_CPPFLAGS = $(AM_CPPFLAGS)
-pulseaudio_LDADD = $(AM_LDADD) libpulsecore.la $(LIBLTDL) \
- $(LIBSAMPLERATE_LIBS) $(LIBSNDFILE_LIBS) $(CAP_LIBS) $(LIBOIL_LIBS)
+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) $(DBUS_LIBS)
# This is needed because automake doesn't properly expand the foreach below
pulseaudio_DEPENDENCIES = libpulsecore.la $(PREOPEN_LIBS)
@@ -138,6 +151,17 @@ else
pulseaudio_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) -dlopen force $(foreach f,$(PREOPEN_LIBS),-dlopen $(f))
endif
+if HAVE_POLKIT
+
+policy_DATA = daemon/org.pulseaudio.policy
+
+pulseaudio_SOURCES += daemon/polkit.c daemon/polkit.h
+pulseaudio_CFLAGS += $(POLKIT_CFLAGS)
+pulseaudio_LDADD += $(POLKIT_LIBS)
+
+
+endif
+
###################################
# Utility programs #
###################################
@@ -145,7 +169,8 @@ endif
bin_PROGRAMS += \
pacat \
pactl \
- paplay
+ paplay \
+ pasuspender
if HAVE_AF_UNIX
bin_PROGRAMS += pacmd
@@ -162,8 +187,8 @@ endif
bin_SCRIPTS = esdcompat
pacat_SOURCES = utils/pacat.c
-pacat_LDADD = $(AM_LDADD) libpulse.la
-pacat_CFLAGS = $(AM_CFLAGS)
+pacat_LDADD = $(AM_LDADD) libpulse.la
+pacat_CFLAGS = $(AM_CFLAGS)
pacat_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
paplay_SOURCES = utils/paplay.c
@@ -171,17 +196,22 @@ paplay_LDADD = $(AM_LDADD) libpulse.la $(LIBSNDFILE_LIBS)
paplay_CFLAGS = $(AM_CFLAGS) $(LIBSNDFILE_CFLAGS)
paplay_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
-pactl_SOURCES = utils/pactl.c
+pactl_SOURCES = utils/pactl.c pulsecore/core-util.c pulsecore/core-util.h pulsecore/core-error.c pulsecore/core-error.h pulsecore/log.c pulsecore/log.h pulsecore/once.c pulsecore/once.h $(PA_THREAD_OBJS)
pactl_LDADD = $(AM_LDADD) libpulse.la $(LIBSNDFILE_LIBS)
pactl_CFLAGS = $(AM_CFLAGS) $(LIBSNDFILE_CFLAGS)
pactl_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
-pacmd_SOURCES = utils/pacmd.c pulsecore/pid.c pulsecore/pid.h
+pasuspender_SOURCES = utils/pasuspender.c pulsecore/core-util.c pulsecore/core-util.h pulsecore/core-error.c pulsecore/core-error.h pulsecore/log.c pulsecore/log.h pulsecore/once.c pulsecore/once.h $(PA_THREAD_OBJS)
+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 pulsecore/core-util.c pulsecore/core-util.h pulsecore/core-error.c pulsecore/core-error.h pulsecore/log.c pulsecore/log.h pulsecore/once.c pulsecore/once.h $(PA_THREAD_OBJS)
pacmd_CFLAGS = $(AM_CFLAGS)
pacmd_LDADD = $(AM_LDADD) libpulse.la
pacmd_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
-pax11publish_SOURCES = utils/pax11publish.c
+pax11publish_SOURCES = utils/pax11publish.c pulsecore/x11prop.c pulsecore/x11prop.h pulse/client-conf.c pulse/client-conf.h pulsecore/authkey.h pulsecore/authkey.c pulsecore/random.h pulsecore/random.c pulsecore/conf-parser.c pulsecore/conf-parser.h pulsecore/core-util.c pulsecore/core-util.h pulsecore/core-error.c pulsecore/core-error.h pulsecore/log.c pulsecore/log.h pulsecore/once.c pulsecore/once.h $(PA_THREAD_OBJS)
pax11publish_CFLAGS = $(AM_CFLAGS) $(X_CFLAGS)
pax11publish_LDADD = $(AM_LDADD) libpulse.la $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS)
pax11publish_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
@@ -201,6 +231,7 @@ noinst_PROGRAMS = \
pacat-simple \
parec-simple \
strlist-test \
+ close-test \
voltest \
memblockq-test \
sync-playback \
@@ -213,7 +244,20 @@ 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 \
+ mix-test \
+ remix-test \
+ envelope-test \
+ proplist-test \
+ rtstutter \
+ stripnul
if HAVE_SIGXCPU
noinst_PROGRAMS += \
@@ -233,7 +277,7 @@ mainloop_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
thread_mainloop_test_SOURCES = tests/thread-mainloop-test.c
thread_mainloop_test_CFLAGS = $(AM_CFLAGS)
-thread_mainloop_test_LDADD = $(AM_LDADD) libpulse.la
+thread_mainloop_test_LDADD = $(AM_LDADD) libpulsecore.la libpulse.la
thread_mainloop_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
utf8_test_SOURCES = tests/utf8-test.c
@@ -241,7 +285,7 @@ utf8_test_CFLAGS = $(AM_CFLAGS)
utf8_test_LDADD = $(AM_LDADD) libpulsecore.la
utf8_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
-get_binary_name_test_SOURCES = tests/get-binary-name-test.c
+get_binary_name_test_SOURCES = tests/get-binary-name-test.c
get_binary_name_test_CFLAGS = $(AM_CFLAGS)
get_binary_name_test_LDADD = $(AM_LDADD) libpulse.la
get_binary_name_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
@@ -258,20 +302,40 @@ hook_list_test_CFLAGS = $(AM_CFLAGS)
hook_list_test_LDADD = $(AM_LDADD) libpulsecore.la
hook_list_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
-memblock_test_SOURCES = tests/memblock-test.c
+memblock_test_SOURCES = tests/memblock-test.c
memblock_test_CFLAGS = $(AM_CFLAGS)
memblock_test_LDADD = $(AM_LDADD) libpulsecore.la
-memblock_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+memblock_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
thread_test_SOURCES = tests/thread-test.c
thread_test_CFLAGS = $(AM_CFLAGS)
thread_test_LDADD = $(AM_LDADD) libpulsecore.la
-thread_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+thread_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
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)
+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)
@@ -293,6 +357,11 @@ strlist_test_CFLAGS = $(AM_CFLAGS)
strlist_test_LDADD = $(AM_LDADD) $(WINSOCK_LIBS) libpulsecore.la libstrlist.la
strlist_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+close_test_SOURCES = tests/close-test.c
+close_test_CFLAGS = $(AM_CFLAGS)
+close_test_LDADD = $(AM_LDADD) $(WINSOCK_LIBS) libpulsecore.la libstrlist.la
+close_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
voltest_SOURCES = tests/voltest.c
voltest_CFLAGS = $(AM_CFLAGS)
voltest_LDADD = $(AM_LDADD) libpulse.la
@@ -325,14 +394,59 @@ memblockq_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
sync_playback_SOURCES = tests/sync-playback.c
sync_playback_LDADD = $(AM_LDADD) libpulse.la
-sync_playback_CFLAGS = $(AM_CFLAGS)
+sync_playback_CFLAGS = $(AM_CFLAGS)
sync_playback_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
interpol_test_SOURCES = tests/interpol-test.c
-interpol_test_LDADD = $(AM_LDADD) libpulse.la
-interpol_test_CFLAGS = $(AM_CFLAGS)
+interpol_test_LDADD = $(AM_LDADD) libpulse.la libpulsecore.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)
+
+mix_test_SOURCES = tests/mix-test.c
+mix_test_LDADD = $(AM_LDADD) libpulsecore.la
+mix_test_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
+mix_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)
+
+remix_test_SOURCES = tests/remix-test.c
+remix_test_LDADD = $(AM_LDADD) libpulsecore.la
+remix_test_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
+remix_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)
+
+envelope_test_SOURCES = tests/envelope-test.c
+envelope_test_LDADD = $(AM_LDADD) libpulsecore.la
+envelope_test_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
+envelope_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)
+
+proplist_test_SOURCES = tests/proplist-test.c
+proplist_test_LDADD = $(AM_LDADD) libpulsecore.la
+proplist_test_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
+proplist_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)
+
+rtstutter_SOURCES = tests/rtstutter.c
+rtstutter_LDADD = $(AM_LDADD) libpulsecore.la
+rtstutter_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
+rtstutter_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)
+
+stripnul_SOURCES = tests/stripnul.c
+stripnul_LDADD = $(AM_LDADD) libpulsecore.la
+stripnul_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
+stripnul_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)
+
###################################
# Client library #
###################################
@@ -360,7 +474,9 @@ pulseinclude_HEADERS = \
pulse/util.h \
pulse/version.h \
pulse/volume.h \
- pulse/xmalloc.h
+ pulse/xmalloc.h \
+ pulse/proplist.h \
+ pulse/gccmacro.h
if HAVE_AVAHI
pulseinclude_HEADERS += \
@@ -410,7 +526,8 @@ libpulse_la_SOURCES = \
pulse/utf8.c pulse/utf8.h \
pulse/util.c pulse/util.h \
pulse/volume.c pulse/volume.h \
- pulse/xmalloc.c pulse/xmalloc.h
+ pulse/xmalloc.c pulse/xmalloc.h \
+ pulse/proplist.c pulse/proplist.h
# Internal stuff that is shared with libpulsecore
libpulse_la_SOURCES += \
@@ -418,7 +535,6 @@ libpulse_la_SOURCES += \
pulsecore/conf-parser.c pulsecore/conf-parser.h \
pulsecore/core-util.c pulsecore/core-util.h \
pulsecore/dynarray.c pulsecore/dynarray.h \
- pulsecore/gccmacro.h \
pulsecore/hashmap.c pulsecore/hashmap.h \
pulsecore/idxset.c pulsecore/idxset.h \
pulsecore/inet_ntop.c pulsecore/inet_ntop.h \
@@ -448,12 +564,17 @@ libpulse_la_SOURCES += \
pulsecore/winsock.h pulsecore/creds.h \
pulsecore/shm.c pulsecore/shm.h \
pulsecore/flist.c pulsecore/flist.h \
- pulsecore/anotify.c pulsecore/anotify.h \
+ pulsecore/object.c pulsecore/object.h \
+ pulsecore/msgobject.c pulsecore/msgobject.h \
+ pulsecore/once.c pulsecore/once.h \
+ pulsecore/rtclock.c pulsecore/rtclock.h \
+ pulsecore/time-smoother.c pulsecore/time-smoother.h \
+ pulsecore/proplist-util.c pulsecore/proplist-util.h \
$(PA_THREAD_OBJS)
if OS_IS_WIN32
libpulse_la_SOURCES += \
- pulsecore/dllmain.c
+ pulsecore/dllmain.c
endif
if HAVE_X11
@@ -463,7 +584,7 @@ libpulse_la_SOURCES += \
endif
libpulse_la_CFLAGS = $(AM_CFLAGS)
-libpulse_la_LDFLAGS = -version-info $(LIBPULSE_VERSION_INFO)
+libpulse_la_LDFLAGS = -version-info $(LIBPULSE_VERSION_INFO) -Wl,-version-script=$(srcdir)/map-file
libpulse_la_LIBADD = $(AM_LIBADD) $(WINSOCK_LIBS) $(LIBICONV)
if HAVE_X11
@@ -476,20 +597,33 @@ libpulse_la_CFLAGS += $(LIBASYNCNS_CFLAGS)
libpulse_la_LIBADD += $(LIBASYNCNS_LIBS)
endif
-libpulse_simple_la_SOURCES = pulse/simple.c pulse/simple.h
+libpulse_simple_la_SOURCES = \
+ pulse/simple.c pulse/simple.h \
+ pulsecore/log.c pulsecore/log.h \
+ pulsecore/core-util.c pulsecore/core-util.h \
+ pulsecore/core-error.c pulsecore/core-error.h \
+ pulsecore/once.c pulsecore/once.h \
+ $(PA_THREAD_OBJS)
+
libpulse_simple_la_CFLAGS = $(AM_CFLAGS)
libpulse_simple_la_LIBADD = $(AM_LIBADD) libpulse.la
-libpulse_simple_la_LDFLAGS = -version-info $(LIBPULSE_SIMPLE_VERSION_INFO)
+libpulse_simple_la_LDFLAGS = -version-info $(LIBPULSE_SIMPLE_VERSION_INFO) -Wl,-version-script=$(srcdir)/map-file
-libpulse_browse_la_SOURCES = pulse/browser.c pulse/browser.h pulsecore/avahi-wrap.c pulsecore/avahi-wrap.h
+libpulse_browse_la_SOURCES = pulse/browser.c pulse/browser.h pulsecore/avahi-wrap.c pulsecore/avahi-wrap.h pulsecore/core-util.c pulsecore/core-util.h pulsecore/core-error.c pulsecore/core-error.h pulsecore/log.c pulsecore/log.h pulsecore/once.c pulsecore/once.h $(PA_THREAD_OBJS)
libpulse_browse_la_CFLAGS = $(AM_CFLAGS) $(AVAHI_CFLAGS)
libpulse_browse_la_LIBADD = $(AM_LIBADD) libpulse.la $(AVAHI_LIBS)
-libpulse_browse_la_LDFLAGS = -version-info $(LIBPULSE_BROWSE_VERSION_INFO)
+libpulse_browse_la_LDFLAGS = -version-info $(LIBPULSE_BROWSE_VERSION_INFO) -Wl,-version-script=$(srcdir)/map-file
-libpulse_mainloop_glib_la_SOURCES = pulse/glib-mainloop.h pulse/glib-mainloop.c
+libpulse_mainloop_glib_la_SOURCES = \
+ pulse/glib-mainloop.h pulse/glib-mainloop.c \
+ pulsecore/log.c pulsecore/log.h \
+ pulsecore/core-util.c pulsecore/core-util.h \
+ pulsecore/core-error.c pulsecore/core-error.h \
+ pulsecore/once.c pulsecore/once.h \
+ $(PA_THREAD_OBJS)
libpulse_mainloop_glib_la_CFLAGS = $(AM_CFLAGS) $(GLIB20_CFLAGS)
libpulse_mainloop_glib_la_LIBADD = $(AM_LIBADD) libpulse.la $(GLIB20_LIBS)
-libpulse_mainloop_glib_la_LDFLAGS = -version-info $(LIBPULSE_MAINLOOP_GLIB_VERSION_INFO)
+libpulse_mainloop_glib_la_LDFLAGS = -version-info $(LIBPULSE_MAINLOOP_GLIB_VERSION_INFO) -Wl,-version-script=$(srcdir)/map-file
###################################
# OSS emulation #
@@ -503,22 +637,38 @@ bin_SCRIPTS += utils/padsp
endif
-libpulsedsp_la_SOURCES = utils/padsp.c
+libpulsedsp_la_SOURCES = utils/padsp.c pulsecore/core-util.c pulsecore/core-util.h pulsecore/core-error.c pulsecore/core-error.h pulsecore/log.c pulsecore/log.h pulsecore/once.c pulsecore/once.h $(PA_THREAD_OBJS)
libpulsedsp_la_CFLAGS = $(AM_CFLAGS)
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 -DFLOATING_POINT
+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 \
pulsecore/cli-text.h \
pulsecore/client.h \
pulsecore/core.h \
- pulsecore/core-def.h \
pulsecore/core-scache.h \
pulsecore/core-subscribe.h \
pulsecore/conf-parser.h \
@@ -560,6 +710,7 @@ pulsecoreinclude_HEADERS = \
pulsecore/refcnt.h \
pulsecore/mutex.h \
pulsecore/thread.h \
+ pulsecore/semaphore.h \
pulsecore/once.h
lib_LTLIBRARIES += libpulsecore.la
@@ -576,7 +727,8 @@ libpulsecore_la_SOURCES = \
pulse/utf8.c pulse/utf8.h \
pulse/util.c pulse/util.h \
pulse/volume.c pulse/volume.h \
- pulse/xmalloc.c pulse/xmalloc.h
+ pulse/xmalloc.c pulse/xmalloc.h \
+ pulse/proplist.c pulse/proplist.h
# Pure core stuff (some are shared in libpulse though).
libpulsecore_la_SOURCES += \
@@ -601,6 +753,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 \
@@ -630,23 +783,38 @@ libpulsecore_la_SOURCES += \
pulsecore/hook-list.c pulsecore/hook-list.h \
pulsecore/shm.c pulsecore/shm.h \
pulsecore/flist.c pulsecore/flist.h \
- pulsecore/anotify.c pulsecore/anotify.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 \
+ pulsecore/start-child.c pulsecore/start-child.h \
+ pulsecore/envelope.c pulsecore/envelope.h \
+ pulsecore/proplist-util.c pulsecore/proplist-util.h \
$(PA_THREAD_OBJS)
if OS_IS_WIN32
libpulsecore_la_SOURCES += \
- pulsecore/dllmain.c
+ pulsecore/dllmain.c
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 \
@@ -695,9 +863,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
@@ -706,7 +874,8 @@ modlibexec_LTLIBRARIES += \
endif
if HAVE_X11
-pulsecoreinclude_HEADERS += \
+#pulsecoreinclude_HEADERS +=
+noinst_HEADERS += \
pulsecore/x11wrap.h \
pulsecore/x11prop.h
@@ -716,7 +885,8 @@ modlibexec_LTLIBRARIES += \
endif
if HAVE_AVAHI
-pulsecoreinclude_HEADERS += \
+#pulsecoreinclude_HEADERS +=
+noinst_HEADERS += \
pulsecore/avahi-wrap.h
modlibexec_LTLIBRARIES += \
@@ -754,7 +924,7 @@ libpstream_la_LIBADD = $(AM_LIBADD) libpulsecore.la libpacket.la libiochannel.la
libpstream_util_la_SOURCES = pulsecore/pstream-util.c pulsecore/pstream-util.h
libpstream_util_la_LDFLAGS = -avoid-version
-libpstream_util_la_LIBADD = $(AM_LIBADD) libpacket.la libpstream.la libtagstruct.la
+libpstream_util_la_LIBADD = $(AM_LIBADD) libpacket.la libpstream.la libtagstruct.la libpulsecore.la
libpdispatch_la_SOURCES = pulsecore/pdispatch.c pulsecore/pdispatch.h
libpdispatch_la_LDFLAGS = -avoid-version
@@ -815,7 +985,7 @@ libsocket_util_la_SOURCES = \
libsocket_util_la_LDFLAGS = -avoid-version
libsocket_util_la_LIBADD = $(AM_LIBADD) $(WINSOCK_LIBS) libpulsecore.la
-librtp_la_SOURCES = modules/rtp/rtp.c modules/rtp/rtp.h modules/rtp/sdp.c modules/rtp/sdp.h modules/rtp/sap.c modules/rtp/sap.h
+librtp_la_SOURCES = modules/rtp/rtp.c modules/rtp/rtp.h modules/rtp/sdp.c modules/rtp/sdp.h modules/rtp/sap.c modules/rtp/sap.h
librtp_la_LDFLAGS = -avoid-version
librtp_la_LIBADD = $(AM_LIBADD) libpulsecore.la
@@ -846,19 +1016,27 @@ modlibexec_LTLIBRARIES += \
module-cli.la \
module-cli-protocol-tcp.la \
module-simple-protocol-tcp.la \
- module-esound-protocol-tcp.la \
+ module-null-sink.la \
+ module-detect.la \
+ module-volume-restore.la \
+ module-device-restore.la \
+ module-default-device-restore.la \
+ module-always-sink.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-sine.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 \
- 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-position-event-sounds.la
+
# See comment at librtp.la above
if !OS_IS_WIN32
@@ -871,9 +1049,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
@@ -896,14 +1074,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
@@ -920,7 +1098,8 @@ endif
if HAVE_AVAHI
modlibexec_LTLIBRARIES += \
- module-zeroconf-publish.la
+ module-zeroconf-publish.la \
+ module-zeroconf-discover.la
endif
if HAVE_LIRC
@@ -939,18 +1118,20 @@ modlibexec_LTLIBRARIES += \
module-jack-source.la
endif
+pulselibexec_PROGRAMS =
+
if HAVE_GCONF
modlibexec_LTLIBRARIES += \
module-gconf.la
-pulselibexec_PROGRAMS = \
+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 += \
@@ -958,6 +1139,20 @@ modlibexec_LTLIBRARIES += \
module-hal-detect.la
endif
+if HAVE_DBUS
+modlibexec_LTLIBRARIES += \
+ libdbus-util.la \
+ module-console-kit.la
+endif
+
+if HAVE_BLUEZ
+modlibexec_LTLIBRARIES += \
+ module-bt-proximity.la
+
+pulselibexec_PROGRAMS += \
+ bt-proximity-helper
+endif
+
# These are generated by a M4 script
SYMDEF_FILES = \
@@ -975,6 +1170,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 \
@@ -983,14 +1180,15 @@ SYMDEF_FILES = \
modules/module-null-sink-symdef.h \
modules/module-esound-sink-symdef.h \
modules/module-zeroconf-publish-symdef.h \
+ modules/module-zeroconf-discover-symdef.h \
modules/module-lirc-symdef.h \
modules/module-mmkbd-evdev-symdef.h \
modules/module-http-protocol-tcp-symdef.h \
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 \
@@ -1001,17 +1199,24 @@ SYMDEF_FILES = \
modules/module-jack-sink-symdef.h \
modules/module-jack-source-symdef.h \
modules/module-volume-restore-symdef.h \
+ modules/module-device-restore-symdef.h \
+ modules/module-default-device-restore-symdef.h \
+ modules/module-always-sink-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
+ modules/module-bt-proximity-symdef.h \
+ modules/gconf/module-gconf-symdef.h \
+ modules/module-position-event-sounds-symdef.h \
+ modules/module-console-kit-symdef.h
EXTRA_DIST += $(SYMDEF_FILES)
BUILT_SOURCES += $(SYMDEF_FILES)
$(SYMDEF_FILES): modules/module-defs.h.m4
- mkdir modules
- mkdir modules/gconf
- mkdir modules/rtp
+ $(MKDIR_P) modules
+ $(MKDIR_P) modules/gconf
+ $(MKDIR_P) modules/rtp
$(M4) -Dfname="$@" $< > $@
# Simple protocol
@@ -1093,7 +1298,7 @@ module_esound_compat_spawnpid_la_LIBADD = $(AM_LIBADD) libpulsecore.la
module_esound_sink_la_SOURCES = modules/module-esound-sink.c
module_esound_sink_la_LDFLAGS = -module -avoid-version
-module_esound_sink_la_LIBADD = $(AM_LIBADD) libpulsecore.la libiochannel.la libsocket-client.la libauthkey.la
+module_esound_sink_la_LIBADD = $(AM_LIBADD) libpulsecore.la libiochannel.la libsocket-client.la libauthkey.la libsocket-util.la
# Pipes
@@ -1121,6 +1326,15 @@ 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:/usr/local/lib64/ladspa:/usr/lib64/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
@@ -1144,7 +1358,12 @@ module_x11_bell_la_LIBADD = $(AM_LIBADD) $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA
module_x11_publish_la_SOURCES = modules/module-x11-publish.c
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
+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) libx11wrap.la libpulsecore.la
# OSS
@@ -1154,11 +1373,7 @@ liboss_util_la_LIBADD = libpulsecore.la
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
-
-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
+module_oss_la_LIBADD = $(AM_LIBADD) libiochannel.la liboss-util.la libpulsecore.la
# ALSA
@@ -1181,7 +1396,7 @@ module_alsa_source_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS)
module_solaris_la_SOURCES = modules/module-solaris.c
module_solaris_la_LDFLAGS = -module -avoid-version
-module_solaris_la_LIBADD = $(AM_LIBADD) libiochannel.la
+module_solaris_la_LIBADD = $(AM_LIBADD) libiochannel.la libpulsecore.la
# Avahi
@@ -1190,6 +1405,11 @@ module_zeroconf_publish_la_LDFLAGS = -module -avoid-version
module_zeroconf_publish_la_LIBADD = $(AM_LIBADD) $(AVAHI_LIBS) libavahi-wrap.la libpulsecore.la
module_zeroconf_publish_la_CFLAGS = $(AM_CFLAGS) $(AVAHI_CFLAGS)
+module_zeroconf_discover_la_SOURCES = modules/module-zeroconf-discover.c
+module_zeroconf_discover_la_LDFLAGS = -module -avoid-version
+module_zeroconf_discover_la_LIBADD = $(AM_LIBADD) $(AVAHI_LIBS) libavahi-wrap.la libpulsecore.la
+module_zeroconf_discover_la_CFLAGS = $(AM_CFLAGS) $(AVAHI_CFLAGS)
+
# LIRC
module_lirc_la_SOURCES = modules/module-lirc.c
@@ -1206,10 +1426,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
@@ -1223,16 +1443,46 @@ module_volume_restore_la_LDFLAGS = -module -avoid-version
module_volume_restore_la_LIBADD = $(AM_LIBADD) libpulsecore.la
module_volume_restore_la_CFLAGS = $(AM_CFLAGS)
+# Position event sounds in space
+module_position_event_sounds_la_SOURCES = modules/module-position-event-sounds.c
+module_position_event_sounds_la_LDFLAGS = -module -avoid-version
+module_position_event_sounds_la_LIBADD = $(AM_LIBADD) libpulsecore.la
+module_position_event_sounds_CFLAGS = $(AM_CFLAGS)
+
+# Device volume restore module
+module_device_restore_la_SOURCES = modules/module-device-restore.c
+module_device_restore_la_LDFLAGS = -module -avoid-version
+module_device_restore_la_LIBADD = $(AM_LIBADD) libpulsecore.la -lgdbm
+module_device_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)
+
+# Always Sink module
+module_always_sink_la_SOURCES = modules/module-always-sink.c
+module_always_sink_la_LDFLAGS = -module -avoid-version
+module_always_sink_la_LIBADD = $(AM_LIBADD) libpulsecore.la
+module_always_sink_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
@@ -1245,24 +1495,29 @@ module_rtp_recv_la_CFLAGS = $(AM_CFLAGS)
module_jack_sink_la_SOURCES = modules/module-jack-sink.c
module_jack_sink_la_LDFLAGS = -module -avoid-version
module_jack_sink_la_LIBADD = $(AM_LIBADD) libpulsecore.la $(JACK_LIBS)
-module_jack_sink_la_CFLAGS = $(AM_LIBADD) $(JACK_CFLAGS)
+module_jack_sink_la_CFLAGS = $(AM_CFLAGS) $(JACK_CFLAGS)
module_jack_source_la_SOURCES = modules/module-jack-source.c
module_jack_source_la_LDFLAGS = -module -avoid-version
module_jack_source_la_LIBADD = $(AM_LIBADD) libpulsecore.la $(JACK_LIBS)
-module_jack_source_la_CFLAGS = $(AM_LIBADD) $(JACK_CFLAGS)
+module_jack_source_la_CFLAGS = $(AM_CFLAGS) $(JACK_CFLAGS)
-# HAL
+# HAL/D-Bus
libdbus_util_la_SOURCES = modules/dbus-util.c modules/dbus-util.h
libdbus_util_la_LDFLAGS = -avoid-version
-libdbus_util_la_LIBADD = $(AM_LIBADD) $(HAL_LIBS) libpulsecore.la
-libdbus_util_la_CFLAGS = $(AM_CFLAGS) $(HAL_CFLAGS)
+libdbus_util_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore.la
+libdbus_util_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
module_hal_detect_la_SOURCES = modules/module-hal-detect.c
module_hal_detect_la_LDFLAGS = -module -avoid-version
module_hal_detect_la_LIBADD = $(AM_LIBADD) $(HAL_LIBS) libpulsecore.la libdbus-util.la
module_hal_detect_la_CFLAGS = $(AM_CFLAGS) $(HAL_CFLAGS)
+module_console_kit_la_SOURCES = modules/module-console-kit.c
+module_console_kit_la_LDFLAGS = -module -avoid-version
+module_console_kit_la_LIBADD = $(AM_LIBADD) $(HAL_LIBS) libpulsecore.la libdbus-util.la
+module_console_kit_la_CFLAGS = $(AM_CFLAGS) $(HAL_CFLAGS)
+
# GConf support
module_gconf_la_SOURCES = modules/gconf/module-gconf.c
module_gconf_la_LDFLAGS = -module -avoid-version
@@ -1270,17 +1525,28 @@ 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)
+# Bluetooth proximity
+module_bt_proximity_la_SOURCES = modules/module-bt-proximity.c
+module_bt_proximity_la_LDFLAGS = -module -avoid-version
+module_bt_proximity_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore.la libdbus-util.la
+module_bt_proximity_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) -DPA_BT_PROXIMITY_HELPER=\"$(pulselibexecdir)/bt-proximity-helper\"
+
+bt_proximity_helper_SOURCES = modules/bt-proximity-helper.c
+bt_proximity_helper_LDADD = $(AM_LDADD) $(BLUEZ_LIBS)
+bt_proximity_helper_CFLAGS = $(AM_CFLAGS) $(BLUEZ_CFLAGS)
+bt_proximity_helper_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
###################################
# Some minor stuff #
###################################
-suid: pulse
- chown root $<
- chmod u+s $<
+suid: pulseaudio .libs/lt-pulseaudio
+ chown root $^
+ chmod u+s $^
CLEANFILES = esdcompat client.conf default.pa daemon.conf
@@ -1293,14 +1559,14 @@ esdcompat: daemon/esdcompat.in Makefile
client.conf: pulse/client.conf.in Makefile
sed -e 's,@PA_BINARY\@,$(PA_BINARY),g' < $< > $@
-if OS_IS_WIN32
+if OS_IS_WIN32
default.pa: daemon/default.pa.win32
cp $< $@
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
@@ -1310,12 +1576,32 @@ daemon.conf: daemon/daemon.conf.in Makefile
install-exec-hook:
chown root $(DESTDIR)$(bindir)/pulseaudio ; true
chmod u+s $(DESTDIR)$(bindir)/pulseaudio
+ -chmod u+s $(DESTDIR)$(pulselibexecdir)/bt-proximity-helper
ln -sf pacat $(DESTDIR)$(bindir)/parec
rm -f $(DESTDIR)$(modlibexecdir)/*.a
rm -f $(DESTDIR)$(libdir)/libpulsedsp.a
rm -f $(DESTDIR)$(libdir)/libpulsedsp.la
+ rm -f $(DESTDIR)$(modlibexecdir)/*.la
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
+
+# Automatically generate linker version script. We use the same one for all public .sos
+update-map-file:
+ ( echo "PULSE_0 {" ; \
+ echo "global:" ; \
+ ctags -I PA_GCC_PURE,PA_GCC_CONST -f - --c-kinds=p $(pulseinclude_HEADERS) | awk '/^pa_/ { print $$1 ";" }' | sort ; \
+ echo "local:" ; \
+ echo "*;" ; \
+ echo "};" ) > $(srcdir)/map-file
+
.PHONY: utils/padsp
diff --git a/src/daemon/caps.c b/src/daemon/caps.c
index cebdaebc..ae07119c 100644
--- a/src/daemon/caps.c
+++ b/src/daemon/caps.c
@@ -1,18 +1,19 @@
-/* $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
@@ -23,15 +24,18 @@
#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>
#endif
+#ifdef HAVE_SYS_PRCTL_H
+#include <sys/prctl.h>
+#endif
#include <pulsecore/core-error.h>
@@ -50,20 +54,23 @@ int setresuid(uid_t r, uid_t e, uid_t s);
/* Drop root rights when called SUID root */
void pa_drop_root(void) {
uid_t uid = getuid();
-
+
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);
+ pa_assert_se(setresuid(uid, uid, uid) >= 0);
#elif defined(HAVE_SETREUID)
- setreuid(uid, uid);
+ pa_assert_se(setreuid(uid, uid) >= 0);
#else
- setuid(uid);
- seteuid(uid);
+ pa_assert_se(setuid(uid) >= 0);
+ pa_assert_se(seteuid(uid) >= 0);
#endif
+
+ pa_assert_se(getuid() == uid);
+ pa_assert_se(geteuid() == uid);
}
#else
@@ -73,77 +80,68 @@ void pa_drop_root(void) {
#endif
-#ifdef HAVE_SYS_CAPABILITY_H
+#if defined(HAVE_SYS_CAPABILITY_H) && defined(HAVE_SYS_PRCTL_H)
-/* Limit capabilities set to CAPSYS_NICE */
-int pa_limit_caps(void) {
- int r = -1;
+/* Limit permitted capabilities set to CAPSYS_NICE */
+void pa_limit_caps(void) {
cap_t caps;
cap_value_t nice_cap = CAP_SYS_NICE;
- /* Only drop caps when called SUID */
- if (getuid() == 0)
- return 0;
-
- caps = cap_init();
- 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);
+ pa_assert_se(caps = cap_init());
+ pa_assert_se(cap_clear(caps) == 0);
+ pa_assert_se(cap_set_flag(caps, CAP_EFFECTIVE, 1, &nice_cap, CAP_SET) == 0);
+ pa_assert_se(cap_set_flag(caps, CAP_PERMITTED, 1, &nice_cap, CAP_SET) == 0);
if (cap_set_proc(caps) < 0)
- goto fail;
+ /* Hmm, so we couldn't limit our caps, which probably means we
+ * hadn't any in the first place, so let's just make sure of
+ * that */
+ pa_drop_caps();
+ else
+ pa_log_info("Limited capabilities successfully to CAP_SYS_NICE.");
- pa_log_info("dropped capabilities successfully.");
-
- r = 0;
+ pa_assert_se(cap_free(caps) == 0);
-fail:
- cap_free (caps);
-
- return r;
+ pa_assert_se(prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == 0);
}
/* Drop all capabilities, effectively becoming a normal user */
-int pa_drop_caps(void) {
+void pa_drop_caps(void) {
cap_t caps;
- int r = -1;
- /* Only drop caps when called SUID */
- if (getuid() == 0)
- return 0;
+ pa_assert_se(prctl(PR_SET_KEEPCAPS, 0, 0, 0, 0) == 0);
- caps = cap_init();
- assert(caps);
+ pa_assert_se(caps = cap_init());
+ pa_assert_se(cap_clear(caps) == 0);
+ pa_assert_se(cap_set_proc(caps) == 0);
+ pa_assert_se(cap_free(caps) == 0);
- cap_clear(caps);
+ pa_assert_se(!pa_have_caps());
+}
- if (cap_set_proc(caps) < 0) {
- pa_log("failed to drop capabilities: %s", pa_cstrerror(errno));
- goto fail;
- }
-
- r = 0;
+pa_bool_t pa_have_caps(void) {
+ cap_t caps;
+ cap_flag_value_t flag = CAP_CLEAR;
-fail:
- cap_free (caps);
-
- return r;
+ pa_assert_se(caps = cap_get_proc());
+ pa_assert_se(cap_get_flag(caps, CAP_SYS_NICE, CAP_EFFECTIVE, &flag) >= 0);
+ pa_assert_se(cap_free(caps) == 0);
+
+ return flag == CAP_SET;
}
#else
/* NOOPs in case capabilities are not available. */
-int pa_limit_caps(void) {
- return 0;
+void pa_limit_caps(void) {
}
-int pa_drop_caps(void) {
+void pa_drop_caps(void) {
pa_drop_root();
- return 0;
}
-#endif
+pa_bool_t pa_have_caps(void) {
+ return FALSE;
+}
+#endif
diff --git a/src/daemon/caps.h b/src/daemon/caps.h
index 8a618286..176aa90e 100644
--- a/src/daemon/caps.h
+++ b/src/daemon/caps.h
@@ -1,29 +1,32 @@
#ifndef foocapshfoo
#define foocapshfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
+ Copyright 2004 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.
***/
+#include <pulsecore/macro.h>
+
void pa_drop_root(void);
-int pa_limit_caps(void);
-int pa_drop_caps(void);
+void pa_drop_caps(void);
+void pa_limit_caps(void);
+pa_bool_t pa_have_caps(void);
#endif
diff --git a/src/daemon/cmdline.c b/src/daemon/cmdline.c
index d368b644..4b2466ce 100644
--- a/src/daemon/cmdline.c
+++ b/src/daemon/cmdline.c
@@ -1,18 +1,18 @@
-/* $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
@@ -24,7 +24,6 @@
#endif
#include <string.h>
-#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <getopt.h>
@@ -34,6 +33,7 @@
#include <pulsecore/core-util.h>
#include <pulsecore/strbuf.h>
+#include <pulsecore/macro.h>
#include "cmdline.h"
@@ -47,6 +47,7 @@ enum {
ARG_FAIL,
ARG_LOG_LEVEL,
ARG_HIGH_PRIORITY,
+ ARG_REALTIME,
ARG_DISALLOW_MODULE_LOADING,
ARG_EXIT_IDLE_TIME,
ARG_MODULE_IDLE_TIME,
@@ -61,11 +62,14 @@ enum {
ARG_CHECK,
ARG_NO_CPU_LIMIT,
ARG_DISABLE_SHM,
- ARG_SYSTEM
+ ARG_DUMP_RESAMPLE_METHODS,
+ ARG_SYSTEM,
+ ARG_CLEANUP_SHM,
+ ARG_START
};
/* Tabel for getopt_long() */
-static struct option long_options[] = {
+static const struct option long_options[] = {
{"help", 0, 0, ARG_HELP},
{"version", 0, 0, ARG_VERSION},
{"dump-conf", 0, 0, ARG_DUMP_CONF},
@@ -75,6 +79,7 @@ static struct option long_options[] = {
{"verbose", 2, 0, ARG_LOG_LEVEL},
{"log-level", 2, 0, ARG_LOG_LEVEL},
{"high-priority", 2, 0, ARG_HIGH_PRIORITY},
+ {"realtime", 2, 0, ARG_REALTIME},
{"disallow-module-loading", 2, 0, ARG_DISALLOW_MODULE_LOADING},
{"exit-idle-time", 2, 0, ARG_EXIT_IDLE_TIME},
{"module-idle-time", 2, 0, ARG_MODULE_IDLE_TIME},
@@ -85,28 +90,36 @@ static struct option long_options[] = {
{"dl-search-path", 1, 0, ARG_DL_SEARCH_PATH},
{"resample-method", 1, 0, ARG_RESAMPLE_METHOD},
{"kill", 0, 0, ARG_KILL},
+ {"start", 0, 0, ARG_START},
{"use-pid-file", 2, 0, ARG_USE_PID_FILE},
{"check", 0, 0, ARG_CHECK},
{"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
e = argv0;
-
+
printf("%s [options]\n\n"
"COMMANDS:\n"
" -h, --help Show this help\n"
" --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"
+ " --start Start the daemon if it is not running\n"
" -k --kill Kill a running daemon\n"
" --check Check for a running daemon\n\n"
@@ -114,8 +127,12 @@ void pa_cmdline_help(const char *argv0) {
" --system[=BOOL] Run as system-wide instance\n"
" -D, --daemonize[=BOOL] Daemonize after startup\n"
" --fail[=BOOL] Quit when startup fails\n"
- " --high-priority[=BOOL] Try to set high process priority\n"
- " (only available as root)\n"
+ " --high-priority[=BOOL] Try to set high nice level\n"
+ " (only available as root, when SUID or\n"
+ " with elevated RLIMIT_NICE)\n"
+ " --realtime[=BOOL] Try to enable realtime scheduling\n"
+ " (only available as root, when SUID or\n"
+ " with elevated RLIMIT_RTPRIO)\n"
" --disallow-module-loading[=BOOL] Disallow module loading after startup\n"
" --exit-idle-time=SECS Terminate the daemon when idle and this\n"
" time passed\n"
@@ -124,14 +141,13 @@ void pa_cmdline_help(const char *argv0) {
" --scache-idle-time=SECS Unload autoloaded samples when idle and\n"
" this time passed\n"
" --log-level[=LEVEL] Increase or set verbosity level\n"
- " -v Increase the verbosity level\n"
+ " -v Increase the verbosity level\n"
" --log-target={auto,syslog,stderr} Specify the log target\n"
" -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"
+ " --resample-method=METHOD Use the specified resampling method\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"
@@ -143,20 +159,23 @@ void pa_cmdline_help(const char *argv0) {
" -F, --file=FILENAME Run the specified script\n"
" -C Open a command line on the running TTY\n"
" after startup\n\n"
-
+
" -n Don't load default script file\n", e);
}
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();
if (conf->script_commands)
pa_strbuf_puts(buf, conf->script_commands);
-
+
while ((c = getopt_long(argc, argv, "L:F:ChDnp:kv", long_options, NULL)) != -1) {
switch (c) {
case ARG_HELP:
@@ -176,39 +195,55 @@ 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;
break;
+ case ARG_START:
+ conf->cmd = PA_CMD_START;
+ conf->daemonize = TRUE;
+ break;
+
case ARG_CHECK:
conf->cmd = PA_CMD_CHECK;
break;
-
+
case ARG_LOAD:
case 'L':
pa_strbuf_printf(buf, "load-module %s\n", optarg);
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");
break;
-
+
case ARG_DAEMONIZE:
case 'D':
- if ((conf->daemonize = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
+ if ((conf->daemonize = optarg ? pa_parse_boolean(optarg) : TRUE) < 0) {
pa_log("--daemonize expects boolean argument");
goto fail;
}
break;
case ARG_FAIL:
- if ((conf->fail = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
+ if ((conf->fail = optarg ? pa_parse_boolean(optarg) : TRUE) < 0) {
pa_log("--fail expects boolean argument");
goto fail;
}
@@ -226,39 +261,45 @@ int pa_cmdline_parse(pa_daemon_conf *conf, int argc, char *const argv [], int *d
if (conf->log_level < PA_LOG_LEVEL_MAX-1)
conf->log_level++;
}
-
+
break;
case ARG_HIGH_PRIORITY:
- if ((conf->high_priority = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
+ if ((conf->high_priority = optarg ? pa_parse_boolean(optarg) : TRUE) < 0) {
pa_log("--high-priority expects boolean argument");
goto fail;
}
break;
+ case ARG_REALTIME:
+ if ((conf->realtime_scheduling = optarg ? pa_parse_boolean(optarg) : TRUE) < 0) {
+ pa_log("--realtime expects boolean argument");
+ goto fail;
+ }
+ break;
+
case ARG_DISALLOW_MODULE_LOADING:
- if ((conf->disallow_module_loading = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
+ if ((conf->disallow_module_loading = optarg ? pa_parse_boolean(optarg) : TRUE) < 0) {
pa_log("--disallow-module-loading expects boolean argument");
goto fail;
}
break;
case ARG_USE_PID_FILE:
- if ((conf->use_pid_file = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
+ if ((conf->use_pid_file = optarg ? pa_parse_boolean(optarg) : TRUE) < 0) {
pa_log("--use-pid-file expects boolean argument");
goto fail;
}
break;
-
+
case 'p':
case ARG_DL_SEARCH_PATH:
pa_xfree(conf->dl_search_path);
conf->dl_search_path = *optarg ? pa_xstrdup(optarg) : NULL;
break;
-
+
case 'n':
- pa_xfree(conf->default_script_file);
- conf->default_script_file = NULL;
+ conf->load_default_script_file = FALSE;
break;
case ARG_LOG_TARGET:
@@ -288,26 +329,26 @@ int pa_cmdline_parse(pa_daemon_conf *conf, int argc, char *const argv [], int *d
break;
case ARG_SYSTEM:
- if ((conf->system_instance = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
+ if ((conf->system_instance = optarg ? pa_parse_boolean(optarg) : TRUE) < 0) {
pa_log("--system expects boolean argument");
goto fail;
}
break;
case ARG_NO_CPU_LIMIT:
- if ((conf->no_cpu_limit = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
+ if ((conf->no_cpu_limit = optarg ? pa_parse_boolean(optarg) : TRUE) < 0) {
pa_log("--no-cpu-limit expects boolean argument");
goto fail;
}
break;
case ARG_DISABLE_SHM:
- if ((conf->disable_shm = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
+ if ((conf->disable_shm = optarg ? pa_parse_boolean(optarg) : TRUE) < 0) {
pa_log("--disable-shm expects boolean argument");
goto fail;
}
break;
-
+
default:
goto fail;
}
@@ -322,12 +363,12 @@ int pa_cmdline_parse(pa_daemon_conf *conf, int argc, char *const argv [], int *d
}
*d = optind;
-
+
return 0;
-
+
fail:
if (buf)
pa_strbuf_free(buf);
-
+
return -1;
}
diff --git a/src/daemon/cmdline.h b/src/daemon/cmdline.h
index 25453e55..fd72a6d3 100644
--- a/src/daemon/cmdline.h
+++ b/src/daemon/cmdline.h
@@ -1,21 +1,21 @@
#ifndef foocmdlinehfoo
#define foocmdlinehfoo
-/* $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
diff --git a/src/daemon/cpulimit.c b/src/daemon/cpulimit.c
index d7466b06..42a71f7e 100644
--- a/src/daemon/cpulimit.c
+++ b/src/daemon/cpulimit.c
@@ -1,18 +1,18 @@
-/* $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
@@ -28,6 +28,7 @@
#include <pulsecore/core-util.h>
#include <pulsecore/core-error.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
#include "cpulimit.h"
@@ -36,7 +37,6 @@
#include <errno.h>
#include <stdio.h>
#include <string.h>
-#include <assert.h>
#include <sys/time.h>
#include <unistd.h>
#include <signal.h>
@@ -80,7 +80,7 @@ static pa_io_event *io_event = NULL;
static struct sigaction sigaction_prev;
/* Nonzero after pa_cpu_limit_init() */
-static int installed = 0;
+static pa_bool_t installed = FALSE;
/* The current state of operation */
static enum {
@@ -90,23 +90,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 */
@@ -116,7 +111,10 @@ static void write_err(const char *p) {
/* The signal handler, called on every SIGXCPU */
static void signal_handler(int sig) {
- assert(sig == SIGXCPU);
+ int saved_errno;
+
+ saved_errno = errno;
+ pa_assert(sig == SIGXCPU);
if (phase == PHASE_IDLE) {
time_t now;
@@ -128,37 +126,44 @@ 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
-
+
if (CPUTIME_INTERVAL_SOFT >= ((now-last_time)*(double)CPUTIME_PERCENT/100)) {
static const char c = 'X';
write_err("Soft CPU time limit exhausted, terminating.\n");
-
+
/* Try a soft cleanup */
write(the_pipe[1], &c, sizeof(c));
phase = PHASE_SOFT;
reset_cpu_time(CPUTIME_INTERVAL_HARD);
-
+
} else {
/* Everything's fine */
reset_cpu_time(CPUTIME_INTERVAL_SOFT);
last_time = now;
}
-
+
} else if (phase == PHASE_SOFT) {
write_err("Hard CPU time limit exhausted, terminating forcibly.\n");
- _exit(1); /* Forced exit */
+ abort(); /* Forced exit */
}
+
+ errno = saved_errno;
}
/* 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 */
}
@@ -166,8 +171,14 @@ 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);
/* Prepare the main loop pipe */
@@ -176,10 +187,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);
@@ -191,40 +202,34 @@ int pa_cpu_limit_init(pa_mainloop_api *m) {
sa.sa_handler = signal_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
-
+
if (sigaction(SIGXCPU, &sa, &sigaction_prev) < 0) {
pa_cpu_limit_done();
return -1;
}
- installed = 1;
+ installed = TRUE;
reset_cpu_time(CPUTIME_INTERVAL_SOFT);
-
+
return 0;
}
/* 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);
- installed = 0;
+ pa_assert_se(sigaction(SIGXCPU, &sigaction_prev, NULL) >= 0);
+ installed = FALSE;
}
}
diff --git a/src/daemon/cpulimit.h b/src/daemon/cpulimit.h
index 21bdd17b..cb9a123d 100644
--- a/src/daemon/cpulimit.h
+++ b/src/daemon/cpulimit.h
@@ -1,21 +1,21 @@
#ifndef foocpulimithfoo
#define foocpulimithfoo
-/* $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
diff --git a/src/daemon/daemon-conf.c b/src/daemon/daemon-conf.c
index dd478126..9ac40901 100644
--- a/src/daemon/daemon-conf.c
+++ b/src/daemon/daemon-conf.c
@@ -1,8 +1,9 @@
-/* $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,
@@ -26,29 +27,27 @@
#include <errno.h>
#include <stdio.h>
#include <string.h>
-#include <assert.h>
#include <unistd.h>
+#include <sched.h>
#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
#include <pulsecore/core-error.h>
#include <pulsecore/core-util.h>
#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 PA_PATH_SEP "default.pa"
+#define DEFAULT_SCRIPT_FILE_USER PA_PATH_SEP "default.pa"
+#define DEFAULT_SYSTEM_SCRIPT_FILE PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "system.pa"
-#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_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"
@@ -56,54 +55,77 @@
static const pa_daemon_conf default_conf = {
.cmd = PA_CMD_DAEMON,
- .daemonize = 0,
- .fail = 1,
- .high_priority = 0,
- .disallow_module_loading = 0,
+ .daemonize = FALSE,
+ .fail = TRUE,
+ .high_priority = TRUE,
+ .nice_level = -11,
+ .realtime_scheduling = FALSE,
+ .realtime_priority = 5, /* Half of JACK's default rtprio */
+ .disallow_module_loading = FALSE,
.exit_idle_time = -1,
.module_idle_time = 20,
.scache_idle_time = 20,
.auto_log_target = 1,
.script_commands = NULL,
.dl_search_path = NULL,
+ .load_default_script_file = TRUE,
.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,
+ .disable_remixing = FALSE,
.config_file = NULL,
- .use_pid_file = 1,
- .system_instance = 0,
- .no_cpu_limit = 0,
- .disable_shm = 0
+ .use_pid_file = TRUE,
+ .system_instance = FALSE,
+ .no_cpu_limit = FALSE,
+ .disable_shm = FALSE,
+ .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_stack = { .value = 0, .is_set = 0 }
+ ,.rlimit_fsize = { .value = 0, .is_set = FALSE },
+ .rlimit_data = { .value = 0, .is_set = FALSE },
+ .rlimit_stack = { .value = 0, .is_set = FALSE },
+ .rlimit_core = { .value = 0, .is_set = FALSE },
+ .rlimit_rss = { .value = 0, .is_set = FALSE }
#ifdef RLIMIT_NPROC
- , .rlimit_nproc = { .value = 0, .is_set = 0 }
+ ,.rlimit_nproc = { .value = 0, .is_set = FALSE }
#endif
+ ,.rlimit_nofile = { .value = 256, .is_set = TRUE }
#ifdef RLIMIT_MEMLOCK
- , .rlimit_memlock = { .value = 0, .is_set = 1 }
+ ,.rlimit_memlock = { .value = 0, .is_set = FALSE }
+#endif
+ ,.rlimit_as = { .value = 0, .is_set = FALSE }
+#ifdef RLIMIT_LOCKS
+ ,.rlimit_locks = { .value = 0, .is_set = FALSE }
+#endif
+#ifdef RLIMIT_SIGPENDING
+ ,.rlimit_sigpending = { .value = 0, .is_set = FALSE }
+#endif
+#ifdef RLIMIT_MSGQUEUE
+ ,.rlimit_msgqueue = { .value = 0, .is_set = FALSE }
+#endif
+#ifdef RLIMIT_NICE
+ ,.rlimit_nice = { .value = 31, .is_set = TRUE } /* nice level of -11 */
+#endif
+#ifdef RLIMIT_RTPRIO
+ ,.rlimit_rtprio = { .value = 9, .is_set = TRUE } /* One below JACK's default for the server */
+#endif
+#ifdef RLIMIT_RTTIME
+ ,.rlimit_rttime = { .value = PA_USEC_PER_SEC, .is_set = TRUE }
#endif
#endif
};
pa_daemon_conf* pa_daemon_conf_new(void) {
- FILE *f;
- pa_daemon_conf *c = pa_xmemdup(&default_conf, sizeof(default_conf));
-
- if ((f = pa_open_config_file(DEFAULT_SCRIPT_FILE, DEFAULT_SCRIPT_FILE_USER, ENV_SCRIPT_FILE, &c->default_script_file, "r")))
- fclose(f);
+ pa_daemon_conf *c = pa_xnewdup(pa_daemon_conf, &default_conf, 1);
c->dl_search_path = pa_xstrdup(PA_DLSEARCHPATH);
return c;
}
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);
@@ -112,7 +134,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;
@@ -130,7 +153,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)
@@ -155,7 +179,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;
@@ -166,7 +191,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);
@@ -178,7 +207,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);
@@ -190,10 +223,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;
}
@@ -203,10 +240,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 */
@@ -215,7 +253,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;
@@ -228,104 +266,313 @@ 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 > (int32_t) 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 > (int32_t) 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;
+}
+
+static int parse_nice_level(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 level;
+
+ pa_assert(filename);
+ pa_assert(lvalue);
+ pa_assert(rvalue);
+ pa_assert(data);
+
+ if (pa_atoi(rvalue, &level) < 0 || level < -20 || level > 19) {
+ pa_log("[%s:%u] Invalid nice level '%s'.", filename, line, rvalue);
+ return -1;
+ }
+
+ c->nice_level = (int) level;
+ return 0;
+}
+
+static int parse_rtprio(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 rtprio;
+
+ pa_assert(filename);
+ pa_assert(lvalue);
+ pa_assert(rvalue);
+ pa_assert(data);
+
+ if (pa_atoi(rvalue, &rtprio) < 0 || rtprio < sched_get_priority_min(SCHED_FIFO) || rtprio > sched_get_priority_max(SCHED_FIFO)) {
+ pa_log("[%s:%u] Invalid realtime priority '%s'.", filename, line, rvalue);
+ return -1;
+ }
+
+ c->realtime_priority = (int) rtprio;
+ 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 },
+ { "realtime-scheduling", pa_config_parse_bool, NULL },
+ { "disallow-module-loading", pa_config_parse_bool, 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 },
+ { "exit-idle-time", pa_config_parse_int, NULL },
+ { "module-idle-time", pa_config_parse_int, NULL },
+ { "scache-idle-time", pa_config_parse_int, NULL },
+ { "realtime-priority", parse_rtprio, 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 },
+ { "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 },
+ { "nice-level", parse_nice_level, NULL },
+ { "disable-remixing", pa_config_parse_bool, NULL },
+ { "load-default-script-file", pa_config_parse_bool, 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-fsize", parse_rlimit, NULL },
+ { "rlimit-data", parse_rlimit, NULL },
+ { "rlimit-stack", parse_rlimit, NULL },
+ { "rlimit-core", parse_rlimit, NULL },
+ { "rlimit-rss", parse_rlimit, NULL },
+ { "rlimit-nofile", parse_rlimit, NULL },
+ { "rlimit-as", 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
+#ifdef RLIMIT_LOCKS
+ { "rlimit-locks", parse_rlimit, NULL },
#endif
+#ifdef RLIMIT_SIGPENDING
+ { "rlimit-sigpending", parse_rlimit, NULL },
#endif
- { NULL, NULL, NULL },
+#ifdef RLIMIT_MSGQUEUE
+ { "rlimit-msgqueue", parse_rlimit, NULL },
+#endif
+#ifdef RLIMIT_NICE
+ { "rlimit-nice", parse_rlimit, NULL },
+#endif
+#ifdef RLIMIT_RTPRIO
+ { "rlimit-rtprio", parse_rlimit, NULL },
+#endif
+#ifdef RLIMIT_RTTIME
+ { "rlimit-rttime", parse_rlimit, NULL },
+#endif
+#endif
+ { NULL, NULL, NULL },
};
-
+
table[0].data = &c->daemonize;
table[1].data = &c->fail;
table[2].data = &c->high_priority;
- table[3].data = &c->disallow_module_loading;
- table[4].data = &c->exit_idle_time;
- table[5].data = &c->module_idle_time;
- table[6].data = &c->scache_idle_time;
- table[7].data = &c->dl_search_path;
- table[8].data = &c->default_script_file;
- table[9].data = c;
- table[10].data = c;
- table[11].data = c;
+ table[3].data = &c->realtime_scheduling;
+ table[4].data = &c->disallow_module_loading;
+ table[5].data = &c->use_pid_file;
+ table[6].data = &c->system_instance;
+ table[7].data = &c->no_cpu_limit;
+ table[8].data = &c->disable_shm;
+ table[9].data = &c->exit_idle_time;
+ table[10].data = &c->module_idle_time;
+ table[11].data = &c->scache_idle_time;
table[12].data = c;
- table[13].data = &c->use_pid_file;
- table[14].data = &c->system_instance;
- table[15].data = &c->no_cpu_limit;
- table[16].data = &c->disable_shm;
+ table[13].data = &c->dl_search_path;
+ table[14].data = &c->default_script_file;
+ table[15].data = c;
+ table[16].data = c;
+ table[17].data = c;
+ table[18].data = c;
+ table[19].data = c;
+ table[20].data = c;
+ table[21].data = c;
+ table[22].data = c;
+ table[23].data = c;
+ table[24].data = c;
+ table[25].data = &c->disable_remixing;
+ table[26].data = &c->load_default_script_file;
#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[27].data = &c->rlimit_fsize;
+ table[28].data = &c->rlimit_data;
+ table[29].data = &c->rlimit_stack;
+ table[30].data = &c->rlimit_as;
+ table[31].data = &c->rlimit_core;
+ table[32].data = &c->rlimit_nofile;
+ table[33].data = &c->rlimit_as;
#ifdef RLIMIT_NPROC
- table[23].data = &c->rlimit_nproc;
+ table[34].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[35].data = &c->rlimit_memlock;
+#endif
+
+#ifdef RLIMIT_LOCKS
+#ifndef RLIMIT_MEMLOCK
+#error "Houston, we have a numbering problem!"
+#endif
+ table[36].data = &c->rlimit_locks;
+#endif
+
+#ifdef RLIMIT_SIGPENDING
+#ifndef RLIMIT_LOCKS
+#error "Houston, we have a numbering problem!"
+#endif
+ table[37].data = &c->rlimit_sigpending;
+#endif
+
+#ifdef RLIMIT_MSGQUEUE
+#ifndef RLIMIT_SIGPENDING
+#error "Houston, we have a numbering problem!"
+#endif
+ table[38].data = &c->rlimit_msgqueue;
+#endif
+
+#ifdef RLIMIT_NICE
+#ifndef RLIMIT_MSGQUEUE
+#error "Houston, we have a numbering problem!"
+#endif
+ table[39].data = &c->rlimit_nice;
+#endif
+
+#ifdef RLIMIT_RTPRIO
+#ifndef RLIMIT_NICE
+#error "Houston, we have a numbering problem!"
+#endif
+ table[40].data = &c->rlimit_rtprio;
#endif
+
+#ifdef RLIMIT_RTTIME
+#ifndef RLIMIT_RTTIME
+#error "Houston, we have a numbering problem!"
+#endif
+ table[41].data = &c->rlimit_rttime;
#endif
-
-
+#endif
+
pa_xfree(c->config_file);
c->config_file = NULL;
f = filename ?
fopen(c->config_file = pa_xstrdup(filename), "r") :
- pa_open_config_file(DEFAULT_CONFIG_FILE, DEFAULT_CONFIG_FILE_USER, ENV_CONFIG_FILE, &c->config_file, "r");
+ pa_open_config_file(DEFAULT_CONFIG_FILE, DEFAULT_CONFIG_FILE_USER, ENV_CONFIG_FILE, &c->config_file);
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", pa_cstrerror(errno));
goto finish;
}
r = f ? pa_config_parse(c->config_file, f, table, NULL) : 0;
-
+
finish:
if (f)
fclose(f);
-
+
return r;
}
int pa_daemon_conf_env(pa_daemon_conf *c) {
char *e;
+ pa_assert(c);
if ((e = getenv(ENV_DL_SEARCH_PATH))) {
pa_xfree(c->dl_search_path);
@@ -339,6 +586,35 @@ int pa_daemon_conf_env(pa_daemon_conf *c) {
return 0;
}
+const char *pa_daemon_conf_get_default_script_file(pa_daemon_conf *c) {
+ pa_assert(c);
+
+ if (!c->default_script_file) {
+ if (c->system_instance)
+ c->default_script_file = pa_find_config_file(DEFAULT_SYSTEM_SCRIPT_FILE, NULL, ENV_SCRIPT_FILE);
+ else
+ c->default_script_file = pa_find_config_file(DEFAULT_SCRIPT_FILE, DEFAULT_SCRIPT_FILE_USER, ENV_SCRIPT_FILE);
+ }
+
+ return c->default_script_file;
+}
+
+FILE *pa_daemon_conf_open_default_script_file(pa_daemon_conf *c) {
+ FILE *f;
+ pa_assert(c);
+
+ if (!c->default_script_file) {
+ if (c->system_instance)
+ f = pa_open_config_file(DEFAULT_SYSTEM_SCRIPT_FILE, NULL, ENV_SCRIPT_FILE, &c->default_script_file);
+ else
+ f = pa_open_config_file(DEFAULT_SCRIPT_FILE, DEFAULT_SCRIPT_FILE_USER, ENV_SCRIPT_FILE, &c->default_script_file);
+ } else
+ f = fopen(c->default_script_file, "r");
+
+ return f;
+}
+
+
static const char* const log_level_to_string[] = {
[PA_LOG_DEBUG] = "debug",
[PA_LOG_INFO] = "info",
@@ -348,43 +624,76 @@ 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_strbuf_printf(s, "daemonize = %i\n", !!c->daemonize);
- pa_strbuf_printf(s, "fail = %i\n", !!c->fail);
- pa_strbuf_printf(s, "high-priority = %i\n", !!c->high_priority);
- pa_strbuf_printf(s, "disallow-module-loading = %i\n", !!c->disallow_module_loading);
+ pa_assert(c->log_level <= PA_LOG_LEVEL_MAX);
+
+ pa_strbuf_printf(s, "daemonize = %s\n", pa_yes_no(c->daemonize));
+ pa_strbuf_printf(s, "fail = %s\n", pa_yes_no(c->fail));
+ pa_strbuf_printf(s, "high-priority = %s\n", pa_yes_no(c->high_priority));
+ pa_strbuf_printf(s, "nice-level = %i\n", c->nice_level);
+ pa_strbuf_printf(s, "realtime-scheduling = %s\n", pa_yes_no(c->realtime_scheduling));
+ pa_strbuf_printf(s, "realtime-priority = %i\n", c->realtime_priority);
+ pa_strbuf_printf(s, "disallow-module-loading = %s\n", pa_yes_no(c->disallow_module_loading));
+ pa_strbuf_printf(s, "use-pid-file = %s\n", pa_yes_no(c->use_pid_file));
+ pa_strbuf_printf(s, "system-instance = %s\n", pa_yes_no(c->system_instance));
+ pa_strbuf_printf(s, "no-cpu-limit = %s\n", pa_yes_no(c->no_cpu_limit));
+ pa_strbuf_printf(s, "disable-shm = %s\n", pa_yes_no(c->disable_shm));
pa_strbuf_printf(s, "exit-idle-time = %i\n", c->exit_idle_time);
pa_strbuf_printf(s, "module-idle-time = %i\n", c->module_idle_time);
pa_strbuf_printf(s, "scache-idle-time = %i\n", c->scache_idle_time);
- pa_strbuf_printf(s, "dl-search-path = %s\n", c->dl_search_path ? c->dl_search_path : "");
- pa_strbuf_printf(s, "default-script-file = %s\n", c->default_script_file);
+ pa_strbuf_printf(s, "dl-search-path = %s\n", pa_strempty(c->dl_search_path));
+ pa_strbuf_printf(s, "default-script-file = %s\n", pa_strempty(pa_daemon_conf_get_default_script_file(c)));
+ pa_strbuf_printf(s, "load-default-script-file = %s\n", pa_yes_no(c->load_default_script_file));
pa_strbuf_printf(s, "log-target = %s\n", c->auto_log_target ? "auto" : (c->log_target == PA_LOG_SYSLOG ? "syslog" : "stderr"));
pa_strbuf_printf(s, "log-level = %s\n", log_level_to_string[c->log_level]);
pa_strbuf_printf(s, "resample-method = %s\n", pa_resample_method_to_string(c->resample_method));
- 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-remixing = %s\n", pa_yes_no(c->disable_remixing));
+ 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);
- pa_strbuf_printf(s, "rlimit-data = %li\n", c->rlimit_data.is_set ? (long int) c->rlimit_data.value : -1);
pa_strbuf_printf(s, "rlimit-fsize = %li\n", c->rlimit_fsize.is_set ? (long int) c->rlimit_fsize.value : -1);
- pa_strbuf_printf(s, "rlimit-nofile = %li\n", c->rlimit_nofile.is_set ? (long int) c->rlimit_nofile.value : -1);
+ pa_strbuf_printf(s, "rlimit-data = %li\n", c->rlimit_data.is_set ? (long int) c->rlimit_data.value : -1);
pa_strbuf_printf(s, "rlimit-stack = %li\n", c->rlimit_stack.is_set ? (long int) c->rlimit_stack.value : -1);
+ pa_strbuf_printf(s, "rlimit-core = %li\n", c->rlimit_core.is_set ? (long int) c->rlimit_core.value : -1);
+ 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-rss = %li\n", c->rlimit_rss.is_set ? (long int) c->rlimit_rss.value : -1);
#ifdef RLIMIT_NPROC
pa_strbuf_printf(s, "rlimit-nproc = %li\n", c->rlimit_nproc.is_set ? (long int) c->rlimit_nproc.value : -1);
#endif
+ pa_strbuf_printf(s, "rlimit-nofile = %li\n", c->rlimit_nofile.is_set ? (long int) c->rlimit_nofile.value : -1);
#ifdef RLIMIT_MEMLOCK
pa_strbuf_printf(s, "rlimit-memlock = %li\n", c->rlimit_memlock.is_set ? (long int) c->rlimit_memlock.value : -1);
#endif
+#ifdef RLIMIT_LOCKS
+ pa_strbuf_printf(s, "rlimit-locks = %li\n", c->rlimit_locks.is_set ? (long int) c->rlimit_locks.value : -1);
+#endif
+#ifdef RLIMIT_SIGPENDING
+ pa_strbuf_printf(s, "rlimit-sigpending = %li\n", c->rlimit_sigpending.is_set ? (long int) c->rlimit_sigpending.value : -1);
+#endif
+#ifdef RLIMIT_MSGQUEUE
+ pa_strbuf_printf(s, "rlimit-msgqueue = %li\n", c->rlimit_msgqueue.is_set ? (long int) c->rlimit_msgqueue.value : -1);
+#endif
+#ifdef RLIMIT_NICE
+ pa_strbuf_printf(s, "rlimit-nice = %li\n", c->rlimit_nice.is_set ? (long int) c->rlimit_nice.value : -1);
#endif
-
+#ifdef RLIMIT_RTPRIO
+ pa_strbuf_printf(s, "rlimit-rtprio = %li\n", c->rlimit_rtprio.is_set ? (long int) c->rlimit_rtprio.value : -1);
+#endif
+#ifdef RLIMIT_RTTIME
+ pa_strbuf_printf(s, "rlimit-rttime = %li\n", c->rlimit_rttime.is_set ? (long int) c->rlimit_rttime.value : -1);
+#endif
+#endif
+
return pa_strbuf_tostring_free(s);
}
diff --git a/src/daemon/daemon-conf.h b/src/daemon/daemon-conf.h
index b4b833ad..be2fe1ab 100644
--- a/src/daemon/daemon-conf.h
+++ b/src/daemon/daemon-conf.h
@@ -1,11 +1,12 @@
#ifndef foodaemonconfhfoo
#define foodaemonconfhfoo
-/* $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,
@@ -23,6 +24,9 @@
***/
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulse/sample.h>
#ifdef HAVE_SYS_RESOURCE_H
#include <sys/resource.h>
@@ -31,52 +35,80 @@
/* The actual command to execute */
typedef enum pa_daemon_conf_cmd {
PA_CMD_DAEMON, /* the default */
+ PA_CMD_START,
PA_CMD_HELP,
PA_CMD_VERSION,
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
typedef struct pa_rlimit {
rlim_t value;
- int is_set;
+ pa_bool_t is_set;
} pa_rlimit;
#endif
/* A structure containing configuration data for the PulseAudio server . */
typedef struct pa_daemon_conf {
pa_daemon_conf_cmd_t cmd;
- int daemonize,
+ pa_bool_t daemonize,
fail,
high_priority,
+ realtime_scheduling,
disallow_module_loading,
- exit_idle_time,
- module_idle_time,
- scache_idle_time,
- auto_log_target,
use_pid_file,
system_instance,
no_cpu_limit,
- disable_shm;
+ disable_shm,
+ disable_remixing,
+ load_default_script_file;
+ int exit_idle_time,
+ module_idle_time,
+ scache_idle_time,
+ auto_log_target,
+ realtime_priority,
+ nice_level,
+ resample_method;
char *script_commands, *dl_search_path, *default_script_file;
pa_log_target_t log_target;
pa_log_level_t log_level;
- int resample_method;
char *config_file;
-
+
#ifdef HAVE_SYS_RESOURCE_H
- pa_rlimit rlimit_as, rlimit_core, rlimit_data, rlimit_fsize, rlimit_nofile, rlimit_stack;
+ pa_rlimit rlimit_fsize, rlimit_data, rlimit_stack, rlimit_core, rlimit_rss, rlimit_nofile, rlimit_as;
#ifdef RLIMIT_NPROC
pa_rlimit rlimit_nproc;
#endif
#ifdef RLIMIT_MEMLOCK
pa_rlimit rlimit_memlock;
#endif
+#ifdef RLIMIT_LOCKS
+ pa_rlimit rlimit_locks;
#endif
-
+#ifdef RLIMIT_SIGPENDING
+ pa_rlimit rlimit_sigpending;
+#endif
+#ifdef RLIMIT_MSGQUEUE
+ pa_rlimit rlimit_msgqueue;
+#endif
+#ifdef RLIMIT_NICE
+ pa_rlimit rlimit_nice;
+#endif
+#ifdef RLIMIT_RTPRIO
+ pa_rlimit rlimit_rtprio;
+#endif
+#ifdef RLIMIT_RTTIME
+ pa_rlimit rlimit_rttime;
+#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 */
@@ -102,4 +134,7 @@ 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);
int pa_daemon_conf_set_resample_method(pa_daemon_conf *c, const char *string);
+const char *pa_daemon_conf_get_default_script_file(pa_daemon_conf *c);
+FILE *pa_daemon_conf_open_default_script_file(pa_daemon_conf *c);
+
#endif
diff --git a/src/daemon/daemon.conf.in b/src/daemon/daemon.conf.in
index 29b22a42..dfabcfb2 100644
--- a/src/daemon/daemon.conf.in
+++ b/src/daemon/daemon.conf.in
@@ -1,5 +1,3 @@
-# $Id$
-#
# This file is part of PulseAudio.
#
# PulseAudio is free software; you can redistribute it and/or modify
@@ -17,85 +15,59 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA.
-## Configuration file for the pulseaudio daemon. Default values are
-## commented out. Use either ; or # for commenting
-
-# Extra verbositiy
-; verbose = 0
+## Configuration file for the PulseAudio daemon. See pulse-daemon.conf(5) for
+## more information. Default values a commented out. Use either ; or # for
+## commenting.
-## Daemonize after startup
-; daemonize = 0
+; daemonize = no
+; fail = yes
+; disallow-module-loading = no
+; use-pid-file = yes
+; system-instance = no
+; disable-shm = no
-## Quit if startup fails
-; fail = 1
+; high-priority = yes
+; nice-level = -11
-## Renice the daemon to level -15 and try to get SCHED_FIFO
-## scheduling. This a good idea if you hear annyoing noise in the
-## playback. However, this is a certain security issue, since it works
-## when called SUID root only. root is dropped immediately after gaining
-## the nice level and SCHED_FIFO scheduling on startup.
-; high-priority = 0
+; realtime-scheduling = no
+; realtime-priority = 5
-## Disallow module loading after startup
-; disallow-module-loading = 0
-
-## Terminate the daemon after the last client quit and this time
-## passed. Use a negative value to disable this feature.
; exit-idle-time = -1
-
-## Unload autoloaded modules after being idle for this time
; module-idle-time = 20
-
-## Unload autoloaded sample cache entries after being idle for this time
; scache-idle-time = 20
-## The path were to look for dynamic shared objects (DSOs aka
-## plugins). You may specify more than one path seperated by
-## colons.
-; dl-search-path = @PA_DLSEARCHPATH@
+; dl-search-path = (depends on architecture)
-## The default script file to load. Specify an empty string for not
-## loading a default script file. The
+; load-defaul-script-file = yes
; default-script-file = @PA_DEFAULT_CONFIG_FILE@
-## The default log target. Use either "stderr", "syslog" or
-## "auto". The latter is equivalent to "sylog" in case daemonize is
-## true, otherwise to "stderr".
; log-target = auto
+; log-level = notice
-## The resampling algorithm to use. Use one of src-sinc-best-quality,
-## src-sinc-medium-quality, src-sinc-fastest, src-zero-order-hold,
-## src-linear, trivial. See the documentation of libsamplerate for an
-## explanation for the different methods. The method 'trivial' is the
-## only algorithm implemented without usage of floating point
-## numbers. If you're tight on CPU consider using this. On the other
-## hand it has the worst quality of all.
-; resample-method = sinc-fastest
-
-## Create a PID file in /tmp/pulseaudio-$USER/pid. Of this is enabled
-## you may use commands like "pulseaudio --kill" or "pulseaudio
-## --check". If you are planning to start more than one pulseaudio
-## process per user, you better disable this option since it
-## effectively disables multiple instances.
-; use-pid-file = 1
-
-## Do not install the CPU load limit, even on platforms where it is
-## supported. This option is useful when debugging/profiling
-## PulseAudio to disable disturbing SIGXCPU signals.
-; no-cpu-limit = 0
+; resample-method = speex-float-3
+; disable-remixing = no
-## Run the daemon as system-wide instance, requires root priviliges
-; system-instance = 0
+; no-cpu-limit = no
-## Resource limits, see getrlimit(2) for more information
-; rlimit-as = -1
-; rlimit-core = -1
-; rlimit-data = -1
; rlimit-fsize = -1
-; rlimit-nofile = 200
+; rlimit-data = -1
; rlimit-stack = -1
+; rlimit-core = -1
+; rlimit-as = -1
+; rlimit-rss = -1
; rlimit-nproc = -1
-; rlimit-memlock = 25
-
-## Disable shared memory data transfer
-; disable-shm = 0
+; rlimit-nofile = 256
+; rlimit-memlock = -1
+; rlimit-locks = -1
+; rlimit-sigpending = -1
+; rlimit-msgqueue = -1
+; rlimit-nice = 31
+; rlimit-rtprio = 9
+; rlimit-rtttime = 1000000
+
+; default-sample-format = s16le
+; default-sample-rate = 44100
+; default-sample-channels = 2
+
+; default-fragments = 4
+; default-fragment-size-msec = 25
diff --git a/src/daemon/default.pa.in b/src/daemon/default.pa.in
index af2a6789..aad9f5d6 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
+
+.fail
-### Load audio drivers statically
+### 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,22 +37,18 @@
#load-module module-pipe-sink
### Automatically load driver modules depending on the hardware available
-@HAVE_HAL_TRUE@load-module module-hal-detect
-
+.ifexists 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
+.ifexists module-esound-protocol-unix@PA_SOEXT@
load-module module-esound-protocol-unix
+.endif
load-module module-native-protocol-unix
### Network access (may be configured with paprefs, so leave this commented
@@ -61,27 +67,50 @@ 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
+### Make sure we always have a sink around, even if it is a null sink.
+load-module module-always-sink
-.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=bell-windowing-system
-### Publish connection data in the X11 root window
-load-module module-x11-publish
+### 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
+
+### If autoexit on idle is enabled we want to make sure we only quit
+### when no local session needs us anymore.
+load-module module-console-kit
+
+### Enable positioned event sounds
+load-module module-position-event-sounds
### 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 module-gconf@PA_SOEXT@
+.nofail
load-module module-gconf
+.fail
+.endif
+
+### Publish connection data in the X11 root window
+.ifexists module-x11-publish@PA_SOEXT@
+.nofail
+load-module module-x11-publish
+.fail
+.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 06734ea6..cd6866aa 100644
--- a/src/daemon/dumpmodules.c
+++ b/src/daemon/dumpmodules.c
@@ -1,8 +1,9 @@
-/* $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,
@@ -25,34 +26,38 @@
#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");
nl = 1;
printf("Name: %s\n", name);
-
+
if (!i->description && !i->version && !i->author && !i->usage)
printf("No module information available\n");
else {
@@ -64,15 +69,18 @@ static void long_info(const char *name, const char *path, pa_modinfo *i) {
printf("Author: %s\n", i->author);
if (i->usage)
printf("Usage: %s\n", i->usage);
+ printf("Load Once: %s\n", pa_yes_no(i->load_once));
}
-
+
if (path)
printf("Path: %s\n", path);
}
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);
@@ -86,11 +94,11 @@ static int is_preloaded(const char *name) {
for (l = lt_preloaded_symbols; l->name; l++) {
char buf[64], *e;
-
+
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;
@@ -112,12 +120,14 @@ static int callback(const char *path, lt_ptr data) {
if (is_preloaded(e))
return 0;
-
+
show_info(e, path, c->log_level >= PA_LOG_INFO ? long_info : short_info);
return 0;
}
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++)
@@ -127,20 +137,20 @@ void pa_dump_modules(pa_daemon_conf *c, int argc, char * const argv[]) {
for (l = lt_preloaded_symbols; l->name; l++) {
char buf[64], *e;
-
+
if (l->address)
continue;
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;
-
+
show_info(buf, NULL, c->log_level >= PA_LOG_INFO ? long_info : short_info);
}
-
+
lt_dlforeachfile(NULL, callback, c);
}
}
diff --git a/src/daemon/dumpmodules.h b/src/daemon/dumpmodules.h
index 05cd86e0..c49a5eda 100644
--- a/src/daemon/dumpmodules.h
+++ b/src/daemon/dumpmodules.h
@@ -1,11 +1,11 @@
#ifndef foodumpmoduleshfoo
#define foodumpmoduleshfoo
-/* $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,
diff --git a/src/daemon/esdcompat.in b/src/daemon/esdcompat.in
index 942389d2..66501803 100755
--- a/src/daemon/esdcompat.in
+++ b/src/daemon/esdcompat.in
@@ -1,7 +1,5 @@
#!/bin/sh
-# $Id$
-#
# This file is part of PulseAudio.
#
# PulseAudio is free software; you can redistribute it and/or modify
diff --git a/src/daemon/ltdl-bind-now.c b/src/daemon/ltdl-bind-now.c
new file mode 100644
index 00000000..b1770674
--- /dev/null
+++ b/src/daemon/ltdl-bind-now.c
@@ -0,0 +1,195 @@
+/***
+ 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
+
+#ifndef HAVE_STRUCT_LT_USER_DLLOADER
+/* Only used with ltdl 2.2 */
+#include <string.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.
+*/
+
+#ifndef HAVE_LT_DLADVISE
+static lt_module bind_now_open(lt_user_data d, const char *fname) {
+#else
+ static lt_module bind_now_open(lt_user_data d, const char *fname, lt_dladvise advise) {
+#endif
+ 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
+# ifdef HAVE_STRUCT_LT_USER_DLLOADER
+ 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
+ };
+# else
+ static const lt_dlvtable *dlopen_loader;
+ static lt_dlvtable bindnow_loader;
+# endif
+#endif
+
+ pa_assert_se(lt_dlinit() == 0);
+ pa_assert_se(libtool_mutex = pa_mutex_new(TRUE, FALSE));
+#ifdef HAVE_LT_DLMUTEX_REGISTER
+ pa_assert_se(lt_dlmutex_register(libtool_lock, libtool_unlock, libtool_set_error, libtool_get_error) == 0);
+#endif
+
+#ifdef PA_BIND_NOW
+# ifdef HAVE_STRUCT_LT_USER_DLLOADER
+
+ 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.");
+# else
+ /* Already initialised */
+ if ( dlopen_loader != NULL ) return;
+
+ if (!(dlopen_loader = lt_dlloader_find("dlopen"))) {
+ pa_log_warn("Failed to find original dlopen loader.");
+ return;
+ }
+
+ memcpy(&bindnow_loader, dlopen_loader, sizeof(bindnow_loader));
+ bindnow_loader.name = "bind-now-loader";
+ bindnow_loader.module_open = bind_now_open;
+ bindnow_loader.module_close = bind_now_close;
+ bindnow_loader.find_sym = bind_now_find_sym;
+ bindnow_loader.priority = LT_DLLOADER_PREPEND;
+
+ /* Add our BIND_NOW loader as the default module loader. */
+ if (lt_dlloader_add(&bindnow_loader) != 0)
+ pa_log_warn("Failed to add bind-now-loader.");
+# endif
+#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..f95d13b4
--- /dev/null
+++ b/src/daemon/ltdl-bind-now.h
@@ -0,0 +1,30 @@
+#ifndef foopulsecoreltdlbindnowhfoo
+#define foopulsecoreltdlbindnowhfoo
+
+/***
+ 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 5d77282c..14594416 100644
--- a/src/daemon/main.c
+++ b/src/daemon/main.c
@@ -1,18 +1,19 @@
-/* $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
@@ -30,7 +31,6 @@
#include <stdio.h>
#include <signal.h>
#include <stddef.h>
-#include <assert.h>
#include <ltdl.h>
#include <limits.h>
#include <fcntl.h>
@@ -56,13 +56,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>
@@ -75,12 +78,23 @@
#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"
+#include "polkit.h"
+
+#define AUTOSPAWN_LOCK "autospawn.lock"
#ifdef HAVE_LIBWRAP
/* Only one instance of these variables */
@@ -101,7 +115,7 @@ static void message_cb(pa_mainloop_api*a, pa_time_event*e, PA_GCC_UNUSED const s
MSG msg;
struct timeval tvnext;
- while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
+ while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT)
raise(SIGTERM);
else {
@@ -117,7 +131,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
@@ -125,7 +139,7 @@ static void signal_callback(pa_mainloop_api*m, PA_GCC_UNUSED pa_signal_event *e,
pa_module_load(userdata, "module-cli", NULL);
break;
#endif
-
+
#ifdef SIGUSR2
case SIGUSR2:
pa_module_load(userdata, "module-cli-protocol-unix", NULL);
@@ -150,16 +164,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)
static int change_user(void) {
@@ -170,7 +174,7 @@ static int change_user(void) {
/* This function is called only in system-wide mode. It creates a
* runtime dir in /var/run/ with proper UID/GID and drops privs
* afterwards. */
-
+
if (!(pw = getpwnam(PA_SYSTEM_USER))) {
pa_log("Failed to find user '%s'.", PA_SYSTEM_USER);
return -1;
@@ -197,7 +201,14 @@ static int change_user(void) {
pa_log("Failed to create '%s': %s", PA_SYSTEM_RUNTIME_PATH, pa_cstrerror(errno));
return -1;
}
-
+
+ if (pa_make_secure_dir(PA_SYSTEM_STATE_PATH, 0700, pw->pw_uid, gr->gr_gid) < 0) {
+ pa_log("Failed to create '%s': %s", PA_SYSTEM_STATE_PATH, pa_cstrerror(errno));
+ return -1;
+ }
+
+ /* We don't create the config dir here, because we don't need to write to it */
+
if (initgroups(PA_SYSTEM_USER, gr->gr_gid) != 0) {
pa_log("Failed to change group list: %s", pa_cstrerror(errno));
return -1;
@@ -235,13 +246,15 @@ static int change_user(void) {
return -1;
}
- set_env("USER", PA_SYSTEM_USER);
- set_env("LOGNAME", PA_SYSTEM_GROUP);
- set_env("HOME", PA_SYSTEM_RUNTIME_PATH);
+ pa_set_env("USER", PA_SYSTEM_USER);
+ pa_set_env("USERNAME", PA_SYSTEM_USER);
+ pa_set_env("LOGNAME", PA_SYSTEM_USER);
+ pa_set_env("HOME", PA_SYSTEM_RUNTIME_PATH);
/* Relevant for pa_runtime_path() */
- set_env("PULSE_RUNTIME_PATH", PA_SYSTEM_RUNTIME_PATH);
- set_env("PULSE_CONFIG_PATH", PA_SYSTEM_RUNTIME_PATH);
+ pa_set_env("PULSE_RUNTIME_PATH", PA_SYSTEM_RUNTIME_PATH);
+ pa_set_env("PULSE_CONFIG_PATH", PA_SYSTEM_CONFIG_PATH);
+ pa_set_env("PULSE_STATE_PATH", PA_SYSTEM_STATE_PATH);
pa_log_info("Successfully dropped root privileges.");
@@ -257,51 +270,57 @@ static int change_user(void) {
#endif /* HAVE_PWD_H && HAVE_GRP_H */
-static int create_runtime_dir(void) {
- char fn[PATH_MAX];
-
- pa_runtime_path(NULL, fn, sizeof(fn));
-
- /* This function is called only when the daemon is started in
- * per-user mode. We create the runtime directory somewhere in
- * /tmp/ with the current UID/GID */
-
- if (pa_make_secure_dir(fn, 0700, (uid_t)-1, (gid_t)-1) < 0) {
- pa_log("Failed to create '%s': %s", fn, pa_cstrerror(errno));
- return -1;
- }
-
- return 0;
-}
-
#ifdef HAVE_SYS_RESOURCE_H
-static void set_one_rlimit(const pa_rlimit *r, int resource, const char *name) {
+static int 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;
+ return 0;
rl.rlim_cur = rl.rlim_max = r->value;
- if (setrlimit(resource, &rl) < 0)
- pa_log_warn("setrlimit(%s, (%u, %u)) failed: %s", name, (unsigned) r->value, (unsigned) r->value, pa_cstrerror(errno));
+ if (setrlimit(resource, &rl) < 0) {
+ pa_log_info("setrlimit(%s, (%u, %u)) failed: %s", name, (unsigned) r->value, (unsigned) r->value, pa_cstrerror(errno));
+ return -1;
+ }
+
+ return 0;
}
static void set_all_rlimits(const pa_daemon_conf *conf) {
- set_one_rlimit(&conf->rlimit_as, RLIMIT_AS, "RLIMIT_AS");
- set_one_rlimit(&conf->rlimit_core, RLIMIT_CORE, "RLIMIT_CORE");
- set_one_rlimit(&conf->rlimit_data, RLIMIT_DATA, "RLIMIT_DATA");
set_one_rlimit(&conf->rlimit_fsize, RLIMIT_FSIZE, "RLIMIT_FSIZE");
- set_one_rlimit(&conf->rlimit_nofile, RLIMIT_NOFILE, "RLIMIT_NOFILE");
+ set_one_rlimit(&conf->rlimit_data, RLIMIT_DATA, "RLIMIT_DATA");
set_one_rlimit(&conf->rlimit_stack, RLIMIT_STACK, "RLIMIT_STACK");
+ set_one_rlimit(&conf->rlimit_core, RLIMIT_CORE, "RLIMIT_CORE");
+ set_one_rlimit(&conf->rlimit_rss, RLIMIT_RSS, "RLIMIT_RSS");
#ifdef RLIMIT_NPROC
set_one_rlimit(&conf->rlimit_nproc, RLIMIT_NPROC, "RLIMIT_NPROC");
#endif
+ set_one_rlimit(&conf->rlimit_nofile, RLIMIT_NOFILE, "RLIMIT_NOFILE");
#ifdef RLIMIT_MEMLOCK
set_one_rlimit(&conf->rlimit_memlock, RLIMIT_MEMLOCK, "RLIMIT_MEMLOCK");
#endif
+ set_one_rlimit(&conf->rlimit_as, RLIMIT_AS, "RLIMIT_AS");
+#ifdef RLIMIT_LOCKS
+ set_one_rlimit(&conf->rlimit_locks, RLIMIT_LOCKS, "RLIMIT_LOCKS");
+#endif
+#ifdef RLIMIT_SIGPENDING
+ set_one_rlimit(&conf->rlimit_sigpending, RLIMIT_SIGPENDING, "RLIMIT_SIGPENDING");
+#endif
+#ifdef RLIMIT_MSGQUEUE
+ set_one_rlimit(&conf->rlimit_msgqueue, RLIMIT_MSGQUEUE, "RLIMIT_MSGQUEUE");
+#endif
+#ifdef RLIMIT_NICE
+ set_one_rlimit(&conf->rlimit_nice, RLIMIT_NICE, "RLIMIT_NICE");
+#endif
+#ifdef RLIMIT_RTPRIO
+ set_one_rlimit(&conf->rlimit_rtprio, RLIMIT_RTPRIO, "RLIMIT_RTPRIO");
+#endif
+#ifdef RLIMIT_RTTIME
+ set_one_rlimit(&conf->rlimit_rttime, RLIMIT_RTTIME, "RLIMIT_RTTIME");
+#endif
}
#endif
@@ -310,57 +329,89 @@ 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 daemon_pipe[2] = { -1, -1 };
- int suid_root, real_root;
- int valid_pid_file = 0;
-
-#ifdef HAVE_GETUID
+ char *s;
+ int r = 0, retval = 1, d = 0;
+ pa_bool_t suid_root, real_root;
+ pa_bool_t valid_pid_file = FALSE;
gid_t gid = (gid_t) -1;
+ pa_bool_t ltdl_init = FALSE;
+ int passed_fd = -1;
+ const char *e;
+#ifdef HAVE_FORK
+ int daemon_pipe[2] = { -1, -1 };
#endif
-
#ifdef OS_IS_WIN32
- pa_time_event *timer;
- struct timeval tv;
+ pa_time_event *win32_timer;
+ struct timeval win32_tv;
#endif
+ char *lf = NULL;
+ int autospawn_lock_fd = -1;
- setlocale(LC_ALL, "");
+#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")) {
+ char *rp;
- pa_limit_caps();
+ /* We have to execute ourselves, because the libc caches the
+ * value of $LD_BIND_NOW on initialization. */
+
+ pa_set_env("LD_BIND_NOW", "1");
+ pa_assert_se(rp = pa_readlink("/proc/self/exe"));
+ pa_assert_se(execv(rp, argv) == 0);
+ }
+#endif
#ifdef HAVE_GETUID
real_root = getuid() == 0;
suid_root = !real_root && geteuid() == 0;
-
- 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"'.");
- pa_drop_root();
- }
#else
- real_root = 0;
- suid_root = 0;
+ real_root = FALSE;
+ suid_root = FALSE;
#endif
-
- LTDL_SET_PRELOADED_SYMBOLS();
-
- r = lt_dlinit();
- assert(r == 0);
-#ifdef OS_IS_WIN32
- {
- WSADATA data;
- WSAStartup(MAKEWORD(2, 0), &data);
+ if (!real_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. */
}
-#endif
- pa_random_seed();
-
+ if ((e = getenv("PULSE_PASSED_FD"))) {
+ passed_fd = atoi(e);
+
+ if (passed_fd <= 2)
+ passed_fd = -1;
+ }
+
+ pa_close_all(passed_fd, -1);
+
+ pa_reset_sigs(-1);
+ pa_unblock_sigs(-1);
+
+ /* At this point, we are a normal user, possibly with CAP_NICE if
+ * we were started SUID. If we are started as normal root, than we
+ * still are normal root. */
+
+ setlocale(LC_ALL, "");
+ pa_log_set_maximal_level(PA_LOG_INFO);
pa_log_set_ident("pulseaudio");
-
+
conf = pa_daemon_conf_new();
-
+
if (pa_daemon_conf_load(conf, NULL) < 0)
goto finish;
@@ -368,24 +419,140 @@ int main(int argc, char *argv[]) {
goto finish;
if (pa_cmdline_parse(conf, argc, argv, &d) < 0) {
- pa_log("failed to parse command line.");
+ pa_log("Failed to parse command line.");
goto finish;
}
pa_log_set_maximal_level(conf->log_level);
pa_log_set_target(conf->auto_log_target ? PA_LOG_STDERR : conf->log_target, NULL);
- if (conf->high_priority && conf->cmd == PA_CMD_DAEMON)
- pa_raise_priority();
+ pa_log_debug("Started as real root: %s, suid root: %s", pa_yes_no(real_root), pa_yes_no(suid_root));
- pa_drop_caps();
+ if (!real_root && pa_have_caps()) {
+ pa_bool_t allow_high_priority = FALSE, allow_realtime = FALSE;
- if (suid_root)
- pa_drop_root();
+ /* Let's better not enable high prio or RT by default */
+
+ if (conf->high_priority && !allow_high_priority) {
+ if (pa_own_uid_in_group(PA_REALTIME_GROUP, &gid) > 0) {
+ pa_log_info("We're in the group '"PA_REALTIME_GROUP"', allowing high-priority scheduling.");
+ allow_high_priority = TRUE;
+ }
+ }
+
+ if (conf->realtime_scheduling && !allow_realtime) {
+ if (pa_own_uid_in_group(PA_REALTIME_GROUP, &gid) > 0) {
+ pa_log_info("We're in the group '"PA_REALTIME_GROUP"', allowing real-time scheduling.");
+ allow_realtime = TRUE;
+ }
+ }
+
+#ifdef HAVE_POLKIT
+ if (conf->high_priority && !allow_high_priority) {
+ if (pa_polkit_check("org.pulseaudio.acquire-high-priority") > 0) {
+ pa_log_info("PolicyKit grants us acquire-high-priority privilege.");
+ allow_high_priority = TRUE;
+ } else
+ pa_log_info("PolicyKit refuses acquire-high-priority privilege.");
+ }
+
+ if (conf->realtime_scheduling && !allow_realtime) {
+ if (pa_polkit_check("org.pulseaudio.acquire-real-time") > 0) {
+ pa_log_info("PolicyKit grants us acquire-real-time privilege.");
+ allow_realtime = TRUE;
+ } else
+ pa_log_info("PolicyKit refuses acquire-real-time privilege.");
+ }
+#endif
+
+ if (!allow_high_priority && !allow_realtime) {
+
+ /* OK, there's no further need to keep CAP_NICE. Hence
+ * let's give it up early */
+
+ pa_drop_caps();
+
+ if (conf->high_priority || conf->realtime_scheduling)
+ pa_log_notice("Called SUID root and real-time/high-priority scheduling was requested in the configuration. However, we lack the necessary priviliges:\n"
+ "We are not in group '"PA_REALTIME_GROUP"' and PolicyKit refuse to grant us priviliges. Dropping SUID again.\n"
+ "For enabling real-time scheduling please acquire the appropriate PolicyKit priviliges, or become a member of '"PA_REALTIME_GROUP"', or increase the RLIMIT_NICE/RLIMIT_RTPRIO resource limits for this user.");
+ }
+ }
+
+#ifdef HAVE_SYS_RESOURCE_H
+ /* Reset resource limits. If we are run as root (for system mode)
+ * this might end up increasing the limits, which is intended
+ * behaviour. For all other cases, i.e. started as normal user, or
+ * SUID root at this point we should have no CAP_SYS_RESOURCE and
+ * increasing the limits thus should fail. Which is, too, intended
+ * behaviour */
+
+ set_all_rlimits(conf);
+#endif
+
+ if (conf->high_priority && !pa_can_high_priority())
+ pa_log_warn("High-priority scheduling enabled in configuration but not allowed by policy.");
+
+ if (conf->high_priority && (conf->cmd == PA_CMD_DAEMON || conf->cmd == PA_CMD_START))
+ pa_raise_priority(conf->nice_level);
+
+ if (pa_have_caps()) {
+ pa_bool_t drop;
+
+ drop = (conf->cmd != PA_CMD_DAEMON && conf->cmd != PA_CMD_START) || !conf->realtime_scheduling;
+
+#ifdef RLIMIT_RTPRIO
+ if (!drop) {
+ struct rlimit rl;
+ /* At this point we still have CAP_NICE if we were loaded
+ * SUID root. If possible let's acquire RLIMIT_RTPRIO
+ * instead and give CAP_NICE up. */
+
+ if (getrlimit(RLIMIT_RTPRIO, &rl) >= 0) {
+
+ if (rl.rlim_cur >= 9)
+ drop = TRUE;
+ else {
+ rl.rlim_max = rl.rlim_cur = 9;
+
+ if (setrlimit(RLIMIT_RTPRIO, &rl) >= 0) {
+ pa_log_info("Successfully increased RLIMIT_RTPRIO");
+ drop = TRUE;
+ } else
+ pa_log_warn("RLIMIT_RTPRIO failed: %s", pa_cstrerror(errno));
+ }
+ }
+ }
+#endif
+
+ if (drop) {
+ pa_log_info("Giving up CAP_NICE");
+ pa_drop_caps();
+ suid_root = FALSE;
+ }
+ }
+
+ if (conf->realtime_scheduling && !pa_can_realtime())
+ pa_log_warn("Real-time scheduling enabled in configuration but not allowed by policy.");
+
+ pa_log_debug("Can realtime: %s, can high-priority: %s", pa_yes_no(pa_can_realtime()), pa_yes_no(pa_can_high_priority()));
+
+ LTDL_SET_PRELOADED_SYMBOLS();
+ pa_ltdl_init();
+ ltdl_init = TRUE;
if (conf->dl_search_path)
lt_dlsetsearchpath(conf->dl_search_path);
+#ifdef OS_IS_WIN32
+ {
+ WSADATA data;
+ WSAStartup(MAKEWORD(2, 0), &data);
+ }
+#endif
+
+ pa_random_seed();
+
switch (conf->cmd) {
case PA_CMD_DUMP_MODULES:
pa_dump_modules(conf, argc-d, argv+d);
@@ -400,6 +567,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;
@@ -413,10 +590,10 @@ int main(int argc, char *argv[]) {
case PA_CMD_CHECK: {
pid_t pid;
- if (pa_pid_file_check_running(&pid) < 0) {
- pa_log_info("daemon not running");
- } else {
- pa_log_info("daemon running as PID %u", pid);
+ if (pa_pid_file_check_running(&pid, "pulseaudio") < 0)
+ pa_log_info("Daemon not running");
+ else {
+ pa_log_info("Daemon running as PID %u", pid);
retval = 0;
}
@@ -425,64 +602,92 @@ int main(int argc, char *argv[]) {
}
case PA_CMD_KILL:
- if (pa_pid_file_kill(SIGINT, NULL) < 0)
- pa_log("failed to kill daemon.");
+ if (pa_pid_file_kill(SIGINT, NULL, "pulseaudio") < 0)
+ pa_log("Failed to kill daemon.");
else
retval = 0;
-
+
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 || conf->cmd == PA_CMD_START);
}
- if (real_root && !conf->system_instance) {
+ if (real_root && !conf->system_instance)
pa_log_warn("This program is not intended to be run as root (unless --system is specified).");
- } else if (!real_root && conf->system_instance) {
+ else if (!real_root && conf->system_instance) {
pa_log("Root priviliges required.");
goto finish;
}
+ if (conf->cmd == PA_CMD_START) {
+ /* If we shall start PA only when it is not running yet, we
+ * first take the autospawn lock to make things
+ * synchronous. */
+
+ lf = pa_runtime_path(AUTOSPAWN_LOCK);
+ autospawn_lock_fd = pa_lock_lockfile(lf);
+ }
+
if (conf->daemonize) {
pid_t child;
int tty_fd;
if (pa_stdio_acquire() < 0) {
- pa_log("failed to acquire stdio.");
+ pa_log("Failed to acquire stdio.");
goto finish;
}
#ifdef HAVE_FORK
if (pipe(daemon_pipe) < 0) {
- pa_log("failed to create pipe.");
+ pa_log("pipe failed: %s", pa_cstrerror(errno));
goto finish;
}
-
+
if ((child = fork()) < 0) {
pa_log("fork() failed: %s", pa_cstrerror(errno));
goto finish;
}
if (child != 0) {
+ ssize_t n;
/* 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)) {
- pa_log("read() failed: %s", pa_cstrerror(errno));
+ if ((n = pa_loop_read(daemon_pipe[0], &retval, sizeof(retval), NULL)) != sizeof(retval)) {
+
+ if (n < 0)
+ pa_log("read() failed: %s", pa_cstrerror(errno));
+
retval = 1;
}
if (retval)
- pa_log("daemon startup failed.");
+ pa_log("Daemon startup failed.");
else
- pa_log_info("daemon startup successful.");
-
+ pa_log_info("Daemon startup successful.");
+
goto finish;
}
- close(daemon_pipe[0]);
+ if (autospawn_lock_fd >= 0) {
+ /* The lock file is unlocked from the parent, so we need
+ * to close it in the child */
+
+ pa_close(autospawn_lock_fd);
+ autospawn_lock_fd = -1;
+ }
+
+ pa_assert_se(pa_close(daemon_pipe[0]) == 0);
daemon_pipe[0] = -1;
#endif
@@ -497,13 +702,13 @@ 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);
- open("/dev/null", O_WRONLY);
+ pa_assert_se(open("/dev/null", O_RDONLY) == 0);
+ pa_assert_se(open("/dev/null", O_WRONLY) == 1);
+ pa_assert_se(open("/dev/null", O_WRONLY) == 2);
#else
FreeConsole();
#endif
@@ -517,60 +722,95 @@ int main(int argc, char *argv[]) {
#ifdef SIGTSTP
signal(SIGTSTP, SIG_IGN);
#endif
-
+
#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_set_env("PULSE_INTERNAL", "1");
+ pa_assert_se(chdir("/") == 0);
umask(0022);
-
- if (conf->system_instance) {
+
+ if (conf->system_instance)
if (change_user() < 0)
goto finish;
- } else if (create_runtime_dir() < 0)
+
+ pa_set_env("PULSE_SYSTEM", conf->system_instance ? "1" : "0");
+
+ pa_log_info("This is PulseAudio " PACKAGE_VERSION);
+ pa_log_info("Page size is %lu bytes", (unsigned long) PA_PAGE_SIZE);
+ if (!(s = pa_get_runtime_dir()))
goto finish;
-
+ pa_log_info("Using runtime directory %s.", s);
+ pa_xfree(s);
+ if (!(s = pa_get_state_dir()))
+ pa_log_info("Using state directory %s.", s);
+ pa_xfree(s);
+
+ pa_log_info("Running in system mode: %s", pa_yes_no(pa_in_system_mode()));
+
if (conf->use_pid_file) {
- if (pa_pid_file_create() < 0) {
+ int z;
+
+ if ((z = pa_pid_file_create("pulseaudio")) != 0) {
+
+ if (conf->cmd == PA_CMD_START && z > 0) {
+ /* If we are already running and with are run in
+ * --start mode, then let's return this as success. */
+
+ pa_log_info("z=%i rock!", z);
+
+ retval = 0;
+ goto finish;
+ }
+
pa_log("pa_pid_file_create() failed.");
-#ifdef HAVE_FORK
- if (conf->daemonize)
- pa_loop_write(daemon_pipe[1], &retval, sizeof(retval), NULL);
-#endif
goto finish;
}
- valid_pid_file = 1;
+ valid_pid_file = TRUE;
}
-#ifdef HAVE_SYS_RESOURCE_H
- set_all_rlimits(conf);
-#endif
-
#ifdef SIGPIPE
signal(SIGPIPE, SIG_IGN);
#endif
- mainloop = pa_mainloop_new();
- assert(mainloop);
+ 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->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->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;
+ c->realtime_priority = conf->realtime_priority;
+ c->realtime_scheduling = !!conf->realtime_scheduling;
+ c->disable_remixing = !!conf->disable_remixing;
+ c->running_as_daemon = !!conf->daemonize;
+
+ 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);
-
#ifdef SIGUSR1
pa_signal_new(SIGUSR1, signal_callback, c);
#endif
@@ -580,84 +820,97 @@ int main(int argc, char *argv[]) {
#ifdef SIGHUP
pa_signal_new(SIGHUP, signal_callback, c);
#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);
+ win32_timer = pa_mainloop_get_api(mainloop)->time_new(pa_mainloop_get_api(mainloop), pa_gettimeofday(&win32_tv), message_cb, NULL);
#endif
- if (conf->daemonize)
- c->running_as_daemon = 1;
-
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)
- r = pa_cli_command_execute_file(c, conf->default_script_file, buf, &conf->fail);
+ if (conf->load_default_script_file) {
+ FILE *f;
+
+ if ((f = pa_daemon_conf_open_default_script_file(conf))) {
+ r = pa_cli_command_execute_file_stream(c, f, buf, &conf->fail);
+ fclose(f);
+ }
+ }
if (r >= 0)
r = pa_cli_command_execute(c, conf->script_commands, buf, &conf->fail);
+
pa_log_error("%s", s = pa_strbuf_tostring_free(buf));
pa_xfree(s);
-
+
+ /* We completed the initial module loading, so let's disable it
+ * from now on, if requested */
+ c->disallow_module_loading = !!conf->disallow_module_loading;
+
if (r < 0 && conf->fail) {
- pa_log("failed to initialize daemon.");
-#ifdef HAVE_FORK
- if (conf->daemonize)
- pa_loop_write(daemon_pipe[1], &retval, sizeof(retval), NULL);
-#endif
- } else if (!c->modules || pa_idxset_size(c->modules) == 0) {
- pa_log("daemon startup without any loaded modules, refusing to work.");
-#ifdef HAVE_FORK
- if (conf->daemonize)
- pa_loop_write(daemon_pipe[1], &retval, sizeof(retval), NULL);
-#endif
- } else {
+ pa_log("Failed to initialize daemon.");
+ goto finish;
+ }
+
+ if (!c->modules || pa_idxset_size(c->modules) == 0) {
+ pa_log("Daemon startup without any loaded modules, refusing to work.");
+ goto finish;
+ }
+
+ if (c->default_sink_name && !pa_namereg_get(c, c->default_sink_name, PA_NAMEREG_SINK, TRUE) && conf->fail) {
+ pa_log_error("Default sink name (%s) does not exist in name register.", c->default_sink_name);
+ goto finish;
+ }
- retval = 0;
#ifdef HAVE_FORK
- if (conf->daemonize)
- 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);
- retval = 1;
- } else {
- pa_log_info("Daemon startup complete.");
- if (pa_mainloop_run(mainloop, &retval) < 0)
- retval = 1;
- pa_log_info("Daemon shutdown initiated.");
- }
+ if (daemon_pipe[1] >= 0) {
+ int ok = 0;
+ pa_loop_write(daemon_pipe[1], &ok, sizeof(ok), NULL);
+ pa_close(daemon_pipe[1]);
+ daemon_pipe[1] = -1;
}
+#endif
+
+ pa_log_info("Daemon startup complete.");
+
+ retval = 0;
+ if (pa_mainloop_run(mainloop, &retval) < 0)
+ goto finish;
+
+ pa_log_info("Daemon shutdown initiated.");
+
+finish:
+
+ if (autospawn_lock_fd >= 0)
+ pa_unlock_lockfile(lf, autospawn_lock_fd);
+
+ if (lf)
+ pa_xfree(lf);
#ifdef OS_IS_WIN32
- pa_mainloop_get_api(mainloop)->time_free(timer);
+ if (win32_timer)
+ pa_mainloop_get_api(mainloop)->time_free(win32_timer);
#endif
- pa_core_free(c);
+ if (c) {
+ pa_core_unref(c);
+ pa_log_info("Daemon terminated.");
+ }
if (!conf->no_cpu_limit)
pa_cpu_limit_done();
-
+
pa_signal_done();
-
- pa_log_info("Daemon terminated.");
-
-finish:
+
+#ifdef HAVE_FORK
+ if (daemon_pipe[1] >= 0)
+ pa_loop_write(daemon_pipe[1], &retval, sizeof(retval), NULL);
+
+ pa_close_pipe(daemon_pipe);
+#endif
if (mainloop)
pa_mainloop_free(mainloop);
@@ -667,14 +920,17 @@ finish:
if (valid_pid_file)
pa_pid_file_remove();
-
- close_pipe(daemon_pipe);
#ifdef OS_IS_WIN32
WSACleanup();
#endif
- lt_dlexit();
-
+ if (ltdl_init)
+ pa_ltdl_done();
+
+#ifdef HAVE_DBUS
+ dbus_shutdown();
+#endif
+
return retval;
}
diff --git a/src/daemon/org.pulseaudio.policy b/src/daemon/org.pulseaudio.policy
new file mode 100644
index 00000000..6cdeec68
--- /dev/null
+++ b/src/daemon/org.pulseaudio.policy
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?><!--*-nxml-*-->
+<!DOCTYPE policyconfig PUBLIC
+ "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd">
+
+<!--
+This file is part of PulseAudio.
+
+PulseAudio is free software; you can redistribute it and/or modify it
+under the terms of the GNU Lesser General Public License as
+published by the Free Software Foundation; either version 2.1 of the
+License, or (at your option) any later version.
+
+PulseAudio is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with PulseAudio; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+USA.
+-->
+
+<policyconfig>
+ <vendor>The PulseAudio Project</vendor>
+ <vendor_url>http://pulseaudio.org/</vendor_url>
+ <icon_name>audio-card</icon_name>
+
+ <action id="org.pulseaudio.acquire-real-time">
+ <description>Real-time scheduling for the PulseAudio daemon</description>
+ <message>System policy prevents PulseAudio from acquiring real-time scheduling.</message>
+ <defaults>
+ <allow_any>no</allow_any>
+ <allow_inactive>no</allow_inactive>
+ <allow_active>no</allow_active>
+ </defaults>
+ </action>
+
+ <action id="org.pulseaudio.acquire-high-priority">
+ <description>High-priority scheduling (negative Unix nice level) for the PulseAudio daemon</description>
+ <message>System policy prevents PulseAudio from acquiring high-priority scheduling.</message>
+ <defaults>
+ <allow_any>no</allow_any>
+ <allow_inactive>no</allow_inactive>
+ <allow_active>no</allow_active>
+ </defaults>
+ </action>
+
+</policyconfig>
diff --git a/src/daemon/polkit.c b/src/daemon/polkit.c
new file mode 100644
index 00000000..256e3199
--- /dev/null
+++ b/src/daemon/polkit.c
@@ -0,0 +1,165 @@
+/***
+ 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 <unistd.h>
+#include <stdlib.h>
+#include <inttypes.h>
+
+#include <dbus/dbus.h>
+#include <polkit-dbus/polkit-dbus.h>
+
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+
+#include "polkit.h"
+
+int pa_polkit_check(const char *action_id) {
+ int ret = -1;
+ DBusError dbus_error;
+ DBusConnection *bus = NULL;
+ PolKitCaller *caller = NULL;
+ PolKitAction *action = NULL;
+ PolKitContext *context = NULL;
+ PolKitError *polkit_error = NULL;
+ PolKitSession *session = NULL;
+ PolKitResult polkit_result;
+
+ dbus_error_init(&dbus_error);
+
+ if (!(bus = dbus_bus_get(DBUS_BUS_SYSTEM, &dbus_error))) {
+ pa_log_error("Cannot connect to system bus: %s", dbus_error.message);
+ goto finish;
+ }
+
+ if (!(caller = polkit_caller_new_from_pid(bus, getpid(), &dbus_error))) {
+ pa_log_error("Cannot get caller from PID: %s", dbus_error.message);
+ goto finish;
+ }
+
+ /* This function is called when PulseAudio is called SUID root. We
+ * want to authenticate the real user that called us and not the
+ * effective user we gained through being SUID root. Hence we
+ * overwrite the UID caller data here explicitly, just for
+ * paranoia. In fact PolicyKit should fill in the UID here anyway
+ * -- an not the EUID or any other user id. */
+
+ if (!(polkit_caller_set_uid(caller, getuid()))) {
+ pa_log_error("Cannot set UID on caller object.");
+ goto finish;
+ }
+
+ if (!(polkit_caller_get_ck_session(caller, &session))) {
+ pa_log_error("Failed to get CK session.");
+ goto finish;
+ }
+
+ /* We need to overwrite the UID in both the caller and the session
+ * object */
+
+ if (!(polkit_session_set_uid(session, getuid()))) {
+ pa_log_error("Cannot set UID on session object.");
+ goto finish;
+ }
+
+ if (!(action = polkit_action_new())) {
+ pa_log_error("Cannot allocate PolKitAction.");
+ goto finish;
+ }
+
+ if (!polkit_action_set_action_id(action, action_id)) {
+ pa_log_error("Cannot set action_id");
+ goto finish;
+ }
+
+ if (!(context = polkit_context_new())) {
+ pa_log_error("Cannot allocate PolKitContext.");
+ goto finish;
+ }
+
+ if (!polkit_context_init(context, &polkit_error)) {
+ pa_log_error("Cannot initialize PolKitContext: %s", polkit_error_get_error_message(polkit_error));
+ goto finish;
+ }
+
+ for (;;) {
+
+ polkit_result = polkit_context_is_caller_authorized(context, action, caller, TRUE, &polkit_error);
+
+ if (polkit_error_is_set(polkit_error)) {
+ pa_log_error("Could not determine whether caller is authorized: %s", polkit_error_get_error_message(polkit_error));
+ goto finish;
+ }
+
+ if (polkit_result == POLKIT_RESULT_ONLY_VIA_ADMIN_AUTH ||
+ polkit_result == POLKIT_RESULT_ONLY_VIA_ADMIN_AUTH_KEEP_SESSION ||
+ polkit_result == POLKIT_RESULT_ONLY_VIA_ADMIN_AUTH_KEEP_ALWAYS ||
+ polkit_result == POLKIT_RESULT_ONLY_VIA_ADMIN_AUTH_ONE_SHOT ||
+ polkit_result == POLKIT_RESULT_ONLY_VIA_SELF_AUTH ||
+ polkit_result == POLKIT_RESULT_ONLY_VIA_SELF_AUTH_KEEP_SESSION ||
+ polkit_result == POLKIT_RESULT_ONLY_VIA_SELF_AUTH_KEEP_ALWAYS ||
+ polkit_result == POLKIT_RESULT_ONLY_VIA_SELF_AUTH_ONE_SHOT
+ ) {
+
+ if (polkit_auth_obtain(action_id, 0, getpid(), &dbus_error)) {
+ polkit_result = POLKIT_RESULT_YES;
+ break;
+ }
+
+ if (dbus_error_is_set(&dbus_error)) {
+ pa_log_error("Cannot obtain auth: %s", dbus_error.message);
+ goto finish;
+ }
+ }
+
+ break;
+ }
+
+ if (polkit_result != POLKIT_RESULT_YES && polkit_result != POLKIT_RESULT_NO)
+ pa_log_warn("PolicyKit responded with '%s'", polkit_result_to_string_representation(polkit_result));
+
+ ret = polkit_result == POLKIT_RESULT_YES;
+
+finish:
+
+ if (caller)
+ polkit_caller_unref(caller);
+
+ if (action)
+ polkit_action_unref(action);
+
+ if (context)
+ polkit_context_unref(context);
+
+ if (bus)
+ dbus_connection_unref(bus);
+
+ dbus_error_free(&dbus_error);
+
+ if (polkit_error)
+ polkit_error_free(polkit_error);
+
+ return ret;
+}
diff --git a/src/pulsecore/core-def.h b/src/daemon/polkit.h
index 718499d1..0d65ec52 100644
--- a/src/pulsecore/core-def.h
+++ b/src/daemon/polkit.h
@@ -1,30 +1,27 @@
-#ifndef foocoredefhfoo
-#define foocoredefhfoo
-
-/* $Id$ */
+#ifndef foopolkithfoo
+#define foopolkithfoo
/***
This file is part of PulseAudio.
-
+
+ Copyright 2007 Lennart Poettering
+
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 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.
***/
-typedef enum pa_mixer {
- PA_MIXER_SOFTWARE,
- PA_MIXER_HARDWARE
-} pa_mixer_t;
+int pa_polkit_check(const char *action);
#endif
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/depmod.py b/src/depmod.py
index a20bc7c0..6cb3cb21 100755
--- a/src/depmod.py
+++ b/src/depmod.py
@@ -1,6 +1,5 @@
#!/usr/bin/python
-# $Id$
-#
+
# This file is part of PulseAudio.
#
# PulseAudio is free software; you can redistribute it and/or modify
diff --git a/src/map-file b/src/map-file
new file mode 100644
index 00000000..8d1c582e
--- /dev/null
+++ b/src/map-file
@@ -0,0 +1,256 @@
+PULSE_0 {
+global:
+pa_browser_new;
+pa_browser_new_full;
+pa_browser_ref;
+pa_browser_set_callback;
+pa_browser_set_error_callback;
+pa_browser_unref;
+pa_bytes_per_second;
+pa_bytes_snprint;
+pa_bytes_to_usec;
+pa_channel_map_equal;
+pa_channel_map_init;
+pa_channel_map_init_auto;
+pa_channel_map_init_extend;
+pa_channel_map_init_mono;
+pa_channel_map_init_stereo;
+pa_channel_map_parse;
+pa_channel_map_snprint;
+pa_channel_map_valid;
+pa_channel_position_to_pretty_string;
+pa_channel_position_to_string;
+pa_context_add_autoload;
+pa_context_connect;
+pa_context_disconnect;
+pa_context_drain;
+pa_context_errno;
+pa_context_exit_daemon;
+pa_context_get_autoload_info_by_index;
+pa_context_get_autoload_info_by_name;
+pa_context_get_autoload_info_list;
+pa_context_get_client_info;
+pa_context_get_client_info_list;
+pa_context_get_index;
+pa_context_get_module_info;
+pa_context_get_module_info_list;
+pa_context_get_protocol_version;
+pa_context_get_sample_info_by_index;
+pa_context_get_sample_info_by_name;
+pa_context_get_sample_info_list;
+pa_context_get_server;
+pa_context_get_server_info;
+pa_context_get_server_protocol_version;
+pa_context_get_sink_info_by_index;
+pa_context_get_sink_info_by_name;
+pa_context_get_sink_info_list;
+pa_context_get_sink_input_info;
+pa_context_get_sink_input_info_list;
+pa_context_get_source_info_by_index;
+pa_context_get_source_info_by_name;
+pa_context_get_source_info_list;
+pa_context_get_source_output_info;
+pa_context_get_source_output_info_list;
+pa_context_get_state;
+pa_context_is_local;
+pa_context_is_pending;
+pa_context_kill_client;
+pa_context_kill_sink_input;
+pa_context_kill_source_output;
+pa_context_load_module;
+pa_context_move_sink_input_by_index;
+pa_context_move_sink_input_by_name;
+pa_context_move_source_output_by_index;
+pa_context_move_source_output_by_name;
+pa_context_new;
+pa_context_new_with_proplist;
+pa_context_play_sample;
+pa_context_play_sample_with_proplist;
+pa_context_proplist_remove;
+pa_context_proplist_update;
+pa_context_ref;
+pa_context_remove_autoload_by_index;
+pa_context_remove_autoload_by_name;
+pa_context_remove_sample;
+pa_context_set_default_sink;
+pa_context_set_default_source;
+pa_context_set_name;
+pa_context_set_sink_input_mute;
+pa_context_set_sink_input_volume;
+pa_context_set_sink_mute_by_index;
+pa_context_set_sink_mute_by_name;
+pa_context_set_sink_volume_by_index;
+pa_context_set_sink_volume_by_name;
+pa_context_set_source_mute_by_index;
+pa_context_set_source_mute_by_name;
+pa_context_set_source_volume_by_index;
+pa_context_set_source_volume_by_name;
+pa_context_set_state_callback;
+pa_context_set_subscribe_callback;
+pa_context_stat;
+pa_context_subscribe;
+pa_context_suspend_sink_by_index;
+pa_context_suspend_sink_by_name;
+pa_context_suspend_source_by_index;
+pa_context_suspend_source_by_name;
+pa_context_unload_module;
+pa_context_unref;
+pa_cvolume_avg;
+pa_cvolume_channels_equal_to;
+pa_cvolume_equal;
+pa_cvolume_set;
+pa_cvolume_snprint;
+pa_cvolume_valid;
+pa_frame_size;
+pa_get_binary_name;
+pa_get_fqdn;
+pa_get_home_dir;
+pa_get_host_name;
+pa_get_library_version;
+pa_gettimeofday;
+pa_get_user_name;
+pa_glib_mainloop_free;
+pa_glib_mainloop_get_api;
+pa_glib_mainloop_new;
+pa_locale_to_utf8;
+pa_mainloop_api_once;
+pa_mainloop_dispatch;
+pa_mainloop_free;
+pa_mainloop_get_api;
+pa_mainloop_get_retval;
+pa_mainloop_iterate;
+pa_mainloop_new;
+pa_mainloop_poll;
+pa_mainloop_prepare;
+pa_mainloop_quit;
+pa_mainloop_run;
+pa_mainloop_set_poll_func;
+pa_mainloop_wakeup;
+pa_msleep;
+pa_operation_cancel;
+pa_operation_get_state;
+pa_operation_ref;
+pa_operation_unref;
+pa_parse_sample_format;
+pa_path_get_filename;
+pa_proplist_free;
+pa_proplist_contains;
+pa_proplist_clear;
+pa_proplist_copy;
+pa_proplist_get;
+pa_proplist_gets;
+pa_proplist_iterate;
+pa_proplist_update;
+pa_proplist_new;
+pa_proplist_set;
+pa_proplist_sets;
+pa_proplist_setf;
+pa_proplist_unset;
+pa_proplist_unset_many;
+pa_proplist_to_string;
+pa_sample_format_to_string;
+pa_sample_size;
+pa_sample_spec_equal;
+pa_sample_spec_snprint;
+pa_sample_spec_valid;
+pa_signal_done;
+pa_signal_free;
+pa_signal_init;
+pa_signal_new;
+pa_signal_set_destroy;
+pa_simple_drain;
+pa_simple_flush;
+pa_simple_free;
+pa_simple_get_latency;
+pa_simple_new;
+pa_simple_read;
+pa_simple_write;
+pa_stream_connect_playback;
+pa_stream_connect_record;
+pa_stream_connect_upload;
+pa_stream_cork;
+pa_stream_disconnect;
+pa_stream_drain;
+pa_stream_drop;
+pa_stream_finish_upload;
+pa_stream_flush;
+pa_stream_get_buffer_attr;
+pa_stream_get_channel_map;
+pa_stream_get_context;
+pa_stream_get_device_index;
+pa_stream_get_device_name;
+pa_stream_get_index;
+pa_stream_get_latency;
+pa_stream_get_monitor_stream;
+pa_stream_get_sample_spec;
+pa_stream_get_state;
+pa_stream_get_time;
+pa_stream_get_timing_info;
+pa_stream_is_corked;
+pa_stream_is_suspended;
+pa_stream_new;
+pa_stream_new_with_proplist;
+pa_stream_peek;
+pa_stream_prebuf;
+pa_stream_proplist_remove;
+pa_stream_proplist_update;
+pa_stream_readable_size;
+pa_stream_ref;
+pa_stream_set_buffer_attr;
+pa_stream_set_latency_update_callback;
+pa_stream_set_moved_callback;
+pa_stream_set_monitor_stream;
+pa_stream_set_name;
+pa_stream_set_overflow_callback;
+pa_stream_set_read_callback;
+pa_stream_set_started_callback;
+pa_stream_set_state_callback;
+pa_stream_set_suspended_callback;
+pa_stream_set_underflow_callback;
+pa_stream_set_write_callback;
+pa_stream_trigger;
+pa_stream_unref;
+pa_stream_update_sample_rate;
+pa_stream_update_timing_info;
+pa_stream_writable_size;
+pa_stream_write;
+pa_strerror;
+pa_sw_cvolume_multiply;
+pa_sw_volume_from_dB;
+pa_sw_volume_from_linear;
+pa_sw_volume_multiply;
+pa_sw_volume_to_dB;
+pa_sw_volume_to_linear;
+pa_threaded_mainloop_accept;
+pa_threaded_mainloop_free;
+pa_threaded_mainloop_get_api;
+pa_threaded_mainloop_get_retval;
+pa_threaded_mainloop_in_thread;
+pa_threaded_mainloop_lock;
+pa_threaded_mainloop_new;
+pa_threaded_mainloop_signal;
+pa_threaded_mainloop_start;
+pa_threaded_mainloop_stop;
+pa_threaded_mainloop_unlock;
+pa_threaded_mainloop_wait;
+pa_timeval_add;
+pa_timeval_age;
+pa_timeval_cmp;
+pa_timeval_diff;
+pa_timeval_load;
+pa_timeval_store;
+pa_timeval_sub;
+pa_usec_to_bytes;
+pa_utf8_filter;
+pa_utf8_to_locale;
+pa_utf8_valid;
+pa_xfree;
+pa_xmalloc;
+pa_xmalloc0;
+pa_xmemdup;
+pa_xrealloc;
+pa_xstrdup;
+pa_xstrndup;
+local:
+*;
+};
diff --git a/src/modules/.gitignore b/src/modules/.gitignore
new file mode 100644
index 00000000..2d2d942d
--- /dev/null
+++ b/src/modules/.gitignore
@@ -0,0 +1 @@
+module-*-symdef.h
diff --git a/src/modules/alsa-util.c b/src/modules/alsa-util.c
index d8b6c5cc..5d52cbc9 100644
--- a/src/modules/alsa-util.c
+++ b/src/modules/alsa-util.c
@@ -1,18 +1,19 @@
-/* $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
@@ -24,12 +25,16 @@
#endif
#include <sys/types.h>
+#include <limits.h>
#include <asoundlib.h>
#include <pulse/sample.h>
#include <pulse/xmalloc.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/atomic.h>
#include "alsa-util.h"
@@ -39,7 +44,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;
@@ -53,11 +57,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;
@@ -66,7 +75,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;
@@ -80,63 +89,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;
}
@@ -146,18 +138,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;
@@ -165,47 +157,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);
@@ -219,29 +205,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;
}
@@ -256,23 +228,27 @@ static int set_format(snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hwparams, pa_s
[PA_SAMPLE_S16BE] = SND_PCM_FORMAT_S16_BE,
[PA_SAMPLE_FLOAT32LE] = SND_PCM_FORMAT_FLOAT_LE,
[PA_SAMPLE_FLOAT32BE] = SND_PCM_FORMAT_FLOAT_BE,
+ [PA_SAMPLE_S32LE] = SND_PCM_FORMAT_S32_LE,
+ [PA_SAMPLE_S32BE] = SND_PCM_FORMAT_S32_BE,
};
static const pa_sample_format_t try_order[] = {
- PA_SAMPLE_S16NE,
- PA_SAMPLE_S16RE,
PA_SAMPLE_FLOAT32NE,
PA_SAMPLE_FLOAT32RE,
- PA_SAMPLE_ULAW,
+ PA_SAMPLE_S32NE,
+ PA_SAMPLE_S32RE,
+ PA_SAMPLE_S16NE,
+ PA_SAMPLE_S16RE,
PA_SAMPLE_ALAW,
+ PA_SAMPLE_ULAW,
PA_SAMPLE_U8,
PA_SAMPLE_INVALID
};
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;
@@ -285,17 +261,21 @@ static int set_format(snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hwparams, pa_s
*f = PA_SAMPLE_S16LE;
else if (*f == PA_SAMPLE_S16LE)
*f = PA_SAMPLE_S16BE;
+ else if (*f == PA_SAMPLE_S32BE)
+ *f = PA_SAMPLE_S32LE;
+ else if (*f == PA_SAMPLE_S32LE)
+ *f = PA_SAMPLE_S32BE;
else
goto try_auto;
if ((ret = snd_pcm_hw_params_set_format(pcm_handle, hwparams, format_trans[*f])) >= 0)
return ret;
-
+
try_auto:
for (i = 0; try_order[i] != PA_SAMPLE_INVALID; i++) {
*f = try_order[i];
-
+
if ((ret = snd_pcm_hw_params_set_format(pcm_handle, hwparams, format_trans[*f])) >= 0)
return ret;
}
@@ -305,89 +285,404 @@ 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,
+ snd_pcm_uframes_t tsched_size,
+ pa_bool_t *use_mmap,
+ pa_bool_t *use_tsched,
+ pa_bool_t require_exact_channel_number) {
+
int ret = -1;
+ snd_pcm_uframes_t _period_size = *period_size;
+ unsigned int _periods = *periods;
snd_pcm_uframes_t buffer_size;
unsigned int r = ss->rate;
unsigned int c = ss->channels;
pa_sample_format_t f = ss->format;
snd_pcm_hw_params_t *hwparams;
-
- assert(pcm_handle);
- assert(ss);
- assert(periods);
- assert(period_size);
-
- 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)
- goto finish;
+ pa_bool_t _use_mmap = use_mmap && *use_mmap;
+ pa_bool_t _use_tsched = use_tsched && *use_tsched;
+ int dir;
- if ((ret = set_format(pcm_handle, hwparams, &f)) < 0)
+ pa_assert(pcm_handle);
+ pa_assert(ss);
+ pa_assert(periods);
+ pa_assert(period_size);
+
+ snd_pcm_hw_params_alloca(&hwparams);
+
+ if ((ret = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0)
goto finish;
- if ((ret = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &r, NULL)) < 0)
+ if ((ret = snd_pcm_hw_params_set_rate_resample(pcm_handle, hwparams, 0)) < 0)
goto finish;
- if ((ret = snd_pcm_hw_params_set_channels_near(pcm_handle, hwparams, &c)) < 0)
+ if (_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;
+
+ _use_mmap = FALSE;
+ }
+
+ } else if ((ret = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
goto finish;
- if ((*period_size > 0 && (ret = snd_pcm_hw_params_set_period_size_near(pcm_handle, hwparams, period_size, NULL)) < 0) ||
- (*periods > 0 && (ret = snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, &buffer_size)) < 0))
+ if (!_use_mmap)
+ _use_tsched = FALSE;
+
+ if ((ret = set_format(pcm_handle, hwparams, &f)) < 0)
goto finish;
- if ((ret = snd_pcm_hw_params(pcm_handle, hwparams)) < 0)
+ if ((ret = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &r, NULL)) < 0)
goto finish;
- if (ss->rate != r) {
- pa_log_warn("device doesn't support %u Hz, changed to %u Hz.", ss->rate, r);
+ /* Adjust the buffer sizes, if we didn't get the rate we were asking for */
+ _period_size = (snd_pcm_uframes_t) (((uint64_t) _period_size * r) / ss->rate);
+ tsched_size = (snd_pcm_uframes_t) (((uint64_t) tsched_size * r) / ss->rate);
- /* If the sample rate deviates too much, we need to resample */
- if (r < ss->rate*.95 || r > ss->rate*1.05)
- ss->rate = r;
+ if (require_exact_channel_number) {
+ if ((ret = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, c)) < 0)
+ goto finish;
+ } else {
+ if ((ret = snd_pcm_hw_params_set_channels_near(pcm_handle, hwparams, &c)) < 0)
+ goto finish;
}
- if (ss->channels != c) {
- pa_log_warn("device doesn't support %u channels, changed to %u.", ss->channels, c);
- ss->channels = c;
+ if (_use_tsched) {
+ _period_size = tsched_size;
+ _periods = 1;
+
+ pa_assert_se(snd_pcm_hw_params_get_buffer_size_max(hwparams, &buffer_size) == 0);
+ pa_log_debug("Maximum hw buffer size is %u ms", (unsigned) buffer_size * 1000 / r);
}
- 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));
- ss->format = f;
+ buffer_size = _periods * _period_size;
+
+ if ((ret = snd_pcm_hw_params_set_periods_integer(pcm_handle, hwparams)) < 0)
+ goto finish;
+
+ if (_periods > 0) {
+ dir = 1;
+ if ((ret = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &_periods, &dir)) < 0) {
+ dir = -1;
+ if ((ret = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &_periods, &dir)) < 0)
+ goto finish;
+ }
}
-
+
+ if (_period_size > 0)
+ if ((ret = snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, &buffer_size)) < 0)
+ goto finish;
+
+ if ((ret = snd_pcm_hw_params(pcm_handle, hwparams)) < 0)
+ goto finish;
+
+ if (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 (ss->channels != c)
+ pa_log_warn("Device %s doesn't support %u channels, changed to %u.", snd_pcm_name(pcm_handle), ss->channels, c);
+
+ if (ss->format != 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));
+
if ((ret = snd_pcm_prepare(pcm_handle)) < 0)
goto finish;
- if ((ret = snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size)) < 0 ||
- (ret = snd_pcm_hw_params_get_period_size(hwparams, period_size, NULL)) < 0)
+ if ((ret = snd_pcm_hw_params_get_period_size(hwparams, &_period_size, &dir)) < 0 ||
+ (ret = snd_pcm_hw_params_get_periods(hwparams, &_periods, &dir)) < 0)
goto finish;
-
- assert(buffer_size > 0);
- assert(*period_size > 0);
- *periods = buffer_size / *period_size;
- assert(*periods > 0);
-
+
+ /* If the sample rate deviates too much, we need to resample */
+ if (r < ss->rate*.95 || r > ss->rate*1.05)
+ ss->rate = r;
+ ss->channels = c;
+ ss->format = f;
+
+ pa_assert(_periods > 0);
+ pa_assert(_period_size > 0);
+
+ *periods = _periods;
+ *period_size = _period_size;
+
+ if (use_mmap)
+ *use_mmap = _use_mmap;
+
+ if (use_tsched)
+ *use_tsched = _use_tsched;
+
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_uframes_t avail_min) {
+ 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_set_avail_min(pcm, swparams, avail_min)) < 0) {
+ pa_log_error("snd_pcm_sw_params_set_avail_min() failed: %s", 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;
+}
+
+struct device_info {
+ pa_channel_map map;
+ const char *name;
+};
+
+static const struct device_info device_table[] = {
+ {{ 2, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT } }, "front" },
+
+ {{ 4, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+ PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT }}, "surround40" },
+
+ {{ 5, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+ PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
+ PA_CHANNEL_POSITION_LFE }}, "surround41" },
+
+ {{ 5, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+ PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
+ PA_CHANNEL_POSITION_CENTER }}, "surround50" },
+
+ {{ 6, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+ PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
+ PA_CHANNEL_POSITION_CENTER, PA_CHANNEL_POSITION_LFE }}, "surround51" },
+
+ {{ 8, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+ PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
+ PA_CHANNEL_POSITION_CENTER, PA_CHANNEL_POSITION_LFE,
+ PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT }} , "surround71" },
+
+ {{ 0, { 0 }}, NULL }
+};
+
+static pa_bool_t channel_map_superset(const pa_channel_map *a, const pa_channel_map *b) {
+ pa_bool_t in_a[PA_CHANNEL_POSITION_MAX];
+ unsigned i;
+
+ pa_assert(a);
+ pa_assert(b);
+
+ memset(in_a, 0, sizeof(in_a));
+
+ for (i = 0; i < a->channels; i++)
+ in_a[a->map[i]] = TRUE;
+
+ for (i = 0; i < b->channels; i++)
+ if (!in_a[b->map[i]])
+ return FALSE;
+
+ return TRUE;
+}
+
+snd_pcm_t *pa_alsa_open_by_device_id(
+ const char *dev_id,
+ char **dev,
+ pa_sample_spec *ss,
+ pa_channel_map* map,
+ int mode,
+ uint32_t *nfrags,
+ snd_pcm_uframes_t *period_size,
+ snd_pcm_uframes_t tsched_size,
+ pa_bool_t *use_mmap,
+ pa_bool_t *use_tsched) {
+
+ int i;
+ int direction = 1;
+ int err;
+ char *d;
+ snd_pcm_t *pcm_handle;
+
+ pa_assert(dev_id);
+ pa_assert(dev);
+ pa_assert(ss);
+ pa_assert(map);
+ pa_assert(nfrags);
+ pa_assert(period_size);
+
+ /* First we try to find a device string with a superset of the
+ * requested channel map and open it without the plug: prefix. We
+ * iterate through our device table from top to bottom and take
+ * the first that matches. If we didn't find a working device that
+ * way, we iterate backwards, and check all devices that do not
+ * provide a superset of the requested channel map.*/
+
+ for (i = 0;; i += direction) {
+ pa_sample_spec try_ss;
+
+ if (i < 0) {
+ pa_assert(direction == -1);
+
+ /* OK, so we iterated backwards, and now are at the
+ * beginning of our list. */
+
+ break;
+
+ } else if (!device_table[i].name) {
+ pa_assert(direction == 1);
+
+ /* OK, so we are at the end of our list. at iterated
+ * forwards. */
+
+ i--;
+ direction = -1;
+ }
+
+ if ((direction > 0) == !channel_map_superset(&device_table[i].map, map))
+ continue;
+
+ d = pa_sprintf_malloc("%s:%s", device_table[i].name, dev_id);
+ pa_log_debug("Trying %s...", d);
+
+ if ((err = snd_pcm_open(&pcm_handle, d, mode,
+ SND_PCM_NONBLOCK|
+ SND_PCM_NO_AUTO_RESAMPLE|
+ SND_PCM_NO_AUTO_CHANNELS|
+ SND_PCM_NO_AUTO_FORMAT)) < 0) {
+ pa_log_info("Couldn't open PCM device %s: %s", d, snd_strerror(err));
+ pa_xfree(d);
+ continue;
+ }
+
+ try_ss.channels = device_table[i].map.channels;
+ try_ss.rate = ss->rate;
+ try_ss.format = ss->format;
+
+ if ((err = pa_alsa_set_hw_params(pcm_handle, &try_ss, nfrags, period_size, tsched_size, use_mmap, use_tsched, TRUE)) < 0) {
+ pa_log_info("PCM device %s refused our hw parameters: %s", d, snd_strerror(err));
+ pa_xfree(d);
+ snd_pcm_close(pcm_handle);
+ continue;
+ }
+
+ *ss = try_ss;
+ *map = device_table[i].map;
+ pa_assert(map->channels == ss->channels);
+ *dev = d;
+ return pcm_handle;
+ }
+
+ /* OK, we didn't find any good device, so let's try the raw plughw: stuff */
+
+ d = pa_sprintf_malloc("plughw:%s", dev_id);
+ pa_log_debug("Trying %s as last resort...", d);
+ pcm_handle = pa_alsa_open_by_device_string(d, dev, ss, map, mode, nfrags, period_size, tsched_size, use_mmap, use_tsched);
+ pa_xfree(d);
+
+ return pcm_handle;
+}
+
+snd_pcm_t *pa_alsa_open_by_device_string(
+ const char *device,
+ char **dev,
+ pa_sample_spec *ss,
+ pa_channel_map* map,
+ int mode,
+ uint32_t *nfrags,
+ snd_pcm_uframes_t *period_size,
+ snd_pcm_uframes_t tsched_size,
+ pa_bool_t *use_mmap,
+ pa_bool_t *use_tsched) {
+
+ int err;
+ char *d;
+ snd_pcm_t *pcm_handle;
+
+ pa_assert(device);
+ pa_assert(dev);
+ pa_assert(ss);
+ pa_assert(map);
+ pa_assert(nfrags);
+ pa_assert(period_size);
+
+ d = pa_xstrdup(device);
+
+ for (;;) {
+
+ if ((err = snd_pcm_open(&pcm_handle, d, mode, SND_PCM_NONBLOCK|
+ SND_PCM_NO_AUTO_RESAMPLE|
+ SND_PCM_NO_AUTO_CHANNELS|
+ SND_PCM_NO_AUTO_FORMAT)) < 0) {
+ pa_log("Error opening PCM device %s: %s", d, snd_strerror(err));
+ pa_xfree(d);
+ return NULL;
+ }
+
+ if ((err = pa_alsa_set_hw_params(pcm_handle, ss, nfrags, period_size, tsched_size, use_mmap, use_tsched, FALSE)) < 0) {
+
+ if (err == -EPERM) {
+ /* Hmm, some hw is very exotic, so we retry with plug, if without it didn't work */
+
+ if (pa_startswith(d, "hw:")) {
+ char *t = pa_sprintf_malloc("plughw:%s", d+3);
+ pa_log_debug("Opening the device as '%s' didn't work, retrying with '%s'.", d, t);
+ pa_xfree(d);
+ d = t;
+
+ snd_pcm_close(pcm_handle);
+ continue;
+ }
+
+ pa_log("Failed to set hardware parameters on %s: %s", d, snd_strerror(err));
+ pa_xfree(d);
+ snd_pcm_close(pcm_handle);
+ return NULL;
+ }
+ }
+
+ *dev = d;
+
+ if (ss->channels != map->channels)
+ pa_channel_map_init_extend(map, ss->channels, PA_CHANNEL_MAP_ALSA);
+
+ return pcm_handle;
+ }
+}
+
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));
+ pa_log_info("Unable to attach to mixer %s: %s", dev, snd_strerror(err));
return -1;
}
@@ -401,25 +696,28 @@ int pa_alsa_prepare_mixer(snd_mixer_t *mixer, const char *dev) {
return -1;
}
+ pa_log_info("Successfully attached to mixer '%s'", dev);
+
return 0;
}
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);
if (!(elem = snd_mixer_find_selem(mixer, sid))) {
- pa_log_warn("Cannot find mixer control \"%s\".", snd_mixer_selem_id_get_name(sid));
+ pa_log_info("Cannot find mixer control \"%s\".", snd_mixer_selem_id_get_name(sid));
if (fallback) {
snd_mixer_selem_id_set_name(sid, fallback);
-
+
if (!(elem = snd_mixer_find_selem(mixer, sid)))
pa_log_warn("Cannot find fallback mixer control \"%s\".", snd_mixer_selem_id_get_name(sid));
}
@@ -430,3 +728,391 @@ snd_mixer_elem_t *pa_alsa_find_elem(snd_mixer_t *mixer, const char *name, const
return elem;
}
+
+static const snd_mixer_selem_channel_id_t alsa_channel_ids[PA_CHANNEL_POSITION_MAX] = {
+ [PA_CHANNEL_POSITION_MONO] = SND_MIXER_SCHN_MONO, /* The ALSA name is just an alias! */
+
+ [PA_CHANNEL_POSITION_FRONT_CENTER] = SND_MIXER_SCHN_FRONT_CENTER,
+ [PA_CHANNEL_POSITION_FRONT_LEFT] = SND_MIXER_SCHN_FRONT_LEFT,
+ [PA_CHANNEL_POSITION_FRONT_RIGHT] = SND_MIXER_SCHN_FRONT_RIGHT,
+
+ [PA_CHANNEL_POSITION_REAR_CENTER] = SND_MIXER_SCHN_REAR_CENTER,
+ [PA_CHANNEL_POSITION_REAR_LEFT] = SND_MIXER_SCHN_REAR_LEFT,
+ [PA_CHANNEL_POSITION_REAR_RIGHT] = SND_MIXER_SCHN_REAR_RIGHT,
+
+ [PA_CHANNEL_POSITION_LFE] = SND_MIXER_SCHN_WOOFER,
+
+ [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = SND_MIXER_SCHN_UNKNOWN,
+
+ [PA_CHANNEL_POSITION_SIDE_LEFT] = SND_MIXER_SCHN_SIDE_LEFT,
+ [PA_CHANNEL_POSITION_SIDE_RIGHT] = SND_MIXER_SCHN_SIDE_RIGHT,
+
+ [PA_CHANNEL_POSITION_AUX0] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX1] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX2] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX3] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX4] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX5] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX6] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX7] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX8] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX9] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX10] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX11] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX12] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX13] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX14] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX15] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX16] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX17] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX18] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX19] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX20] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX21] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX22] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX23] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX24] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX25] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX26] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX27] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX28] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX29] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX30] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_AUX31] = SND_MIXER_SCHN_UNKNOWN,
+
+ [PA_CHANNEL_POSITION_TOP_CENTER] = SND_MIXER_SCHN_UNKNOWN,
+
+ [PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = SND_MIXER_SCHN_UNKNOWN,
+
+ [PA_CHANNEL_POSITION_TOP_REAR_CENTER] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_TOP_REAR_LEFT] = SND_MIXER_SCHN_UNKNOWN,
+ [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = SND_MIXER_SCHN_UNKNOWN
+};
+
+
+int pa_alsa_calc_mixer_map(snd_mixer_elem_t *elem, const pa_channel_map *channel_map, snd_mixer_selem_channel_id_t mixer_map[], pa_bool_t playback) {
+ unsigned i;
+ pa_bool_t alsa_channel_used[SND_MIXER_SCHN_LAST];
+ pa_bool_t mono_used = FALSE;
+
+ pa_assert(elem);
+ pa_assert(channel_map);
+ pa_assert(mixer_map);
+
+ memset(&alsa_channel_used, 0, sizeof(alsa_channel_used));
+
+ if (channel_map->channels > 1 &&
+ ((playback && snd_mixer_selem_has_playback_volume_joined(elem)) ||
+ (!playback && snd_mixer_selem_has_capture_volume_joined(elem)))) {
+ pa_log_info("ALSA device lacks independant volume controls for each channel, falling back to software volume control.");
+ return -1;
+ }
+
+ for (i = 0; i < channel_map->channels; i++) {
+ snd_mixer_selem_channel_id_t id;
+ pa_bool_t is_mono;
+
+ is_mono = channel_map->map[i] == PA_CHANNEL_POSITION_MONO;
+ id = alsa_channel_ids[channel_map->map[i]];
+
+ if (!is_mono && id == SND_MIXER_SCHN_UNKNOWN) {
+ pa_log_info("Configured channel map contains channel '%s' that is unknown to the ALSA mixer. Falling back to software volume control.", pa_channel_position_to_string(channel_map->map[i]));
+ return -1;
+ }
+
+ if ((is_mono && mono_used) || (!is_mono && alsa_channel_used[id])) {
+ pa_log_info("Channel map has duplicate channel '%s', falling back to software volume control.", pa_channel_position_to_string(channel_map->map[i]));
+ return -1;
+ }
+
+ if ((playback && (!snd_mixer_selem_has_playback_channel(elem, id) || (is_mono && !snd_mixer_selem_is_playback_mono(elem)))) ||
+ (!playback && (!snd_mixer_selem_has_capture_channel(elem, id) || (is_mono && !snd_mixer_selem_is_capture_mono(elem))))) {
+
+ pa_log_info("ALSA device lacks separate volumes control for channel '%s', falling back to software volume control.", pa_channel_position_to_string(channel_map->map[i]));
+ return -1;
+ }
+
+ if (is_mono) {
+ mixer_map[i] = SND_MIXER_SCHN_MONO;
+ mono_used = TRUE;
+ } else {
+ mixer_map[i] = id;
+ alsa_channel_used[id] = TRUE;
+ }
+ }
+
+ pa_log_info("All %u channels can be mapped to mixer channels.", channel_map->channels);
+
+ return 0;
+}
+
+void pa_alsa_0dB_playback(snd_mixer_elem_t *elem) {
+ long min, max, v;
+
+ pa_assert(elem);
+
+ /* Try to enable 0 dB if possible. If ALSA cannot do dB, then use
+ * raw volume levels and fix them to 75% */
+
+ if (snd_mixer_selem_set_playback_dB_all(elem, 0, -1) >= 0)
+ return;
+
+ if (snd_mixer_selem_set_playback_dB_all(elem, 0, 1) >= 0)
+ return;
+
+ if (snd_mixer_selem_get_playback_volume_range(elem, &min, &max) < 0)
+ return;
+
+ v = min + ((max - min) * 3) / 4; /* 75% */
+
+ if (v <= min)
+ v = max;
+
+ snd_mixer_selem_set_playback_volume_all(elem, v);
+}
+
+void pa_alsa_0dB_capture(snd_mixer_elem_t *elem) {
+ long min, max, v;
+
+ pa_assert(elem);
+
+ /* Try to enable 0 dB if possible. If ALSA cannot do dB, then use
+ * raw volume levels and fix them to 75% */
+
+ if (snd_mixer_selem_set_capture_dB_all(elem, 0, -1) >= 0)
+ return;
+
+ if (snd_mixer_selem_set_capture_dB_all(elem, 0, 1) >= 0)
+ return;
+
+ if (snd_mixer_selem_get_capture_volume_range(elem, &min, &max) < 0)
+ return;
+
+ v = min + ((max - min) * 3) / 4; /* 75% */
+
+ if (v <= min)
+ v = max;
+
+ snd_mixer_selem_set_capture_volume_all(elem, v);
+}
+
+void pa_alsa_dump(snd_pcm_t *pcm) {
+ int err;
+ snd_output_t *out;
+
+ pa_assert(pcm);
+
+ pa_assert_se(snd_output_buffer_open(&out) == 0);
+
+ if ((err = snd_pcm_dump(pcm, out)) < 0)
+ pa_log_debug("snd_pcm_dump(): %s", snd_strerror(err));
+ else {
+ char *s = NULL;
+ snd_output_buffer_string(out, &s);
+ pa_log_debug("snd_pcm_dump():\n%s", pa_strnull(s));
+ }
+
+ pa_assert_se(snd_output_close(out) == 0);
+}
+
+void pa_alsa_dump_status(snd_pcm_t *pcm) {
+ int err;
+ snd_output_t *out;
+ snd_pcm_status_t *status;
+
+ pa_assert(pcm);
+
+ snd_pcm_status_alloca(&status);
+
+ pa_assert_se(snd_output_buffer_open(&out) == 0);
+
+ pa_assert_se(snd_pcm_status(pcm, status) == 0);
+
+ if ((err = snd_pcm_status_dump(status, out)) < 0)
+ pa_log_debug("snd_pcm_dump(): %s", snd_strerror(err));
+ else {
+ char *s = NULL;
+ snd_output_buffer_string(out, &s);
+ pa_log_debug("snd_pcm_dump():\n%s", pa_strnull(s));
+ }
+
+ pa_assert_se(snd_output_close(out) == 0);
+}
+
+static void alsa_error_handler(const char *file, int line, const char *function, int err, const char *fmt,...) {
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ pa_log_levelv_meta(PA_LOG_WARN, file, line, function, fmt, ap);
+
+ va_end(ap);
+}
+
+static pa_atomic_t n_error_handler_installed = PA_ATOMIC_INIT(0);
+
+void pa_alsa_redirect_errors_inc(void) {
+ /* This is not really thread safe, but we do our best */
+
+ if (pa_atomic_inc(&n_error_handler_installed) == 0)
+ snd_lib_error_set_handler(alsa_error_handler);
+}
+
+void pa_alsa_redirect_errors_dec(void) {
+ int r;
+
+ pa_assert_se((r = pa_atomic_dec(&n_error_handler_installed)) >= 1);
+
+ if (r == 1)
+ snd_lib_error_set_handler(NULL);
+}
+
+void pa_alsa_init_proplist(pa_proplist *p, snd_pcm_info_t *pcm_info) {
+
+ static const char * const alsa_class_table[SND_PCM_CLASS_LAST+1] = {
+ [SND_PCM_CLASS_GENERIC] = "generic",
+ [SND_PCM_CLASS_MULTI] = "multi",
+ [SND_PCM_CLASS_MODEM] = "modem",
+ [SND_PCM_CLASS_DIGITIZER] = "digitizer"
+ };
+ static const char * const class_table[SND_PCM_CLASS_LAST+1] = {
+ [SND_PCM_CLASS_GENERIC] = "sound",
+ [SND_PCM_CLASS_MULTI] = NULL,
+ [SND_PCM_CLASS_MODEM] = "modem",
+ [SND_PCM_CLASS_DIGITIZER] = NULL
+ };
+ static const char * const alsa_subclass_table[SND_PCM_SUBCLASS_LAST+1] = {
+ [SND_PCM_SUBCLASS_GENERIC_MIX] = "generic-mix",
+ [SND_PCM_SUBCLASS_MULTI_MIX] = "multi-mix"
+ };
+
+ snd_pcm_class_t class;
+ snd_pcm_subclass_t subclass;
+ const char *n, *id, *sdn;
+ char *cn = NULL, *lcn = NULL;
+ int card;
+
+ pa_assert(p);
+ pa_assert(pcm_info);
+
+ pa_proplist_sets(p, PA_PROP_DEVICE_API, "alsa");
+
+ class = snd_pcm_info_get_class(pcm_info);
+ if (class <= SND_PCM_CLASS_LAST) {
+ if (class_table[class])
+ pa_proplist_sets(p, PA_PROP_DEVICE_CLASS, class_table[class]);
+ if (alsa_class_table[class])
+ pa_proplist_sets(p, "alsa.class", alsa_class_table[class]);
+ }
+ subclass = snd_pcm_info_get_subclass(pcm_info);
+ if (subclass <= SND_PCM_SUBCLASS_LAST)
+ if (alsa_subclass_table[subclass])
+ pa_proplist_sets(p, "alsa.subclass", alsa_subclass_table[subclass]);
+
+ if ((n = snd_pcm_info_get_name(pcm_info)))
+ pa_proplist_sets(p, "alsa.name", n);
+
+ if ((id = snd_pcm_info_get_id(pcm_info)))
+ pa_proplist_sets(p, "alsa.id", id);
+
+ pa_proplist_setf(p, "alsa.subdevice", "%u", snd_pcm_info_get_subdevice(pcm_info));
+ if ((sdn = snd_pcm_info_get_subdevice_name(pcm_info)))
+ pa_proplist_sets(p, "alsa.subdevice_name", sdn);
+
+ pa_proplist_setf(p, "alsa.device", "%u", snd_pcm_info_get_device(pcm_info));
+
+ if ((card = snd_pcm_info_get_card(pcm_info)) >= 0) {
+ pa_proplist_setf(p, "alsa.card", "%i", card);
+
+ if (snd_card_get_name(card, &cn) >= 0)
+ pa_proplist_sets(p, "alsa.card_name", cn);
+
+ if (snd_card_get_longname(card, &lcn) >= 0)
+ pa_proplist_sets(p, "alsa.long_card_name", lcn);
+ }
+
+ if (cn && n)
+ pa_proplist_setf(p, PA_PROP_DEVICE_DESCRIPTION, "%s - %s", cn, n);
+ else if (cn)
+ pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, cn);
+ else if (n)
+ pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, n);
+
+ free(lcn);
+ free(cn);
+}
+
+int pa_alsa_recover_from_poll(snd_pcm_t *pcm, int revents) {
+ snd_pcm_state_t state;
+ int err;
+
+ pa_assert(pcm);
+
+ 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");
+
+ state = snd_pcm_state(pcm);
+ pa_log_warn("PCM state is %s", snd_pcm_state_name(state));
+
+ /* Try to recover from this error */
+
+ switch (state) {
+
+ case SND_PCM_STATE_XRUN:
+ if ((err = snd_pcm_recover(pcm, -EPIPE, 1)) != 0) {
+ pa_log_warn("Could not recover from POLLERR|POLLNVAL|POLLHUP and XRUN: %s", snd_strerror(err));
+ return -1;
+ }
+ break;
+
+ case SND_PCM_STATE_SUSPENDED:
+ if ((err = snd_pcm_recover(pcm, -ESTRPIPE, 1)) != 0) {
+ pa_log_warn("Could not recover from POLLERR|POLLNVAL|POLLHUP and SUSPENDED: %s", snd_strerror(err));
+ return -1;
+ }
+ break;
+
+ default:
+
+ snd_pcm_drop(pcm);
+
+ if ((err = snd_pcm_prepare(pcm)) < 0) {
+ pa_log_warn("Could not recover from POLLERR|POLLNVAL|POLLHUP with snd_pcm_prepare(): %s", snd_strerror(err));
+ return -1;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+pa_rtpoll_item* pa_alsa_build_pollfd(snd_pcm_t *pcm, pa_rtpoll *rtpoll) {
+ int n, err;
+ struct pollfd *pollfd;
+ pa_rtpoll_item *item;
+
+ pa_assert(pcm);
+
+ if ((n = snd_pcm_poll_descriptors_count(pcm)) < 0) {
+ pa_log("snd_pcm_poll_descriptors_count() failed: %s", snd_strerror(n));
+ return NULL;
+ }
+
+ item = pa_rtpoll_item_new(rtpoll, PA_RTPOLL_NEVER, n);
+ pollfd = pa_rtpoll_item_get_pollfd(item, NULL);
+
+ if ((err = snd_pcm_poll_descriptors(pcm, pollfd, n)) < 0) {
+ pa_log("snd_pcm_poll_descriptors() failed: %s", snd_strerror(err));
+ pa_rtpoll_item_free(item);
+ return NULL;
+ }
+
+ return item;
+}
diff --git a/src/modules/alsa-util.h b/src/modules/alsa-util.h
index 215844b4..4de8bcd2 100644
--- a/src/modules/alsa-util.h
+++ b/src/modules/alsa-util.h
@@ -1,21 +1,22 @@
#ifndef fooalsautilhfoo
#define fooalsautilhfoo
-/* $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
@@ -26,20 +27,71 @@
#include <pulse/sample.h>
#include <pulse/mainloop-api.h>
-
#include <pulse/channelmap.h>
+#include <pulse/proplist.h>
-struct pa_alsa_fdlist;
+#include <pulsecore/rtpoll.h>
+
+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,
+ snd_pcm_uframes_t tsched_size,
+ pa_bool_t *use_mmap,
+ pa_bool_t *use_tsched,
+ pa_bool_t require_exact_channel_number);
-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_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min);
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_pcm_t *pa_alsa_open_by_device_id(
+ const char *dev_id,
+ char **dev,
+ pa_sample_spec *ss,
+ pa_channel_map* map,
+ int mode,
+ uint32_t *nfrags,
+ snd_pcm_uframes_t *period_size,
+ snd_pcm_uframes_t tsched_size,
+ pa_bool_t *use_mmap,
+ pa_bool_t *use_tsched);
+
+snd_pcm_t *pa_alsa_open_by_device_string(
+ const char *device,
+ char **dev,
+ pa_sample_spec *ss,
+ pa_channel_map* map,
+ int mode,
+ uint32_t *nfrags,
+ snd_pcm_uframes_t *period_size,
+ snd_pcm_uframes_t tsched_size,
+ pa_bool_t *use_mmap,
+ pa_bool_t *use_tsched);
+
+int pa_alsa_calc_mixer_map(snd_mixer_elem_t *elem, const pa_channel_map *channel_map, snd_mixer_selem_channel_id_t mixer_map[], pa_bool_t playback);
+
+void pa_alsa_0dB_playback(snd_mixer_elem_t *elem);
+void pa_alsa_0dB_capture(snd_mixer_elem_t *elem);
+
+void pa_alsa_dump(snd_pcm_t *pcm);
+void pa_alsa_dump_status(snd_pcm_t *pcm);
+
+void pa_alsa_redirect_errors_inc(void);
+void pa_alsa_redirect_errors_dec(void);
+
+void pa_alsa_init_proplist(pa_proplist *p, snd_pcm_info_t *pcm_info);
+
+int pa_alsa_recover_from_poll(snd_pcm_t *pcm, int revents);
+
+pa_rtpoll_item* pa_alsa_build_pollfd(snd_pcm_t *pcm, pa_rtpoll *rtpoll);
+
#endif
diff --git a/src/modules/bt-proximity-helper.c b/src/modules/bt-proximity-helper.c
new file mode 100644
index 00000000..3767f01c
--- /dev/null
+++ b/src/modules/bt-proximity-helper.c
@@ -0,0 +1,202 @@
+/*
+ * Small SUID helper that allows us to ping a BT device. Borrows
+ * heavily from bluez-utils' l2ping, which is licensed as GPL2+
+ * and comes with a copyright like this:
+ *
+ * Copyright (C) 2000-2001 Qualcomm Incorporated
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2007 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#undef NDEBUG
+
+#include <assert.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/select.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/l2cap.h>
+
+#define PING_STRING "PulseAudio"
+#define IDENT 200
+#define TIMEOUT 4
+#define INTERVAL 2
+
+static void update_status(int found) {
+ static int status = -1;
+
+ if (!found && status != 0)
+ printf("-");
+ if (found && status <= 0)
+ printf("+");
+
+ fflush(stdout);
+ status = !!found;
+}
+
+int main(int argc, char *argv[]) {
+ struct sockaddr_l2 addr;
+ union {
+ l2cap_cmd_hdr hdr;
+ uint8_t buf[L2CAP_CMD_HDR_SIZE + sizeof(PING_STRING)];
+ } packet;
+ int fd = -1;
+ uint8_t id = IDENT;
+ int connected = 0;
+
+ assert(argc == 2);
+
+ for (;;) {
+ fd_set fds;
+ struct timeval end;
+ ssize_t r;
+
+ if (!connected) {
+
+ if (fd >= 0)
+ close(fd);
+
+ if ((fd = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP)) < 0) {
+ fprintf(stderr, "socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP) failed: %s", strerror(errno));
+ goto finish;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.l2_family = AF_BLUETOOTH;
+ bacpy(&addr.l2_bdaddr, BDADDR_ANY);
+
+ if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ fprintf(stderr, "bind() failed: %s", strerror(errno));
+ goto finish;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.l2_family = AF_BLUETOOTH;
+ str2ba(argv[1], &addr.l2_bdaddr);
+
+ if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+
+ if (errno == EHOSTDOWN || errno == ECONNRESET || errno == ETIMEDOUT) {
+ update_status(0);
+ sleep(INTERVAL);
+ continue;
+ }
+
+ fprintf(stderr, "connect() failed: %s", strerror(errno));
+ goto finish;
+ }
+
+ connected = 1;
+ }
+
+ assert(connected);
+
+ memset(&packet, 0, sizeof(packet));
+ strcpy((char*) packet.buf + L2CAP_CMD_HDR_SIZE, PING_STRING);
+ packet.hdr.ident = id;
+ packet.hdr.len = htobs(sizeof(PING_STRING));
+ packet.hdr.code = L2CAP_ECHO_REQ;
+
+ if ((r = send(fd, &packet, sizeof(packet), 0)) < 0) {
+
+ if (errno == EHOSTDOWN || errno == ECONNRESET || errno == ETIMEDOUT) {
+ update_status(0);
+ connected = 0;
+ sleep(INTERVAL);
+ continue;
+ }
+
+ fprintf(stderr, "send() failed: %s", strerror(errno));
+ goto finish;
+ }
+
+ assert(r == sizeof(packet));
+
+ gettimeofday(&end, NULL);
+ end.tv_sec += TIMEOUT;
+
+ for (;;) {
+ struct timeval now, delta;
+
+ gettimeofday(&now, NULL);
+
+ if (timercmp(&end, &now, <=)) {
+ update_status(0);
+ connected = 0;
+ sleep(INTERVAL);
+ break;
+ }
+
+ timersub(&end, &now, &delta);
+
+ FD_ZERO(&fds);
+ FD_SET(fd, &fds);
+
+ if (select(fd+1, &fds, NULL, NULL, &delta) < 0) {
+ fprintf(stderr, "select() failed: %s", strerror(errno));
+ goto finish;
+ }
+
+ if ((r = recv(fd, &packet, sizeof(packet), 0)) <= 0) {
+
+ if (errno == EHOSTDOWN || errno == ECONNRESET || errno == ETIMEDOUT) {
+ update_status(0);
+ connected = 0;
+ sleep(INTERVAL);
+ break;
+ }
+
+ fprintf(stderr, "send() failed: %s", r == 0 ? "EOF" : strerror(errno));
+ goto finish;
+ }
+
+ assert(r >= L2CAP_CMD_HDR_SIZE);
+
+ if (packet.hdr.ident != id)
+ continue;
+
+ if (packet.hdr.code == L2CAP_ECHO_RSP || packet.hdr.code == L2CAP_COMMAND_REJ) {
+
+ if (++id >= 0xFF)
+ id = IDENT;
+
+ update_status(1);
+ sleep(INTERVAL);
+ break;
+ }
+ }
+ }
+
+finish:
+
+ if (fd >= 0)
+ close(fd);
+
+ return 1;
+}
diff --git a/src/modules/dbus-util.c b/src/modules/dbus-util.c
index 165ccff6..905be13f 100644
--- a/src/modules/dbus-util.c
+++ b/src/modules/dbus-util.c
@@ -1,18 +1,19 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
+ 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 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
@@ -23,25 +24,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);
@@ -49,14 +50,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:
@@ -65,11 +69,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))
@@ -80,21 +86,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;
}
@@ -111,10 +118,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;
@@ -127,218 +132,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);
+
+ 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, (void*) ev, c->mainloop->io_free); */
- dbus_watch_set_data(watch, (void*) ev, NULL);
+ 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/dbus-util.h b/src/modules/dbus-util.h
index 7a9871a4..2b24ac63 100644
--- a/src/modules/dbus-util.h
+++ b/src/modules/dbus-util.h
@@ -1,21 +1,21 @@
#ifndef foodbusutilhfoo
#define foodbusutilhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
+ 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 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
diff --git a/src/modules/gconf/gconf-helper.c b/src/modules/gconf/gconf-helper.c
index 40724f4e..f5016faf 100644
--- a/src/modules/gconf/gconf-helper.c
+++ b/src/modules/gconf/gconf-helper.c
@@ -1,18 +1,18 @@
-/* $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
@@ -30,6 +30,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"
@@ -38,38 +40,38 @@ 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);
if (enabled) {
-
+
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);
-
+
g_free(n);
g_free(a);
}
-
+
printf("%c", 0);
}
-
+
fflush(stdout);
}
@@ -81,7 +83,7 @@ static void modules_callback(
const char *n;
char buf[128];
-
+
g_assert(strncmp(entry->key, PA_GCONF_PATH_MODULES"/", sizeof(PA_GCONF_PATH_MODULES)) == 0);
n = entry->key + sizeof(PA_GCONF_PATH_MODULES);
@@ -111,17 +113,17 @@ int main(int argc, char *argv[]) {
char *e = strrchr(m->data, '/');
handle_module(client, e ? e+1 : m->data);
}
-
+
g_slist_free(modules);
/* Signal the parent that we are now initialized */
printf("!");
fflush(stdout);
-
+
g = g_main_loop_new(NULL, FALSE);
g_main_loop_run(g);
g_main_loop_unref(g);
-
+
g_object_unref(G_OBJECT(client));
return 0;
diff --git a/src/modules/gconf/module-gconf.c b/src/modules/gconf/module-gconf.c
index d9f649fd..a2a43278 100644
--- a/src/modules/gconf/module-gconf.c
+++ b/src/modules/gconf/module-gconf.c
@@ -1,18 +1,18 @@
-/* $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
@@ -23,7 +23,6 @@
#include <config.h>
#endif
-#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
@@ -33,28 +32,22 @@
#include <sys/wait.h>
#include <fcntl.h>
-#ifdef HAVE_SYS_PRCTL_H
-#include <sys/prctl.h>
-#endif
-#ifdef HAVE_SYS_RESOURCE_H
-#include <sys/resource.h>
-#endif
-
+#include <pulse/xmalloc.h>
#include <pulsecore/module.h>
#include <pulsecore/core.h>
#include <pulsecore/llist.h>
#include <pulsecore/core-util.h>
#include <pulsecore/log.h>
#include <pulse/mainloop-api.h>
-#include <pulse/xmalloc.h>
#include <pulsecore/core-error.h>
+#include <pulsecore/start-child.h>
#include "module-gconf-symdef.h"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("GConf Adapter")
-PA_MODULE_VERSION(PACKAGE_VERSION)
-PA_MODULE_USAGE("")
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("GConf Adapter");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
#define MAX_MODULES 10
#define BUF_MAX 2048
@@ -78,7 +71,7 @@ struct module_info {
struct userdata {
pa_core *core;
pa_module *module;
-
+
pa_hashmap *module_infos;
pid_t pid;
@@ -93,7 +86,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");
@@ -109,25 +102,25 @@ 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;
-
+
if ((e = memchr(u->buf, 0, u->buf_fill))) {
char *ret = pa_xstrdup(u->buf);
u->buf_fill -= e - u->buf +1;
@@ -141,13 +134,13 @@ 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;
-
+
pa_log_debug("Unloading module #%i", m->items[i].index);
pa_module_unload_by_index(u->core, m->items[i].index);
m->items[i].index = PA_INVALID_INDEX;
@@ -158,9 +151,9 @@ 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);
@@ -177,11 +170,11 @@ static void load_module(
int is_new) {
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 &&
@@ -191,18 +184,18 @@ static void load_module(
unload_one_module(u, m, i);
}
-
+
pa_log_debug("Loading module '%s' with args '%s' due to GConf configuration.", name, args);
m->items[i].name = pa_xstrdup(name);
m->items[i].args = pa_xstrdup(args);
m->items[i].index = PA_INVALID_INDEX;
-
+
if (!(mod = pa_module_load(u->core, name, args))) {
pa_log("pa_module_load() failed");
return;
}
-
+
m->items[i].index = mod->index;
}
@@ -210,8 +203,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);
@@ -223,20 +216,23 @@ static int handle_event(struct userdata *u) {
int ret = 0;
do {
- if ((opcode = read_byte(u)) < 0)
+ if ((opcode = read_byte(u)) < 0){
+ if (errno == EINTR || errno == EAGAIN)
+ break;
goto fail;
-
+ }
+
switch (opcode) {
case '!':
/* The helper tool is now initialized */
ret = 1;
break;
-
+
case '+': {
char *name;
struct module_info *m;
unsigned i, j;
-
+
if (!(name = read_string(u)))
goto fail;
@@ -280,16 +276,16 @@ static int handle_event(struct userdata *u) {
/* Unload all removed modules */
for (j = i; j < m->n_items; j++)
unload_one_module(u, m, j);
-
+
m->n_items = i;
-
+
break;
}
-
+
case '-': {
char *name;
struct module_info *m;
-
+
if (!(name = read_string(u)))
goto fail;
@@ -299,7 +295,7 @@ static int handle_event(struct userdata *u) {
}
pa_xfree(name);
-
+
break;
}
}
@@ -322,101 +318,22 @@ static void io_event_cb(
struct userdata *u = userdata;
if (handle_event(u) < 0) {
-
+
if (u->io_event) {
u->core->mainloop->io_free(u->io_event);
u->io_event = NULL;
}
-
- pa_module_unload_request(u->module);
- }
-}
-
-static int start_client(const char *n, pid_t *pid) {
- pid_t child;
- int pipe_fds[2] = { -1, -1 };
-
- if (pipe(pipe_fds) < 0) {
- pa_log("pipe() failed: %s", pa_cstrerror(errno));
- goto fail;
- }
-
- if ((child = fork()) == (pid_t) -1) {
- pa_log("fork() failed: %s", pa_cstrerror(errno));
- goto fail;
- } else if (child != 0) {
-
- /* Parent */
- close(pipe_fds[1]);
-
- if (pid)
- *pid = child;
-
- return pipe_fds[0];
- } else {
- int max_fd, i;
-
- /* child */
-
- close(pipe_fds[0]);
- dup2(pipe_fds[1], 1);
-
- if (pipe_fds[1] != 1)
- close(pipe_fds[1]);
-
- close(0);
- open("/dev/null", O_RDONLY);
-
- close(2);
- open("/dev/null", O_WRONLY);
-
- max_fd = 1024;
-
-#ifdef HAVE_SYS_RESOURCE_H
- {
- struct rlimit r;
- if (getrlimit(RLIMIT_NOFILE, &r) == 0)
- max_fd = r.rlim_max;
- }
-#endif
-
- for (i = 3; i < max_fd; i++)
- close(i);
-#ifdef PR_SET_PDEATHSIG
- /* On Linux we can use PR_SET_PDEATHSIG to have the helper
- process killed when the daemon dies abnormally. On non-Linux
- machines the client will die as soon as it writes data to
- stdout again (SIGPIPE) */
-
- prctl(PR_SET_PDEATHSIG, SIGTERM, 0, 0, 0);
-#endif
-
-#ifdef SIGPIPE
- /* Make sure that SIGPIPE kills the child process */
- signal(SIGPIPE, SIG_DFL);
-#endif
-
- execl(n, n, NULL);
- _exit(1);
+ pa_module_unload_request(u->module);
}
-
-fail:
- if (pipe_fds[0] >= 0)
- close(pipe_fds[0]);
-
- if (pipe_fds[1] >= 0)
- close(pipe_fds[1]);
-
- 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);
@@ -425,17 +342,17 @@ int pa__init(pa_core *c, pa_module*m) {
u->fd_type = 0;
u->io_event = NULL;
u->buf_fill = 0;
-
- if ((u->fd = start_client(PA_GCONF_HELPER, &u->pid)) < 0)
+
+ if ((u->fd = pa_start_child_for_read(PA_GCONF_HELPER, NULL, &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,
u);
-
+
do {
if ((r = handle_event(u)) < 0)
goto fail;
@@ -443,37 +360,36 @@ int pa__init(pa_core *c, pa_module*m) {
/* Read until the client signalled us that it is ready with
* initialization */
} while (r != 1);
-
+
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);
-
- if (u->fd >= 0)
- close(u->fd);
-
if (u->pid != (pid_t) -1) {
kill(u->pid, SIGTERM);
waitpid(u->pid, NULL, 0);
}
+ if (u->io_event)
+ m->core->mainloop->io_free(u->io_event);
+
+ if (u->fd >= 0)
+ pa_close(u->fd);
+
+
if (u->module_infos)
pa_hashmap_free(u->module_infos, module_info_free, u);
pa_xfree(u);
}
-
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 7bbd7de2..6765775a 100644
--- a/src/modules/module-alsa-sink.c
+++ b/src/modules/module-alsa-sink.c
@@ -1,18 +1,19 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
+ Copyright 2004-2008 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
@@ -23,18 +24,13 @@
#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 <pulse/timeval.h>
#include <pulsecore/core.h>
#include <pulsecore/module.h>
@@ -44,514 +40,1479 @@
#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 <pulsecore/rtclock.h>
+#include <pulsecore/time-smoother.h>
#include "alsa-util.h"
#include "module-alsa-sink-symdef.h"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("ALSA Sink")
-PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("ALSA Sink");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
PA_MODULE_USAGE(
"sink_name=<name for the sink> "
"device=<ALSA device> "
+ "device_id=<ALSA card index> "
"format=<sample format> "
- "channels=<number of channels> "
"rate=<sample rate> "
+ "channels=<number of channels> "
+ "channel_map=<channel map> "
"fragments=<number of fragments> "
"fragment_size=<fragment size> "
- "channel_map=<channel map>")
-
-struct userdata {
- snd_pcm_t *pcm_handle;
- 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;
-};
+ "mmap=<enable memory mapping?> "
+ "tsched=<enable system timer based scheduling mode?> "
+ "tsched_buffer_size=<buffer size when using timer based scheduling> "
+ "tsched_buffer_watermark=<lower fill watermark> "
+ "mixer_reset=<reset hw volume and mute settings to sane defaults when falling back to software?>");
static const char* const valid_modargs[] = {
- "device",
"sink_name",
+ "device",
+ "device_id",
"format",
- "channels",
"rate",
+ "channels",
+ "channel_map",
"fragments",
"fragment_size",
- "channel_map",
+ "mmap",
+ "tsched",
+ "tsched_buffer_size",
+ "tsched_buffer_watermark",
+ "mixer_reset",
NULL
};
#define DEFAULT_DEVICE "default"
+#define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC) /* 2s */
+#define DEFAULT_TSCHED_WATERMARK_USEC (20*PA_USEC_PER_MSEC) /* 20ms */
+#define TSCHED_MIN_SLEEP_USEC (3*PA_USEC_PER_MSEC) /* 3ms */
+#define TSCHED_MIN_WAKEUP_USEC (3*PA_USEC_PER_MSEC) /* 3ms */
-static void update_usage(struct userdata *u) {
- pa_module_set_used(u->module, u->sink ? pa_sink_used_by(u->sink) : 0);
+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;
+ long hw_volume_max, hw_volume_min;
+ long hw_dB_max, hw_dB_min;
+ pa_bool_t hw_dB_supported;
+
+ size_t frame_size, fragment_size, hwbuf_size, tsched_watermark;
+ unsigned nfragments;
+ pa_memchunk memchunk;
+
+ char *device_name;
+
+ pa_bool_t use_mmap, use_tsched;
+
+ pa_bool_t first, after_rewind;
+
+ pa_rtpoll_item *alsa_rtpoll_item;
+
+ snd_mixer_selem_channel_id_t mixer_map[SND_MIXER_SCHN_LAST];
+
+ pa_smoother *smoother;
+ int64_t frame_index;
+ uint64_t since_start;
+
+ snd_pcm_sframes_t hwbuf_unused_frames;
+};
+
+static void fix_tsched_watermark(struct userdata *u) {
+ size_t max_use;
+ size_t min_sleep, min_wakeup;
+ pa_assert(u);
+
+ max_use = u->hwbuf_size - u->hwbuf_unused_frames * u->frame_size;
+
+ min_sleep = pa_usec_to_bytes(TSCHED_MIN_SLEEP_USEC, &u->sink->sample_spec);
+ min_wakeup = pa_usec_to_bytes(TSCHED_MIN_WAKEUP_USEC, &u->sink->sample_spec);
+
+ if (min_sleep > max_use/2)
+ min_sleep = pa_frame_align(max_use/2, &u->sink->sample_spec);
+ if (min_sleep < u->frame_size)
+ min_sleep = u->frame_size;
+
+ if (min_wakeup > max_use/2)
+ min_wakeup = pa_frame_align(max_use/2, &u->sink->sample_spec);
+ if (min_wakeup < u->frame_size)
+ min_wakeup = u->frame_size;
+
+ if (u->tsched_watermark > max_use-min_sleep)
+ u->tsched_watermark = max_use-min_sleep;
+
+ if (u->tsched_watermark < min_wakeup)
+ u->tsched_watermark = min_wakeup;
}
-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->pcm_fdl)
- pa_alsa_fdlist_free(u->pcm_fdl);
- if (u->mixer_fdl)
- pa_alsa_fdlist_free(u->mixer_fdl);
+static void hw_sleep_time(struct userdata *u, pa_usec_t *sleep_usec, pa_usec_t*process_usec) {
+ pa_usec_t usec, wm;
- u->pcm_fdl = u->mixer_fdl = NULL;
+ pa_assert(sleep_usec);
+ pa_assert(process_usec);
- if (u->mixer_handle) {
- snd_mixer_close(u->mixer_handle);
- u->mixer_handle = NULL;
- }
-
- if (u->pcm_handle) {
- snd_pcm_drop(u->pcm_handle);
- snd_pcm_close(u->pcm_handle);
- u->pcm_handle = NULL;
+ pa_assert(u);
+
+ usec = pa_sink_get_requested_latency_within_thread(u->sink);
+
+ if (usec == (pa_usec_t) -1)
+ usec = pa_bytes_to_usec(u->hwbuf_size, &u->sink->sample_spec);
+
+/* pa_log_debug("hw buffer time: %u ms", (unsigned) (usec / PA_USEC_PER_MSEC)); */
+
+ wm = pa_bytes_to_usec(u->tsched_watermark, &u->sink->sample_spec);
+
+ if (usec >= wm) {
+ *sleep_usec = usec - wm;
+ *process_usec = wm;
+ } else
+ *process_usec = *sleep_usec = usec / 2;
+
+/* pa_log_debug("after watermark: %u ms", (unsigned) (*sleep_usec / PA_USEC_PER_MSEC)); */
+}
+
+static int try_recover(struct userdata *u, const char *call, int err) {
+ pa_assert(u);
+ pa_assert(call);
+ pa_assert(err < 0);
+
+ pa_log_debug("%s: %s", call, snd_strerror(err));
+
+ pa_assert(err != -EAGAIN);
+
+ if (err == -EPIPE)
+ pa_log_debug("%s: Buffer underrun!", call);
+
+ if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) == 0) {
+ u->first = TRUE;
+ u->since_start = 0;
+ return 0;
}
+
+ pa_log("%s: %s", call, snd_strerror(err));
+ return -1;
}
-static int xrun_recovery(struct userdata *u) {
- int ret;
- assert(u);
+static size_t check_left_to_play(struct userdata *u, snd_pcm_sframes_t n) {
+ size_t left_to_play;
- pa_log_info("*** ALSA-XRUN (playback) ***");
-
- if ((ret = snd_pcm_prepare(u->pcm_handle)) < 0) {
- pa_log("snd_pcm_prepare() failed: %s", snd_strerror(-ret));
+ if (n*u->frame_size < u->hwbuf_size)
+ left_to_play = u->hwbuf_size - (n*u->frame_size);
+ else
+ left_to_play = 0;
- clear_up(u);
- pa_module_unload_request(u->module);
- return -1;
+ if (left_to_play > 0) {
+/* pa_log_debug("%0.2f ms left to play", (double) pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) / PA_USEC_PER_MSEC); */
+ } else if (!u->first && !u->after_rewind) {
+ pa_log_info("Underrun!");
+
+ if (u->use_tsched) {
+ size_t old_watermark = u->tsched_watermark;
+
+ u->tsched_watermark *= 2;
+ fix_tsched_watermark(u);
+
+ if (old_watermark != u->tsched_watermark)
+ pa_log_notice("Increasing wakeup watermark to %0.2f ms",
+ (double) pa_bytes_to_usec(u->tsched_watermark, &u->sink->sample_spec) / PA_USEC_PER_MSEC);
+ }
}
- return ret;
+ return left_to_play;
}
-static void do_write(struct userdata *u) {
- assert(u);
+static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec) {
+ int work_done = 0;
+ pa_usec_t max_sleep_usec, process_usec;
+ size_t left_to_play;
+
+ pa_assert(u);
+ pa_sink_assert_ref(u->sink);
+
+ if (u->use_tsched)
+ hw_sleep_time(u, &max_sleep_usec, &process_usec);
- update_usage(u);
-
for (;;) {
- void *p;
- 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;
- }
-
- assert(memchunk->memblock);
- assert(memchunk->length);
- assert((memchunk->length % u->frame_size) == 0);
-
- p = pa_memblock_acquire(memchunk->memblock);
-
- if ((frames = snd_pcm_writei(u->pcm_handle, (uint8_t*) p + memchunk->index, memchunk->length / u->frame_size)) < 0) {
- pa_memblock_release(memchunk->memblock);
-
- if (frames == -EAGAIN)
- return;
-
- if (frames == -EPIPE) {
- if (xrun_recovery(u) < 0)
- return;
-
+ snd_pcm_sframes_t n;
+ int r;
+
+ snd_pcm_hwsync(u->pcm_handle);
+
+ /* First we determine how many samples are missing to fill the
+ * buffer up to 100% */
+
+ if (PA_UNLIKELY((n = snd_pcm_avail_update(u->pcm_handle)) < 0)) {
+
+ if ((r = try_recover(u, "snd_pcm_avail_update", n)) == 0)
continue;
+
+ return r;
+ }
+
+ left_to_play = check_left_to_play(u, n);
+
+ if (u->use_tsched)
+
+ /* We won't fill up the playback buffer before at least
+ * half the sleep time is over because otherwise we might
+ * ask for more data from the clients then they expect. We
+ * need to guarantee that clients only have to keep around
+ * a single hw buffer length. */
+
+ if (pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) > process_usec+max_sleep_usec/2)
+ break;
+
+ if (PA_UNLIKELY(n <= u->hwbuf_unused_frames))
+ break;
+
+ n -= u->hwbuf_unused_frames;
+
+/* pa_log_debug("Filling up"); */
+
+ for (;;) {
+ pa_memchunk chunk;
+ void *p;
+ int err;
+ const snd_pcm_channel_area_t *areas;
+ snd_pcm_uframes_t offset, frames = (snd_pcm_uframes_t) n;
+
+/* pa_log_debug("%lu frames to write", (unsigned long) frames); */
+
+ if (PA_UNLIKELY((err = snd_pcm_mmap_begin(u->pcm_handle, &areas, &offset, &frames)) < 0)) {
+
+ if ((r = try_recover(u, "snd_pcm_mmap_begin", err)) == 0)
+ continue;
+
+ return r;
+ }
+
+ /* Make sure that if these memblocks need to be copied they will fit into one slot */
+ if (frames > pa_mempool_block_size_max(u->sink->core->mempool)/u->frame_size)
+ frames = pa_mempool_block_size_max(u->sink->core->mempool)/u->frame_size;
+
+ /* Check these are multiples of 8 bit */
+ pa_assert((areas[0].first & 7) == 0);
+ pa_assert((areas[0].step & 7)== 0);
+
+ /* We assume a single interleaved memory buffer */
+ pa_assert((areas[0].first >> 3) == 0);
+ pa_assert((areas[0].step >> 3) == u->frame_size);
+
+ p = (uint8_t*) areas[0].addr + (offset * u->frame_size);
+
+ chunk.memblock = pa_memblock_new_fixed(u->core->mempool, p, frames * u->frame_size, TRUE);
+ 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 (PA_UNLIKELY((err = snd_pcm_mmap_commit(u->pcm_handle, offset, frames)) < 0)) {
+
+ if ((r = try_recover(u, "snd_pcm_mmap_commit", err)) == 0)
+ continue;
+
+ return r;
}
- pa_log("snd_pcm_writei() failed: %s", snd_strerror(-frames));
+ work_done = 1;
+
+ u->frame_index += frames;
+ u->since_start += frames * u->frame_size;
+
+/* pa_log_debug("wrote %lu frames", (unsigned long) frames); */
+
+ if (frames >= (snd_pcm_uframes_t) n)
+ break;
+
+ n -= frames;
+ }
+ }
+
+ *sleep_usec = pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) - process_usec;
+ return work_done;
+}
+
+static int unix_write(struct userdata *u, pa_usec_t *sleep_usec) {
+ int work_done = 0;
+ pa_usec_t max_sleep_usec, process_usec;
+ size_t left_to_play;
+
+ pa_assert(u);
+ pa_sink_assert_ref(u->sink);
+
+ if (u->use_tsched)
+ hw_sleep_time(u, &max_sleep_usec, &process_usec);
+
+ for (;;) {
+ snd_pcm_sframes_t n;
+ int r;
+
+ snd_pcm_hwsync(u->pcm_handle);
+
+ if (PA_UNLIKELY((n = snd_pcm_avail_update(u->pcm_handle)) < 0)) {
+
+ if ((r = try_recover(u, "snd_pcm_avail_update", n)) == 0)
+ continue;
- clear_up(u);
- pa_module_unload_request(u->module);
- return;
+ return r;
}
- pa_memblock_release(memchunk->memblock);
-
+ left_to_play = check_left_to_play(u, n);
+
+ if (u->use_tsched)
+
+ /* We won't fill up the playback buffer before at least
+ * half the sleep time is over because otherwise we might
+ * ask for more data from the clients then they expect. We
+ * need to guarantee that clients only have to keep around
+ * a single hw buffer length. */
+
+ if (pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) > process_usec+max_sleep_usec/2)
+ break;
+
+ if (PA_UNLIKELY(n <= u->hwbuf_unused_frames))
+ break;
+
+ n -= u->hwbuf_unused_frames;
+
+ for (;;) {
+ snd_pcm_sframes_t frames;
+ void *p;
+
+/* pa_log_debug("%lu frames to write", (unsigned long) frames); */
+
+ if (u->memchunk.length <= 0)
+ pa_sink_render(u->sink, n * u->frame_size, &u->memchunk);
+
+ pa_assert(u->memchunk.length > 0);
+
+ frames = u->memchunk.length / u->frame_size;
+
+ if (frames > n)
+ frames = n;
+
+ p = pa_memblock_acquire(u->memchunk.memblock);
+ frames = snd_pcm_writei(u->pcm_handle, (const uint8_t*) p + u->memchunk.index, frames);
+ pa_memblock_release(u->memchunk.memblock);
+
+ pa_assert(frames != 0);
+
+ if (PA_UNLIKELY(frames < 0)) {
+
+ if ((r = try_recover(u, "snd_pcm_writei", n)) == 0)
+ continue;
+
+ return r;
+ }
- if (memchunk == &u->memchunk) {
- size_t l = frames * u->frame_size;
- memchunk->index += l;
- memchunk->length -= l;
+ u->memchunk.index += frames * u->frame_size;
+ u->memchunk.length -= frames * u->frame_size;
- if (memchunk->length == 0) {
- pa_memblock_unref(memchunk->memblock);
- memchunk->memblock = NULL;
- memchunk->index = memchunk->length = 0;
+ if (u->memchunk.length <= 0) {
+ pa_memblock_unref(u->memchunk.memblock);
+ pa_memchunk_reset(&u->memchunk);
}
+
+ work_done = 1;
+
+ u->frame_index += frames;
+ u->since_start += frames * u->frame_size;
+
+/* pa_log_debug("wrote %lu frames", (unsigned long) frames); */
+
+ if (frames >= n)
+ break;
+
+ n -= frames;
}
-
- break;
}
+
+ *sleep_usec = pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) - process_usec;
+ return work_done;
}
-static void fdl_callback(void *userdata) {
- struct userdata *u = userdata;
- assert(u);
+static void update_smoother(struct userdata *u) {
+ snd_pcm_sframes_t delay = 0;
+ int64_t frames;
+ int err;
+ pa_usec_t now1, now2;
+/* struct timeval timestamp; */
+ snd_pcm_status_t *status;
- if (snd_pcm_state(u->pcm_handle) == SND_PCM_STATE_XRUN)
- if (xrun_recovery(u) < 0)
- return;
+ snd_pcm_status_alloca(&status);
+
+ pa_assert(u);
+ pa_assert(u->pcm_handle);
+
+ /* Let's update the time smoother */
+
+ snd_pcm_hwsync(u->pcm_handle);
+ snd_pcm_avail_update(u->pcm_handle);
+
+/* if (PA_UNLIKELY((err = snd_pcm_status(u->pcm_handle, status)) < 0)) { */
+/* pa_log("Failed to query DSP status data: %s", snd_strerror(err)); */
+/* return; */
+/* } */
+
+/* delay = snd_pcm_status_get_delay(status); */
+
+ if (PA_UNLIKELY((err = snd_pcm_delay(u->pcm_handle, &delay)) < 0)) {
+ pa_log("Failed to query DSP status data: %s", snd_strerror(err));
+ return;
+ }
- do_write(u);
+ frames = u->frame_index - delay;
+
+/* pa_log_debug("frame_index = %llu, delay = %llu, p = %llu", (unsigned long long) u->frame_index, (unsigned long long) delay, (unsigned long long) frames); */
+
+/* snd_pcm_status_get_tstamp(status, &timestamp); */
+/* pa_rtclock_from_wallclock(&timestamp); */
+/* now1 = pa_timeval_load(&timestamp); */
+
+ now1 = pa_rtclock_usec();
+ now2 = pa_bytes_to_usec(frames * u->frame_size, &u->sink->sample_spec);
+ pa_smoother_put(u->smoother, now1, now2);
}
-static int mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {
- struct userdata *u = snd_mixer_elem_get_callback_private(elem);
+static pa_usec_t sink_get_latency(struct userdata *u) {
+ pa_usec_t r = 0;
+ int64_t delay;
+ pa_usec_t now1, now2;
- assert(u && u->mixer_handle);
+ pa_assert(u);
- if (mask == SND_CTL_EVENT_MASK_REMOVE)
- return 0;
+ now1 = pa_rtclock_usec();
+ now2 = pa_smoother_get(u->smoother, now1);
- 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);
+ delay = (int64_t) pa_bytes_to_usec(u->frame_index * u->frame_size, &u->sink->sample_spec) - (int64_t) now2;
+
+ if (delay > 0)
+ r = (pa_usec_t) delay;
+
+ 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) {
+ pa_assert(u);
+ pa_assert(u->pcm_handle);
+
+ if (u->alsa_rtpoll_item)
+ pa_rtpoll_item_free(u->alsa_rtpoll_item);
+
+ if (!(u->alsa_rtpoll_item = pa_alsa_build_pollfd(u->pcm_handle, u->rtpoll)))
+ return -1;
+
+ return 0;
+}
+
+static int suspend(struct userdata *u) {
+ pa_assert(u);
+ pa_assert(u->pcm_handle);
+
+ pa_smoother_pause(u->smoother, pa_rtclock_usec());
+
+ /* Let's suspend */
+ snd_pcm_drain(u->pcm_handle);
+ snd_pcm_close(u->pcm_handle);
+ u->pcm_handle = NULL;
+
+ if (u->alsa_rtpoll_item) {
+ pa_rtpoll_item_free(u->alsa_rtpoll_item);
+ u->alsa_rtpoll_item = NULL;
}
+ pa_log_info("Device suspended...");
+
return 0;
}
-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;
+static int update_sw_params(struct userdata *u) {
+ snd_pcm_uframes_t avail_min;
int err;
-
- assert(s && u && u->sink);
- 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;
+ pa_assert(u);
+
+ /* Use the full buffer if noone asked us for anything specific */
+ u->hwbuf_unused_frames = 0;
+
+ if (u->use_tsched) {
+ pa_usec_t latency;
+
+ if ((latency = pa_sink_get_requested_latency_within_thread(u->sink)) != (pa_usec_t) -1) {
+ size_t b;
+
+ pa_log_debug("latency set to %0.2f", (double) latency / PA_USEC_PER_MSEC);
+
+ b = pa_usec_to_bytes(latency, &u->sink->sample_spec);
+
+ /* We need at least one sample in our buffer */
+
+ if (PA_UNLIKELY(b < u->frame_size))
+ b = u->frame_size;
+
+ u->hwbuf_unused_frames =
+ PA_LIKELY(b < u->hwbuf_size) ?
+ ((u->hwbuf_size - b) / u->frame_size) : 0;
+
+ fix_tsched_watermark(u);
+ }
}
- if (frames < 0)
- frames = 0;
+ pa_log_debug("hwbuf_unused_frames=%lu", (unsigned long) u->hwbuf_unused_frames);
- r += pa_bytes_to_usec(frames * u->frame_size, &s->sample_spec);
+ /* We need at last one frame in the used part of the buffer */
+ avail_min = u->hwbuf_unused_frames + 1;
- if (u->memchunk.memblock)
- r += pa_bytes_to_usec(u->memchunk.length, &s->sample_spec);
+ if (u->use_tsched) {
+ pa_usec_t sleep_usec, process_usec;
- return r;
+ hw_sleep_time(u, &sleep_usec, &process_usec);
+ avail_min += pa_usec_to_bytes(sleep_usec, &u->sink->sample_spec);
+ }
+
+ pa_log_debug("setting avail_min=%lu", (unsigned long) avail_min);
+
+ if ((err = pa_alsa_set_sw_params(u->pcm_handle, avail_min)) < 0) {
+ pa_log("Failed to set software parameters: %s", snd_strerror(err));
+ return err;
+ }
+
+ pa_sink_set_max_request(u->sink, u->hwbuf_size - u->hwbuf_unused_frames * u->frame_size);
+
+ return 0;
+}
+
+static int unsuspend(struct userdata *u) {
+ pa_sample_spec ss;
+ int err;
+ pa_bool_t b, d;
+ unsigned nfrags;
+ snd_pcm_uframes_t period_size;
+
+ pa_assert(u);
+ pa_assert(!u->pcm_handle);
+
+ pa_log_info("Trying resume...");
+
+ 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;
+ d = u->use_tsched;
+
+ if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &nfrags, &period_size, u->hwbuf_size / u->frame_size, &b, &d, TRUE)) < 0) {
+ pa_log("Failed to set hardware parameters: %s", snd_strerror(err));
+ goto fail;
+ }
+
+ if (b != u->use_mmap || d != u->use_tsched) {
+ 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 (update_sw_params(u) < 0)
+ goto fail;
+
+ if (build_pollfd(u) < 0)
+ goto fail;
+
+ /* FIXME: We need to reload the volume somehow */
+
+ u->first = TRUE;
+ u->since_start = 0;
+
+ 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 int sink_get_hw_volume_cb(pa_sink *s) {
+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 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_IS_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;
+ }
+
+ return pa_sink_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_sink_get_volume(u->sink);
+ pa_sink_get_mute(u->sink);
+ }
+
+ return 0;
+}
+
+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++) {
- long set_vol, vol;
+ for (i = 0; i < s->sample_spec.channels; i++) {
+ long alsa_vol;
- assert(snd_mixer_selem_has_playback_channel(u->mixer_elem, i));
+ pa_assert(snd_mixer_selem_has_playback_channel(u->mixer_elem, u->mixer_map[i]));
- if ((err = snd_mixer_selem_get_playback_volume(u->mixer_elem, i, &vol)) < 0)
- goto fail;
+ if (u->hw_dB_supported) {
- 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;
+ if ((err = snd_mixer_selem_get_playback_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol)) >= 0) {
+ s->volume.values[i] = pa_sw_volume_from_dB(alsa_vol / 100.0);
+ continue;
+ }
- /* 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));
+ u->hw_dB_supported = FALSE;
+ }
+
+ if ((err = snd_mixer_selem_get_playback_volume(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0)
+ goto fail;
+
+ s->volume.values[i] = (pa_volume_t) roundf(((float) (alsa_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;
+
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;
-
- assert(snd_mixer_selem_has_playback_channel(u->mixer_elem, i));
+ pa_volume_t vol;
+
+ pa_assert(snd_mixer_selem_has_playback_channel(u->mixer_elem, u->mixer_map[i]));
- vol = s->hw_volume.values[i];
+ vol = PA_MIN(s->volume.values[i], PA_VOLUME_NORM);
+
+ if (u->hw_dB_supported) {
+ alsa_vol = (long) (pa_sw_volume_to_dB(vol) * 100);
+ alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_dB_min, u->hw_dB_max);
+
+ if ((err = snd_mixer_selem_set_playback_dB(u->mixer_elem, u->mixer_map[i], alsa_vol, -1)) >= 0) {
+
+ if (snd_mixer_selem_get_playback_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol) >= 0)
+ s->volume.values[i] = pa_sw_volume_from_dB(alsa_vol / 100.0);
+
+ continue;
+ }
+
+ u->hw_dB_supported = FALSE;
+
+ }
- if (vol > PA_VOLUME_NORM)
- vol = PA_VOLUME_NORM;
-
alsa_vol = (long) roundf(((float) vol * (u->hw_volume_max - u->hw_volume_min)) / PA_VOLUME_NORM) + u->hw_volume_min;
+ alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_volume_min, u->hw_volume_max);
- if ((err = snd_mixer_selem_set_playback_volume(u->mixer_elem, i, alsa_vol)) < 0)
+ if ((err = snd_mixer_selem_set_playback_volume(u->mixer_elem, u->mixer_map[i], alsa_vol)) < 0)
goto fail;
+
+ if (snd_mixer_selem_get_playback_volume(u->mixer_elem, u->mixer_map[i], &alsa_vol) >= 0)
+ s->volume.values[i] = (pa_volume_t) roundf(((float) (alsa_vol - u->hw_volume_min) * PA_VOLUME_NORM) / (u->hw_volume_max - u->hw_volume_min));
}
return 0;
fail:
pa_log_error("Unable to set volume: %s", snd_strerror(err));
- s->get_hw_volume = NULL;
- s->set_hw_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;
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;
return -1;
}
return 0;
}
-int pa__init(pa_core *c, pa_module*m) {
+static void sink_update_requested_latency_cb(pa_sink *s) {
+ struct userdata *u = s->userdata;
+ snd_pcm_sframes_t before;
+ pa_assert(u);
+
+ if (!u->pcm_handle)
+ return;
+
+ before = u->hwbuf_unused_frames;
+ update_sw_params(u);
+
+ /* Let's check whether we now use only a smaller part of the
+ buffer then before. If so, we need to make sure that subsequent
+ rewinds are relative to the new maxium fill level and not to the
+ current fill level. Thus, let's do a full rewind once, to clear
+ things up. */
+
+ if (u->hwbuf_unused_frames > before) {
+ pa_log_debug("Requesting rewind due to latency change.");
+ pa_sink_request_rewind(s, 0);
+ }
+}
+
+static int process_rewind(struct userdata *u) {
+ snd_pcm_sframes_t unused;
+ size_t rewind_nbytes, unused_nbytes, limit_nbytes;
+ pa_assert(u);
+
+ /* Figure out how much we shall rewind and reset the counter */
+ rewind_nbytes = u->sink->thread_info.rewind_nbytes;
+ u->sink->thread_info.rewind_nbytes = 0;
+
+ pa_assert(rewind_nbytes > 0);
+ pa_log_debug("Requested to rewind %lu bytes.", (unsigned long) rewind_nbytes);
+
+ snd_pcm_hwsync(u->pcm_handle);
+ if ((unused = snd_pcm_avail_update(u->pcm_handle)) < 0) {
+ pa_log("snd_pcm_avail_update() failed: %s", snd_strerror(unused));
+ return -1;
+ }
+
+ unused_nbytes = u->tsched_watermark + (size_t) unused * u->frame_size;
+
+ if (u->hwbuf_size > unused_nbytes)
+ limit_nbytes = u->hwbuf_size - unused_nbytes;
+ else
+ limit_nbytes = 0;
+
+ if (rewind_nbytes > limit_nbytes)
+ rewind_nbytes = limit_nbytes;
+
+ if (rewind_nbytes > 0) {
+ snd_pcm_sframes_t in_frames, out_frames;
+
+ pa_log_debug("Limited to %lu bytes.", (unsigned long) rewind_nbytes);
+
+ in_frames = (snd_pcm_sframes_t) rewind_nbytes / u->frame_size;
+ pa_log_debug("before: %lu", (unsigned long) in_frames);
+ if ((out_frames = snd_pcm_rewind(u->pcm_handle, in_frames)) < 0) {
+ pa_log("snd_pcm_rewind() failed: %s", snd_strerror(out_frames));
+ return -1;
+ }
+ pa_log_debug("after: %lu", (unsigned long) out_frames);
+
+ rewind_nbytes = out_frames * u->frame_size;
+
+ if (rewind_nbytes <= 0)
+ pa_log_info("Tried rewind, but was apparently not possible.");
+ else {
+ u->frame_index -= out_frames;
+ pa_log_debug("Rewound %lu bytes.", (unsigned long) rewind_nbytes);
+ pa_sink_process_rewind(u->sink, rewind_nbytes);
+
+ u->after_rewind = TRUE;
+ }
+ } else
+ pa_log_debug("Mhmm, actually there is nothing to rewind.");
+
+ return 0;
+}
+
+static void thread_func(void *userdata) {
+ struct userdata *u = userdata;
+
+ pa_assert(u);
+
+ pa_log_debug("Thread starting up");
+
+ if (u->core->realtime_scheduling)
+ pa_make_realtime(u->core->realtime_priority);
+
+ pa_thread_mq_install(&u->thread_mq);
+ pa_rtpoll_install(u->rtpoll);
+
+ for (;;) {
+ int ret;
+
+/* pa_log_debug("loop"); */
+
+ /* Render some data and write it to the dsp */
+ if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
+ int work_done;
+ pa_usec_t sleep_usec;
+
+ if (u->sink->thread_info.rewind_nbytes > 0)
+ if (process_rewind(u) < 0)
+ goto fail;
+
+ if (u->use_mmap)
+ work_done = mmap_write(u, &sleep_usec);
+ else
+ work_done = unix_write(u, &sleep_usec);
+
+ if (work_done < 0)
+ goto fail;
+
+/* pa_log_debug("work_done = %i", work_done); */
+
+ if (work_done) {
+
+ if (u->first) {
+ pa_log_info("Starting playback.");
+ snd_pcm_start(u->pcm_handle);
+
+ pa_smoother_resume(u->smoother, pa_rtclock_usec());
+ }
+
+ update_smoother(u);
+ }
+
+ if (u->use_tsched) {
+ pa_usec_t cusec;
+
+ if (u->since_start <= u->hwbuf_size) {
+
+ /* USB devices on ALSA seem to hit a buffer
+ * underrun during the first iterations much
+ * quicker then we calculate here, probably due to
+ * the transport latency. To accomodate for that
+ * we artificially decrease the sleep time until
+ * we have filled the buffer at least once
+ * completely.*/
+
+ /*pa_log_debug("Cutting sleep time for the initial iterations by half.");*/
+ sleep_usec /= 2;
+ }
+
+ /* OK, the playback buffer is now full, let's
+ * calculate when to wake up next */
+/* pa_log_debug("Waking up in %0.2fms (sound card clock).", (double) sleep_usec / PA_USEC_PER_MSEC); */
+
+ /* Convert from the sound card time domain to the
+ * system time domain */
+ cusec = pa_smoother_translate(u->smoother, pa_rtclock_usec(), sleep_usec);
+
+/* pa_log_debug("Waking up in %0.2fms (system clock).", (double) cusec / PA_USEC_PER_MSEC); */
+
+ /* We don't trust the conversion, so we wake up whatever comes first */
+ pa_rtpoll_set_timer_relative(u->rtpoll, PA_MIN(sleep_usec, cusec));
+ }
+
+ u->first = FALSE;
+ u->after_rewind = FALSE;
+
+ } else if (u->use_tsched)
+
+ /* OK, we're in an invalid state, let's disable our timers */
+ 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;
+
+ /* Tell ALSA about this and process its response */
+ if (PA_SINK_IS_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 (pa_alsa_recover_from_poll(u->pcm_handle, revents) < 0)
+ goto fail;
+
+ u->first = TRUE;
+ u->since_start = 0;
+ }
+
+ if (revents && u->use_tsched)
+ pa_log_debug("Wakeup from ALSA! (%i)", revents);
+ }
+ }
+
+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;
+ const char *dev_id;
pa_sample_spec ss;
pa_channel_map map;
- uint32_t periods, fragsize;
- snd_pcm_uframes_t period_size;
+ uint32_t nfrags, hwbuf_size, frag_size, tsched_size, tsched_watermark;
+ snd_pcm_uframes_t period_frames, tsched_frames;
size_t frame_size;
snd_pcm_info_t *pcm_info = NULL;
int err;
- char *t;
const char *name;
char *name_buf = NULL;
- int namereg_fail;
-
+ pa_bool_t namereg_fail;
+ pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, mixer_reset = TRUE;
+ pa_usec_t usec;
+ pa_sink_new_data data;
+
+ snd_pcm_info_alloca(&pcm_info);
+
+ pa_assert(m);
+
+ pa_alsa_redirect_errors_inc();
+
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;
- 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");
+ nfrags = m->core->default_n_fragments;
+ frag_size = pa_usec_to_bytes(m->core->default_fragment_size_msec*PA_USEC_PER_MSEC, &ss);
+ if (frag_size <= 0)
+ frag_size = frame_size;
+ tsched_size = pa_usec_to_bytes(DEFAULT_TSCHED_BUFFER_USEC, &ss);
+ tsched_watermark = pa_usec_to_bytes(DEFAULT_TSCHED_WATERMARK_USEC, &ss);
+
+ if (pa_modargs_get_value_u32(ma, "fragments", &nfrags) < 0 ||
+ pa_modargs_get_value_u32(ma, "fragment_size", &frag_size) < 0 ||
+ pa_modargs_get_value_u32(ma, "tsched_buffer_size", &tsched_size) < 0 ||
+ pa_modargs_get_value_u32(ma, "tsched_buffer_watermark", &tsched_watermark) < 0) {
+ pa_log("Failed to parse buffer metrics");
goto fail;
}
- period_size = fragsize/frame_size;
-
- u = pa_xnew0(struct userdata, 1);
- m->userdata = u;
- u->module = m;
-
- 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));
+
+ hwbuf_size = frag_size * nfrags;
+ period_frames = frag_size/frame_size;
+ tsched_frames = tsched_size/frame_size;
+
+ if (pa_modargs_get_value_boolean(ma, "mmap", &use_mmap) < 0) {
+ pa_log("Failed to parse mmap argument.");
goto fail;
}
- if ((err = snd_pcm_info_malloc(&pcm_info)) < 0 ||
- (err = snd_pcm_info(u->pcm_handle, pcm_info)) < 0) {
- pa_log("Error fetching PCM info: %s", snd_strerror(err));
+ if (pa_modargs_get_value_boolean(ma, "tsched", &use_tsched) < 0) {
+ pa_log("Failed to parse timer_scheduling argument.");
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 (use_tsched && !pa_rtclock_hrtimer()) {
+ pa_log("Disabling timer-based scheduling because high-resolution timers are not available from the kernel.");
+ use_tsched = FALSE;
+ }
+
+ if (pa_modargs_get_value_boolean(ma, "mixer_reset", &mixer_reset) < 0) {
+ pa_log("Failed to parse mixer_reset argument.");
goto fail;
}
- 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) {
- pa_log("Error opening mixer: %s", snd_strerror(err));
+ u = pa_xnew0(struct userdata, 1);
+ u->core = m->core;
+ u->module = m;
+ m->userdata = u;
+ u->use_mmap = use_mmap;
+ u->use_tsched = use_tsched;
+ u->first = TRUE;
+ u->since_start = 0;
+ u->after_rewind = FALSE;
+ u->rtpoll = pa_rtpoll_new();
+ pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
+ u->alsa_rtpoll_item = NULL;
+
+ u->smoother = pa_smoother_new(DEFAULT_TSCHED_BUFFER_USEC*2, DEFAULT_TSCHED_BUFFER_USEC*2, TRUE, 5);
+ usec = pa_rtclock_usec();
+ pa_smoother_set_time_offset(u->smoother, usec);
+ pa_smoother_pause(u->smoother, usec);
+
+ snd_config_update_free_global();
+
+ b = use_mmap;
+ d = use_tsched;
+
+ if ((dev_id = pa_modargs_get_value(ma, "device_id", NULL))) {
+
+ if (!(u->pcm_handle = pa_alsa_open_by_device_id(
+ dev_id,
+ &u->device_name,
+ &ss, &map,
+ SND_PCM_STREAM_PLAYBACK,
+ &nfrags, &period_frames, tsched_frames,
+ &b, &d)))
+
+ goto fail;
+
+ } else {
+
+ if (!(u->pcm_handle = pa_alsa_open_by_device_string(
+ pa_modargs_get_value(ma, "device", DEFAULT_DEVICE),
+ &u->device_name,
+ &ss, &map,
+ SND_PCM_STREAM_PLAYBACK,
+ &nfrags, &period_frames, tsched_frames,
+ &b, &d)))
+ goto fail;
+
+ }
+
+ pa_assert(u->device_name);
+ pa_log_info("Successfully opened device %s.", u->device_name);
+
+ if (use_mmap && !b) {
+ pa_log_info("Device doesn't support mmap(), falling back to UNIX read/write mode.");
+ u->use_mmap = use_mmap = FALSE;
+ }
+
+ if (use_tsched && (!b || !d)) {
+ pa_log_info("Cannot enabled timer-based scheduling, falling back to sound IRQ scheduling.");
+ u->use_tsched = use_tsched = FALSE;
+ }
+
+ if (u->use_mmap)
+ pa_log_info("Successfully enabled mmap() mode.");
+
+ if (u->use_tsched)
+ pa_log_info("Successfully enabled timer-based scheduling 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 ((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;
+ /* ALSA might tweak the sample spec, so recalculate the frame size */
+ frame_size = pa_frame_size(&ss);
+
+ if ((err = snd_mixer_open(&u->mixer_handle, 0)) < 0)
+ pa_log_warn("Error opening mixer: %s", snd_strerror(err));
+ else {
+ pa_bool_t found = FALSE;
+
+ if (pa_alsa_prepare_mixer(u->mixer_handle, u->device_name) >= 0)
+ found = TRUE;
+ else {
+ snd_pcm_info_t *info;
+
+ snd_pcm_info_alloca(&info);
+
+ if (snd_pcm_info(u->pcm_handle, info) >= 0) {
+ char *md;
+ int card;
+
+ if ((card = snd_pcm_info_get_card(info)) >= 0) {
+
+ md = pa_sprintf_malloc("hw:%i", card);
+
+ if (strcmp(u->device_name, md))
+ if (pa_alsa_prepare_mixer(u->mixer_handle, md) >= 0)
+ found = TRUE;
+ pa_xfree(md);
+ }
+ }
+ }
+
+ if (found)
+ if (!(u->mixer_elem = pa_alsa_find_elem(u->mixer_handle, "Master", "PCM")))
+ found = FALSE;
+
+ if (!found) {
+ snd_mixer_close(u->mixer_handle);
+ u->mixer_handle = NULL;
+ }
}
if ((name = pa_modargs_get_value(ma, "sink_name", NULL)))
- namereg_fail = 1;
+ namereg_fail = TRUE;
else {
- name = name_buf = pa_sprintf_malloc("alsa_output.%s", dev);
- namereg_fail = 0;
+ name = name_buf = pa_sprintf_malloc("alsa_output.%s", u->device_name);
+ namereg_fail = FALSE;
}
- if (!(u->sink = pa_sink_new(c, __FILE__, name, namereg_fail, &ss, &map))) {
+ pa_sink_new_data_init(&data);
+ data.driver = __FILE__;
+ data.module = m;
+ pa_sink_new_data_set_name(&data, name);
+ data.namereg_fail = namereg_fail;
+ pa_sink_new_data_set_sample_spec(&data, &ss);
+ pa_sink_new_data_set_channel_map(&data, &map);
+
+ pa_alsa_init_proplist(data.proplist, pcm_info);
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_name);
+ pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (period_frames * frame_size * nfrags));
+ pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (period_frames * frame_size));
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, u->use_tsched ? "mmap+timer" : (u->use_mmap ? "mmap" : "serial"));
+
+ u->sink = pa_sink_new(m->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY);
+ pa_sink_new_data_done(&data);
+ 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->update_requested_latency = sink_update_requested_latency_cb;
+ u->sink->userdata = u;
+
+ pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
+ pa_sink_set_rtpoll(u->sink, u->rtpoll);
+
+ u->frame_size = frame_size;
+ u->fragment_size = frag_size = period_frames * frame_size;
+ u->nfragments = nfrags;
+ u->hwbuf_size = u->fragment_size * nfrags;
+ u->hwbuf_unused_frames = 0;
+ u->tsched_watermark = tsched_watermark;
+ u->frame_index = 0;
+ u->hw_dB_supported = FALSE;
+ u->hw_dB_min = u->hw_dB_max = 0;
+ u->hw_volume_min = u->hw_volume_max = 0;
+
+ if (use_tsched)
+ fix_tsched_watermark(u);
+
+ u->sink->thread_info.max_rewind = use_tsched ? u->hwbuf_size : 0;
+ u->sink->thread_info.max_request = u->hwbuf_size;
+
+ pa_sink_set_latency_range(u->sink,
+ !use_tsched ? pa_bytes_to_usec(u->hwbuf_size, &ss) : (pa_usec_t) -1,
+ pa_bytes_to_usec(u->hwbuf_size, &ss));
+
+ pa_log_info("Using %u fragments of size %lu bytes, buffer time is %0.2fms",
+ nfrags, (long unsigned) u->fragment_size,
+ (double) pa_bytes_to_usec(u->hwbuf_size, &ss) / PA_USEC_PER_MSEC);
+
+ if (use_tsched)
+ pa_log_info("Time scheduling watermark is %0.2fms",
+ (double) pa_bytes_to_usec(u->tsched_watermark, &ss) / PA_USEC_PER_MSEC);
+
+ if (update_sw_params(u) < 0)
+ goto fail;
+
+ pa_memchunk_reset(&u->memchunk);
+
if (u->mixer_handle) {
- assert(u->mixer_elem);
- if (snd_mixer_selem_has_playback_volume(u->mixer_elem)) {
- int i;
+ pa_assert(u->mixer_elem);
- for (i = 0;i < ss.channels;i++) {
- if (!snd_mixer_selem_has_playback_channel(u->mixer_elem, i))
- break;
- }
+ if (snd_mixer_selem_has_playback_volume(u->mixer_elem))
+
+ if (pa_alsa_calc_mixer_map(u->mixer_elem, &map, u->mixer_map, TRUE) >= 0 &&
+ snd_mixer_selem_get_playback_volume_range(u->mixer_elem, &u->hw_volume_min, &u->hw_volume_max) >= 0) {
- 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_bool_t suitable = TRUE;
+
+ pa_log_info("Volume ranges from %li to %li.", u->hw_volume_min, u->hw_volume_max);
+
+ if (u->hw_volume_min > u->hw_volume_max) {
+
+ pa_log_info("Minimal volume %li larger than maximum volume %li. Strange stuff Falling back to software volume control.", u->hw_volume_min, u->hw_volume_max);
+ suitable = FALSE;
+
+ } else if (u->hw_volume_max - u->hw_volume_min < 3) {
+
+ pa_log_info("Device has less than 4 volume levels. Falling back to software volume control.");
+ suitable = FALSE;
+
+ } else if (snd_mixer_selem_get_playback_dB_range(u->mixer_elem, &u->hw_dB_min, &u->hw_dB_max) >= 0) {
+
+ /* u->hw_dB_max = 0; u->hw_dB_min = -3000; Use this to make valgrind shut up */
+
+ pa_log_info("Volume ranges from %0.2f dB to %0.2f dB.", u->hw_dB_min/100.0, u->hw_dB_max/100.0);
+
+ /* Let's see if this thing actually is useful for muting */
+ if (u->hw_dB_min > -6000) {
+ pa_log_info("Device cannot attenuate for more than -60 dB (only %0.2f dB supported), falling back to software volume control.", ((double) u->hw_dB_min) / 100);
+
+ suitable = FALSE;
+ } else if (u->hw_dB_max < 0) {
+
+ pa_log_info("Device is still attenuated at maximum volume setting (%0.2f dB is maximum). Strange stuff. Falling back to software volume control.", ((double) u->hw_dB_max) / 100);
+ suitable = FALSE;
+
+ } else if (u->hw_dB_min >= u->hw_dB_max) {
+
+ pa_log_info("Minimal dB (%0.2f) larger or equal to maximum dB (%0.2f). Strange stuff. Falling back to software volume control.", ((double) u->hw_dB_min) / 100, ((double) u->hw_dB_max) / 100);
+ suitable = FALSE;
+
+ } else {
+
+ if (u->hw_dB_max > 0) {
+ /* dB > 0 means overamplification, and clipping, we don't want that here */
+ pa_log_info("Device can do overamplification for %0.2f dB. Limiting to 0 db", ((double) u->hw_dB_max) / 100);
+ u->hw_dB_max = 0;
+ }
+
+ u->hw_dB_supported = TRUE;
+ }
+ }
+
+ if (suitable) {
+ u->sink->get_volume = sink_get_volume_cb;
+ u->sink->set_volume = sink_set_volume_cb;
+ u->sink->flags |= PA_SINK_HW_VOLUME_CTRL | (u->hw_dB_supported ? PA_SINK_DECIBEL_VOLUME : 0);
+ pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->hw_dB_supported ? "supported" : "not supported");
+
+ } else if (mixer_reset) {
+ pa_log_info("Using software volume control. Trying to reset sound card to 0 dB.");
+ pa_alsa_0dB_playback(u->mixer_elem);
+ } else
+ pa_log_info("Using software volume control. Leaving hw mixer controls untouched.");
}
- }
+
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->flags |= PA_SINK_HW_MUTE_CTRL;
}
- }
- 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) {
- pa_log("failed to initialise file descriptor monitoring");
+
+ if (pa_alsa_fdlist_set_mixer(u->mixer_fdl, u->mixer_handle, m->core->mainloop) < 0) {
+ pa_log("Failed to initialize 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;
+ pa_alsa_dump(u->pcm_handle);
- 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);
-
-finish:
+ if (data.volume_is_set) {
+ if (u->sink->set_volume)
+ u->sink->set_volume(u->sink);
+ } else {
+ if (u->sink->get_volume)
+ u->sink->get_volume(u->sink);
+ }
- pa_xfree(name_buf);
-
- if (ma)
- pa_modargs_free(ma);
+ if (data.muted_is_set) {
+ if (u->sink->set_mute)
+ u->sink->set_mute(u->sink);
+ } else {
+ if (u->sink->get_mute)
+ u->sink->get_mute(u->sink);
+ }
+
+ pa_sink_put(u->sink);
- if (pcm_info)
- snd_pcm_info_free(pcm_info);
-
- return ret;
+ pa_modargs_free(ma);
+
+ return 0;
fail:
-
- if (u)
- pa__done(c, m);
- goto finish;
+ if (ma)
+ pa_modargs_free(ma);
+
+ 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);
- if (!(u = m->userdata))
+ pa_assert(m);
+
+ if (!(u = m->userdata)) {
+ pa_alsa_redirect_errors_dec();
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);
+ }
- clear_up(u);
+ 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);
+ }
+
+ if (u->smoother)
+ pa_smoother_free(u->smoother);
+
+ pa_xfree(u->device_name);
pa_xfree(u);
-}
+ snd_config_update_free_global();
+
+ pa_alsa_redirect_errors_dec();
+}
diff --git a/src/modules/module-alsa-source.c b/src/modules/module-alsa-source.c
index 9bde46da..1cc467d9 100644
--- a/src/modules/module-alsa-source.c
+++ b/src/modules/module-alsa-source.c
@@ -1,18 +1,19 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
+ Copyright 2004-2008 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
@@ -23,18 +24,13 @@
#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 <pulse/timeval.h>
#include <pulsecore/core-error.h>
#include <pulsecore/core.h>
@@ -45,495 +41,1292 @@
#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 <pulsecore/time-smoother.h>
+#include <pulsecore/rtclock.h>
#include "alsa-util.h"
#include "module-alsa-source-symdef.h"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("ALSA Source")
-PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("ALSA Source");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
PA_MODULE_USAGE(
"source_name=<name for the source> "
"device=<ALSA device> "
+ "device_id=<ALSA card index> "
"format=<sample format> "
- "channels=<number of channels> "
"rate=<sample rate> "
+ "channels=<number of channels> "
+ "channel_map=<channel map> "
"fragments=<number of fragments> "
"fragment_size=<fragment size> "
- "channel_map=<channel map>")
-
-struct userdata {
- snd_pcm_t *pcm_handle;
- 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;
-};
+ "mmap=<enable memory mapping?> "
+ "tsched=<enable system timer based scheduling mode?> "
+ "tsched_buffer_size=<buffer size when using timer based scheduling> "
+ "tsched_buffer_watermark=<upper fill watermark> "
+ "mixer_reset=<reset hw volume and mute settings to sane defaults when falling back to software?>");
static const char* const valid_modargs[] = {
- "device",
"source_name",
- "channels",
- "rate",
+ "device",
+ "device_id",
"format",
+ "rate",
+ "channels",
+ "channel_map",
"fragments",
"fragment_size",
- "channel_map",
+ "mmap",
+ "tsched",
+ "tsched_buffer_size",
+ "tsched_buffer_watermark",
+ "mixer_reset",
NULL
};
#define DEFAULT_DEVICE "default"
+#define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC) /* 2s */
+#define DEFAULT_TSCHED_WATERMARK_USEC (20*PA_USEC_PER_MSEC) /* 20ms */
+#define TSCHED_MIN_SLEEP_USEC (3*PA_USEC_PER_MSEC) /* 3ms */
+#define TSCHED_MIN_WAKEUP_USEC (3*PA_USEC_PER_MSEC) /* 3ms */
+
+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;
+ long hw_volume_max, hw_volume_min;
+ long hw_dB_max, hw_dB_min;
+ pa_bool_t hw_dB_supported;
+
+ size_t frame_size, fragment_size, hwbuf_size, tsched_watermark;
+ unsigned nfragments;
+
+ char *device_name;
+
+ pa_bool_t use_mmap, use_tsched;
+
+ pa_rtpoll_item *alsa_rtpoll_item;
+
+ snd_mixer_selem_channel_id_t mixer_map[SND_MIXER_SCHN_LAST];
+
+ pa_smoother *smoother;
+ int64_t frame_index;
-static void update_usage(struct userdata *u) {
- pa_module_set_used(u->module, u->source ? pa_source_used_by(u->source) : 0);
+ snd_pcm_sframes_t hwbuf_unused_frames;
+};
+
+static void fix_tsched_watermark(struct userdata *u) {
+ size_t max_use;
+ size_t min_sleep, min_wakeup;
+ pa_assert(u);
+
+ max_use = u->hwbuf_size - u->hwbuf_unused_frames * u->frame_size;
+
+ min_sleep = pa_usec_to_bytes(TSCHED_MIN_SLEEP_USEC, &u->source->sample_spec);
+ min_wakeup = pa_usec_to_bytes(TSCHED_MIN_WAKEUP_USEC, &u->source->sample_spec);
+
+ if (min_sleep > max_use/2)
+ min_sleep = pa_frame_align(max_use/2, &u->source->sample_spec);
+ if (min_sleep < u->frame_size)
+ min_sleep = u->frame_size;
+
+ if (min_wakeup > max_use/2)
+ min_wakeup = pa_frame_align(max_use/2, &u->source->sample_spec);
+ if (min_wakeup < u->frame_size)
+ min_wakeup = u->frame_size;
+
+ if (u->tsched_watermark > max_use-min_sleep)
+ u->tsched_watermark = max_use-min_sleep;
+
+ if (u->tsched_watermark < min_wakeup)
+ u->tsched_watermark = min_wakeup;
}
-static void clear_up(struct userdata *u) {
- assert(u);
-
- if (u->source) {
- pa_source_disconnect(u->source);
- pa_source_unref(u->source);
- u->source = NULL;
- }
-
- if (u->pcm_fdl)
- pa_alsa_fdlist_free(u->pcm_fdl);
- if (u->mixer_fdl)
- pa_alsa_fdlist_free(u->mixer_fdl);
+static pa_usec_t hw_sleep_time(struct userdata *u, pa_usec_t *sleep_usec, pa_usec_t*process_usec) {
+ pa_usec_t wm, usec;
- u->pcm_fdl = u->mixer_fdl = NULL;
+ pa_assert(u);
- if (u->mixer_handle) {
- snd_mixer_close(u->mixer_handle);
- u->mixer_handle = NULL;
- }
-
- if (u->pcm_handle) {
- snd_pcm_drop(u->pcm_handle);
- snd_pcm_close(u->pcm_handle);
- u->pcm_handle = NULL;
+ usec = pa_source_get_requested_latency_within_thread(u->source);
+
+ if (usec == (pa_usec_t) -1)
+ usec = pa_bytes_to_usec(u->hwbuf_size, &u->source->sample_spec);
+
+/* pa_log_debug("hw buffer time: %u ms", (unsigned) (usec / PA_USEC_PER_MSEC)); */
+
+ wm = pa_bytes_to_usec(u->tsched_watermark, &u->source->sample_spec);
+
+ if (usec >= wm) {
+ *sleep_usec = usec - wm;
+ *process_usec = wm;
+ } else
+ *process_usec = *sleep_usec = usec /= 2;
+
+/* pa_log_debug("after watermark: %u ms", (unsigned) (*sleep_usec / PA_USEC_PER_MSEC)); */
+
+ return usec;
+}
+
+static int try_recover(struct userdata *u, const char *call, int err) {
+ pa_assert(u);
+ pa_assert(call);
+ pa_assert(err < 0);
+
+ pa_log_debug("%s: %s", call, snd_strerror(err));
+
+ pa_assert(err != -EAGAIN);
+
+ if (err == -EPIPE)
+ pa_log_debug("%s: Buffer overrun!", call);
+
+ if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) == 0) {
+ snd_pcm_start(u->pcm_handle);
+ return 0;
}
+
+ pa_log("%s: %s", call, snd_strerror(err));
+ return -1;
}
-static int xrun_recovery(struct userdata *u) {
- int ret;
- assert(u);
+static size_t check_left_to_record(struct userdata *u, snd_pcm_sframes_t n) {
+ size_t left_to_record;
- pa_log_info("*** ALSA-XRUN (capture) ***");
-
- if ((ret = snd_pcm_prepare(u->pcm_handle)) < 0) {
- pa_log("snd_pcm_prepare() failed: %s", snd_strerror(-ret));
+ if (n*u->frame_size < u->hwbuf_size)
+ left_to_record = u->hwbuf_size - (n*u->frame_size);
+ else
+ left_to_record = 0;
- clear_up(u);
- pa_module_unload_request(u->module);
+ if (left_to_record > 0) {
+/* pa_log_debug("%0.2f ms left to record", (double) pa_bytes_to_usec(left_to_record, &u->source->sample_spec) / PA_USEC_PER_MSEC); */
+ } else {
+ pa_log_info("Overrun!");
- return -1;
+ if (u->use_tsched) {
+ size_t old_watermark = u->tsched_watermark;
+
+ u->tsched_watermark *= 2;
+ fix_tsched_watermark(u);
+
+ if (old_watermark != u->tsched_watermark)
+ pa_log_notice("Increasing wakeup watermark to %0.2f ms",
+ (double) pa_bytes_to_usec(u->tsched_watermark, &u->source->sample_spec) / PA_USEC_PER_MSEC);
+ }
}
- return 0;
+ return left_to_record;
}
-static void do_read(struct userdata *u) {
- assert(u);
+static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec) {
+ int work_done = 0;
+ pa_usec_t max_sleep_usec, process_usec;
+ size_t left_to_record;
+
+ pa_assert(u);
+ pa_source_assert_ref(u->source);
+
+ if (u->use_tsched)
+ hw_sleep_time(u, &max_sleep_usec, &process_usec);
- update_usage(u);
-
for (;;) {
- pa_memchunk post_memchunk;
- snd_pcm_sframes_t frames;
- size_t l;
- void *p;
-
- if (!u->memchunk.memblock) {
- u->memchunk.memblock = pa_memblock_new(u->source->core->mempool, u->memchunk.length = u->fragment_size);
- u->memchunk.index = 0;
+ snd_pcm_sframes_t n;
+ int r;
+
+ snd_pcm_hwsync(u->pcm_handle);
+
+ if (PA_UNLIKELY((n = snd_pcm_avail_update(u->pcm_handle)) < 0)) {
+
+ if ((r = try_recover(u, "snd_pcm_avail_update", n)) == 0)
+ continue;
+
+ return r;
}
-
- assert(u->memchunk.memblock);
- assert(u->memchunk.length);
- assert(u->memchunk.length % u->frame_size == 0);
-
- p = pa_memblock_acquire(u->memchunk.memblock);
-
- if ((frames = snd_pcm_readi(u->pcm_handle, (uint8_t*) p + u->memchunk.index, u->memchunk.length / u->frame_size)) < 0) {
- pa_memblock_release(u->memchunk.memblock);
-
- if (frames == -EAGAIN)
- return;
-
- if (frames == -EPIPE) {
- if (xrun_recovery(u) < 0)
- return;
-
+
+ left_to_record = check_left_to_record(u, n);
+
+ if (u->use_tsched)
+ if (pa_bytes_to_usec(left_to_record, &u->source->sample_spec) > process_usec+max_sleep_usec/2)
+ break;
+
+ if (PA_UNLIKELY(n <= 0))
+ break;
+
+ for (;;) {
+ int err;
+ const snd_pcm_channel_area_t *areas;
+ snd_pcm_uframes_t offset, frames = (snd_pcm_uframes_t) n;
+ pa_memchunk chunk;
+ void *p;
+
+/* pa_log_debug("%lu frames to read", (unsigned long) frames); */
+
+ if (PA_UNLIKELY((err = snd_pcm_mmap_begin(u->pcm_handle, &areas, &offset, &frames)) < 0)) {
+
+ if ((r = try_recover(u, "snd_pcm_mmap_begin", err)) == 0)
+ continue;
+
+ return r;
+ }
+
+ /* Make sure that if these memblocks need to be copied they will fit into one slot */
+ if (frames > pa_mempool_block_size_max(u->source->core->mempool)/u->frame_size)
+ frames = pa_mempool_block_size_max(u->source->core->mempool)/u->frame_size;
+
+ /* Check these are multiples of 8 bit */
+ pa_assert((areas[0].first & 7) == 0);
+ pa_assert((areas[0].step & 7)== 0);
+
+ /* We assume a single interleaved memory buffer */
+ pa_assert((areas[0].first >> 3) == 0);
+ pa_assert((areas[0].step >> 3) == u->frame_size);
+
+ p = (uint8_t*) areas[0].addr + (offset * u->frame_size);
+
+ chunk.memblock = pa_memblock_new_fixed(u->core->mempool, p, frames * u->frame_size, TRUE);
+ chunk.length = pa_memblock_get_length(chunk.memblock);
+ chunk.index = 0;
+
+ pa_source_post(u->source, &chunk);
+ pa_memblock_unref_fixed(chunk.memblock);
+
+ if (PA_UNLIKELY((err = snd_pcm_mmap_commit(u->pcm_handle, offset, frames)) < 0)) {
+
+ if ((r = try_recover(u, "snd_pcm_mmap_commit", err)) == 0)
+ continue;
+
+ return r;
+ }
+
+ work_done = 1;
+
+ u->frame_index += frames;
+
+/* pa_log_debug("read %lu frames", (unsigned long) frames); */
+
+ if (frames >= (snd_pcm_uframes_t) n)
+ break;
+
+ n -= frames;
+ }
+ }
+
+ *sleep_usec = pa_bytes_to_usec(left_to_record, &u->source->sample_spec) - process_usec;
+ return work_done;
+}
+
+static int unix_read(struct userdata *u, pa_usec_t *sleep_usec) {
+ int work_done = 0;
+ pa_usec_t max_sleep_usec, process_usec;
+ size_t left_to_record;
+
+ pa_assert(u);
+ pa_source_assert_ref(u->source);
+
+ if (u->use_tsched)
+ hw_sleep_time(u, &max_sleep_usec, &process_usec);
+
+ for (;;) {
+ snd_pcm_sframes_t n;
+ int r;
+
+ snd_pcm_hwsync(u->pcm_handle);
+
+ if (PA_UNLIKELY((n = snd_pcm_avail_update(u->pcm_handle)) < 0)) {
+
+ if ((r = try_recover(u, "snd_pcm_avail_update", n)) == 0)
continue;
+
+ return r;
+ }
+
+ left_to_record = check_left_to_record(u, n);
+
+ if (u->use_tsched)
+ if (pa_bytes_to_usec(left_to_record, &u->source->sample_spec) > process_usec+max_sleep_usec/2)
+ break;
+
+ if (PA_UNLIKELY(n <= 0))
+ return work_done;
+
+ for (;;) {
+ void *p;
+ snd_pcm_sframes_t frames;
+ pa_memchunk chunk;
+
+ chunk.memblock = pa_memblock_new(u->core->mempool, (size_t) -1);
+
+ frames = pa_memblock_get_length(chunk.memblock) / u->frame_size;
+
+ if (frames > n)
+ frames = n;
+
+/* pa_log_debug("%lu frames to read", (unsigned long) n); */
+
+ p = pa_memblock_acquire(chunk.memblock);
+ frames = snd_pcm_readi(u->pcm_handle, (uint8_t*) p, frames);
+ pa_memblock_release(chunk.memblock);
+
+ pa_assert(frames != 0);
+
+ if (PA_UNLIKELY(frames < 0)) {
+ pa_memblock_unref(chunk.memblock);
+
+ if ((r = try_recover(u, "snd_pcm_readi", n)) == 0)
+ continue;
+
+ return r;
}
- pa_log("snd_pcm_readi() failed: %s", snd_strerror(-frames));
+ chunk.index = 0;
+ chunk.length = frames * u->frame_size;
+
+ pa_source_post(u->source, &chunk);
+ pa_memblock_unref(chunk.memblock);
+
+ work_done = 1;
+
+ u->frame_index += frames;
- clear_up(u);
- pa_module_unload_request(u->module);
- return;
+/* pa_log_debug("read %lu frames", (unsigned long) frames); */
+
+ if (frames >= n)
+ break;
+
+ n -= frames;
}
- pa_memblock_release(u->memchunk.memblock);
-
- l = frames * u->frame_size;
-
- post_memchunk = u->memchunk;
- post_memchunk.length = l;
-
- pa_source_post(u->source, &post_memchunk);
-
- u->memchunk.index += l;
- u->memchunk.length -= l;
-
- if (u->memchunk.length == 0) {
- pa_memblock_unref(u->memchunk.memblock);
- u->memchunk.memblock = NULL;
- u->memchunk.index = u->memchunk.length = 0;
+ }
+
+ *sleep_usec = pa_bytes_to_usec(left_to_record, &u->source->sample_spec) - process_usec;
+ return work_done;
+}
+
+static void update_smoother(struct userdata *u) {
+ snd_pcm_sframes_t delay = 0;
+ int64_t frames;
+ int err;
+ pa_usec_t now1, now2;
+
+ pa_assert(u);
+ pa_assert(u->pcm_handle);
+
+ /* Let's update the time smoother */
+
+ snd_pcm_hwsync(u->pcm_handle);
+ snd_pcm_avail_update(u->pcm_handle);
+
+ if (PA_UNLIKELY((err = snd_pcm_delay(u->pcm_handle, &delay)) < 0)) {
+ pa_log_warn("Failed to get delay: %s", snd_strerror(err));
+ return;
+ }
+
+ frames = u->frame_index + delay;
+
+ now1 = pa_rtclock_usec();
+ now2 = pa_bytes_to_usec(frames * u->frame_size, &u->source->sample_spec);
+
+ pa_smoother_put(u->smoother, now1, now2);
+}
+
+static pa_usec_t source_get_latency(struct userdata *u) {
+ pa_usec_t r = 0;
+ int64_t delay;
+ pa_usec_t now1, now2;
+
+ pa_assert(u);
+
+ now1 = pa_rtclock_usec();
+ now2 = pa_smoother_get(u->smoother, now1);
+
+ delay = (int64_t) now2 - pa_bytes_to_usec(u->frame_index * u->frame_size, &u->source->sample_spec);
+
+ if (delay > 0)
+ r = (pa_usec_t) delay;
+
+ return r;
+}
+
+static int build_pollfd(struct userdata *u) {
+ pa_assert(u);
+ pa_assert(u->pcm_handle);
+
+ if (u->alsa_rtpoll_item)
+ pa_rtpoll_item_free(u->alsa_rtpoll_item);
+
+ if (!(u->alsa_rtpoll_item = pa_alsa_build_pollfd(u->pcm_handle, u->rtpoll)))
+ return -1;
+
+ return 0;
+}
+
+static int suspend(struct userdata *u) {
+ pa_assert(u);
+ pa_assert(u->pcm_handle);
+
+ pa_smoother_pause(u->smoother, pa_rtclock_usec());
+
+ /* Let's suspend */
+ snd_pcm_close(u->pcm_handle);
+ u->pcm_handle = NULL;
+
+ if (u->alsa_rtpoll_item) {
+ pa_rtpoll_item_free(u->alsa_rtpoll_item);
+ u->alsa_rtpoll_item = NULL;
+ }
+
+ pa_log_info("Device suspended...");
+
+ return 0;
+}
+
+static int update_sw_params(struct userdata *u) {
+ snd_pcm_uframes_t avail_min;
+ int err;
+
+ pa_assert(u);
+
+ /* Use the full buffer if noone asked us for anything specific */
+ u->hwbuf_unused_frames = 0;
+
+ if (u->use_tsched) {
+ pa_usec_t latency;
+
+ if ((latency = pa_source_get_requested_latency_within_thread(u->source)) != (pa_usec_t) -1) {
+ size_t b;
+
+ pa_log_debug("latency set to %0.2f", (double) latency / PA_USEC_PER_MSEC);
+
+ b = pa_usec_to_bytes(latency, &u->source->sample_spec);
+
+ /* We need at least one sample in our buffer */
+
+ if (PA_UNLIKELY(b < u->frame_size))
+ b = u->frame_size;
+
+ u->hwbuf_unused_frames =
+ PA_LIKELY(b < u->hwbuf_size) ?
+ ((u->hwbuf_size - b) / u->frame_size) : 0;
+
+ fix_tsched_watermark(u);
}
-
- break;
}
+
+ pa_log_debug("hwbuf_unused_frames=%lu", (unsigned long) u->hwbuf_unused_frames);
+
+ avail_min = 1;
+
+ if (u->use_tsched) {
+ pa_usec_t sleep_usec, process_usec;
+
+ hw_sleep_time(u, &sleep_usec, &process_usec);
+ avail_min += pa_usec_to_bytes(sleep_usec, &u->source->sample_spec);
+ }
+
+ pa_log_debug("setting avail_min=%lu", (unsigned long) avail_min);
+
+ if ((err = pa_alsa_set_sw_params(u->pcm_handle, avail_min)) < 0) {
+ pa_log("Failed to set software parameters: %s", snd_strerror(err));
+ return err;
+ }
+
+ return 0;
}
-static void fdl_callback(void *userdata) {
- struct userdata *u = userdata;
- assert(u);
+static int unsuspend(struct userdata *u) {
+ pa_sample_spec ss;
+ int err;
+ pa_bool_t b, d;
+ unsigned nfrags;
+ snd_pcm_uframes_t period_size;
+
+ 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;
+ pa_log_info("Trying resume...");
+
+ 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;
+ }
+
+ ss = u->source->sample_spec;
+ nfrags = u->nfragments;
+ period_size = u->fragment_size / u->frame_size;
+ b = u->use_mmap;
+ d = u->use_tsched;
+
+ if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &nfrags, &period_size, u->hwbuf_size / u->frame_size, &b, &d, TRUE)) < 0) {
+ pa_log("Failed to set hardware parameters: %s", snd_strerror(err));
+ goto fail;
+ }
+
+ if (b != u->use_mmap || d != u->use_tsched) {
+ 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 (update_sw_params(u) < 0)
+ goto fail;
+
+ if (build_pollfd(u) < 0)
+ goto fail;
+
+ /* FIXME: We need to reload the volume somehow */
+
+ snd_pcm_start(u->pcm_handle);
+ pa_smoother_resume(u->smoother, pa_rtclock_usec());
- do_read(u);
+ 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 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_IS_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;
+
+ 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);
- assert(u && u->mixer_handle);
+ pa_assert(u);
+ pa_assert(u->mixer_handle);
if (mask == SND_CTL_EVENT_MASK_REMOVE)
return 0;
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);
-
- pa_subscription_post(u->source->core,
- PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE,
- u->source->index);
+ pa_source_get_volume(u->source);
+ pa_source_get_mute(u->source);
}
return 0;
}
-static pa_usec_t source_get_latency_cb(pa_source *s) {
+static int source_get_volume_cb(pa_source *s) {
struct userdata *u = s->userdata;
- snd_pcm_sframes_t frames;
- assert(s && u && u->source);
+ int err;
+ int i;
- if (snd_pcm_delay(u->pcm_handle, &frames) < 0) {
- pa_log("failed to get delay");
- s->get_latency = NULL;
- return 0;
- }
+ pa_assert(u);
+ pa_assert(u->mixer_elem);
- return pa_bytes_to_usec(frames * u->frame_size, &s->sample_spec);
-}
+ for (i = 0; i < s->sample_spec.channels; i++) {
+ long alsa_vol;
-static int source_get_hw_volume_cb(pa_source *s) {
- struct userdata *u = s->userdata;
- long vol;
- int err;
- int i;
+ pa_assert(snd_mixer_selem_has_capture_channel(u->mixer_elem, u->mixer_map[i]));
+
+ if (u->hw_dB_supported) {
+
+ if ((err = snd_mixer_selem_get_capture_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol)) >= 0) {
+ s->volume.values[i] = pa_sw_volume_from_dB(alsa_vol / 100.0);
+ continue;
+ }
- assert(u && u->mixer_elem);
+ u->hw_dB_supported = FALSE;
+ }
- for (i = 0;i < s->hw_volume.channels;i++) {
- long set_vol;
-
- 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)
+ if ((err = snd_mixer_selem_get_capture_volume(u->mixer_elem, u->mixer_map[i], &alsa_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;
-
- /* 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) (alsa_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;
+
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, u->mixer_map[i]));
- if (vol > PA_VOLUME_NORM)
- vol = PA_VOLUME_NORM;
+ vol = PA_MIN(s->volume.values[i], PA_VOLUME_NORM);
- vol = (long) roundf(((float) vol * (u->hw_volume_max - u->hw_volume_min)) / PA_VOLUME_NORM) + u->hw_volume_min;
+ if (u->hw_dB_supported) {
+ alsa_vol = (long) (pa_sw_volume_to_dB(vol) * 100);
+ alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_dB_min, u->hw_dB_max);
- if ((err = snd_mixer_selem_set_capture_volume(u->mixer_elem, i, vol)) < 0)
+
+ if ((err = snd_mixer_selem_set_capture_dB(u->mixer_elem, u->mixer_map[i], alsa_vol, -1)) >= 0) {
+
+ if (snd_mixer_selem_get_capture_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol) >= 0)
+ s->volume.values[i] = pa_sw_volume_from_dB(alsa_vol / 100.0);
+
+ continue;
+ }
+
+ u->hw_dB_supported = FALSE;
+ }
+
+ alsa_vol = (long) roundf(((float) vol * (u->hw_volume_max - u->hw_volume_min)) / PA_VOLUME_NORM) + u->hw_volume_min;
+ alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_volume_min, u->hw_volume_max);
+
+ if ((err = snd_mixer_selem_set_capture_volume(u->mixer_elem, u->mixer_map[i], alsa_vol)) < 0)
goto fail;
+
+ if (snd_mixer_selem_get_capture_volume(u->mixer_elem, u->mixer_map[i], &alsa_vol) >= 0)
+ s->volume.values[i] = (pa_volume_t) roundf(((float) (alsa_vol - u->hw_volume_min) * PA_VOLUME_NORM) / (u->hw_volume_max - u->hw_volume_min));
}
return 0;
fail:
pa_log_error("Unable to set volume: %s", snd_strerror(err));
- s->get_hw_volume = NULL;
- s->set_hw_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;
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;
return -1;
}
return 0;
}
-int pa__init(pa_core *c, pa_module*m) {
+static void source_update_requested_latency_cb(pa_source *s) {
+ struct userdata *u = s->userdata;
+ pa_assert(u);
+
+ if (!u->pcm_handle)
+ return;
+
+ update_sw_params(u);
+}
+
+static void thread_func(void *userdata) {
+ struct userdata *u = userdata;
+
+ pa_assert(u);
+
+ pa_log_debug("Thread starting up");
+
+ if (u->core->realtime_scheduling)
+ pa_make_realtime(u->core->realtime_priority);
+
+ pa_thread_mq_install(&u->thread_mq);
+ pa_rtpoll_install(u->rtpoll);
+
+ for (;;) {
+ int ret;
+
+/* pa_log_debug("loop"); */
+
+ /* Read some data and pass it to the sources */
+ if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
+ int work_done = 0;
+ pa_usec_t sleep_usec;
+
+ if (u->use_mmap)
+ work_done = mmap_read(u, &sleep_usec);
+ else
+ work_done = unix_read(u, &sleep_usec);
+
+ if (work_done < 0)
+ goto fail;
+
+/* pa_log_debug("work_done = %i", work_done); */
+
+ if (work_done)
+ update_smoother(u);
+
+ if (u->use_tsched) {
+ pa_usec_t cusec;
+
+ /* OK, the capture buffer is now empty, let's
+ * calculate when to wake up next */
+
+/* pa_log_debug("Waking up in %0.2fms (sound card clock).", (double) sleep_usec / PA_USEC_PER_MSEC); */
+
+ /* Convert from the sound card time domain to the
+ * system time domain */
+ cusec = pa_smoother_translate(u->smoother, pa_rtclock_usec(), sleep_usec);
+
+/* pa_log_debug("Waking up in %0.2fms (system clock).", (double) cusec / PA_USEC_PER_MSEC); */
+
+ /* We don't trust the conversion, so we wake up whatever comes first */
+ pa_rtpoll_set_timer_relative(u->rtpoll, PA_MIN(sleep_usec, cusec));
+ }
+ } else if (u->use_tsched)
+
+ /* OK, we're in an invalid state, let's disable our timers */
+ 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;
+
+ /* Tell ALSA about this and process its response */
+ if (PA_SOURCE_IS_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 (pa_alsa_recover_from_poll(u->pcm_handle, revents) < 0)
+ goto fail;
+
+ snd_pcm_start(u->pcm_handle);
+ }
+
+ if (revents && u->use_tsched)
+ pa_log_debug("Wakeup from ALSA! (%i)", revents);
+ }
+ }
+
+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;
+ const char *dev_id;
pa_sample_spec ss;
pa_channel_map map;
- unsigned periods, fragsize;
- snd_pcm_uframes_t period_size;
+ uint32_t nfrags, hwbuf_size, frag_size, tsched_size, tsched_watermark;
+ snd_pcm_uframes_t period_frames, tsched_frames;
size_t frame_size;
snd_pcm_info_t *pcm_info = NULL;
int err;
- char *t;
const char *name;
char *name_buf = NULL;
- int namereg_fail;
-
+ pa_bool_t namereg_fail;
+ pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, mixer_reset = TRUE;
+ pa_source_new_data data;
+
+ snd_pcm_info_alloca(&pcm_info);
+
+ pa_assert(m);
+
+ pa_alsa_redirect_errors_inc();
+
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;
-
- 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");
+ nfrags = m->core->default_n_fragments;
+ frag_size = pa_usec_to_bytes(m->core->default_fragment_size_msec*PA_USEC_PER_MSEC, &ss);
+ if (frag_size <= 0)
+ frag_size = frame_size;
+ tsched_size = pa_usec_to_bytes(DEFAULT_TSCHED_BUFFER_USEC, &ss);
+ tsched_watermark = pa_usec_to_bytes(DEFAULT_TSCHED_WATERMARK_USEC, &ss);
+
+ if (pa_modargs_get_value_u32(ma, "fragments", &nfrags) < 0 ||
+ pa_modargs_get_value_u32(ma, "fragment_size", &frag_size) < 0 ||
+ pa_modargs_get_value_u32(ma, "tsched_buffer_size", &tsched_size) < 0 ||
+ pa_modargs_get_value_u32(ma, "tsched_buffer_watermark", &tsched_watermark) < 0) {
+ pa_log("Failed to parse buffer metrics");
goto fail;
}
- period_size = fragsize/frame_size;
-
- u = pa_xnew0(struct userdata, 1);
- m->userdata = u;
- u->module = m;
-
- 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));
+
+ hwbuf_size = frag_size * nfrags;
+ period_frames = frag_size/frame_size;
+ tsched_frames = tsched_size/frame_size;
+
+ if (pa_modargs_get_value_boolean(ma, "mmap", &use_mmap) < 0) {
+ pa_log("Failed to parse mmap argument.");
goto fail;
}
- if ((err = snd_pcm_info_malloc(&pcm_info)) < 0 ||
- (err = snd_pcm_info(u->pcm_handle, pcm_info)) < 0) {
- pa_log("Error fetching PCM info: %s", snd_strerror(err));
+ if (pa_modargs_get_value_boolean(ma, "tsched", &use_tsched) < 0) {
+ pa_log("Failed to parse timer_scheduling argument.");
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 (use_tsched && !pa_rtclock_hrtimer()) {
+ pa_log("Disabling timer-based scheduling because high-resolution timers are not available from the kernel.");
+ use_tsched = FALSE;
+ }
+
+ if (pa_modargs_get_value_boolean(ma, "mixer_reset", &mixer_reset) < 0) {
+ pa_log("Failed to parse mixer_reset argument.");
goto fail;
}
- 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);
+ u = pa_xnew0(struct userdata, 1);
+ u->core = m->core;
+ u->module = m;
+ m->userdata = u;
+ u->use_mmap = use_mmap;
+ u->use_tsched = use_tsched;
+ u->rtpoll = pa_rtpoll_new();
+ pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
+ u->alsa_rtpoll_item = NULL;
+
+ u->smoother = pa_smoother_new(DEFAULT_TSCHED_WATERMARK_USEC, DEFAULT_TSCHED_WATERMARK_USEC, TRUE, 5);
+ pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec());
- if ((err = snd_mixer_open(&u->mixer_handle, 0)) < 0) {
- pa_log("Error opening mixer: %s", snd_strerror(err));
+ snd_config_update_free_global();
+
+ b = use_mmap;
+ d = use_tsched;
+
+ if ((dev_id = pa_modargs_get_value(ma, "device_id", NULL))) {
+
+ if (!(u->pcm_handle = pa_alsa_open_by_device_id(
+ dev_id,
+ &u->device_name,
+ &ss, &map,
+ SND_PCM_STREAM_CAPTURE,
+ &nfrags, &period_frames, tsched_frames,
+ &b, &d)))
+ goto fail;
+
+ } else {
+
+ if (!(u->pcm_handle = pa_alsa_open_by_device_string(
+ pa_modargs_get_value(ma, "device", DEFAULT_DEVICE),
+ &u->device_name,
+ &ss, &map,
+ SND_PCM_STREAM_CAPTURE,
+ &nfrags, &period_frames, tsched_frames,
+ &b, &d)))
+ goto fail;
+ }
+
+ pa_assert(u->device_name);
+ pa_log_info("Successfully opened device %s.", u->device_name);
+
+ if (use_mmap && !b) {
+ pa_log_info("Device doesn't support mmap(), falling back to UNIX read/write mode.");
+ u->use_mmap = use_mmap = FALSE;
+ }
+
+ if (use_tsched && (!b || !d)) {
+ pa_log_info("Cannot enabled timer-based scheduling, falling back to sound IRQ scheduling.");
+ u->use_tsched = use_tsched = FALSE;
+ }
+
+ if (u->use_mmap)
+ pa_log_info("Successfully enabled mmap() mode.");
+
+ if (u->use_tsched)
+ pa_log_info("Successfully enabled timer-based scheduling 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 ((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;
+ /* ALSA might tweak the sample spec, so recalculate the frame size */
+ frame_size = pa_frame_size(&ss);
+
+ if ((err = snd_mixer_open(&u->mixer_handle, 0)) < 0)
+ pa_log("Error opening mixer: %s", snd_strerror(err));
+ else {
+ pa_bool_t found = FALSE;
+
+ if (pa_alsa_prepare_mixer(u->mixer_handle, u->device_name) >= 0)
+ found = TRUE;
+ else {
+ snd_pcm_info_t* info;
+
+ snd_pcm_info_alloca(&info);
+
+ if (snd_pcm_info(u->pcm_handle, info) >= 0) {
+ char *md;
+ int card;
+
+ if ((card = snd_pcm_info_get_card(info)) >= 0) {
+
+ md = pa_sprintf_malloc("hw:%i", card);
+
+ if (strcmp(u->device_name, md))
+ if (pa_alsa_prepare_mixer(u->mixer_handle, md) >= 0)
+ found = TRUE;
+ pa_xfree(md);
+ }
+ }
+ }
+
+ if (found)
+ if (!(u->mixer_elem = pa_alsa_find_elem(u->mixer_handle, "Capture", "Mic")))
+ found = FALSE;
+
+ if (!found) {
+ snd_mixer_close(u->mixer_handle);
+ u->mixer_handle = NULL;
+ }
}
if ((name = pa_modargs_get_value(ma, "source_name", NULL)))
- namereg_fail = 1;
+ namereg_fail = TRUE;
else {
- name = name_buf = pa_sprintf_malloc("alsa_input.%s", dev);
- namereg_fail = 0;
+ name = name_buf = pa_sprintf_malloc("alsa_input.%s", u->device_name);
+ namereg_fail = FALSE;
}
-
- if (!(u->source = pa_source_new(c, __FILE__, name, namereg_fail, &ss, &map))) {
+
+ pa_source_new_data_init(&data);
+ data.driver = __FILE__;
+ data.module = m;
+ pa_source_new_data_set_name(&data, name);
+ data.namereg_fail = namereg_fail;
+ pa_source_new_data_set_sample_spec(&data, &ss);
+ pa_source_new_data_set_channel_map(&data, &map);
+
+ pa_alsa_init_proplist(data.proplist, pcm_info);
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_name);
+ pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (period_frames * frame_size * nfrags));
+ pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (period_frames * frame_size));
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, u->use_tsched ? "mmap+timer" : (u->use_mmap ? "mmap" : "serial"));
+
+ u->source = pa_source_new(m->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY);
+ pa_source_new_data_done(&data);
+ 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->update_requested_latency = source_update_requested_latency_cb;
u->source->userdata = u;
- u->source->get_latency = source_get_latency_cb;
+
+ pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
+ pa_source_set_rtpoll(u->source, u->rtpoll);
+
+ u->frame_size = frame_size;
+ u->fragment_size = frag_size = period_frames * frame_size;
+ u->nfragments = nfrags;
+ u->hwbuf_size = u->fragment_size * nfrags;
+ u->hwbuf_unused_frames = 0;
+ u->tsched_watermark = tsched_watermark;
+ u->frame_index = 0;
+ u->hw_dB_supported = FALSE;
+ u->hw_dB_min = u->hw_dB_max = 0;
+ u->hw_volume_min = u->hw_volume_max = 0;
+
+ if (use_tsched)
+ fix_tsched_watermark(u);
+
+ pa_source_set_latency_range(u->source,
+ !use_tsched ? pa_bytes_to_usec(u->hwbuf_size, &ss) : (pa_usec_t) -1,
+ pa_bytes_to_usec(u->hwbuf_size, &ss));
+
+ pa_log_info("Using %u fragments of size %lu bytes, buffer time is %0.2fms",
+ nfrags, (long unsigned) u->fragment_size,
+ (double) pa_bytes_to_usec(u->hwbuf_size, &ss) / PA_USEC_PER_MSEC);
+
+ if (use_tsched)
+ pa_log_info("Time scheduling watermark is %0.2fms",
+ (double) pa_bytes_to_usec(u->tsched_watermark, &ss) / PA_USEC_PER_MSEC);
+
+ if (update_sw_params(u) < 0)
+ goto fail;
+
if (u->mixer_handle) {
- assert(u->mixer_elem);
- if (snd_mixer_selem_has_capture_volume(u->mixer_elem)) {
- int i;
+ pa_assert(u->mixer_elem);
- for (i = 0;i < ss.channels;i++) {
- if (!snd_mixer_selem_has_capture_channel(u->mixer_elem, i))
- break;
- }
+ if (snd_mixer_selem_has_capture_volume(u->mixer_elem))
+ if (pa_alsa_calc_mixer_map(u->mixer_elem, &map, u->mixer_map, FALSE) >= 0 &&
+ snd_mixer_selem_get_capture_volume_range(u->mixer_elem, &u->hw_volume_min, &u->hw_volume_max) >= 0) {
+
+ pa_bool_t suitable = TRUE;
+
+ pa_log_info("Volume ranges from %li to %li.", u->hw_volume_min, u->hw_volume_max);
+
+ if (u->hw_volume_min > u->hw_volume_max) {
+
+ pa_log_info("Minimal volume %li larger than maximum volume %li. Strange stuff Falling back to software volume control.", u->hw_volume_min, u->hw_volume_max);
+ suitable = FALSE;
+
+ } else if (u->hw_volume_max - u->hw_volume_min < 3) {
+
+ pa_log_info("Device has less than 4 volume levels. Falling back to software volume control.");
+ suitable = FALSE;
+
+ } else if (snd_mixer_selem_get_capture_dB_range(u->mixer_elem, &u->hw_dB_min, &u->hw_dB_max) >= 0) {
+
+ pa_log_info("Volume ranges from %0.2f dB to %0.2f dB.", u->hw_dB_min/100.0, u->hw_dB_max/100.0);
+
+ /* Let's see if this thing actually is useful for muting */
+ if (u->hw_dB_min > -6000) {
+ pa_log_info("Device cannot attenuate for more than -60 dB (only %0.2f dB supported), falling back to software volume control.", ((double) u->hw_dB_min) / 100);
+
+ suitable = FALSE;
+ } else if (u->hw_dB_max < 0) {
+
+ pa_log_info("Device is still attenuated at maximum volume setting (%0.2f dB is maximum). Strange stuff. Falling back to software volume control.", ((double) u->hw_dB_max) / 100);
+ suitable = FALSE;
+
+ } else if (u->hw_dB_min >= u->hw_dB_max) {
+
+ pa_log_info("Minimal dB (%0.2f) larger or equal to maximum dB (%0.2f). Strange stuff. Falling back to software volume control.", ((double) u->hw_dB_min) / 100, ((double) u->hw_dB_max) / 100);
+ suitable = FALSE;
+
+ } else
+ u->hw_dB_supported = TRUE;
+ }
+
+ if (suitable) {
+ u->source->get_volume = source_get_volume_cb;
+ u->source->set_volume = source_set_volume_cb;
+ u->source->flags |= PA_SOURCE_HW_VOLUME_CTRL | (u->hw_dB_supported ? PA_SOURCE_DECIBEL_VOLUME : 0);
+ pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->hw_dB_supported ? "supported" : "not supported");
+
+ } else if (mixer_reset) {
+ pa_log_info("Using software volume control. Trying to reset sound card to 0 dB.");
+ pa_alsa_0dB_capture(u->mixer_elem);
+ } else
+ pa_log_info("Using software volume control. Leaving hw mixer controls untouched.");
- 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);
}
- }
+
+
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;
+ u->source->flags |= PA_SOURCE_HW_MUTE_CTRL;
}
- }
- 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) {
- pa_log("failed to initialise file descriptor monitoring");
+
+ if (pa_alsa_fdlist_set_mixer(u->mixer_fdl, u->mixer_handle, m->core->mainloop) < 0) {
+ pa_log("Failed to initialize 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_alsa_dump(u->pcm_handle);
- pa_log_info("using %u fragments of size %lu bytes.", periods, (long unsigned) u->fragment_size);
+ if (!(u->thread = pa_thread_new(thread_func, u))) {
+ pa_log("Failed to create thread.");
+ goto fail;
+ }
+ /* Get initial mixer settings */
+ if (data.volume_is_set) {
+ if (u->source->set_volume)
+ u->source->set_volume(u->source);
+ } else {
+ if (u->source->get_volume)
+ u->source->get_volume(u->source);
+ }
- u->memchunk.memblock = NULL;
- u->memchunk.index = u->memchunk.length = 0;
+ if (data.muted_is_set) {
+ if (u->source->set_mute)
+ u->source->set_mute(u->source);
+ } else {
+ if (u->source->get_mute)
+ u->source->get_mute(u->source);
+ }
- snd_pcm_start(u->pcm_handle);
-
- ret = 0;
+ pa_source_put(u->source);
- /* 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);
+ pa_modargs_free(ma);
-finish:
- pa_xfree(name_buf);
+ return 0;
- if (ma)
- pa_modargs_free(ma);
+fail:
- if (pcm_info)
- snd_pcm_info_free(pcm_info);
-
- return ret;
+ if (ma)
+ pa_modargs_free(ma);
-fail:
-
- if (u)
- pa__done(c, m);
+ pa__done(m);
- goto finish;
+ return -1;
}
-void pa__done(pa_core *c, pa_module*m) {
+void pa__done(pa_module*m) {
struct userdata *u;
- assert(c && m);
- if (!(u = m->userdata))
+ pa_assert(m);
+
+ if (!(u = m->userdata)) {
+ pa_alsa_redirect_errors_dec();
return;
+ }
+
+ 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->alsa_rtpoll_item)
+ pa_rtpoll_item_free(u->alsa_rtpoll_item);
+
+ if (u->rtpoll)
+ pa_rtpoll_free(u->rtpoll);
- clear_up(u);
-
- if (u->memchunk.memblock)
- pa_memblock_unref(u->memchunk.memblock);
-
+ 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);
+ }
+
+ if (u->smoother)
+ pa_smoother_free(u->smoother);
+
+ pa_xfree(u->device_name);
pa_xfree(u);
-}
+ snd_config_update_free_global();
+ pa_alsa_redirect_errors_dec();
+}
diff --git a/src/modules/module-always-sink.c b/src/modules/module-always-sink.c
new file mode 100644
index 00000000..8b67a36d
--- /dev/null
+++ b/src/modules/module-always-sink.c
@@ -0,0 +1,178 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2008 Colin Guthrie
+
+ 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.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/core-util.h>
+
+#include "module-always-sink-symdef.h"
+
+PA_MODULE_AUTHOR("Colin Guthrie");
+PA_MODULE_DESCRIPTION("Always keeps at least one sink loaded even if it's a null one");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+PA_MODULE_USAGE(
+ "sink_name=<name of sink>");
+
+#define DEFAULT_SINK_NAME "auto_null"
+
+static const char* const valid_modargs[] = {
+ "sink_name",
+ NULL,
+};
+
+struct userdata {
+ pa_hook_slot *put_slot, *unlink_slot;
+ pa_module* null_module;
+ pa_bool_t ignore;
+ char *sink_name;
+};
+
+static void load_null_sink_if_needed(pa_core *c, pa_sink *sink, struct userdata* u) {
+ pa_sink *target;
+ uint32_t idx;
+ char *t;
+
+ pa_assert(c);
+ pa_assert(u);
+ pa_assert(!u->null_module);
+
+ /* Loop through all sinks and check to see if we have *any*
+ * sinks. Ignore the sink passed in (if it's not null) */
+ for (target = pa_idxset_first(c->sinks, &idx); target; target = pa_idxset_next(c->sinks, &idx))
+ if (!sink || target != sink)
+ break;
+
+ if (target)
+ return;
+
+ pa_log_debug("Autoloading null-sink as no other sinks detected.");
+
+ u->ignore = TRUE;
+
+ t = pa_sprintf_malloc("sink_name=%s", u->sink_name);
+ u->null_module = pa_module_load(c, "module-null-sink", t);
+ pa_xfree(t);
+
+ u->ignore = FALSE;
+
+ if (!u->null_module)
+ pa_log_warn("Unable to load module-null-sink");
+}
+
+static pa_hook_result_t put_hook_callback(pa_core *c, pa_sink *sink, void* userdata) {
+ struct userdata *u = userdata;
+
+ pa_assert(c);
+ pa_assert(sink);
+ pa_assert(u);
+
+ /* This is us detecting ourselves on load... just ignore this. */
+ if (u->ignore)
+ return PA_HOOK_OK;
+
+ /* Auto-loaded null-sink not active, so ignoring newly detected sink. */
+ if (!u->null_module)
+ return PA_HOOK_OK;
+
+ /* This is us detecting ourselves on load in a different way... just ignore this too. */
+ if (sink->module == u->null_module)
+ return PA_HOOK_OK;
+
+ pa_log_info("A new sink has been discovered. Unloading null-sink.");
+
+ pa_module_unload_request(u->null_module);
+ u->null_module = NULL;
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t unlink_hook_callback(pa_core *c, pa_sink *sink, void* userdata) {
+ struct userdata *u = userdata;
+
+ pa_assert(c);
+ pa_assert(sink);
+ pa_assert(u);
+
+ /* First check to see if it's our own null-sink that's been removed... */
+ if (u->null_module && sink->module == u->null_module) {
+ pa_log_debug("Autoloaded null-sink removed");
+ u->null_module = NULL;
+ return PA_HOOK_OK;
+ }
+
+ load_null_sink_if_needed(c, sink, u);
+
+ return PA_HOOK_OK;
+}
+
+int pa__init(pa_module*m) {
+ pa_modargs *ma = NULL;
+ struct userdata *u;
+
+ pa_assert(m);
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log("Failed to parse module arguments");
+ return -1;
+ }
+
+ m->userdata = u = pa_xnew(struct userdata, 1);
+ u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
+ u->put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE, (pa_hook_cb_t) put_hook_callback, u);
+ u->unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_EARLY, (pa_hook_cb_t) unlink_hook_callback, u);
+ u->null_module = NULL;
+ u->ignore = FALSE;
+
+ pa_modargs_free(ma);
+
+ load_null_sink_if_needed(m->core, NULL, u);
+
+ return 0;
+}
+
+void pa__done(pa_module*m) {
+ struct userdata *u;
+
+ pa_assert(m);
+
+ if (!(u = m->userdata))
+ return;
+
+ if (u->put_slot)
+ pa_hook_slot_free(u->put_slot);
+ if (u->unlink_slot)
+ pa_hook_slot_free(u->unlink_slot);
+ if (u->null_module)
+ pa_module_unload_request(u->null_module);
+
+ pa_xfree(u->sink_name);
+ pa_xfree(u);
+}
diff --git a/src/modules/module-bt-proximity.c b/src/modules/module-bt-proximity.c
new file mode 100644
index 00000000..77b95868
--- /dev/null
+++ b/src/modules/module-bt-proximity.c
@@ -0,0 +1,490 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2005-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 <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/module.h>
+#include <pulsecore/log.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/start-child.h>
+
+#include "dbus-util.h"
+#include "module-bt-proximity-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Bluetooth Proximity Volume Control");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+PA_MODULE_USAGE(
+ "sink=<sink name> "
+ "hci=<hci device> "
+);
+
+#define DEFAULT_HCI "hci0"
+
+static const char* const valid_modargs[] = {
+ "sink",
+ "rssi",
+ "hci",
+ NULL,
+};
+
+struct bonding {
+ struct userdata *userdata;
+ char address[18];
+
+ pid_t pid;
+ int fd;
+
+ pa_io_event *io_event;
+
+ enum {
+ UNKNOWN,
+ FOUND,
+ NOT_FOUND
+ } state;
+};
+
+struct userdata {
+ pa_module *module;
+ pa_dbus_connection *dbus_connection;
+
+ char *sink_name;
+ char *hci, *hci_path;
+
+ pa_hashmap *bondings;
+
+ unsigned n_found;
+ unsigned n_unknown;
+
+ pa_bool_t muted;
+};
+
+static void update_volume(struct userdata *u) {
+ pa_assert(u);
+
+ if (u->muted && u->n_found > 0) {
+ pa_sink *s;
+
+ u->muted = FALSE;
+
+ if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, FALSE))) {
+ pa_log_warn("Sink device '%s' not available for unmuting.", pa_strnull(u->sink_name));
+ return;
+ }
+
+ pa_log_info("Found %u BT devices, unmuting.", u->n_found);
+ pa_sink_set_mute(s, FALSE);
+
+ } else if (!u->muted && (u->n_found+u->n_unknown) <= 0) {
+ pa_sink *s;
+
+ u->muted = TRUE;
+
+ if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, FALSE))) {
+ pa_log_warn("Sink device '%s' not available for muting.", pa_strnull(u->sink_name));
+ return;
+ }
+
+ pa_log_info("No BT devices found, muting.");
+ pa_sink_set_mute(s, TRUE);
+
+ } else
+ pa_log_info("%u devices now active, %u with unknown state.", u->n_found, u->n_unknown);
+}
+
+static void bonding_free(struct bonding *b) {
+ pa_assert(b);
+
+ if (b->state == FOUND)
+ pa_assert_se(b->userdata->n_found-- >= 1);
+
+ if (b->state == UNKNOWN)
+ pa_assert_se(b->userdata->n_unknown-- >= 1);
+
+ if (b->pid != (pid_t) -1) {
+ kill(b->pid, SIGTERM);
+ waitpid(b->pid, NULL, 0);
+ }
+
+ if (b->fd >= 0)
+ pa_close(b->fd);
+
+ if (b->io_event)
+ b->userdata->module->core->mainloop->io_free(b->io_event);
+
+ pa_xfree(b);
+}
+
+static void io_event_cb(
+ pa_mainloop_api*a,
+ pa_io_event* e,
+ int fd,
+ pa_io_event_flags_t events,
+ void *userdata) {
+
+ struct bonding *b = userdata;
+ char x;
+ ssize_t r;
+
+ pa_assert(b);
+
+ if ((r = read(fd, &x, 1)) <= 0) {
+ pa_log_warn("Child watching '%s' died abnormally: %s", b->address, r == 0 ? "EOF" : pa_cstrerror(errno));
+
+ pa_assert_se(pa_hashmap_remove(b->userdata->bondings, b->address) == b);
+ bonding_free(b);
+ return;
+ }
+
+ pa_assert_se(r == 1);
+
+ if (b->state == UNKNOWN)
+ pa_assert_se(b->userdata->n_unknown-- >= 1);
+
+ if (x == '+') {
+ pa_assert(b->state == UNKNOWN || b->state == NOT_FOUND);
+
+ b->state = FOUND;
+ b->userdata->n_found++;
+
+ pa_log_info("Device '%s' is alive.", b->address);
+
+ } else {
+ pa_assert(x == '-');
+ pa_assert(b->state == UNKNOWN || b->state == FOUND);
+
+ if (b->state == FOUND)
+ b->userdata->n_found--;
+
+ b->state = NOT_FOUND;
+
+ pa_log_info("Device '%s' is dead.", b->address);
+ }
+
+ update_volume(b->userdata);
+}
+
+static struct bonding* bonding_new(struct userdata *u, const char *a) {
+ struct bonding *b = NULL;
+ DBusMessage *m = NULL, *r = NULL;
+ DBusError e;
+ const char *class;
+
+ pa_assert(u);
+ pa_assert(a);
+
+ pa_return_val_if_fail(strlen(a) == 17, NULL);
+ pa_return_val_if_fail(!pa_hashmap_get(u->bondings, a), NULL);
+
+ dbus_error_init(&e);
+
+ pa_assert_se(m = dbus_message_new_method_call("org.bluez", u->hci_path, "org.bluez.Adapter", "GetRemoteMajorClass"));
+ pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_STRING, &a, DBUS_TYPE_INVALID));
+ r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->dbus_connection), m, -1, &e);
+
+ if (!r) {
+ pa_log("org.bluez.Adapter.GetRemoteMajorClass(%s) failed: %s", a, e.message);
+ goto fail;
+ }
+
+ if (!(dbus_message_get_args(r, &e, DBUS_TYPE_STRING, &class, DBUS_TYPE_INVALID))) {
+ pa_log("Malformed org.bluez.Adapter.GetRemoteMajorClass signal: %s", e.message);
+ goto fail;
+ }
+
+ if (strcmp(class, "phone")) {
+ pa_log_info("Found device '%s' of class '%s', ignoring.", a, class);
+ goto fail;
+ }
+
+ b = pa_xnew(struct bonding, 1);
+ b->userdata = u;
+ pa_strlcpy(b->address, a, sizeof(b->address));
+ b->pid = (pid_t) -1;
+ b->fd = -1;
+ b->io_event = NULL;
+ b->state = UNKNOWN;
+ u->n_unknown ++;
+
+ pa_log_info("Watching device '%s' of class '%s'.", b->address, class);
+
+ if ((b->fd = pa_start_child_for_read(PA_BT_PROXIMITY_HELPER, a, &b->pid)) < 0) {
+ pa_log("Failed to start helper tool.");
+ goto fail;
+ }
+
+ b->io_event = u->module->core->mainloop->io_new(
+ u->module->core->mainloop,
+ b->fd,
+ PA_IO_EVENT_INPUT,
+ io_event_cb,
+ b);
+
+ dbus_message_unref(m);
+ dbus_message_unref(r);
+
+ pa_hashmap_put(u->bondings, b->address, b);
+
+ return b;
+
+fail:
+ if (m)
+ dbus_message_unref(m);
+ if (r)
+ dbus_message_unref(r);
+
+ if (b)
+ bonding_free(b);
+
+ dbus_error_free(&e);
+ return NULL;
+}
+
+static void bonding_remove(struct userdata *u, const char *a) {
+ struct bonding *b;
+ pa_assert(u);
+
+ pa_return_if_fail((b = pa_hashmap_remove(u->bondings, a)));
+
+ pa_log_info("No longer watching device '%s'", b->address);
+ bonding_free(b);
+}
+
+static DBusHandlerResult filter_func(DBusConnection *connection, DBusMessage *m, void *userdata) {
+ struct userdata *u = userdata;
+ DBusError e;
+
+ dbus_error_init(&e);
+
+ if (dbus_message_is_signal(m, "org.bluez.Adapter", "BondingCreated")) {
+ const char *a;
+
+ if (!(dbus_message_get_args(m, &e, DBUS_TYPE_STRING, &a, DBUS_TYPE_INVALID))) {
+ pa_log("Malformed org.bluez.Adapter.BondingCreated signal: %s", e.message);
+ goto finish;
+ }
+
+ bonding_new(u, a);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+
+ } else if (dbus_message_is_signal(m, "org.bluez.Adapter", "BondingRemoved")) {
+
+ const char *a;
+
+ if (!(dbus_message_get_args(m, &e, DBUS_TYPE_STRING, &a, DBUS_TYPE_INVALID))) {
+ pa_log("Malformed org.bluez.Adapter.BondingRemoved signal: %s", e.message);
+ goto finish;
+ }
+
+ bonding_remove(u, a);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+finish:
+
+ dbus_error_free(&e);
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static int add_matches(struct userdata *u, pa_bool_t add) {
+ char *filter1, *filter2;
+ DBusError e;
+ int r = -1;
+
+ pa_assert(u);
+ dbus_error_init(&e);
+
+ filter1 = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='BondingCreated',path='%s'", u->hci_path);
+ filter2 = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='BondingRemoved',path='%s'", u->hci_path);
+
+ if (add) {
+ dbus_bus_add_match(pa_dbus_connection_get(u->dbus_connection), filter1, &e);
+
+ if (dbus_error_is_set(&e)) {
+ pa_log("dbus_bus_add_match(%s) failed: %s", filter1, e.message);
+ goto finish;
+ }
+ } else
+ dbus_bus_remove_match(pa_dbus_connection_get(u->dbus_connection), filter1, &e);
+
+
+ if (add) {
+ dbus_bus_add_match(pa_dbus_connection_get(u->dbus_connection), filter2, &e);
+
+ if (dbus_error_is_set(&e)) {
+ pa_log("dbus_bus_add_match(%s) failed: %s", filter2, e.message);
+ dbus_bus_remove_match(pa_dbus_connection_get(u->dbus_connection), filter2, &e);
+ goto finish;
+ }
+ } else
+ dbus_bus_remove_match(pa_dbus_connection_get(u->dbus_connection), filter2, &e);
+
+
+ if (add)
+ pa_assert_se(dbus_connection_add_filter(pa_dbus_connection_get(u->dbus_connection), filter_func, u, NULL));
+ else
+ dbus_connection_remove_filter(pa_dbus_connection_get(u->dbus_connection), filter_func, u);
+
+ r = 0;
+
+finish:
+ pa_xfree(filter1);
+ pa_xfree(filter2);
+ dbus_error_free(&e);
+
+ return r;
+}
+
+int pa__init(pa_module*m) {
+ pa_modargs *ma = NULL;
+ struct userdata *u;
+ DBusError e;
+ DBusMessage *msg = NULL, *r = NULL;
+ DBusMessageIter iter, sub;
+
+ pa_assert(m);
+ dbus_error_init(&e);
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log("Failed to parse module arguments");
+ goto fail;
+ }
+
+ m->userdata = u = pa_xnew0(struct userdata, 1);
+ u->module = m;
+ u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
+ u->hci = pa_xstrdup(pa_modargs_get_value(ma, "hci", DEFAULT_HCI));
+ u->hci_path = pa_sprintf_malloc("/org/bluez/%s", u->hci);
+ u->n_found = u->n_unknown = 0;
+ u->muted = FALSE;
+
+ u->bondings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+
+ if (!(u->dbus_connection = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &e))) {
+ pa_log("Failed to get D-Bus connection: %s", e.message);
+ goto fail;
+ }
+
+ if (add_matches(u, TRUE) < 0)
+ goto fail;
+
+ pa_assert_se(msg = dbus_message_new_method_call("org.bluez", u->hci_path, "org.bluez.Adapter", "ListBondings"));
+
+ if (!(r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->dbus_connection), msg, -1, &e))) {
+ pa_log("org.bluez.Adapter.ListBondings failed: %s", e.message);
+ goto fail;
+ }
+
+ dbus_message_iter_init(r, &iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+ pa_log("Malformed reply to org.bluez.Adapter.ListBondings.");
+ goto fail;
+ }
+
+ dbus_message_iter_recurse(&iter, &sub);
+
+ while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING) {
+ const char *a = NULL;
+
+ dbus_message_iter_get_basic(&sub, &a);
+ bonding_new(u, a);
+
+ dbus_message_iter_next(&sub);
+ }
+
+ dbus_message_unref(r);
+ dbus_message_unref(msg);
+
+ pa_modargs_free(ma);
+
+ if (pa_hashmap_size(u->bondings) == 0)
+ pa_log_warn("Warning: no phone device bonded.");
+
+ update_volume(u);
+
+ return 0;
+
+fail:
+
+ if (ma)
+ pa_modargs_free(ma);
+
+ pa__done(m);
+
+ dbus_error_free(&e);
+
+ if (msg)
+ dbus_message_unref(msg);
+
+ if (r)
+ dbus_message_unref(r);
+
+ return -1;
+}
+
+void pa__done(pa_module*m) {
+ struct userdata *u;
+ pa_assert(m);
+
+ if (!(u = m->userdata))
+ return;
+
+ if (u->bondings) {
+ struct bonding *b;
+
+ while ((b = pa_hashmap_steal_first(u->bondings)))
+ bonding_free(b);
+
+ pa_hashmap_free(u->bondings, NULL, NULL);
+ }
+
+ if (u->dbus_connection) {
+ add_matches(u, FALSE);
+ pa_dbus_connection_unref(u->dbus_connection);
+ }
+
+ pa_xfree(u->sink_name);
+ pa_xfree(u->hci_path);
+ pa_xfree(u->hci);
+ pa_xfree(u);
+}
diff --git a/src/modules/module-cli.c b/src/modules/module-cli.c
index d5374838..df7783fa 100644
--- a/src/modules/module-cli.c
+++ b/src/modules/module-cli.c
@@ -1,18 +1,18 @@
-/* $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
@@ -24,7 +24,6 @@
#endif
#include <stdio.h>
-#include <assert.h>
#include <unistd.h>
#include <pulsecore/module.h>
@@ -33,13 +32,15 @@
#include <pulsecore/sioman.h>
#include <pulsecore/log.h>
#include <pulsecore/modargs.h>
+#include <pulsecore/macro.h>
#include "module-cli-symdef.h"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("Command line interface")
-PA_MODULE_VERSION(PACKAGE_VERSION)
-PA_MODULE_USAGE("exit_on_eof=<exit daemon after EOF?>")
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Command line interface");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+PA_MODULE_USAGE("exit_on_eof=<exit daemon after EOF?>");
static const char* const valid_modargs[] = {
"exit_on_eof",
@@ -48,9 +49,9 @@ 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);
}
@@ -58,21 +59,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_bool_t exit_on_eof = FALSE;
+
+ 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;
}
@@ -81,7 +81,7 @@ int pa__init(pa_core *c, pa_module*m) {
pa_log("failed to parse module arguments.");
goto fail;
}
-
+
if (pa_modargs_get_value_boolean(ma, "exit_on_eof", &exit_on_eof) < 0) {
pa_log("exit_on_eof= expects boolean argument.");
goto fail;
@@ -92,17 +92,15 @@ 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);
pa_modargs_free(ma);
-
+
return 0;
fail:
@@ -113,11 +111,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 f3bb3fd3..cef7a99a 100644
--- a/src/modules/module-combine.c
+++ b/src/modules/module-combine.c
@@ -1,18 +1,18 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
+ Copyright 2004-2008 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
@@ -23,12 +23,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>
@@ -38,32 +39,40 @@
#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 <pulsecore/time-smoother.h>
#include "module-combine-symdef.h"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("Combine multiple sinks to one")
-PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Combine multiple sinks to one");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
PA_MODULE_USAGE(
"sink_name=<name for the sink> "
- "master=<master sink> "
"slaves=<slave sinks> "
"adjust_time=<seconds> "
"resample_method=<method> "
"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 MEMBLOCKQ_MAXLENGTH (1024*1024*16)
+
+#define DEFAULT_ADJUST_TIME 10
+
+#define REQUEST_LATENCY_USEC (PA_USEC_PER_MSEC * 200)
static const char* const valid_modargs[] = {
"sink_name",
- "master",
"slaves",
"adjust_time",
"resample_method",
@@ -76,95 +85,150 @@ 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_read, *inq_rtpoll_item_write;
+ pa_rtpoll_item *outq_rtpoll_item_read, *outq_rtpoll_item_write;
+
pa_memblockq *memblockq;
+
pa_usec_t total_latency;
+
+ pa_atomic_t max_request;
+
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;
+
+ pa_hook_slot *sink_put_slot, *sink_unlink_slot, *sink_state_changed_slot;
+
+ pa_resample_method_t resample_method;
+
+ struct timeval adjust_timestamp;
+
+ pa_usec_t block_usec;
+
+ 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 */
+ pa_usec_t timestamp;
+ pa_bool_t in_null_mode;
+ pa_smoother *smoother;
+ uint64_t counter;
+ } 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,
+ SINK_MESSAGE_UPDATE_LATENCY,
+ SINK_MESSAGE_UPDATE_MAX_REQUEST
+};
-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 adjust_rates(struct userdata *u) {
struct output *o;
- pa_usec_t max_sink_latency = 0, min_total_latency = (pa_usec_t) -1, target_latency;
+ pa_usec_t max_sink_latency = 0, min_total_latency = (pa_usec_t) -1, target_latency, avg_total_latency = 0;
uint32_t base_rate;
- assert(u && u->sink);
+ uint32_t idx;
+ unsigned n = 0;
+
+ pa_assert(u);
+ pa_sink_assert_ref(u->sink);
+
+ if (pa_idxset_size(u->outputs) <= 0)
+ return;
+
+ if (!PA_SINK_IS_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_IS_OPENED(pa_sink_get_state(o->sink)))
+ continue;
+
+ o->total_latency = pa_sink_input_get_latency(o->sink_input, &sink_latency);
+ o->total_latency += sink_latency;
- 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;
-
- 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;
+
+ avg_total_latency += o->total_latency;
+ n++;
}
- assert(min_total_latency != (pa_usec_t) -1);
+ if (min_total_latency == (pa_usec_t) -1)
+ return;
+
+ avg_total_latency /= n;
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] avg total latency is %0.2f msec.", u->sink->name, (double) avg_total_latency / PA_USEC_PER_MSEC);
+ pa_log_info("[%s] target latency is %0.2f msec.", u->sink->name, (double) target_latency / PA_USEC_PER_MSEC);
base_rate = u->sink->sample_spec.rate;
- for (o = u->outputs; o; o = o->next) {
- uint32_t r = base_rate;
-
+ 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_IS_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))
- pa_log_warn("[%s] sample rates too different, not adjusting (%u vs. %u).", o->sink_input->name, base_rate, r);
- 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);
+ 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).", pa_proplist_gets(o->sink_input->proplist, PA_PROP_MEDIA_NAME), base_rate, r);
+ 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.", pa_proplist_gets(o->sink_input->proplist, PA_PROP_MEDIA_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);
-
- update_usage(u);
-
- if (pa_sink_render(u->sink, RENDER_SIZE, &chunk) < 0)
- return;
- for (o = u->outputs; o; o = o->next)
- pa_memblockq_push_align(o->memblockq, &chunk);
-
- pa_memblock_unref(chunk.memblock);
+ pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_UPDATE_LATENCY, NULL, (int64_t) avg_total_latency, NULL);
}
-static void time_callback(pa_mainloop_api*a, pa_time_event* e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
+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;
- assert(u && a && u->time_event == e);
+
+ pa_assert(u);
+ pa_assert(a);
+ pa_assert(u->time_event == e);
adjust_rates(u);
@@ -173,73 +237,603 @@ static void time_callback(pa_mainloop_api*a, pa_time_event* e, PA_GCC_UNUSED con
u->sink->core->mainloop->time_restart(e, &n);
}
-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);
+static void process_render_null(struct userdata *u, pa_usec_t now) {
+ size_t ate = 0;
+ pa_assert(u);
- if (pa_memblockq_peek(o->memblockq, chunk) >= 0)
- return 0;
-
- /* Try harder */
- request_memblock(o->userdata);
-
- return pa_memblockq_peek(o->memblockq, chunk);
+ if (u->thread_info.in_null_mode)
+ u->thread_info.timestamp = now;
+
+ while (u->thread_info.timestamp < now + u->block_usec) {
+ pa_memchunk chunk;
+
+ pa_sink_render(u->sink, u->sink->thread_info.max_request, &chunk);
+ pa_memblock_unref(chunk.memblock);
+
+ u->thread_info.counter += chunk.length;
+
+/* pa_log_debug("Ate %lu bytes.", (unsigned long) chunk.length); */
+ u->thread_info.timestamp += pa_bytes_to_usec(chunk.length, &u->sink->sample_spec);
+
+ ate += chunk.length;
+
+ if (ate >= u->sink->thread_info.max_request)
+ break;
+ }
+
+/* pa_log_debug("Ate in sum %lu bytes (of %lu)", (unsigned long) ate, (unsigned long) nbytes); */
+
+ pa_smoother_put(u->thread_info.smoother, now,
+ pa_bytes_to_usec(u->thread_info.counter, &u->sink->sample_spec) - (u->thread_info.timestamp - now));
+}
+
+static void thread_func(void *userdata) {
+ struct userdata *u = userdata;
+
+ pa_assert(u);
+
+ pa_log_debug("Thread starting up");
+
+ if (u->core->realtime_scheduling)
+ pa_make_realtime(u->core->realtime_priority+1);
+
+ pa_thread_mq_install(&u->thread_mq);
+ pa_rtpoll_install(u->rtpoll);
+
+ u->thread_info.timestamp = pa_rtclock_usec();
+ u->thread_info.in_null_mode = FALSE;
+
+ for (;;) {
+ int ret;
+
+ /* If no outputs are connected, render some data and drop it immediately. */
+ if (PA_SINK_IS_OPENED(u->sink->thread_info.state) && !u->thread_info.active_outputs) {
+ pa_usec_t now;
+
+ now = pa_rtclock_usec();
+
+ if (!u->thread_info.in_null_mode || u->thread_info.timestamp <= now)
+ process_render_null(u, now);
+
+ 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
+ * 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;
+
+ /* 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);
+
+ u->thread_info.counter += chunk.length;
+
+ /* OK, let's send this data to the other threads */
+ for (j = u->thread_info.active_outputs; j; j = j->next)
+
+ /* 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);
+ }
+}
+
+/* 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);
+
+ /* 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)
+ ;
+
+ /* 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);
+}
+
+/* Called from I/O thread context */
+static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
+ struct output *o;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(o = i->userdata);
+
+ /* If necessary, get some new data */
+ request_memblock(o, nbytes);
+
+ if (pa_memblockq_peek(o->memblockq, chunk) < 0)
+ return -1;
+
+ pa_memblockq_drop(o->memblockq, chunk->length);
+ return 0;
+}
+
+/* Called from I/O thread context */
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
+ struct output *o;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert(nbytes > 0);
+ pa_assert_se(o = i->userdata);
+
+ pa_memblockq_rewind(o->memblockq, nbytes);
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
+ struct output *o;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(o = i->userdata);
+
+ pa_memblockq_set_maxrewind(o->memblockq, nbytes);
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {
+ struct output *o;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(o = i->userdata);
+
+ if (pa_atomic_load(&o->max_request) == (int) nbytes)
+ return;
+
+ pa_atomic_store(&o->max_request, (int) nbytes);
+
+ pa_asyncmsgq_post(o->outq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_UPDATE_MAX_REQUEST, NULL, 0, NULL, NULL);
}
-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_attach_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;
+ /* Set up the queue from the sink thread to us */
+ pa_assert(!o->inq_rtpoll_item_read && !o->outq_rtpoll_item_write);
+
+ o->inq_rtpoll_item_read = pa_rtpoll_item_new_asyncmsgq_read(
+ i->sink->rtpoll,
+ PA_RTPOLL_LATE, /* This one is not that important, since we check for data in _peek() anyway. */
+ o->inq);
+
+ o->outq_rtpoll_item_write = pa_rtpoll_item_new_asyncmsgq_write(
+ i->sink->rtpoll,
+ PA_RTPOLL_EARLY,
+ o->outq);
}
+/* 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);
+
+ /* Shut down the queue from the sink thread to us */
+ pa_assert(o->inq_rtpoll_item_read && o->outq_rtpoll_item_write);
+
+ pa_rtpoll_item_free(o->inq_rtpoll_item_read);
+ o->inq_rtpoll_item_read = NULL;
+
+ pa_rtpoll_item_free(o->outq_rtpoll_item_write);
+ o->outq_rtpoll_item_write = 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 IO thread context */
+static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) {
+ struct userdata *u;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(u = i->userdata);
+
+ /* If we are added for the first time, ask for a rewinding so that
+ * we are heard right-away. */
+ if (PA_SINK_INPUT_IS_LINKED(state) &&
+ i->thread_info.state == PA_SINK_INPUT_INIT)
+ pa_sink_input_request_rewind(i, 0, FALSE, TRUE);
+}
+
+/* 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_IS_OPENED(o->sink_input->sink->thread_info.state))
+ pa_memblockq_push_align(o->memblockq, chunk);
+ else
+ pa_memblockq_flush(o->memblockq);
+
+ return 0;
+ }
+
+ return pa_sink_input_process_msg(obj, code, data, offset, chunk);
+}
+
+/* Called from main context */
+static void disable_output(struct output *o) {
+ pa_assert(o);
+
+ if (!o->sink_input)
+ return;
+
+ pa_sink_input_unlink(o->sink_input);
+ pa_asyncmsgq_send(o->userdata->sink->asyncmsgq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_REMOVE_OUTPUT, o, 0, NULL);
+ pa_sink_input_unref(o->sink_input);
+ o->sink_input = NULL;
+}
+
+/* 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_IS_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);
+ }
}
-static pa_usec_t sink_input_get_latency_cb(pa_sink_input *i) {
- struct output *o = i->userdata;
- assert(i && o && o->sink_input);
-
- return pa_bytes_to_usec(pa_memblockq_get_length(o->memblockq), &i->sample_spec);
+/* 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);
+
+ pa_log_info("Device suspended...");
}
-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 unsuspend(struct userdata *u) {
+ struct output *o;
+ uint32_t idx;
+
+ pa_assert(u);
- return
- pa_sink_input_get_latency(u->master->sink_input) +
- pa_sink_get_latency(u->master->sink_input->sink);
+ /* 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_IS_OPENED(pa_sink_get_state(o->sink)))
+ enable_output(o);
+ }
+
+ pa_log_info("Resumed successfully...");
}
-static void sink_notify(pa_sink *s) {
+/* 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_IS_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 IO context */
+static void update_max_request(struct userdata *u) {
+ size_t max_request = 0;
struct output *o;
-
- assert(s);
- u = s->userdata;
- assert(u);
-
- for (o = u->outputs; o; o = o->next)
- pa_sink_notify(o->sink_input->sink);
-}
-
-static struct output *output_new(struct userdata *u, pa_sink *sink, int resample_method) {
- struct output *o = NULL;
- char t[256];
+
+ for (o = u->thread_info.active_outputs; o; o = o->next) {
+ size_t mr = (size_t) pa_atomic_load(&o->max_request);
+
+ if (mr > max_request)
+ max_request = mr;
+ }
+
+ if (max_request <= 0)
+ max_request = pa_usec_to_bytes(u->block_usec, &u->sink->sample_spec);
+
+ pa_sink_set_max_request(u->sink, max_request);
+}
+
+/* Called from thread context of the io thread */
+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);
+
+ if (PA_PTR_TO_UINT(data) == PA_SINK_SUSPENDED)
+ pa_smoother_pause(u->thread_info.smoother, pa_rtclock_usec());
+ else
+ pa_smoother_resume(u->thread_info.smoother, pa_rtclock_usec());
+
+ break;
+
+ case PA_SINK_MESSAGE_GET_LATENCY: {
+ pa_usec_t x, y, c, *delay = data;
+
+ x = pa_rtclock_usec();
+ y = pa_smoother_get(u->thread_info.smoother, x);
+
+ c = pa_bytes_to_usec(u->thread_info.counter, &u->sink->sample_spec);
+
+ if (y < c)
+ *delay = c - y;
+ else
+ *delay = 0;
+
+ return 0;
+ }
+
+ 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_read && !op->inq_rtpoll_item_write);
+
+ op->outq_rtpoll_item_read = pa_rtpoll_item_new_asyncmsgq_read(
+ u->rtpoll,
+ PA_RTPOLL_EARLY-1, /* This item is very important */
+ op->outq);
+ op->inq_rtpoll_item_write = pa_rtpoll_item_new_asyncmsgq_write(
+ u->rtpoll,
+ PA_RTPOLL_EARLY,
+ op->inq);
+
+ update_max_request(u);
+ return 0;
+ }
+
+ case SINK_MESSAGE_REMOVE_OUTPUT: {
+ struct output *op = data;
+
+ PA_LLIST_REMOVE(struct output, u->thread_info.active_outputs, op);
+
+ pa_assert(op->outq_rtpoll_item_read && op->inq_rtpoll_item_write);
+
+ pa_rtpoll_item_free(op->outq_rtpoll_item_read);
+ op->outq_rtpoll_item_read = NULL;
+
+ pa_rtpoll_item_free(op->inq_rtpoll_item_write);
+ op->inq_rtpoll_item_write = NULL;
+
+ update_max_request(u);
+ return 0;
+ }
+
+ case SINK_MESSAGE_NEED:
+ render_memblock(u, (struct output*) data, (size_t) offset);
+ return 0;
+
+ case SINK_MESSAGE_UPDATE_LATENCY: {
+ pa_usec_t x, y, latency = (pa_usec_t) offset;
+
+ x = pa_rtclock_usec();
+ y = pa_bytes_to_usec(u->thread_info.counter, &u->sink->sample_spec);
+
+ if (y > latency)
+ y -= latency;
+ else
+ y = 0;
+
+ pa_smoother_put(u->thread_info.smoother, x, y);
+ return 0;
+ }
+
+ case SINK_MESSAGE_UPDATE_MAX_REQUEST:
+
+ update_max_request(u);
+ break;
+ }
+
+ return pa_sink_process_msg(o, code, data, offset, chunk);
+}
+
+static void update_description(struct userdata *u) {
+ pa_bool_t first = TRUE;
+ 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, pa_strnull(pa_proplist_gets(o->sink->proplist, PA_PROP_DEVICE_DESCRIPTION)));
+ first = FALSE;
+ } else
+ e = pa_sprintf_malloc("%s, %s", t, pa_strnull(pa_proplist_gets(o->sink->proplist, PA_PROP_DEVICE_DESCRIPTION)));
+
+ pa_xfree(t);
+ t = e;
+ }
+
+ pa_sink_set_description(u->sink, t);
+ pa_xfree(t);
+}
+
+static int output_create_sink_input(struct output *o) {
pa_sink_input_new_data data;
-
- assert(u && sink && u->sink);
-
- o = pa_xmalloc(sizeof(struct output));
+
+ pa_assert(o);
+
+ if (o->sink_input)
+ return 0;
+
+ pa_sink_input_new_data_init(&data);
+ data.sink = o->sink;
+ data.driver = __FILE__;
+ pa_proplist_setf(data.proplist, PA_PROP_MEDIA_NAME, "Simultaneous output on %s", pa_strnull(pa_proplist_gets(o->sink->proplist, PA_PROP_DEVICE_DESCRIPTION)));
+ pa_proplist_sets(data.proplist, PA_PROP_MEDIA_ROLE, "filter");
+ 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_sink_input_new_data_done(&data);
+
+ if (!o->sink_input)
+ return -1;
+
+ o->sink_input->parent.process_msg = sink_input_process_msg;
+ o->sink_input->pop = sink_input_pop_cb;
+ o->sink_input->process_rewind = sink_input_process_rewind_cb;
+ o->sink_input->state_change = sink_input_state_change_cb;
+ o->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
+ o->sink_input->update_max_request = sink_input_update_max_request_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;
+
+ pa_sink_input_set_requested_latency(o->sink_input, REQUEST_LATENCY_USEC);
+
+ return 0;
+}
+
+static struct output *output_new(struct userdata *u, pa_sink *sink) {
+ struct output *o;
+ pa_sink_state_t state;
+
+ pa_assert(u);
+ pa_assert(sink);
+ pa_assert(u->sink);
+
+ o = pa_xnew(struct output, 1);
o->userdata = u;
-
- o->counter = 0;
+ o->inq = pa_asyncmsgq_new(0);
+ o->outq = pa_asyncmsgq_new(0);
+ o->inq_rtpoll_item_write = o->inq_rtpoll_item_read = NULL;
+ o->outq_rtpoll_item_write = o->outq_rtpoll_item_read = NULL;
+ o->sink = sink;
+ o->sink_input = NULL;
o->memblockq = pa_memblockq_new(
0,
MEMBLOCKQ_MAXLENGTH,
@@ -247,92 +841,174 @@ static struct output *output_new(struct userdata *u, pa_sink *sink, int resample
pa_frame_size(&u->sink->sample_spec),
1,
0,
+ 0,
NULL);
+ pa_atomic_store(&o->max_request, 0);
+ PA_LLIST_INIT(struct output, o);
+
+ pa_assert_se(pa_idxset_put(u->outputs, o, NULL) == 0);
+
+ state = pa_sink_get_state(u->sink);
+
+ if (state != PA_SINK_INIT)
+ 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);
+
+ o->outq_rtpoll_item_read = pa_rtpoll_item_new_asyncmsgq_read(
+ u->rtpoll,
+ PA_RTPOLL_EARLY-1, /* This item is very important */
+ o->outq);
+ o->inq_rtpoll_item_write = pa_rtpoll_item_new_asyncmsgq_write(
+ u->rtpoll,
+ PA_RTPOLL_EARLY,
+ o->inq);
+ }
- snprintf(t, sizeof(t), "Output stream #%u of sink %s", u->n_outputs+1, u->sink->name);
+ if (PA_SINK_IS_OPENED(state) || state == PA_SINK_INIT) {
+ pa_sink_suspend(sink, FALSE);
- 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 (!(o->sink_input = pa_sink_input_new(u->core, &data, PA_SINK_INPUT_VARIABLE_RATE)))
- goto fail;
+ if (PA_SINK_IS_OPENED(pa_sink_get_state(sink)))
+ if (output_create_sink_input(o) < 0)
+ goto fail;
+ }
+
+ update_description(u);
- 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;
-
- 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_bool_t is_suitable_sink(struct userdata *u, pa_sink *s) {
+ const char *t;
+
+ pa_sink_assert_ref(s);
+
+ if (!(s->flags & PA_SINK_HARDWARE))
+ return FALSE;
+
+ if (s == u->sink)
+ return FALSE;
+
+ if ((t = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_CLASS)))
+ if (strcmp(t, "sound"))
+ return FALSE;
+
+ return TRUE;
}
-static void clear_up(struct userdata *u) {
+static pa_hook_result_t sink_put_hook_cb(pa_core *c, pa_sink *s, struct userdata* u) {
struct output *o;
- assert(u);
-
- if (u->time_event) {
- u->core->mainloop->time_free(u->time_event);
- u->time_event = NULL;
- }
-
- while ((o = u->outputs))
- output_free(o);
-
- u->master = NULL;
-
- if (u->sink) {
- pa_sink_disconnect(u->sink);
- pa_sink_unref(u->sink);
- u->sink = NULL;
+
+ pa_core_assert_ref(c);
+ pa_sink_assert_ref(s);
+ pa_assert(u);
+ pa_assert(u->automatic);
+
+ if (!is_suitable_sink(u, s))
+ 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);
+
+ return PA_HOOK_OK;
}
-int pa__init(pa_core *c, pa_module*m) {
+static struct output* find_output(struct userdata *u, pa_sink *s) {
+ struct output *o;
+ uint32_t idx;
+
+ pa_assert(u);
+ pa_assert(s);
+
+ if (u->sink == s)
+ return NULL;
+
+ for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx))
+ if (o->sink == s)
+ return o;
+
+ return NULL;
+}
+
+static pa_hook_result_t sink_unlink_hook_cb(pa_core *c, pa_sink *s, struct userdata* u) {
+ struct output *o;
+
+ pa_assert(c);
+ pa_sink_assert_ref(s);
+ pa_assert(u);
+
+ if (!(o = find_output(u, s)))
+ 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;
+ pa_sink_state_t state;
+
+ if (!(o = find_output(u, s)))
+ return PA_HOOK_OK;
+
+ state = pa_sink_get_state(s);
+
+ if (PA_SINK_IS_OPENED(state) && PA_SINK_IS_OPENED(pa_sink_get_state(u->sink)) && !o->sink_input)
+ enable_output(o);
+
+ if (state == PA_SINK_SUSPENDED && o->sink_input)
+ disable_output(o);
+
+ return PA_HOOK_OK;
+}
+
+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;
+ const char *slaves, *rm;
+ int resample_method = PA_RESAMPLER_TRIVIAL;
pa_sample_spec ss;
pa_channel_map map;
-
- assert(c && m);
+ struct output *o;
+ uint32_t idx;
+ pa_sink_new_data data;
+
+ pa_assert(m);
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
pa_log("failed to parse module arguments");
@@ -345,118 +1021,236 @@ int pa__init(pa_core *c, pa_module*m) {
goto fail;
}
}
-
- u = pa_xnew(struct userdata, 1);
- m->userdata = u;
- u->sink = NULL;
- u->n_outputs = 0;
- u->master = NULL;
+
+ m->userdata = u = pa_xnew(struct userdata, 1);
+ u->core = m->core;
u->module = m;
- u->core = c;
+ u->sink = NULL;
u->time_event = NULL;
u->adjust_time = DEFAULT_ADJUST_TIME;
- PA_LLIST_HEAD_INIT(struct output, u->outputs);
+ u->rtpoll = pa_rtpoll_new();
+ pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
+ 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_put_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;
+ u->thread_info.counter = 0;
+ u->thread_info.smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC*2, TRUE, 10);
if (pa_modargs_get_value_u32(ma, "adjust_time", &u->adjust_time) < 0) {
- 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");
+ pa_log("Failed to parse adjust_time value");
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;
- }
+ slaves = pa_modargs_get_value(ma, "slaves", NULL);
+ u->automatic = !slaves;
+ ss = m->core->default_sample_spec;
- ss = master_sink->sample_spec;
- if ((pa_modargs_get_sample_spec(ma, &ss) < 0)) {
- pa_log("invalid sample specification.");
+ if ((pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0)) {
+ pa_log("Invalid sample specification.");
goto fail;
}
- if (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);
+ pa_sink_new_data_init(&data);
+ data.namereg_fail = FALSE;
+ data.driver = __FILE__;
+ data.module = m;
+ pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
+ pa_sink_new_data_set_sample_spec(&data, &ss);
+ pa_sink_new_data_set_channel_map(&data, &map);
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Simultaneous Output");
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "filter");
- if ((pa_modargs_get_channel_map(ma, &map) < 0)) {
- pa_log("invalid channel map.");
- goto fail;
- }
+ if (slaves)
+ pa_proplist_sets(data.proplist, "combine.slaves", slaves);
- if (ss.channels != map.channels) {
- 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");
+ u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY);
+ pa_sink_new_data_done(&data);
+
+ if (!u->sink) {
+ 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->get_latency = sink_get_latency_cb;
- u->sink->notify = sink_notify;
+ u->sink->parent.process_msg = sink_process_msg;
+ 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;
- }
-
- split_state = NULL;
- while ((n = pa_split(slaves, ",", &split_state))) {
- pa_sink *slave_sink;
-
- if (!(slave_sink = pa_namereg_get(c, n, PA_NAMEREG_SINK, 1))) {
- pa_log("invalid slave sink '%s'", n);
- goto fail;
+
+ pa_sink_set_rtpoll(u->sink, u->rtpoll);
+ pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
+
+ pa_sink_set_latency_range(u->sink, REQUEST_LATENCY_USEC, REQUEST_LATENCY_USEC);
+ u->block_usec = u->sink->thread_info.max_latency;
+
+ u->sink->thread_info.max_request =
+ pa_usec_to_bytes(u->block_usec, &u->sink->sample_spec);
+
+ if (!u->automatic) {
+ const char*split_state;
+ char *n = NULL;
+ pa_assert(slaves);
+
+ /* The slaves have been specified manually */
+
+ split_state = NULL;
+ while ((n = pa_split(slaves, ",", &split_state))) {
+ pa_sink *slave_sink;
+
+ if (!(slave_sink = pa_namereg_get(m->core, n, PA_NAMEREG_SINK, TRUE)) || 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;
+ }
}
- pa_xfree(n);
+ if (pa_idxset_size(u->outputs) <= 1)
+ pa_log_warn("No slave sinks specified.");
- if (!output_new(u, slave_sink, resample_method)) {
- pa_log("failed to create slave sink input on sink '%s'.", slave_sink->name);
- goto fail;
+ u->sink_put_slot = NULL;
+
+ } else {
+ pa_sink *s;
+
+ /* We're in automatic mode, we add every sink that matches our needs */
+
+ for (s = pa_idxset_first(m->core->sinks, &idx); s; s = pa_idxset_next(m->core->sinks, &idx)) {
+
+ if (!is_suitable_sink(u, s))
+ continue;
+
+ if (!output_new(u, s)) {
+ pa_log("Failed to create sink input on sink '%s'.", s->name);
+ goto fail;
+ }
}
+
+ u->sink_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE, (pa_hook_cb_t) sink_put_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_EARLY, (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_NORMAL, (pa_hook_cb_t) sink_state_changed_hook_cb, u);
+
+ 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;
+
+ 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);
+
+ 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_read)
+ pa_rtpoll_item_free(o->inq_rtpoll_item_read);
+ if (o->inq_rtpoll_item_write)
+ pa_rtpoll_item_free(o->inq_rtpoll_item_write);
+
+ if (o->outq_rtpoll_item_read)
+ pa_rtpoll_item_free(o->outq_rtpoll_item_read);
+ if (o->outq_rtpoll_item_write)
+ pa_rtpoll_item_free(o->outq_rtpoll_item_write);
+
+ 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_put_slot)
+ pa_hook_slot_free(u->sink_put_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);
+ if (u->thread_info.smoother)
+ pa_smoother_free(u->thread_info.smoother);
+ pa_xfree(u);
+}
diff --git a/src/modules/module-console-kit.c b/src/modules/module-console-kit.c
new file mode 100644
index 00000000..3adee99e
--- /dev/null
+++ b/src/modules/module-console-kit.c
@@ -0,0 +1,334 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2008 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 <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/module.h>
+#include <pulsecore/log.h>
+#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 "dbus-util.h"
+#include "module-console-kit-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Create a client for each ConsoleKit session of this user");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+
+static const char* const valid_modargs[] = {
+ NULL
+};
+
+struct session {
+ char *id;
+ pa_client *client;
+};
+
+struct userdata {
+ pa_core *core;
+ pa_dbus_connection *connection;
+ pa_hashmap *sessions;
+};
+
+static void add_session(struct userdata *u, const char *id) {
+ DBusError error;
+ DBusMessage *m = NULL, *reply = NULL;
+ int32_t uid;
+ struct session *session;
+ char *t;
+
+ dbus_error_init (&error);
+
+ if (pa_hashmap_get(u->sessions, id)) {
+ pa_log_warn("Duplicate session %s, ignoring.", id);
+ return;
+ }
+
+ if (!(m = dbus_message_new_method_call("org.freedesktop.ConsoleKit", id, "org.freedesktop.ConsoleKit.Session", "GetUnixUser"))) {
+ pa_log("Failed to allocate GetUnixUser() method call.");
+ goto fail;
+ }
+
+ if (!(reply = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->connection), m, -1, &error))) {
+ pa_log("GetUnixUser() call failed: %s: %s", error.name, error.message);
+ goto fail;
+ }
+
+ /* FIXME: Why is this in int32? and not an uint32? */
+ if (!dbus_message_get_args(reply, &error, DBUS_TYPE_INT32, &uid, DBUS_TYPE_INVALID)) {
+ pa_log("Failed to parse GetUnixUser() result: %s: %s", error.name, error.message);
+ goto fail;
+ }
+
+ /* We only care about our own sessions */
+ if ((uid_t) uid != getuid())
+ goto fail;
+
+ session = pa_xnew(struct session, 1);
+ session->id = pa_xstrdup(id);
+
+ t = pa_sprintf_malloc("ConsoleKit Session %s", id);
+ session->client = pa_client_new(u->core, __FILE__, t);
+ pa_xfree(t);
+
+ pa_proplist_sets(session->client->proplist, "console-kit.session", id);
+
+ pa_hashmap_put(u->sessions, session->id, session);
+
+ pa_log_debug("Added new session %s", id);
+
+fail:
+
+ if (m)
+ dbus_message_unref(m);
+
+ if (reply)
+ dbus_message_unref(reply);
+
+ dbus_error_free(&error);
+}
+
+static void free_session(struct session *session) {
+ pa_assert(session);
+
+ pa_log_debug("Removing session %s", session->id);
+
+ pa_client_free(session->client);
+ pa_xfree(session->id);
+ pa_xfree(session);
+}
+
+static void remove_session(struct userdata *u, const char *id) {
+ struct session *session;
+
+ if (!(session = pa_hashmap_remove(u->sessions, id)))
+ return;
+
+ free_session(session);
+}
+
+static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, void *userdata) {
+ struct userdata *u = userdata;
+ DBusError error;
+ const char *path;
+
+ pa_assert(bus);
+ pa_assert(message);
+ pa_assert(u);
+
+ dbus_error_init(&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.ConsoleKit.Seat", "SessionAdded")) {
+
+ if (!dbus_message_get_args(message, &error, DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID) || dbus_error_is_set(&error)) {
+ pa_log_error("Failed to parse SessionAdded message: %s: %s", error.name, error.message);
+ goto finish;
+ }
+
+ add_session(u, path);
+
+ } else if (dbus_message_is_signal(message, "org.freedesktop.ConsoleKit.Seat", "SessionRemoved")) {
+
+ if (!dbus_message_get_args(message, &error, DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID) || dbus_error_is_set(&error)) {
+ pa_log_error("Failed to parse SessionRemoved message: %s: %s", error.name, error.message);
+ goto finish;
+ }
+
+ remove_session(u, path);
+ }
+
+finish:
+ dbus_error_free(&error);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static int get_session_list(struct userdata *u) {
+ DBusError error;
+ DBusMessage *m = NULL, *reply = NULL;
+ uint32_t uid;
+ DBusMessageIter iter, sub;
+ int ret = -1;
+
+ pa_assert(u);
+
+ dbus_error_init(&error);
+
+ if (!(m = dbus_message_new_method_call("org.freedesktop.ConsoleKit", "/org/freedesktop/ConsoleKit/Manager", "org.freedesktop.ConsoleKit.Manager", "GetSessionsForUnixUser"))) {
+ pa_log("Failed to allocate GetSessionsForUnixUser() method call.");
+ goto fail;
+ }
+
+ uid = (uint32_t) getuid();
+ if (!(dbus_message_append_args(m, DBUS_TYPE_UINT32, &uid, DBUS_TYPE_INVALID))) {
+ pa_log("Failed to append arguments to GetSessionsForUnixUser() method call.");
+ goto fail;
+ }
+
+ if (!(reply = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->connection), m, -1, &error))) {
+ pa_log("GetSessionsForUnixUser() call failed: %s: %s", error.name, error.message);
+ goto fail;
+ }
+
+ dbus_message_iter_init(reply, &iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
+ dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_OBJECT_PATH) {
+ pa_log("Failed to parse GetSessionsForUnixUser() result.");
+ goto fail;
+ }
+
+ dbus_message_iter_recurse(&iter, &sub);
+
+ for (;;) {
+ int at;
+ const char *id;
+
+ if ((at = dbus_message_iter_get_arg_type(&sub)) == DBUS_TYPE_INVALID)
+ break;
+
+ assert(at == DBUS_TYPE_OBJECT_PATH);
+ dbus_message_iter_get_basic(&sub, &id);
+
+ add_session(u, id);
+
+ dbus_message_iter_next(&sub);
+ }
+
+ ret = 0;
+
+fail:
+
+ if (m)
+ dbus_message_unref(m);
+
+ if (reply)
+ dbus_message_unref(reply);
+
+ dbus_error_free(&error);
+
+ return ret;
+}
+
+int pa__init(pa_module*m) {
+ DBusError error;
+ pa_dbus_connection *connection;
+ struct userdata *u = NULL;
+ pa_modargs *ma;
+
+ pa_assert(m);
+
+ dbus_error_init(&error);
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log("Failed to parse module arguments");
+ goto fail;
+ }
+
+ if (!(connection = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &error)) || dbus_error_is_set(&error)) {
+
+ if (connection)
+ pa_dbus_connection_unref(connection);
+
+ pa_log_error("Unable to contact D-Bus system bus: %s: %s", error.name, error.message);
+ goto fail;
+ }
+
+ m->userdata = u = pa_xnew(struct userdata, 1);
+ u->core = m->core;
+ u->connection = connection;
+ u->sessions = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+
+ if (!dbus_connection_add_filter(pa_dbus_connection_get(connection), filter_cb, u, NULL)) {
+ pa_log_error("Failed to add filter function");
+ goto fail;
+ }
+
+ dbus_bus_add_match(pa_dbus_connection_get(connection), "type='signal',sender='org.freedesktop.ConsoleKit', interface='org.freedesktop.ConsoleKit.Seat'", &error);
+ if (dbus_error_is_set(&error)) {
+ pa_log_error("Unable to subscribe to ConsoleKit signals: %s: %s", error.name, error.message);
+ goto fail;
+ }
+
+ if (get_session_list(u) < 0)
+ goto fail;
+
+ 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_module *m) {
+ struct userdata *u;
+ struct session *session;
+
+ pa_assert(m);
+
+ if (!(u = m->userdata))
+ return;
+
+ if (u->sessions) {
+ while ((session = pa_hashmap_steal_first(u->sessions)))
+ free_session(session);
+
+ pa_hashmap_free(u->sessions, NULL, NULL);
+ }
+
+ if (u->connection)
+ pa_dbus_connection_unref(u->connection);
+
+ 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..2168ac71
--- /dev/null
+++ b/src/modules/module-default-device-restore.c
@@ -0,0 +1,201 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2006-2008 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 <errno.h>
+#include <stdio.h>
+
+#include <pulse/timeval.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/module.h>
+#include <pulsecore/log.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/core-error.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);
+PA_MODULE_LOAD_ONCE(TRUE);
+
+#define DEFAULT_SINK_FILE "default-sink"
+#define DEFAULT_SOURCE_FILE "default-source"
+#define DEFAULT_SAVE_INTERVAL 5
+
+struct userdata {
+ pa_core *core;
+ pa_subscription *subscription;
+ pa_time_event *time_event;
+ char *sink_filename, *source_filename;
+ pa_bool_t modified;
+};
+
+static void load(struct userdata *u) {
+ FILE *f;
+
+ /* We never overwrite manually configured settings */
+
+ if (u->core->default_sink_name)
+ pa_log_info("Manually configured default sink, not overwriting.");
+ else if ((f = fopen(u->sink_filename, "r"))) {
+ char ln[256] = "";
+
+ fgets(ln, sizeof(ln)-1, f);
+ pa_strip_nl(ln);
+ fclose(f);
+
+ if (!ln[0])
+ pa_log_info("No previous default sink setting, ignoring.");
+ else if (pa_namereg_get(u->core, ln, PA_NAMEREG_SINK, TRUE)) {
+ pa_namereg_set_default(u->core, ln, PA_NAMEREG_SINK);
+ pa_log_info("Restored default sink '%s'.", ln);
+ } else
+ pa_log_info("Saved default sink '%s' not existant, not restoring default sink setting.", ln);
+
+ } else if (errno != ENOENT)
+ pa_log("Failed to load default sink: %s", pa_cstrerror(errno));
+
+ if (u->core->default_source_name)
+ pa_log_info("Manually configured default source, not overwriting.");
+ else if ((f = fopen(u->source_filename, "r"))) {
+ char ln[256] = "";
+
+ fgets(ln, sizeof(ln)-1, f);
+ pa_strip_nl(ln);
+ fclose(f);
+
+ if (!ln[0])
+ pa_log_info("No previous default source setting, ignoring.");
+ else if (pa_namereg_get(u->core, ln, PA_NAMEREG_SOURCE, TRUE)) {
+ pa_namereg_set_default(u->core, ln, PA_NAMEREG_SOURCE);
+ pa_log_info("Restored default source '%s'.", ln);
+ } else
+ pa_log_info("Saved default source '%s' not existant, not restoring default source setting.", ln);
+
+ } else if (errno != ENOENT)
+ pa_log("Failed to load default sink: %s", pa_cstrerror(errno));
+}
+
+static void save(struct userdata *u) {
+ FILE *f;
+
+ if (!u->modified)
+ return;
+
+ if (u->sink_filename) {
+ if ((f = fopen(u->sink_filename, "w"))) {
+ const char *n = pa_namereg_get_default_sink_name(u->core);
+ fprintf(f, "%s\n", pa_strempty(n));
+ fclose(f);
+ } else
+ pa_log("Failed to save default sink: %s", pa_cstrerror(errno));
+ }
+
+ if (u->source_filename) {
+ if ((f = fopen(u->source_filename, "w"))) {
+ const char *n = pa_namereg_get_default_source_name(u->core);
+ fprintf(f, "%s\n", pa_strempty(n));
+ fclose(f);
+ } else
+ pa_log("Failed to save default source: %s", pa_cstrerror(errno));
+ }
+
+ u->modified = FALSE;
+}
+
+static void time_cb(pa_mainloop_api *a, pa_time_event *e, const struct timeval *tv, void *userdata) {
+ struct userdata *u = userdata;
+
+ pa_assert(u);
+ save(u);
+
+ if (u->time_event) {
+ u->core->mainloop->time_free(u->time_event);
+ u->time_event = NULL;
+ }
+}
+
+static void subscribe_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
+ struct userdata *u = userdata;
+
+ pa_assert(u);
+
+ u->modified = TRUE;
+
+ if (!u->time_event) {
+ struct timeval tv;
+ pa_gettimeofday(&tv);
+ pa_timeval_add(&tv, DEFAULT_SAVE_INTERVAL*PA_USEC_PER_SEC);
+ u->time_event = u->core->mainloop->time_new(u->core->mainloop, &tv, time_cb, u);
+ }
+}
+
+int pa__init(pa_module *m) {
+ struct userdata *u;
+
+ pa_assert(m);
+
+ u = pa_xnew0(struct userdata, 1);
+ u->core = m->core;
+
+ if (!(u->sink_filename = pa_state_path(DEFAULT_SINK_FILE)))
+ goto fail;
+
+ if (!(u->source_filename = pa_state_path(DEFAULT_SOURCE_FILE)))
+ goto fail;
+
+ load(u);
+
+ u->subscription = pa_subscription_new(u->core, PA_SUBSCRIPTION_MASK_SERVER, subscribe_cb, u);
+
+ return 0;
+
+fail:
+ pa__done(m);
+
+ return -1;
+}
+
+void pa__done(pa_module*m) {
+ struct userdata *u;
+
+ pa_assert(m);
+
+ if (!(u = m->userdata))
+ return;
+
+ save(u);
+
+ if (u->subscription)
+ pa_subscription_free(u->subscription);
+
+ if (u->time_event)
+ m->core->mainloop->time_free(u->time_event);
+
+ pa_xfree(u->sink_filename);
+ pa_xfree(u->source_filename);
+ pa_xfree(u);
+}
diff --git a/src/modules/module-defs.h.m4 b/src/modules/module-defs.h.m4
index c961412d..64ce1928 100644
--- a/src/modules/module-defs.h.m4
+++ b/src/modules/module-defs.h.m4
@@ -1,4 +1,3 @@
-dnl $Id$
changecom(`/*', `*/')dnl
define(`module_name', patsubst(patsubst(patsubst(fname, `-symdef.h$'), `^.*/'), `[^0-9a-zA-Z]', `_'))dnl
define(`c_symbol', patsubst(module_name, `[^0-9a-zA-Z]', `_'))dnl
@@ -10,6 +9,7 @@ define(`gen_symbol', `#define $1 'module_name`_LTX_$1')dnl
#include <pulsecore/core.h>
#include <pulsecore/module.h>
+#include <pulsecore/macro.h>
gen_symbol(pa__init)
gen_symbol(pa__done)
@@ -17,13 +17,15 @@ gen_symbol(pa__get_author)
gen_symbol(pa__get_description)
gen_symbol(pa__get_usage)
gen_symbol(pa__get_version)
+gen_symbol(pa__load_once)
-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);
const char* pa__get_usage(void);
const char* pa__get_version(void);
+pa_bool_t pa__load_once(void);
#endif
diff --git a/src/modules/module-detect.c b/src/modules/module-detect.c
index 84ccd14c..13bcfcd1 100644
--- a/src/modules/module-detect.c
+++ b/src/modules/module-detect.c
@@ -1,18 +1,20 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
+ Copyright 2006 Lennart Poettering
+ Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+ Copyright 2006 Diego Pettenò
+
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
@@ -24,7 +26,6 @@
#endif
#include <stdio.h>
-#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
@@ -40,13 +41,20 @@
#include <pulsecore/modargs.h>
#include <pulsecore/log.h>
#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
#include "module-detect-symdef.h"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("Detect available audio hardware and load matching drivers")
-PA_MODULE_VERSION(PACKAGE_VERSION)
-PA_MODULE_USAGE("just-one=<boolean>")
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Detect available audio hardware and load matching drivers");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+PA_MODULE_USAGE("just-one=<boolean>");
+
+static const char* const valid_modargs[] = {
+ "just-one",
+ NULL
+};
#ifdef HAVE_ALSA
@@ -58,7 +66,7 @@ static int detect_alsa(pa_core *c, int just_one) {
if (errno != ENOENT)
pa_log_error("open(\"/proc/asound/devices\") failed: %s", pa_cstrerror(errno));
-
+
return -1;
}
@@ -66,7 +74,7 @@ static int detect_alsa(pa_core *c, int just_one) {
char line[64], args[64];
unsigned device, subdevice;
int is_sink;
-
+
if (!fgets(line, sizeof(line), f))
break;
@@ -81,7 +89,7 @@ static int detect_alsa(pa_core *c, int just_one) {
if (just_one && is_sink && n_sink >= 1)
continue;
-
+
if (just_one && !is_sink && n_source >= 1)
continue;
@@ -92,7 +100,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;
@@ -105,7 +113,7 @@ static int detect_alsa(pa_core *c, int just_one) {
}
fclose(f);
-
+
return n;
}
#endif
@@ -114,7 +122,7 @@ static int detect_alsa(pa_core *c, int just_one) {
static int detect_oss(pa_core *c, int just_one) {
FILE *f;
int n = 0, b = 0;
-
+
if (!(f = fopen("/dev/sndstat", "r")) &&
!(f = fopen("/proc/sndstat", "r")) &&
!(f = fopen("/proc/asound/oss/sndstat", "r"))) {
@@ -128,36 +136,36 @@ static int detect_oss(pa_core *c, int just_one) {
while (!feof(f)) {
char line[64], args[64];
unsigned device;
-
+
if (!fgets(line, sizeof(line), f))
break;
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;
}
if (line[0] == 0)
break;
-
+
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++;
@@ -189,7 +197,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;
@@ -211,39 +219,34 @@ static int detect_waveout(pa_core *c, int just_one) {
}
#endif
-int pa__init(pa_core *c, pa_module*m) {
- int just_one = 0, n = 0;
+int pa__init(pa_module*m) {
+ pa_bool_t just_one = FALSE;
+ int 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");
goto fail;
}
-
+
if (pa_modargs_get_value_boolean(ma, "just-one", &just_one) < 0) {
pa_log("just_one= expects a boolean argument.");
goto fail;
}
#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.");
@@ -251,7 +254,7 @@ int pa__init(pa_core *c, pa_module*m) {
}
pa_log_info("loaded %i modules.", n);
-
+
/* We were successful and can unload ourselves now. */
pa_module_unload_request(m);
@@ -262,12 +265,6 @@ int pa__init(pa_core *c, pa_module*m) {
fail:
if (ma)
pa_modargs_free(ma);
-
- return -1;
-}
-
-void pa__done(PA_GCC_UNUSED pa_core *c, PA_GCC_UNUSED pa_module*m) {
- /* NOP */
+ return -1;
}
-
diff --git a/src/modules/module-device-restore.c b/src/modules/module-device-restore.c
new file mode 100644
index 00000000..b7a1e1b5
--- /dev/null
+++ b/src/modules/module-device-restore.c
@@ -0,0 +1,348 @@
+/***
+ 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 <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <gdbm.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/volume.h>
+#include <pulse/timeval.h>
+#include <pulse/util.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-subscribe.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/source-output.h>
+#include <pulsecore/namereg.h>
+
+#include "module-device-restore-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Automatically restore the volume/mute state of devices");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+
+#define SAVE_INTERVAL 10
+
+static const char* const valid_modargs[] = {
+ NULL,
+};
+
+struct userdata {
+ pa_core *core;
+ pa_subscription *subscription;
+ pa_hook_slot *sink_fixate_hook_slot, *source_fixate_hook_slot;
+ pa_time_event *save_time_event;
+ GDBM_FILE gdbm_file;
+};
+
+struct entry {
+ pa_cvolume volume;
+ int muted;
+};
+
+static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) {
+ struct userdata *u = userdata;
+
+ pa_assert(a);
+ pa_assert(e);
+ pa_assert(tv);
+ pa_assert(u);
+
+ pa_assert(e == u->save_time_event);
+ u->core->mainloop->time_free(u->save_time_event);
+ u->save_time_event = NULL;
+
+ gdbm_sync(u->gdbm_file);
+ pa_log_info("Synced.");
+}
+
+static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
+ struct userdata *u = userdata;
+ struct entry entry;
+ char *name;
+ datum key, data;
+
+ pa_assert(c);
+ pa_assert(u);
+
+ if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) &&
+ t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE) &&
+ t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW) &&
+ t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE))
+ return;
+
+ if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
+ pa_sink *sink;
+
+ if (!(sink = pa_idxset_get_by_index(c->sinks, idx)))
+ return;
+
+ name = pa_sprintf_malloc("sink:%s", sink->name);
+ entry.volume = *pa_sink_get_volume(sink);
+ entry.muted = pa_sink_get_mute(sink);
+
+ } else {
+ pa_source *source;
+
+ pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE);
+
+ if (!(source = pa_idxset_get_by_index(c->sources, idx)))
+ return;
+
+ name = pa_sprintf_malloc("source:%s", source->name);
+ entry.volume = *pa_source_get_volume(source);
+ entry.muted = pa_source_get_mute(source);
+ }
+
+ key.dptr = name;
+ key.dsize = strlen(name);
+
+ data = gdbm_fetch(u->gdbm_file, key);
+
+ if (data.dptr) {
+
+ if (data.dsize == sizeof(struct entry)) {
+ struct entry *old = (struct entry*) data.dptr;
+
+ if (pa_cvolume_valid(&old->volume)) {
+
+ if (pa_cvolume_equal(&old->volume, &entry.volume) &&
+ !old->muted == !entry.muted) {
+
+ pa_xfree(data.dptr);
+ pa_xfree(name);
+ return;
+ }
+ } else
+ pa_log_warn("Invalid volume stored in database for device %s", name);
+
+ } else
+ pa_log_warn("Database contains entry for device %s of wrong size %lu != %lu", name, (unsigned long) data.dsize, (unsigned long) sizeof(struct entry));
+
+ pa_xfree(data.dptr);
+ }
+
+ data.dptr = (void*) &entry;
+ data.dsize = sizeof(entry);
+
+ pa_log_info("Storing volume/mute for device %s.", name);
+
+ gdbm_store(u->gdbm_file, key, data, GDBM_REPLACE);
+
+ if (!u->save_time_event) {
+ struct timeval tv;
+ pa_gettimeofday(&tv);
+ tv.tv_sec += SAVE_INTERVAL;
+ u->save_time_event = u->core->mainloop->time_new(u->core->mainloop, &tv, save_time_callback, u);
+ }
+
+ pa_xfree(name);
+}
+
+static struct entry* read_entry(struct userdata *u, char *name) {
+ datum key, data;
+ struct entry *e;
+
+ pa_assert(u);
+ pa_assert(name);
+
+ key.dptr = name;
+ key.dsize = strlen(name);
+
+ data = gdbm_fetch(u->gdbm_file, key);
+
+ if (!data.dptr)
+ goto fail;
+
+ if (data.dsize != sizeof(struct entry)) {
+ pa_log_warn("Database contains entry for device %s of wrong size %lu != %lu", name, (unsigned long) data.dsize, (unsigned long) sizeof(struct entry));
+ goto fail;
+ }
+
+ e = (struct entry*) data.dptr;
+
+ if (!(pa_cvolume_valid(&e->volume))) {
+ pa_log_warn("Invalid volume stored in database for device %s", name);
+ goto fail;
+ }
+
+ return e;
+
+fail:
+
+ pa_xfree(data.dptr);
+ return NULL;
+}
+
+
+static pa_hook_result_t sink_fixate_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
+ char *name;
+ struct entry *e;
+
+ pa_assert(new_data);
+
+ name = pa_sprintf_malloc("sink:%s", new_data->name);
+
+ if ((e = read_entry(u, name))) {
+
+ if (e->volume.channels == new_data->sample_spec.channels) {
+ pa_log_info("Restoring volume for sink %s.", new_data->name);
+ pa_sink_new_data_set_volume(new_data, &e->volume);
+ }
+
+ pa_log_info("Restoring mute state for sink %s.", new_data->name);
+ pa_sink_new_data_set_muted(new_data, e->muted);
+ pa_xfree(e);
+ }
+
+ pa_xfree(name);
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_fixate_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) {
+ char *name;
+ struct entry *e;
+
+ pa_assert(new_data);
+
+ name = pa_sprintf_malloc("source:%s", new_data->name);
+
+ if ((e = read_entry(u, name))) {
+
+ if (e->volume.channels == new_data->sample_spec.channels) {
+ pa_log_info("Restoring volume for source %s.", new_data->name);
+ pa_source_new_data_set_volume(new_data, &e->volume);
+ }
+
+ pa_log_info("Restoring mute state for source %s.", new_data->name);
+ pa_source_new_data_set_muted(new_data, e->muted);
+ pa_xfree(e);
+ }
+
+ pa_xfree(name);
+
+ return PA_HOOK_OK;
+}
+
+int pa__init(pa_module*m) {
+ pa_modargs *ma = NULL;
+ struct userdata *u;
+ char *fname, *fn;
+ char hn[256];
+ pa_sink *sink;
+ pa_source *source;
+ uint32_t idx;
+
+ pa_assert(m);
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log("Failed to parse module arguments");
+ goto fail;
+ }
+
+ u = pa_xnew(struct userdata, 1);
+ u->core = m->core;
+ u->save_time_event = NULL;
+
+ u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE, subscribe_callback, u);
+
+ u->sink_fixate_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_FIXATE], PA_HOOK_EARLY, (pa_hook_cb_t) sink_fixate_hook_callback, u);
+ u->source_fixate_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_FIXATE], PA_HOOK_EARLY, (pa_hook_cb_t) source_fixate_hook_callback, u);
+
+ m->userdata = u;
+
+ if (!pa_get_host_name(hn, sizeof(hn)))
+ goto fail;
+
+ fn = pa_sprintf_malloc("device-volumes.%s.gdbm", hn);
+ fname = pa_state_path(fn);
+ pa_xfree(fn);
+
+ if (!fname)
+ goto fail;
+
+ if (!(u->gdbm_file = gdbm_open(fname, 0, GDBM_WRCREAT, 0600, NULL))) {
+ pa_log("Failed to open volume database '%s': %s", fname, gdbm_strerror(gdbm_errno));
+ pa_xfree(fname);
+ goto fail;
+ }
+
+ pa_log_info("Sucessfully opened database file '%s'.", fname);
+ pa_xfree(fname);
+
+ for (sink = pa_idxset_first(m->core->sinks, &idx); sink; sink = pa_idxset_next(m->core->sinks, &idx))
+ subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, u);
+
+ for (source = pa_idxset_first(m->core->sources, &idx); source; source = pa_idxset_next(m->core->sources, &idx))
+ subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u);
+
+ pa_modargs_free(ma);
+ return 0;
+
+fail:
+ pa__done(m);
+
+ if (ma)
+ pa_modargs_free(ma);
+
+ return -1;
+}
+
+void pa__done(pa_module*m) {
+ struct userdata* u;
+
+ pa_assert(m);
+
+ if (!(u = m->userdata))
+ return;
+
+ if (u->subscription)
+ pa_subscription_free(u->subscription);
+
+ if (u->sink_fixate_hook_slot)
+ pa_hook_slot_free(u->sink_fixate_hook_slot);
+ if (u->source_fixate_hook_slot)
+ pa_hook_slot_free(u->source_fixate_hook_slot);
+
+ if (u->save_time_event)
+ u->core->mainloop->time_free(u->save_time_event);
+
+ if (u->gdbm_file)
+ gdbm_close(u->gdbm_file);
+
+ pa_xfree(u);
+}
diff --git a/src/modules/module-esound-compat-spawnfd.c b/src/modules/module-esound-compat-spawnfd.c
index 263e81f9..8eb4985b 100644
--- a/src/modules/module-esound-compat-spawnfd.c
+++ b/src/modules/module-esound-compat-spawnfd.c
@@ -1,18 +1,18 @@
-/* $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
@@ -24,7 +24,6 @@
#endif
#include <unistd.h>
-#include <assert.h>
#include <string.h>
#include <errno.h>
@@ -36,33 +35,36 @@
#include "module-esound-compat-spawnfd-symdef.h"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("ESOUND compatibility module: -spawnfd emulation")
-PA_MODULE_USAGE("fd=<file descriptor>")
-PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("ESOUND compatibility module: -spawnfd emulation");
+PA_MODULE_LOAD_ONCE(TRUE);
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_USAGE("fd=<file descriptor>");
static const char* const valid_modargs[] = {
"fd",
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);
@@ -74,9 +76,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 7a662c2d..67f0a231 100644
--- a/src/modules/module-esound-compat-spawnpid.c
+++ b/src/modules/module-esound-compat-spawnpid.c
@@ -1,17 +1,19 @@
/***
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
@@ -23,7 +25,6 @@
#endif
#include <unistd.h>
-#include <assert.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
@@ -36,21 +37,23 @@
#include "module-esound-compat-spawnpid-symdef.h"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("ESOUND compatibility module: -spawnpid emulation")
-PA_MODULE_USAGE("pid=<process id>")
-PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("ESOUND compatibility module: -spawnpid emulation");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+PA_MODULE_USAGE("pid=<process id>");
static const char* const valid_modargs[] = {
"pid",
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 ||
@@ -60,7 +63,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);
@@ -72,9 +75,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 ca1f16ce..e189febd 100644
--- a/src/modules/module-esound-sink.c
+++ b/src/modules/module-esound-sink.c
@@ -1,18 +1,18 @@
-/* $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
@@ -26,14 +26,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>
@@ -45,40 +54,67 @@
#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_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("ESOUND Sink");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
+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;
-
+
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[] = {
@@ -91,42 +127,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_IS_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_IS_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_IS_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));
@@ -134,52 +339,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) {
- void *p;
-
- 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;
-
- assert(u->memchunk.memblock);
- assert(u->memchunk.length);
-
- p = pa_memblock_acquire(u->memchunk.memblock);
-
- if ((r = pa_iochannel_write(u->io, (uint8_t*) p + u->memchunk.index, u->memchunk.length)) < 0) {
- pa_memblock_release(u->memchunk.memblock);
- pa_log("write() failed: %s", pa_cstrerror(errno));
- return -1;
- }
- pa_memblock_release(u->memchunk.memblock);
-
- u->memchunk.index += r;
- u->memchunk.length -= r;
-
- if (u->memchunk.length <= 0) {
- pa_memblock_unref(u->memchunk.memblock);
- u->memchunk.memblock = NULL;
- }
}
-
+
+ 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 */
+
+ pa_assert(u->fd < 0);
+ u->fd = pa_iochannel_get_send_fd(u->io);
+
+ pa_iochannel_set_noclose(u->io, TRUE);
+ pa_iochannel_free(u->io);
+ u->io = NULL;
+
+ pa_make_tcp_socket_low_delay(u->fd);
+
+ pa_log_debug("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) {
@@ -188,32 +385,32 @@ 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);
-
+
break;
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;
@@ -221,45 +418,44 @@ 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);
u->read_data = NULL;
u->read_index = u->read_length = 0;
-
+
break;
}
-
+
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;
-
+
if (u->state == STATE_AUTH || u->state == STATE_LATENCY) {
ssize_t r;
-
+
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);
@@ -268,42 +464,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) {
@@ -311,32 +484,36 @@ static void on_connection(PA_GCC_UNUSED pa_socket_client *c, pa_iochannel*io, vo
pa_socket_client_unref(u->client);
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_debug("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;
-
- assert(c && m);
-
+ const char *espeaker;
+ uint32_t key;
+ pa_sink_new_data data;
+
+ 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;
@@ -347,93 +524,142 @@ int pa__init(pa_core *c, pa_module*m) {
pa_log("esound sample type support is limited to mono/stereo and U8 or S16NE sample data");
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, 10);
+ pa_memchunk_reset(&u->memchunk);
+ u->offset = 0;
+
+ u->rtpoll = pa_rtpoll_new();
+ pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
+ 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))) {
- pa_log("failed to create sink.");
+ if (!(espeaker = getenv("ESPEAKER")))
+ espeaker = ESD_UNIX_SOCKET_NAME;
+
+ espeaker = pa_modargs_get_value(ma, "server", espeaker);
+
+ pa_sink_new_data_init(&data);
+ data.driver = __FILE__;
+ data.module = m;
+ pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
+ pa_sink_new_data_set_sample_spec(&data, &ss);
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, espeaker);
+ pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Esound sink '%s'", espeaker);
+
+ u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY|PA_SINK_NETWORK);
+ pa_sink_new_data_done(&data);
+
+ if (!u->sink) {
+ 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;
+
+ pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
+ pa_sink_set_rtpoll(u->sink, u->rtpoll);
+
+ if (!(u->client = pa_socket_client_new_string(u->core->mainloop, espeaker, ESD_DEFAULT_PORT))) {
+ pa_log("Failed to connect to server.");
goto fail;
}
+
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;
+
+ key = ESD_ENDIAN_KEY;
+ memcpy((uint8_t*) u->write_data + ESD_KEY_LEN, &key, sizeof(key));
/* 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;
+ 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);
- c->mainloop->defer_enable(u->defer_event, 0);
+ pa_sink_put(u->sink);
-
pa_modargs_free(ma);
-
+
return 0;
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);
if (u->client)
pa_socket_client_unref(u->client);
-
+
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 8232cd38..19430a3d 100644
--- a/src/modules/module-hal-detect.c
+++ b/src/modules/module-hal-detect.c
@@ -1,22 +1,23 @@
-/* $Id$ */
-
/***
- This file is part of PulseAudio.
-
- PulseAudio is free software; you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published
- by the Free Software Foundation; either version 2 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.
+ This file is part of PulseAudio.
+
+ 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 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
@@ -24,7 +25,6 @@
#endif
#include <stdio.h>
-#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
@@ -42,46 +42,40 @@
#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>
#include "dbus-util.h"
#include "module-hal-detect-symdef.h"
-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,
+PA_MODULE_AUTHOR("Shahms King");
+PA_MODULE_DESCRIPTION("Detect available audio hardware and load matching drivers");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+#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
-#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",
-#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;
+ const char *capability;
};
struct timerdata {
@@ -89,23 +83,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;
@@ -113,6 +114,7 @@ static const char *strip_udi(const char *udi) {
}
#ifdef HAVE_ALSA
+
typedef enum {
ALSA_TYPE_SINK,
ALSA_TYPE_SOURCE,
@@ -120,227 +122,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_id=%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_id=%u source_name=%s", card, *source_name);
+ }
+
+ pa_log_debug("Loading %s with arguments '%s'", 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 pa_module_load(u->core, module_name, args);
+
+ 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;
-
- if (!strcmp(type, "pcm")) {
- char *e;
-
- device = libhal_device_get_property_int(ctx, udi, "oss.device", error);
- if (dbus_error_is_set(error) || device != 0)
- goto exit;
-
- device_file = libhal_device_get_property_string(ctx, udi, "oss.device_file",
- error);
- if (!device_file || dbus_error_is_set(error))
- goto exit;
-
- /* hack to ignore /dev/audio style devices */
- if ((e = strrchr(device_file, '/')))
- rv = !pa_startswith(e + 1, "audio");
- }
+ class = libhal_device_get_property_string(context, udi, "oss.type", error);
+ if (dbus_error_is_set(error) || !class)
+ goto finish;
-exit:
- libhal_free_string(type);
- libhal_free_string(device_file);
- return rv;
+ if (strcmp(class, "pcm"))
+ goto finish;
+
+ dev = libhal_device_get_property_string(context, udi, "oss.device_file", error);
+ if (dbus_error_is_set(error) || !dev)
+ goto finish;
+
+ if ((e = strrchr(dev, '/')))
+ if (pa_startswith(e + 1, "audio"))
+ goto finish;
+
+ device = libhal_device_get_property_int(context, udi, "oss.device", error);
+ if (dbus_error_is_set(error) || device != 0)
+ goto finish;
+
+ 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;
+
+ if (!hal_oss_device_is_pcm(u->context, udi, &error) || dbus_error_is_set(&error))
+ goto fail;
- snprintf(args, sizeof(args), "device=%s sink_name=oss_output.%s source_name=oss_input.%s", device, strip_udi(udi), strip_udi(udi));
+ 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);
- return pa_module_load(u->core, "module-oss", args);
+ 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 NULL;
}
#endif
-static dbus_bool_t hal_device_add(struct userdata *u, const char *udi,
- DBusError *error)
-{
- pa_module* m;
+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:
- 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, FALSE, PA_VOLUME_NORM, NULL, NULL);
+ 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, FALSE, PA_VOLUME_NORM, NULL, NULL);
+ }
+ }
}
pa_xfree(td->udi);
@@ -348,28 +420,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 */
@@ -379,179 +491,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, FALSE, PA_VOLUME_NORM, NULL, NULL);
+ 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, FALSE, PA_VOLUME_NORM, NULL, NULL);
+ }
+ }
+ }
+
+ 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 n;
+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 ((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 (!(hal_ctx = pa_hal_context_new(c, pa_dbus_connection_get(conn)))) {
+ 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
- if ((n = hal_device_add_all(u, CAP_ALSA)) <= 0)
+ n = hal_device_add_all(u, CAPABILITY_ALSA);
+#endif
+#if defined(HAVE_ALSA) && defined(HAVE_OSS)
+ if (n <= 0)
#endif
#ifdef HAVE_OSS
- if ((n = hal_device_add_all(u, CAP_OSS)) <= 0)
+ n += hal_device_add_all(u, CAPABILITY_OSS);
#endif
- {
- pa_log_warn("failed to detect any sound hardware.");
- userdata_free(u);
- return -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;
}
- libhal_ctx_set_user_data(hal_ctx, (void*) 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;
}
- pa_log_info("loaded %i modules.", n);
+ 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_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 66ded27f..c4d47f8e 100644
--- a/src/modules/module-jack-sink.c
+++ b/src/modules/module-jack-sink.c
@@ -1,18 +1,18 @@
-/* $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
@@ -26,46 +26,59 @@
#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/sink.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-sink-symdef.h"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("Jack Sink")
-PA_MODULE_VERSION(PACKAGE_VERSION)
+/* 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_LOAD_ONCE(TRUE);
+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?> "
- "channel_map=<channel map>")
+ "channel_map=<channel map>");
#define DEFAULT_SINK_NAME "jack_out"
struct userdata {
pa_core *core;
pa_module *module;
-
pa_sink *sink;
unsigned channels;
@@ -73,19 +86,18 @@ struct userdata {
jack_port_t* port[PA_CHANNELS_MAX];
jack_client_t *client;
- pthread_mutex_t mutex;
- pthread_cond_t cond;
-
- void * buffer[PA_CHANNELS_MAX];
- jack_nframes_t frames_requested;
- int quit_requested;
+ void *buffer[PA_CHANNELS_MAX];
- int pipe_fd_type;
- int pipe_fds[2];
- pa_io_event *io_event;
+ pa_thread_mq thread_mq;
+ pa_asyncmsgq *jack_msgq;
+ pa_rtpoll *rtpoll;
+ pa_rtpoll_item *rtpoll_item;
+
+ pa_thread *thread;
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[] = {
@@ -98,146 +110,160 @@ static const char* const valid_modargs[] = {
NULL
};
-static void stop_sink(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_sink_disconnect(u->sink);
- pa_sink_unref(u->sink);
- u->sink = NULL;
- pa_module_unload_request(u->module);
-}
+enum {
+ SINK_MESSAGE_RENDER = PA_SINK_MESSAGE_MAX,
+ SINK_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;
-
- assert(m);
- assert(e);
- assert(flags == PA_IO_EVENT_INPUT);
- assert(u);
- assert(u->pipe_fds[0] == fd);
-
- pa_read(fd, &x, 1, &u->pipe_fd_type);
-
- if (u->quit_requested) {
- stop_sink(u);
- u->quit_requested = 0;
- return;
- }
-
- pthread_mutex_lock(&u->mutex);
-
- if (u->frames_requested > 0) {
- unsigned fs;
- jack_nframes_t frame_idx;
- pa_memchunk chunk;
- void *p;
-
- fs = pa_frame_size(&u->sink->sample_spec);
-
- pa_sink_render_full(u->sink, u->frames_requested * fs, &chunk);
- p = pa_memblock_acquire(chunk.memblock);
-
- for (frame_idx = 0; frame_idx < u->frames_requested; frame_idx ++) {
- unsigned c;
-
- for (c = 0; c < u->channels; c++) {
- float *s = ((float*) ((uint8_t*) p + chunk.index)) + (frame_idx * u->channels) + c;
- float *d = ((float*) u->buffer[c]) + frame_idx;
-
- *d = *s;
+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;
+
+ switch (code) {
+
+ case SINK_MESSAGE_RENDER:
+
+ /* Handle the request from the JACK thread */
+
+ if (u->sink->thread_info.state == PA_SINK_RUNNING) {
+ pa_memchunk chunk;
+ size_t nbytes;
+ void *p;
+
+ pa_assert(offset > 0);
+ nbytes = offset * pa_frame_size(&u->sink->sample_spec);
+
+ pa_sink_render_full(u->sink, nbytes, &chunk);
+
+ 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;
+
+ /* Humm, we're not RUNNING, hence let's write some silence */
+
+ ss = u->sink->sample_spec;
+ ss.channels = 1;
+
+ for (c = 0; c < u->channels; c++)
+ pa_silence_memory(u->buffer[c], offset * pa_sample_size(&ss), &ss);
}
- }
-
- pa_memblock_release(chunk.memblock);
- pa_memblock_unref(chunk.memblock);
- u->frames_requested = 0;
-
- pthread_cond_signal(&u->cond);
- }
+ u->frames_in_buffer = offset;
+ u->saved_frame_time = * (jack_nframes_t*) data;
+ u->saved_frame_time_valid = TRUE;
- pthread_mutex_unlock(&u->mutex);
-}
+ return 0;
-static void request_render(struct userdata *u) {
- char c = 'x';
- assert(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;
- assert(u->pipe_fds[1] >= 0);
- pa_write(u->pipe_fds[1], &c, 1, &u->pipe_fd_type);
-}
+ case PA_SINK_MESSAGE_GET_LATENCY: {
+ jack_nframes_t l, ft, d;
+ size_t n;
-static void jack_shutdown(void *arg) {
- struct userdata *u = arg;
- assert(u);
+ /* 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;
+ }
+ }
- u->quit_requested = 1;
- request_render(u);
+ return pa_sink_process_msg(o, code, data, offset, memchunk);
}
static int jack_process(jack_nframes_t nframes, void *arg) {
struct userdata *u = arg;
- assert(u);
-
- if (jack_transport_query(u->client, NULL) == JackTransportRolling) {
- unsigned c;
-
- pthread_mutex_lock(&u->mutex);
-
- u->frames_requested = nframes;
-
- for (c = 0; c < u->channels; c++) {
- u->buffer[c] = jack_port_get_buffer(u->port[c], nframes);
- assert(u->buffer[c]);
- }
-
- request_render(u);
-
- pthread_cond_wait(&u->cond, &u->mutex);
-
- u->frames_in_buffer = nframes;
- u->timestamp = jack_get_current_transport_frame(u->client);
-
- pthread_mutex_unlock(&u->mutex);
- }
-
+ unsigned c;
+ jack_nframes_t frame_time;
+ pa_assert(u);
+
+ /* We just forward the request to our other RT thread */
+
+ for (c = 0; c < u->channels; c++)
+ pa_assert_se(u->buffer[c] = jack_port_get_buffer(u->port[c], nframes));
+
+ frame_time = jack_frame_time(u->client);
+
+ pa_assert_se(pa_asyncmsgq_send(u->jack_msgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_RENDER, &frame_time, nframes, NULL) == 0);
return 0;
}
-static pa_usec_t sink_get_latency_cb(pa_sink *s) {
- struct userdata *u;
- jack_nframes_t n, l, d;
-
- assert(s);
- u = s->userdata;
-
- if (jack_transport_query(u->client, NULL) != JackTransportRolling)
- return 0;
-
- n = jack_get_current_transport_frame(u->client);
-
- if (n < u->timestamp)
- return 0;
-
- d = n - u->timestamp;
- l = jack_port_get_total_latency(u->client, u->port[0]) + u->frames_in_buffer;
-
- if (d >= l)
- return 0;
-
- return pa_bytes_to_usec((l - d) * pa_frame_size(&s->sample_spec), &s->sample_spec);
+static void thread_func(void *userdata) {
+ struct userdata *u = userdata;
+
+ pa_assert(u);
+
+ pa_log_debug("Thread starting up");
+
+ if (u->core->realtime_scheduling)
+ pa_make_realtime(u->core->realtime_priority);
+
+ pa_thread_mq_install(&u->thread_mq);
+ pa_rtpoll_install(u->rtpoll);
+
+ for (;;) {
+ int ret;
+
+ if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 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");
}
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);
}
-int pa__init(pa_core *c, pa_module*m) {
+static void jack_init(void *arg) {
+ struct userdata *u = arg;
+
+ pa_log_info("JACK thread starting up.");
+
+ if (u->core->realtime_scheduling)
+ pa_make_realtime(u->core->realtime_priority+4);
+}
+
+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->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;
@@ -245,78 +271,78 @@ int pa__init(pa_core *c, pa_module*m) {
jack_status_t status;
const char *server_name, *client_name;
uint32_t channels = 0;
- int do_connect = 1;
+ pa_bool_t do_connect = TRUE;
unsigned i;
const char **ports = NULL, **p;
- char *t;
-
- assert(c);
- assert(m);
+ pa_sink_new_data data;
+
+ 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 Sink");
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);
-
- if (pipe(u->pipe_fds) < 0) {
- pa_log("pipe() failed: %s", pa_cstrerror(errno));
- goto fail;
- }
+ m->userdata = u;
+ u->saved_frame_time_valid = FALSE;
+ u->rtpoll = pa_rtpoll_new();
+ pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
+
+ /* 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_read(u->rtpoll, PA_RTPOLL_EARLY-1, u->jack_msgq);
- pa_make_nonblock_fd(u->pipe_fds[1]);
-
if (!(u->client = jack_client_open(client_name, server_name ? JackServerName : JackNullOption, &status, server_name))) {
pa_log("jack_client_open() failed.");
goto fail;
}
ports = jack_get_ports(u->client, NULL, NULL, JackPortIsPhysical|JackPortIsInput);
-
+
channels = 0;
for (p = ports; *p; p++)
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.");
+ pa_log("Failed to parse channels= argument.");
goto fail;
}
- pa_channel_map_init_auto(&map, channels, PA_CHANNEL_MAP_ALSA);
- if (pa_modargs_get_channel_map(ma, &map) < 0 || map.channels != channels) {
- pa_log("failed to parse channel_map= argument.");
+ pa_channel_map_init_extend(&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;
}
-
+
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;
- 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, JackPortIsOutput|JackPortIsTerminal, 0))) {
@@ -325,19 +351,40 @@ int pa__init(pa_core *c, pa_module*m) {
}
}
- 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.");
+ pa_sink_new_data_init(&data);
+ data.driver = __FILE__;
+ data.module = m;
+ pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
+ pa_sink_new_data_set_sample_spec(&data, &ss);
+ pa_sink_new_data_set_channel_map(&data, &map);
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "jack");
+ if (server_name)
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, server_name);
+ pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Jack sink (%s)", jack_get_client_name(u->client));
+ pa_proplist_sets(data.proplist, "jack.client_name", jack_get_client_name(u->client));
+
+ u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY);
+ pa_sink_new_data_done(&data);
+
+ if (!u->sink) {
+ pa_log("Failed to create sink.");
goto fail;
}
+ 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("Jack sink (%s)", jack_get_client_name(u->client)));
- pa_xfree(t);
- u->sink->get_latency = sink_get_latency_cb;
+
+ pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
+ pa_sink_set_rtpoll(u->sink, u->rtpoll);
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");
@@ -348,25 +395,24 @@ int pa__init(pa_core *c, pa_module*m) {
for (i = 0, p = ports; i < ss.channels; i++, p++) {
if (!*p) {
- pa_log("not enough physical output ports, leaving unconnected.");
+ pa_log("Not enough physical output ports, leaving unconnected.");
break;
}
- pa_log_info("connecting %s to %s", jack_port_name(u->port[i]), *p);
-
+ 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);
+ pa_log("Failed to connect %s to %s, leaving unconnected.", jack_port_name(u->port[i]), *p);
break;
}
}
-
}
- u->io_event = c->mainloop->io_new(c->mainloop, u->pipe_fds[0], PA_IO_EVENT_INPUT, io_event_cb, u);
-
+ pa_sink_put(u->sink);
+
free(ports);
pa_modargs_free(ma);
-
+
return 0;
fail:
@@ -374,15 +420,16 @@ fail:
pa_modargs_free(ma);
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;
@@ -390,20 +437,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->sink)
+ pa_sink_unlink(u->sink);
- if (u->sink) {
- pa_sink_disconnect(u->sink);
- pa_sink_unref(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->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->sink)
+ pa_sink_unref(u->sink);
+
+ 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-jack-source.c b/src/modules/module-jack-source.c
index 5270b241..03f9d15c 100644
--- a/src/modules/module-jack-source.c
+++ b/src/modules/module-jack-source.c
@@ -1,18 +1,18 @@
-/* $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
@@ -26,46 +26,49 @@
#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"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("Jack Source")
-PA_MODULE_VERSION(PACKAGE_VERSION)
+/* 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_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
PA_MODULE_USAGE(
"source_name=<name of source> "
"server_name=<jack server name> "
"client_name=<jack client name> "
"channels=<number of channels> "
"connect=<connect ports?>"
- "channel_map=<channel map>")
+ "channel_map=<channel map>");
#define DEFAULT_SOURCE_NAME "jack_in"
struct userdata {
pa_core *core;
pa_module *module;
-
pa_source *source;
unsigned channels;
@@ -73,19 +76,15 @@ struct userdata {
jack_port_t* port[PA_CHANNELS_MAX];
jack_client_t *client;
- pthread_mutex_t mutex;
- pthread_cond_t cond;
-
- void * buffer[PA_CHANNELS_MAX];
- jack_nframes_t frames_posted;
- int quit_requested;
+ pa_thread_mq thread_mq;
+ pa_asyncmsgq *jack_msgq;
+ pa_rtpoll *rtpoll;
+ pa_rtpoll_item *rtpoll_item;
- int pipe_fds[2];
- int pipe_fd_type;
- pa_io_event *io_event;
+ pa_thread *thread;
- 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[] = {
@@ -98,146 +97,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;
-
- assert(m);
- assert(flags == PA_IO_EVENT_INPUT);
- assert(u);
- assert(u->pipe_fds[0] == fd);
-
- pa_read(fd, &x, 1, &u->pipe_fd_type);
-
- if (u->quit_requested) {
- stop_source(u);
- u->quit_requested = 0;
- return;
- }
-
- pthread_mutex_lock(&u->mutex);
-
- if (u->frames_posted > 0) {
- unsigned fs;
- jack_nframes_t frame_idx;
- pa_memchunk chunk;
- void *p;
-
- fs = pa_frame_size(&u->source->sample_spec);
-
- chunk.memblock = pa_memblock_new(u->core->mempool, chunk.length = u->frames_posted * fs);
- chunk.index = 0;
-
- p = pa_memblock_acquire(chunk.memblock);
-
- for (frame_idx = 0; frame_idx < u->frames_posted; frame_idx ++) {
- unsigned c;
-
- for (c = 0; c < u->channels; c++) {
- float *s = ((float*) u->buffer[c]) + frame_idx;
- float *d = ((float*) ((uint8_t*) p + chunk.index)) + (frame_idx * u->channels) + c;
-
- *d = *s;
- }
- }
+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;
- pa_memblock_release(chunk.memblock);
-
- pa_source_post(u->source, &chunk);
- pa_memblock_unref(chunk.memblock);
+ switch (code) {
- u->frames_posted = 0;
-
- pthread_cond_signal(&u->cond);
- }
+ case SOURCE_MESSAGE_POST:
- pthread_mutex_unlock(&u->mutex);
-}
+ /* Handle the new block from the JACK thread */
+ pa_assert(chunk);
+ pa_assert(chunk->length > 0);
-static void request_post(struct userdata *u) {
- char c = 'x';
- assert(u);
+ if (u->source->thread_info.state == PA_SOURCE_RUNNING)
+ pa_source_post(u->source, chunk);
- assert(u->pipe_fds[1] >= 0);
- pa_write(u->pipe_fds[1], &c, 1, &u->pipe_fd_type);
-}
+ u->saved_frame_time = offset;
+ u->saved_frame_time_valid = TRUE;
-static void jack_shutdown(void *arg) {
- struct userdata *u = arg;
- assert(u);
+ return 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;
+
+ case PA_SOURCE_MESSAGE_GET_LATENCY: {
+ jack_nframes_t l, ft, d;
+ size_t n;
+
+ /* This is the "worst-case" latency */
+ l = jack_port_get_total_latency(u->client, u->port[0]);
+
+ if (u->saved_frame_time_valid) {
+ /* Adjust the worst case latency by the time that
+ * passed since we last handed data to JACK */
- u->quit_requested = 1;
- request_post(u);
+ ft = jack_frame_time(u->client);
+ d = ft > u->saved_frame_time ? ft - u->saved_frame_time : 0;
+ l += d;
+ }
+
+ /* 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);
+
+ return 0;
+ }
+ }
+
+ 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;
-
- pthread_mutex_lock(&u->mutex);
-
- u->frames_posted = nframes;
-
- for (c = 0; c < u->channels; c++) {
- u->buffer[c] = jack_port_get_buffer(u->port[c], nframes);
- assert(u->buffer[c]);
- }
-
- request_post(u);
-
- pthread_cond_wait(&u->cond, &u->mutex);
-
- u->frames_in_buffer = nframes;
- u->timestamp = jack_get_current_transport_frame(u->client);
-
- pthread_mutex_unlock(&u->mutex);
- }
-
+ const void *buffer[PA_CHANNELS_MAX];
+ void *p;
+ jack_nframes_t frame_time;
+ pa_memchunk chunk;
+
+ pa_assert(u);
+
+ for (c = 0; c < u->channels; c++)
+ pa_assert(buffer[c] = jack_port_get_buffer(u->port[c], nframes));
+
+ /* We interleave the data and pass it on to the other RT thread */
+
+ 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);
+
+ frame_time = jack_frame_time(u->client);
+
+ pa_asyncmsgq_post(u->jack_msgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_POST, NULL, frame_time, &chunk, NULL);
+
+ 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;
-
- assert(s);
- u = s->userdata;
-
- if (jack_transport_query(u->client, NULL) != JackTransportRolling)
- return 0;
-
- n = jack_get_current_transport_frame(u->client);
-
- if (n < u->timestamp)
- return 0;
-
- d = n - u->timestamp;
- l = jack_port_get_total_latency(u->client, u->port[0]);
-
- return pa_bytes_to_usec((l + d) * pa_frame_size(&s->sample_spec), &s->sample_spec);
+static void thread_func(void *userdata) {
+ struct userdata *u = userdata;
+
+ pa_assert(u);
+
+ pa_log_debug("Thread starting up");
+
+ if (u->core->realtime_scheduling)
+ pa_make_realtime(u->core->realtime_priority);
+
+ pa_thread_mq_install(&u->thread_mq);
+ pa_rtpoll_install(u->rtpoll);
+
+ for (;;) {
+ int ret;
+
+ if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 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");
}
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->realtime_scheduling)
+ pa_make_realtime(u->core->realtime_priority+4);
}
-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;
@@ -245,78 +248,72 @@ int pa__init(pa_core *c, pa_module*m) {
jack_status_t status;
const char *server_name, *client_name;
uint32_t channels = 0;
- int do_connect = 1;
+ pa_bool_t do_connect = TRUE;
unsigned i;
const char **ports = NULL, **p;
- char *t;
-
- assert(c);
- assert(m);
+ pa_source_new_data data;
+
+ 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);
-
- if (pipe(u->pipe_fds) < 0) {
- pa_log("pipe() failed: %s", pa_cstrerror(errno));
- goto fail;
- }
+ m->userdata = u;
+ u->saved_frame_time_valid = FALSE;
+
+ u->rtpoll = pa_rtpoll_new();
+ pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
+
+ u->jack_msgq = pa_asyncmsgq_new(0);
+ u->rtpoll_item = pa_rtpoll_item_new_asyncmsgq_read(u->rtpoll, PA_RTPOLL_EARLY-1, u->jack_msgq);
- pa_make_nonblock_fd(u->pipe_fds[1]);
-
if (!(u->client = jack_client_open(client_name, server_name ? JackServerName : JackNullOption, &status, server_name))) {
pa_log("jack_client_open() failed.");
goto fail;
}
ports = jack_get_ports(u->client, NULL, NULL, JackPortIsPhysical|JackPortIsOutput);
-
+
channels = 0;
for (p = ports; *p; p++)
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.");
goto fail;
}
- pa_channel_map_init_auto(&map, channels, PA_CHANNEL_MAP_ALSA);
- if (pa_modargs_get_channel_map(ma, &map) < 0 || map.channels != channels) {
+ pa_channel_map_init_extend(&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;
}
-
+
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;
- 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))) {
@@ -325,19 +322,40 @@ 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))) {
- pa_log("failed to create source.");
+ pa_source_new_data_init(&data);
+ data.driver = __FILE__;
+ data.module = m;
+ pa_source_new_data_set_name(&data, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME));
+ pa_source_new_data_set_sample_spec(&data, &ss);
+ pa_source_new_data_set_channel_map(&data, &map);
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "jack");
+ if (server_name)
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, server_name);
+ pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Jack source (%s)", jack_get_client_name(u->client));
+ pa_proplist_sets(data.proplist, "jack.client_name", jack_get_client_name(u->client));
+
+ u->source = pa_source_new(m->core, &data, PA_SOURCE_LATENCY);
+ pa_source_new_data_done(&data);
+
+ if (!u->source) {
+ 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);
- 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;
+
+ pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
+ pa_source_set_rtpoll(u->source, u->rtpoll);
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");
@@ -353,7 +371,7 @@ int pa__init(pa_core *c, pa_module*m) {
}
pa_log_info("connecting %s to %s", jack_port_name(u->port[i]), *p);
-
+
if (jack_connect(u->client, *p, jack_port_name(u->port[i]))) {
pa_log("failed to connect %s to %s, leaving unconnected.", jack_port_name(u->port[i]), *p);
break;
@@ -362,11 +380,11 @@ 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);
-
+
return 0;
fail:
@@ -374,15 +392,15 @@ fail:
pa_modargs_free(ma);
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;
@@ -390,20 +408,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..3e0babfa
--- /dev/null
+++ b/src/modules/module-ladspa-sink.c
@@ -0,0 +1,799 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2008 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 <pulsecore/ltdl-helper.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_LOAD_ONCE(FALSE);
+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>");
+
+#define MEMBLOCKQ_MAXLENGTH (16*1024*1024)
+
+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_memblockq *memblockq;
+};
+
+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;
+
+ /* Get the latency of the master sink */
+ if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
+ usec = 0;
+
+ /* Add the latency internal to our sink input on top */
+ usec += pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->master->sample_spec);
+
+ *((pa_usec_t*) data) = usec;
+ 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_IS_LINKED(state) &&
+ u->sink_input &&
+ PA_SINK_INPUT_IS_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 void sink_request_rewind(pa_sink *s) {
+ struct userdata *u;
+
+ pa_sink_assert_ref(s);
+ pa_assert_se(u = s->userdata);
+
+ /* Just hand this one over to the master sink */
+ pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes + pa_memblockq_get_length(u->memblockq), TRUE, FALSE);
+}
+
+/* Called from I/O thread context */
+static void sink_update_requested_latency(pa_sink *s) {
+ struct userdata *u;
+
+ pa_sink_assert_ref(s);
+ pa_assert_se(u = s->userdata);
+
+ /* Just hand this one over to the master sink */
+ pa_sink_input_set_requested_latency_within_thread(
+ u->sink_input,
+ pa_sink_get_requested_latency_within_thread(s));
+}
+
+/* Called from I/O thread context */
+static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
+ struct userdata *u;
+ float *src, *dst;
+ size_t fs;
+ unsigned n, c;
+ pa_memchunk tchunk;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert(chunk);
+ pa_assert_se(u = i->userdata);
+
+ if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state))
+ return -1;
+
+ while (pa_memblockq_peek(u->memblockq, &tchunk) < 0) {
+ pa_memchunk nchunk;
+
+ pa_sink_render(u->sink, nbytes, &nchunk);
+ pa_memblockq_push(u->memblockq, &nchunk);
+ pa_memblock_unref(nchunk.memblock);
+ }
+
+ tchunk.length = PA_MIN(nbytes, tchunk.length);
+ pa_assert(tchunk.length > 0);
+
+ fs = pa_frame_size(&i->sample_spec);
+ n = PA_MIN(tchunk.length, u->block_size) / fs;
+
+ pa_assert(n > 0);
+
+ chunk->index = 0;
+ chunk->length = n*fs;
+ chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length);
+
+ pa_memblockq_drop(u->memblockq, chunk->length);
+
+ src = (float*) ((uint8_t*) pa_memblock_acquire(tchunk.memblock) + tchunk.index);
+ dst = (float*) pa_memblock_acquire(chunk->memblock);
+
+ for (c = 0; c < u->channels; c++) {
+ pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input, sizeof(float), src+c, u->channels*sizeof(float), n);
+ u->descriptor->run(u->handle[c], n);
+ pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+c, u->channels*sizeof(float), u->output, sizeof(float), n);
+ }
+
+ pa_memblock_release(tchunk.memblock);
+ pa_memblock_release(chunk->memblock);
+
+ pa_memblock_unref(tchunk.memblock);
+
+ return 0;
+}
+
+/* Called from I/O thread context */
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
+ struct userdata *u;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(u = i->userdata);
+ pa_assert(nbytes > 0);
+
+ if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state))
+ return;
+
+ if (u->sink->thread_info.rewind_nbytes > 0) {
+ size_t max_rewrite, amount;
+
+ max_rewrite = nbytes + pa_memblockq_get_length(u->memblockq);
+ amount = PA_MIN(u->sink->thread_info.rewind_nbytes, max_rewrite);
+ u->sink->thread_info.rewind_nbytes = 0;
+
+ if (amount > 0) {
+ unsigned c;
+
+ pa_memblockq_seek(u->memblockq, - (int64_t) amount, PA_SEEK_RELATIVE);
+ pa_sink_process_rewind(u->sink, amount);
+
+ pa_log_debug("Resetting plugin");
+
+ /* Reset the plugin */
+ if (u->descriptor->deactivate)
+ for (c = 0; c < u->channels; c++)
+ u->descriptor->deactivate(u->handle[c]);
+ if (u->descriptor->activate)
+ for (c = 0; c < u->channels; c++)
+ u->descriptor->activate(u->handle[c]);
+ }
+ }
+
+ pa_memblockq_rewind(u->memblockq, nbytes);
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
+ struct userdata *u;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(u = i->userdata);
+
+ if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
+ return;
+
+ pa_memblockq_set_maxrewind(u->memblockq, nbytes);
+ pa_sink_set_max_rewind(u->sink, nbytes);
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {
+ struct userdata *u;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(u = i->userdata);
+
+ if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
+ return;
+
+ pa_sink_set_max_request(u->sink, nbytes);
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) {
+ struct userdata *u;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(u = i->userdata);
+
+ if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
+ return;
+
+ pa_sink_update_latency_range(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency);
+}
+
+/* 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);
+
+ if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
+ return;
+
+ pa_sink_detach_within_thread(u->sink);
+ pa_sink_set_asyncmsgq(u->sink, NULL);
+ pa_sink_set_rtpoll(u->sink, NULL);
+}
+
+/* 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);
+
+ if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
+ return;
+
+ 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);
+
+ pa_sink_update_latency_range(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency);
+}
+
+/* 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_unlink(u->sink);
+ pa_sink_input_unlink(u->sink_input);
+
+ pa_sink_unref(u->sink);
+ u->sink = NULL;
+ pa_sink_input_unref(u->sink_input);
+ u->sink_input = NULL;
+
+ pa_module_unload_request(u->module);
+}
+
+/* Called from IO thread context */
+static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) {
+ struct userdata *u;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(u = i->userdata);
+
+ /* If we are added for the first time, ask for a rewinding so that
+ * we are heard right-away. */
+ if (PA_SINK_INPUT_IS_LINKED(state) &&
+ i->thread_info.state == PA_SINK_INPUT_INIT) {
+ pa_log_debug("Requesting rewind due to state change.");
+ pa_sink_input_request_rewind(i, 0, FALSE, TRUE);
+ }
+}
+
+int pa__init(pa_module*m) {
+ struct userdata *u;
+ pa_sample_spec ss;
+ pa_channel_map map;
+ pa_modargs *ma;
+ char *t;
+ const char *z;
+ pa_sink *master;
+ pa_sink_input_new_data sink_input_data;
+ pa_sink_new_data sink_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;
+
+ 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;
+ u->sink = NULL;
+ u->sink_input = NULL;
+ u->memblockq = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, pa_frame_size(&ss), 1, 1, 0, NULL);
+
+ 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) pa_load_sym(m->dl, NULL, "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'.", label, plugin);
+ 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_debug("Ignored control output port \"%s\".", 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)) && p < n_control) {
+ double f;
+
+ if (*k == 0) {
+ use_default[p++] = TRUE;
+ pa_xfree(k);
+ continue;
+ }
+
+ if (pa_atod(k, &f) < 0) {
+ pa_log("Failed to parse control value '%s'", k);
+ pa_xfree(k);
+ goto fail;
+ }
+
+ pa_xfree(k);
+
+ use_default[p] = FALSE;
+ u->control[p++] = f;
+ }
+
+ /* The previous loop doesn't take the last control value into account
+ if it is left empty, so we do it here. */
+ if (*cdata == 0 || cdata[strlen(cdata) - 1] == ',') {
+ if (p < n_control)
+ use_default[p] = TRUE;
+ p++;
+ }
+
+ if (p > n_control || k) {
+ pa_log("Too many control values passed, %lu expected.", n_control);
+ pa_xfree(k);
+ goto fail;
+ }
+
+ 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]);
+
+ /* Create sink */
+ pa_sink_new_data_init(&sink_data);
+ sink_data.driver = __FILE__;
+ sink_data.module = m;
+ if (!(sink_data.name = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", NULL))))
+ sink_data.name = pa_sprintf_malloc("%s.ladspa", master->name);
+ sink_data.namereg_fail = FALSE;
+ pa_sink_new_data_set_sample_spec(&sink_data, &ss);
+ pa_sink_new_data_set_channel_map(&sink_data, &map);
+ z = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION);
+ pa_proplist_setf(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "LADSPA Plugin %s on %s", label, z ? z : master->name);
+ pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, master->name);
+ pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_CLASS, "filter");
+ pa_proplist_sets(sink_data.proplist, "device.ladspa.module", plugin);
+ pa_proplist_sets(sink_data.proplist, "device.ladspa.label", d->Label);
+ pa_proplist_sets(sink_data.proplist, "device.ladspa.name", d->Name);
+ pa_proplist_sets(sink_data.proplist, "device.ladspa.maker", d->Maker);
+ pa_proplist_sets(sink_data.proplist, "device.ladspa.copyright", d->Copyright);
+ pa_proplist_setf(sink_data.proplist, "device.ladspa.unique_id", "%lu", (unsigned long) d->UniqueID);
+
+ u->sink = pa_sink_new(m->core, &sink_data, PA_SINK_LATENCY);
+ pa_sink_new_data_done(&sink_data);
+
+ if (!u->sink) {
+ 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->update_requested_latency = sink_update_requested_latency;
+ u->sink->request_rewind = sink_request_rewind;
+ u->sink->userdata = u;
+
+ 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(&sink_input_data);
+ sink_input_data.driver = __FILE__;
+ sink_input_data.module = m;
+ sink_input_data.sink = u->master;
+ pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "LADSPA Stream");
+ pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
+ pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss);
+ pa_sink_input_new_data_set_channel_map(&sink_input_data, &map);
+
+ u->sink_input = pa_sink_input_new(m->core, &sink_input_data, PA_SINK_INPUT_DONT_MOVE);
+ pa_sink_input_new_data_done(&sink_input_data);
+
+ if (!u->sink_input)
+ goto fail;
+
+ u->sink_input->pop = sink_input_pop_cb;
+ u->sink_input->process_rewind = sink_input_process_rewind_cb;
+ u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
+ u->sink_input->update_max_request = sink_input_update_max_request_cb;
+ u->sink_input->update_sink_latency_range = sink_input_update_sink_latency_range_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->state_change = sink_input_state_change_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);
+
+ return 0;
+
+fail:
+ if (ma)
+ pa_modargs_free(ma);
+
+ pa_xfree(use_default);
+
+ 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) {
+ pa_sink_unlink(u->sink);
+ pa_sink_unref(u->sink);
+ }
+
+ if (u->sink_input) {
+ pa_sink_input_unlink(u->sink_input);
+ pa_sink_input_unref(u->sink_input);
+ }
+
+ 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);
+
+ if (u->memblockq)
+ pa_memblockq_free(u->memblockq);
+
+ pa_xfree(u->input);
+
+ pa_xfree(u->control);
+
+ pa_xfree(u);
+}
diff --git a/src/modules/module-lirc.c b/src/modules/module-lirc.c
index 18b2ddf1..0570a6a1 100644
--- a/src/modules/module-lirc.c
+++ b/src/modules/module-lirc.c
@@ -1,18 +1,18 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
+ Copyright 2005-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
@@ -24,12 +24,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>
@@ -37,13 +37,15 @@
#include <pulsecore/namereg.h>
#include <pulsecore/sink.h>
#include <pulsecore/modargs.h>
+#include <pulsecore/macro.h>
#include "module-lirc-symdef.h"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("LIRC volume control")
-PA_MODULE_VERSION(PACKAGE_VERSION)
-PA_MODULE_USAGE("config=<config file> sink=<sink name> appname=<lirc application name>")
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("LIRC volume control");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+PA_MODULE_USAGE("config=<config file> sink=<sink name> appname=<lirc application name>");
static const char* const valid_modargs[] = {
"config",
@@ -66,27 +68,28 @@ 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;
}
-
+
if (events & PA_IO_EVENT_INPUT) {
char *c;
-
+
if (lirc_nextcode(&code) != 0 || !code) {
pa_log("lirc_nextcode() failed.");
goto fail;
}
-
+
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) {
enum {
INVALID,
@@ -96,9 +99,9 @@ static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GC
RESET,
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;
else if (strcasecmp(name, "volume-down") == 0)
@@ -109,17 +112,17 @@ static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GC
volchange = MUTE_TOGGLE;
else if (strcasecmp(name, "reset") == 0)
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)
@@ -132,9 +135,9 @@ 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:
for (i = 0; i < cv.channels; i++) {
if (cv.values[i] >= DELTA)
@@ -142,21 +145,21 @@ static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GC
else
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:
@@ -170,32 +173,33 @@ static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GC
pa_xfree(code);
return;
-
+
fail:
u->module->core->mainloop->io_free(u->io);
u->io = NULL;
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.");
return -1;
}
-
+
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->config = NULL;
@@ -212,13 +216,13 @@ int pa__init(pa_core *c, pa_module*m) {
pa_log("lirc_readconfig() failed.");
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;
pa_modargs_free(ma);
-
+
return 0;
fail:
@@ -226,14 +230,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 eb5de64e..769a6b59 100644
--- a/src/modules/module-match.c
+++ b/src/modules/module-match.c
@@ -1,18 +1,18 @@
-/* $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
@@ -24,7 +24,6 @@
#endif
#include <unistd.h>
-#include <assert.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
@@ -45,10 +44,11 @@
#include "module-match-symdef.h"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("Playback stream expression matching module")
-PA_MODULE_USAGE("table=<filename>")
-PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Playback stream expression matching module");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+PA_MODULE_USAGE("table=<filename>");
#define WHITESPACE "\n\r \t"
@@ -78,17 +78,21 @@ static int load_rules(struct userdata *u, const char *filename) {
struct rule *end = NULL;
char *fn = NULL;
- f = filename ?
- fopen(fn = pa_xstrdup(filename), "r") :
- pa_open_config_file(DEFAULT_MATCH_TABLE_FILE, DEFAULT_MATCH_TABLE_FILE_USER, NULL, &fn, "r");
+ pa_assert(u);
+
+ if (filename)
+ f = fopen(fn = pa_xstrdup(filename), "r");
+ else
+ f = pa_open_config_file(DEFAULT_MATCH_TABLE_FILE, DEFAULT_MATCH_TABLE_FILE_USER, NULL, &fn);
if (!f) {
- pa_log("failed to open file '%s': %s", fn, pa_cstrerror(errno));
+ pa_xfree(fn);
+ pa_log("Failed to open file config file: %s", pa_cstrerror(errno));
goto finish;
}
pa_lock_fd(fileno(f), 1);
-
+
while (!feof(f)) {
char *d, *v;
pa_volume_t volume;
@@ -96,12 +100,12 @@ static int load_rules(struct userdata *u, const char *filename) {
regex_t regex;
char ln[256];
struct rule *rule;
-
+
if (!fgets(ln, sizeof(ln), f))
break;
n++;
-
+
pa_strip_nl(ln);
if (ln[0] == '#' || !*ln )
@@ -110,7 +114,7 @@ static int load_rules(struct userdata *u, const char *filename) {
d = ln+strcspn(ln, WHITESPACE);
v = d+strspn(d, WHITESPACE);
-
+
if (!*v) {
pa_log(__FILE__ ": [%s:%u] failed to parse line - too few words", filename, n);
goto finish;
@@ -124,13 +128,13 @@ static int load_rules(struct userdata *u, const char *filename) {
volume = (pa_volume_t) k;
-
+
if (regcomp(&regex, ln, REG_EXTENDED|REG_NOSUB) != 0) {
pa_log("[%s:%u] invalid regular expression", filename, n);
goto finish;
}
- rule = pa_xmalloc(sizeof(struct rule));
+ rule = pa_xnew(struct rule, 1);
rule->regex = regex;
rule->volume = volume;
rule->next = NULL;
@@ -140,12 +144,12 @@ static int load_rules(struct userdata *u, const char *filename) {
else
u->rules = rule;
end = rule;
-
+
*d = 0;
}
ret = 0;
-
+
finish:
if (f) {
pa_lock_fd(fileno(f), 0);
@@ -162,7 +166,10 @@ 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);
+ const char *n;
+
+ pa_assert(c);
+ pa_assert(u);
if (t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW))
return;
@@ -170,61 +177,63 @@ static void callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, v
if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx)))
return;
- if (!si->name)
+ if (!(n = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_NAME)))
return;
-
+
for (r = u->rules; r; r = r->next) {
- if (!regexec(&r->regex, si->name, 0, NULL, 0)) {
+ if (!regexec(&r->regex, n, 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_log_debug("changing volume of sink input '%s' to 0x%03x", n, r->volume);
+ 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;
-
+
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;
if (u->subscription)
pa_subscription_free(u->subscription);
-
+
for (r = u->rules; r; r = n) {
n = r->next;
@@ -234,5 +243,3 @@ void pa__done(pa_core *c, pa_module*m) {
pa_xfree(u);
}
-
-
diff --git a/src/modules/module-mmkbd-evdev.c b/src/modules/module-mmkbd-evdev.c
index 37234d92..4388e49c 100644
--- a/src/modules/module-mmkbd-evdev.c
+++ b/src/modules/module-mmkbd-evdev.c
@@ -1,18 +1,18 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
+ Copyright 2005-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
@@ -24,7 +24,6 @@
#endif
#include <stdio.h>
-#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
@@ -45,10 +44,11 @@
#include "module-mmkbd-evdev-symdef.h"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("Multimedia keyboard support via Linux evdev")
-PA_MODULE_VERSION(PACKAGE_VERSION)
-PA_MODULE_USAGE("device=<evdev device> sink=<sink name>")
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Multimedia keyboard support via Linux evdev");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
+PA_MODULE_USAGE("device=<evdev device> sink=<sink name>");
#define DEFAULT_DEVICE "/dev/input/event0"
@@ -78,26 +78,27 @@ 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;
}
-
+
if (events & PA_IO_EVENT_INPUT) {
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;
@@ -107,15 +108,15 @@ static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GC
if (volchange != INVALID) {
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)
-
+
switch (volchange) {
case UP:
for (i = 0; i < cv.channels; i++) {
@@ -125,9 +126,9 @@ 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:
for (i = 0; i < cv.channels; i++) {
if (cv.values[i] >= DELTA)
@@ -135,13 +136,13 @@ static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GC
else
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:
@@ -153,7 +154,7 @@ static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GC
}
return;
-
+
fail:
u->module->core->mainloop->io_free(u->io);
u->io = NULL;
@@ -162,22 +163,24 @@ 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));
@@ -219,14 +222,14 @@ 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);
-
+
return 0;
fail:
@@ -234,14 +237,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;
@@ -250,7 +253,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 dd3b4abe..1a6f5368 100644
--- a/src/modules/module-native-protocol-fd.c
+++ b/src/modules/module-native-protocol-fd.c
@@ -1,18 +1,18 @@
-/* $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
@@ -24,10 +24,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>
@@ -35,9 +35,10 @@
#include "module-native-protocol-fd-symdef.h"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("Native protocol autospawn helper")
-PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Native protocol autospawn helper");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
static const char* const valid_modargs[] = {
"fd",
@@ -46,25 +47,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);
- if (!(m->userdata = pa_protocol_native_new_iochannel(c, io, m, ma))) {
+ io = pa_iochannel_new(m->core->mainloop, fd, fd);
+
+ if (!(m->userdata = pa_protocol_native_new_iochannel(m->core, io, m, ma))) {
pa_iochannel_free(io);
goto finish;
}
@@ -74,12 +76,12 @@ int pa__init(pa_core *c, pa_module*m) {
finish:
if (ma)
pa_modargs_free(ma);
-
+
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 50e58853..604ab158 100644
--- a/src/modules/module-null-sink.c
+++ b/src/modules/module-null-sink.c
@@ -1,18 +1,18 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
+ Copyright 2004-2008 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
@@ -26,7 +26,6 @@
#include <stdlib.h>
#include <sys/stat.h>
#include <stdio.h>
-#include <assert.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
@@ -36,37 +35,48 @@
#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"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("Clocked NULL sink")
-PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Clocked NULL sink");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
PA_MODULE_USAGE(
"format=<sample format> "
"channels=<number of channels> "
"rate=<sample rate> "
- "sink_name=<name of sink>"
- "channel_map=<channel map>"
- "description=<description for the sink>")
+ "sink_name=<name of sink> "
+ "channel_map=<channel map> "
+ "description=<description for the sink>");
#define DEFAULT_SINK_NAME "null"
+#define MAX_LATENCY_USEC (PA_USEC_PER_SEC * 2)
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;
+ pa_usec_t block_usec;
+ pa_usec_t timestamp;
};
static const char* const valid_modargs[] = {
@@ -79,103 +89,249 @@ 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:
+
+ if (PA_PTR_TO_UINT(data) == PA_SINK_RUNNING)
+ u->timestamp = pa_rtclock_usec();
+
+ break;
+
+ case PA_SINK_MESSAGE_GET_LATENCY: {
+ pa_usec_t now;
+
+ now = pa_rtclock_usec();
+ *((pa_usec_t*) data) = u->timestamp > now ? u->timestamp - now : 0;
+
+ return 0;
+ }
+ }
+
+ return pa_sink_process_msg(o, code, data, offset, chunk);
+}
+
+static void sink_update_requested_latency_cb(pa_sink *s) {
+ struct userdata *u;
+
+ pa_sink_assert_ref(s);
+ u = s->userdata;
+ pa_assert(u);
+
+ u->block_usec = pa_sink_get_requested_latency_within_thread(s);
+}
+
+static void process_rewind(struct userdata *u, pa_usec_t now) {
+ size_t rewind_nbytes, in_buffer;
+ pa_usec_t delay;
+
+ pa_assert(u);
+
+ /* Figure out how much we shall rewind and reset the counter */
+ rewind_nbytes = u->sink->thread_info.rewind_nbytes;
+ u->sink->thread_info.rewind_nbytes = 0;
+
+ pa_assert(rewind_nbytes > 0);
+ pa_log_debug("Requested to rewind %lu bytes.", (unsigned long) rewind_nbytes);
+
+ if (u->timestamp <= now)
+ return;
+
+ delay = u->timestamp - now;
+ in_buffer = pa_usec_to_bytes(delay, &u->sink->sample_spec);
+
+ if (in_buffer <= 0)
+ return;
+
+ if (rewind_nbytes > in_buffer)
+ rewind_nbytes = in_buffer;
+
+ pa_sink_process_rewind(u->sink, rewind_nbytes);
+ u->timestamp -= pa_bytes_to_usec(rewind_nbytes, &u->sink->sample_spec);
+
+ pa_log_debug("Rewound %lu bytes.", (unsigned long) rewind_nbytes);
+}
+
+static void process_render(struct userdata *u, pa_usec_t now) {
+ size_t ate = 0;
+
+ pa_assert(u);
+
+ /* This is the configured latency. Sink inputs connected to us
+ might not have a single frame more than the maxrequest value
+ queed. Hence: at maximum read this many bytes from the sink
+ inputs. */
- assert(u);
+ /* Fill the buffer up the the latency size */
+ while (u->timestamp < now + u->block_usec) {
+ pa_memchunk chunk;
- if (pa_sink_render(u->sink, u->block_size, &chunk) >= 0) {
- l = chunk.length;
+ pa_sink_render(u->sink, u->sink->thread_info.max_request, &chunk);
pa_memblock_unref(chunk.memblock);
- } else
- l = u->block_size;
- pa_timeval_add(&ntv, pa_bytes_to_usec(l, &u->sink->sample_spec));
- m->time_restart(e, &ntv);
+/* pa_log_debug("Ate %lu bytes.", (unsigned long) chunk.length); */
+ u->timestamp += pa_bytes_to_usec(chunk.length, &u->sink->sample_spec);
- u->n_bytes += l;
+ ate += chunk.length;
+
+ if (ate >= u->sink->thread_info.max_request)
+ break;
+ }
+
+/* pa_log_debug("Ate in sum %lu bytes (of %lu)", (unsigned long) ate, (unsigned long) nbytes); */
}
-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);
+
+ u->timestamp = pa_rtclock_usec();
+
+ for (;;) {
+ int ret;
+
+ /* Render some data and drop it immediately */
+ if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
+ pa_usec_t now;
- a = pa_timeval_diff(pa_gettimeofday(&now), &u->start_time);
- b = pa_bytes_to_usec(u->n_bytes, &s->sample_spec);
+ now = pa_rtclock_usec();
- return b > a ? b - a : 0;
+ if (u->sink->thread_info.rewind_nbytes > 0)
+ process_rewind(u, now);
+
+ if (u->timestamp <= now)
+ process_render(u, now);
+
+ 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, TRUE)) < 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_sink_new_data data;
+
+ 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;
+
+ m->userdata = u = pa_xnew0(struct userdata, 1);
+ u->core = m->core;
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.");
+ u->rtpoll = pa_rtpoll_new();
+ pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
+
+ pa_sink_new_data_init(&data);
+ data.driver = __FILE__;
+ data.module = m;
+ pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
+ pa_sink_new_data_set_sample_spec(&data, &ss);
+ pa_sink_new_data_set_channel_map(&data, &map);
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, pa_modargs_get_value(ma, "description", "Null Output"));
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "abstract");
+
+ u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY);
+ pa_sink_new_data_done(&data);
+
+ if (!u->sink) {
+ pa_log("Failed to create sink object.");
goto fail;
}
- u->sink->get_latency = get_latency;
+ u->sink->parent.process_msg = sink_process_msg;
+ u->sink->update_requested_latency = sink_update_requested_latency_cb;
u->sink->userdata = u;
- pa_sink_set_owner(u->sink, m);
- pa_sink_set_description(u->sink, pa_modargs_get_value(ma, "description", "NULL sink"));
- u->n_bytes = 0;
- pa_gettimeofday(&u->start_time);
-
- u->time_event = c->mainloop->time_new(c->mainloop, &u->start_time, time_callback, u);
+ pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
+ pa_sink_set_rtpoll(u->sink, u->rtpoll);
+
+ pa_sink_set_latency_range(u->sink, (pa_usec_t) -1, MAX_LATENCY_USEC);
+ u->block_usec = u->sink->thread_info.max_latency;
+
+ u->sink->thread_info.max_rewind =
+ u->sink->thread_info.max_request =
+ pa_usec_to_bytes(u->block_usec, &u->sink->sample_spec);
+
+ if (!(u->thread = pa_thread_new(thread_func, u))) {
+ pa_log("Failed to create thread.");
+ goto fail;
+ }
+
+ pa_sink_put(u->sink);
- u->block_size = pa_bytes_per_second(&ss) / 10;
-
pa_modargs_free(ma);
-
+
return 0;
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);
- u->core->mainloop->time_free(u->time_event);
+ 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);
pa_xfree(u);
}
diff --git a/src/modules/module-oss-mmap.c b/src/modules/module-oss-mmap.c
deleted file mode 100644
index 39a8511f..00000000
--- a/src/modules/module-oss-mmap.c
+++ /dev/null
@@ -1,634 +0,0 @@
-/* $Id$ */
-
-/***
- This file is part of PulseAudio.
-
- PulseAudio is free software; you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published
- by the Free Software Foundation; either version 2 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 = pa_memblock_get_length(chunk.memblock);
- 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 = pa_memblock_get_length(chunk.memblock);
- 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 73f0d57e..76b13ecc 100644
--- a/src/modules/module-oss.c
+++ b/src/modules/module-oss.c
@@ -1,45 +1,65 @@
-/* $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.
***/
+/* 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>
@@ -47,13 +67,17 @@
#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"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("OSS Sink/Source")
-PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("OSS Sink/Source");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
PA_MODULE_USAGE(
"sink_name=<name for the sink> "
"source_name=<name for the source> "
@@ -65,21 +89,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[] = {
@@ -94,324 +145,1074 @@ 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->sink) {
- pa_sink_disconnect(u->sink);
- pa_sink_unref(u->sink);
- u->sink = NULL;
+ if (u->fd < 0)
+ return;
+
+ pa_log_debug("trigger");
+
+ if (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state))
+ enable_bits |= PCM_ENABLE_INPUT;
+
+ if (u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state))
+ enable_bits |= PCM_ENABLE_OUTPUT;
+
+ pa_log_debug("trigger: %i", enable_bits);
+
+
+ if (u->use_mmap) {
+
+ if (!quick)
+ ioctl(u->fd, SNDCTL_DSP_SETTRIGGER, &zero);
+
+#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 (ioctl(u->fd, SNDCTL_DSP_SETTRIGGER, &enable_bits) < 0)
+ pa_log_warn("SNDCTL_DSP_SETTRIGGER: %s", pa_cstrerror(errno));
+
+ 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_IS_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);
+ }
+ }
}
-
- if (u->source) {
- pa_source_disconnect(u->source);
- pa_source_unref(u->source);
- u->source = NULL;
+}
+
+static void mmap_fill_memblocks(struct userdata *u, unsigned n) {
+ pa_assert(u);
+ pa_assert(u->out_mmap_memblocks);
+
+/* pa_log("Mmmap writing %u blocks", n); */
+
+ while (n > 0) {
+ pa_memchunk chunk;
+
+ if (u->out_mmap_memblocks[u->out_mmap_current])
+ pa_memblock_unref_fixed(u->out_mmap_memblocks[u->out_mmap_current]);
+
+ 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);
+
+ chunk.length = pa_memblock_get_length(chunk.memblock);
+ chunk.index = 0;
+
+ pa_sink_render_into_full(u->sink, &chunk);
+
+ u->out_mmap_current++;
+ while (u->out_mmap_current >= u->out_nfrags)
+ u->out_mmap_current -= u->out_nfrags;
+
+ n--;
}
+}
+
+static int mmap_write(struct userdata *u) {
+ struct count_info info;
- if (u->io) {
- pa_iochannel_free(u->io);
- u->io = NULL;
+ 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);
+
+ return info.blocks;
}
-static void do_write(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->sink || !pa_iochannel_is_writable(u->io))
- return;
+/* pa_log("Mmmap reading %u blocks", n); */
- update_usage(u);
+ while (n > 0) {
+ pa_memchunk chunk;
- l = u->out_fragment_size;
-
- if (u->use_getospace) {
- audio_buf_info info;
-
- 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->in_mmap_memblocks[u->in_mmap_current]) {
+
+ 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);
+
+ 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 {
- void *p;
- memchunk = &u->memchunk;
-
- if (!memchunk->length)
- if (pa_sink_render(u->sink, l, memchunk) < 0)
- memchunk = &u->silence;
-
- assert(memchunk->memblock);
- assert(memchunk->length);
-
- p = pa_memblock_acquire(memchunk->memblock);
- if ((r = pa_iochannel_write(u->io, (uint8_t*) p + memchunk->index, memchunk->length)) < 0) {
- pa_memblock_release(memchunk->memblock);
- pa_log("write() failed: %s", pa_cstrerror(errno));
-
- clear_up(u);
- pa_module_unload_request(u->module);
- break;
- }
- pa_memblock_release(memchunk->memblock);
-
- if (memchunk == &u->silence)
- assert(r % u->sample_size == 0);
- else {
- u->memchunk.index += r;
- u->memchunk.length -= r;
-
- if (u->memchunk.length <= 0) {
- pa_memblock_unref(u->memchunk.memblock);
- u->memchunk.memblock = NULL;
- }
+static void mmap_clear_memblocks(struct userdata*u, unsigned n) {
+ unsigned i = u->in_mmap_current;
+
+ pa_assert(u);
+ pa_assert(u->in_mmap_memblocks);
+
+ if (n > u->in_nfrags)
+ n = u->in_nfrags;
+
+ while (n > 0) {
+ if (u->in_mmap_memblocks[i]) {
+ pa_memblock_unref_fixed(u->in_mmap_memblocks[i]);
+ u->in_mmap_memblocks[i] = NULL;
}
- l = l > (size_t) r ? l - r : 0;
- } while (loop && l > 0);
+ i++;
+ while (i >= u->in_nfrags)
+ i -= u->in_nfrags;
+
+ n--;
+ }
}
-static void do_read(struct userdata *u) {
- pa_memchunk memchunk;
- ssize_t r;
- size_t l;
- int loop = 0;
- assert(u);
-
- if (!u->source || !pa_iochannel_is_readable(u->io) || !pa_idxset_size(u->source->outputs))
- return;
+static int mmap_read(struct userdata *u) {
+ struct count_info info;
+ pa_assert(u);
+ pa_assert(u->source);
- update_usage(u);
+/* pa_log("Mmmap reading..."); */
- l = u->in_fragment_size;
+ if (ioctl(u->fd, SNDCTL_DSP_GETIPTR, &info) < 0) {
+ pa_log("SNDCTL_DSP_GETIPTR: %s", pa_cstrerror(errno));
+ return -1;
+ }
- if (u->use_getispace) {
- audio_buf_info info;
-
- 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;
- }
- }
+/* 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);
}
-
- do {
- void *p;
- memchunk.memblock = pa_memblock_new(u->core->mempool, l);
-
- p = pa_memblock_acquire(memchunk.memblock);
-
- if ((r = pa_iochannel_read(u->io, p, pa_memblock_get_length(memchunk.memblock))) < 0) {
- pa_memblock_release(memchunk.memblock);
- pa_memblock_unref(memchunk.memblock);
- if (errno != EAGAIN) {
- pa_log("read() failed: %s", pa_cstrerror(errno));
- clear_up(u);
- pa_module_unload_request(u->module);
- }
- break;
- }
- pa_memblock_release(memchunk.memblock);
-
- assert(r <= (ssize_t) pa_memblock_get_length(memchunk.memblock));
- memchunk.length = r;
- memchunk.index = 0;
-
- pa_source_post(u->source, &memchunk);
- pa_memblock_unref(memchunk.memblock);
-
- l = l > (size_t) r ? l - r : 0;
- } while (loop && l > 0);
+
+ 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;
-
- if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) < 0) {
- u->use_getispace = 0;
- return 0;
+static pa_usec_t io_source_get_latency(struct userdata *u) {
+ pa_usec_t r = 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 pa_bytes_to_usec(info.bytes, &s->sample_spec);
+ return r;
}
-static int sink_get_hw_volume(pa_sink *s) {
- struct userdata *u = s->userdata;
+static void build_pollfd(struct userdata *u) {
+ struct pollfd *pollfd;
- 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_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 suspend(struct userdata *u) {
+ pa_assert(u);
+ pa_assert(u->fd >= 0);
+
+ 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 sink_get_volume(pa_sink *s);
+static int source_get_volume(pa_source *s);
+
+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;
- 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;
+ pa_assert(u);
+ pa_assert(u->fd < 0);
+
+ m = u->mode;
+
+ pa_log_info("Trying resume...");
+
+ 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)
+ sink_get_volume(u->sink);
+ if (u->source)
+ 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_IS_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_IS_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_IS_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_IS_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;
+}
+
+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->realtime_scheduling)
+ pa_make_realtime(u->core->realtime_priority);
+
+ 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_IS_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_IS_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_IS_OPENED(u->source->thread_info.state)) ? POLLIN : 0) |
+ ((u->sink && PA_SINK_IS_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_core *c, pa_module*m) {
+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;
+ pa_bool_t record = TRUE, playback = TRUE, use_mmap = TRUE;
pa_sample_spec ss;
pa_channel_map map;
pa_modargs *ma = NULL;
- char hwdesc[64], *t;
+ char hwdesc[64];
const char *name;
- char *name_buf = NULL;
- int namereg_fail;
-
- assert(c);
- assert(m);
+ pa_bool_t namereg_fail;
+ pa_sink_new_data sink_new_data;
+ pa_source_new_data source_new_data;
+
+ 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;
}
-
+
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 argument.");
+ pa_log("record= and playback= expect boolean argument.");
goto fail;
}
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 (pa_modargs_get_value_boolean(ma, "mmap", &use_mmap) < 0) {
+ pa_log("Failed to parse mmap argument.");
goto fail;
}
- if ((fd = pa_oss_open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), &mode, NULL)) < 0)
+ if ((fd = pa_oss_open(dev = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), &mode, &caps)) < 0)
goto fail;
- if (pa_oss_get_hw_description(p, hwdesc, sizeof(hwdesc)) >= 0)
- pa_log_info("hardware name is '%s'.", hwdesc);
+ 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)
- goto fail;
+ if (pa_oss_set_fragments(fd, nfrags, frag_size) < 0)
+ goto fail;
if (pa_oss_auto_format(fd, &ss) < 0)
goto fail;
@@ -420,152 +1221,310 @@ 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_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 = TRUE;
+ u->use_getodelay = TRUE;
+ 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;
+ u->rtpoll = pa_rtpoll_new();
+ pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
+ u->rtpoll_item = NULL;
+ build_pollfd(u);
- u = pa_xmalloc(sizeof(struct userdata));
- u->core = c;
- u->use_getospace = u->use_getispace = 0;
-
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;
- u->use_getispace = 1;
+ 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 = TRUE;
}
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;
- u->use_getospace = 1;
+ 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 = TRUE;
}
+ 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 = FALSE;
+ 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;
+ namereg_fail = TRUE;
else {
- name = name_buf = pa_sprintf_malloc("oss_input.%s", pa_path_get_filename(p));
- namereg_fail = 0;
+ name = name_buf = pa_sprintf_malloc("oss_input.%s", pa_path_get_filename(dev));
+ namereg_fail = FALSE;
}
-
- if (!(u->source = pa_source_new(c, __FILE__, name, namereg_fail, &ss, &map)))
+
+ pa_source_new_data_init(&source_new_data);
+ source_new_data.driver = __FILE__;
+ source_new_data.module = m;
+ pa_source_new_data_set_name(&source_new_data, name);
+ source_new_data.namereg_fail = namereg_fail;
+ pa_source_new_data_set_sample_spec(&source_new_data, &ss);
+ pa_source_new_data_set_channel_map(&source_new_data, &map);
+ pa_proplist_sets(source_new_data.proplist, PA_PROP_DEVICE_STRING, dev);
+ pa_proplist_sets(source_new_data.proplist, PA_PROP_DEVICE_API, "oss");
+ pa_proplist_sets(source_new_data.proplist, PA_PROP_DEVICE_DESCRIPTION, hwdesc[0] ? hwdesc : dev);
+ pa_proplist_sets(source_new_data.proplist, PA_PROP_DEVICE_ACCESS_MODE, use_mmap ? "mmap" : "serial");
+ pa_proplist_setf(source_new_data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (u->in_hwbuf_size));
+ pa_proplist_setf(source_new_data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (u->in_fragment_size));
+
+ u->source = pa_source_new(m->core, &source_new_data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY);
+ pa_source_new_data_done(&source_new_data);
+ 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_xfree(t);
- u->source->is_hardware = 1;
- } else
- u->source = NULL;
-
- pa_xfree(name_buf);
- name_buf = NULL;
+
+ pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
+ pa_source_set_rtpoll(u->source, u->rtpoll);
+ u->source->refresh_volume = TRUE;
+
+ 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;
+ namereg_fail = TRUE;
else {
- name = name_buf = pa_sprintf_malloc("oss_output.%s", pa_path_get_filename(p));
- namereg_fail = 0;
+ name = name_buf = pa_sprintf_malloc("oss_output.%s", pa_path_get_filename(dev));
+ namereg_fail = FALSE;
}
-
- if (!(u->sink = pa_sink_new(c, __FILE__, name, namereg_fail, &ss, &map)))
+
+ pa_sink_new_data_init(&sink_new_data);
+ sink_new_data.driver = __FILE__;
+ sink_new_data.module = m;
+ pa_sink_new_data_set_name(&sink_new_data, name);
+ sink_new_data.namereg_fail = namereg_fail;
+ pa_sink_new_data_set_sample_spec(&sink_new_data, &ss);
+ pa_sink_new_data_set_channel_map(&sink_new_data, &map);
+ pa_proplist_sets(sink_new_data.proplist, PA_PROP_DEVICE_STRING, dev);
+ pa_proplist_sets(sink_new_data.proplist, PA_PROP_DEVICE_API, "oss");
+ pa_proplist_sets(sink_new_data.proplist, PA_PROP_DEVICE_DESCRIPTION, hwdesc[0] ? hwdesc : dev);
+ pa_proplist_sets(sink_new_data.proplist, PA_PROP_DEVICE_ACCESS_MODE, use_mmap ? "mmap" : "serial");
+ pa_proplist_setf(sink_new_data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (u->out_hwbuf_size));
+ pa_proplist_setf(sink_new_data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (u->out_fragment_size));
+
+ u->sink = pa_sink_new(m->core, &sink_new_data, PA_SINK_HARDWARE|PA_SINK_LATENCY);
+ pa_sink_new_data_done(&sink_new_data);
+ 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_xfree(t);
- u->sink->is_hardware = 1;
- } else
- u->sink = NULL;
-
- pa_xfree(name_buf);
- name_buf = NULL;
-
- assert(u->source || u->sink);
-
- 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;
- u->memchunk.memblock = NULL;
- u->memchunk.length = 0;
- u->sample_size = pa_frame_size(&ss);
+ pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
+ pa_sink_set_rtpoll(u->sink, u->rtpoll);
+ u->sink->refresh_volume = TRUE;
- 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;
+ u->sink->thread_info.max_request = u->out_hwbuf_size;
- u->module = m;
- m->userdata = u;
+ if (use_mmap)
+ u->out_mmap_memblocks = pa_xnew0(pa_memblock*, u->out_nfrags);
+ }
- pa_modargs_free(ma);
+ if ((u->mixer_fd = pa_oss_open_mixer_for_device(u->device_name)) >= 0) {
+ pa_bool_t do_close = TRUE;
+ u->mixer_devmask = 0;
- /*
- * 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);
+ if (ioctl(fd, SOUND_MIXER_READ_DEVMASK, &u->mixer_devmask) < 0)
+ pa_log_warn("SOUND_MIXER_READ_DEVMASK failed: %s", pa_cstrerror(errno));
+
+ 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 = FALSE;
+ }
+
+ 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 = FALSE;
+ }
+ }
+
+ if (do_close) {
+ pa_close(u->mixer_fd);
+ u->mixer_fd = -1;
+ }
+ }
+
+go_on:
+
+ pa_assert(u->source || u->sink);
+
+ 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) {
+ if (sink_new_data.volume_is_set) {
+ if (u->sink->set_volume)
+ u->sink->set_volume(u->sink);
+ } else {
+ if (u->sink->get_volume)
+ u->sink->get_volume(u->sink);
+ }
+ }
+
+ if (u->source) {
+ if (source_new_data.volume_is_set) {
+ if (u->source->set_volume)
+ u->source->set_volume(u->source);
+ } else {
+ if (u->source->get_volume)
+ u->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 59d91aa4..cd25b890 100644
--- a/src/modules/module-pipe-sink.c
+++ b/src/modules/module-pipe-sink.c
@@ -1,18 +1,18 @@
-/* $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
@@ -26,50 +26,60 @@
#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"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("UNIX pipe sink")
-PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("UNIX pipe sink");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
PA_MODULE_USAGE(
"sink_name=<name for the sink> "
"file=<path of the FIFO> "
"format=<sample format> "
"channels=<number of channels> "
"rate=<sample rate>"
- "channel_map=<channel map>")
+ "channel_map=<channel map>");
-#define DEFAULT_FIFO_NAME "/tmp/music.output"
+#define DEFAULT_FILE_NAME "fifo_output"
#define DEFAULT_SINK_NAME "fifo_output"
struct userdata {
pa_core *core;
+ pa_module *module;
+ pa_sink *sink;
+
+ pa_thread *thread;
+ pa_thread_mq thread_mq;
+ pa_rtpoll *rtpoll;
char *filename;
-
- pa_sink *sink;
- pa_iochannel *io;
- pa_defer_event *defer_event;
+ int fd;
pa_memchunk memchunk;
- pa_module *module;
+
+ pa_rtpoll_item *rtpoll_item;
+
+ int write_type;
};
static const char* const valid_modargs[] = {
@@ -82,175 +92,273 @@ static const char* const valid_modargs[] = {
NULL
};
-static void do_write(struct userdata *u) {
- ssize_t r;
- void *p;
-
- 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);
-
- if (!pa_iochannel_is_writable(u->io))
- return;
+ switch (code) {
- 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;
+ case PA_SINK_MESSAGE_GET_LATENCY: {
+ size_t n = 0;
+ int l;
- assert(u->memchunk.memblock);
- assert(u->memchunk.length);
+#ifdef TIOCINQ
+ if (ioctl(u->fd, TIOCINQ, &l) >= 0 && l > 0)
+ n = (size_t) l;
+#endif
- p = pa_memblock_acquire(u->memchunk.memblock);
-
- if ((r = pa_iochannel_write(u->io, (uint8_t*) p + u->memchunk.index, u->memchunk.length)) < 0) {
- pa_memblock_release(u->memchunk.memblock);
- pa_log("write(): %s", pa_cstrerror(errno));
- return;
- }
- pa_memblock_release(u->memchunk.memblock);
-
- u->memchunk.index += r;
- u->memchunk.length -= r;
-
- if (u->memchunk.length <= 0) {
- pa_memblock_unref(u->memchunk.memblock);
- u->memchunk.memblock = NULL;
+ n += u->memchunk.length;
+
+ *((pa_usec_t*) data) = pa_bytes_to_usec(n, &u->sink->sample_spec);
+ return 0;
+ }
}
+
+ 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 process_rewind(struct userdata *u) {
+ pa_assert(u);
- if (pa_iochannel_is_writable(u->io))
- u->core->mainloop->defer_enable(u->defer_event, 1);
+ pa_log_debug("Rewind requested but not supported by pipe sink. Ignoring.");
+ u->sink->thread_info.rewind_nbytes = 0;
}
-static pa_usec_t get_latency_cb(pa_sink *s) {
- struct userdata *u = s->userdata;
- assert(s && u);
+static int process_render(struct userdata *u) {
+ pa_assert(u);
- return u->memchunk.memblock ? pa_bytes_to_usec(u->memchunk.length, &s->sample_spec) : 0;
-}
+ if (u->memchunk.length <= 0)
+ pa_sink_render(u->sink, PIPE_BUF, &u->memchunk);
-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);
+ pa_assert(u->memchunk.length > 0);
+
+ for (;;) {
+ ssize_t l;
+ void *p;
+
+ p = pa_memblock_acquire(u->memchunk.memblock);
+ l = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length, &u->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));
+ return -1;
+ }
+
+ } 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);
+ }
+ }
+
+ return 0;
+ }
}
-static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void*userdata) {
+static void thread_func(void *userdata) {
struct userdata *u = userdata;
- assert(u);
- do_write(u);
+
+ pa_assert(u);
+
+ pa_log_debug("Thread starting up");
+
+ pa_thread_mq_install(&u->thread_mq);
+ pa_rtpoll_install(u->rtpoll);
+
+ for (;;) {
+ struct pollfd *pollfd;
+ int ret;
+
+ 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) {
+
+ if (u->sink->thread_info.rewind_nbytes > 0)
+ process_rewind(u);
+
+ if (pollfd->revents) {
+ if (process_render(u) < 0)
+ goto fail;
+
+ 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;
- char *t;
-
- assert(c && m);
-
+ pa_modargs *ma;
+ struct pollfd *pollfd;
+ pa_sink_new_data data;
+
+ 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);
- if ((fd = open(p, O_RDWR)) < 0) {
- pa_log("open('%s'): %s", p, pa_cstrerror(errno));
+ u = pa_xnew0(struct userdata, 1);
+ u->core = m->core;
+ u->module = m;
+ m->userdata = u;
+ pa_memchunk_reset(&u->memchunk);
+ u->rtpoll = pa_rtpoll_new();
+ pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
+ u->write_type = 0;
+
+ u->filename = pa_runtime_path(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);
-
- if (fstat(fd, &st) < 0) {
- pa_log("fstat('%s'): %s", p, pa_cstrerror(errno));
+ pa_make_fd_cloexec(u->fd);
+ pa_make_fd_nonblock(u->fd);
+
+ 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.");
+ pa_sink_new_data_init(&data);
+ data.driver = __FILE__;
+ data.module = m;
+ pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->filename);
+ pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Unix FIFO sink %s", u->filename);
+ pa_sink_new_data_set_sample_spec(&data, &ss);
+ pa_sink_new_data_set_channel_map(&data, &map);
+
+ u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY);
+ pa_sink_new_data_done(&data);
+
+ if (!u->sink) {
+ 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));
- pa_xfree(t);
- u->io = pa_iochannel_new(c->mainloop, -1, fd);
- assert(u->io);
- pa_iochannel_set_callback(u->io, io_callback, u);
+ pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
+ pa_sink_set_rtpoll(u->sink, u->rtpoll);
- u->memchunk.memblock = NULL;
- u->memchunk.length = 0;
+ 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;
+
+ 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);
-
+
return 0;
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_sink_disconnect(u->sink);
- pa_sink_unref(u->sink);
- pa_iochannel_free(u->io);
- u->core->mainloop->defer_free(u->defer_event);
-
- assert(u->filename);
- unlink(u->filename);
- pa_xfree(u->filename);
-
+ pa_memblock_unref(u->memchunk.memblock);
+
+ 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);
+ }
+
+ 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 99f4f3b9..b0de34ca 100644
--- a/src/modules/module-pipe-source.c
+++ b/src/modules/module-pipe-source.c
@@ -1,18 +1,18 @@
-/* $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
@@ -26,48 +26,57 @@
#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"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("UNIX pipe source")
-PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("UNIX pipe source");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
PA_MODULE_USAGE(
"source_name=<name for the source> "
"file=<path of the FIFO> "
"format=<sample format> "
"channels=<number of channels> "
"rate=<sample rate> "
- "channel_map=<channel map>")
+ "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;
-
- pa_source *source;
- pa_iochannel *io;
- pa_module *module;
- pa_memchunk chunk;
+ int fd;
+
+ pa_memchunk memchunk;
+
+ pa_rtpoll_item *rtpoll_item;
};
static const char* const valid_modargs[] = {
@@ -80,150 +89,227 @@ static const char* const valid_modargs[] = {
NULL
};
-static void do_read(struct userdata *u) {
- ssize_t r;
- void *p;
- 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);
- assert(pa_memblock_get_length(u->chunk.memblock) > u->chunk.index);
+ for (;;) {
+ int ret;
+ struct pollfd *pollfd;
- p = pa_memblock_acquire(u->chunk.memblock);
- if ((r = pa_iochannel_read(u->io, (uint8_t*) p + u->chunk.index, pa_memblock_get_length(u->chunk.memblock) - u->chunk.index)) <= 0) {
- pa_memblock_release(u->chunk.memblock);
- pa_log("read(): %s", pa_cstrerror(errno));
- return;
- }
- pa_memblock_release(u->chunk.memblock);
+ pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
- u->chunk.length = r;
- pa_source_post(u->source, &u->chunk);
- u->chunk.index += r;
+ /* 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;
- if (u->chunk.index >= pa_memblock_get_length(u->chunk.memblock)) {
- u->chunk.index = u->chunk.length = 0;
- pa_memblock_unref(u->chunk.memblock);
- u->chunk.memblock = NULL;
+ if (!u->memchunk.memblock) {
+ u->memchunk.memblock = pa_memblock_new(u->core->mempool, PIPE_BUF);
+ u->memchunk.index = u->memchunk.length = 0;
+ }
+
+ 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, TRUE)) < 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;
- char *t;
-
- assert(c && m);
-
+ pa_modargs *ma;
+ struct pollfd *pollfd;
+ pa_source_new_data data;
+
+ 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);
- if ((fd = open(p, O_RDWR)) < 0) {
- pa_log("open('%s'): %s", p, pa_cstrerror(errno));
+ u = pa_xnew0(struct userdata, 1);
+ u->core = m->core;
+ u->module = m;
+ m->userdata = u;
+ pa_memchunk_reset(&u->memchunk);
+ u->rtpoll = pa_rtpoll_new();
+ pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
+
+ u->filename = pa_runtime_path(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);
-
- if (fstat(fd, &st) < 0) {
- pa_log("fstat('%s'): %s", p, pa_cstrerror(errno));
+ pa_make_fd_cloexec(u->fd);
+ pa_make_fd_nonblock(u->fd);
+
+ 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));
+ pa_source_new_data_init(&data);
+ data.driver = __FILE__;
+ data.module = m;
+ pa_source_new_data_set_name(&data, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME));
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->filename);
+ pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Unix FIFO source %s", u->filename);
+ pa_source_new_data_set_sample_spec(&data, &ss);
+ pa_source_new_data_set_channel_map(&data, &map);
- 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.");
+ u->source = pa_source_new(m->core, &data, 0);
+ pa_source_new_data_done(&data);
+
+ if (!u->source) {
+ 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));
- pa_xfree(t);
- u->io = pa_iochannel_new(c->mainloop, fd, -1);
- assert(u->io);
- pa_iochannel_set_callback(u->io, io_callback, u);
+ pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
+ pa_source_set_rtpoll(u->source, u->rtpoll);
- u->chunk.memblock = NULL;
- u->chunk.index = u->chunk.length = 0;
-
- u->module = m;
- m->userdata = 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;
+
+ if (!(u->thread = pa_thread_new(thread_func, u))) {
+ pa_log("Failed to create thread.");
+ goto fail;
+ }
+
+ pa_source_put(u->source);
pa_modargs_free(ma);
-
+
return 0;
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);
-
- pa_source_disconnect(u->source);
- pa_source_unref(u->source);
- pa_iochannel_free(u->io);
-
- assert(u->filename);
- unlink(u->filename);
- pa_xfree(u->filename);
-
+
+ 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);
+
+ 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);
+ }
+
+ if (u->fd >= 0)
+ pa_assert_se(pa_close(u->fd) == 0);
+
pa_xfree(u);
}
diff --git a/src/modules/module-position-event-sounds.c b/src/modules/module-position-event-sounds.c
new file mode 100644
index 00000000..90e693a3
--- /dev/null
+++ b/src/modules/module-position-event-sounds.c
@@ -0,0 +1,165 @@
+/***
+ 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 <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/volume.h>
+#include <pulse/channelmap.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/sink-input.h>
+
+#include "module-position-event-sounds-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Position event sounds between L and R depending on the position on screen of the widget triggering them.");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+
+static const char* const valid_modargs[] = {
+ NULL
+};
+
+struct userdata {
+ pa_core *core;
+ pa_hook_slot *sink_input_fixate_hook_slot;
+};
+
+static pa_bool_t is_left(pa_channel_position_t p) {
+ return
+ p == PA_CHANNEL_POSITION_FRONT_LEFT ||
+ p == PA_CHANNEL_POSITION_REAR_LEFT ||
+ p == PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER ||
+ p == PA_CHANNEL_POSITION_SIDE_LEFT ||
+ p == PA_CHANNEL_POSITION_TOP_FRONT_LEFT ||
+ p == PA_CHANNEL_POSITION_TOP_REAR_LEFT;
+}
+
+static pa_bool_t is_right(pa_channel_position_t p) {
+ return
+ p == PA_CHANNEL_POSITION_FRONT_RIGHT ||
+ p == PA_CHANNEL_POSITION_REAR_RIGHT||
+ p == PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER ||
+ p == PA_CHANNEL_POSITION_SIDE_RIGHT ||
+ p == PA_CHANNEL_POSITION_TOP_FRONT_RIGHT ||
+ p == PA_CHANNEL_POSITION_TOP_REAR_RIGHT;
+}
+
+static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *core, pa_sink_input_new_data *data, struct userdata *u) {
+ const char *hpos;
+ double f;
+ unsigned c;
+ char t[PA_CVOLUME_SNPRINT_MAX];
+
+ pa_assert(data);
+
+ if (!(hpos = pa_proplist_gets(data->proplist, PA_PROP_EVENT_MOUSE_HPOS)))
+ return PA_HOOK_OK;
+
+ if (pa_atod(hpos, &f) < 0) {
+ pa_log_warn("Failed to parse "PA_PROP_EVENT_MOUSE_HPOS" property '%s'.", hpos);
+ return PA_HOOK_OK;
+ }
+
+ if (f < 0.0 || f > 1.0) {
+ pa_log_warn("Property "PA_PROP_EVENT_MOUSE_HPOS" out of range %0.2f", f);
+ return PA_HOOK_OK;
+ }
+
+ pa_log_debug("Positioning event sound '%s' at %0.2f.", pa_strnull(pa_proplist_gets(data->proplist, PA_PROP_EVENT_ID)), f);
+
+ if (!data->volume_is_set) {
+ pa_cvolume_reset(&data->volume, data->sample_spec.channels);
+ data->volume_is_set = TRUE;
+ }
+
+ for (c = 0; c < data->sample_spec.channels; c++) {
+
+ if (is_left(data->channel_map.map[c]))
+ data->volume.values[c] =
+ pa_sw_volume_multiply(data->volume.values[c], (pa_volume_t) (PA_VOLUME_NORM * (1.0 - f)));
+
+ if (is_right(data->channel_map.map[c]))
+ data->volume.values[c] =
+ pa_sw_volume_multiply(data->volume.values[c], (pa_volume_t) (PA_VOLUME_NORM * f));
+ }
+
+ pa_log_debug("Final volume %s.", pa_cvolume_snprint(t, sizeof(t), &data->volume));
+
+ return PA_HOOK_OK;
+}
+
+int pa__init(pa_module*m) {
+ pa_modargs *ma = NULL;
+ struct userdata *u;
+
+ 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_xnew(struct userdata, 1);
+ u->core = m->core;
+ u->sink_input_fixate_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_fixate_hook_callback, u);
+
+ pa_modargs_free(ma);
+
+ return 0;
+
+fail:
+ pa__done(m);
+
+ if (ma)
+ pa_modargs_free(ma);
+
+ return -1;
+}
+
+void pa__done(pa_module*m) {
+ struct userdata* u;
+
+ pa_assert(m);
+
+ if (!(u = m->userdata))
+ return;
+
+ if (u->sink_input_fixate_hook_slot)
+ pa_hook_slot_free(u->sink_input_fixate_hook_slot);
+
+ pa_xfree(u);
+}
diff --git a/src/modules/module-protocol-stub.c b/src/modules/module-protocol-stub.c
index df58958a..0c9529c3 100644
--- a/src/modules/module-protocol-stub.c
+++ b/src/modules/module-protocol-stub.c
@@ -1,18 +1,19 @@
-/* $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
@@ -26,7 +27,6 @@
#include <string.h>
#include <errno.h>
#include <stdio.h>
-#include <assert.h>
#include <unistd.h>
#include <limits.h>
@@ -40,10 +40,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>
@@ -75,7 +74,7 @@
#else
#include "module-simple-protocol-unix-symdef.h"
#endif
- PA_MODULE_DESCRIPTION("Simple protocol "SOCKET_DESCRIPTION)
+PA_MODULE_DESCRIPTION("Simple protocol "SOCKET_DESCRIPTION);
PA_MODULE_USAGE("rate=<sample rate> "
"format=<sample format> "
"channels=<number of channels> "
@@ -83,22 +82,22 @@
"source=<source to connect to> "
"playback=<enable playback?> "
"record=<enable record?> "
- SOCKET_USAGE)
+ SOCKET_USAGE);
#elif defined(USE_PROTOCOL_CLI)
- #include <pulsecore/protocol-cli.h>
+ #include <pulsecore/protocol-cli.h>
#define protocol_new pa_protocol_cli_new
#define protocol_free pa_protocol_cli_free
#define TCPWRAP_SERVICE "pulseaudio-cli"
#define IPV4_PORT 4712
#define UNIX_SOCKET "cli"
- #define MODULE_ARGUMENTS
+ #define MODULE_ARGUMENTS
#ifdef USE_TCP_SOCKETS
#include "module-cli-protocol-tcp-symdef.h"
#else
#include "module-cli-protocol-unix-symdef.h"
#endif
- PA_MODULE_DESCRIPTION("Command line interface protocol "SOCKET_DESCRIPTION)
- PA_MODULE_USAGE(SOCKET_USAGE)
+ PA_MODULE_DESCRIPTION("Command line interface protocol "SOCKET_DESCRIPTION);
+ PA_MODULE_USAGE(SOCKET_USAGE);
#elif defined(USE_PROTOCOL_HTTP)
#include <pulsecore/protocol-http.h>
#define protocol_new pa_protocol_http_new
@@ -106,14 +105,14 @@
#define TCPWRAP_SERVICE "pulseaudio-http"
#define IPV4_PORT 4714
#define UNIX_SOCKET "http"
- #define MODULE_ARGUMENTS
+ #define MODULE_ARGUMENTS
#ifdef USE_TCP_SOCKETS
#include "module-http-protocol-tcp-symdef.h"
#else
#include "module-http-protocol-unix-symdef.h"
#endif
- PA_MODULE_DESCRIPTION("HTTP "SOCKET_DESCRIPTION)
- PA_MODULE_USAGE(SOCKET_USAGE)
+ PA_MODULE_DESCRIPTION("HTTP "SOCKET_DESCRIPTION);
+ PA_MODULE_USAGE(SOCKET_USAGE);
#elif defined(USE_PROTOCOL_NATIVE)
#include <pulsecore/protocol-native.h>
#define protocol_new pa_protocol_native_new
@@ -129,21 +128,21 @@
#endif
#if defined(HAVE_CREDS) && !defined(USE_TCP_SOCKETS)
- #define MODULE_ARGUMENTS MODULE_ARGUMENTS_COMMON "auth-group", "auth-group-enable",
+ #define MODULE_ARGUMENTS MODULE_ARGUMENTS_COMMON "auth-group", "auth-group-enable",
#define AUTH_USAGE "auth-group=<system group to allow access> auth-group-enable=<enable auth by UNIX group?> "
#elif defined(USE_TCP_SOCKETS)
- #define MODULE_ARGUMENTS MODULE_ARGUMENTS_COMMON "auth-ip-acl",
+ #define MODULE_ARGUMENTS MODULE_ARGUMENTS_COMMON "auth-ip-acl",
#define AUTH_USAGE "auth-ip-acl=<IP address ACL to allow access> "
#else
#define MODULE_ARGUMENTS MODULE_ARGUMENTS_COMMON
#define AUTH_USAGE
#endif
-
- PA_MODULE_DESCRIPTION("Native protocol "SOCKET_DESCRIPTION)
+
+ PA_MODULE_DESCRIPTION("Native protocol "SOCKET_DESCRIPTION);
PA_MODULE_USAGE("auth-anonymous=<don't check for cookies?> "
"cookie=<path to cookie file> "
AUTH_USAGE
- SOCKET_USAGE)
+ SOCKET_USAGE);
#elif defined(USE_PROTOCOL_ESOUND)
#include <pulsecore/protocol-esound.h>
#include <pulsecore/esound.h>
@@ -151,7 +150,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"
@@ -160,26 +158,27 @@
#endif
#if defined(USE_TCP_SOCKETS)
- #define MODULE_ARGUMENTS MODULE_ARGUMENTS_COMMON "auth-ip-acl",
+ #define MODULE_ARGUMENTS MODULE_ARGUMENTS_COMMON "auth-ip-acl",
#define AUTH_USAGE "auth-ip-acl=<IP address ACL to allow access> "
#else
#define MODULE_ARGUMENTS MODULE_ARGUMENTS_COMMON
#define AUTH_USAGE
#endif
- PA_MODULE_DESCRIPTION("ESOUND protocol "SOCKET_DESCRIPTION)
+ PA_MODULE_DESCRIPTION("ESOUND protocol "SOCKET_DESCRIPTION);
PA_MODULE_USAGE("sink=<sink to connect to> "
"source=<source to connect to> "
"auth-anonymous=<don't verify cookies?> "
"cookie=<path to cookie file> "
AUTH_USAGE
- SOCKET_USAGE)
+ SOCKET_USAGE);
#else
- #error "Broken build system"
+ #error "Broken build system"
#endif
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_LOAD_ONCE(FALSE);
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_VERSION(PACKAGE_VERSION);
static const char* const valid_modargs[] = {
MODULE_ARGUMENTS
@@ -202,10 +201,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)
@@ -215,10 +213,9 @@ int pa__init(pa_core *c, pa_module*m) {
#else
pa_socket_server *s;
int r;
- char tmp[PATH_MAX];
#endif
- assert(c && m);
+ pa_assert(m);
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
pa_log("Failed to parse module arguments");
@@ -236,57 +233,68 @@ 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)))
- pa_socket_server_unref(s_ipv4);
-
+ u->protocol_ipv4 = protocol_new(m->core, s_ipv4, m, ma);
if (s_ipv6)
- if (!(u->protocol_ipv6 = protocol_new(c, s_ipv6, m, ma)))
- pa_socket_server_unref(s_ipv6);
+ u->protocol_ipv6 = protocol_new(m->core, s_ipv6, m, ma);
if (!u->protocol_ipv4 && !u->protocol_ipv6)
goto fail;
-#else
+ if (s_ipv6)
+ pa_socket_server_unref(s_ipv6);
+ if (s_ipv6)
+ pa_socket_server_unref(s_ipv4);
- pa_runtime_path(pa_modargs_get_value(ma, "socket", UNIX_SOCKET), tmp, sizeof(tmp));
- u->socket_path = pa_xstrdup(tmp);
+#else
#if defined(USE_PROTOCOL_ESOUND)
+#if defined(USE_PER_USER_ESOUND_SOCKET)
+ u->socket_path = pa_sprintf_malloc("/tmp/.esd-%lu/socket", (unsigned long) getuid());
+#else
+ u->socket_path = pa_xstrdup("/tmp/.esd/socket");
+#endif
+
/* 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) {
- pa_log("Failed to create socket directory: %s\n", pa_cstrerror(errno));
+
+ if (pa_make_secure_parent_dir(u->socket_path, pa_in_system_mode() ? 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;
}
-#endif
-
- if ((r = pa_unix_socket_remove_stale(tmp)) < 0) {
- pa_log("Failed to remove stale UNIX socket '%s': %s", tmp, pa_cstrerror(errno));
+
+#else
+ if (!(u->socket_path = pa_runtime_path(pa_modargs_get_value(ma, "socket", UNIX_SOCKET)))) {
+ pa_log("Failed to generate socket path.");
goto fail;
}
-
- if (r)
- pa_log("Removed stale UNIX socket '%s'.", tmp);
-
- if (!(s = pa_socket_server_new_unix(c->mainloop, tmp)))
+#endif
+
+ if ((r = pa_unix_socket_remove_stale(u->socket_path)) < 0) {
+ pa_log("Failed to remove stale UNIX socket '%s': %s", u->socket_path, pa_cstrerror(errno));
goto fail;
+ } else if (r > 0)
+ pa_log_info("Removed stale UNIX socket '%s'.", u->socket_path);
- if (!(u->protocol_unix = protocol_new(c, s, m, ma)))
+ if (!(s = pa_socket_server_new_unix(m->core->mainloop, u->socket_path)))
goto fail;
+ if (!(u->protocol_unix = protocol_new(m->core, s, m, ma)))
+ goto fail;
+
+ pa_socket_server_unref(s);
+
#endif
m->userdata = u;
@@ -309,32 +317,29 @@ fail:
#else
if (u->protocol_unix)
protocol_free(u->protocol_unix);
-
- if (u->socket_path)
- pa_xfree(u->socket_path);
+ pa_xfree(u->socket_path);
#endif
pa_xfree(u);
- } else {
+ }
+
#if defined(USE_TCP_SOCKETS)
- if (s_ipv4)
- pa_socket_server_unref(s_ipv4);
- if (s_ipv6)
- pa_socket_server_unref(s_ipv6);
+ if (s_ipv4)
+ pa_socket_server_unref(s_ipv4);
+ if (s_ipv6)
+ pa_socket_server_unref(s_ipv6);
#else
- if (s)
- pa_socket_server_unref(s);
+ if (s)
+ pa_socket_server_unref(s);
#endif
- }
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;
@@ -347,15 +352,14 @@ void pa__done(pa_core *c, pa_module*m) {
if (u->protocol_unix)
protocol_free(u->protocol_unix);
-#if defined(USE_PROTOCOL_ESOUND)
+#if defined(USE_PROTOCOL_ESOUND) && !defined(USE_PER_USER_ESOUND_SOCKET)
if (u->socket_path) {
char *p = pa_parent_dir(u->socket_path);
rmdir(p);
pa_xfree(p);
}
#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..c87b1ece
--- /dev/null
+++ b/src/modules/module-remap-sink.c
@@ -0,0 +1,429 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2008 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_LOAD_ONCE(FALSE);
+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> "
+ "remix=<remix channels?>");
+
+struct userdata {
+ pa_core *core;
+ pa_module *module;
+
+ pa_sink *sink, *master;
+ pa_sink_input *sink_input;
+};
+
+static const char* const valid_modargs[] = {
+ "sink_name",
+ "master",
+ "master_channel_map",
+ "rate",
+ "format",
+ "channels",
+ "channel_map",
+ "remix",
+ 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;
+
+ /* Get the latency of the master sink */
+ if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
+ usec = 0;
+
+ /* Add the latency internal to our sink input on top */
+ usec += pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->master->sample_spec);
+
+ *((pa_usec_t*) data) = usec;
+ 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_IS_LINKED(state) &&
+ u->sink_input &&
+ PA_SINK_INPUT_IS_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 void sink_request_rewind(pa_sink *s) {
+ struct userdata *u;
+
+ pa_sink_assert_ref(s);
+ pa_assert_se(u = s->userdata);
+
+ pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes, TRUE, FALSE);
+}
+
+/* Called from I/O thread context */
+static void sink_update_requested_latency(pa_sink *s) {
+ struct userdata *u;
+
+ pa_sink_assert_ref(s);
+ pa_assert_se(u = s->userdata);
+
+ /* Just hand this one over to the master sink */
+ pa_sink_input_set_requested_latency_within_thread(
+ u->sink_input,
+ pa_sink_get_requested_latency_within_thread(s));
+}
+
+/* Called from I/O thread context */
+static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
+ struct userdata *u;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert(chunk);
+ pa_assert_se(u = i->userdata);
+
+ if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state))
+ return -1;
+
+ pa_sink_render(u->sink, nbytes, chunk);
+ return 0;
+}
+
+/* Called from I/O thread context */
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
+ struct userdata *u;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(u = i->userdata);
+ pa_assert(nbytes > 0);
+
+ if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state))
+ return;
+
+ if (u->sink->thread_info.rewind_nbytes > 0) {
+ size_t amount;
+
+ amount = PA_MIN(u->sink->thread_info.rewind_nbytes, nbytes);
+ u->sink->thread_info.rewind_nbytes = 0;
+
+ if (amount > 0)
+ pa_sink_process_rewind(u->sink, amount);
+ }
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
+ struct userdata *u;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(u = i->userdata);
+
+ if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
+ return;
+
+ pa_sink_set_max_rewind(u->sink, nbytes);
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {
+ struct userdata *u;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(u = i->userdata);
+
+ if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
+ return;
+
+ pa_sink_set_max_request(u->sink, nbytes);
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) {
+ struct userdata *u;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(u = i->userdata);
+
+ if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
+ return;
+
+ pa_sink_update_latency_range(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency);
+}
+
+/* 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);
+
+ if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
+ return;
+
+ pa_sink_detach_within_thread(u->sink);
+ pa_sink_set_asyncmsgq(u->sink, NULL);
+ pa_sink_set_rtpoll(u->sink, NULL);
+}
+
+/* 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);
+
+ if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
+ return;
+
+ 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);
+
+ pa_sink_update_latency_range(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency);
+}
+
+/* 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_unlink(u->sink);
+ pa_sink_input_unlink(u->sink_input);
+
+ pa_sink_unref(u->sink);
+ u->sink = NULL;
+ pa_sink_input_unref(u->sink_input);
+ u->sink_input = NULL;
+
+ pa_module_unload_request(u->module);
+}
+
+/* Called from IO thread context */
+static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) {
+ struct userdata *u;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(u = i->userdata);
+
+ /* If we are added for the first time, ask for a rewinding so that
+ * we are heard right-away. */
+ if (PA_SINK_INPUT_IS_LINKED(state) &&
+ i->thread_info.state == PA_SINK_INPUT_INIT) {
+ pa_log_debug("Requesting rewind due to state change.");
+ pa_sink_input_request_rewind(i, 0, FALSE, TRUE);
+ }
+}
+
+int pa__init(pa_module*m) {
+ struct userdata *u;
+ pa_sample_spec ss;
+ pa_channel_map sink_map, stream_map;
+ pa_modargs *ma;
+ const char *k;
+ pa_sink *master;
+ pa_sink_input_new_data sink_input_data;
+ pa_sink_new_data sink_data;
+ pa_bool_t remix = TRUE;
+
+ 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 channel map");
+ goto fail;
+ }
+
+ if (stream_map.channels != ss.channels) {
+ pa_log("Number of channels doesn't match");
+ goto fail;
+ }
+
+ if (pa_channel_map_equal(&stream_map, &master->channel_map))
+ pa_log_warn("No remapping configured, proceeding nonetheless!");
+
+ if (pa_modargs_get_value_boolean(ma, "remix", &remix) < 0) {
+ pa_log("Invalid boolean remix parameter");
+ goto fail;
+ }
+
+ u = pa_xnew0(struct userdata, 1);
+ u->core = m->core;
+ u->module = m;
+ m->userdata = u;
+ u->master = master;
+ u->sink = NULL;
+ u->sink_input = NULL;
+
+ /* Create sink */
+ pa_sink_new_data_init(&sink_data);
+ sink_data.driver = __FILE__;
+ sink_data.module = m;
+ if (!(sink_data.name = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", NULL))))
+ sink_data.name = pa_sprintf_malloc("%s.remapped", master->name);
+ pa_sink_new_data_set_sample_spec(&sink_data, &ss);
+ pa_sink_new_data_set_channel_map(&sink_data, &sink_map);
+ k = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION);
+ pa_proplist_setf(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Remapped %s", k ? k : master->name);
+ pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, master->name);
+ pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_CLASS, "filter");
+
+ u->sink = pa_sink_new(m->core, &sink_data, PA_SINK_LATENCY);
+ pa_sink_new_data_done(&sink_data);
+
+ if (!u->sink) {
+ 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->update_requested_latency = sink_update_requested_latency;
+ u->sink->request_rewind = sink_request_rewind;
+ u->sink->userdata = u;
+
+ 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(&sink_input_data);
+ sink_input_data.driver = __FILE__;
+ sink_input_data.module = m;
+ sink_input_data.sink = u->master;
+ pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Remapped Stream");
+ pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
+ pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss);
+ pa_sink_input_new_data_set_channel_map(&sink_input_data, &stream_map);
+
+ u->sink_input = pa_sink_input_new(m->core, &sink_input_data, PA_SINK_INPUT_DONT_MOVE | (remix ? 0 : PA_SINK_INPUT_NO_REMIX));
+ pa_sink_input_new_data_done(&sink_input_data);
+
+ if (!u->sink_input)
+ goto fail;
+
+ u->sink_input->pop = sink_input_pop_cb;
+ u->sink_input->process_rewind = sink_input_process_rewind_cb;
+ u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
+ u->sink_input->update_max_request = sink_input_update_max_request_cb;
+ u->sink_input->update_sink_latency_range = sink_input_update_sink_latency_range_cb;
+ u->sink_input->attach = sink_input_attach_cb;
+ u->sink_input->detach = sink_input_detach_cb;
+ u->sink_input->kill = sink_input_kill_cb;
+ u->sink_input->state_change = sink_input_state_change_cb;
+ u->sink_input->userdata = u;
+
+ pa_sink_put(u->sink);
+ pa_sink_input_put(u->sink_input);
+
+ pa_modargs_free(ma);
+
+ return 0;
+
+fail:
+ if (ma)
+ pa_modargs_free(ma);
+
+ pa__done(m);
+
+ return -1;
+}
+
+void pa__done(pa_module*m) {
+ struct userdata *u;
+
+ pa_assert(m);
+
+ if (!(u = m->userdata))
+ return;
+
+ if (u->sink) {
+ pa_sink_unlink(u->sink);
+ pa_sink_unref(u->sink);
+ }
+
+ if (u->sink_input) {
+ pa_sink_input_unlink(u->sink_input);
+ pa_sink_input_unref(u->sink_input);
+ }
+
+ pa_xfree(u);
+}
diff --git a/src/modules/module-rescue-streams.c b/src/modules/module-rescue-streams.c
index 7aa205bd..cc6717cb 100644
--- a/src/modules/module-rescue-streams.c
+++ b/src/modules/module-rescue-streams.c
@@ -1,18 +1,18 @@
-/* $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
@@ -34,9 +34,10 @@
#include "module-rescue-streams-symdef.h"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("When a sink/source is removed, try to move their streams to the default sink/source")
-PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("When a sink/source is removed, try to move their streams to the default sink/source");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
static const char* const valid_modargs[] = {
NULL,
@@ -49,73 +50,86 @@ struct userdata {
static pa_hook_result_t sink_hook_callback(pa_core *c, pa_sink *sink, void* userdata) {
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;
- }
- assert(target != sink);
+ 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;
+
+ 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) {
- pa_log_warn("Failed to move sink input %u \"%s\" to %s.", i->index, i->name, target->name);
+ if (pa_sink_input_move_to(i, target) < 0) {
+ pa_log_warn("Failed to move sink input %u \"%s\" to %s.", i->index, pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME), target->name);
return PA_HOOK_OK;
}
- pa_log_info("Sucessfully moved sink input %u \"%s\" to %s.", i->index, i->name, target->name);
+ pa_log_info("Sucessfully moved sink input %u \"%s\" to %s.", i->index, pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME), target->name);
}
-
+
return PA_HOOK_OK;
}
static pa_hook_result_t source_hook_callback(pa_core *c, pa_source *source, void* userdata) {
pa_source_output *o;
pa_source *target;
-
- assert(c);
- assert(source);
+
+ pa_assert(c);
+ pa_assert(source);
if (!pa_idxset_size(source->outputs)) {
pa_log_debug("No source outputs to move away.");
return PA_HOOK_OK;
}
-
- if (!(target = pa_namereg_get(c, NULL, PA_NAMEREG_SOURCE, 0))) {
- pa_log_info("No evacuation source found.");
- return PA_HOOK_OK;
+
+ if (!(target = pa_namereg_get(c, NULL, PA_NAMEREG_SOURCE, 0)) || target == source) {
+ uint32_t idx;
+
+ for (target = pa_idxset_first(c->sources, &idx); target; target = pa_idxset_next(c->sources, &idx))
+ if (target != source && !target->monitor_of == !source->monitor_of)
+ break;
+
+ if (!target) {
+ pa_log_info("No evacuation source found.");
+ return PA_HOOK_OK;
+ }
}
- assert(target != source);
+ pa_assert(target != source);
while ((o = pa_idxset_first(source->outputs, NULL))) {
if (pa_source_output_move_to(o, target) < 0) {
- pa_log_warn("Failed to move source output %u \"%s\" to %s.", o->index, o->name, target->name);
+ pa_log_warn("Failed to move source output %u \"%s\" to %s.", o->index, pa_proplist_gets(o->proplist, PA_PROP_APPLICATION_NAME), target->name);
return PA_HOOK_OK;
}
- pa_log_info("Sucessfully moved source output %u \"%s\" to %s.", o->index, o->name, target->name);
+ pa_log_info("Sucessfully moved source output %u \"%s\" to %s.", o->index, pa_proplist_gets(o->proplist, PA_PROP_APPLICATION_NAME), target->name);
}
-
+
return PA_HOOK_OK;
}
-int pa__init(pa_core *c, pa_module*m) {
+int pa__init(pa_module*m) {
pa_modargs *ma = NULL;
struct userdata *u;
-
- assert(c);
- assert(m);
+
+ pa_assert(m);
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
pa_log("Failed to parse module arguments");
@@ -123,18 +137,17 @@ int pa__init(pa_core *c, pa_module*m) {
}
m->userdata = u = pa_xnew(struct userdata, 1);
- u->sink_slot = pa_hook_connect(&c->hook_sink_disconnect, (pa_hook_cb_t) sink_hook_callback, NULL);
- u->source_slot = pa_hook_connect(&c->hook_source_disconnect, (pa_hook_cb_t) source_hook_callback, NULL);
+ u->sink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) sink_hook_callback, NULL);
+ u->source_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) source_hook_callback, NULL);
pa_modargs_free(ma);
return 0;
}
-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 (!m->userdata)
return;
diff --git a/src/modules/module-sine.c b/src/modules/module-sine.c
index f65b1f3a..38780f24 100644
--- a/src/modules/module-sine.c
+++ b/src/modules/module-sine.c
@@ -1,18 +1,18 @@
-/* $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
@@ -24,7 +24,6 @@
#endif
#include <stdio.h>
-#include <assert.h>
#include <math.h>
#include <pulse/xmalloc.h>
@@ -34,13 +33,15 @@
#include <pulsecore/modargs.h>
#include <pulsecore/namereg.h>
#include <pulsecore/log.h>
+#include <pulsecore/core-util.h>
#include "module-sine-symdef.h"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("Sine wave generator")
-PA_MODULE_USAGE("sink=<sink to connect to> frequency=<frequency in Hz>")
-PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Sine wave generator");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
+PA_MODULE_USAGE("sink=<sink to connect to> frequency=<frequency in Hz>");
struct userdata {
pa_core *core;
@@ -56,60 +57,80 @@ static const char* const valid_modargs[] = {
NULL,
};
-static int sink_input_peek(pa_sink_input *i, pa_memchunk *chunk) {
+static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
struct userdata *u;
- assert(i && chunk && i->userdata);
- u = i->userdata;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(u = i->userdata);
+ pa_assert(chunk);
chunk->memblock = pa_memblock_ref(u->memblock);
- chunk->index = u->peek_index;
chunk->length = pa_memblock_get_length(u->memblock) - u->peek_index;
+ chunk->index = u->peek_index;
+
+ u->peek_index = 0;
+
return 0;
}
-static void sink_input_drop(pa_sink_input *i, const pa_memchunk *chunk, size_t length) {
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
+ size_t l;
struct userdata *u;
- assert(i && chunk && length && i->userdata);
- u = i->userdata;
- assert(chunk->memblock == u->memblock);
- assert(length <= pa_memblock_get_length(u->memblock)-u->peek_index);
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(u = i->userdata);
- u->peek_index += length;
+ l = pa_memblock_get_length(u->memblock);
+ nbytes %= l;
- if (u->peek_index >= pa_memblock_get_length(u->memblock))
- u->peek_index = 0;
+ if (u->peek_index >= nbytes)
+ u->peek_index -= nbytes;
+ else
+ u->peek_index = l + u->peek_index - nbytes;
}
-static void sink_input_kill(pa_sink_input *i) {
+static void sink_input_kill_cb(pa_sink_input *i) {
struct userdata *u;
- assert(i && i->userdata);
- u = i->userdata;
- pa_sink_input_disconnect(u->sink_input);
+ 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_module_unload_request(u->module);
}
+/* Called from IO thread context */
+static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) {
+ struct userdata *u;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(u = i->userdata);
+
+ /* If we are added for the first time, ask for a rewinding so that
+ * we are heard right-away. */
+ if (PA_SINK_INPUT_IS_LINKED(state) &&
+ i->thread_info.state == PA_SINK_INPUT_INIT)
+ pa_sink_input_request_rewind(i, 0, FALSE, TRUE);
+}
+
static void calc_sine(float *f, size_t l, float freq) {
size_t i;
l /= sizeof(float);
-
+
for (i = 0; i < l; i++)
f[i] = (float) sin((double) i/l*M_PI*2*freq)/2;
}
-int pa__init(pa_core *c, pa_module*m) {
+int pa__init(pa_module*m) {
pa_modargs *ma = NULL;
struct userdata *u;
pa_sink *sink;
- const char *sink_name;
pa_sample_spec ss;
uint32_t frequency;
- char t[256];
void *p;
pa_sink_input_new_data data;
@@ -117,16 +138,15 @@ int pa__init(pa_core *c, pa_module*m) {
pa_log("Failed to parse module arguments");
goto fail;
}
-
- m->userdata = u = pa_xmalloc(sizeof(struct userdata));
- u->core = c;
+
+ m->userdata = u = pa_xnew0(struct userdata, 1);
+ u->core = m->core;
u->module = m;
u->sink_input = NULL;
u->memblock = NULL;
+ u->peek_index = 0;
- sink_name = pa_modargs_get_value(ma, "sink", NULL);
-
- if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK, 1))) {
+ if (!(sink = pa_namereg_get(m->core, pa_modargs_get_value(ma, "sink", NULL), PA_NAMEREG_SINK, 1))) {
pa_log("No such sink.");
goto fail;
}
@@ -140,56 +160,61 @@ int pa__init(pa_core *c, pa_module*m) {
pa_log("Invalid frequency specification");
goto fail;
}
-
- u->memblock = pa_memblock_new(c->mempool, pa_bytes_per_second(&ss));
+
+ u->memblock = pa_memblock_new(m->core->mempool, pa_bytes_per_second(&ss));
p = pa_memblock_acquire(u->memblock);
calc_sine(p, pa_memblock_get_length(u->memblock), frequency);
pa_memblock_release(u->memblock);
-
- snprintf(t, sizeof(t), "Sine Generator at %u Hz", frequency);
pa_sink_input_new_data_init(&data);
data.sink = sink;
data.driver = __FILE__;
- data.name = t;
+ pa_proplist_setf(data.proplist, PA_PROP_MEDIA_NAME, "%u Hz Sine", frequency);
+ pa_proplist_sets(data.proplist, PA_PROP_MEDIA_ROLE, "abstract");
+ pa_proplist_setf(data.proplist, "sine.hz", "%u", frequency);
pa_sink_input_new_data_set_sample_spec(&data, &ss);
data.module = m;
- if (!(u->sink_input = pa_sink_input_new(c, &data, 0)))
+ u->sink_input = pa_sink_input_new(m->core, &data, 0);
+ pa_sink_input_new_data_done(&data);
+
+ if (!u->sink_input)
goto fail;
- u->sink_input->peek = sink_input_peek;
- u->sink_input->drop = sink_input_drop;
- u->sink_input->kill = sink_input_kill;
+ u->sink_input->pop = sink_input_pop_cb;
+ u->sink_input->process_rewind = sink_input_process_rewind_cb;
+ u->sink_input->kill = sink_input_kill_cb;
+ u->sink_input->state_change = sink_input_state_change_cb;
u->sink_input->userdata = u;
- u->peek_index = 0;
-
+ pa_sink_input_put(u->sink_input);
+
pa_modargs_free(ma);
return 0;
-
+
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) {
- struct userdata *u = m->userdata;
- assert(c && m);
+void pa__done(pa_module*m) {
+ struct userdata *u;
+
+ pa_assert(m);
- if (!u)
+ if (!(u = m->userdata))
return;
if (u->sink_input) {
- pa_sink_input_disconnect(u->sink_input);
+ pa_sink_input_unlink(u->sink_input);
pa_sink_input_unref(u->sink_input);
}
-
+
if (u->memblock)
pa_memblock_unref(u->memblock);
+
pa_xfree(u);
}
-
diff --git a/src/modules/module-solaris.c b/src/modules/module-solaris.c
index 66968cb1..6f50543a 100644
--- a/src/modules/module-solaris.c
+++ b/src/modules/module-solaris.c
@@ -1,18 +1,19 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
+ Copyright 2006 Lennart Poettering
+ Copyright 2006-2007 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
@@ -25,7 +26,6 @@
#include <stdlib.h>
#include <stdio.h>
-#include <assert.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
@@ -54,6 +54,9 @@
#include <pulsecore/modargs.h>
#include <pulsecore/log.h>
#include <pulsecore/core-error.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/thread.h>
#include "module-solaris-symdef.h"
@@ -72,12 +75,14 @@ PA_MODULE_USAGE(
"channel_map=<channel map>")
struct userdata {
+ pa_core *core;
pa_sink *sink;
pa_source *source;
- pa_iochannel *io;
- pa_core *core;
- pa_time_event *timer;
- pa_usec_t poll_timeout;
+
+ pa_thread *thread;
+ pa_thread_mq thread_mq;
+ pa_rtpoll *rtpoll;
+
pa_signal_event *sig;
pa_memchunk memchunk;
@@ -87,9 +92,9 @@ struct userdata {
uint32_t frame_size;
uint32_t buffer_size;
unsigned int written_bytes, read_bytes;
- int sink_underflow;
int fd;
+ pa_rtpoll_item *rtpoll_item;
pa_module *module;
};
@@ -111,309 +116,357 @@ static const char* const valid_modargs[] = {
#define DEFAULT_SOURCE_NAME "solaris_input"
#define DEFAULT_DEVICE "/dev/audio"
-#define CHUNK_SIZE 2048
+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 err;
+ audio_info_t info;
-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));
-}
+ switch (code) {
+ case PA_SINK_MESSAGE_GET_LATENCY: {
+ pa_usec_t r = 0;
-static void do_write(struct userdata *u) {
- audio_info_t info;
- int err;
- size_t len;
- ssize_t r;
-
- assert(u);
+ if (u->fd >= 0) {
- /* We cannot check pa_iochannel_is_writable() because of our buffer hack */
- if (!u->sink)
- return;
+ err = ioctl(u->fd, AUDIO_GETINFO, &info);
+ pa_assert(err >= 0);
- update_usage(u);
+ r += pa_bytes_to_usec(u->written_bytes, &PA_SINK(o)->sample_spec);
+ r -= pa_bytes_to_usec(info.play.samples * u->frame_size, &PA_SINK(o)->sample_spec);
- err = ioctl(u->fd, AUDIO_GETINFO, &info);
- assert(err >= 0);
+ if (u->memchunk.memblock)
+ r += pa_bytes_to_usec(u->memchunk.length, &PA_SINK(o)->sample_spec);
+ }
- /*
- * Since we cannot modify the size of the output buffer we fake it
- * by not filling it more than u->buffer_size.
- */
- len = u->buffer_size;
- len -= u->written_bytes - (info.play.samples * u->frame_size);
+ *((pa_usec_t*) data) = r;
- /* The sample counter can sometimes go backwards :( */
- if (len > u->buffer_size)
- len = 0;
+ return 0;
+ }
- if (!u->sink_underflow && (len == u->buffer_size))
- pa_log_debug("Solaris buffer underflow!");
+ case PA_SINK_MESSAGE_SET_VOLUME:
+ if (u->fd >= 0) {
+ AUDIO_INITINFO(&info);
+
+ info.play.gain = pa_cvolume_avg((pa_cvolume*)data) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
+ assert(info.play.gain <= AUDIO_MAX_GAIN);
+
+ if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) {
+ if (errno == EINVAL)
+ pa_log("AUDIO_SETINFO: Unsupported volume.");
+ else
+ pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
+ } else {
+ return 0;
+ }
+ }
+ break;
- len -= len % u->frame_size;
+ case PA_SINK_MESSAGE_GET_VOLUME:
+ if (u->fd >= 0) {
+ err = ioctl(u->fd, AUDIO_GETINFO, &info);
+ assert(err >= 0);
- if (len == 0)
- return;
+ pa_cvolume_set((pa_cvolume*) data, ((pa_cvolume*) data)->channels,
+ info.play.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN);
- if (!u->memchunk.length) {
- if (pa_sink_render(u->sink, len, &u->memchunk) < 0) {
- u->sink_underflow = 1;
- return;
+ return 0;
}
- }
+ break;
- u->sink_underflow = 0;
-
- assert(u->memchunk.memblock);
- assert(u->memchunk.memblock->data);
- assert(u->memchunk.length);
+ case PA_SINK_MESSAGE_SET_MUTE:
+ if (u->fd >= 0) {
+ AUDIO_INITINFO(&info);
- if (u->memchunk.length < len) {
- len = u->memchunk.length;
- len -= len % u->frame_size;
- assert(len);
- }
+ info.output_muted = !!PA_PTR_TO_UINT(data);
- if ((r = pa_iochannel_write(u->io,
- (uint8_t*) u->memchunk.memblock->data + u->memchunk.index, len)) < 0) {
- pa_log("write() failed: %s", pa_cstrerror(errno));
- return;
- }
+ if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0)
+ pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
+ else
+ return 0;
+ }
+ break;
- assert(r % u->frame_size == 0);
-
- u->memchunk.index += r;
- u->memchunk.length -= r;
-
- if (u->memchunk.length <= 0) {
- pa_memblock_unref(u->memchunk.memblock);
- u->memchunk.memblock = NULL;
+ case PA_SINK_MESSAGE_GET_MUTE:
+ if (u->fd >= 0) {
+ err = ioctl(u->fd, AUDIO_GETINFO, &info);
+ pa_assert(err >= 0);
+
+ *(int*)data = !!info.output_muted;
+
+ return 0;
+ }
+ break;
}
- u->written_bytes += r;
+ return pa_sink_process_msg(o, code, data, offset, chunk);
}
-static void do_read(struct userdata *u) {
- pa_memchunk memchunk;
+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 err;
- size_t l;
- ssize_t r;
- assert(u);
-
- if (!u->source || !pa_iochannel_is_readable(u->io))
- return;
+ audio_info_t info;
- update_usage(u);
+ switch (code) {
+ case PA_SOURCE_MESSAGE_GET_LATENCY: {
+ pa_usec_t r = 0;
- err = ioctl(u->fd, I_NREAD, &l);
- assert(err >= 0);
+ if (u->fd) {
+ err = ioctl(u->fd, AUDIO_GETINFO, &info);
+ pa_assert(err >= 0);
- /* This is to make sure it fits in the memory pool. Also, a page
- should be the most efficient transfer size. */
- if (l > u->page_size)
- l = u->page_size;
+ r += pa_bytes_to_usec(info.record.samples * u->frame_size, &PA_SOURCE(o)->sample_spec);
+ r -= pa_bytes_to_usec(u->read_bytes, &PA_SOURCE(o)->sample_spec);
+ }
- 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);
- if (errno != EAGAIN)
- pa_log("read() failed: %s", pa_cstrerror(errno));
- return;
- }
-
- assert(r <= (ssize_t) memchunk.memblock->length);
- memchunk.length = memchunk.memblock->length = r;
- memchunk.index = 0;
-
- pa_source_post(u->source, &memchunk);
- pa_memblock_unref(memchunk.memblock);
-
- u->read_bytes += r;
-}
+ *((pa_usec_t*) data) = r;
-static void io_callback(pa_iochannel *io, void*userdata) {
- struct userdata *u = userdata;
- assert(u);
- do_write(u);
- do_read(u);
-}
+ return 0;
+ }
-static void timer_cb(pa_mainloop_api*a, pa_time_event *e, const struct timeval *tv, void *userdata) {
- struct userdata *u = userdata;
- struct timeval ntv;
+ case PA_SOURCE_MESSAGE_SET_VOLUME:
+ if (u->fd >= 0) {
+ AUDIO_INITINFO(&info);
+
+ info.record.gain = pa_cvolume_avg((pa_cvolume*) data) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
+ assert(info.record.gain <= AUDIO_MAX_GAIN);
+
+ if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) {
+ if (errno == EINVAL)
+ pa_log("AUDIO_SETINFO: Unsupported volume.");
+ else
+ pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
+ } else {
+ return 0;
+ }
+ }
+ break;
- assert(u);
+ case PA_SOURCE_MESSAGE_GET_VOLUME:
+ if (u->fd >= 0) {
+ err = ioctl(u->fd, AUDIO_GETINFO, &info);
+ pa_assert(err >= 0);
- do_write(u);
+ pa_cvolume_set((pa_cvolume*) data, ((pa_cvolume*) data)->channels,
+ info.record.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN);
- pa_gettimeofday(&ntv);
- pa_timeval_add(&ntv, u->poll_timeout);
+ return 0;
+ }
+ break;
+ }
- a->time_restart(e, &ntv);
+ return pa_source_process_msg(o, code, data, offset, chunk);
}
-static void sig_callback(pa_mainloop_api *api, pa_signal_event*e, int sig, void *userdata) {
- struct userdata *u = userdata;
- pa_cvolume old_vol;
-
- assert(u);
+static void clear_underflow(struct userdata *u)
+{
+ audio_info_t info;
- if (u->sink) {
- assert(u->sink->get_hw_volume);
- memcpy(&old_vol, &u->sink->hw_volume, sizeof(pa_cvolume));
- if (u->sink->get_hw_volume(u->sink) < 0)
- return;
- if (memcmp(&old_vol, &u->sink->hw_volume, sizeof(pa_cvolume)) != 0) {
- pa_subscription_post(u->sink->core,
- PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE,
- u->sink->index);
- }
- }
+ AUDIO_INITINFO(&info);
- if (u->source) {
- assert(u->source->get_hw_volume);
- memcpy(&old_vol, &u->source->hw_volume, sizeof(pa_cvolume));
- if (u->source->get_hw_volume(u->source) < 0)
- return;
- if (memcmp(&old_vol, &u->source->hw_volume, sizeof(pa_cvolume)) != 0) {
- pa_subscription_post(u->source->core,
- PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE,
- u->source->index);
- }
- }
+ info.play.error = 0;
+
+ if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0)
+ pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
}
-static pa_usec_t sink_get_latency_cb(pa_sink *s) {
- pa_usec_t r = 0;
+static void clear_overflow(struct userdata *u)
+{
audio_info_t info;
- int err;
- struct userdata *u = s->userdata;
- assert(s && u && u->sink);
-
- err = ioctl(u->fd, AUDIO_GETINFO, &info);
- assert(err >= 0);
- r += pa_bytes_to_usec(u->written_bytes, &s->sample_spec);
- r -= pa_bytes_to_usec(info.play.samples * u->frame_size, &s->sample_spec);
+ AUDIO_INITINFO(&info);
- if (u->memchunk.memblock)
- r += pa_bytes_to_usec(u->memchunk.length, &s->sample_spec);
+ info.record.error = 0;
- return r;
+ if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0)
+ pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
}
-static pa_usec_t source_get_latency_cb(pa_source *s) {
- pa_usec_t r = 0;
- struct userdata *u = s->userdata;
- audio_info_t info;
- int err;
- assert(s && u && u->source);
+static void thread_func(void *userdata) {
+ struct userdata *u = userdata;
+ unsigned short revents = 0;
+ int ret;
- err = ioctl(u->fd, AUDIO_GETINFO, &info);
- assert(err >= 0);
+ pa_assert(u);
- r += pa_bytes_to_usec(info.record.samples * u->frame_size, &s->sample_spec);
- r -= pa_bytes_to_usec(u->read_bytes, &s->sample_spec);
+ pa_log_debug("Thread starting up");
- return r;
-}
+ if (u->core->high_priority)
+ pa_make_realtime();
-static int sink_get_hw_volume_cb(pa_sink *s) {
- struct userdata *u = s->userdata;
- audio_info_t info;
- int err;
+ pa_thread_mq_install(&u->thread_mq);
+ pa_rtpoll_install(u->rtpoll);
- err = ioctl(u->fd, AUDIO_GETINFO, &info);
- assert(err >= 0);
+ for (;;) {
+ /* Render some data and write it to the dsp */
- pa_cvolume_set(&s->hw_volume, s->hw_volume.channels,
- info.play.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN);
+ if (u->sink && PA_SINK_OPENED(u->sink->thread_info.state)) {
+ audio_info_t info;
+ int err;
+ size_t len;
- return 0;
-}
+ err = ioctl(u->fd, AUDIO_GETINFO, &info);
+ pa_assert(err >= 0);
-static int sink_set_hw_volume_cb(pa_sink *s) {
- struct userdata *u = s->userdata;
- audio_info_t info;
+ /*
+ * Since we cannot modify the size of the output buffer we fake it
+ * by not filling it more than u->buffer_size.
+ */
+ len = u->buffer_size;
+ len -= u->written_bytes - (info.play.samples * u->frame_size);
- AUDIO_INITINFO(&info);
+ /* The sample counter can sometimes go backwards :( */
+ if (len > u->buffer_size)
+ len = 0;
- info.play.gain = pa_cvolume_avg(&s->hw_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
- assert(info.play.gain <= AUDIO_MAX_GAIN);
+ if (info.play.error) {
+ pa_log_debug("Solaris buffer underflow!");
+ clear_underflow(u);
+ }
- if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) {
- if (errno == EINVAL)
- pa_log("AUDIO_SETINFO: Unsupported volume.");
- else
- pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
- return -1;
- }
+ len -= len % u->frame_size;
- return 0;
-}
+ while (len) {
+ void *p;
+ ssize_t r;
-static int sink_get_hw_mute_cb(pa_sink *s) {
- struct userdata *u = s->userdata;
- audio_info_t info;
- int err;
+ if (!u->memchunk.length)
+ pa_sink_render(u->sink, len, &u->memchunk);
- err = ioctl(u->fd, AUDIO_GETINFO, &info);
- assert(err >= 0);
+ pa_assert(u->memchunk.length);
- s->hw_muted = !!info.output_muted;
+ p = pa_memblock_acquire(u->memchunk.memblock);
+ r = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length, NULL);
+ pa_memblock_release(u->memchunk.memblock);
- return 0;
-}
+ if (r < 0) {
+ if (errno == EINTR)
+ continue;
+ else if (errno != EAGAIN) {
+ pa_log("Failed to read data from DSP: %s", pa_cstrerror(errno));
+ goto fail;
+ }
+ } else {
+ pa_assert(r % u->frame_size == 0);
-static int sink_set_hw_mute_cb(pa_sink *s) {
- struct userdata *u = s->userdata;
- audio_info_t info;
+ u->memchunk.index += r;
+ u->memchunk.length -= r;
- AUDIO_INITINFO(&info);
+ if (u->memchunk.length <= 0) {
+ pa_memblock_unref(u->memchunk.memblock);
+ pa_memchunk_reset(&u->memchunk);
+ }
- info.output_muted = !!s->hw_muted;
+ len -= r;
+ u->written_bytes += r;
+ }
+ }
+ }
- if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) {
- pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
- return -1;
- }
+ /* 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))) {
+ pa_memchunk memchunk;
+ int err;
+ size_t l;
+ void *p;
+ ssize_t r;
+ audio_info_t info;
+
+ err = ioctl(u->fd, AUDIO_GETINFO, &info);
+ pa_assert(err >= 0);
+
+ if (info.record.error) {
+ pa_log_debug("Solaris buffer overflow!");
+ clear_overflow(u);
+ }
+
+ err = ioctl(u->fd, I_NREAD, &l);
+ pa_assert(err >= 0);
+
+ if (l > 0) {
+ /* This is to make sure it fits in the memory pool. Also, a page
+ should be the most efficient transfer size. */
+ if (l > u->page_size)
+ l = u->page_size;
+
+ memchunk.memblock = pa_memblock_new(u->core->mempool, l);
+ pa_assert(memchunk.memblock);
+
+ p = pa_memblock_acquire(memchunk.memblock);
+ r = pa_read(u->fd, p, l, NULL);
+ pa_memblock_release(memchunk.memblock);
+
+ if (r < 0) {
+ pa_memblock_unref(memchunk.memblock);
+ if (errno != EAGAIN) {
+ pa_log("Failed to read data from DSP: %s", pa_cstrerror(errno));
+ goto fail;
+ }
+ } else {
+ memchunk.index = 0;
+ memchunk.length = r;
+
+ pa_source_post(u->source, &memchunk);
+ pa_memblock_unref(memchunk.memblock);
+
+ u->read_bytes += r;
+
+ revents &= ~POLLIN;
+ }
+ }
+ }
- return 0;
-}
+ if (u->fd >= 0) {
+ struct pollfd *pollfd;
-static int source_get_hw_volume_cb(pa_source *s) {
- struct userdata *u = s->userdata;
- audio_info_t info;
- int err;
+ pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+ pollfd->events =
+ ((u->source && PA_SOURCE_OPENED(u->source->thread_info.state)) ? POLLIN : 0);
+ }
- err = ioctl(u->fd, AUDIO_GETINFO, &info);
- assert(err >= 0);
+ /* Hmm, nothing to do. Let's sleep */
+ if ((ret = pa_rtpoll_run(u->rtpoll, 1)) < 0)
+ goto fail;
- pa_cvolume_set(&s->hw_volume, s->hw_volume.channels,
- info.record.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN);
+ if (ret == 0)
+ goto finish;
- return 0;
-}
+ if (u->fd >= 0) {
+ struct pollfd *pollfd;
-static int source_set_hw_volume_cb(pa_source *s) {
- struct userdata *u = s->userdata;
- audio_info_t info;
+ pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
- AUDIO_INITINFO(&info);
+ if (pollfd->revents & ~(POLLOUT|POLLIN)) {
+ pa_log("DSP shutdown.");
+ goto fail;
+ }
- info.record.gain = pa_cvolume_avg(&s->hw_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
- assert(info.record.gain <= AUDIO_MAX_GAIN);
+ revents = pollfd->revents;
+ } else
+ revents = 0;
+ }
- if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) {
- if (errno == EINVAL)
- pa_log("AUDIO_SETINFO: Unsupported volume.");
- else
- pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
- return -1;
+fail:
+ /* We have to continue processing messages until we receive the
+ * SHUTDOWN message */
+ 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 sig_callback(pa_mainloop_api *api, pa_signal_event*e, int sig, void *userdata) {
+ struct userdata *u = userdata;
+
+ assert(u);
+
+ if (u->sink) {
+ pa_sink_get_volume(u->sink);
+ pa_sink_get_mute(u->sink);
}
- return 0;
+ if (u->source)
+ pa_source_get_volume(u->source);
}
static int pa_solaris_auto_format(int fd, int mode, pa_sample_spec *ss) {
@@ -487,6 +540,7 @@ static int pa_solaris_set_buffer(int fd, int buffer_size) {
AUDIO_INITINFO(&info);
+ info.play.buffer_size = buffer_size;
info.record.buffer_size = buffer_size;
if (ioctl(fd, AUDIO_SETINFO, &info) < 0) {
@@ -500,7 +554,7 @@ static int pa_solaris_set_buffer(int fd, int buffer_size) {
return 0;
}
-int pa__init(pa_core *c, pa_module*m) {
+int pa__init(pa_module *m) {
struct userdata *u = NULL;
const char *p;
int fd = -1;
@@ -510,15 +564,16 @@ int pa__init(pa_core *c, pa_module*m) {
pa_sample_spec ss;
pa_channel_map map;
pa_modargs *ma = NULL;
- struct timeval tv;
char *t;
- assert(c && m);
+ struct pollfd *pollfd;
+
+ pa_assert(m);
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 argument.");
goto fail;
@@ -531,18 +586,18 @@ int pa__init(pa_core *c, pa_module*m) {
mode = (playback&&record) ? O_RDWR : (playback ? O_WRONLY : (record ? O_RDONLY : 0));
- buffer_size = 16384;
+ buffer_size = 16384;
if (pa_modargs_get_value_s32(ma, "buffer_size", &buffer_size) < 0) {
pa_log("failed to parse buffer size argument");
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("failed to parse sample specification");
goto fail;
}
-
+
if ((fd = open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), mode | O_NONBLOCK)) < 0)
goto fail;
@@ -551,55 +606,18 @@ int pa__init(pa_core *c, pa_module*m) {
if (pa_solaris_auto_format(fd, mode, &ss) < 0)
goto fail;
- if ((mode != O_WRONLY) && (buffer_size >= 1))
- if (pa_solaris_set_buffer(fd, buffer_size) < 0)
- goto fail;
+ if (pa_solaris_set_buffer(fd, buffer_size) < 0)
+ goto fail;
u = pa_xmalloc(sizeof(struct userdata));
- u->core = c;
-
- if (mode != O_WRONLY) {
- u->source = pa_source_new(c, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, &map);
- assert(u->source);
- u->source->userdata = u;
- u->source->get_latency = source_get_latency_cb;
- u->source->get_hw_volume = source_get_hw_volume_cb;
- u->source->set_hw_volume = source_set_hw_volume_cb;
- pa_source_set_owner(u->source, m);
- pa_source_set_description(u->source, t = pa_sprintf_malloc("Solaris PCM on '%s'", p));
- pa_xfree(t);
- u->source->is_hardware = 1;
- } else
- u->source = NULL;
+ u->core = m->core;
- if (mode != O_RDONLY) {
- u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, &map);
- assert(u->sink);
- u->sink->get_latency = sink_get_latency_cb;
- u->sink->get_hw_volume = sink_get_hw_volume_cb;
- u->sink->set_hw_volume = sink_set_hw_volume_cb;
- u->sink->get_hw_mute = sink_get_hw_mute_cb;
- u->sink->set_hw_mute = sink_set_hw_mute_cb;
- u->sink->userdata = u;
- pa_sink_set_owner(u->sink, m);
- pa_sink_set_description(u->sink, t = pa_sprintf_malloc("Solaris PCM on '%s'", p));
- pa_xfree(t);
- u->sink->is_hardware = 1;
- } else
- u->sink = NULL;
-
- assert(u->source || u->sink);
-
- u->io = pa_iochannel_new(c->mainloop, u->source ? fd : -1, u->sink ? fd : 0);
- assert(u->io);
- pa_iochannel_set_callback(u->io, io_callback, u);
u->fd = fd;
- u->memchunk.memblock = NULL;
- u->memchunk.length = 0;
+ pa_memchunk_reset(&u->memchunk);
/* We use this to get a reasonable chunk size */
- u->page_size = sysconf(_SC_PAGESIZE);
+ u->page_size = PA_PAGE_SIZE;
u->frame_size = pa_frame_size(&ss);
u->buffer_size = buffer_size;
@@ -607,70 +625,140 @@ int pa__init(pa_core *c, pa_module*m) {
u->written_bytes = 0;
u->read_bytes = 0;
- u->sink_underflow = 1;
-
u->module = m;
m->userdata = u;
- u->poll_timeout = pa_bytes_to_usec(u->buffer_size / 10, &ss);
+ 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_gettimeofday(&tv);
- pa_timeval_add(&tv, u->poll_timeout);
+ pa_rtpoll_set_timer_periodic(u->rtpoll, pa_bytes_to_usec(u->buffer_size / 10, &ss));
- u->timer = c->mainloop->time_new(c->mainloop, &tv, timer_cb, u);
- assert(u->timer);
+ 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 = fd;
+ pollfd->events = 0;
+ pollfd->revents = 0;
+
+ if (mode != O_WRONLY) {
+ u->source = pa_source_new(m->core, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, &map);
+ pa_assert(u->source);
+
+ u->source->userdata = u;
+ u->source->parent.process_msg = source_process_msg;
+
+ pa_source_set_module(u->source, m);
+ pa_source_set_description(u->source, t = pa_sprintf_malloc("Solaris PCM on '%s'", p));
+ pa_xfree(t);
+ pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
+ pa_source_set_rtpoll(u->source, u->rtpoll);
+
+ u->source->flags = PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY|PA_SOURCE_HW_VOLUME_CTRL;
+ u->source->refresh_volume = 1;
+ } else
+ u->source = NULL;
+
+ if (mode != O_RDONLY) {
+ u->sink = pa_sink_new(m->core, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, &map);
+ pa_assert(u->sink);
+
+ u->sink->userdata = u;
+ u->sink->parent.process_msg = sink_process_msg;
+
+ pa_sink_set_module(u->sink, m);
+ pa_sink_set_description(u->sink, t = pa_sprintf_malloc("Solaris PCM on '%s'", p));
+ pa_xfree(t);
+ pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
+ pa_sink_set_rtpoll(u->sink, u->rtpoll);
+
+ u->sink->flags = PA_SINK_HARDWARE|PA_SINK_LATENCY|PA_SINK_HW_VOLUME_CTRL;
+ u->sink->refresh_volume = 1;
+ u->sink->refresh_mute = 1;
+ } else
+ u->sink = NULL;
+
+ pa_assert(u->source || u->sink);
u->sig = pa_signal_new(SIGPOLL, sig_callback, u);
- assert(u->sig);
+ pa_assert(u->sig);
ioctl(u->fd, I_SETSIG, S_MSG);
- pa_modargs_free(ma);
+ 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_cb(u->source);
+ pa_asyncmsgq_send(u->thread_mq.inq, PA_MSGOBJECT(u->source), PA_SOURCE_MESSAGE_GET_VOLUME, &u->source->volume, 0, NULL);
if (u->sink) {
- sink_get_hw_volume_cb(u->sink);
- sink_get_hw_mute_cb(u->sink);
+ pa_asyncmsgq_send(u->thread_mq.inq, PA_MSGOBJECT(u->sink), PA_SINK_MESSAGE_GET_VOLUME, &u->sink->volume, 0, NULL);
+ pa_asyncmsgq_send(u->thread_mq.inq, PA_MSGOBJECT(u->sink), PA_SINK_MESSAGE_GET_MUTE, &u->sink->muted, 0, NULL);
}
+ if (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)
+ if (u)
+ pa__done(m);
+ else if (fd >= 0)
close(fd);
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;
- assert(c && m);
+
+ pa_assert(m);
if (!(u = m->userdata))
return;
- if (u->timer)
- c->mainloop->time_free(u->timer);
ioctl(u->fd, I_SETSIG, 0);
pa_signal_free(u->sig);
-
- if (u->memchunk.memblock)
- pa_memblock_unref(u->memchunk.memblock);
- if (u->sink) {
- pa_sink_disconnect(u->sink);
- pa_sink_unref(u->sink);
+ 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);
}
-
- if (u->source) {
- pa_source_disconnect(u->source);
+
+ pa_thread_mq_done(&u->thread_mq);
+
+ if (u->sink)
+ pa_sink_unref(u->sink);
+
+ if (u->source)
pa_source_unref(u->source);
- }
-
- pa_iochannel_free(u->io);
+
+ if (u->memchunk.memblock)
+ pa_memblock_unref(u->memchunk.memblock);
+
+ if (u->rtpoll_item)
+ pa_rtpoll_item_free(u->rtpoll_item);
+
+ if (u->rtpoll)
+ pa_rtpoll_free(u->rtpoll);
+
+ if (u->fd >= 0)
+ close(u->fd);
+
pa_xfree(u);
}
diff --git a/src/modules/module-suspend-on-idle.c b/src/modules/module-suspend-on-idle.c
new file mode 100644
index 00000000..bc7c023c
--- /dev/null
+++ b/src/modules/module-suspend-on-idle.c
@@ -0,0 +1,444 @@
+/***
+ 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 <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+
+#include <pulsecore/core.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/source-output.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/namereg.h>
+
+#include "module-suspend-on-idle-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("When a sink/source is idle for too long, suspend it");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+
+static const char* const valid_modargs[] = {
+ "timeout",
+ NULL,
+};
+
+struct userdata {
+ pa_core *core;
+ pa_usec_t timeout;
+ pa_hashmap *device_infos;
+ pa_hook_slot
+ *sink_new_slot,
+ *source_new_slot,
+ *sink_unlink_slot,
+ *source_unlink_slot,
+ *sink_state_changed_slot,
+ *source_state_changed_slot;
+
+ pa_hook_slot
+ *sink_input_new_slot,
+ *source_output_new_slot,
+ *sink_input_unlink_slot,
+ *source_output_unlink_slot,
+ *sink_input_move_slot,
+ *source_output_move_slot,
+ *sink_input_state_changed_slot,
+ *source_output_state_changed_slot;
+};
+
+struct device_info {
+ struct userdata *userdata;
+ pa_sink *sink;
+ pa_source *source;
+ struct timeval last_use;
+ pa_time_event *time_event;
+};
+
+static void timeout_cb(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) {
+ struct device_info *d = userdata;
+
+ pa_assert(d);
+
+ d->userdata->core->mainloop->time_restart(d->time_event, NULL);
+
+ if (d->sink && pa_sink_used_by(d->sink) <= 0 && pa_sink_get_state(d->sink) != PA_SINK_SUSPENDED) {
+ pa_log_info("Sink %s idle for too long, suspending ...", d->sink->name);
+ pa_sink_suspend(d->sink, TRUE);
+ }
+
+ if (d->source && pa_source_used_by(d->source) <= 0 && pa_source_get_state(d->source) != PA_SOURCE_SUSPENDED) {
+ pa_log_info("Source %s idle for too long, suspending ...", d->source->name);
+ pa_source_suspend(d->source, TRUE);
+ }
+}
+
+static void restart(struct device_info *d) {
+ struct timeval tv;
+ pa_assert(d);
+
+ pa_gettimeofday(&tv);
+ d->last_use = tv;
+ pa_timeval_add(&tv, d->userdata->timeout*1000000);
+ d->userdata->core->mainloop->time_restart(d->time_event, &tv);
+
+ if (d->sink)
+ pa_log_debug("Sink %s becomes idle.", d->sink->name);
+ if (d->source)
+ pa_log_debug("Source %s becomes idle.", d->source->name);
+}
+
+static void resume(struct device_info *d) {
+ pa_assert(d);
+
+ d->userdata->core->mainloop->time_restart(d->time_event, NULL);
+
+ if (d->sink) {
+ pa_sink_suspend(d->sink, FALSE);
+
+ pa_log_debug("Sink %s becomes busy.", d->sink->name);
+ }
+
+ if (d->source) {
+ pa_source_suspend(d->source, FALSE);
+
+ pa_log_debug("Source %s becomes busy.", d->source->name);
+ }
+}
+
+static pa_hook_result_t sink_input_fixate_hook_cb(pa_core *c, pa_sink_input_new_data *data, struct userdata *u) {
+ struct device_info *d;
+
+ pa_assert(c);
+ pa_assert(data);
+ pa_assert(u);
+
+ if ((d = pa_hashmap_get(u->device_infos, data->sink)))
+ resume(d);
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_output_fixate_hook_cb(pa_core *c, pa_source_output_new_data *data, struct userdata *u) {
+ struct device_info *d;
+
+ pa_assert(c);
+ pa_assert(data);
+ pa_assert(u);
+
+ if ((d = pa_hashmap_get(u->device_infos, data->source)))
+ resume(d);
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_input_unlink_hook_cb(pa_core *c, pa_sink_input *s, struct userdata *u) {
+ pa_assert(c);
+ pa_sink_input_assert_ref(s);
+ pa_assert(u);
+
+ if (pa_sink_used_by(s->sink) <= 0) {
+ struct device_info *d;
+ if ((d = pa_hashmap_get(u->device_infos, s->sink)))
+ restart(d);
+ }
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_output_unlink_hook_cb(pa_core *c, pa_source_output *s, struct userdata *u) {
+ pa_assert(c);
+ pa_source_output_assert_ref(s);
+ pa_assert(u);
+
+ if (pa_source_used_by(s->source) <= 0) {
+ struct device_info *d;
+ if ((d = pa_hashmap_get(u->device_infos, s->source)))
+ restart(d);
+ }
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_input_move_hook_cb(pa_core *c, pa_sink_input_move_hook_data *data, struct userdata *u) {
+ struct device_info *d;
+
+ pa_assert(c);
+ pa_assert(data);
+ pa_assert(u);
+
+ if ((d = pa_hashmap_get(u->device_infos, data->destination)))
+ resume(d);
+
+ if (pa_sink_used_by(data->sink_input->sink) <= 1)
+ if ((d = pa_hashmap_get(u->device_infos, data->sink_input->sink)))
+ restart(d);
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_output_move_hook_cb(pa_core *c, pa_source_output_move_hook_data *data, struct userdata *u) {
+ struct device_info *d;
+
+ pa_assert(c);
+ pa_assert(data);
+ pa_assert(u);
+
+ if ((d = pa_hashmap_get(u->device_infos, data->destination)))
+ resume(d);
+
+ if (pa_source_used_by(data->source_output->source) <= 1)
+ if ((d = pa_hashmap_get(u->device_infos, data->source_output->source)))
+ restart(d);
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_input_state_changed_hook_cb(pa_core *c, pa_sink_input *s, struct userdata *u) {
+ struct device_info *d;
+ pa_sink_input_state_t state;
+ pa_assert(c);
+ pa_sink_input_assert_ref(s);
+ pa_assert(u);
+
+ state = pa_sink_input_get_state(s);
+ if (state == PA_SINK_INPUT_RUNNING || state == PA_SINK_INPUT_DRAINED)
+ if ((d = pa_hashmap_get(u->device_infos, s->sink)))
+ resume(d);
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_output_state_changed_hook_cb(pa_core *c, pa_source_output *s, struct userdata *u) {
+ struct device_info *d;
+ pa_source_output_state_t state;
+ pa_assert(c);
+ pa_source_output_assert_ref(s);
+ pa_assert(u);
+
+ state = pa_source_output_get_state(s);
+ if (state == PA_SOURCE_OUTPUT_RUNNING)
+ if ((d = pa_hashmap_get(u->device_infos, s->source)))
+ resume(d);
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t device_new_hook_cb(pa_core *c, pa_object *o, struct userdata *u) {
+ struct device_info *d;
+ pa_source *source;
+ pa_sink *sink;
+
+ pa_assert(c);
+ pa_object_assert_ref(o);
+ pa_assert(u);
+
+ source = pa_source_isinstance(o) ? PA_SOURCE(o) : NULL;
+ sink = pa_sink_isinstance(o) ? PA_SINK(o) : NULL;
+
+ pa_assert(source || sink);
+
+ d = pa_xnew(struct device_info, 1);
+ d->userdata = u;
+ d->source = source ? pa_source_ref(source) : NULL;
+ d->sink = sink ? pa_sink_ref(sink) : NULL;
+ d->time_event = c->mainloop->time_new(c->mainloop, NULL, timeout_cb, d);
+ pa_hashmap_put(u->device_infos, o, d);
+
+ if ((d->sink && pa_sink_used_by(d->sink) <= 0) ||
+ (d->source && pa_source_used_by(d->source) <= 0))
+ restart(d);
+
+ return PA_HOOK_OK;
+}
+
+static void device_info_free(struct device_info *d) {
+ pa_assert(d);
+
+ if (d->source)
+ pa_source_unref(d->source);
+ if (d->sink)
+ pa_sink_unref(d->sink);
+
+ d->userdata->core->mainloop->time_free(d->time_event);
+
+ pa_xfree(d);
+}
+
+static pa_hook_result_t device_unlink_hook_cb(pa_core *c, pa_object *o, struct userdata *u) {
+ struct device_info *d;
+
+ pa_assert(c);
+ pa_object_assert_ref(o);
+ pa_assert(u);
+
+ if ((d = pa_hashmap_remove(u->device_infos, o)))
+ device_info_free(d);
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t device_state_changed_hook_cb(pa_core *c, pa_object *o, struct userdata *u) {
+ struct device_info *d;
+
+ pa_assert(c);
+ pa_object_assert_ref(o);
+ pa_assert(u);
+
+ if (!(d = pa_hashmap_get(u->device_infos, o)))
+ return PA_HOOK_OK;
+
+ if (pa_sink_isinstance(o)) {
+ pa_sink *s = PA_SINK(o);
+ pa_sink_state_t state = pa_sink_get_state(s);
+
+ if (pa_sink_used_by(s) <= 0) {
+
+ if (PA_SINK_IS_OPENED(state))
+ restart(d);
+
+ }
+
+ } else if (pa_source_isinstance(o)) {
+ pa_source *s = PA_SOURCE(o);
+ pa_source_state_t state = pa_source_get_state(s);
+
+ if (pa_source_used_by(s) <= 0) {
+
+ if (PA_SOURCE_IS_OPENED(state))
+ restart(d);
+ }
+ }
+
+ return PA_HOOK_OK;
+}
+
+int pa__init(pa_module*m) {
+ pa_modargs *ma = NULL;
+ struct userdata *u;
+ uint32_t timeout = 1;
+ uint32_t idx;
+ pa_sink *sink;
+ pa_source *source;
+
+ pa_assert(m);
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log("Failed to parse module arguments.");
+ goto fail;
+ }
+
+ if (pa_modargs_get_value_u32(ma, "timeout", &timeout) < 0) {
+ pa_log("Failed to parse timeout value.");
+ goto fail;
+ }
+
+ m->userdata = u = pa_xnew(struct userdata, 1);
+ u->core = m->core;
+ u->timeout = timeout;
+ u->device_infos = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+
+ for (sink = pa_idxset_first(m->core->sinks, &idx); sink; sink = pa_idxset_next(m->core->sinks, &idx))
+ device_new_hook_cb(m->core, PA_OBJECT(sink), u);
+
+ for (source = pa_idxset_first(m->core->sources, &idx); source; source = pa_idxset_next(m->core->sources, &idx))
+ device_new_hook_cb(m->core, PA_OBJECT(source), u);
+
+ u->sink_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_NORMAL, (pa_hook_cb_t) device_new_hook_cb, u);
+ u->source_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_NORMAL, (pa_hook_cb_t) device_new_hook_cb, u);
+ u->sink_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK_POST], PA_HOOK_NORMAL, (pa_hook_cb_t) device_unlink_hook_cb, u);
+ u->source_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK_POST], PA_HOOK_NORMAL, (pa_hook_cb_t) device_unlink_hook_cb, u);
+ u->sink_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) device_state_changed_hook_cb, u);
+ u->source_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) device_state_changed_hook_cb, u);
+
+ u->sink_input_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_fixate_hook_cb, u);
+ u->source_output_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_fixate_hook_cb, u);
+ u->sink_input_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK_POST], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_unlink_hook_cb, u);
+ u->source_output_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_unlink_hook_cb, u);
+ u->sink_input_move_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_move_hook_cb, u);
+ u->source_output_move_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_move_hook_cb, u);
+ u->sink_input_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_state_changed_hook_cb, u);
+ u->source_output_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_state_changed_hook_cb, u);
+
+ pa_modargs_free(ma);
+ return 0;
+
+fail:
+
+ if (ma)
+ pa_modargs_free(ma);
+
+ return -1;
+}
+
+void pa__done(pa_module*m) {
+ struct userdata *u;
+ struct device_info *d;
+
+ pa_assert(m);
+
+ if (!m->userdata)
+ return;
+
+ u = m->userdata;
+
+ 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->source_new_slot)
+ pa_hook_slot_free(u->source_new_slot);
+ if (u->source_unlink_slot)
+ pa_hook_slot_free(u->source_unlink_slot);
+ if (u->source_state_changed_slot)
+ pa_hook_slot_free(u->source_state_changed_slot);
+
+ if (u->sink_input_new_slot)
+ pa_hook_slot_free(u->sink_input_new_slot);
+ if (u->sink_input_unlink_slot)
+ pa_hook_slot_free(u->sink_input_unlink_slot);
+ if (u->sink_input_move_slot)
+ pa_hook_slot_free(u->sink_input_move_slot);
+ if (u->sink_input_state_changed_slot)
+ pa_hook_slot_free(u->sink_input_state_changed_slot);
+
+ if (u->source_output_new_slot)
+ pa_hook_slot_free(u->source_output_new_slot);
+ if (u->source_output_unlink_slot)
+ pa_hook_slot_free(u->source_output_unlink_slot);
+ if (u->source_output_move_slot)
+ pa_hook_slot_free(u->source_output_move_slot);
+ if (u->source_output_state_changed_slot)
+ pa_hook_slot_free(u->source_output_state_changed_slot);
+
+ while ((d = pa_hashmap_steal_first(u->device_infos)))
+ device_info_free(d);
+
+ pa_hashmap_free(u->device_infos, NULL, NULL);
+
+ pa_xfree(u);
+}
diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c
index a110c57e..86f30817 100644
--- a/src/modules/module-tunnel.c
+++ b/src/modules/module-tunnel.c
@@ -1,18 +1,19 @@
-/* $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
@@ -24,7 +25,6 @@
#endif
#include <unistd.h>
-#include <assert.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
@@ -49,10 +49,21 @@
#include <pulsecore/socket-client.h>
#include <pulsecore/socket-util.h>
#include <pulsecore/authkey-prop.h>
+#include <pulsecore/time-smoother.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/rtclock.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/proplist-util.h>
#ifdef TUNNEL_SINK
#include "module-tunnel-sink-symdef.h"
-PA_MODULE_DESCRIPTION("Tunnel module for sinks")
+#else
+#include "module-tunnel-source-symdef.h"
+#endif
+
+#ifdef TUNNEL_SINK
+PA_MODULE_DESCRIPTION("Tunnel module for sinks");
PA_MODULE_USAGE(
"server=<address> "
"sink=<remote sink name> "
@@ -61,10 +72,9 @@ PA_MODULE_USAGE(
"channels=<number of channels> "
"rate=<sample rate> "
"sink_name=<name for the local sink> "
- "channel_map=<channel map>")
+ "channel_map=<channel map>");
#else
-#include "module-tunnel-source-symdef.h"
-PA_MODULE_DESCRIPTION("Tunnel module for sources")
+PA_MODULE_DESCRIPTION("Tunnel module for sources");
PA_MODULE_USAGE(
"server=<address> "
"source=<remote source name> "
@@ -73,21 +83,12 @@ PA_MODULE_USAGE(
"channels=<number of channels> "
"rate=<sample rate> "
"source_name=<name for the local source> "
- "channel_map=<channel map>")
+ "channel_map=<channel map>");
#endif
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_VERSION(PACKAGE_VERSION)
-
-#define DEFAULT_TLENGTH (44100*2*2/10) //(10240*8)
-#define DEFAULT_MAXLENGTH ((DEFAULT_TLENGTH*3)/2)
-#define DEFAULT_MINREQ 512
-#define DEFAULT_PREBUF (DEFAULT_TLENGTH-DEFAULT_MINREQ)
-#define DEFAULT_FRAGSIZE 1024
-
-#define DEFAULT_TIMEOUT 5
-
-#define LATENCY_INTERVAL 10
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
static const char* const valid_modargs[] = {
"server",
@@ -106,23 +107,70 @@ static const char* const valid_modargs[] = {
NULL,
};
-static void command_stream_killed(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
-static void command_subscribe_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+#define DEFAULT_TIMEOUT 5
+
+#define LATENCY_INTERVAL 10
+
+#define MIN_NETWORK_LATENCY_USEC (8*PA_USEC_PER_MSEC)
+
+#ifdef TUNNEL_SINK
+
+enum {
+ SINK_MESSAGE_REQUEST = PA_SINK_MESSAGE_MAX,
+ SINK_MESSAGE_REMOTE_SUSPEND,
+ SINK_MESSAGE_UPDATE_LATENCY,
+ SINK_MESSAGE_POST
+};
+
+#define DEFAULT_TLENGTH_MSEC 150
+#define DEFAULT_MINREQ_MSEC 25
+
+#else
+
+enum {
+ SOURCE_MESSAGE_POST = PA_SOURCE_MESSAGE_MAX,
+ SOURCE_MESSAGE_REMOTE_SUSPEND,
+ SOURCE_MESSAGE_UPDATE_LATENCY
+};
+
+#define DEFAULT_FRAGSIZE_MSEC 25
+
+#endif
#ifdef TUNNEL_SINK
static void command_request(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_started(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
#endif
+static void command_subscribe_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_stream_killed(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_suspended(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_moved(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
#ifdef TUNNEL_SINK
[PA_COMMAND_REQUEST] = command_request,
-#endif
+ [PA_COMMAND_STARTED] = command_started,
+#endif
+ [PA_COMMAND_SUBSCRIBE_EVENT] = command_subscribe_event,
+ [PA_COMMAND_OVERFLOW] = command_overflow_or_underflow,
+ [PA_COMMAND_UNDERFLOW] = command_overflow_or_underflow,
[PA_COMMAND_PLAYBACK_STREAM_KILLED] = command_stream_killed,
[PA_COMMAND_RECORD_STREAM_KILLED] = command_stream_killed,
- [PA_COMMAND_SUBSCRIBE_EVENT] = command_subscribe_event,
+ [PA_COMMAND_PLAYBACK_STREAM_SUSPENDED] = command_suspended,
+ [PA_COMMAND_RECORD_STREAM_SUSPENDED] = command_suspended,
+ [PA_COMMAND_PLAYBACK_STREAM_MOVED] = command_moved,
+ [PA_COMMAND_RECORD_STREAM_MOVED] = command_moved,
};
struct userdata {
+ pa_core *core;
+ pa_module *module;
+
+ pa_thread_mq thread_mq;
+ pa_rtpoll *rtpoll;
+ pa_thread *thread;
+
pa_socket_client *client;
pa_pstream *pstream;
pa_pdispatch *pdispatch;
@@ -131,14 +179,11 @@ struct userdata {
#ifdef TUNNEL_SINK
char *sink_name;
pa_sink *sink;
- uint32_t requested_bytes;
+ int32_t requested_bytes;
#else
char *source_name;
pa_source *source;
#endif
-
- pa_module *module;
- pa_core *core;
uint8_t auth_cookie[PA_NATIVE_COOKIE_LENGTH];
@@ -146,225 +191,589 @@ struct userdata {
uint32_t ctag;
uint32_t device_index;
uint32_t channel;
-
- pa_usec_t host_latency;
- pa_time_event *time_event;
+ int64_t counter, counter_delta;
- int auth_cookie_in_property;
-};
+ pa_bool_t remote_corked:1;
+ pa_bool_t remote_suspended:1;
-static void close_stuff(struct userdata *u) {
- assert(u);
-
- if (u->pstream) {
- pa_pstream_close(u->pstream);
- pa_pstream_unref(u->pstream);
- u->pstream = NULL;
- }
+ pa_usec_t transport_usec;
+ pa_bool_t transport_usec_valid;
- if (u->pdispatch) {
- pa_pdispatch_unref(u->pdispatch);
- u->pdispatch = NULL;
- }
+ uint32_t ignore_latency_before;
- if (u->client) {
- pa_socket_client_unref(u->client);
- u->client = NULL;
- }
+ pa_time_event *time_event;
+
+ pa_bool_t auth_cookie_in_property;
+
+ pa_smoother *smoother;
+ char *device_description;
+ char *server_fqdn;
+ char *user_name;
+
+ uint32_t maxlength;
#ifdef TUNNEL_SINK
- if (u->sink) {
- pa_sink_disconnect(u->sink);
- pa_sink_unref(u->sink);
- u->sink = NULL;
- }
+ uint32_t tlength;
+ uint32_t minreq;
+ uint32_t prebuf;
#else
- if (u->source) {
- pa_source_disconnect(u->source);
- pa_source_unref(u->source);
- u->source = NULL;
- }
+ uint32_t fragsize;
#endif
+};
- if (u->time_event) {
- u->core->mainloop->time_free(u->time_event);
- u->time_event = NULL;
- }
-}
+static void request_latency(struct userdata *u);
+
+/* Called from main context */
+static void command_stream_killed(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct userdata *u = userdata;
-static void die(struct userdata *u) {
- assert(u);
- close_stuff(u);
+ pa_assert(pd);
+ pa_assert(t);
+ pa_assert(u);
+ pa_assert(u->pdispatch == pd);
+
+ pa_log_warn("Stream killed");
pa_module_unload_request(u->module);
}
-static void command_stream_killed(pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+/* Called from main context */
+static void command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
struct userdata *u = userdata;
- assert(pd && t && u && u->pdispatch == pd);
- pa_log("stream killed");
- die(u);
-}
+ pa_assert(pd);
+ pa_assert(t);
+ pa_assert(u);
+ pa_assert(u->pdispatch == pd);
-static void request_info(struct userdata *u);
+ pa_log_info("Server signalled buffer overrun/underrun.");
+ request_latency(u);
+}
-static void command_subscribe_event(pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+/* Called from main context */
+static void command_suspended(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
struct userdata *u = userdata;
- pa_subscription_event_type_t e;
- uint32_t idx;
+ uint32_t channel;
+ pa_bool_t suspended;
- assert(pd && t && u);
- assert(command == PA_COMMAND_SUBSCRIBE_EVENT);
+ pa_assert(pd);
+ pa_assert(t);
+ pa_assert(u);
+ pa_assert(u->pdispatch == pd);
- if (pa_tagstruct_getu32(t, &e) < 0 ||
- pa_tagstruct_getu32(t, &idx) < 0 ||
+ if (pa_tagstruct_getu32(t, &channel) < 0 ||
+ pa_tagstruct_get_boolean(t, &suspended) < 0 ||
!pa_tagstruct_eof(t)) {
- pa_log("invalid protocol reply");
- die(u);
+ pa_log("Invalid packet");
+ pa_module_unload_request(u->module);
return;
}
#ifdef TUNNEL_SINK
- if (e != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE))
- return;
+ pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_REMOTE_SUSPEND, PA_UINT32_TO_PTR(!!suspended), 0, NULL);
#else
- if (e != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE))
- return;
+ pa_asyncmsgq_send(u->source->asyncmsgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_REMOTE_SUSPEND, PA_UINT32_TO_PTR(!!suspended), 0, NULL);
#endif
- request_info(u);
+ request_latency(u);
+}
+
+/* Called from main context */
+static void command_moved(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct userdata *u = userdata;
+
+ pa_assert(pd);
+ pa_assert(t);
+ pa_assert(u);
+ pa_assert(u->pdispatch == pd);
+
+ pa_log_debug("Server reports a stream move.");
+ request_latency(u);
}
#ifdef TUNNEL_SINK
-static void send_prebuf_request(struct userdata *u) {
+
+/* Called from main context */
+static void command_started(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct userdata *u = userdata;
+
+ pa_assert(pd);
+ pa_assert(t);
+ pa_assert(u);
+ pa_assert(u->pdispatch == pd);
+
+ pa_log_debug("Server reports playback started.");
+ request_latency(u);
+}
+
+#endif
+
+/* Called from IO thread context */
+static void stream_cork_within_thread(struct userdata *u, pa_bool_t cork) {
+ pa_usec_t x;
+ pa_assert(u);
+
+ if (u->remote_corked == cork)
+ return;
+
+ u->remote_corked = cork;
+ x = pa_rtclock_usec();
+
+ /* Correct by the time this needs to travel to the other side.
+ * This is a valid thread-safe access, because the main thread is
+ * waiting for us */
+ if (u->transport_usec_valid)
+ x += u->transport_usec;
+
+ if (u->remote_suspended || u->remote_corked)
+ pa_smoother_pause(u->smoother, x);
+ else
+ pa_smoother_resume(u->smoother, x);
+}
+
+/* Called from main context */
+static void stream_cork(struct userdata *u, pa_bool_t cork) {
pa_tagstruct *t;
+ pa_assert(u);
+
+ if (!u->pstream)
+ return;
t = pa_tagstruct_new(NULL, 0);
- pa_tagstruct_putu32(t, PA_COMMAND_PREBUF_PLAYBACK_STREAM);
+#ifdef TUNNEL_SINK
+ pa_tagstruct_putu32(t, PA_COMMAND_CORK_PLAYBACK_STREAM);
+#else
+ pa_tagstruct_putu32(t, PA_COMMAND_CORK_RECORD_STREAM);
+#endif
pa_tagstruct_putu32(t, u->ctag++);
pa_tagstruct_putu32(t, u->channel);
+ pa_tagstruct_put_boolean(t, !!cork);
pa_pstream_send_tagstruct(u->pstream, t);
+
+ request_latency(u);
}
-static void send_bytes(struct userdata *u) {
- assert(u);
+/* Called from IO thread context */
+static void stream_suspend_within_thread(struct userdata *u, pa_bool_t suspend) {
+ pa_usec_t x;
+ pa_assert(u);
- if (!u->pstream)
+ if (u->remote_suspended == suspend)
return;
+ u->remote_suspended = suspend;
+
+ x = pa_rtclock_usec();
+
+ /* Correct by the time this needed to travel from the other side.
+ * This is a valid thread-safe access, because the main thread is
+ * waiting for us */
+ if (u->transport_usec_valid)
+ x -= u->transport_usec;
+
+ if (u->remote_suspended || u->remote_corked)
+ pa_smoother_pause(u->smoother, x);
+ else
+ pa_smoother_resume(u->smoother, x);
+}
+
+#ifdef TUNNEL_SINK
+
+/* Called from IO thread context */
+static void send_data(struct userdata *u) {
+ pa_assert(u);
+
while (u->requested_bytes > 0) {
- pa_memchunk chunk;
- if (pa_sink_render(u->sink, u->requested_bytes, &chunk) < 0) {
-
- if (u->requested_bytes >= DEFAULT_TLENGTH-DEFAULT_PREBUF)
- send_prebuf_request(u);
-
- return;
+ pa_memchunk memchunk;
+
+ pa_sink_render(u->sink, u->requested_bytes, &memchunk);
+ pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_POST, NULL, 0, &memchunk, NULL);
+ pa_memblock_unref(memchunk.memblock);
+
+ u->requested_bytes -= memchunk.length;
+
+ u->counter += memchunk.length;
+ }
+}
+
+/* This function is called from IO context -- except when it is not. */
+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: {
+ int r;
+
+ /* First, change the state, because otherwide pa_sink_render() would fail */
+ if ((r = pa_sink_process_msg(o, code, data, offset, chunk)) >= 0) {
+
+ stream_cork_within_thread(u, u->sink->state == PA_SINK_SUSPENDED);
+
+ if (PA_SINK_IS_OPENED(u->sink->state))
+ send_data(u);
+ }
+
+ return r;
}
- pa_pstream_send_memblock(u->pstream, u->channel, 0, PA_SEEK_RELATIVE, &chunk);
- pa_memblock_unref(chunk.memblock);
+ case PA_SINK_MESSAGE_GET_LATENCY: {
+ pa_usec_t yl, yr, *usec = data;
-/* pa_log("sent %lu", (unsigned long) chunk.length); */
+ yl = pa_bytes_to_usec(u->counter, &u->sink->sample_spec);
+ yr = pa_smoother_get(u->smoother, pa_rtclock_usec());
- if (chunk.length > u->requested_bytes)
- u->requested_bytes = 0;
- else
- u->requested_bytes -= chunk.length;
+ *usec = yl > yr ? yl - yr : 0;
+ return 0;
+ }
+
+ case SINK_MESSAGE_REQUEST:
+
+ pa_assert(offset > 0);
+ u->requested_bytes += (size_t) offset;
+
+ if (PA_SINK_IS_OPENED(u->sink->thread_info.state))
+ send_data(u);
+
+ return 0;
+
+
+ case SINK_MESSAGE_REMOTE_SUSPEND:
+
+ stream_suspend_within_thread(u, !!PA_PTR_TO_UINT(data));
+ return 0;
+
+
+ case SINK_MESSAGE_UPDATE_LATENCY: {
+ pa_usec_t y;
+
+ y = pa_bytes_to_usec(u->counter, &u->sink->sample_spec);
+
+ if (y > (pa_usec_t) offset || offset < 0)
+ y -= offset;
+ else
+ y = 0;
+
+ pa_smoother_put(u->smoother, pa_rtclock_usec(), y);
+
+ return 0;
+ }
+
+ case SINK_MESSAGE_POST:
+
+ /* OK, This might be a bit confusing. This message is
+ * delivered to us from the main context -- NOT from the
+ * IO thread context where the rest of the messages are
+ * dispatched. Yeah, ugly, but I am a lazy bastard. */
+
+ pa_pstream_send_memblock(u->pstream, u->channel, 0, PA_SEEK_RELATIVE, chunk);
+
+ u->counter_delta += chunk->length;
+
+ 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);
+ u = s->userdata;
+
+ switch ((pa_sink_state_t) state) {
+
+ case PA_SINK_SUSPENDED:
+ pa_assert(PA_SINK_IS_OPENED(s->state));
+ stream_cork(u, TRUE);
+ break;
+
+ case PA_SINK_IDLE:
+ case PA_SINK_RUNNING:
+ if (s->state == PA_SINK_SUSPENDED)
+ stream_cork(u, FALSE);
+ break;
+
+ case PA_SINK_UNLINKED:
+ case PA_SINK_INIT:
+ ;
+ }
+
+ return 0;
+}
+
+#else
+
+/* This function is called from IO context -- except when it is not. */
+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_SINK_MESSAGE_SET_STATE: {
+ int r;
+
+ if ((r = pa_sink_process_msg(o, code, data, offset, chunk)) >= 0)
+ stream_cork_within_thread(u, u->source->state == PA_SOURCE_SUSPENDED);
+
+ return r;
+ }
+
+ case PA_SOURCE_MESSAGE_GET_LATENCY: {
+ pa_usec_t yr, yl, *usec = data;
+
+ yl = pa_bytes_to_usec(u->counter, &PA_SINK(o)->sample_spec);
+ yr = pa_smoother_get(u->smoother, pa_rtclock_usec());
+
+ *usec = yr > yl ? yr - yl : 0;
+ return 0;
+ }
+
+ case SOURCE_MESSAGE_POST:
+
+ if (PA_SOURCE_IS_OPENED(u->source->thread_info.state))
+ pa_source_post(u->source, chunk);
+
+ u->counter += chunk->length;
+
+ return 0;
+
+ case SOURCE_MESSAGE_REMOTE_SUSPEND:
+
+ stream_suspend_within_thread(u, !!PA_PTR_TO_UINT(data));
+ return 0;
+
+ case SOURCE_MESSAGE_UPDATE_LATENCY: {
+ pa_usec_t y;
+
+ y = pa_bytes_to_usec(u->counter, &u->source->sample_spec);
+
+ if (offset >= 0 || y > (pa_usec_t) -offset)
+ y += offset;
+ else
+ y = 0;
+
+ pa_smoother_put(u->smoother, pa_rtclock_usec(), y);
+
+ return 0;
+ }
+ }
+
+ return pa_source_process_msg(o, code, data, offset, chunk);
+}
+
+/* Called from main context */
+static int source_set_state(pa_source *s, pa_source_state_t state) {
+ struct userdata *u;
+ pa_source_assert_ref(s);
+ u = s->userdata;
+
+ switch ((pa_source_state_t) state) {
+
+ case PA_SOURCE_SUSPENDED:
+ pa_assert(PA_SOURCE_IS_OPENED(s->state));
+ stream_cork(u, TRUE);
+ break;
+
+ case PA_SOURCE_IDLE:
+ case PA_SOURCE_RUNNING:
+ if (s->state == PA_SOURCE_SUSPENDED)
+ stream_cork(u, FALSE);
+ break;
+
+ case PA_SOURCE_UNLINKED:
+ case PA_SOURCE_INIT:
+ ;
+ }
+
+ return 0;
+}
+
+#endif
+
+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);
+
+ for (;;) {
+ int ret;
+
+ if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 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");
}
-static void command_request(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+#ifdef TUNNEL_SINK
+/* Called from main context */
+static void command_request(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
struct userdata *u = userdata;
uint32_t bytes, channel;
- assert(pd && command == PA_COMMAND_REQUEST && t && u && u->pdispatch == pd);
+
+ pa_assert(pd);
+ pa_assert(command == PA_COMMAND_REQUEST);
+ pa_assert(t);
+ pa_assert(u);
+ pa_assert(u->pdispatch == pd);
if (pa_tagstruct_getu32(t, &channel) < 0 ||
- pa_tagstruct_getu32(t, &bytes) < 0 ||
- !pa_tagstruct_eof(t)) {
- pa_log("invalid protocol reply");
- die(u);
- return;
+ pa_tagstruct_getu32(t, &bytes) < 0) {
+ pa_log("Invalid protocol reply");
+ goto fail;
}
if (channel != u->channel) {
- pa_log("recieved data for invalid channel");
- die(u);
- return;
+ pa_log("Recieved data for invalid channel");
+ goto fail;
}
-
- u->requested_bytes += bytes;
- send_bytes(u);
+
+ pa_asyncmsgq_post(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_REQUEST, NULL, bytes, NULL, NULL);
+ return;
+
+fail:
+ pa_module_unload_request(u->module);
}
#endif
-static void stream_get_latency_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+/* Called from main context */
+static void stream_get_latency_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
struct userdata *u = userdata;
pa_usec_t sink_usec, source_usec, transport_usec;
- int playing;
+ pa_bool_t playing;
int64_t write_index, read_index;
struct timeval local, remote, now;
- assert(pd && u);
+ pa_sample_spec *ss;
+ int64_t delay;
+
+ pa_assert(pd);
+ pa_assert(u);
if (command != PA_COMMAND_REPLY) {
if (command == PA_COMMAND_ERROR)
- pa_log("failed to get latency.");
+ pa_log("Failed to get latency.");
else
- pa_log("protocol error.");
- die(u);
- return;
+ pa_log("Protocol error.");
+ goto fail;
}
-
+
if (pa_tagstruct_get_usec(t, &sink_usec) < 0 ||
pa_tagstruct_get_usec(t, &source_usec) < 0 ||
pa_tagstruct_get_boolean(t, &playing) < 0 ||
pa_tagstruct_get_timeval(t, &local) < 0 ||
pa_tagstruct_get_timeval(t, &remote) < 0 ||
pa_tagstruct_gets64(t, &write_index) < 0 ||
- pa_tagstruct_gets64(t, &read_index) < 0 ||
- !pa_tagstruct_eof(t)) {
- pa_log("invalid reply. (latency)");
- die(u);
+ pa_tagstruct_gets64(t, &read_index) < 0) {
+ pa_log("Invalid reply.");
+ goto fail;
+ }
+
+#ifdef TUNNEL_SINK
+ if (u->version >= 13) {
+ uint64_t underrun_for = 0, playing_for = 0;
+
+ if (pa_tagstruct_getu64(t, &underrun_for) < 0 ||
+ pa_tagstruct_getu64(t, &playing_for) < 0) {
+ pa_log("Invalid reply.");
+ goto fail;
+ }
+ }
+#endif
+
+ if (!pa_tagstruct_eof(t)) {
+ pa_log("Invalid reply.");
+ goto fail;
+ }
+
+ if (tag < u->ignore_latency_before) {
+ request_latency(u);
return;
}
pa_gettimeofday(&now);
- /* FIXME! This could use some serious love. */
-
+ /* Calculate transport usec */
if (pa_timeval_cmp(&local, &remote) < 0 && pa_timeval_cmp(&remote, &now)) {
/* local and remote seem to have synchronized clocks */
#ifdef TUNNEL_SINK
- transport_usec = pa_timeval_diff(&remote, &local);
+ u->transport_usec = pa_timeval_diff(&remote, &local);
#else
- transport_usec = pa_timeval_diff(&now, &remote);
-#endif
+ u->transport_usec = pa_timeval_diff(&now, &remote);
+#endif
} else
- transport_usec = pa_timeval_diff(&now, &local)/2;
+ u->transport_usec = pa_timeval_diff(&now, &local)/2;
+ u->transport_usec_valid = TRUE;
+ /* First, take the device's delay */
#ifdef TUNNEL_SINK
- u->host_latency = sink_usec + transport_usec;
+ delay = (int64_t) sink_usec;
+ ss = &u->sink->sample_spec;
#else
- u->host_latency = source_usec + transport_usec;
- if (u->host_latency > sink_usec)
- u->host_latency -= sink_usec;
+ delay = (int64_t) source_usec;
+ ss = &u->source->sample_spec;
+#endif
+
+ /* Add the length of our server-side buffer */
+ if (write_index >= read_index)
+ delay += (int64_t) pa_bytes_to_usec(write_index-read_index, ss);
else
- u->host_latency = 0;
+ delay -= (int64_t) pa_bytes_to_usec(read_index-write_index, ss);
+
+ /* Our measurements are already out of date, hence correct by the *
+ * transport latency */
+#ifdef TUNNEL_SINK
+ delay -= (int64_t) transport_usec;
+#else
+ delay += (int64_t) transport_usec;
#endif
-/* pa_log("estimated host latency: %0.0f usec", (double) u->host_latency); */
+ /* Now correct by what we have have read/written since we requested the update */
+#ifdef TUNNEL_SINK
+ delay += (int64_t) pa_bytes_to_usec(u->counter_delta, ss);
+#else
+ delay -= (int64_t) pa_bytes_to_usec(u->counter_delta, ss);
+#endif
+
+#ifdef TUNNEL_SINK
+ pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_UPDATE_LATENCY, 0, delay, NULL);
+#else
+ pa_asyncmsgq_send(u->source->asyncmsgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_UPDATE_LATENCY, 0, delay, NULL);
+#endif
+
+ return;
+
+fail:
+
+ pa_module_unload_request(u->module);
}
+/* Called from main context */
static void request_latency(struct userdata *u) {
pa_tagstruct *t;
struct timeval now;
uint32_t tag;
- assert(u);
+ pa_assert(u);
t = pa_tagstruct_new(NULL, 0);
-#ifdef TUNNEL_SINK
+#ifdef TUNNEL_SINK
pa_tagstruct_putu32(t, PA_COMMAND_GET_PLAYBACK_LATENCY);
#else
pa_tagstruct_putu32(t, PA_COMMAND_GET_RECORD_LATENCY);
@@ -374,37 +783,157 @@ static void request_latency(struct userdata *u) {
pa_gettimeofday(&now);
pa_tagstruct_put_timeval(t, &now);
-
+
pa_pstream_send_tagstruct(u->pstream, t);
pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, stream_get_latency_callback, u, NULL);
+
+ u->ignore_latency_before = tag;
+ u->counter_delta = 0;
}
-static void stream_get_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+/* Called from main context */
+static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, const struct timeval *tv, void *userdata) {
struct userdata *u = userdata;
- uint32_t idx, owner_module, monitor_source;
- pa_usec_t latency;
+ struct timeval ntv;
+
+ pa_assert(m);
+ pa_assert(e);
+ pa_assert(u);
+
+ request_latency(u);
+
+ pa_gettimeofday(&ntv);
+ ntv.tv_sec += LATENCY_INTERVAL;
+ m->time_restart(e, &ntv);
+}
+
+/* Called from main context */
+static void update_description(struct userdata *u) {
+ char *d;
+ char un[128], hn[128];
+ pa_tagstruct *t;
+
+ pa_assert(u);
+
+ if (!u->server_fqdn || !u->user_name || !u->device_description)
+ return;
+
+ d = pa_sprintf_malloc("%s on %s@%s", u->device_description, u->user_name, u->server_fqdn);
+
+#ifdef TUNNEL_SINK
+ pa_sink_set_description(u->sink, d);
+ pa_proplist_sets(u->sink->proplist, "tunnel.remote.user", u->user_name);
+ pa_proplist_sets(u->sink->proplist, "tunnel.remote.fqdn", u->server_fqdn);
+ pa_proplist_sets(u->sink->proplist, "tunnel.remote.description", u->device_description);
+#else
+ pa_source_set_description(u->source, d);
+ pa_proplist_sets(u->source->proplist, "tunnel.remote.user", u->user_name);
+ pa_proplist_sets(u->source->proplist, "tunnel.remote.fqdn", u->server_fqdn);
+ pa_proplist_sets(u->source->proplist, "tunnel.remote.description", u->device_description);
+#endif
+
+ pa_xfree(d);
+
+ d = pa_sprintf_malloc("%s for %s@%s", u->device_description,
+ pa_get_user_name(un, sizeof(un)),
+ pa_get_host_name(hn, sizeof(hn)));
+
+ t = pa_tagstruct_new(NULL, 0);
+#ifdef TUNNEL_SINK
+ pa_tagstruct_putu32(t, PA_COMMAND_SET_PLAYBACK_STREAM_NAME);
+#else
+ pa_tagstruct_putu32(t, PA_COMMAND_SET_RECORD_STREAM_NAME);
+#endif
+ pa_tagstruct_putu32(t, u->ctag++);
+ pa_tagstruct_putu32(t, u->channel);
+ pa_tagstruct_puts(t, d);
+ pa_pstream_send_tagstruct(u->pstream, t);
+
+ pa_xfree(d);
+}
+
+/* Called from main context */
+static void server_info_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct userdata *u = userdata;
+ pa_sample_spec ss;
+ const char *server_name, *server_version, *user_name, *host_name, *default_sink_name, *default_source_name;
+ uint32_t cookie;
+
+ pa_assert(pd);
+ pa_assert(u);
+
+ if (command != PA_COMMAND_REPLY) {
+ if (command == PA_COMMAND_ERROR)
+ pa_log("Failed to get info.");
+ else
+ pa_log("Protocol error.");
+ goto fail;
+ }
+
+ if (pa_tagstruct_gets(t, &server_name) < 0 ||
+ pa_tagstruct_gets(t, &server_version) < 0 ||
+ pa_tagstruct_gets(t, &user_name) < 0 ||
+ pa_tagstruct_gets(t, &host_name) < 0 ||
+ pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
+ pa_tagstruct_gets(t, &default_sink_name) < 0 ||
+ pa_tagstruct_gets(t, &default_source_name) < 0 ||
+ pa_tagstruct_getu32(t, &cookie) < 0) {
+
+ pa_log("Parse failure");
+ goto fail;
+ }
+
+ if (!pa_tagstruct_eof(t)) {
+ pa_log("Packet too long");
+ goto fail;
+ }
+
+ pa_xfree(u->server_fqdn);
+ u->server_fqdn = pa_xstrdup(host_name);
+
+ pa_xfree(u->user_name);
+ u->user_name = pa_xstrdup(user_name);
+
+ update_description(u);
+
+ return;
+
+fail:
+ pa_module_unload_request(u->module);
+}
+
+#ifdef TUNNEL_SINK
+
+/* Called from main context */
+static void sink_info_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct userdata *u = userdata;
+ uint32_t idx, owner_module, monitor_source, flags;
const char *name, *description, *monitor_source_name, *driver;
- int mute;
- uint32_t flags;
- pa_sample_spec sample_spec;
- pa_channel_map channel_map;
+ pa_sample_spec ss;
+ pa_channel_map cm;
pa_cvolume volume;
- assert(pd && u);
+ pa_bool_t mute;
+ pa_usec_t latency;
+ pa_proplist *pl;
+
+ pa_assert(pd);
+ pa_assert(u);
+
+ pl = pa_proplist_new();
if (command != PA_COMMAND_REPLY) {
if (command == PA_COMMAND_ERROR)
- pa_log("failed to get info.");
+ pa_log("Failed to get info.");
else
- pa_log("protocol error.");
- die(u);
- return;
+ pa_log("Protocol error.");
+ goto fail;
}
if (pa_tagstruct_getu32(t, &idx) < 0 ||
pa_tagstruct_gets(t, &name) < 0 ||
pa_tagstruct_gets(t, &description) < 0 ||
- pa_tagstruct_get_sample_spec(t, &sample_spec) < 0 ||
- pa_tagstruct_get_channel_map(t, &channel_map) < 0 ||
+ pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
+ pa_tagstruct_get_channel_map(t, &cm) < 0 ||
pa_tagstruct_getu32(t, &owner_module) < 0 ||
pa_tagstruct_get_cvolume(t, &volume) < 0 ||
pa_tagstruct_get_boolean(t, &mute) < 0 ||
@@ -412,159 +941,411 @@ static void stream_get_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_
pa_tagstruct_gets(t, &monitor_source_name) < 0 ||
pa_tagstruct_get_usec(t, &latency) < 0 ||
pa_tagstruct_gets(t, &driver) < 0 ||
- pa_tagstruct_getu32(t, &flags) < 0 ||
- !pa_tagstruct_eof(t)) {
- pa_log("invalid reply. (get_info)");
- die(u);
+ pa_tagstruct_getu32(t, &flags) < 0) {
+
+ pa_log("Parse failure");
+ goto fail;
+ }
+
+ if (u->version >= 13) {
+ pa_usec_t configured_latency;
+
+ if (pa_tagstruct_get_proplist(t, pl) < 0 ||
+ pa_tagstruct_get_usec(t, &configured_latency) < 0) {
+
+ pa_log("Parse failure");
+ goto fail;
+ }
+ }
+
+ if (!pa_tagstruct_eof(t)) {
+ pa_log("Packet too long");
+ goto fail;
+ }
+
+ pa_proplist_free(pl);
+
+ if (!u->sink_name || strcmp(name, u->sink_name))
return;
+
+ pa_xfree(u->device_description);
+ u->device_description = pa_xstrdup(description);
+
+ update_description(u);
+
+ return;
+
+fail:
+ pa_module_unload_request(u->module);
+ pa_proplist_free(pl);
+}
+
+/* Called from main context */
+static void sink_input_info_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct userdata *u = userdata;
+ uint32_t idx, owner_module, client, sink;
+ pa_usec_t buffer_usec, sink_usec;
+ const char *name, *driver, *resample_method;
+ pa_bool_t mute;
+ pa_sample_spec sample_spec;
+ pa_channel_map channel_map;
+ pa_cvolume volume;
+ pa_proplist *pl;
+
+ pa_assert(pd);
+ pa_assert(u);
+
+ pl = pa_proplist_new();
+
+ if (command != PA_COMMAND_REPLY) {
+ if (command == PA_COMMAND_ERROR)
+ pa_log("Failed to get info.");
+ else
+ pa_log("Protocol error.");
+ goto fail;
}
-#ifdef TUNNEL_SINK
- assert(u->sink);
- if ((!!mute == !!u->sink->hw_muted) &&
- pa_cvolume_equal(&volume, &u->sink->hw_volume))
+ if (pa_tagstruct_getu32(t, &idx) < 0 ||
+ pa_tagstruct_gets(t, &name) < 0 ||
+ pa_tagstruct_getu32(t, &owner_module) < 0 ||
+ pa_tagstruct_getu32(t, &client) < 0 ||
+ pa_tagstruct_getu32(t, &sink) < 0 ||
+ pa_tagstruct_get_sample_spec(t, &sample_spec) < 0 ||
+ pa_tagstruct_get_channel_map(t, &channel_map) < 0 ||
+ pa_tagstruct_get_cvolume(t, &volume) < 0 ||
+ pa_tagstruct_get_usec(t, &buffer_usec) < 0 ||
+ pa_tagstruct_get_usec(t, &sink_usec) < 0 ||
+ pa_tagstruct_gets(t, &resample_method) < 0 ||
+ pa_tagstruct_gets(t, &driver) < 0) {
+
+ pa_log("Parse failure");
+ goto fail;
+ }
+
+ if (u->version >= 11) {
+ if (pa_tagstruct_get_boolean(t, &mute) < 0) {
+
+ pa_log("Parse failure");
+ goto fail;
+ }
+ }
+
+ if (u->version >= 13) {
+ if (pa_tagstruct_get_proplist(t, pl) < 0) {
+
+ pa_log("Parse failure");
+ goto fail;
+ }
+ }
+
+ if (!pa_tagstruct_eof(t)) {
+ pa_log("Packet too long");
+ goto fail;
+ }
+
+ pa_proplist_free(pl);
+
+ if (idx != u->device_index)
return;
-#else
- assert(u->source);
- if ((!!mute == !!u->source->hw_muted) &&
- pa_cvolume_equal(&volume, &u->source->hw_volume))
+
+ pa_assert(u->sink);
+
+ if ((u->version < 11 || !!mute == !!u->sink->muted) &&
+ pa_cvolume_equal(&volume, &u->sink->volume))
return;
-#endif
-#ifdef TUNNEL_SINK
- memcpy(&u->sink->hw_volume, &volume, sizeof(pa_cvolume));
- u->sink->hw_muted = !!mute;
+ memcpy(&u->sink->volume, &volume, sizeof(pa_cvolume));
+
+ if (u->version >= 11)
+ u->sink->muted = !!mute;
+
+ pa_subscription_post(u->sink->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, u->sink->index);
+ return;
+
+fail:
+ pa_module_unload_request(u->module);
+ pa_proplist_free(pl);
+}
- pa_subscription_post(u->sink->core,
- PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE,
- u->sink->index);
#else
- memcpy(&u->source->hw_volume, &volume, sizeof(pa_cvolume));
- u->source->hw_muted = !!mute;
- pa_subscription_post(u->source->core,
- PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE,
- u->source->index);
-#endif
+/* Called from main context */
+static void source_info_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct userdata *u = userdata;
+ uint32_t idx, owner_module, monitor_of_sink, flags;
+ const char *name, *description, *monitor_of_sink_name, *driver;
+ pa_sample_spec ss;
+ pa_channel_map cm;
+ pa_cvolume volume;
+ pa_bool_t mute;
+ pa_usec_t latency, configured_latency;
+ pa_proplist *pl;
+
+ pa_assert(pd);
+ pa_assert(u);
+
+ pl = pa_proplist_new();
+
+ if (command != PA_COMMAND_REPLY) {
+ if (command == PA_COMMAND_ERROR)
+ pa_log("Failed to get info.");
+ else
+ pa_log("Protocol error.");
+ goto fail;
+ }
+
+ if (pa_tagstruct_getu32(t, &idx) < 0 ||
+ pa_tagstruct_gets(t, &name) < 0 ||
+ pa_tagstruct_gets(t, &description) < 0 ||
+ pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
+ pa_tagstruct_get_channel_map(t, &cm) < 0 ||
+ pa_tagstruct_getu32(t, &owner_module) < 0 ||
+ pa_tagstruct_get_cvolume(t, &volume) < 0 ||
+ pa_tagstruct_get_boolean(t, &mute) < 0 ||
+ pa_tagstruct_getu32(t, &monitor_of_sink) < 0 ||
+ pa_tagstruct_gets(t, &monitor_of_sink_name) < 0 ||
+ pa_tagstruct_get_usec(t, &latency) < 0 ||
+ pa_tagstruct_gets(t, &driver) < 0 ||
+ pa_tagstruct_getu32(t, &flags) < 0) {
+
+ pa_log("Parse failure");
+ goto fail;
+ }
+
+ if (u->version >= 13) {
+ if (pa_tagstruct_get_proplist(t, pl) < 0 ||
+ pa_tagstruct_get_usec(t, &configured_latency) < 0) {
+
+ pa_log("Parse failure");
+ goto fail;
+ }
+ }
+
+ if (!pa_tagstruct_eof(t)) {
+ pa_log("Packet too long");
+ goto fail;
+ }
+
+ pa_proplist_free(pl);
+
+ if (!u->source_name || strcmp(name, u->source_name))
+ return;
+
+ pa_xfree(u->device_description);
+ u->device_description = pa_xstrdup(description);
+
+ update_description(u);
+
+ return;
+
+fail:
+ pa_module_unload_request(u->module);
+ pa_proplist_free(pl);
}
+#endif
+
+/* Called from main context */
static void request_info(struct userdata *u) {
pa_tagstruct *t;
uint32_t tag;
- assert(u);
+ pa_assert(u);
t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_GET_SERVER_INFO);
+ pa_tagstruct_putu32(t, tag = u->ctag++);
+ pa_pstream_send_tagstruct(u->pstream, t);
+ pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, server_info_cb, u, NULL);
+
#ifdef TUNNEL_SINK
- pa_tagstruct_putu32(t, PA_COMMAND_GET_SINK_INFO);
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_GET_SINK_INPUT_INFO);
+ pa_tagstruct_putu32(t, tag = u->ctag++);
+ pa_tagstruct_putu32(t, u->device_index);
+ pa_pstream_send_tagstruct(u->pstream, t);
+ pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, sink_input_info_cb, u, NULL);
+
+ if (u->sink_name) {
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_GET_SINK_INFO);
+ pa_tagstruct_putu32(t, tag = u->ctag++);
+ pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+ pa_tagstruct_puts(t, u->sink_name);
+ pa_pstream_send_tagstruct(u->pstream, t);
+ pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, sink_info_cb, u, NULL);
+ }
#else
- pa_tagstruct_putu32(t, PA_COMMAND_GET_SOURCE_INFO);
+ if (u->source_name) {
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_GET_SOURCE_INFO);
+ pa_tagstruct_putu32(t, tag = u->ctag++);
+ pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+ pa_tagstruct_puts(t, u->source_name);
+ pa_pstream_send_tagstruct(u->pstream, t);
+ pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, source_info_cb, u, NULL);
+ }
#endif
- pa_tagstruct_putu32(t, tag = u->ctag++);
+}
+
+/* Called from main context */
+static void command_subscribe_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct userdata *u = userdata;
+ pa_subscription_event_type_t e;
+ uint32_t idx;
+
+ pa_assert(pd);
+ pa_assert(t);
+ pa_assert(u);
+ pa_assert(command == PA_COMMAND_SUBSCRIBE_EVENT);
+
+ if (pa_tagstruct_getu32(t, &e) < 0 ||
+ pa_tagstruct_getu32(t, &idx) < 0) {
+ pa_log("Invalid protocol reply");
+ pa_module_unload_request(u->module);
+ return;
+ }
- pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+ if (e != (PA_SUBSCRIPTION_EVENT_SERVER|PA_SUBSCRIPTION_EVENT_CHANGE) &&
#ifdef TUNNEL_SINK
- pa_tagstruct_puts(t, u->sink_name);
+ e != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE) &&
+ e != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE)
#else
- pa_tagstruct_puts(t, u->source_name);
+ e != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE)
#endif
+ )
+ return;
- pa_pstream_send_tagstruct(u->pstream, t);
- pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, stream_get_info_callback, u, NULL);
+ request_info(u);
}
+/* Called from main context */
static void start_subscribe(struct userdata *u) {
pa_tagstruct *t;
uint32_t tag;
- assert(u);
+ pa_assert(u);
t = pa_tagstruct_new(NULL, 0);
pa_tagstruct_putu32(t, PA_COMMAND_SUBSCRIBE);
pa_tagstruct_putu32(t, tag = u->ctag++);
-
+ pa_tagstruct_putu32(t, PA_SUBSCRIPTION_MASK_SERVER|
#ifdef TUNNEL_SINK
- pa_tagstruct_putu32(t, PA_SUBSCRIPTION_MASK_SINK);
+ PA_SUBSCRIPTION_MASK_SINK_INPUT|PA_SUBSCRIPTION_MASK_SINK
#else
- pa_tagstruct_putu32(t, PA_SUBSCRIPTION_MASK_SOURCE);
+ PA_SUBSCRIPTION_MASK_SOURCE
#endif
+ );
pa_pstream_send_tagstruct(u->pstream, t);
}
-static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
+/* Called from main context */
+static void create_stream_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
struct userdata *u = userdata;
struct timeval ntv;
- assert(m && e && u);
-
- request_latency(u);
-
- pa_gettimeofday(&ntv);
- ntv.tv_sec += LATENCY_INTERVAL;
- m->time_restart(e, &ntv);
-}
+#ifdef TUNNEL_SINK
+ uint32_t bytes;
+#endif
-static void create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
- struct userdata *u = userdata;
- struct timeval ntv;
- assert(pd && u && u->pdispatch == pd);
+ pa_assert(pd);
+ pa_assert(u);
+ pa_assert(u->pdispatch == pd);
if (command != PA_COMMAND_REPLY) {
if (command == PA_COMMAND_ERROR)
- pa_log("failed to create stream.");
+ pa_log("Failed to create stream.");
else
- pa_log("protocol error.");
- die(u);
- return;
+ pa_log("Protocol error.");
+ goto fail;
}
if (pa_tagstruct_getu32(t, &u->channel) < 0 ||
pa_tagstruct_getu32(t, &u->device_index) < 0
-#ifdef TUNNEL_SINK
- || pa_tagstruct_getu32(t, &u->requested_bytes) < 0
-#endif
+#ifdef TUNNEL_SINK
+ || pa_tagstruct_getu32(t, &bytes) < 0
+#endif
)
goto parse_error;
if (u->version >= 9) {
#ifdef TUNNEL_SINK
- uint32_t maxlength, tlength, prebuf, minreq;
-
- if (pa_tagstruct_getu32(t, &maxlength) < 0 ||
- pa_tagstruct_getu32(t, &tlength) < 0 ||
- pa_tagstruct_getu32(t, &prebuf) < 0 ||
- pa_tagstruct_getu32(t, &minreq) < 0)
+ if (pa_tagstruct_getu32(t, &u->maxlength) < 0 ||
+ pa_tagstruct_getu32(t, &u->tlength) < 0 ||
+ pa_tagstruct_getu32(t, &u->prebuf) < 0 ||
+ pa_tagstruct_getu32(t, &u->minreq) < 0)
goto parse_error;
#else
- uint32_t maxlength, fragsize;
-
- if (pa_tagstruct_getu32(t, &maxlength) < 0 ||
- pa_tagstruct_getu32(t, &fragsize) < 0)
+ if (pa_tagstruct_getu32(t, &u->maxlength) < 0 ||
+ pa_tagstruct_getu32(t, &u->fragsize) < 0)
goto parse_error;
#endif
}
-
+
+ if (u->version >= 12) {
+ pa_sample_spec ss;
+ pa_channel_map cm;
+ uint32_t device_index;
+ const char *dn;
+ pa_bool_t suspended;
+
+ if (pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
+ pa_tagstruct_get_channel_map(t, &cm) < 0 ||
+ pa_tagstruct_getu32(t, &device_index) < 0 ||
+ pa_tagstruct_gets(t, &dn) < 0 ||
+ pa_tagstruct_get_boolean(t, &suspended) < 0)
+ goto parse_error;
+
+#ifdef TUNNEL_SINK
+ pa_xfree(u->sink_name);
+ u->sink_name = pa_xstrdup(dn);
+#else
+ pa_xfree(u->source_name);
+ u->source_name = pa_xstrdup(dn);
+#endif
+ }
+
+ if (u->version >= 13) {
+ pa_usec_t usec;
+
+ if (pa_tagstruct_get_usec(t, &usec) < 0)
+ goto parse_error;
+
+#ifdef TUNNEL_SINK
+ pa_sink_set_latency_range(u->sink, usec + MIN_NETWORK_LATENCY_USEC, 0);
+#else
+ pa_source_set_latency_range(u->source, usec + MIN_NETWORK_LATENCY_USEC, 0);
+#endif
+ }
+
if (!pa_tagstruct_eof(t))
goto parse_error;
start_subscribe(u);
request_info(u);
- assert(!u->time_event);
+ pa_assert(!u->time_event);
pa_gettimeofday(&ntv);
ntv.tv_sec += LATENCY_INTERVAL;
u->time_event = u->core->mainloop->time_new(u->core->mainloop, &ntv, timeout_callback, u);
request_latency(u);
+
+ pa_log_debug("Stream created.");
+
#ifdef TUNNEL_SINK
- send_bytes(u);
+ pa_asyncmsgq_post(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_REQUEST, NULL, bytes, NULL, NULL);
#endif
return;
-
+
parse_error:
- pa_log("invalid reply. (create stream)");
- die(u);
+ pa_log("Invalid reply. (Create stream)");
+
+fail:
+ pa_module_unload_request(u->module);
+
}
+/* Called from main context */
static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
struct userdata *u = userdata;
pa_tagstruct *reply;
@@ -572,123 +1353,218 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
#ifdef TUNNEL_SINK
pa_cvolume volume;
#endif
- assert(pd && u && u->pdispatch == pd);
+
+ pa_assert(pd);
+ pa_assert(u);
+ pa_assert(u->pdispatch == pd);
if (command != PA_COMMAND_REPLY ||
pa_tagstruct_getu32(t, &u->version) < 0 ||
!pa_tagstruct_eof(t)) {
+
if (command == PA_COMMAND_ERROR)
- pa_log("failed to authenticate");
+ pa_log("Failed to authenticate");
else
- pa_log("protocol error.");
- die(u);
- return;
+ pa_log("Protocol error.");
+
+ goto fail;
}
/* Minimum supported protocol version */
if (u->version < 8) {
- pa_log("incompatible protocol version");
- die(u);
- return;
+ pa_log("Incompatible protocol version");
+ goto fail;
}
+ /* Starting with protocol version 13 the MSB of the version tag
+ reflects if shm is enabled for this connection or not. We don't
+ support SHM here at all, so we just ignore this. */
+
+ if (u->version >= 13)
+ u->version &= 0x7FFFFFFFU;
+
+ pa_log_debug("Protocol version: remote %u, local %u", u->version, PA_PROTOCOL_VERSION);
+
#ifdef TUNNEL_SINK
- snprintf(name, sizeof(name), "Tunnel from host %s, user %s, sink %s",
- pa_get_host_name(hn, sizeof(hn)),
- pa_get_user_name(un, sizeof(un)),
- u->sink->name);
+ pa_snprintf(name, sizeof(name), "%s for %s@%s",
+ u->sink_name,
+ pa_get_user_name(un, sizeof(un)),
+ pa_get_host_name(hn, sizeof(hn)));
#else
- snprintf(name, sizeof(name), "Tunnel from host %s, user %s, source %s",
- pa_get_host_name(hn, sizeof(hn)),
- pa_get_user_name(un, sizeof(un)),
- u->source->name);
+ pa_snprintf(name, sizeof(name), "%s for %s@%s",
+ u->source_name,
+ pa_get_user_name(un, sizeof(un)),
+ pa_get_host_name(hn, sizeof(hn)));
#endif
-
+
reply = pa_tagstruct_new(NULL, 0);
pa_tagstruct_putu32(reply, PA_COMMAND_SET_CLIENT_NAME);
pa_tagstruct_putu32(reply, tag = u->ctag++);
- pa_tagstruct_puts(reply, name);
+
+ if (u->version >= 13) {
+ pa_proplist *pl;
+ pl = pa_proplist_new();
+ pa_init_proplist(pl);
+ pa_proplist_sets(pl, PA_PROP_APPLICATION_ID, "org.PulseAudio.PulseAudio");
+ pa_proplist_sets(pl, PA_PROP_APPLICATION_VERSION, PACKAGE_VERSION);
+ pa_tagstruct_put_proplist(reply, pl);
+ pa_proplist_free(pl);
+ } else
+ pa_tagstruct_puts(reply, "PulseAudio");
+
pa_pstream_send_tagstruct(u->pstream, reply);
/* We ignore the server's reply here */
reply = pa_tagstruct_new(NULL, 0);
-#ifdef TUNNEL_SINK
+
+ if (u->version < 13)
+ /* Only for older PA versions we need to fill in the maxlength */
+ u->maxlength = 4*1024*1024;
+
+#ifdef TUNNEL_SINK
+ u->tlength = pa_usec_to_bytes(PA_USEC_PER_MSEC * DEFAULT_TLENGTH_MSEC, &u->sink->sample_spec);
+ u->minreq = pa_usec_to_bytes(PA_USEC_PER_MSEC * DEFAULT_MINREQ_MSEC, &u->sink->sample_spec);
+ u->prebuf = u->tlength;
+#else
+ u->fragsize = pa_usec_to_bytes(PA_USEC_PER_MSEC * DEFAULT_FRAGSIZE_MSEC, &u->source->sample_spec);
+#endif
+
+#ifdef TUNNEL_SINK
pa_tagstruct_putu32(reply, PA_COMMAND_CREATE_PLAYBACK_STREAM);
pa_tagstruct_putu32(reply, tag = u->ctag++);
- pa_tagstruct_puts(reply, name);
+
+ if (u->version < 13)
+ pa_tagstruct_puts(reply, name);
+
pa_tagstruct_put_sample_spec(reply, &u->sink->sample_spec);
pa_tagstruct_put_channel_map(reply, &u->sink->channel_map);
pa_tagstruct_putu32(reply, PA_INVALID_INDEX);
pa_tagstruct_puts(reply, u->sink_name);
- pa_tagstruct_putu32(reply, DEFAULT_MAXLENGTH);
- pa_tagstruct_put_boolean(reply, 0);
- pa_tagstruct_putu32(reply, DEFAULT_TLENGTH);
- pa_tagstruct_putu32(reply, DEFAULT_PREBUF);
- pa_tagstruct_putu32(reply, DEFAULT_MINREQ);
+ pa_tagstruct_putu32(reply, u->maxlength);
+ pa_tagstruct_put_boolean(reply, !PA_SINK_IS_OPENED(pa_sink_get_state(u->sink)));
+ pa_tagstruct_putu32(reply, u->tlength);
+ pa_tagstruct_putu32(reply, u->prebuf);
+ pa_tagstruct_putu32(reply, u->minreq);
pa_tagstruct_putu32(reply, 0);
pa_cvolume_reset(&volume, u->sink->sample_spec.channels);
pa_tagstruct_put_cvolume(reply, &volume);
#else
pa_tagstruct_putu32(reply, PA_COMMAND_CREATE_RECORD_STREAM);
pa_tagstruct_putu32(reply, tag = u->ctag++);
- pa_tagstruct_puts(reply, name);
+
+ if (u->version < 13)
+ pa_tagstruct_puts(reply, name);
+
pa_tagstruct_put_sample_spec(reply, &u->source->sample_spec);
pa_tagstruct_put_channel_map(reply, &u->source->channel_map);
pa_tagstruct_putu32(reply, PA_INVALID_INDEX);
pa_tagstruct_puts(reply, u->source_name);
- pa_tagstruct_putu32(reply, DEFAULT_MAXLENGTH);
- pa_tagstruct_put_boolean(reply, 0);
- pa_tagstruct_putu32(reply, DEFAULT_FRAGSIZE);
+ pa_tagstruct_putu32(reply, u->maxlength);
+ pa_tagstruct_put_boolean(reply, !PA_SOURCE_IS_OPENED(pa_source_get_state(u->source)));
+ pa_tagstruct_putu32(reply, u->fragsize);
+#endif
+
+ if (u->version >= 12) {
+ pa_tagstruct_put_boolean(reply, FALSE); /* no_remap */
+ pa_tagstruct_put_boolean(reply, FALSE); /* no_remix */
+ pa_tagstruct_put_boolean(reply, FALSE); /* fix_format */
+ pa_tagstruct_put_boolean(reply, FALSE); /* fix_rate */
+ pa_tagstruct_put_boolean(reply, FALSE); /* fix_channels */
+ pa_tagstruct_put_boolean(reply, TRUE); /* no_move */
+ pa_tagstruct_put_boolean(reply, FALSE); /* variable_rate */
+ }
+
+ if (u->version >= 13) {
+ pa_proplist *pl;
+
+ pa_tagstruct_put_boolean(reply, FALSE); /* start muted/peak detect*/
+ pa_tagstruct_put_boolean(reply, TRUE); /* adjust_latency */
+
+ pl = pa_proplist_new();
+ pa_proplist_sets(pl, PA_PROP_MEDIA_NAME, name);
+ pa_proplist_sets(pl, PA_PROP_MEDIA_ROLE, "abstract");
+ pa_tagstruct_put_proplist(reply, pl);
+ pa_proplist_free(pl);
+
+#ifndef TUNNEL_SINK
+ pa_tagstruct_putu32(reply, PA_INVALID_INDEX); /* direct on input */
#endif
-
+ }
+
pa_pstream_send_tagstruct(u->pstream, reply);
pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, create_stream_callback, u, NULL);
+
+ pa_log_debug("Connection authenticated, creating stream ...");
+
+ return;
+
+fail:
+ pa_module_unload_request(u->module);
}
+/* Called from main context */
static void pstream_die_callback(pa_pstream *p, void *userdata) {
struct userdata *u = userdata;
- assert(p && u);
- pa_log("stream died.");
- die(u);
+ pa_assert(p);
+ pa_assert(u);
+
+ pa_log_warn("Stream died.");
+ pa_module_unload_request(u->module);
}
+/* Called from main context */
static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, const pa_creds *creds, void *userdata) {
struct userdata *u = userdata;
- assert(p && packet && u);
+
+ pa_assert(p);
+ pa_assert(packet);
+ pa_assert(u);
if (pa_pdispatch_run(u->pdispatch, packet, creds, u) < 0) {
- pa_log("invalid packet");
- die(u);
+ pa_log("Invalid packet");
+ pa_module_unload_request(u->module);
+ return;
}
}
#ifndef TUNNEL_SINK
-static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, PA_GCC_UNUSED int64_t offset, PA_GCC_UNUSED pa_seek_mode_t seek, const pa_memchunk *chunk, void *userdata) {
+/* Called from main context */
+static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk, void *userdata) {
struct userdata *u = userdata;
- assert(p && chunk && u);
+
+ pa_assert(p);
+ pa_assert(chunk);
+ pa_assert(u);
if (channel != u->channel) {
- pa_log("recieved memory block on bad channel.");
- die(u);
+ pa_log("Recieved memory block on bad channel.");
+ pa_module_unload_request(u->module);
return;
}
-
- pa_source_post(u->source, chunk);
+
+ pa_asyncmsgq_send(u->source->asyncmsgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_POST, PA_UINT_TO_PTR(seek), offset, chunk);
+
+ u->counter_delta += chunk->length;
}
+
#endif
+/* Called from main context */
static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata) {
struct userdata *u = userdata;
pa_tagstruct *t;
uint32_t tag;
- assert(sc && u && u->client == sc);
+
+ pa_assert(sc);
+ pa_assert(u);
+ pa_assert(u->client == sc);
pa_socket_client_unref(u->client);
u->client = NULL;
-
+
if (!io) {
- pa_log("connection failed.");
+ pa_log("Connection failed: %s", pa_cstrerror(errno));
pa_module_unload_request(u->module);
return;
}
@@ -701,207 +1577,130 @@ static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata
#ifndef TUNNEL_SINK
pa_pstream_set_recieve_memblock_callback(u->pstream, pstream_memblock_callback, u);
#endif
-
+
t = pa_tagstruct_new(NULL, 0);
pa_tagstruct_putu32(t, PA_COMMAND_AUTH);
pa_tagstruct_putu32(t, tag = u->ctag++);
pa_tagstruct_putu32(t, PA_PROTOCOL_VERSION);
pa_tagstruct_put_arbitrary(t, u->auth_cookie, sizeof(u->auth_cookie));
- pa_pstream_send_tagstruct(u->pstream, t);
- pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, u, NULL);
-
-}
-
-#ifdef TUNNEL_SINK
-static void sink_notify(pa_sink*sink) {
- struct userdata *u;
- assert(sink && sink->userdata);
- u = sink->userdata;
-
- send_bytes(u);
-}
-
-static pa_usec_t sink_get_latency(pa_sink *sink) {
- struct userdata *u;
- uint32_t l;
- pa_usec_t usec = 0;
- assert(sink && sink->userdata);
- u = sink->userdata;
- l = DEFAULT_TLENGTH;
+#ifdef HAVE_CREDS
+{
+ pa_creds ucred;
- if (l > u->requested_bytes) {
- l -= u->requested_bytes;
- usec += pa_bytes_to_usec(l, &u->sink->sample_spec);
- }
+ if (pa_iochannel_creds_supported(io))
+ pa_iochannel_creds_enable(io);
- usec += u->host_latency;
+ ucred.uid = getuid();
+ ucred.gid = getgid();
- return usec;
+ pa_pstream_send_tagstruct_with_creds(u->pstream, t, &ucred);
}
-
-static int sink_get_hw_volume(pa_sink *sink) {
- struct userdata *u;
- assert(sink && sink->userdata);
- u = sink->userdata;
-
- return 0;
-}
-
-static int sink_set_hw_volume(pa_sink *sink) {
- struct userdata *u;
- pa_tagstruct *t;
- uint32_t tag;
- assert(sink && sink->userdata);
- u = sink->userdata;
-
- t = pa_tagstruct_new(NULL, 0);
- pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_VOLUME);
- pa_tagstruct_putu32(t, tag = u->ctag++);
-
- pa_tagstruct_putu32(t, PA_INVALID_INDEX);
- pa_tagstruct_puts(t, u->sink_name);
- pa_tagstruct_put_cvolume(t, &sink->hw_volume);
+#else
pa_pstream_send_tagstruct(u->pstream, t);
+#endif
- return 0;
-}
-
-static int sink_get_hw_mute(pa_sink *sink) {
- struct userdata *u;
- assert(sink && sink->userdata);
- u = sink->userdata;
+ pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, u, NULL);
- return 0;
+ pa_log_debug("Connection established, authenticating ...");
}
-static int sink_set_hw_mute(pa_sink *sink) {
+#ifdef TUNNEL_SINK
+
+/* Called from main context */
+static int sink_set_volume(pa_sink *sink) {
struct userdata *u;
pa_tagstruct *t;
uint32_t tag;
- assert(sink && sink->userdata);
+
+ pa_assert(sink);
u = sink->userdata;
+ pa_assert(u);
t = pa_tagstruct_new(NULL, 0);
- pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_MUTE);
+ pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_INPUT_VOLUME);
pa_tagstruct_putu32(t, tag = u->ctag++);
-
- pa_tagstruct_putu32(t, PA_INVALID_INDEX);
- pa_tagstruct_puts(t, u->sink_name);
- pa_tagstruct_put_boolean(t, !!sink->hw_muted);
+ pa_tagstruct_putu32(t, u->device_index);
+ pa_tagstruct_put_cvolume(t, &sink->volume);
pa_pstream_send_tagstruct(u->pstream, t);
return 0;
}
-#else
-static pa_usec_t source_get_latency(pa_source *source) {
- struct userdata *u;
- assert(source && source->userdata);
- u = source->userdata;
-
- return u->host_latency;
-}
-
-static int source_get_hw_volume(pa_source *source) {
- struct userdata *u;
- assert(source && source->userdata);
- u = source->userdata;
-
- return 0;
-}
-
-static int source_set_hw_volume(pa_source *source) {
+/* Called from main context */
+static int sink_set_mute(pa_sink *sink) {
struct userdata *u;
pa_tagstruct *t;
uint32_t tag;
- assert(source && source->userdata);
- u = source->userdata;
-
- t = pa_tagstruct_new(NULL, 0);
- pa_tagstruct_putu32(t, PA_COMMAND_SET_SOURCE_VOLUME);
- pa_tagstruct_putu32(t, tag = u->ctag++);
-
- pa_tagstruct_putu32(t, PA_INVALID_INDEX);
- pa_tagstruct_puts(t, u->source_name);
- pa_tagstruct_put_cvolume(t, &source->hw_volume);
- pa_pstream_send_tagstruct(u->pstream, t);
-
- return 0;
-}
-static int source_get_hw_mute(pa_source *source) {
- struct userdata *u;
- assert(source && source->userdata);
- u = source->userdata;
-
- return 0;
-}
+ pa_assert(sink);
+ u = sink->userdata;
+ pa_assert(u);
-static int source_set_hw_mute(pa_source *source) {
- struct userdata *u;
- pa_tagstruct *t;
- uint32_t tag;
- assert(source && source->userdata);
- u = source->userdata;
+ if (u->version < 11)
+ return -1;
t = pa_tagstruct_new(NULL, 0);
- pa_tagstruct_putu32(t, PA_COMMAND_SET_SOURCE_MUTE);
+ pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_INPUT_MUTE);
pa_tagstruct_putu32(t, tag = u->ctag++);
-
- pa_tagstruct_putu32(t, PA_INVALID_INDEX);
- pa_tagstruct_puts(t, u->source_name);
- pa_tagstruct_put_boolean(t, !!source->hw_muted);
+ pa_tagstruct_putu32(t, u->device_index);
+ pa_tagstruct_put_boolean(t, !!sink->muted);
pa_pstream_send_tagstruct(u->pstream, t);
return 0;
}
+
#endif
+/* Called from main context */
static int load_key(struct userdata *u, const char*fn) {
- assert(u);
+ pa_assert(u);
+
+ u->auth_cookie_in_property = FALSE;
- u->auth_cookie_in_property = 0;
-
if (!fn && pa_authkey_prop_get(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME, u->auth_cookie, sizeof(u->auth_cookie)) >= 0) {
- pa_log_debug("using already loaded auth cookie.");
+ pa_log_debug("Using already loaded auth cookie.");
pa_authkey_prop_ref(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME);
u->auth_cookie_in_property = 1;
return 0;
}
-
+
if (!fn)
fn = PA_NATIVE_COOKIE_FILE;
if (pa_authkey_load_auto(fn, u->auth_cookie, sizeof(u->auth_cookie)) < 0)
return -1;
- pa_log_debug("loading cookie from disk.");
-
+ pa_log_debug("Loading cookie from disk.");
+
if (pa_authkey_prop_put(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME, u->auth_cookie, sizeof(u->auth_cookie)) >= 0)
- u->auth_cookie_in_property = 1;
+ u->auth_cookie_in_property = TRUE;
return 0;
}
-int pa__init(pa_core *c, pa_module*m) {
+int pa__init(pa_module*m) {
pa_modargs *ma = NULL;
struct userdata *u = NULL;
pa_sample_spec ss;
pa_channel_map map;
- char *t, *dn = NULL;
-
- assert(c && m);
+ char *dn = NULL;
+#ifdef TUNNEL_SINK
+ pa_sink_new_data data;
+#else
+ pa_source_new_data data;
+#endif
+
+ 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;
}
- u = pa_xmalloc(sizeof(struct userdata));
- m->userdata = u;
+ m->userdata = u = pa_xnew0(struct userdata, 1);
+ u->core = m->core;
u->module = m;
- u->core = c;
u->client = NULL;
u->pdispatch = NULL;
u->pstream = NULL;
@@ -914,33 +1713,38 @@ int pa__init(pa_core *c, pa_module*m) {
u->source_name = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL));;
u->source = NULL;
#endif
+ u->smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC*2, TRUE, 10);
u->ctag = 1;
u->device_index = u->channel = PA_INVALID_INDEX;
- u->host_latency = 0;
- u->auth_cookie_in_property = 0;
+ u->auth_cookie_in_property = FALSE;
u->time_event = NULL;
-
+ u->ignore_latency_before = 0;
+ u->transport_usec = 0;
+ u->transport_usec_valid = FALSE;
+ u->remote_suspended = u->remote_corked = FALSE;
+ u->counter = u->counter_delta = 0;
+
+ u->rtpoll = pa_rtpoll_new();
+ pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
+
if (load_key(u, pa_modargs_get_value(ma, "cookie", NULL)) < 0)
goto fail;
-
+
if (!(u->server_name = pa_xstrdup(pa_modargs_get_value(ma, "server", NULL)))) {
- pa_log("no server specified.");
+ pa_log("No server specified.");
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");
goto fail;
}
- if (!(u->client = pa_socket_client_new_string(c->mainloop, u->server_name, PA_NATIVE_DEFAULT_PORT))) {
- pa_log("failed to connect to server '%s'", u->server_name);
+ if (!(u->client = pa_socket_client_new_string(m->core->mainloop, u->server_name, PA_NATIVE_DEFAULT_PORT))) {
+ pa_log("Failed to connect to server '%s'", u->server_name);
goto fail;
}
-
- if (!u->client)
- goto fail;
pa_socket_client_set_callback(u->client, on_connection, u);
@@ -949,76 +1753,167 @@ int pa__init(pa_core *c, pa_module*m) {
if (!(dn = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", NULL))))
dn = pa_sprintf_malloc("tunnel.%s", u->server_name);
- if (!(u->sink = pa_sink_new(c, __FILE__, dn, 0, &ss, &map))) {
- pa_log("failed to create sink.");
+ pa_sink_new_data_init(&data);
+ data.driver = __FILE__;
+ data.module = m;
+ data.namereg_fail = TRUE;
+ pa_sink_new_data_set_name(&data, dn);
+ pa_sink_new_data_set_sample_spec(&data, &ss);
+ pa_sink_new_data_set_channel_map(&data, &map);
+ pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "%s%s%s", pa_strempty(u->sink_name), u->sink_name ? " on " : "", u->server_name);
+ pa_proplist_sets(data.proplist, "tunnel.remote.server", u->server_name);
+ if (u->sink_name)
+ pa_proplist_sets(data.proplist, "tunnel.remote.sink", u->sink_name);
+
+ u->sink = pa_sink_new(m->core, &data, PA_SINK_NETWORK|PA_SINK_LATENCY|PA_SINK_HW_VOLUME_CTRL);
+ pa_sink_new_data_done(&data);
+
+ if (!u->sink) {
+ pa_log("Failed to create sink.");
goto fail;
}
- u->sink->get_latency = sink_get_latency;
- u->sink->get_hw_volume = sink_get_hw_volume;
- u->sink->set_hw_volume = sink_set_hw_volume;
- u->sink->get_hw_mute = sink_get_hw_mute;
- u->sink->set_hw_mute = sink_set_hw_mute;
- u->sink->notify = sink_notify;
+ u->sink->parent.process_msg = sink_process_msg;
u->sink->userdata = u;
- pa_sink_set_description(u->sink, t = pa_sprintf_malloc("Tunnel to %s%s%s", u->sink_name ? u->sink_name : "", u->sink_name ? " on " : "", u->server_name));
- pa_xfree(t);
+ u->sink->set_state = sink_set_state;
+ u->sink->set_volume = sink_set_volume;
+ u->sink->set_mute = sink_set_mute;
+
+ u->sink->refresh_volume = u->sink->refresh_muted = FALSE;
+
+ pa_sink_set_latency_range(u->sink, MIN_NETWORK_LATENCY_USEC, 0);
+
+ pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
+ pa_sink_set_rtpoll(u->sink, u->rtpoll);
- pa_sink_set_owner(u->sink, m);
#else
if (!(dn = pa_xstrdup(pa_modargs_get_value(ma, "source_name", NULL))))
dn = pa_sprintf_malloc("tunnel.%s", u->server_name);
- if (!(u->source = pa_source_new(c, __FILE__, dn, 0, &ss, &map))) {
- pa_log("failed to create source.");
+ pa_source_new_data_init(&data);
+ data.driver = __FILE__;
+ data.module = m;
+ data.namereg_fail = TRUE;
+ pa_source_new_data_set_name(&data, dn);
+ pa_source_new_data_set_sample_spec(&data, &ss);
+ pa_source_new_data_set_channel_map(&data, &map);
+ pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "%s%s%s", pa_strempty(u->source_name), u->source_name ? " on " : "", u->server_name);
+ pa_proplist_sets(data.proplist, "tunnel.remote.server", u->server_name);
+ if (u->source_name)
+ pa_proplist_sets(data.proplist, "tunnel.remote.source", u->source_name);
+
+ u->source = pa_source_new(m->core, &data, PA_SOURCE_NETWORK|PA_SOURCE_LATENCY);
+ pa_source_new_data_done(&data);
+
+ if (!u->source) {
+ pa_log("Failed to create source.");
goto fail;
}
- u->source->get_latency = source_get_latency;
- u->source->get_hw_volume = source_get_hw_volume;
- u->source->set_hw_volume = source_set_hw_volume;
- u->source->get_hw_mute = source_get_hw_mute;
- u->source->set_hw_mute = source_set_hw_mute;
+ u->source->parent.process_msg = source_process_msg;
+ u->source->set_state = source_set_state;
u->source->userdata = u;
- pa_source_set_description(u->source, t = pa_sprintf_malloc("Tunnel to %s%s%s", u->source_name ? u->source_name : "", u->source_name ? " on " : "", u->server_name));
- pa_xfree(t);
+ pa_source_set_latency_range(u->source, MIN_NETWORK_LATENCY_USEC, 0);
- pa_source_set_owner(u->source, m);
+ pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
+ pa_source_set_rtpoll(u->source, u->rtpoll);
#endif
-
+
pa_xfree(dn);
u->time_event = NULL;
+ u->maxlength = 0;
+#ifdef TUNNEL_SINK
+ u->tlength = u->minreq = u->prebuf = 0;
+#else
+ u->fragsize = 0;
+#endif
+
+ pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec());
+
+ if (!(u->thread = pa_thread_new(thread_func, u))) {
+ pa_log("Failed to create thread.");
+ goto fail;
+ }
+
+#ifdef TUNNEL_SINK
+ pa_sink_put(u->sink);
+#else
+ pa_source_put(u->source);
+#endif
+
pa_modargs_free(ma);
return 0;
-
+
fail:
- pa__done(c, m);
+ pa__done(m);
if (ma)
pa_modargs_free(ma);
pa_xfree(dn);
-
+
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;
- close_stuff(u);
+#ifdef TUNNEL_SINK
+ if (u->sink)
+ pa_sink_unlink(u->sink);
+#else
+ if (u->source)
+ pa_source_unlink(u->source);
+#endif
+
+ 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);
+
+#ifdef TUNNEL_SINK
+ if (u->sink)
+ pa_sink_unref(u->sink);
+#else
+ if (u->source)
+ pa_source_unref(u->source);
+#endif
+
+ if (u->rtpoll)
+ pa_rtpoll_free(u->rtpoll);
+
+ if (u->pstream) {
+ pa_pstream_unlink(u->pstream);
+ pa_pstream_unref(u->pstream);
+ }
+
+ if (u->pdispatch)
+ pa_pdispatch_unref(u->pdispatch);
+
+ if (u->client)
+ pa_socket_client_unref(u->client);
if (u->auth_cookie_in_property)
- pa_authkey_prop_unref(c, PA_NATIVE_COOKIE_PROPERTY_NAME);
-
+ pa_authkey_prop_unref(m->core, PA_NATIVE_COOKIE_PROPERTY_NAME);
+
+ if (u->smoother)
+ pa_smoother_free(u->smoother);
+
+ if (u->time_event)
+ u->core->mainloop->time_free(u->time_event);
+
#ifdef TUNNEL_SINK
pa_xfree(u->sink_name);
#else
@@ -1026,7 +1921,9 @@ void pa__done(pa_core *c, pa_module*m) {
#endif
pa_xfree(u->server_name);
+ pa_xfree(u->device_description);
+ pa_xfree(u->server_fqdn);
+ pa_xfree(u->user_name);
+
pa_xfree(u);
}
-
-
diff --git a/src/modules/module-volume-restore.c b/src/modules/module-volume-restore.c
index efa59f40..d862c203 100644
--- a/src/modules/module-volume-restore.c
+++ b/src/modules/module-volume-restore.c
@@ -1,18 +1,18 @@
-/* $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
@@ -24,7 +24,6 @@
#endif
#include <unistd.h>
-#include <assert.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
@@ -33,6 +32,8 @@
#include <ctype.h>
#include <pulse/xmalloc.h>
+#include <pulse/volume.h>
+#include <pulse/timeval.h>
#include <pulsecore/core-error.h>
#include <pulsecore/module.h>
@@ -42,57 +43,66 @@
#include <pulsecore/core-subscribe.h>
#include <pulsecore/sink-input.h>
#include <pulsecore/source-output.h>
-#include <pulsecore/core-util.h>
#include <pulsecore/namereg.h>
-#include <pulse/volume.h>
#include "module-volume-restore-symdef.h"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("Automatically restore the volume and the devices of streams")
-PA_MODULE_USAGE("table=<filename>")
-PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Automatically restore the volume and the devices of streams");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+PA_MODULE_USAGE(
+ "table=<filename> "
+ "restore_device=<Restore the device for each stream?> "
+ "restore_volume=<Restore the volume for each stream?>"
+);
#define WHITESPACE "\n\r \t"
-
#define DEFAULT_VOLUME_TABLE_FILE "volume-restore.table"
+#define SAVE_INTERVAL 10
static const char* const valid_modargs[] = {
"table",
+ "restore_device",
+ "restore_volume",
NULL,
};
struct rule {
char* name;
- int volume_is_set;
+ pa_bool_t volume_is_set;
pa_cvolume volume;
- char *sink;
- char *source;
+ char *sink, *source;
};
struct userdata {
+ pa_core *core;
pa_hashmap *hashmap;
pa_subscription *subscription;
- pa_hook_slot *sink_input_hook_slot, *source_output_hook_slot;
- int modified;
+ pa_hook_slot
+ *sink_input_new_hook_slot,
+ *sink_input_fixate_hook_slot,
+ *source_output_new_hook_slot;
+ pa_bool_t modified;
char *table_file;
+ pa_time_event *save_time_event;
};
static pa_cvolume* parse_volume(const char *s, pa_cvolume *v) {
char *p;
long k;
unsigned i;
-
- assert(s);
- assert(v);
+
+ pa_assert(s);
+ pa_assert(v);
if (!isdigit(*s))
return NULL;
k = strtol(s, &p, 0);
- if (k <= 0 || k > PA_CHANNELS_MAX)
+ if (k <= 0 || k > (long) PA_CHANNELS_MAX)
return NULL;
-
+
v->channels = (unsigned) k;
for (i = 0; i < v->channels; i++) {
@@ -103,9 +113,9 @@ static pa_cvolume* parse_volume(const char *s, pa_cvolume *v) {
k = strtol(p, &p, 0);
- if (k < PA_VOLUME_MUTED)
+ if (k < (long) PA_VOLUME_MUTED)
return NULL;
-
+
v->values[i] = (pa_volume_t) k;
}
@@ -122,32 +132,28 @@ static int load_rules(struct userdata *u) {
char buf_name[256], buf_volume[256], buf_sink[256], buf_source[256];
char *ln = buf_name;
- f = u->table_file ?
- fopen(u->table_file, "r") :
- pa_open_config_file(NULL, DEFAULT_VOLUME_TABLE_FILE, NULL, &u->table_file, "r");
-
- if (!f) {
+ if (!(f = fopen(u->table_file, "r"))) {
if (errno == ENOENT) {
- pa_log_info("starting with empty ruleset.");
+ pa_log_info("Starting with empty ruleset.");
ret = 0;
} else
- pa_log("failed to open file '%s': %s", u->table_file, pa_cstrerror(errno));
-
+ pa_log("Failed to open file '%s': %s", u->table_file, pa_cstrerror(errno));
+
goto finish;
}
pa_lock_fd(fileno(f), 1);
-
+
while (!feof(f)) {
struct rule *rule;
pa_cvolume v;
- int v_is_set;
-
+ pa_bool_t v_is_set;
+
if (!fgets(ln, sizeof(buf_name), f))
break;
n++;
-
+
pa_strip_nl(ln);
if (ln[0] == '#')
@@ -168,7 +174,7 @@ static int load_rules(struct userdata *u) {
continue;
}
- assert(ln == buf_source);
+ pa_assert(ln == buf_source);
if (buf_volume[0]) {
if (!parse_volume(buf_volume, &v)) {
@@ -176,17 +182,17 @@ static int load_rules(struct userdata *u) {
goto finish;
}
- v_is_set = 1;
+ v_is_set = TRUE;
} else
- v_is_set = 0;
+ v_is_set = FALSE;
ln = buf_name;
-
+
if (pa_hashmap_get(u->hashmap, buf_name)) {
pa_log("double entry in %s:%u, ignoring", u->table_file, n);
continue;
}
-
+
rule = pa_xnew(struct rule, 1);
rule->name = pa_xstrdup(buf_name);
if ((rule->volume_is_set = v_is_set))
@@ -203,7 +209,7 @@ static int load_rules(struct userdata *u) {
}
ret = 0;
-
+
finish:
if (f) {
pa_lock_fd(fileno(f), 0);
@@ -218,13 +224,14 @@ static int save_rules(struct userdata *u) {
int ret = -1;
void *state = NULL;
struct rule *rule;
-
- f = u->table_file ?
- fopen(u->table_file, "w") :
- pa_open_config_file(NULL, DEFAULT_VOLUME_TABLE_FILE, NULL, &u->table_file, "w");
- if (!f) {
- pa_log("failed to open file '%s': %s", u->table_file, pa_cstrerror(errno));
+ if (!u->modified)
+ return 0;
+
+ pa_log_info("Saving rules...");
+
+ if (!(f = fopen(u->table_file, "w"))) {
+ pa_log("Failed to open file '%s': %s", u->table_file, pa_cstrerror(errno));
goto finish;
}
@@ -232,7 +239,7 @@ static int save_rules(struct userdata *u) {
while ((rule = pa_hashmap_iterate(u->hashmap, &state, NULL))) {
unsigned i;
-
+
fprintf(f, "%s\n", rule->name);
if (rule->volume_is_set) {
@@ -241,14 +248,16 @@ static int save_rules(struct userdata *u) {
for (i = 0; i < rule->volume.channels; i++)
fprintf(f, " %u", rule->volume.values[i]);
}
-
+
fprintf(f, "\n%s\n%s\n",
rule->sink ? rule->sink : "",
rule->source ? rule->source : "");
}
-
+
ret = 0;
-
+ u->modified = FALSE;
+ pa_log_debug("Successfully saved rules...");
+
finish:
if (f) {
pa_lock_fd(fileno(f), 0);
@@ -260,11 +269,11 @@ finish:
static char* client_name(pa_client *c) {
char *t, *e;
-
- if (!c->name || !c->driver)
+
+ if (!pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME) || !c->driver)
return NULL;
- t = pa_sprintf_malloc("%s$%s", c->driver, c->name);
+ t = pa_sprintf_malloc("%s$%s", c->driver, pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME));
t[strcspn(t, "\n\r#")] = 0;
if (!*t) {
@@ -280,23 +289,38 @@ static char* client_name(pa_client *c) {
* sessions of the same application, which is something we
* explicitly don't want. Besides other stuff this makes xmms
* with esound work properly for us. */
-
+
if (*k == ')' && *(k+1) == 0)
*e = 0;
}
-
+
return t;
}
+static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) {
+ struct userdata *u = userdata;
+
+ pa_assert(a);
+ pa_assert(e);
+ pa_assert(tv);
+ pa_assert(u);
+
+ pa_assert(e == u->save_time_event);
+ u->core->mainloop->time_free(u->save_time_event);
+ u->save_time_event = NULL;
+
+ save_rules(u);
+}
+
static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
struct userdata *u = userdata;
pa_sink_input *si = NULL;
pa_source_output *so = NULL;
struct rule *r;
char *name;
-
- assert(c);
- assert(u);
+
+ pa_assert(c);
+ pa_assert(u);
if (t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW) &&
t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE) &&
@@ -307,15 +331,15 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT) {
if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx)))
return;
-
+
if (!si->client || !(name = client_name(si->client)))
return;
} else {
- assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT);
+ pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT);
if (!(so = pa_idxset_get_by_index(c->source_outputs, idx)))
return;
-
+
if (!so->client || !(name = client_name(so->client)))
return;
}
@@ -328,27 +352,27 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
if (!r->volume_is_set || !pa_cvolume_equal(pa_sink_input_get_volume(si), &r->volume)) {
pa_log_info("Saving volume for <%s>", r->name);
r->volume = *pa_sink_input_get_volume(si);
- r->volume_is_set = 1;
- u->modified = 1;
+ r->volume_is_set = TRUE;
+ u->modified = TRUE;
}
if (!r->sink || strcmp(si->sink->name, r->sink) != 0) {
pa_log_info("Saving sink for <%s>", r->name);
pa_xfree(r->sink);
r->sink = pa_xstrdup(si->sink->name);
- u->modified = 1;
+ u->modified = TRUE;
}
} else {
- assert(so);
+ pa_assert(so);
if (!r->source || strcmp(so->source->name, r->source) != 0) {
pa_log_info("Saving source for <%s>", r->name);
pa_xfree(r->source);
r->source = pa_xstrdup(so->source->name);
- u->modified = 1;
+ u->modified = TRUE;
}
}
-
+
} else {
pa_log_info("Creating new entry for <%s>", name);
@@ -357,26 +381,60 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
if (si) {
r->volume = *pa_sink_input_get_volume(si);
- r->volume_is_set = 1;
+ r->volume_is_set = TRUE;
r->sink = pa_xstrdup(si->sink->name);
r->source = NULL;
} else {
- assert(so);
- r->volume_is_set = 0;
+ pa_assert(so);
+ r->volume_is_set = FALSE;
r->sink = NULL;
r->source = pa_xstrdup(so->source->name);
}
-
+
pa_hashmap_put(u->hashmap, r->name, r);
- u->modified = 1;
+ u->modified = TRUE;
+ }
+
+ if (u->modified && !u->save_time_event) {
+ struct timeval tv;
+ pa_gettimeofday(&tv);
+ tv.tv_sec += SAVE_INTERVAL;
+ u->save_time_event = u->core->mainloop->time_new(u->core->mainloop, &tv, save_time_callback, u);
+ }
+}
+
+static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *data, struct userdata *u) {
+ struct rule *r;
+ char *name;
+
+ pa_assert(data);
+
+ /* In the NEW hook we only adjust the device. Adjusting the volume
+ * is left for the FIXATE hook */
+
+ if (!data->client || !(name = client_name(data->client)))
+ return PA_HOOK_OK;
+
+ if ((r = pa_hashmap_get(u->hashmap, name))) {
+ if (!data->sink && r->sink) {
+ if ((data->sink = pa_namereg_get(c, r->sink, PA_NAMEREG_SINK, 1)))
+ pa_log_info("Restoring sink for <%s>", r->name);
+ }
}
+
+ pa_xfree(name);
+
+ return PA_HOOK_OK;
}
-static pa_hook_result_t sink_input_hook_callback(pa_core *c, pa_sink_input_new_data *data, struct userdata *u) {
+static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_input_new_data *data, struct userdata *u) {
struct rule *r;
char *name;
- assert(data);
+ pa_assert(data);
+
+ /* In the FIXATE hook we only adjust the volum. Adjusting the device
+ * is left for the NEW hook */
if (!data->client || !(name = client_name(data->client)))
return PA_HOOK_OK;
@@ -387,21 +445,18 @@ static pa_hook_result_t sink_input_hook_callback(pa_core *c, pa_sink_input_new_d
pa_log_info("Restoring volume for <%s>", r->name);
pa_sink_input_new_data_set_volume(data, &r->volume);
}
-
- if (!data->sink && r->sink) {
- if ((data->sink = pa_namereg_get(c, r->sink, PA_NAMEREG_SINK, 1)))
- pa_log_info("Restoring sink for <%s>", r->name);
- }
}
+ pa_xfree(name);
+
return PA_HOOK_OK;
}
-static pa_hook_result_t source_output_hook_callback(pa_core *c, pa_source_output_new_data *data, struct userdata *u) {
+static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *data, struct userdata *u) {
struct rule *r;
char *name;
- assert(data);
+ pa_assert(data);
if (!data->client || !(name = client_name(data->client)))
return PA_HOOK_OK;
@@ -416,12 +471,12 @@ static pa_hook_result_t source_output_hook_callback(pa_core *c, pa_source_output
return PA_HOOK_OK;
}
-int pa__init(pa_core *c, pa_module*m) {
+int pa__init(pa_module*m) {
pa_modargs *ma = NULL;
struct userdata *u;
-
- assert(c);
- assert(m);
+ pa_bool_t restore_device = TRUE, restore_volume = TRUE;
+
+ pa_assert(m);
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
pa_log("Failed to parse module arguments");
@@ -429,35 +484,56 @@ int pa__init(pa_core *c, pa_module*m) {
}
u = pa_xnew(struct userdata, 1);
+ u->core = m->core;
u->hashmap = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+ u->modified = FALSE;
u->subscription = NULL;
- u->table_file = pa_xstrdup(pa_modargs_get_value(ma, "table", NULL));
- u->modified = 0;
-
+ u->sink_input_new_hook_slot = u->sink_input_fixate_hook_slot = u->source_output_new_hook_slot = NULL;
+ u->save_time_event = NULL;
+
m->userdata = u;
-
+
+ if (!(u->table_file = pa_state_path(pa_modargs_get_value(ma, "table", DEFAULT_VOLUME_TABLE_FILE))))
+ goto fail;
+
+ if (pa_modargs_get_value_boolean(ma, "restore_device", &restore_device) < 0 ||
+ pa_modargs_get_value_boolean(ma, "restore_volume", &restore_volume) < 0) {
+ pa_log("restore_volume= and restore_device= expect boolean arguments");
+ goto fail;
+ }
+
+ if (!(restore_device || restore_volume)) {
+ pa_log("Both restrong the volume and restoring the device are disabled. There's no point in using this module at all then, failing.");
+ goto fail;
+ }
+
if (load_rules(u) < 0)
goto fail;
- u->subscription = pa_subscription_new(c, PA_SUBSCRIPTION_MASK_SINK_INPUT|PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, subscribe_callback, u);
- u->sink_input_hook_slot = pa_hook_connect(&c->hook_sink_input_new, (pa_hook_cb_t) sink_input_hook_callback, u);
- u->source_output_hook_slot = pa_hook_connect(&c->hook_source_output_new, (pa_hook_cb_t) source_output_hook_callback, u);
+ u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK_INPUT|PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, subscribe_callback, u);
+
+ if (restore_device) {
+ u->sink_input_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_new_hook_callback, u);
+ u->source_output_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) source_output_new_hook_callback, u);
+ }
+
+ if (restore_volume)
+ u->sink_input_fixate_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_fixate_hook_callback, u);
pa_modargs_free(ma);
return 0;
fail:
- pa__done(c, m);
-
+ pa__done(m);
if (ma)
pa_modargs_free(ma);
-
+
return -1;
}
static void free_func(void *p, void *userdata) {
struct rule *r = p;
- assert(r);
+ pa_assert(r);
pa_xfree(r->name);
pa_xfree(r->sink);
@@ -465,11 +541,10 @@ static void free_func(void *p, void *userdata) {
pa_xfree(r);
}
-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;
@@ -477,21 +552,21 @@ void pa__done(pa_core *c, pa_module*m) {
if (u->subscription)
pa_subscription_free(u->subscription);
- if (u->sink_input_hook_slot)
- pa_hook_slot_free(u->sink_input_hook_slot);
- if (u->source_output_hook_slot)
- pa_hook_slot_free(u->source_output_hook_slot);
+ if (u->sink_input_new_hook_slot)
+ pa_hook_slot_free(u->sink_input_new_hook_slot);
+ if (u->sink_input_fixate_hook_slot)
+ pa_hook_slot_free(u->sink_input_fixate_hook_slot);
+ if (u->source_output_new_hook_slot)
+ pa_hook_slot_free(u->source_output_new_hook_slot);
if (u->hashmap) {
-
- if (u->modified)
- save_rules(u);
-
+ save_rules(u);
pa_hashmap_free(u->hashmap, free_func, NULL);
}
+ if (u->save_time_event)
+ u->core->mainloop->time_free(u->save_time_event);
+
pa_xfree(u->table_file);
pa_xfree(u);
}
-
-
diff --git a/src/modules/module-waveout.c b/src/modules/module-waveout.c
index 4043c136..b452c3bf 100644
--- a/src/modules/module-waveout.c
+++ b/src/modules/module-waveout.c
@@ -1,18 +1,19 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
+ Copyright 2006 Lennart Poettering
+ Copyright 2006-2007 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
@@ -25,7 +26,6 @@
#include <windows.h>
#include <mmsystem.h>
-#include <assert.h>
#include <pulse/mainloop-api.h>
@@ -47,7 +47,8 @@ PA_MODULE_DESCRIPTION("Windows waveOut Sink/Source")
PA_MODULE_VERSION(PACKAGE_VERSION)
PA_MODULE_USAGE(
"sink_name=<name for the sink> "
- "source_name=<name for the source>"
+ "source_name=<name for the source> "
+ "device=<device number> "
"record=<enable source?> "
"playback=<enable sink?> "
"format=<sample format> "
@@ -90,6 +91,7 @@ struct userdata {
static const char* const valid_modargs[] = {
"sink_name",
"source_name",
+ "device",
"record",
"playback",
"fragments",
@@ -172,7 +174,7 @@ static void do_write(struct userdata *u)
pa_log_error(__FILE__ ": ERROR: Unable to write waveOut block: %d",
res);
}
-
+
u->written_bytes += hdr->dwBufferLength;
EnterCriticalSection(&u->crit);
@@ -233,7 +235,7 @@ static void do_read(struct userdata *u)
pa_log_error(__FILE__ ": ERROR: Unable to add waveIn block: %d",
res);
}
-
+
free_frags--;
u->cur_ihdr++;
u->cur_ihdr %= u->fragments;
@@ -432,6 +434,7 @@ int pa__init(pa_core *c, pa_module*m) {
WAVEFORMATEX wf;
int nfrags, frag_size;
int record = 1, playback = 1;
+ unsigned int device;
pa_sample_spec ss;
pa_channel_map map;
pa_modargs *ma = NULL;
@@ -455,6 +458,12 @@ int pa__init(pa_core *c, pa_module*m) {
goto fail;
}
+ device = WAVE_MAPPER;
+ if (pa_modargs_get_value_u32(ma, "device", &device) < 0) {
+ pa_log("failed to parse device argument");
+ goto fail;
+ }
+
nfrags = 5;
frag_size = 8192;
if (pa_modargs_get_value_s32(ma, "fragments", &nfrags) < 0 || pa_modargs_get_value_s32(ma, "fragment_size", &frag_size) < 0) {
@@ -474,7 +483,7 @@ int pa__init(pa_core *c, pa_module*m) {
u = pa_xmalloc(sizeof(struct userdata));
if (record) {
- if (waveInOpen(&hwi, WAVE_MAPPER, &wf, (DWORD_PTR)chunk_ready_cb, (DWORD_PTR)u, CALLBACK_FUNCTION) != MMSYSERR_NOERROR) {
+ if (waveInOpen(&hwi, device, &wf, (DWORD_PTR)chunk_ready_cb, (DWORD_PTR)u, CALLBACK_FUNCTION) != MMSYSERR_NOERROR) {
pa_log("failed to open waveIn");
goto fail;
}
@@ -486,7 +495,7 @@ int pa__init(pa_core *c, pa_module*m) {
}
if (playback) {
- if (waveOutOpen(&hwo, WAVE_MAPPER, &wf, (DWORD_PTR)chunk_done_cb, (DWORD_PTR)u, CALLBACK_FUNCTION) != MMSYSERR_NOERROR) {
+ if (waveOutOpen(&hwo, device, &wf, (DWORD_PTR)chunk_done_cb, (DWORD_PTR)u, CALLBACK_FUNCTION) != MMSYSERR_NOERROR) {
pa_log("failed to open waveOut");
goto fail;
}
@@ -561,7 +570,7 @@ int pa__init(pa_core *c, pa_module*m) {
u->ohdrs[i].lpData = pa_xmalloc(u->fragment_size);
assert(u->ohdrs);
}
-
+
u->module = m;
m->userdata = u;
@@ -585,7 +594,7 @@ fail:
if (ma)
pa_modargs_free(ma);
-
+
return -1;
}
@@ -597,7 +606,7 @@ void pa__done(pa_core *c, pa_module*m) {
if (!(u = m->userdata))
return;
-
+
if (u->event)
c->mainloop->time_free(u->event);
@@ -608,12 +617,12 @@ void pa__done(pa_core *c, pa_module*m) {
pa_sink_disconnect(u->sink);
pa_sink_unref(u->sink);
}
-
+
if (u->source) {
pa_source_disconnect(u->source);
pa_source_unref(u->source);
}
-
+
if (u->hwi != INVALID_HANDLE_VALUE) {
waveInReset(u->hwi);
waveInClose(u->hwi);
@@ -633,6 +642,6 @@ void pa__done(pa_core *c, pa_module*m) {
pa_xfree(u->ohdrs);
DeleteCriticalSection(&u->crit);
-
+
pa_xfree(u);
}
diff --git a/src/modules/module-x11-bell.c b/src/modules/module-x11-bell.c
index 48e95c8c..f7be48f7 100644
--- a/src/modules/module-x11-bell.c
+++ b/src/modules/module-x11-bell.c
@@ -1,18 +1,18 @@
-/* $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
@@ -24,7 +24,6 @@
#endif
#include <stdio.h>
-#include <assert.h>
#include <stdlib.h>
#include <string.h>
@@ -43,20 +42,11 @@
#include "module-x11-bell-symdef.h"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("X11 Bell interceptor")
-PA_MODULE_VERSION(PACKAGE_VERSION)
-PA_MODULE_USAGE("sink=<sink to connect to> sample=<sample name> display=<X11 display>")
-
-struct userdata {
- pa_core *core;
- int xkb_event_base;
- char *sink_name;
- char *scache_item;
-
- pa_x11_wrapper *x11_wrapper;
- pa_x11_client *x11_client;
-};
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("X11 bell interceptor");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
+PA_MODULE_USAGE("sink=<sink to connect to> sample=<sample name> display=<X11 display>");
static const char* const valid_modargs[] = {
"sink",
@@ -65,30 +55,34 @@ static const char* const valid_modargs[] = {
NULL
};
-static int ring_bell(struct userdata *u, int percent) {
- pa_sink *s;
- assert(u);
+struct userdata {
+ pa_core *core;
+ pa_module *module;
- if (!(s = pa_namereg_get(u->core, u->sink_name, PA_NAMEREG_SINK, 1))) {
- pa_log("Invalid sink: %s", u->sink_name);
- return -1;
- }
+ int xkb_event_base;
- pa_scache_play_item(u->core, u->scache_item, s, (percent*PA_VOLUME_NORM)/100);
- return 0;
-}
+ char *sink_name;
+ char *scache_item;
+
+ pa_x11_wrapper *x11_wrapper;
+ pa_x11_client *x11_client;
+};
-static int x11_event_callback(pa_x11_wrapper *w, XEvent *e, void *userdata) {
+static int x11_event_cb(pa_x11_wrapper *w, XEvent *e, void *userdata) {
XkbBellNotifyEvent *bne;
struct userdata *u = userdata;
- assert(w && e && u && u->x11_wrapper == w);
+
+ pa_assert(w);
+ pa_assert(e);
+ pa_assert(u);
+ pa_assert(u->x11_wrapper == w);
if (((XkbEvent*) e)->any.xkb_type != XkbBellNotify)
return 0;
bne = (XkbBellNotifyEvent*) e;
- if (ring_bell(u, bne->percent) < 0) {
+ if (pa_scache_play_item_by_name(u->core, u->scache_item, u->sink_name, TRUE, (bne->percent*PA_VOLUME_NORM)/100, NULL, NULL) < 0) {
pa_log_info("Ringing bell failed, reverting to X11 device bell.");
XkbForceDeviceBell(pa_x11_wrapper_get_display(w), bne->device, bne->bell_class, bne->bell_id, bne->percent);
}
@@ -96,30 +90,52 @@ static int x11_event_callback(pa_x11_wrapper *w, XEvent *e, void *userdata) {
return 1;
}
-int pa__init(pa_core *c, pa_module*m) {
+static void x11_kill_cb(pa_x11_wrapper *w, void *userdata) {
+ struct userdata *u = userdata;
+
+ pa_assert(w);
+ pa_assert(u);
+ pa_assert(u->x11_wrapper == w);
+
+ if (u->x11_client)
+ pa_x11_client_free(u->x11_client);
+
+ if (u->x11_wrapper)
+ pa_x11_wrapper_unref(u->x11_wrapper);
+
+ u->x11_client = NULL;
+ u->x11_wrapper = NULL;
+
+ pa_module_unload_request(u->module);
+}
+
+int pa__init(pa_module*m) {
+
struct userdata *u = NULL;
pa_modargs *ma = NULL;
int major, minor;
unsigned int auto_ctrls, auto_values;
- 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;
}
-
- m->userdata = u = pa_xmalloc(sizeof(struct userdata));
- u->core = c;
- u->scache_item = pa_xstrdup(pa_modargs_get_value(ma, "sample", "x11-bell"));
+
+ m->userdata = u = pa_xnew(struct userdata, 1);
+ u->core = m->core;
+ u->module = m;
+ u->scache_item = pa_xstrdup(pa_modargs_get_value(ma, "sample", "bell-window-system"));
u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
u->x11_client = NULL;
- if (!(u->x11_wrapper = pa_x11_wrapper_get(c, pa_modargs_get_value(ma, "display", NULL))))
+ if (!(u->x11_wrapper = pa_x11_wrapper_get(m->core, pa_modargs_get_value(ma, "display", NULL))))
goto fail;
major = XkbMajorVersion;
minor = XkbMinorVersion;
-
+
if (!XkbLibraryVersion(&major, &minor)) {
pa_log("XkbLibraryVersion() failed");
goto fail;
@@ -128,7 +144,6 @@ int pa__init(pa_core *c, pa_module*m) {
major = XkbMajorVersion;
minor = XkbMinorVersion;
-
if (!XkbQueryExtension(pa_x11_wrapper_get_display(u->x11_wrapper), NULL, &u->xkb_event_base, NULL, &major, &minor)) {
pa_log("XkbQueryExtension() failed");
goto fail;
@@ -139,23 +154,28 @@ int pa__init(pa_core *c, pa_module*m) {
XkbSetAutoResetControls(pa_x11_wrapper_get_display(u->x11_wrapper), XkbAudibleBellMask, &auto_ctrls, &auto_values);
XkbChangeEnabledControls(pa_x11_wrapper_get_display(u->x11_wrapper), XkbUseCoreKbd, XkbAudibleBellMask, 0);
- u->x11_client = pa_x11_client_new(u->x11_wrapper, x11_event_callback, u);
-
+ u->x11_client = pa_x11_client_new(u->x11_wrapper, x11_event_cb, x11_kill_cb, u);
+
pa_modargs_free(ma);
-
+
return 0;
-
+
fail:
if (ma)
pa_modargs_free(ma);
- if (m->userdata)
- pa__done(c, m);
+
+ pa__done(m);
+
return -1;
}
-void pa__done(pa_core *c, pa_module*m) {
- struct userdata *u = m->userdata;
- assert(c && m && u);
+void pa__done(pa_module*m) {
+ struct userdata *u;
+
+ pa_assert(m);
+
+ if (!(u = m->userdata))
+ return;
pa_xfree(u->scache_item);
pa_xfree(u->sink_name);
diff --git a/src/modules/module-x11-publish.c b/src/modules/module-x11-publish.c
index f2cace14..705d90f4 100644
--- a/src/modules/module-x11-publish.c
+++ b/src/modules/module-x11-publish.c
@@ -1,18 +1,18 @@
-/* $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
@@ -24,7 +24,6 @@
#endif
#include <stdio.h>
-#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@@ -52,10 +51,11 @@
#include "module-x11-publish-symdef.h"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("X11 Credential Publisher")
-PA_MODULE_VERSION(PACKAGE_VERSION)
-PA_MODULE_USAGE("display=<X11 display>")
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("X11 credential publisher");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
+PA_MODULE_USAGE("display=<X11 display>");
static const char* const valid_modargs[] = {
"display",
@@ -67,39 +67,62 @@ static const char* const valid_modargs[] = {
struct userdata {
pa_core *core;
- pa_x11_wrapper *x11_wrapper;
+ pa_module *module;
+
char *id;
uint8_t auth_cookie[PA_NATIVE_COOKIE_LENGTH];
- int auth_cookie_in_property;
+ pa_bool_t auth_cookie_in_property;
+
+ pa_x11_wrapper *x11_wrapper;
+ pa_x11_client *x11_client;
};
+static void x11_kill_cb(pa_x11_wrapper *w, void *userdata) {
+ struct userdata *u = userdata;
+
+ pa_assert(w);
+ pa_assert(u);
+ pa_assert(u->x11_wrapper == w);
+
+ if (u->x11_client)
+ pa_x11_client_free(u->x11_client);
+
+ if (u->x11_wrapper)
+ pa_x11_wrapper_unref(u->x11_wrapper);
+
+ u->x11_client = NULL;
+ u->x11_wrapper = NULL;
+
+ pa_module_unload_request(u->module);
+}
+
static int load_key(struct userdata *u, const char*fn) {
- assert(u);
+ pa_assert(u);
+
+ u->auth_cookie_in_property = FALSE;
- u->auth_cookie_in_property = 0;
-
if (!fn && pa_authkey_prop_get(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME, u->auth_cookie, sizeof(u->auth_cookie)) >= 0) {
pa_log_debug("using already loaded auth cookie.");
pa_authkey_prop_ref(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME);
u->auth_cookie_in_property = 1;
return 0;
}
-
+
if (!fn)
fn = PA_NATIVE_COOKIE_FILE;
if (pa_authkey_load_auto(fn, u->auth_cookie, sizeof(u->auth_cookie)) < 0)
return -1;
- pa_log_debug("loading cookie from disk.");
-
+ pa_log_debug("Loading cookie from disk.");
+
if (pa_authkey_prop_put(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME, u->auth_cookie, sizeof(u->auth_cookie)) >= 0)
- u->auth_cookie_in_property = 1;
+ u->auth_cookie_in_property = TRUE;
return 0;
}
-int pa__init(pa_core *c, pa_module*m) {
+int pa__init(pa_module*m) {
struct userdata *u;
pa_modargs *ma = NULL;
char hn[256], un[128];
@@ -108,32 +131,40 @@ int pa__init(pa_core *c, pa_module*m) {
char *s;
pa_strlist *l;
+ 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));
- u->core = c;
+ m->userdata = u = pa_xnew(struct userdata, 1);
+ u->core = m->core;
+ u->module = m;
u->id = NULL;
- u->auth_cookie_in_property = 0;
+ u->auth_cookie_in_property = FALSE;
+ u->x11_client = NULL;
+ u->x11_wrapper = NULL;
if (load_key(u, pa_modargs_get_value(ma, "cookie", NULL)) < 0)
goto fail;
- if (!(u->x11_wrapper = pa_x11_wrapper_get(c, pa_modargs_get_value(ma, "display", NULL))))
+ if (!(u->x11_wrapper = pa_x11_wrapper_get(m->core, pa_modargs_get_value(ma, "display", NULL))))
goto fail;
- if (!(l = pa_property_get(c, PA_NATIVE_SERVER_PROPERTY_NAME)))
+ if (!(l = pa_property_get(m->core, PA_NATIVE_SERVER_PROPERTY_NAME)))
goto fail;
+ l = pa_strlist_reverse(l);
s = pa_strlist_tostring(l);
+ l = pa_strlist_reverse(l);
+
pa_x11_set_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_SERVER", s);
pa_xfree(s);
-
+
if (!pa_get_fqdn(hn, sizeof(hn)) || !pa_get_user_name(un, sizeof(un)))
goto fail;
-
+
u->id = pa_sprintf_malloc("%s@%s/%u", un, hn, (unsigned) getpid());
pa_x11_set_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_ID", u->id);
@@ -144,25 +175,33 @@ int pa__init(pa_core *c, pa_module*m) {
pa_x11_set_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_SINK", t);
pa_x11_set_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_COOKIE", pa_hexstr(u->auth_cookie, sizeof(u->auth_cookie), hx, sizeof(hx)));
-
+
+ u->x11_client = pa_x11_client_new(u->x11_wrapper, NULL, x11_kill_cb, u);
+
pa_modargs_free(ma);
+
return 0;
-
+
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;
-
+
+ if (u->x11_client)
+ pa_x11_client_free(u->x11_client);
+
if (u->x11_wrapper) {
char t[256];
@@ -177,15 +216,13 @@ void pa__done(pa_core *c, pa_module*m) {
pa_x11_del_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_COOKIE");
XSync(pa_x11_wrapper_get_display(u->x11_wrapper), False);
}
- }
-
- if (u->x11_wrapper)
+
pa_x11_wrapper_unref(u->x11_wrapper);
+ }
if (u->auth_cookie_in_property)
- pa_authkey_prop_unref(c, PA_NATIVE_COOKIE_PROPERTY_NAME);
+ pa_authkey_prop_unref(m->core, PA_NATIVE_COOKIE_PROPERTY_NAME);
pa_xfree(u->id);
pa_xfree(u);
}
-
diff --git a/src/modules/module-x11-xsmp.c b/src/modules/module-x11-xsmp.c
new file mode 100644
index 00000000..696826d8
--- /dev/null
+++ b/src/modules/module-x11-xsmp.c
@@ -0,0 +1,247 @@
+/***
+ 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <X11/Xlib.h>
+#include <X11/SM/SMlib.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+
+#include <pulsecore/iochannel.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/core-scache.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/x11wrap.h>
+
+#include "module-x11-xsmp-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("X11 session management");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+PA_MODULE_USAGE("session_manager=<session manager string> display=<X11 display>");
+
+static pa_bool_t ice_in_use = FALSE;
+
+static const char* const valid_modargs[] = {
+ "session_manager",
+ "display",
+ NULL
+};
+
+struct userdata {
+ pa_core *core;
+ pa_module *module;
+ pa_client *client;
+ SmcConn connection;
+ pa_x11_wrapper *x11;
+};
+
+static void die_cb(SmcConn connection, SmPointer client_data){
+ struct userdata *u = client_data;
+ pa_assert(u);
+
+ pa_log_debug("Got die message from XSMP.");
+
+ pa_x11_wrapper_kill(u->x11);
+
+ pa_x11_wrapper_unref(u->x11);
+ u->x11 = NULL;
+
+ pa_module_unload_request(u->module);
+}
+
+static void save_complete_cb(SmcConn connection, SmPointer client_data) {
+}
+
+static void shutdown_cancelled_cb(SmcConn connection, SmPointer client_data) {
+ SmcSaveYourselfDone(connection, True);
+}
+
+static void save_yourself_cb(SmcConn connection, SmPointer client_data, int save_type, Bool _shutdown, int interact_style, Bool fast) {
+ SmcSaveYourselfDone(connection, True);
+}
+
+static void ice_io_cb(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t flags, void *userdata) {
+ IceConn connection = userdata;
+
+ if (IceProcessMessages(connection, NULL, NULL) == IceProcessMessagesIOError) {
+ IceSetShutdownNegotiation(connection, False);
+ IceCloseConnection(connection);
+ }
+}
+
+static void new_ice_connection(IceConn connection, IcePointer client_data, Bool opening, IcePointer *watch_data) {
+ struct pa_core *c = client_data;
+
+ if (opening)
+ *watch_data = c->mainloop->io_new(
+ c->mainloop,
+ IceConnectionNumber(connection),
+ PA_IO_EVENT_INPUT,
+ ice_io_cb,
+ connection);
+ else
+ c->mainloop->io_free(*watch_data);
+}
+
+int pa__init(pa_module*m) {
+
+ pa_modargs *ma = NULL;
+ char t[256], *vendor, *client_id, *k;
+ SmcCallbacks callbacks;
+ SmProp prop_program, prop_user;
+ SmProp *prop_list[2];
+ SmPropValue val_program, val_user;
+ struct userdata *u;
+ const char *e;
+
+ pa_assert(m);
+
+ if (ice_in_use) {
+ pa_log("module-x11-xsmp may no be loaded twice.");
+ return -1;
+ }
+
+ IceAddConnectionWatch(new_ice_connection, m->core);
+ ice_in_use = TRUE;
+
+ m->userdata = u = pa_xnew(struct userdata, 1);
+ u->core = m->core;
+ u->module = m;
+ u->client = NULL;
+ u->connection = NULL;
+ u->x11 = NULL;
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log("Failed to parse module arguments");
+ goto fail;
+ }
+
+ if (!(u->x11 = pa_x11_wrapper_get(m->core, pa_modargs_get_value(ma, "display", NULL))))
+ goto fail;
+
+ e = pa_modargs_get_value(ma, "session_manager", NULL);
+
+ if (!e && !getenv("SESSION_MANAGER")) {
+ pa_log("X11 session manager not running.");
+ goto fail;
+ }
+
+ memset(&callbacks, 0, sizeof(callbacks));
+ callbacks.die.callback = die_cb;
+ callbacks.die.client_data = u;
+ callbacks.save_yourself.callback = save_yourself_cb;
+ callbacks.save_yourself.client_data = m->core;
+ callbacks.save_complete.callback = save_complete_cb;
+ callbacks.save_complete.client_data = m->core;
+ callbacks.shutdown_cancelled.callback = shutdown_cancelled_cb;
+ callbacks.shutdown_cancelled.client_data = m->core;
+
+ if (!(u->connection = SmcOpenConnection(
+ (char*) e, m->core,
+ SmProtoMajor, SmProtoMinor,
+ SmcSaveYourselfProcMask | SmcDieProcMask | SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask,
+ &callbacks, NULL, &client_id,
+ sizeof(t), t))) {
+
+ pa_log("Failed to open connection to session manager: %s", t);
+ goto fail;
+ }
+
+ prop_program.name = (char*) SmProgram;
+ prop_program.type = (char*) SmARRAY8;
+ val_program.value = (char*) PACKAGE_NAME;
+ val_program.length = strlen(val_program.value);
+ prop_program.num_vals = 1;
+ prop_program.vals = &val_program;
+ prop_list[0] = &prop_program;
+
+ prop_user.name = (char*) SmUserID;
+ prop_user.type = (char*) SmARRAY8;
+ pa_get_user_name(t, sizeof(t));
+ val_user.value = t;
+ val_user.length = strlen(val_user.value);
+ prop_user.num_vals = 1;
+ prop_user.vals = &val_user;
+ prop_list[1] = &prop_user;
+
+ SmcSetProperties(u->connection, PA_ELEMENTSOF(prop_list), prop_list);
+
+ pa_log_info("Connected to session manager '%s' as '%s'.", vendor = SmcVendor(u->connection), client_id);
+ k = pa_sprintf_malloc("XSMP Session on %s as %s", vendor, client_id);
+ u->client = pa_client_new(u->core, __FILE__, k);
+ pa_xfree(k);
+
+ pa_proplist_sets(u->client->proplist, "xsmp.vendor", vendor);
+ pa_proplist_sets(u->client->proplist, "xsmp.client.id", client_id);
+
+ free(vendor);
+ free(client_id);
+
+ pa_modargs_free(ma);
+
+ return 0;
+
+fail:
+ if (ma)
+ pa_modargs_free(ma);
+
+ pa__done(m);
+
+ return -1;
+}
+
+void pa__done(pa_module*m) {
+ struct userdata *u;
+
+ pa_assert(m);
+
+ if ((u = m->userdata)) {
+
+ if (u->connection)
+ SmcCloseConnection(u->connection, 0, NULL);
+
+ if (u->client)
+ pa_client_free(u->client);
+
+ if (u->x11)
+ pa_x11_wrapper_unref(u->x11);
+
+ pa_xfree(u);
+ }
+
+ if (ice_in_use) {
+ IceRemoveConnectionWatch(new_ice_connection, m->core);
+ ice_in_use = FALSE;
+ }
+}
diff --git a/src/modules/module-zeroconf-discover.c b/src/modules/module-zeroconf-discover.c
new file mode 100644
index 00000000..2fc81370
--- /dev/null
+++ b/src/modules/module-zeroconf-discover.c
@@ -0,0 +1,438 @@
+/***
+ 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <avahi-client/client.h>
+#include <avahi-client/lookup.h>
+#include <avahi-common/alternative.h>
+#include <avahi-common/error.h>
+#include <avahi-common/domain.h>
+#include <avahi-common/malloc.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+
+#include <pulsecore/sink.h>
+#include <pulsecore/source.h>
+#include <pulsecore/native-common.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-subscribe.h>
+#include <pulsecore/hashmap.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/avahi-wrap.h>
+
+#include "module-zeroconf-discover-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("mDNS/DNS-SD Service Discovery");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+
+#define SERVICE_TYPE_SINK "_pulse-sink._tcp"
+#define SERVICE_TYPE_SOURCE "_non-monitor._sub._pulse-source._tcp"
+
+static const char* const valid_modargs[] = {
+ NULL
+};
+
+struct tunnel {
+ AvahiIfIndex interface;
+ AvahiProtocol protocol;
+ char *name, *type, *domain;
+ uint32_t module_index;
+};
+
+struct userdata {
+ pa_core *core;
+ pa_module *module;
+ AvahiPoll *avahi_poll;
+ AvahiClient *client;
+ AvahiServiceBrowser *source_browser, *sink_browser;
+
+ pa_hashmap *tunnels;
+};
+
+static unsigned tunnel_hash(const void *p) {
+ const struct tunnel *t = p;
+
+ return
+ (unsigned) t->interface +
+ (unsigned) t->protocol +
+ pa_idxset_string_hash_func(t->name) +
+ pa_idxset_string_hash_func(t->type) +
+ pa_idxset_string_hash_func(t->domain);
+}
+
+static int tunnel_compare(const void *a, const void *b) {
+ const struct tunnel *ta = a, *tb = b;
+ int r;
+
+ if (ta->interface != tb->interface)
+ return 1;
+ if (ta->protocol != tb->protocol)
+ return 1;
+ if ((r = strcmp(ta->name, tb->name)))
+ return r;
+ if ((r = strcmp(ta->type, tb->type)))
+ return r;
+ if ((r = strcmp(ta->domain, tb->domain)))
+ return r;
+
+ return 0;
+}
+
+static struct tunnel *tunnel_new(
+ AvahiIfIndex interface, AvahiProtocol protocol,
+ const char *name, const char *type, const char *domain) {
+
+ struct tunnel *t;
+ t = pa_xnew(struct tunnel, 1);
+ t->interface = interface;
+ t->protocol = protocol;
+ t->name = pa_xstrdup(name);
+ t->type = pa_xstrdup(type);
+ t->domain = pa_xstrdup(domain);
+ t->module_index = PA_IDXSET_INVALID;
+ return t;
+}
+
+static void tunnel_free(struct tunnel *t) {
+ pa_assert(t);
+ pa_xfree(t->name);
+ pa_xfree(t->type);
+ pa_xfree(t->domain);
+ pa_xfree(t);
+}
+
+static void resolver_cb(
+ AvahiServiceResolver *r,
+ AvahiIfIndex interface, AvahiProtocol protocol,
+ AvahiResolverEvent event,
+ const char *name, const char *type, const char *domain,
+ const char *host_name, const AvahiAddress *a, uint16_t port,
+ AvahiStringList *txt,
+ AvahiLookupResultFlags flags,
+ void *userdata) {
+
+ struct userdata *u = userdata;
+ struct tunnel *tnl;
+
+ pa_assert(u);
+
+ tnl = tunnel_new(interface, protocol, name, type, domain);
+
+ if (event != AVAHI_RESOLVER_FOUND)
+ pa_log("Resolving of '%s' failed: %s", name, avahi_strerror(avahi_client_errno(u->client)));
+ else {
+ char *device = NULL, *dname, *module_name, *args;
+ const char *t;
+ char at[AVAHI_ADDRESS_STR_MAX], cmt[PA_CHANNEL_MAP_SNPRINT_MAX];
+ pa_sample_spec ss;
+ pa_channel_map cm;
+ AvahiStringList *l;
+ pa_bool_t channel_map_set = FALSE;
+ pa_module *m;
+
+ ss = u->core->default_sample_spec;
+ pa_channel_map_init_extend(&cm, ss.channels, PA_CHANNEL_MAP_DEFAULT);
+
+ for (l = txt; l; l = l->next) {
+ char *key, *value;
+ pa_assert_se(avahi_string_list_get_pair(l, &key, &value, NULL) == 0);
+
+ if (strcmp(key, "device") == 0) {
+ pa_xfree(device);
+ device = value;
+ value = NULL;
+ } else if (strcmp(key, "rate") == 0)
+ ss.rate = atoi(value);
+ else if (strcmp(key, "channels") == 0)
+ ss.channels = atoi(value);
+ else if (strcmp(key, "format") == 0)
+ ss.format = pa_parse_sample_format(value);
+ else if (strcmp(key, "channel_map") == 0) {
+ pa_channel_map_parse(&cm, value);
+ channel_map_set = TRUE;
+ }
+
+ avahi_free(key);
+ avahi_free(value);
+ }
+
+ if (!channel_map_set && cm.channels != ss.channels)
+ pa_channel_map_init_extend(&cm, ss.channels, PA_CHANNEL_MAP_DEFAULT);
+
+ if (!pa_sample_spec_valid(&ss)) {
+ pa_log("Service '%s' contains an invalid sample specification.", name);
+ avahi_free(device);
+ goto finish;
+ }
+
+ if (!pa_channel_map_valid(&cm) || cm.channels != ss.channels) {
+ pa_log("Service '%s' contains an invalid channel map.", name);
+ avahi_free(device);
+ goto finish;
+ }
+
+ if (device)
+ dname = pa_sprintf_malloc("tunnel.%s.%s", host_name, device);
+ else
+ dname = pa_sprintf_malloc("tunnel.%s", host_name);
+
+ if (!pa_namereg_is_valid_name(dname)) {
+ pa_log("Cannot construct valid device name from credentials of service '%s'.", dname);
+ avahi_free(device);
+ pa_xfree(dname);
+ goto finish;
+ }
+
+ t = strstr(type, "sink") ? "sink" : "source";
+
+ module_name = pa_sprintf_malloc("module-tunnel-%s", t);
+ args = pa_sprintf_malloc("server=[%s]:%u "
+ "%s=%s "
+ "format=%s "
+ "channels=%u "
+ "rate=%u "
+ "%s_name=%s "
+ "channel_map=%s",
+ avahi_address_snprint(at, sizeof(at), a), port,
+ t, device,
+ pa_sample_format_to_string(ss.format),
+ ss.channels,
+ ss.rate,
+ t, dname,
+ pa_channel_map_snprint(cmt, sizeof(cmt), &cm));
+
+ pa_log_debug("Loading %s with arguments '%s'", module_name, args);
+
+ if ((m = pa_module_load(u->core, module_name, args))) {
+ tnl->module_index = m->index;
+ pa_hashmap_put(u->tunnels, tnl, tnl);
+ tnl = NULL;
+ }
+
+ pa_xfree(module_name);
+ pa_xfree(dname);
+ pa_xfree(args);
+ avahi_free(device);
+ }
+
+finish:
+
+ avahi_service_resolver_free(r);
+
+ if (tnl)
+ tunnel_free(tnl);
+}
+
+static void browser_cb(
+ AvahiServiceBrowser *b,
+ AvahiIfIndex interface, AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ const char *name, const char *type, const char *domain,
+ AvahiLookupResultFlags flags,
+ void *userdata) {
+
+ struct userdata *u = userdata;
+ struct tunnel *t;
+
+ pa_assert(u);
+
+ if (flags & AVAHI_LOOKUP_RESULT_LOCAL)
+ return;
+
+ t = tunnel_new(interface, protocol, name, type, domain);
+
+ if (event == AVAHI_BROWSER_NEW) {
+
+ if (!pa_hashmap_get(u->tunnels, t))
+ if (!(avahi_service_resolver_new(u->client, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, resolver_cb, u)))
+ pa_log("avahi_service_resolver_new() failed: %s", avahi_strerror(avahi_client_errno(u->client)));
+
+ /* We ignore the returned resolver object here, since the we don't
+ * need to attach any special data to it, and we can still destory
+ * it from the callback */
+
+ } else if (event == AVAHI_BROWSER_REMOVE) {
+ struct tunnel *t2;
+
+ if ((t2 = pa_hashmap_get(u->tunnels, t))) {
+ pa_module_unload_by_index(u->core, t2->module_index);
+ pa_hashmap_remove(u->tunnels, t2);
+ tunnel_free(t2);
+ }
+ }
+
+ tunnel_free(t);
+}
+
+static void client_callback(AvahiClient *c, AvahiClientState state, void *userdata) {
+ struct userdata *u = userdata;
+
+ pa_assert(c);
+ pa_assert(u);
+
+ u->client = c;
+
+ switch (state) {
+ case AVAHI_CLIENT_S_REGISTERING:
+ case AVAHI_CLIENT_S_RUNNING:
+ case AVAHI_CLIENT_S_COLLISION:
+
+ if (!u->sink_browser) {
+
+ if (!(u->sink_browser = avahi_service_browser_new(
+ c,
+ AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
+ SERVICE_TYPE_SINK,
+ NULL,
+ 0,
+ browser_cb, u))) {
+
+ pa_log("avahi_service_browser_new() failed: %s", avahi_strerror(avahi_client_errno(c)));
+ pa_module_unload_request(u->module);
+ }
+ }
+
+ if (!u->source_browser) {
+
+ if (!(u->source_browser = avahi_service_browser_new(
+ c,
+ AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
+ SERVICE_TYPE_SOURCE,
+ NULL,
+ 0,
+ browser_cb, u))) {
+
+ pa_log("avahi_service_browser_new() failed: %s", avahi_strerror(avahi_client_errno(c)));
+ pa_module_unload_request(u->module);
+ }
+ }
+
+ break;
+
+ case AVAHI_CLIENT_FAILURE:
+ if (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED) {
+ int error;
+
+ pa_log_debug("Avahi daemon disconnected.");
+
+ if (!(u->client = avahi_client_new(u->avahi_poll, AVAHI_CLIENT_NO_FAIL, client_callback, u, &error))) {
+ pa_log("avahi_client_new() failed: %s", avahi_strerror(error));
+ pa_module_unload_request(u->module);
+ }
+ }
+
+ /* Fall through */
+
+ case AVAHI_CLIENT_CONNECTING:
+
+ if (u->sink_browser) {
+ avahi_service_browser_free(u->sink_browser);
+ u->sink_browser = NULL;
+ }
+
+ if (u->source_browser) {
+ avahi_service_browser_free(u->source_browser);
+ u->source_browser = NULL;
+ }
+
+ break;
+
+ default: ;
+ }
+}
+
+int pa__init(pa_module*m) {
+
+ struct userdata *u;
+ pa_modargs *ma = NULL;
+ int error;
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log("Failed to parse module arguments.");
+ goto fail;
+ }
+
+ m->userdata = u = pa_xnew(struct userdata, 1);
+ u->core = m->core;
+ u->module = m;
+ u->sink_browser = u->source_browser = NULL;
+
+ u->tunnels = pa_hashmap_new(tunnel_hash, tunnel_compare);
+
+ u->avahi_poll = pa_avahi_poll_new(m->core->mainloop);
+
+ if (!(u->client = avahi_client_new(u->avahi_poll, AVAHI_CLIENT_NO_FAIL, client_callback, u, &error))) {
+ pa_log("pa_avahi_client_new() failed: %s", avahi_strerror(error));
+ goto fail;
+ }
+
+ pa_modargs_free(ma);
+
+ return 0;
+
+fail:
+ pa__done(m);
+
+ if (ma)
+ pa_modargs_free(ma);
+
+ return -1;
+}
+
+void pa__done(pa_module*m) {
+ struct userdata*u;
+ pa_assert(m);
+
+ if (!(u = m->userdata))
+ return;
+
+ if (u->client)
+ avahi_client_free(u->client);
+
+ if (u->avahi_poll)
+ pa_avahi_poll_free(u->avahi_poll);
+
+ if (u->tunnels) {
+ struct tunnel *t;
+
+ while ((t = pa_hashmap_steal_first(u->tunnels))) {
+ pa_module_unload_by_index(u->core, t->module_index);
+ tunnel_free(t);
+ }
+
+ pa_hashmap_free(u->tunnels, NULL, NULL);
+ }
+
+ pa_xfree(u);
+}
diff --git a/src/modules/module-zeroconf-publish.c b/src/modules/module-zeroconf-publish.c
index 696d8afe..cb9c1285 100644
--- a/src/modules/module-zeroconf-publish.c
+++ b/src/modules/module-zeroconf-publish.c
@@ -1,18 +1,18 @@
-/* $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
@@ -24,7 +24,6 @@
#endif
#include <stdio.h>
-#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@@ -33,11 +32,11 @@
#include <avahi-client/publish.h>
#include <avahi-common/alternative.h>
#include <avahi-common/error.h>
+#include <avahi-common/domain.h>
#include <pulse/xmalloc.h>
#include <pulse/util.h>
-#include <pulsecore/autoload.h>
#include <pulsecore/sink.h>
#include <pulsecore/source.h>
#include <pulsecore/native-common.h>
@@ -51,74 +50,89 @@
#include "module-zeroconf-publish-symdef.h"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("mDNS/DNS-SD Service Publisher")
-PA_MODULE_VERSION(PACKAGE_VERSION)
-PA_MODULE_USAGE("port=<IP port number>")
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("mDNS/DNS-SD Service Publisher");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+PA_MODULE_USAGE("port=<IP port number>");
#define SERVICE_TYPE_SINK "_pulse-sink._tcp"
#define SERVICE_TYPE_SOURCE "_pulse-source._tcp"
#define SERVICE_TYPE_SERVER "_pulse-server._tcp"
+#define SERVICE_SUBTYPE_SINK_HARDWARE "_hardware._sub."SERVICE_TYPE_SINK
+#define SERVICE_SUBTYPE_SINK_VIRTUAL "_virtual._sub."SERVICE_TYPE_SINK
+#define SERVICE_SUBTYPE_SOURCE_HARDWARE "_hardware._sub."SERVICE_TYPE_SOURCE
+#define SERVICE_SUBTYPE_SOURCE_VIRTUAL "_virtual._sub."SERVICE_TYPE_SOURCE
+#define SERVICE_SUBTYPE_SOURCE_MONITOR "_monitor._sub."SERVICE_TYPE_SOURCE
+#define SERVICE_SUBTYPE_SOURCE_NON_MONITOR "_non-monitor._sub."SERVICE_TYPE_SOURCE
static const char* const valid_modargs[] = {
"port",
NULL
};
+enum service_subtype {
+ SUBTYPE_HARDWARE,
+ SUBTYPE_VIRTUAL,
+ SUBTYPE_MONITOR
+};
+
struct service {
struct userdata *userdata;
AvahiEntryGroup *entry_group;
char *service_name;
- char *name;
- enum { UNPUBLISHED, PUBLISHED_REAL, PUBLISHED_AUTOLOAD } published ;
-
- struct {
- int valid;
- pa_namereg_type_t type;
- uint32_t index;
- } loaded;
-
- struct {
- int valid;
- pa_namereg_type_t type;
- uint32_t index;
- } autoload;
+ pa_object *device;
+ enum service_subtype subtype;
};
struct userdata {
pa_core *core;
+ pa_module *module;
AvahiPoll *avahi_poll;
AvahiClient *client;
+
pa_hashmap *services;
- pa_dynarray *sink_dynarray, *source_dynarray, *autoload_dynarray;
- pa_subscription *subscription;
char *service_name;
AvahiEntryGroup *main_entry_group;
uint16_t port;
+
+ pa_hook_slot *sink_new_slot, *source_new_slot, *sink_unlink_slot, *source_unlink_slot, *sink_changed_slot, *source_changed_slot;
};
-static void get_service_data(struct userdata *u, struct service *s, pa_sample_spec *ret_ss, char **ret_description) {
- assert(u && s && s->loaded.valid && ret_ss && ret_description);
+static void get_service_data(struct service *s, pa_sample_spec *ret_ss, pa_channel_map *ret_map, const char **ret_name, const char **ret_description, enum service_subtype *ret_subtype) {
+ pa_assert(s);
+ pa_assert(ret_ss);
+ pa_assert(ret_description);
+ pa_assert(ret_subtype);
+
+ if (pa_sink_isinstance(s->device)) {
+ pa_sink *sink = PA_SINK(s->device);
- if (s->loaded.type == PA_NAMEREG_SINK) {
- pa_sink *sink = pa_idxset_get_by_index(u->core->sinks, s->loaded.index);
- assert(sink);
*ret_ss = sink->sample_spec;
- *ret_description = sink->description;
- } else if (s->loaded.type == PA_NAMEREG_SOURCE) {
- pa_source *source = pa_idxset_get_by_index(u->core->sources, s->loaded.index);
- assert(source);
+ *ret_map = sink->channel_map;
+ *ret_name = sink->name;
+ *ret_description = pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION));
+ *ret_subtype = sink->flags & PA_SINK_HARDWARE ? SUBTYPE_HARDWARE : SUBTYPE_VIRTUAL;
+
+ } else if (pa_source_isinstance(s->device)) {
+ pa_source *source = PA_SOURCE(s->device);
+
*ret_ss = source->sample_spec;
- *ret_description = source->description;
+ *ret_map = source->channel_map;
+ *ret_name = source->name;
+ *ret_description = pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION));
+ *ret_subtype = source->monitor_of ? SUBTYPE_MONITOR : (source->flags & PA_SOURCE_HARDWARE ? SUBTYPE_HARDWARE : SUBTYPE_VIRTUAL);
+
} else
- assert(0);
+ pa_assert_not_reached();
}
static AvahiStringList* txt_record_server_data(pa_core *c, AvahiStringList *l) {
char s[128];
- assert(c);
+
+ pa_assert(c);
l = avahi_string_list_add_pair(l, "server-version", PACKAGE_NAME" "PACKAGE_VERSION);
l = avahi_string_list_add_pair(l, "user-name", pa_get_user_name(s, sizeof(s)));
@@ -128,332 +142,276 @@ static AvahiStringList* txt_record_server_data(pa_core *c, AvahiStringList *l) {
return l;
}
-static int publish_service(struct userdata *u, struct service *s);
+static int publish_service(struct service *s);
static void service_entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
struct service *s = userdata;
- if (state == AVAHI_ENTRY_GROUP_COLLISION) {
- char *t;
+ pa_assert(s);
+
+ switch (state) {
+
+ case AVAHI_ENTRY_GROUP_ESTABLISHED:
+ pa_log_info("Successfully established service %s.", s->service_name);
+ break;
- t = avahi_alternative_service_name(s->service_name);
- pa_xfree(s->service_name);
- s->service_name = t;
+ case AVAHI_ENTRY_GROUP_COLLISION: {
+ char *t;
- publish_service(s->userdata, s);
+ t = avahi_alternative_service_name(s->service_name);
+ pa_log_info("Name collision, renaming %s to %s.", s->service_name, t);
+ pa_xfree(s->service_name);
+ s->service_name = t;
+
+ publish_service(s);
+ break;
+ }
+
+ case AVAHI_ENTRY_GROUP_FAILURE: {
+ pa_log("Failed to register service: %s", avahi_strerror(avahi_client_errno(avahi_entry_group_get_client(g))));
+
+ avahi_entry_group_free(g);
+ s->entry_group = NULL;
+
+ break;
+ }
+
+ case AVAHI_ENTRY_GROUP_UNCOMMITED:
+ case AVAHI_ENTRY_GROUP_REGISTERING:
+ ;
}
}
-static int publish_service(struct userdata *u, struct service *s) {
+static void service_free(struct service *s);
+
+static int publish_service(struct service *s) {
int r = -1;
AvahiStringList *txt = NULL;
+ const char *description = NULL, *name = NULL;
+ pa_sample_spec ss;
+ pa_channel_map map;
+ char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+ enum service_subtype subtype;
- assert(u);
- assert(s);
+ const char * const subtype_text[] = {
+ [SUBTYPE_HARDWARE] = "hardware",
+ [SUBTYPE_VIRTUAL] = "virtual",
+ [SUBTYPE_MONITOR] = "monitor"
+ };
- if (!u->client || avahi_client_get_state(u->client) != AVAHI_CLIENT_S_RUNNING)
- return 0;
-
- if ((s->published == PUBLISHED_REAL && s->loaded.valid) ||
- (s->published == PUBLISHED_AUTOLOAD && s->autoload.valid && !s->loaded.valid))
+ pa_assert(s);
+
+ if (!s->userdata->client || avahi_client_get_state(s->userdata->client) != AVAHI_CLIENT_S_RUNNING)
return 0;
- if (s->published != UNPUBLISHED) {
- avahi_entry_group_reset(s->entry_group);
- s->published = UNPUBLISHED;
- }
-
- if (s->loaded.valid || s->autoload.valid) {
- pa_namereg_type_t type;
-
- if (!s->entry_group) {
- if (!(s->entry_group = avahi_entry_group_new(u->client, service_entry_group_callback, s))) {
- pa_log("avahi_entry_group_new(): %s", avahi_strerror(avahi_client_errno(u->client)));
- goto finish;
- }
+ if (!s->entry_group) {
+ if (!(s->entry_group = avahi_entry_group_new(s->userdata->client, service_entry_group_callback, s))) {
+ pa_log("avahi_entry_group_new(): %s", avahi_strerror(avahi_client_errno(s->userdata->client)));
+ goto finish;
}
-
- txt = avahi_string_list_add_pair(txt, "device", s->name);
- txt = txt_record_server_data(u->core, txt);
-
- if (s->loaded.valid) {
- char *description;
- pa_sample_spec ss;
-
- get_service_data(u, s, &ss, &description);
-
- txt = avahi_string_list_add_printf(txt, "rate=%u", ss.rate);
- txt = avahi_string_list_add_printf(txt, "channels=%u", ss.channels);
- txt = avahi_string_list_add_pair(txt, "format", pa_sample_format_to_string(ss.format));
- if (description)
- txt = avahi_string_list_add_pair(txt, "description", description);
-
- type = s->loaded.type;
- } else if (s->autoload.valid)
- type = s->autoload.type;
-
- if (avahi_entry_group_add_service_strlst(
+ } else
+ avahi_entry_group_reset(s->entry_group);
+
+ txt = txt_record_server_data(s->userdata->core, txt);
+
+ get_service_data(s, &ss, &map, &name, &description, &subtype);
+ txt = avahi_string_list_add_pair(txt, "device", name);
+ txt = avahi_string_list_add_printf(txt, "rate=%u", ss.rate);
+ txt = avahi_string_list_add_printf(txt, "channels=%u", ss.channels);
+ txt = avahi_string_list_add_pair(txt, "format", pa_sample_format_to_string(ss.format));
+ txt = avahi_string_list_add_pair(txt, "channel_map", pa_channel_map_snprint(cm, sizeof(cm), &map));
+ txt = avahi_string_list_add_pair(txt, "subtype", subtype_text[subtype]);
+
+ if (avahi_entry_group_add_service_strlst(
+ s->entry_group,
+ AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
+ 0,
+ s->service_name,
+ pa_sink_isinstance(s->device) ? SERVICE_TYPE_SINK : SERVICE_TYPE_SOURCE,
+ NULL,
+ NULL,
+ s->userdata->port,
+ txt) < 0) {
+
+ pa_log("avahi_entry_group_add_service_strlst(): %s", avahi_strerror(avahi_client_errno(s->userdata->client)));
+ goto finish;
+ }
+
+ if (avahi_entry_group_add_service_subtype(
+ s->entry_group,
+ AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
+ 0,
+ s->service_name,
+ pa_sink_isinstance(s->device) ? SERVICE_TYPE_SINK : SERVICE_TYPE_SOURCE,
+ NULL,
+ pa_sink_isinstance(s->device) ? (subtype == SUBTYPE_HARDWARE ? SERVICE_SUBTYPE_SINK_HARDWARE : SERVICE_SUBTYPE_SINK_VIRTUAL) :
+ (subtype == SUBTYPE_HARDWARE ? SERVICE_SUBTYPE_SOURCE_HARDWARE : (subtype == SUBTYPE_VIRTUAL ? SERVICE_SUBTYPE_SOURCE_VIRTUAL : SERVICE_SUBTYPE_SOURCE_MONITOR))) < 0) {
+
+ pa_log("avahi_entry_group_add_service_subtype(): %s", avahi_strerror(avahi_client_errno(s->userdata->client)));
+ goto finish;
+ }
+
+ if (pa_source_isinstance(s->device) && subtype != SUBTYPE_MONITOR) {
+ if (avahi_entry_group_add_service_subtype(
s->entry_group,
AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
0,
s->service_name,
- type == PA_NAMEREG_SINK ? SERVICE_TYPE_SINK : SERVICE_TYPE_SOURCE,
- NULL,
+ SERVICE_TYPE_SOURCE,
NULL,
- u->port,
- txt) < 0) {
-
- pa_log("avahi_entry_group_add_service_strlst(): %s", avahi_strerror(avahi_client_errno(u->client)));
- goto finish;
- }
-
- if (avahi_entry_group_commit(s->entry_group) < 0) {
- pa_log("avahi_entry_group_commit(): %s", avahi_strerror(avahi_client_errno(u->client)));
+ SERVICE_SUBTYPE_SOURCE_NON_MONITOR) < 0) {
+
+ pa_log("avahi_entry_group_add_service_subtype(): %s", avahi_strerror(avahi_client_errno(s->userdata->client)));
goto finish;
}
-
- if (s->loaded.valid)
- s->published = PUBLISHED_REAL;
- else if (s->autoload.valid)
- s->published = PUBLISHED_AUTOLOAD;
}
-
+
+ if (avahi_entry_group_commit(s->entry_group) < 0) {
+ pa_log("avahi_entry_group_commit(): %s", avahi_strerror(avahi_client_errno(s->userdata->client)));
+ goto finish;
+ }
+
r = 0;
-
+ pa_log_debug("Successfully created entry group for %s.", s->service_name);
+
finish:
- if (s->published == UNPUBLISHED) {
- /* Remove this service */
+ /* Remove this service */
+ if (r < 0)
+ service_free(s);
- if (s->entry_group)
- avahi_entry_group_free(s->entry_group);
-
- pa_hashmap_remove(u->services, s->name);
- pa_xfree(s->name);
- pa_xfree(s->service_name);
- pa_xfree(s);
- }
+ avahi_string_list_free(txt);
- if (txt)
- avahi_string_list_free(txt);
-
return r;
}
-static struct service *get_service(struct userdata *u, const char *name, const char *description) {
+static struct service *get_service(struct userdata *u, pa_object *device) {
struct service *s;
- char hn[64];
-
- if ((s = pa_hashmap_get(u->services, name)))
+ char hn[64], un[64];
+ const char *n;
+
+ pa_assert(u);
+ pa_object_assert_ref(device);
+
+ if ((s = pa_hashmap_get(u->services, device)))
return s;
-
+
s = pa_xnew(struct service, 1);
s->userdata = u;
s->entry_group = NULL;
- s->published = UNPUBLISHED;
- s->name = pa_xstrdup(name);
- s->loaded.valid = s->autoload.valid = 0;
- s->service_name = pa_sprintf_malloc("%s on %s", description ? description : s->name, pa_get_host_name(hn, sizeof(hn)));
+ s->device = device;
+
+ if (pa_sink_isinstance(device)) {
+ if (!(n = pa_proplist_gets(PA_SINK(device)->proplist, PA_PROP_DEVICE_DESCRIPTION)))
+ n = PA_SINK(device)->name;
+ } else {
+ if (!(n = pa_proplist_gets(PA_SOURCE(device)->proplist, PA_PROP_DEVICE_DESCRIPTION)))
+ n = PA_SOURCE(device)->name;
+ }
- pa_hashmap_put(u->services, s->name, s);
+ s->service_name = pa_truncate_utf8(pa_sprintf_malloc("%s@%s: %s",
+ pa_get_user_name(un, sizeof(un)),
+ pa_get_host_name(hn, sizeof(hn)),
+ n),
+ AVAHI_LABEL_MAX-1);
+
+ pa_hashmap_put(u->services, s->device, s);
return s;
}
-static int publish_sink(struct userdata *u, pa_sink *s) {
- struct service *svc;
- int ret;
- assert(u && s);
-
- svc = get_service(u, s->name, s->description);
- if (svc->loaded.valid)
- return publish_service(u, svc);
+static void service_free(struct service *s) {
+ pa_assert(s);
- svc->loaded.valid = 1;
- svc->loaded.type = PA_NAMEREG_SINK;
- svc->loaded.index = s->index;
+ pa_hashmap_remove(s->userdata->services, s->device);
- if ((ret = publish_service(u, svc)) < 0)
- return ret;
+ if (s->entry_group) {
+ pa_log_debug("Removing entry group for %s.", s->service_name);
+ avahi_entry_group_free(s->entry_group);
+ }
- pa_dynarray_put(u->sink_dynarray, s->index, svc);
- return ret;
+ pa_xfree(s->service_name);
+ pa_xfree(s);
}
-static int publish_source(struct userdata *u, pa_source *s) {
- struct service *svc;
- int ret;
-
- assert(u && s);
-
- svc = get_service(u, s->name, s->description);
- if (svc->loaded.valid)
- return publish_service(u, svc);
+static pa_bool_t shall_ignore(pa_object *o) {
+ pa_object_assert_ref(o);
- svc->loaded.valid = 1;
- svc->loaded.type = PA_NAMEREG_SOURCE;
- svc->loaded.index = s->index;
+ if (pa_sink_isinstance(o))
+ return !!(PA_SINK(o)->flags & PA_SINK_NETWORK);
- pa_dynarray_put(u->source_dynarray, s->index, svc);
-
- if ((ret = publish_service(u, svc)) < 0)
- return ret;
+ if (pa_source_isinstance(o))
+ return PA_SOURCE(o)->monitor_of || (PA_SOURCE(o)->flags & PA_SOURCE_NETWORK);
- pa_dynarray_put(u->sink_dynarray, s->index, svc);
- return ret;
+ pa_assert_not_reached();
}
-static int publish_autoload(struct userdata *u, pa_autoload_entry *s) {
- struct service *svc;
- int ret;
-
- assert(u && s);
-
- svc = get_service(u, s->name, NULL);
- if (svc->autoload.valid)
- return publish_service(u, svc);
-
- svc->autoload.valid = 1;
- svc->autoload.type = s->type;
- svc->autoload.index = s->index;
-
- if ((ret = publish_service(u, svc)) < 0)
- return ret;
-
- pa_dynarray_put(u->autoload_dynarray, s->index, svc);
- return ret;
-}
-
-static int remove_sink(struct userdata *u, uint32_t idx) {
- struct service *svc;
- assert(u && idx != PA_INVALID_INDEX);
+static pa_hook_result_t device_new_or_changed_cb(pa_core *c, pa_object *o, struct userdata *u) {
+ pa_assert(c);
+ pa_object_assert_ref(o);
- if (!(svc = pa_dynarray_get(u->sink_dynarray, idx)))
- return 0;
+ if (!shall_ignore(o))
+ publish_service(get_service(u, o));
- if (!svc->loaded.valid || svc->loaded.type != PA_NAMEREG_SINK)
- return 0;
-
- svc->loaded.valid = 0;
- pa_dynarray_put(u->sink_dynarray, idx, NULL);
-
- return publish_service(u, svc);
+ return PA_HOOK_OK;
}
-static int remove_source(struct userdata *u, uint32_t idx) {
- struct service *svc;
- assert(u && idx != PA_INVALID_INDEX);
-
- if (!(svc = pa_dynarray_get(u->source_dynarray, idx)))
- return 0;
+static pa_hook_result_t device_unlink_cb(pa_core *c, pa_object *o, struct userdata *u) {
+ struct service *s;
- if (!svc->loaded.valid || svc->loaded.type != PA_NAMEREG_SOURCE)
- return 0;
+ pa_assert(c);
+ pa_object_assert_ref(o);
- svc->loaded.valid = 0;
- pa_dynarray_put(u->source_dynarray, idx, NULL);
+ if ((s = pa_hashmap_get(u->services, o)))
+ service_free(s);
- return publish_service(u, svc);
+ return PA_HOOK_OK;
}
-static int remove_autoload(struct userdata *u, uint32_t idx) {
- struct service *svc;
- assert(u && idx != PA_INVALID_INDEX);
-
- if (!(svc = pa_dynarray_get(u->autoload_dynarray, idx)))
- return 0;
-
- if (!svc->autoload.valid)
- return 0;
-
- svc->autoload.valid = 0;
- pa_dynarray_put(u->autoload_dynarray, idx, NULL);
-
- return publish_service(u, svc);
-}
+static int publish_main_service(struct userdata *u);
-static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
+static void main_entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
struct userdata *u = userdata;
- assert(u && c);
+ pa_assert(u);
- switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK)
- case PA_SUBSCRIPTION_EVENT_SINK: {
- if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
- pa_sink *sink;
+ switch (state) {
- if ((sink = pa_idxset_get_by_index(c->sinks, idx))) {
- if (publish_sink(u, sink) < 0)
- goto fail;
- }
- } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
- if (remove_sink(u, idx) < 0)
- goto fail;
- }
-
+ case AVAHI_ENTRY_GROUP_ESTABLISHED:
+ pa_log_info("Successfully established main service.");
break;
- case PA_SUBSCRIPTION_EVENT_SOURCE:
+ case AVAHI_ENTRY_GROUP_COLLISION: {
+ char *t;
- if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
- pa_source *source;
-
- if ((source = pa_idxset_get_by_index(c->sources, idx))) {
- if (publish_source(u, source) < 0)
- goto fail;
- }
- } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
- if (remove_source(u, idx) < 0)
- goto fail;
- }
-
- break;
+ t = avahi_alternative_service_name(u->service_name);
+ pa_log_info("Name collision: renaming main service %s to %s.", u->service_name, t);
+ pa_xfree(u->service_name);
+ u->service_name = t;
- case PA_SUBSCRIPTION_EVENT_AUTOLOAD:
- if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
- pa_autoload_entry *autoload;
-
- if ((autoload = pa_idxset_get_by_index(c->autoload_idxset, idx))) {
- if (publish_autoload(u, autoload) < 0)
- goto fail;
- }
- } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
- if (remove_autoload(u, idx) < 0)
- goto fail;
- }
-
+ publish_main_service(u);
break;
- }
-
- return;
-
-fail:
- if (u->subscription) {
- pa_subscription_free(u->subscription);
- u->subscription = NULL;
- }
-}
-
-static int publish_main_service(struct userdata *u);
-
-static void main_entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
- struct userdata *u = userdata;
- assert(u);
+ }
- if (state == AVAHI_ENTRY_GROUP_COLLISION) {
- char *t;
+ case AVAHI_ENTRY_GROUP_FAILURE: {
+ pa_log("Failed to register main service: %s", avahi_strerror(avahi_client_errno(avahi_entry_group_get_client(g))));
- t = avahi_alternative_service_name(u->service_name);
- pa_xfree(u->service_name);
- u->service_name = t;
+ avahi_entry_group_free(g);
+ u->main_entry_group = NULL;
+ break;
+ }
- publish_main_service(u);
+ case AVAHI_ENTRY_GROUP_UNCOMMITED:
+ case AVAHI_ENTRY_GROUP_REGISTERING:
+ break;
}
}
static int publish_main_service(struct userdata *u) {
AvahiStringList *txt = NULL;
int r = -1;
-
+
+ pa_assert(u);
+
if (!u->main_entry_group) {
if (!(u->main_entry_group = avahi_entry_group_new(u->client, main_entry_group_callback, u))) {
pa_log("avahi_entry_group_new() failed: %s", avahi_strerror(avahi_client_errno(u->client)));
@@ -461,8 +419,8 @@ static int publish_main_service(struct userdata *u) {
}
} else
avahi_entry_group_reset(u->main_entry_group);
-
- txt = txt_record_server_data(u->core, NULL);
+
+ txt = txt_record_server_data(u->core, txt);
if (avahi_entry_group_add_service_strlst(
u->main_entry_group,
@@ -474,18 +432,18 @@ static int publish_main_service(struct userdata *u) {
NULL,
u->port,
txt) < 0) {
-
+
pa_log("avahi_entry_group_add_service_strlst() failed: %s", avahi_strerror(avahi_client_errno(u->client)));
goto fail;
}
-
+
if (avahi_entry_group_commit(u->main_entry_group) < 0) {
pa_log("avahi_entry_group_commit() failed: %s", avahi_strerror(avahi_client_errno(u->client)));
goto fail;
}
r = 0;
-
+
fail:
avahi_string_list_free(txt);
@@ -495,197 +453,196 @@ fail:
static int publish_all_services(struct userdata *u) {
pa_sink *sink;
pa_source *source;
- pa_autoload_entry *autoload;
int r = -1;
uint32_t idx;
-
- assert(u);
- pa_log_debug("Publishing services in Zeroconf");
+ pa_assert(u);
- for (sink = pa_idxset_first(u->core->sinks, &idx); sink; sink = pa_idxset_next(u->core->sinks, &idx))
- if (publish_sink(u, sink) < 0)
- goto fail;
+ pa_log_debug("Publishing services in Zeroconf");
- for (source = pa_idxset_first(u->core->sources, &idx); source; source = pa_idxset_next(u->core->sources, &idx))
- if (publish_source(u, source) < 0)
- goto fail;
+ for (sink = PA_SINK(pa_idxset_first(u->core->sinks, &idx)); sink; sink = PA_SINK(pa_idxset_next(u->core->sinks, &idx)))
+ if (!shall_ignore(PA_OBJECT(sink)))
+ publish_service(get_service(u, PA_OBJECT(sink)));
- if (u->core->autoload_idxset)
- for (autoload = pa_idxset_first(u->core->autoload_idxset, &idx); autoload; autoload = pa_idxset_next(u->core->autoload_idxset, &idx))
- if (publish_autoload(u, autoload) < 0)
- goto fail;
+ for (source = PA_SOURCE(pa_idxset_first(u->core->sources, &idx)); source; source = PA_SOURCE(pa_idxset_next(u->core->sources, &idx)))
+ if (!shall_ignore(PA_OBJECT(source)))
+ publish_service(get_service(u, PA_OBJECT(source)));
if (publish_main_service(u) < 0)
goto fail;
-
+
r = 0;
-
+
fail:
return r;
}
-static void unpublish_all_services(struct userdata *u, int rem) {
+static void unpublish_all_services(struct userdata *u, pa_bool_t rem) {
void *state = NULL;
struct service *s;
-
- assert(u);
+
+ pa_assert(u);
pa_log_debug("Unpublishing services in Zeroconf");
while ((s = pa_hashmap_iterate(u->services, &state, NULL))) {
if (s->entry_group) {
if (rem) {
+ pa_log_debug("Removing entry group for %s.", s->service_name);
avahi_entry_group_free(s->entry_group);
s->entry_group = NULL;
- } else
+ } else {
avahi_entry_group_reset(s->entry_group);
+ pa_log_debug("Resetting entry group for %s.", s->service_name);
+ }
}
-
- s->published = UNPUBLISHED;
}
if (u->main_entry_group) {
if (rem) {
+ pa_log_debug("Removing main entry group.");
avahi_entry_group_free(u->main_entry_group);
u->main_entry_group = NULL;
- } else
+ } else {
avahi_entry_group_reset(u->main_entry_group);
+ pa_log_debug("Resetting main entry group.");
+ }
}
}
static void client_callback(AvahiClient *c, AvahiClientState state, void *userdata) {
struct userdata *u = userdata;
- assert(c);
+
+ pa_assert(c);
+ pa_assert(u);
u->client = c;
-
+
switch (state) {
case AVAHI_CLIENT_S_RUNNING:
publish_all_services(u);
break;
-
+
case AVAHI_CLIENT_S_COLLISION:
- unpublish_all_services(u, 0);
+ pa_log_debug("Host name collision");
+ unpublish_all_services(u, FALSE);
break;
case AVAHI_CLIENT_FAILURE:
if (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED) {
int error;
- unpublish_all_services(u, 1);
+
+ pa_log_debug("Avahi daemon disconnected.");
+
+ unpublish_all_services(u, TRUE);
avahi_client_free(u->client);
- if (!(u->client = avahi_client_new(u->avahi_poll, AVAHI_CLIENT_NO_FAIL, client_callback, u, &error)))
- pa_log("pa_avahi_client_new() failed: %s", avahi_strerror(error));
+ if (!(u->client = avahi_client_new(u->avahi_poll, AVAHI_CLIENT_NO_FAIL, client_callback, u, &error))) {
+ pa_log("avahi_client_new() failed: %s", avahi_strerror(error));
+ pa_module_unload_request(u->module);
+ }
}
-
+
break;
default: ;
}
}
-int pa__init(pa_core *c, pa_module*m) {
+int pa__init(pa_module*m) {
+
struct userdata *u;
uint32_t port = PA_NATIVE_DEFAULT_PORT;
pa_modargs *ma = NULL;
- char hn[256];
+ char hn[256], un[256];
int error;
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_u32(ma, "port", &port) < 0 || port == 0 || port >= 0xFFFF) {
- pa_log("invalid port specified.");
+ if (pa_modargs_get_value_u32(ma, "port", &port) < 0 || port <= 0 || port > 0xFFFF) {
+ pa_log("Invalid port specified.");
goto fail;
}
m->userdata = u = pa_xnew(struct userdata, 1);
- u->core = c;
+ u->core = m->core;
+ u->module = m;
u->port = (uint16_t) port;
- u->avahi_poll = pa_avahi_poll_new(c->mainloop);
-
- u->services = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
- u->sink_dynarray = pa_dynarray_new();
- u->source_dynarray = pa_dynarray_new();
- u->autoload_dynarray = pa_dynarray_new();
+ u->avahi_poll = pa_avahi_poll_new(m->core->mainloop);
+
+ u->services = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
- u->subscription = pa_subscription_new(c,
- PA_SUBSCRIPTION_MASK_SINK|
- PA_SUBSCRIPTION_MASK_SOURCE|
- PA_SUBSCRIPTION_MASK_AUTOLOAD, subscribe_callback, u);
+ u->sink_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE, (pa_hook_cb_t) device_new_or_changed_cb, u);
+ u->sink_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], PA_HOOK_LATE, (pa_hook_cb_t) device_new_or_changed_cb, u);
+ u->sink_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) device_unlink_cb, u);
+ u->source_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_LATE, (pa_hook_cb_t) device_new_or_changed_cb, u);
+ u->source_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED], PA_HOOK_LATE, (pa_hook_cb_t) device_new_or_changed_cb, u);
+ u->source_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) device_unlink_cb, u);
u->main_entry_group = NULL;
- u->service_name = pa_xstrdup(pa_get_host_name(hn, sizeof(hn)));
+ u->service_name = pa_truncate_utf8(pa_sprintf_malloc("%s@%s", pa_get_user_name(un, sizeof(un)), pa_get_host_name(hn, sizeof(hn))), AVAHI_LABEL_MAX);
if (!(u->client = avahi_client_new(u->avahi_poll, AVAHI_CLIENT_NO_FAIL, client_callback, u, &error))) {
- pa_log("pa_avahi_client_new() failed: %s", avahi_strerror(error));
+ pa_log("avahi_client_new() failed: %s", avahi_strerror(error));
goto fail;
}
pa_modargs_free(ma);
-
+
return 0;
-
+
fail:
- pa__done(c, m);
+ pa__done(m);
if (ma)
pa_modargs_free(ma);
-
- return -1;
-}
-
-static void service_free(void *p, void *userdata) {
- struct service *s = p;
- struct userdata *u = userdata;
-
- assert(s);
- assert(u);
- if (s->entry_group)
- avahi_entry_group_free(s->entry_group);
-
- pa_xfree(s->service_name);
- pa_xfree(s->name);
- pa_xfree(s);
+ 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->services)
- pa_hashmap_free(u->services, service_free, u);
+ if (u->services) {
+ struct service *s;
- if (u->subscription)
- pa_subscription_free(u->subscription);
+ while ((s = pa_hashmap_get_first(u->services)))
+ service_free(s);
- if (u->sink_dynarray)
- pa_dynarray_free(u->sink_dynarray, NULL, NULL);
- if (u->source_dynarray)
- pa_dynarray_free(u->source_dynarray, NULL, NULL);
- if (u->autoload_dynarray)
- pa_dynarray_free(u->autoload_dynarray, NULL, NULL);
-
+ pa_hashmap_free(u->services, NULL, NULL);
+ }
+
+ if (u->sink_new_slot)
+ pa_hook_slot_free(u->sink_new_slot);
+ if (u->source_new_slot)
+ pa_hook_slot_free(u->source_new_slot);
+ if (u->sink_changed_slot)
+ pa_hook_slot_free(u->sink_changed_slot);
+ if (u->source_changed_slot)
+ pa_hook_slot_free(u->source_changed_slot);
+ if (u->sink_unlink_slot)
+ pa_hook_slot_free(u->sink_unlink_slot);
+ if (u->source_unlink_slot)
+ pa_hook_slot_free(u->source_unlink_slot);
if (u->main_entry_group)
avahi_entry_group_free(u->main_entry_group);
-
+
if (u->client)
avahi_client_free(u->client);
-
+
if (u->avahi_poll)
pa_avahi_poll_free(u->avahi_poll);
pa_xfree(u->service_name);
pa_xfree(u);
}
-
diff --git a/src/modules/oss-util.c b/src/modules/oss-util.c
index 0aaf6971..2791e165 100644
--- a/src/modules/oss-util.c
+++ b/src/modules/oss-util.c
@@ -1,18 +1,19 @@
-/* $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
@@ -23,7 +24,6 @@
#include <config.h>
#endif
-#include <assert.h>
#include <sys/soundcard.h>
#include <sys/ioctl.h>
#include <stdio.h>
@@ -34,9 +34,11 @@
#include <sys/stat.h>
#include <fcntl.h>
+#include <pulse/xmalloc.h>
#include <pulsecore/core-error.h>
#include <pulsecore/core-util.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
#include "oss-util.h"
@@ -44,63 +46,62 @@ int pa_oss_open(const char *device, int *mode, int* pcaps) {
int fd = -1;
int caps;
- assert(device && mode && (*mode == O_RDWR || *mode == O_RDONLY || *mode == O_WRONLY));
+ pa_assert(device);
+ pa_assert(mode);
+ pa_assert(*mode == O_RDWR || *mode == O_RDONLY || *mode == O_WRONLY);
if(!pcaps)
pcaps = &caps;
-
+
if (*mode == O_RDWR) {
- if ((fd = open(device, O_RDWR|O_NDELAY)) >= 0) {
- int dcaps, *tcaps;
+ if ((fd = open(device, O_RDWR|O_NDELAY|O_NOCTTY)) >= 0) {
ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0);
- tcaps = pcaps ? pcaps : &dcaps;
-
- if (ioctl(fd, SNDCTL_DSP_GETCAPS, tcaps) < 0) {
+ if (ioctl(fd, SNDCTL_DSP_GETCAPS, pcaps) < 0) {
pa_log("SNDCTL_DSP_GETCAPS: %s", pa_cstrerror(errno));
goto fail;
}
- if (*tcaps & DSP_CAP_DUPLEX)
+ if (*pcaps & DSP_CAP_DUPLEX)
goto success;
pa_log_warn("'%s' doesn't support full duplex", device);
- close(fd);
+ pa_close(fd);
}
-
- if ((fd = open(device, (*mode = O_WRONLY)|O_NDELAY)) < 0) {
- if ((fd = open(device, (*mode = O_RDONLY)|O_NDELAY)) < 0) {
+
+ if ((fd = open(device, (*mode = O_WRONLY)|O_NDELAY|O_NOCTTY)) < 0) {
+ if ((fd = open(device, (*mode = O_RDONLY)|O_NDELAY|O_NOCTTY)) < 0) {
pa_log("open('%s'): %s", device, pa_cstrerror(errno));
goto fail;
}
}
} else {
- if ((fd = open(device, *mode|O_NDELAY)) < 0) {
+ if ((fd = open(device, *mode|O_NDELAY|O_NOCTTY)) < 0) {
pa_log("open('%s'): %s", device, pa_cstrerror(errno));
goto fail;
}
- }
-
-success:
+ }
*pcaps = 0;
-
+
if (ioctl(fd, SNDCTL_DSP_GETCAPS, pcaps) < 0) {
pa_log("SNDCTL_DSP_GETCAPS: %s", pa_cstrerror(errno));
goto fail;
}
-
+
+success:
+
pa_log_debug("capabilities:%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
*pcaps & DSP_CAP_BATCH ? " BATCH" : "",
#ifdef DSP_CAP_BIND
*pcaps & DSP_CAP_BIND ? " BIND" : "",
#else
- "",
+ "",
#endif
*pcaps & DSP_CAP_COPROC ? " COPROC" : "",
*pcaps & DSP_CAP_DUPLEX ? " DUPLEX" : "",
-#ifdef DSP_CAP_FREERATE
+#ifdef DSP_CAP_FREERATE
*pcaps & DSP_CAP_FREERATE ? " FREERATE" : "",
#else
"",
@@ -119,7 +120,7 @@ success:
#ifdef DSP_CAP_MULTI
*pcaps & DSP_CAP_MULTI ? " MULTI" : "",
#else
- "",
+ "",
#endif
#ifdef DSP_CAP_OUTPUT
*pcaps & DSP_CAP_OUTPUT ? " OUTPUT" : "",
@@ -139,20 +140,20 @@ success:
#endif
*pcaps & DSP_CAP_TRIGGER ? " TRIGGER" : "");
- pa_fd_set_cloexec(fd, 1);
-
+ pa_make_fd_cloexec(fd);
+
return fd;
fail:
if (fd >= 0)
- close(fd);
+ pa_close(fd);
return -1;
}
int pa_oss_auto_format(int fd, pa_sample_spec *ss) {
int format, channels, speed, reqformat;
pa_sample_format_t orig_format;
-
+
static const int format_trans[PA_SAMPLE_MAX] = {
[PA_SAMPLE_U8] = AFMT_U8,
[PA_SAMPLE_ALAW] = AFMT_A_LAW,
@@ -161,12 +162,15 @@ int pa_oss_auto_format(int fd, pa_sample_spec *ss) {
[PA_SAMPLE_S16BE] = AFMT_S16_BE,
[PA_SAMPLE_FLOAT32LE] = AFMT_QUERY, /* not supported */
[PA_SAMPLE_FLOAT32BE] = AFMT_QUERY, /* not supported */
+ [PA_SAMPLE_S32LE] = AFMT_QUERY, /* not supported */
+ [PA_SAMPLE_S32BE] = AFMT_QUERY, /* not supported */
};
- assert(fd >= 0 && ss);
+ pa_assert(fd >= 0);
+ pa_assert(ss);
orig_format = ss->format;
-
+
reqformat = format = format_trans[ss->format];
if (reqformat == AFMT_QUERY || ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != reqformat) {
format = AFMT_S16_NE;
@@ -190,13 +194,13 @@ int pa_oss_auto_format(int fd, pa_sample_spec *ss) {
pa_log_warn("device doesn't support sample format %s, changed to %s.",
pa_sample_format_to_string(orig_format),
pa_sample_format_to_string(ss->format));
-
+
channels = ss->channels;
if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) < 0) {
pa_log("SNDCTL_DSP_CHANNELS: %s", pa_cstrerror(errno));
return -1;
}
- assert(channels > 0);
+ pa_assert(channels > 0);
if (ss->channels != channels) {
pa_log_warn("device doesn't support %i channels, using %i channels.", ss->channels, channels);
@@ -208,7 +212,7 @@ int pa_oss_auto_format(int fd, pa_sample_spec *ss) {
pa_log("SNDCTL_DSP_SPEED: %s", pa_cstrerror(errno));
return -1;
}
- assert(speed > 0);
+ pa_assert(speed > 0);
if (ss->rate != (unsigned) speed) {
pa_log_warn("device doesn't support %i Hz, changed to %i Hz.", ss->rate, speed);
@@ -229,14 +233,14 @@ static int simple_log2(int v) {
if (!v) break;
k++;
}
-
+
return k;
}
int pa_oss_set_fragments(int fd, int nfrags, int frag_size) {
int arg;
arg = ((int) nfrags << 16) | simple_log2(frag_size);
-
+
if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &arg) < 0) {
pa_log("SNDCTL_DSP_SETFRAGMENT: %s", pa_cstrerror(errno));
return -1;
@@ -245,27 +249,29 @@ int pa_oss_set_fragments(int fd, int nfrags, int frag_size) {
return 0;
}
-static int pa_oss_get_volume(int fd, int mixer, const pa_sample_spec *ss, pa_cvolume *volume) {
+int pa_oss_get_volume(int fd, unsigned long mixer, const pa_sample_spec *ss, pa_cvolume *volume) {
char cv[PA_CVOLUME_SNPRINT_MAX];
unsigned vol;
- assert(fd >= 0);
- assert(ss);
- assert(volume);
-
+ pa_assert(fd >= 0);
+ pa_assert(ss);
+ pa_assert(volume);
+
if (ioctl(fd, mixer, &vol) < 0)
return -1;
+ pa_cvolume_reset(volume, ss->channels);
+
volume->values[0] = ((vol & 0xFF) * PA_VOLUME_NORM) / 100;
- if ((volume->channels = ss->channels) >= 2)
+ if (volume->channels >= 2)
volume->values[1] = (((vol >> 8) & 0xFF) * PA_VOLUME_NORM) / 100;
pa_log_debug("Read mixer settings: %s", pa_cvolume_snprint(cv, sizeof(cv), volume));
return 0;
}
-static int pa_oss_set_volume(int fd, int mixer, const pa_sample_spec *ss, const pa_cvolume *volume) {
+int pa_oss_set_volume(int fd, unsigned long mixer, const pa_sample_spec *ss, const pa_cvolume *volume) {
char cv[PA_CVOLUME_SNPRINT_MAX];
unsigned vol;
pa_volume_t l, r;
@@ -278,7 +284,7 @@ static int pa_oss_set_volume(int fd, int mixer, const pa_sample_spec *ss, const
r = volume->values[1] > PA_VOLUME_NORM ? PA_VOLUME_NORM : volume->values[1];
vol |= ((r*100)/PA_VOLUME_NORM) << 8;
}
-
+
if (ioctl(fd, mixer, &vol) < 0)
return -1;
@@ -286,42 +292,50 @@ static int pa_oss_set_volume(int fd, int mixer, const pa_sample_spec *ss, const
return 0;
}
-int pa_oss_get_pcm_volume(int fd, const pa_sample_spec *ss, pa_cvolume *volume) {
- return pa_oss_get_volume(fd, SOUND_MIXER_READ_PCM, ss, volume);
-}
+static int get_device_number(const char *dev) {
+ const char *p, *e;
+ char *rp = NULL;
+ int r;
-int pa_oss_set_pcm_volume(int fd, const pa_sample_spec *ss, const pa_cvolume *volume) {
- return pa_oss_set_volume(fd, SOUND_MIXER_WRITE_PCM, ss, volume);
-}
+ if (!(p = rp = pa_readlink(dev))) {
+ if (errno != EINVAL && errno != ENOLINK) {
+ r = -1;
+ goto finish;
+ }
-int pa_oss_get_input_volume(int fd, const pa_sample_spec *ss, pa_cvolume *volume) {
- return pa_oss_get_volume(fd, SOUND_MIXER_READ_IGAIN, ss, volume);
-}
+ p = dev;
+ }
+
+ if ((e = strrchr(p, '/')))
+ p = e+1;
+
+ if (p == 0) {
+ r = 0;
+ goto finish;
+ }
+
+ p = strchr(p, 0) -1;
+
+ if (*p >= '0' && *p <= '9') {
+ r = *p - '0';
+ goto finish;
+ }
+
+ r = -1;
-int pa_oss_set_input_volume(int fd, const pa_sample_spec *ss, const pa_cvolume *volume) {
- return pa_oss_set_volume(fd, SOUND_MIXER_WRITE_IGAIN, ss, volume);
+finish:
+ pa_xfree(rp);
+ return r;
}
int pa_oss_get_hw_description(const char *dev, char *name, size_t l) {
FILE *f;
- const char *e = NULL;
int n, r = -1;
int b = 0;
- if (strncmp(dev, "/dev/dsp", 8) == 0)
- e = dev+8;
- else if (strncmp(dev, "/dev/adsp", 9) == 0)
- e = dev+9;
- else
+ if ((n = get_device_number(dev)) < 0)
return -1;
- if (*e == 0)
- n = 0;
- else if (*e >= '0' && *e <= '9' && *(e+1) == 0)
- n = *e - '0';
- else
- return -1;
-
if (!(f = fopen("/dev/sndstat", "r")) &&
!(f = fopen("/proc/sndstat", "r")) &&
!(f = fopen("/proc/asound/oss/sndstat", "r"))) {
@@ -335,7 +349,7 @@ int pa_oss_get_hw_description(const char *dev, char *name, size_t l) {
while (!feof(f)) {
char line[64];
int device;
-
+
if (!fgets(line, sizeof(line), f))
break;
@@ -348,19 +362,19 @@ int pa_oss_get_hw_description(const char *dev, char *name, size_t l) {
if (line[0] == 0)
break;
-
+
if (sscanf(line, "%i: ", &device) != 1)
continue;
if (device == n) {
char *k = strchr(line, ':');
- assert(k);
+ pa_assert(k);
k++;
k += strspn(k, " ");
if (pa_endswith(k, " (DUPLEX)"))
k[strlen(k)-9] = 0;
-
+
pa_strlcpy(name, k, l);
r = 0;
break;
@@ -370,3 +384,34 @@ int pa_oss_get_hw_description(const char *dev, char *name, size_t l) {
fclose(f);
return r;
}
+
+static int open_mixer(const char *mixer) {
+ int fd;
+
+ if ((fd = open(mixer, O_RDWR|O_NDELAY|O_NOCTTY)) >= 0)
+ return fd;
+
+ return -1;
+}
+
+int pa_oss_open_mixer_for_device(const char *device) {
+ int n;
+ char *fn;
+ int fd;
+
+ if ((n = get_device_number(device)) < 0)
+ return -1;
+
+ if (n == 0)
+ if ((fd = open_mixer("/dev/mixer")) >= 0)
+ return fd;
+
+ fn = pa_sprintf_malloc("/dev/mixer%i", n);
+ fd = open_mixer(fn);
+ pa_xfree(fn);
+
+ if (fd < 0)
+ pa_log_warn("Failed to open mixer '%s': %s", device, pa_cstrerror(errno));
+
+ return fd;
+}
diff --git a/src/modules/oss-util.h b/src/modules/oss-util.h
index 12855f4e..654f7bba 100644
--- a/src/modules/oss-util.h
+++ b/src/modules/oss-util.h
@@ -1,21 +1,22 @@
#ifndef fooossutilhfoo
#define fooossutilhfoo
-/* $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
@@ -30,12 +31,11 @@ int pa_oss_auto_format(int fd, pa_sample_spec *ss);
int pa_oss_set_fragments(int fd, int frags, int frag_size);
-int pa_oss_get_pcm_volume(int fd, const pa_sample_spec *ss, pa_cvolume *volume);
-int pa_oss_set_pcm_volume(int fd, const pa_sample_spec *ss, const pa_cvolume *volume);
-
-int pa_oss_get_input_volume(int fd, const pa_sample_spec *ss, pa_cvolume *volume);
-int pa_oss_set_input_volume(int fd, const pa_sample_spec *ss, const pa_cvolume *volume);
+int pa_oss_set_volume(int fd, unsigned long mixer, const pa_sample_spec *ss, const pa_cvolume *volume);
+int pa_oss_get_volume(int fd, unsigned long mixer, const pa_sample_spec *ss, pa_cvolume *volume);
int pa_oss_get_hw_description(const char *dev, char *name, size_t l);
+int pa_oss_open_mixer_for_device(const char *device);
+
#endif
diff --git a/src/modules/rtp/module-rtp-recv.c b/src/modules/rtp/module-rtp-recv.c
index 338d57cf..cff5cf8b 100644
--- a/src/modules/rtp/module-rtp-recv.c
+++ b/src/modules/rtp/module-rtp-recv.c
@@ -1,17 +1,19 @@
/***
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
@@ -22,7 +24,6 @@
#include <config.h>
#endif
-#include <assert.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
@@ -30,6 +31,7 @@
#include <errno.h>
#include <string.h>
#include <unistd.h>
+#include <poll.h>
#include <pulse/timeval.h>
#include <pulse/xmalloc.h>
@@ -45,6 +47,11 @@
#include <pulsecore/modargs.h>
#include <pulsecore/namereg.h>
#include <pulsecore/sample-util.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/atomic.h>
+#include <pulsecore/rtclock.h>
+#include <pulsecore/atomic.h>
+#include <pulsecore/time-smoother.h>
#include "module-rtp-recv-symdef.h"
@@ -52,19 +59,22 @@
#include "sdp.h"
#include "sap.h"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("Recieve data from a network via RTP/SAP/SDP")
-PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Recieve data from a network via RTP/SAP/SDP");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
PA_MODULE_USAGE(
"sink=<name of the sink> "
"sap_address=<multicast address to listen on> "
-)
+);
#define SAP_PORT 9875
#define DEFAULT_SAP_ADDRESS "224.0.0.56"
-#define MEMBLOCKQ_MAXLENGTH (1024*170)
+#define MEMBLOCKQ_MAXLENGTH (1024*1024*40)
#define MAX_SESSIONS 16
-#define DEATH_TIMEOUT 20000000
+#define DEATH_TIMEOUT 20
+#define RATE_UPDATE_INTERVAL (5*PA_USEC_PER_SEC)
+#define LATENCY_USEC (500*PA_USEC_PER_MSEC)
static const char* const valid_modargs[] = {
"sink",
@@ -74,102 +84,148 @@ static const char* const valid_modargs[] = {
struct session {
struct userdata *userdata;
+ PA_LLIST_FIELDS(struct session);
pa_sink_input *sink_input;
pa_memblockq *memblockq;
- pa_time_event *death_event;
-
- int first_packet;
+ pa_bool_t first_packet;
uint32_t ssrc;
uint32_t offset;
struct pa_sdp_info sdp_info;
pa_rtp_context rtp_context;
- pa_io_event* rtp_event;
+
+ pa_rtpoll_item *rtpoll_item;
+
+ pa_atomic_t timestamp;
+
+ pa_smoother *smoother;
+ pa_usec_t intended_latency;
+ pa_usec_t sink_latency;
+
+ pa_usec_t last_rate_update;
};
struct userdata {
pa_module *module;
- pa_core *core;
pa_sap_context sap_context;
pa_io_event* sap_event;
- pa_hashmap *by_origin;
+ pa_time_event *check_death_event;
char *sink_name;
+ PA_LLIST_HEAD(struct session, sessions);
+ pa_hashmap *by_origin;
int n_sessions;
};
-static void session_free(struct session *s, int from_hash);
+static void session_free(struct session *s);
+
+/* 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 session *s = PA_SINK_INPUT(o)->userdata;
-static int sink_input_peek(pa_sink_input *i, pa_memchunk *chunk) {
+ switch (code) {
+ case PA_SINK_INPUT_MESSAGE_GET_LATENCY:
+ *((pa_usec_t*) data) = pa_bytes_to_usec(pa_memblockq_get_length(s->memblockq), &s->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_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
struct session *s;
- assert(i);
- s = i->userdata;
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(s = i->userdata);
+
+ if (pa_memblockq_peek(s->memblockq, chunk) < 0)
+ return -1;
- return pa_memblockq_peek(s->memblockq, chunk);
+ pa_memblockq_drop(s->memblockq, chunk->length);
+
+ return 0;
}
-static void sink_input_drop(pa_sink_input *i, const pa_memchunk *chunk, size_t length) {
+/* Called from I/O thread context */
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
struct session *s;
- assert(i);
- s = i->userdata;
- pa_memblockq_drop(s->memblockq, chunk, length);
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(s = i->userdata);
+
+ pa_memblockq_rewind(s->memblockq, nbytes);
}
-static void sink_input_kill(pa_sink_input* i) {
+/* Called from thread context */
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
struct session *s;
- assert(i);
- s = i->userdata;
- session_free(s, 1);
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(s = i->userdata);
+
+ pa_memblockq_set_maxrewind(s->memblockq, nbytes);
}
-static pa_usec_t sink_input_get_latency(pa_sink_input *i) {
+/* Called from main context */
+static void sink_input_kill(pa_sink_input* i) {
struct session *s;
- assert(i);
- s = i->userdata;
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(s = i->userdata);
- return pa_bytes_to_usec(pa_memblockq_get_length(s->memblockq), &i->sample_spec);
+ session_free(s);
}
-static void rtp_event_cb(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event_flags_t flags, void *userdata) {
- struct session *s = userdata;
+/* Called from I/O thread context */
+static int rtpoll_work_cb(pa_rtpoll_item *i) {
pa_memchunk chunk;
int64_t k, j, delta;
- struct timeval tv;
-
- assert(m);
- assert(e);
- assert(s);
- assert(fd == s->rtp_context.fd);
- assert(flags == PA_IO_EVENT_INPUT);
-
- if (pa_rtp_recv(&s->rtp_context, &chunk, s->userdata->core->mempool) < 0)
- return;
+ struct timeval now;
+ struct session *s;
+ struct pollfd *p;
+
+ pa_assert_se(s = pa_rtpoll_item_get_userdata(i));
+
+ p = pa_rtpoll_item_get_pollfd(i, NULL);
+
+ if (p->revents & (POLLERR|POLLNVAL|POLLHUP|POLLOUT)) {
+ pa_log("poll() signalled bad revents.");
+ return -1;
+ }
+
+ if ((p->revents & POLLIN) == 0)
+ return 0;
+
+ p->revents = 0;
+
+ if (pa_rtp_recv(&s->rtp_context, &chunk, s->userdata->module->core->mempool) < 0)
+ return 0;
if (s->sdp_info.payload != s->rtp_context.payload) {
pa_memblock_unref(chunk.memblock);
- return;
+ return 0;
}
-
+
if (!s->first_packet) {
- s->first_packet = 1;
+ s->first_packet = TRUE;
s->ssrc = s->rtp_context.ssrc;
s->offset = s->rtp_context.timestamp;
- if (s->ssrc == s->userdata->core->cookie)
- pa_log_warn("WARNING! Detected RTP packet loop!");
+ if (s->ssrc == s->userdata->module->core->cookie)
+ pa_log_warn("Detected RTP packet loop!");
} else {
if (s->ssrc != s->rtp_context.ssrc) {
pa_memblock_unref(chunk.memblock);
- return;
+ return 0;
}
}
@@ -181,40 +237,125 @@ static void rtp_event_cb(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event
delta = k;
else
delta = j;
-
+
pa_memblockq_seek(s->memblockq, delta * s->rtp_context.frame_size, PA_SEEK_RELATIVE);
+ pa_rtclock_get(&now);
+
+ pa_smoother_put(s->smoother, pa_timeval_load(&now), pa_bytes_to_usec(pa_memblockq_get_write_index(s->memblockq), &s->sink_input->sample_spec));
+
if (pa_memblockq_push(s->memblockq, &chunk) < 0) {
- /* queue overflow, let's flush it and try again */
- pa_memblockq_flush(s->memblockq);
- pa_memblockq_push(s->memblockq, &chunk);
+ pa_log_warn("Queue overrun");
+ pa_memblockq_seek(s->memblockq, chunk.length, PA_SEEK_RELATIVE);
}
-
+
+ pa_log("blocks in q: %u", pa_memblockq_get_nblocks(s->memblockq));
+
+ pa_memblock_unref(chunk.memblock);
+
/* The next timestamp we expect */
s->offset = s->rtp_context.timestamp + (chunk.length / s->rtp_context.frame_size);
-
- pa_memblock_unref(chunk.memblock);
- /* Reset death timer */
- pa_gettimeofday(&tv);
- pa_timeval_add(&tv, DEATH_TIMEOUT);
- m->time_restart(s->death_event, &tv);
+ pa_atomic_store(&s->timestamp, now.tv_sec);
+
+ if (s->last_rate_update + RATE_UPDATE_INTERVAL < pa_timeval_load(&now)) {
+ pa_usec_t wi, ri, render_delay, sink_delay = 0, latency, fix;
+ unsigned fix_samples;
+
+ pa_log("Updating sample rate");
+
+ wi = pa_smoother_get(s->smoother, pa_timeval_load(&now));
+ ri = pa_bytes_to_usec(pa_memblockq_get_read_index(s->memblockq), &s->sink_input->sample_spec);
+
+ if (PA_MSGOBJECT(s->sink_input->sink)->process_msg(PA_MSGOBJECT(s->sink_input->sink), PA_SINK_MESSAGE_GET_LATENCY, &sink_delay, 0, NULL) < 0)
+ sink_delay = 0;
+
+ render_delay = pa_bytes_to_usec(pa_memblockq_get_length(s->sink_input->thread_info.render_memblockq), &s->sink_input->sink->sample_spec);
+
+ if (ri > render_delay+sink_delay)
+ ri -= render_delay+sink_delay;
+ else
+ ri = 0;
+
+ if (wi < ri)
+ latency = 0;
+ else
+ latency = wi - ri;
+
+ pa_log_debug("Write index deviates by %0.2f ms, expected %0.2f ms", (double) latency/PA_USEC_PER_MSEC, (double) s->intended_latency/PA_USEC_PER_MSEC);
+
+ /* Calculate deviation */
+ if (latency < s->intended_latency)
+ fix = s->intended_latency - latency;
+ else
+ fix = latency - s->intended_latency;
+
+ /* How many samples is this per second? */
+ fix_samples = fix * s->sink_input->thread_info.sample_spec.rate / RATE_UPDATE_INTERVAL;
+
+ /* Check if deviation is in bounds */
+ if (fix_samples > s->sink_input->sample_spec.rate*.20)
+ pa_log_debug("Hmmm, rate fix is too large (%lu Hz), not applying.", (unsigned long) fix_samples);
+
+ /* Fix up rate */
+ if (latency < s->intended_latency)
+ s->sink_input->sample_spec.rate -= fix_samples;
+ else
+ s->sink_input->sample_spec.rate += fix_samples;
+
+ pa_resampler_set_input_rate(s->sink_input->thread_info.resampler, s->sink_input->sample_spec.rate);
+
+ pa_log_debug("Updated sampling rate to %lu Hz.", (unsigned long) s->sink_input->sample_spec.rate);
+
+ s->last_rate_update = pa_timeval_load(&now);
+ }
+
+ if (pa_memblockq_is_readable(s->memblockq) &&
+ s->sink_input->thread_info.underrun_for > 0) {
+ pa_log_debug("Requesting rewind due to end of underrun");
+ pa_sink_input_request_rewind(s->sink_input, 0, FALSE, TRUE);
+ }
+
+ return 1;
+}
+
+/* Called from I/O thread context */
+static void sink_input_attach(pa_sink_input *i) {
+ struct session *s;
+ struct pollfd *p;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(s = i->userdata);
+
+ pa_assert(!s->rtpoll_item);
+ s->rtpoll_item = pa_rtpoll_item_new(i->sink->rtpoll, PA_RTPOLL_LATE, 1);
+
+ p = pa_rtpoll_item_get_pollfd(s->rtpoll_item, NULL);
+ p->fd = s->rtp_context.fd;
+ p->events = POLLIN;
+ p->revents = 0;
+
+ pa_rtpoll_item_set_work_callback(s->rtpoll_item, rtpoll_work_cb);
+ pa_rtpoll_item_set_userdata(s->rtpoll_item, s);
}
-static void death_event_cb(pa_mainloop_api *m, pa_time_event *t, const struct timeval *tv, void *userdata) {
- struct session *s = userdata;
-
- assert(m);
- assert(t);
- assert(tv);
- assert(s);
+/* Called from I/O thread context */
+static void sink_input_detach(pa_sink_input *i) {
+ struct session *s;
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(s = i->userdata);
- session_free(s, 1);
+ pa_assert(s->rtpoll_item);
+ pa_rtpoll_item_free(s->rtpoll_item);
+ s->rtpoll_item = NULL;
}
static int mcast_socket(const struct sockaddr* sa, socklen_t salen) {
int af, fd = -1, r, one;
-
+
+ pa_assert(sa);
+ pa_assert(salen > 0);
+
af = sa->sa_family;
if ((fd = socket(af, SOCK_DGRAM, 0)) < 0) {
pa_log("Failed to create socket: %s", pa_cstrerror(errno));
@@ -226,7 +367,7 @@ static int mcast_socket(const struct sockaddr* sa, socklen_t salen) {
pa_log("SO_REUSEADDR failed: %s", pa_cstrerror(errno));
goto fail;
}
-
+
if (af == AF_INET) {
struct ip_mreq mr4;
memset(&mr4, 0, sizeof(mr4));
@@ -243,14 +384,14 @@ static int mcast_socket(const struct sockaddr* sa, socklen_t salen) {
pa_log_info("Joining mcast group failed: %s", pa_cstrerror(errno));
goto fail;
}
-
+
if (bind(fd, sa, salen) < 0) {
pa_log("bind() failed: %s", pa_cstrerror(errno));
goto fail;
}
return fd;
-
+
fail:
if (fd >= 0)
close(fd);
@@ -260,136 +401,149 @@ fail:
static struct session *session_new(struct userdata *u, const pa_sdp_info *sdp_info) {
struct session *s = NULL;
- struct timeval tv;
- char *c;
pa_sink *sink;
int fd = -1;
- pa_memblock *silence;
+ pa_memchunk silence;
pa_sink_input_new_data data;
+ struct timeval now;
+
+ pa_assert(u);
+ pa_assert(sdp_info);
if (u->n_sessions >= MAX_SESSIONS) {
- pa_log("session limit reached.");
+ pa_log("Session limit reached.");
goto fail;
}
-
- if (!(sink = pa_namereg_get(u->core, u->sink_name, PA_NAMEREG_SINK, 1))) {
- pa_log("sink does not exist.");
+
+ if (!(sink = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, TRUE))) {
+ pa_log("Sink does not exist.");
goto fail;
}
+ pa_rtclock_get(&now);
+
s = pa_xnew0(struct session, 1);
s->userdata = u;
- s->first_packet = 0;
+ s->first_packet = FALSE;
s->sdp_info = *sdp_info;
+ s->rtpoll_item = NULL;
+ s->intended_latency = LATENCY_USEC;
+ s->smoother = pa_smoother_new(PA_USEC_PER_SEC*5, PA_USEC_PER_SEC*2, TRUE, 10);
+ pa_smoother_set_time_offset(s->smoother, pa_timeval_load(&now));
+ s->last_rate_update = pa_timeval_load(&now);
+ pa_atomic_store(&s->timestamp, now.tv_sec);
if ((fd = mcast_socket((const struct sockaddr*) &sdp_info->sa, sdp_info->salen)) < 0)
goto fail;
- c = pa_sprintf_malloc("RTP Stream%s%s%s",
- sdp_info->session_name ? " (" : "",
- sdp_info->session_name ? sdp_info->session_name : "",
- sdp_info->session_name ? ")" : "");
-
pa_sink_input_new_data_init(&data);
data.sink = sink;
data.driver = __FILE__;
- data.name = c;
+ pa_proplist_sets(data.proplist, PA_PROP_MEDIA_ROLE, "stream");
+ pa_proplist_setf(data.proplist, PA_PROP_MEDIA_NAME,
+ "RTP Stream%s%s%s",
+ sdp_info->session_name ? " (" : "",
+ sdp_info->session_name ? sdp_info->session_name : "",
+ sdp_info->session_name ? ")" : "");
+
+ if (sdp_info->session_name)
+ pa_proplist_sets(data.proplist, "rtp.session", sdp_info->session_name);
+ pa_proplist_sets(data.proplist, "rtp.origin", sdp_info->origin);
+ pa_proplist_setf(data.proplist, "rtp.payload", "%u", (unsigned) sdp_info->payload);
data.module = u->module;
pa_sink_input_new_data_set_sample_spec(&data, &sdp_info->sample_spec);
-
- s->sink_input = pa_sink_input_new(u->core, &data, 0);
- pa_xfree(c);
-
+
+ s->sink_input = pa_sink_input_new(u->module->core, &data, 0);
+ pa_sink_input_new_data_done(&data);
+
if (!s->sink_input) {
- pa_log("failed to create sink input.");
+ pa_log("Failed to create sink input.");
goto fail;
}
s->sink_input->userdata = s;
- s->sink_input->peek = sink_input_peek;
- s->sink_input->drop = sink_input_drop;
+ s->sink_input->parent.process_msg = sink_input_process_msg;
+ s->sink_input->pop = sink_input_pop_cb;
+ s->sink_input->process_rewind = sink_input_process_rewind_cb;
+ s->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
s->sink_input->kill = sink_input_kill;
- s->sink_input->get_latency = sink_input_get_latency;
+ s->sink_input->attach = sink_input_attach;
+ s->sink_input->detach = sink_input_detach;
+
+ pa_sink_input_get_silence(s->sink_input, &silence);
+
+ s->sink_latency = pa_sink_input_set_requested_latency(s->sink_input, s->intended_latency/2);
+
+ if (s->intended_latency < s->sink_latency*2)
+ s->intended_latency = s->sink_latency*2;
- silence = pa_silence_memblock_new(s->userdata->core->mempool,
- &s->sink_input->sample_spec,
- (pa_bytes_per_second(&s->sink_input->sample_spec)/128/pa_frame_size(&s->sink_input->sample_spec))*
- pa_frame_size(&s->sink_input->sample_spec));
-
s->memblockq = pa_memblockq_new(
0,
MEMBLOCKQ_MAXLENGTH,
MEMBLOCKQ_MAXLENGTH,
pa_frame_size(&s->sink_input->sample_spec),
- pa_bytes_per_second(&s->sink_input->sample_spec)/10+1,
+ pa_usec_to_bytes(s->intended_latency - s->sink_latency, &s->sink_input->sample_spec),
+ 0,
0,
- silence);
+ &silence);
- pa_memblock_unref(silence);
+ pa_memblock_unref(silence.memblock);
- s->rtp_event = u->core->mainloop->io_new(u->core->mainloop, fd, PA_IO_EVENT_INPUT, rtp_event_cb, s);
-
- pa_gettimeofday(&tv);
- pa_timeval_add(&tv, DEATH_TIMEOUT);
- s->death_event = u->core->mainloop->time_new(u->core->mainloop, &tv, death_event_cb, s);
+ pa_rtp_context_init_recv(&s->rtp_context, fd, pa_frame_size(&s->sdp_info.sample_spec));
pa_hashmap_put(s->userdata->by_origin, s->sdp_info.origin, s);
+ u->n_sessions++;
+ PA_LLIST_PREPEND(struct session, s->userdata->sessions, s);
- pa_rtp_context_init_recv(&s->rtp_context, fd, pa_frame_size(&s->sdp_info.sample_spec));
+ pa_sink_input_put(s->sink_input);
- pa_log_info("Found new session '%s'", s->sdp_info.session_name);
+ pa_log_info("New session '%s'", s->sdp_info.session_name);
- u->n_sessions++;
-
return s;
fail:
- if (s) {
- if (fd >= 0)
- close(fd);
-
- pa_xfree(s);
- }
+ pa_xfree(s);
+
+ if (fd >= 0)
+ pa_close(fd);
return NULL;
}
-static void session_free(struct session *s, int from_hash) {
- assert(s);
+static void session_free(struct session *s) {
+ pa_assert(s);
pa_log_info("Freeing session '%s'", s->sdp_info.session_name);
- s->userdata->core->mainloop->time_free(s->death_event);
- s->userdata->core->mainloop->io_free(s->rtp_event);
-
- if (from_hash)
- pa_hashmap_remove(s->userdata->by_origin, s->sdp_info.origin);
-
- pa_sink_input_disconnect(s->sink_input);
+ pa_sink_input_unlink(s->sink_input);
pa_sink_input_unref(s->sink_input);
+ PA_LLIST_REMOVE(struct session, s->userdata->sessions, s);
+ pa_assert(s->userdata->n_sessions >= 1);
+ s->userdata->n_sessions--;
+ pa_hashmap_remove(s->userdata->by_origin, s->sdp_info.origin);
+
pa_memblockq_free(s->memblockq);
pa_sdp_info_destroy(&s->sdp_info);
pa_rtp_context_destroy(&s->rtp_context);
- assert(s->userdata->n_sessions >= 1);
- s->userdata->n_sessions--;
-
+ pa_smoother_free(s->smoother);
+
pa_xfree(s);
}
static void sap_event_cb(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event_flags_t flags, void *userdata) {
struct userdata *u = userdata;
- int goodbye;
+ pa_bool_t goodbye = FALSE;
pa_sdp_info info;
struct session *s;
-
- assert(m);
- assert(e);
- assert(u);
- assert(fd == u->sap_context.fd);
- assert(flags == PA_IO_EVENT_INPUT);
+
+ pa_assert(m);
+ pa_assert(e);
+ pa_assert(u);
+ pa_assert(fd == u->sap_context.fd);
+ pa_assert(flags == PA_IO_EVENT_INPUT);
if (pa_sap_recv(&u->sap_context, &goodbye) < 0)
return;
@@ -400,7 +554,7 @@ static void sap_event_cb(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event
if (goodbye) {
if ((s = pa_hashmap_get(u->by_origin, info.origin)))
- session_free(s, 1);
+ session_free(s);
pa_sdp_info_destroy(&info);
} else {
@@ -408,20 +562,49 @@ static void sap_event_cb(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event
if (!(s = pa_hashmap_get(u->by_origin, info.origin))) {
if (!(s = session_new(u, &info)))
pa_sdp_info_destroy(&info);
-
+
} else {
- struct timeval tv;
-
- pa_gettimeofday(&tv);
- pa_timeval_add(&tv, DEATH_TIMEOUT);
- m->time_restart(s->death_event, &tv);
-
+ struct timeval now;
+ pa_rtclock_get(&now);
+ pa_atomic_store(&s->timestamp, now.tv_sec);
+
pa_sdp_info_destroy(&info);
}
}
}
-int pa__init(pa_core *c, pa_module*m) {
+static void check_death_event_cb(pa_mainloop_api *m, pa_time_event *t, const struct timeval *ptv, void *userdata) {
+ struct session *s, *n;
+ struct userdata *u = userdata;
+ struct timeval now;
+ struct timeval tv;
+
+ pa_assert(m);
+ pa_assert(t);
+ pa_assert(ptv);
+ pa_assert(u);
+
+ pa_rtclock_get(&now);
+
+ pa_log_debug("Checking for dead streams ...");
+
+ for (s = u->sessions; s; s = n) {
+ int k;
+ n = s->next;
+
+ k = pa_atomic_load(&s->timestamp);
+
+ if (k + DEATH_TIMEOUT < now.tv_sec)
+ session_free(s);
+ }
+
+ /* Restart timer */
+ pa_gettimeofday(&tv);
+ pa_timeval_add(&tv, DEATH_TIMEOUT*PA_USEC_PER_SEC);
+ m->time_restart(t, &tv);
+}
+
+int pa__init(pa_module*m) {
struct userdata *u;
pa_modargs *ma = NULL;
struct sockaddr_in sa4;
@@ -430,9 +613,9 @@ int pa__init(pa_core *c, pa_module*m) {
socklen_t salen;
const char *sap_address;
int fd = -1;
-
- assert(c);
- assert(m);
+ struct timeval tv;
+
+ pa_assert(m);
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
pa_log("failed to parse module arguments");
@@ -440,7 +623,7 @@ int pa__init(pa_core *c, pa_module*m) {
}
sap_address = pa_modargs_get_value(ma, "sap_address", DEFAULT_SAP_ADDRESS);
-
+
if (inet_pton(AF_INET6, sap_address, &sa6.sin6_addr) > 0) {
sa6.sin6_family = AF_INET6;
sa6.sin6_port = htons(SAP_PORT);
@@ -452,7 +635,7 @@ int pa__init(pa_core *c, pa_module*m) {
sa = (struct sockaddr*) &sa4;
salen = sizeof(sa4);
} else {
- pa_log("invalid SAP address '%s'", sap_address);
+ pa_log("Invalid SAP address '%s'", sap_address);
goto fail;
}
@@ -462,16 +645,19 @@ int pa__init(pa_core *c, pa_module*m) {
u = pa_xnew(struct userdata, 1);
m->userdata = u;
u->module = m;
- u->core = c;
u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
- u->n_sessions = 0;
- u->sap_event = c->mainloop->io_new(c->mainloop, fd, PA_IO_EVENT_INPUT, sap_event_cb, u);
+ u->sap_event = m->core->mainloop->io_new(m->core->mainloop, fd, PA_IO_EVENT_INPUT, sap_event_cb, u);
+ pa_sap_context_init_recv(&u->sap_context, fd);
+ PA_LLIST_HEAD_INIT(struct session, u->sessions);
+ u->n_sessions = 0;
u->by_origin = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
-
- pa_sap_context_init_recv(&u->sap_context, fd);
-
+
+ pa_gettimeofday(&tv);
+ pa_timeval_add(&tv, DEATH_TIMEOUT * PA_USEC_PER_SEC);
+ u->check_death_event = m->core->mainloop->time_new(m->core->mainloop, &tv, check_death_event_cb, u);
+
pa_modargs_free(ma);
return 0;
@@ -481,28 +667,35 @@ fail:
pa_modargs_free(ma);
if (fd >= 0)
- close(fd);
-
- return -1;
-}
+ pa_close(fd);
-static void free_func(void *p, PA_GCC_UNUSED void *userdata) {
- session_free(p, 0);
+ return -1;
}
-void pa__done(pa_core *c, pa_module*m) {
+void pa__done(pa_module*m) {
struct userdata *u;
- assert(c);
- assert(m);
+ struct session *s;
+
+ pa_assert(m);
if (!(u = m->userdata))
return;
- c->mainloop->io_free(u->sap_event);
+ if (u->sap_event)
+ m->core->mainloop->io_free(u->sap_event);
+
+ if (u->check_death_event)
+ m->core->mainloop->time_free(u->check_death_event);
+
pa_sap_context_destroy(&u->sap_context);
- pa_hashmap_free(u->by_origin, free_func, NULL);
-
+ if (u->by_origin) {
+ while ((s = pa_hashmap_get_first(u->by_origin)))
+ session_free(s);
+
+ pa_hashmap_free(u->by_origin, NULL, NULL);
+ }
+
pa_xfree(u->sink_name);
pa_xfree(u);
}
diff --git a/src/modules/rtp/module-rtp-send.c b/src/modules/rtp/module-rtp-send.c
index 7bbfabee..d0d06c4d 100644
--- a/src/modules/rtp/module-rtp-send.c
+++ b/src/modules/rtp/module-rtp-send.c
@@ -1,18 +1,18 @@
-/* $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
@@ -23,7 +23,6 @@
#include <config.h>
#endif
-#include <assert.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
@@ -46,6 +45,9 @@
#include <pulsecore/core-util.h>
#include <pulsecore/modargs.h>
#include <pulsecore/namereg.h>
+#include <pulsecore/sample-util.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/socket-util.h>
#include "module-rtp-send-symdef.h"
@@ -53,9 +55,10 @@
#include "sdp.h"
#include "sap.h"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("Read data from source and send it to the network via RTP/SAP/SDP")
-PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Read data from source and send it to the network via RTP/SAP/SDP");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
PA_MODULE_USAGE(
"source=<name of the source> "
"format=<sample format> "
@@ -65,14 +68,14 @@ PA_MODULE_USAGE(
"port=<port number> "
"mtu=<maximum transfer unit> "
"loop=<loopback to local host?>"
-)
+);
#define DEFAULT_PORT 46000
#define SAP_PORT 9875
#define DEFAULT_DESTINATION "224.0.0.56"
#define MEMBLOCKQ_MAXLENGTH (1024*170)
#define DEFAULT_MTU 1280
-#define SAP_INTERVAL 5000000
+#define SAP_INTERVAL 5
static const char* const valid_modargs[] = {
"source",
@@ -88,7 +91,6 @@ static const char* const valid_modargs[] = {
struct userdata {
pa_module *module;
- pa_core *core;
pa_source_output *source_output;
pa_memblockq *memblockq;
@@ -100,56 +102,67 @@ struct userdata {
pa_time_event *sap_event;
};
+/* Called from I/O thread context */
+static int source_output_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+ struct userdata *u;
+ pa_assert_se(u = PA_SOURCE_OUTPUT(o)->userdata);
+
+ switch (code) {
+ case PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY:
+ *((pa_usec_t*) data) = pa_bytes_to_usec(pa_memblockq_get_length(u->memblockq), &u->source_output->sample_spec);
+
+ /* Fall through, the default handler will add in the extra
+ * latency added by the resampler */
+ break;
+ }
+
+ return pa_source_output_process_msg(o, code, data, offset, chunk);
+}
+
+/* Called from I/O thread context */
static void source_output_push(pa_source_output *o, const pa_memchunk *chunk) {
struct userdata *u;
- assert(o);
- u = o->userdata;
+ pa_source_output_assert_ref(o);
+ pa_assert_se(u = o->userdata);
if (pa_memblockq_push(u->memblockq, chunk) < 0) {
- pa_log("Failed to push chunk into memblockq.");
+ pa_log_warn("Failed to push chunk into memblockq.");
return;
}
-
+
pa_rtp_send(&u->rtp_context, u->mtu, u->memblockq);
}
+/* Called from main context */
static void source_output_kill(pa_source_output* o) {
struct userdata *u;
- assert(o);
- u = o->userdata;
+ pa_source_output_assert_ref(o);
+ pa_assert_se(u = o->userdata);
pa_module_unload_request(u->module);
- pa_source_output_disconnect(u->source_output);
+ pa_source_output_unlink(u->source_output);
pa_source_output_unref(u->source_output);
u->source_output = NULL;
}
-static pa_usec_t source_output_get_latency (pa_source_output *o) {
- struct userdata *u;
- assert(o);
- u = o->userdata;
-
- return pa_bytes_to_usec(pa_memblockq_get_length(u->memblockq), &o->sample_spec);
-}
-
static void sap_event_cb(pa_mainloop_api *m, pa_time_event *t, const struct timeval *tv, void *userdata) {
struct userdata *u = userdata;
struct timeval next;
-
- assert(m);
- assert(t);
- assert(tv);
- assert(u);
+
+ pa_assert(m);
+ pa_assert(t);
+ pa_assert(tv);
+ pa_assert(u);
pa_sap_send(&u->sap_context, 0);
pa_gettimeofday(&next);
- pa_timeval_add(&next, SAP_INTERVAL);
+ pa_timeval_add(&next, SAP_INTERVAL * PA_USEC_PER_SEC);
m->time_restart(t, &next);
}
-int pa__init(pa_core *c, pa_module*m) {
+int pa__init(pa_module*m) {
struct userdata *u;
pa_modargs *ma = NULL;
const char *dest;
@@ -164,28 +177,27 @@ int pa__init(pa_core *c, pa_module*m) {
pa_source_output *o = NULL;
uint8_t payload;
char *p;
- int r;
+ int r, j;
socklen_t k;
struct timeval tv;
char hn[128], *n;
- int loop = 0;
+ pa_bool_t loop = FALSE;
pa_source_output_new_data data;
-
- 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;
}
if (!(s = pa_namereg_get(m->core, pa_modargs_get_value(ma, "source", NULL), PA_NAMEREG_SOURCE, 1))) {
- pa_log("source does not exist.");
+ pa_log("Source does not exist.");
goto fail;
}
if (pa_modargs_get_value_boolean(ma, "loop", &loop) < 0) {
- pa_log("failed to parse \"loop\" parameter.");
+ pa_log("Failed to parse \"loop\" parameter.");
goto fail;
}
@@ -193,12 +205,12 @@ int pa__init(pa_core *c, pa_module*m) {
pa_rtp_sample_spec_fixup(&ss);
cm = s->channel_map;
if (pa_modargs_get_sample_spec(ma, &ss) < 0) {
- pa_log("failed to parse sample specification");
+ pa_log("Failed to parse sample specification");
goto fail;
}
if (!pa_rtp_sample_spec_valid(&ss)) {
- pa_log("specified sample type not compatible with RTP");
+ pa_log("Specified sample type not compatible with RTP");
goto fail;
}
@@ -207,10 +219,10 @@ int pa__init(pa_core *c, pa_module*m) {
payload = pa_rtp_payload_from_sample_spec(&ss);
- mtu = (DEFAULT_MTU/pa_frame_size(&ss))*pa_frame_size(&ss);
-
+ mtu = pa_frame_align(DEFAULT_MTU, &ss);
+
if (pa_modargs_get_value_u32(ma, "mtu", &mtu) < 0 || mtu < 1 || mtu % pa_frame_size(&ss) != 0) {
- pa_log("invalid mtu.");
+ pa_log("Invalid MTU.");
goto fail;
}
@@ -221,7 +233,7 @@ int pa__init(pa_core *c, pa_module*m) {
}
if (port & 1)
- pa_log_warn("WARNING: port number not even as suggested in RFC3550!");
+ pa_log_warn("Port number not even as suggested in RFC3550!");
dest = pa_modargs_get_value(ma, "destination", DEFAULT_DESTINATION);
@@ -236,10 +248,10 @@ int pa__init(pa_core *c, pa_module*m) {
sap_sa4 = sa4;
sap_sa4.sin_port = htons(SAP_PORT);
} else {
- pa_log("invalid destination '%s'", dest);
+ pa_log("Invalid destination '%s'", dest);
goto fail;
}
-
+
if ((fd = socket(af, SOCK_DGRAM, 0)) < 0) {
pa_log("socket() failed: %s", pa_cstrerror(errno));
goto fail;
@@ -260,37 +272,49 @@ int pa__init(pa_core *c, pa_module*m) {
goto fail;
}
- if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop)) < 0 ||
- setsockopt(sap_fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop)) < 0) {
+ j = !!loop;
+ if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &j, sizeof(j)) < 0 ||
+ setsockopt(sap_fd, IPPROTO_IP, IP_MULTICAST_LOOP, &j, sizeof(j)) < 0) {
pa_log("IP_MULTICAST_LOOP failed: %s", pa_cstrerror(errno));
goto fail;
}
+ /* If the socket queue is full, let's drop packets */
+ pa_make_fd_nonblock(fd);
+ pa_make_udp_socket_low_delay(fd);
+ pa_make_fd_cloexec(fd);
+ pa_make_fd_cloexec(sap_fd);
+
pa_source_output_new_data_init(&data);
- data.name = "RTP Monitor Stream";
+ pa_proplist_sets(data.proplist, PA_PROP_MEDIA_NAME, "RTP Monitor Stream");
+ pa_proplist_sets(data.proplist, "rtp.destination", dest);
+ pa_proplist_setf(data.proplist, "rtp.mtu", "%lu", (unsigned long) mtu);
+ pa_proplist_setf(data.proplist, "rtp.port", "%lu", (unsigned long) port);
data.driver = __FILE__;
data.module = m;
data.source = s;
pa_source_output_new_data_set_sample_spec(&data, &ss);
pa_source_output_new_data_set_channel_map(&data, &cm);
-
- if (!(o = pa_source_output_new(c, &data, 0))) {
+
+ o = pa_source_output_new(m->core, &data, 0);
+ pa_source_output_new_data_done(&data);
+
+ if (!o) {
pa_log("failed to create source output.");
goto fail;
}
+ o->parent.process_msg = source_output_process_msg;
o->push = source_output_push;
o->kill = source_output_kill;
- o->get_latency = source_output_get_latency;
-
+
u = pa_xnew(struct userdata, 1);
m->userdata = u;
o->userdata = u;
u->module = m;
- u->core = c;
u->source_output = o;
-
+
u->memblockq = pa_memblockq_new(
0,
MEMBLOCKQ_MAXLENGTH,
@@ -298,34 +322,36 @@ int pa__init(pa_core *c, pa_module*m) {
pa_frame_size(&ss),
1,
0,
+ 0,
NULL);
u->mtu = mtu;
-
+
k = sizeof(sa_dst);
- r = getsockname(fd, (struct sockaddr*) &sa_dst, &k);
- assert(r >= 0);
+ pa_assert_se((r = getsockname(fd, (struct sockaddr*) &sa_dst, &k)) >= 0);
n = pa_sprintf_malloc("PulseAudio RTP Stream on %s", pa_get_fqdn(hn, sizeof(hn)));
-
+
p = pa_sdp_build(af,
af == AF_INET ? (void*) &((struct sockaddr_in*) &sa_dst)->sin_addr : (void*) &((struct sockaddr_in6*) &sa_dst)->sin6_addr,
af == AF_INET ? (void*) &sa4.sin_addr : (void*) &sa6.sin6_addr,
n, port, payload, &ss);
pa_xfree(n);
-
- pa_rtp_context_init_send(&u->rtp_context, fd, c->cookie, payload, pa_frame_size(&ss));
+
+ pa_rtp_context_init_send(&u->rtp_context, fd, m->core->cookie, payload, pa_frame_size(&ss));
pa_sap_context_init_send(&u->sap_context, sap_fd, p);
pa_log_info("RTP stream initialized with mtu %u on %s:%u, SSRC=0x%08x, payload=%u, initial sequence #%u", mtu, dest, port, u->rtp_context.ssrc, payload, u->rtp_context.sequence);
- pa_log_info("SDP-Data:\n%s\n"__FILE__": EOF", p);
+ pa_log_info("SDP-Data:\n%s\nEOF", p);
pa_sap_send(&u->sap_context, 0);
pa_gettimeofday(&tv);
- pa_timeval_add(&tv, SAP_INTERVAL);
- u->sap_event = c->mainloop->time_new(c->mainloop, &tv, sap_event_cb, u);
+ pa_timeval_add(&tv, SAP_INTERVAL * PA_USEC_PER_SEC);
+ u->sap_event = m->core->mainloop->time_new(m->core->mainloop, &tv, sap_event_cb, u);
+
+ pa_source_output_put(u->source_output);
pa_modargs_free(ma);
@@ -336,31 +362,31 @@ fail:
pa_modargs_free(ma);
if (fd >= 0)
- close(fd);
-
+ pa_close(fd);
+
if (sap_fd >= 0)
- close(sap_fd);
+ pa_close(sap_fd);
if (o) {
- pa_source_output_disconnect(o);
+ pa_source_output_unlink(o);
pa_source_output_unref(o);
}
-
+
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;
- c->mainloop->time_free(u->sap_event);
-
+ if (u->sap_event)
+ m->core->mainloop->time_free(u->sap_event);
+
if (u->source_output) {
- pa_source_output_disconnect(u->source_output);
+ pa_source_output_unlink(u->source_output);
pa_source_output_unref(u->source_output);
}
@@ -369,7 +395,8 @@ void pa__done(pa_core *c, pa_module*m) {
pa_sap_send(&u->sap_context, 1);
pa_sap_context_destroy(&u->sap_context);
- pa_memblockq_free(u->memblockq);
-
+ if (u->memblockq)
+ pa_memblockq_free(u->memblockq);
+
pa_xfree(u);
}
diff --git a/src/modules/rtp/rtp.c b/src/modules/rtp/rtp.c
index a4362f84..5a33ebc2 100644
--- a/src/modules/rtp/rtp.c
+++ b/src/modules/rtp/rtp.c
@@ -1,18 +1,18 @@
-/* $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
@@ -23,7 +23,6 @@
#include <config.h>
#endif
-#include <assert.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
@@ -38,12 +37,14 @@
#include <pulsecore/core-error.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
#include "rtp.h"
pa_rtp_context* pa_rtp_context_init_send(pa_rtp_context *c, int fd, uint32_t ssrc, uint8_t payload, size_t frame_size) {
- assert(c);
- assert(fd >= 0);
+ pa_assert(c);
+ pa_assert(fd >= 0);
c->fd = fd;
c->sequence = (uint16_t) (rand()*rand());
@@ -51,7 +52,9 @@ pa_rtp_context* pa_rtp_context_init_send(pa_rtp_context *c, int fd, uint32_t ssr
c->ssrc = ssrc ? ssrc : (uint32_t) (rand()*rand());
c->payload = payload & 127;
c->frame_size = frame_size;
-
+
+ pa_memchunk_reset(&c->memchunk);
+
return c;
}
@@ -61,37 +64,39 @@ int pa_rtp_send(pa_rtp_context *c, size_t size, pa_memblockq *q) {
struct iovec iov[MAX_IOVECS];
pa_memblock* mb[MAX_IOVECS];
int iov_idx = 1;
- size_t n = 0, skip = 0;
-
- assert(c);
- assert(size > 0);
- assert(q);
+ size_t n = 0;
+
+ pa_assert(c);
+ pa_assert(size > 0);
+ pa_assert(q);
if (pa_memblockq_get_length(q) < size)
return 0;
-
+
for (;;) {
int r;
pa_memchunk chunk;
+ pa_memchunk_reset(&chunk);
+
if ((r = pa_memblockq_peek(q, &chunk)) >= 0) {
size_t k = n + chunk.length > size ? size - n : chunk.length;
- if (chunk.memblock) {
- iov[iov_idx].iov_base = (void*)((uint8_t*) pa_memblock_acquire(chunk.memblock) + chunk.index);
- iov[iov_idx].iov_len = k;
- mb[iov_idx] = chunk.memblock;
- iov_idx ++;
+ pa_assert(chunk.memblock);
- n += k;
- }
+ iov[iov_idx].iov_base = ((uint8_t*) pa_memblock_acquire(chunk.memblock) + chunk.index);
+ iov[iov_idx].iov_len = k;
+ mb[iov_idx] = chunk.memblock;
+ iov_idx ++;
- skip += k;
- pa_memblockq_drop(q, &chunk, k);
+ n += k;
+ pa_memblockq_drop(q, k);
}
- if (r < 0 || !chunk.memblock || n >= size || iov_idx >= MAX_IOVECS) {
+ pa_assert(n % c->frame_size == 0);
+
+ if (r < 0 || n >= size || iov_idx >= MAX_IOVECS) {
uint32_t header[3];
struct msghdr m;
int k, i;
@@ -103,7 +108,7 @@ int pa_rtp_send(pa_rtp_context *c, size_t size, pa_memblockq *q) {
iov[0].iov_base = (void*)header;
iov[0].iov_len = sizeof(header);
-
+
m.msg_name = NULL;
m.msg_namelen = 0;
m.msg_iov = iov;
@@ -111,7 +116,7 @@ int pa_rtp_send(pa_rtp_context *c, size_t size, pa_memblockq *q) {
m.msg_control = NULL;
m.msg_controllen = 0;
m.msg_flags = 0;
-
+
k = sendmsg(c->fd, &m, MSG_DONTWAIT);
for (i = 1; i < iov_idx; i++) {
@@ -123,19 +128,18 @@ int pa_rtp_send(pa_rtp_context *c, size_t size, pa_memblockq *q) {
} else
k = 0;
- c->timestamp += skip/c->frame_size;
-
+ c->timestamp += n/c->frame_size;
+
if (k < 0) {
- if (errno != EAGAIN) /* If the queue is full, just ignore it */
+ if (errno != EAGAIN && errno != EINTR) /* If the queue is full, just ignore it */
pa_log("sendmsg() failed: %s", pa_cstrerror(errno));
return -1;
}
-
+
if (r < 0 || pa_memblockq_get_length(q) < size)
break;
n = 0;
- skip = 0;
iov_idx = 1;
}
}
@@ -144,10 +148,12 @@ int pa_rtp_send(pa_rtp_context *c, size_t size, pa_memblockq *q) {
}
pa_rtp_context* pa_rtp_context_init_recv(pa_rtp_context *c, int fd, size_t frame_size) {
- assert(c);
+ pa_assert(c);
c->fd = fd;
c->frame_size = frame_size;
+
+ pa_memchunk_reset(&c->memchunk);
return c;
}
@@ -158,23 +164,39 @@ int pa_rtp_recv(pa_rtp_context *c, pa_memchunk *chunk, pa_mempool *pool) {
uint32_t header;
int cc;
ssize_t r;
-
- assert(c);
- assert(chunk);
- chunk->memblock = NULL;
+ pa_assert(c);
+ pa_assert(chunk);
+
+ pa_memchunk_reset(chunk);
if (ioctl(c->fd, FIONREAD, &size) < 0) {
- pa_log("FIONREAD failed: %s", pa_cstrerror(errno));
+ pa_log_warn("FIONREAD failed: %s", pa_cstrerror(errno));
goto fail;
}
- if (!size)
+ if (size <= 0)
return 0;
- chunk->memblock = pa_memblock_new(pool, size);
+ if (c->memchunk.length < (unsigned) size) {
+ size_t l;
+
+ if (c->memchunk.memblock)
+ pa_memblock_unref(c->memchunk.memblock);
- iov.iov_base = pa_memblock_acquire(chunk->memblock);
+ l = PA_MAX((size_t) size, pa_mempool_block_size_max(pool));
+
+ c->memchunk.memblock = pa_memblock_new(pool, l);
+ c->memchunk.index = 0;
+ c->memchunk.length = pa_memblock_get_length(c->memchunk.memblock);
+ }
+
+ pa_assert(c->memchunk.length >= (size_t) size);
+
+ chunk->memblock = pa_memblock_ref(c->memchunk.memblock);
+ chunk->index = c->memchunk.index;
+
+ iov.iov_base = (uint8_t*) pa_memblock_acquire(chunk->memblock) + chunk->index;
iov.iov_len = size;
m.msg_name = NULL;
@@ -184,37 +206,42 @@ int pa_rtp_recv(pa_rtp_context *c, pa_memchunk *chunk, pa_mempool *pool) {
m.msg_control = NULL;
m.msg_controllen = 0;
m.msg_flags = 0;
-
- if ((r = recvmsg(c->fd, &m, 0)) != size) {
- pa_log("recvmsg() failed: %s", r < 0 ? pa_cstrerror(errno) : "size mismatch");
+
+ r = recvmsg(c->fd, &m, 0);
+ pa_memblock_release(chunk->memblock);
+
+ if (r != size) {
+ if (r < 0 && errno != EAGAIN && errno != EINTR)
+ pa_log_warn("recvmsg() failed: %s", r < 0 ? pa_cstrerror(errno) : "size mismatch");
+
goto fail;
}
if (size < 12) {
- pa_log("RTP packet too short.");
+ pa_log_warn("RTP packet too short.");
goto fail;
}
memcpy(&header, iov.iov_base, sizeof(uint32_t));
memcpy(&c->timestamp, (uint8_t*) iov.iov_base + 4, sizeof(uint32_t));
memcpy(&c->ssrc, (uint8_t*) iov.iov_base + 8, sizeof(uint32_t));
-
+
header = ntohl(header);
c->timestamp = ntohl(c->timestamp);
c->ssrc = ntohl(c->ssrc);
if ((header >> 30) != 2) {
- pa_log("Unsupported RTP version.");
+ pa_log_warn("Unsupported RTP version.");
goto fail;
}
if ((header >> 29) & 1) {
- pa_log("RTP padding not supported.");
+ pa_log_warn("RTP padding not supported.");
goto fail;
}
if ((header >> 28) & 1) {
- pa_log("RTP header extensions not supported.");
+ pa_log_warn("RTP header extensions not supported.");
goto fail;
}
@@ -223,31 +250,37 @@ int pa_rtp_recv(pa_rtp_context *c, pa_memchunk *chunk, pa_mempool *pool) {
c->sequence = header & 0xFFFF;
if (12 + cc*4 > size) {
- pa_log("RTP packet too short. (CSRC)");
+ pa_log_warn("RTP packet too short. (CSRC)");
goto fail;
}
- chunk->index = 12 + cc*4;
- chunk->length = size - chunk->index;
+ chunk->index += 12 + cc*4;
+ chunk->length = size - 12 + cc*4;
if (chunk->length % c->frame_size != 0) {
- pa_log("Vad RTP packet size.");
+ pa_log_warn("Bad RTP packet size.");
goto fail;
}
-
+
+ c->memchunk.index = chunk->index + chunk->length;
+ c->memchunk.length = pa_memblock_get_length(c->memchunk.memblock) - c->memchunk.index;
+
+ if (c->memchunk.length <= 0) {
+ pa_memblock_unref(c->memchunk.memblock);
+ pa_memchunk_reset(&c->memchunk);
+ }
+
return 0;
fail:
- if (chunk->memblock) {
- pa_memblock_release(chunk->memblock);
+ if (chunk->memblock)
pa_memblock_unref(chunk->memblock);
- }
return -1;
}
uint8_t pa_rtp_payload_from_sample_spec(const pa_sample_spec *ss) {
- assert(ss);
+ pa_assert(ss);
if (ss->format == PA_SAMPLE_ULAW && ss->rate == 8000 && ss->channels == 1)
return 0;
@@ -257,12 +290,12 @@ uint8_t pa_rtp_payload_from_sample_spec(const pa_sample_spec *ss) {
return 10;
if (ss->format == PA_SAMPLE_S16BE && ss->rate == 44100 && ss->channels == 1)
return 11;
-
+
return 127;
}
pa_sample_spec *pa_rtp_sample_spec_from_payload(uint8_t payload, pa_sample_spec *ss) {
- assert(ss);
+ pa_assert(ss);
switch (payload) {
case 0:
@@ -282,7 +315,7 @@ pa_sample_spec *pa_rtp_sample_spec_from_payload(uint8_t payload, pa_sample_spec
ss->format = PA_SAMPLE_S16BE;
ss->rate = 44100;
break;
-
+
case 11:
ss->channels = 1;
ss->format = PA_SAMPLE_S16BE;
@@ -297,17 +330,17 @@ pa_sample_spec *pa_rtp_sample_spec_from_payload(uint8_t payload, pa_sample_spec
}
pa_sample_spec *pa_rtp_sample_spec_fixup(pa_sample_spec * ss) {
- assert(ss);
+ pa_assert(ss);
if (!pa_rtp_sample_spec_valid(ss))
ss->format = PA_SAMPLE_S16BE;
- assert(pa_rtp_sample_spec_valid(ss));
+ pa_assert(pa_rtp_sample_spec_valid(ss));
return ss;
}
int pa_rtp_sample_spec_valid(const pa_sample_spec *ss) {
- assert(ss);
+ pa_assert(ss);
if (!pa_sample_spec_valid(ss))
return 0;
@@ -320,9 +353,12 @@ int pa_rtp_sample_spec_valid(const pa_sample_spec *ss) {
}
void pa_rtp_context_destroy(pa_rtp_context *c) {
- assert(c);
+ pa_assert(c);
+
+ pa_assert_se(pa_close(c->fd) == 0);
- close(c->fd);
+ if (c->memchunk.memblock)
+ pa_memblock_unref(c->memchunk.memblock);
}
const char* pa_rtp_format_to_string(pa_sample_format_t f) {
@@ -341,8 +377,8 @@ const char* pa_rtp_format_to_string(pa_sample_format_t f) {
}
pa_sample_format_t pa_rtp_string_to_format(const char *s) {
- assert(s);
-
+ pa_assert(s);
+
if (!(strcmp(s, "L16")))
return PA_SAMPLE_S16BE;
else if (!strcmp(s, "L8"))
@@ -354,4 +390,3 @@ pa_sample_format_t pa_rtp_string_to_format(const char *s) {
else
return PA_SAMPLE_INVALID;
}
-
diff --git a/src/modules/rtp/rtp.h b/src/modules/rtp/rtp.h
index 123602b2..a2728f05 100644
--- a/src/modules/rtp/rtp.h
+++ b/src/modules/rtp/rtp.h
@@ -1,21 +1,21 @@
#ifndef foortphfoo
#define foortphfoo
-/* $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
@@ -35,6 +35,8 @@ typedef struct pa_rtp_context {
uint32_t ssrc;
uint8_t payload;
size_t frame_size;
+
+ pa_memchunk memchunk;
} pa_rtp_context;
pa_rtp_context* pa_rtp_context_init_send(pa_rtp_context *c, int fd, uint32_t ssrc, uint8_t payload, size_t frame_size);
diff --git a/src/modules/rtp/sap.c b/src/modules/rtp/sap.c
index 022c7fa3..5d9b58fa 100644
--- a/src/modules/rtp/sap.c
+++ b/src/modules/rtp/sap.c
@@ -1,18 +1,18 @@
-/* $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
@@ -23,7 +23,6 @@
#include <config.h>
#endif
-#include <assert.h>
#include <time.h>
#include <stdlib.h>
#include <sys/types.h>
@@ -44,6 +43,7 @@
#include <pulsecore/core-error.h>
#include <pulsecore/core-util.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
#include "sap.h"
#include "sdp.h"
@@ -51,25 +51,25 @@
#define MIME_TYPE "application/sdp"
pa_sap_context* pa_sap_context_init_send(pa_sap_context *c, int fd, char *sdp_data) {
- assert(c);
- assert(fd >= 0);
- assert(sdp_data);
+ pa_assert(c);
+ pa_assert(fd >= 0);
+ pa_assert(sdp_data);
c->fd = fd;
c->sdp_data = sdp_data;
c->msg_id_hash = (uint16_t) (rand()*rand());
-
- return c;
+
+ return c;
}
void pa_sap_context_destroy(pa_sap_context *c) {
- assert(c);
+ pa_assert(c);
- close(c->fd);
+ pa_close(c->fd);
pa_xfree(c->sdp_data);
}
-int pa_sap_send(pa_sap_context *c, int goodbye) {
+int pa_sap_send(pa_sap_context *c, pa_bool_t goodbye) {
uint32_t header;
struct sockaddr_storage sa_buf;
struct sockaddr *sa = (struct sockaddr*) &sa_buf;
@@ -83,8 +83,8 @@ int pa_sap_send(pa_sap_context *c, int goodbye) {
return -1;
}
- assert(sa->sa_family == AF_INET || sa->sa_family == AF_INET6);
-
+ pa_assert(sa->sa_family == AF_INET || sa->sa_family == AF_INET6);
+
header = htonl(((uint32_t) 1 << 29) |
(sa->sa_family == AF_INET6 ? (uint32_t) 1 << 28 : 0) |
(goodbye ? (uint32_t) 1 << 26 : 0) |
@@ -101,7 +101,7 @@ int pa_sap_send(pa_sap_context *c, int goodbye) {
iov[3].iov_base = c->sdp_data;
iov[3].iov_len = strlen(c->sdp_data);
-
+
m.msg_name = NULL;
m.msg_namelen = 0;
m.msg_iov = iov;
@@ -109,23 +109,23 @@ int pa_sap_send(pa_sap_context *c, int goodbye) {
m.msg_control = NULL;
m.msg_controllen = 0;
m.msg_flags = 0;
-
+
if ((k = sendmsg(c->fd, &m, MSG_DONTWAIT)) < 0)
- pa_log("sendmsg() failed: %s\n", pa_cstrerror(errno));
+ pa_log_warn("sendmsg() failed: %s\n", pa_cstrerror(errno));
return k;
}
pa_sap_context* pa_sap_context_init_recv(pa_sap_context *c, int fd) {
- assert(c);
- assert(fd >= 0);
+ pa_assert(c);
+ pa_assert(fd >= 0);
c->fd = fd;
c->sdp_data = NULL;
return c;
}
-int pa_sap_recv(pa_sap_context *c, int *goodbye) {
+int pa_sap_recv(pa_sap_context *c, pa_bool_t *goodbye) {
struct msghdr m;
struct iovec iov;
int size, k;
@@ -133,21 +133,18 @@ int pa_sap_recv(pa_sap_context *c, int *goodbye) {
uint32_t header;
int six, ac;
ssize_t r;
-
- assert(c);
- assert(goodbye);
+
+ pa_assert(c);
+ pa_assert(goodbye);
if (ioctl(c->fd, FIONREAD, &size) < 0) {
- pa_log("FIONREAD failed: %s", pa_cstrerror(errno));
+ pa_log_warn("FIONREAD failed: %s", pa_cstrerror(errno));
goto fail;
}
- if (!size)
- return 0;
-
buf = pa_xnew(char, size+1);
buf[size] = 0;
-
+
iov.iov_base = buf;
iov.iov_len = size;
@@ -158,14 +155,14 @@ int pa_sap_recv(pa_sap_context *c, int *goodbye) {
m.msg_control = NULL;
m.msg_controllen = 0;
m.msg_flags = 0;
-
+
if ((r = recvmsg(c->fd, &m, 0)) != size) {
- pa_log("recvmsg() failed: %s", r < 0 ? pa_cstrerror(errno) : "size mismatch");
+ pa_log_warn("recvmsg() failed: %s", r < 0 ? pa_cstrerror(errno) : "size mismatch");
goto fail;
}
if (size < 4) {
- pa_log("SAP packet too short.");
+ pa_log_warn("SAP packet too short.");
goto fail;
}
@@ -173,17 +170,17 @@ int pa_sap_recv(pa_sap_context *c, int *goodbye) {
header = ntohl(header);
if (header >> 29 != 1) {
- pa_log("Unsupported SAP version.");
+ pa_log_warn("Unsupported SAP version.");
goto fail;
}
if ((header >> 25) & 1) {
- pa_log("Encrypted SAP not supported.");
+ pa_log_warn("Encrypted SAP not supported.");
goto fail;
}
if ((header >> 24) & 1) {
- pa_log("Compressed SAP not supported.");
+ pa_log_warn("Compressed SAP not supported.");
goto fail;
}
@@ -192,7 +189,7 @@ int pa_sap_recv(pa_sap_context *c, int *goodbye) {
k = 4 + (six ? 16 : 4) + ac*4;
if (size < k) {
- pa_log("SAP packet too short (AD).");
+ pa_log_warn("SAP packet too short (AD).");
goto fail;
}
@@ -203,18 +200,18 @@ int pa_sap_recv(pa_sap_context *c, int *goodbye) {
e += sizeof(MIME_TYPE);
size -= sizeof(MIME_TYPE);
} else if ((unsigned) size < sizeof(PA_SDP_HEADER)-1 || strncmp(e, PA_SDP_HEADER, sizeof(PA_SDP_HEADER)-1)) {
- pa_log("Invalid SDP header.");
+ pa_log_warn("Invalid SDP header.");
goto fail;
}
if (c->sdp_data)
pa_xfree(c->sdp_data);
-
+
c->sdp_data = pa_xstrndup(e, size);
pa_xfree(buf);
-
+
*goodbye = !!((header >> 26) & 1);
-
+
return 0;
fail:
diff --git a/src/modules/rtp/sap.h b/src/modules/rtp/sap.h
index 987403e3..69c757cb 100644
--- a/src/modules/rtp/sap.h
+++ b/src/modules/rtp/sap.h
@@ -1,21 +1,21 @@
#ifndef foosaphfoo
#define foosaphfoo
-/* $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
@@ -38,9 +38,9 @@ typedef struct pa_sap_context {
pa_sap_context* pa_sap_context_init_send(pa_sap_context *c, int fd, char *sdp_data);
void pa_sap_context_destroy(pa_sap_context *c);
-int pa_sap_send(pa_sap_context *c, int goodbye);
+int pa_sap_send(pa_sap_context *c, pa_bool_t goodbye);
pa_sap_context* pa_sap_context_init_recv(pa_sap_context *c, int fd);
-int pa_sap_recv(pa_sap_context *c, int *goodbye);
+int pa_sap_recv(pa_sap_context *c, pa_bool_t *goodbye);
#endif
diff --git a/src/modules/rtp/sdp.c b/src/modules/rtp/sdp.c
index 1b71a9a0..cef90433 100644
--- a/src/modules/rtp/sdp.c
+++ b/src/modules/rtp/sdp.c
@@ -1,18 +1,18 @@
-/* $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
@@ -23,7 +23,6 @@
#include <config.h>
#endif
-#include <assert.h>
#include <time.h>
#include <stdlib.h>
#include <sys/types.h>
@@ -33,37 +32,34 @@
#include <string.h>
#include <pulse/xmalloc.h>
+#include <pulse/util.h>
#include <pulsecore/core-util.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
#include "sdp.h"
#include "rtp.h"
-
char *pa_sdp_build(int af, const void *src, const void *dst, const char *name, uint16_t port, uint8_t payload, const pa_sample_spec *ss) {
uint32_t ntp;
- char buf_src[64], buf_dst[64];
+ char buf_src[64], buf_dst[64], un[64];
const char *u, *f, *a;
- assert(src);
- assert(dst);
- assert(af == AF_INET || af == AF_INET6);
-
- f = pa_rtp_format_to_string(ss->format);
- assert(f);
-
- if (!(u = getenv("USER")))
- if (!(u = getenv("USERNAME")))
- u = "-";
-
+ pa_assert(src);
+ pa_assert(dst);
+ pa_assert(af == AF_INET || af == AF_INET6);
+
+ pa_assert_se(f = pa_rtp_format_to_string(ss->format));
+
+ if (!(u = pa_get_user_name(un, sizeof(un))))
+ u = "-";
+
ntp = time(NULL) + 2208988800U;
- a = inet_ntop(af, src, buf_src, sizeof(buf_src));
- assert(a);
- a = inet_ntop(af, dst, buf_dst, sizeof(buf_dst));
- assert(a);
-
+ pa_assert_se(a = inet_ntop(af, src, buf_src, sizeof(buf_src)));
+ pa_assert_se(a = inet_ntop(af, dst, buf_dst, sizeof(buf_dst)));
+
return pa_sprintf_malloc(
PA_SDP_HEADER
"o=%s %lu 0 IN %s %s\n"
@@ -84,8 +80,8 @@ char *pa_sdp_build(int af, const void *src, const void *dst, const char *name, u
static pa_sample_spec *parse_sdp_sample_spec(pa_sample_spec *ss, char *c) {
unsigned rate, channels;
- assert(ss);
- assert(c);
+ pa_assert(ss);
+ pa_assert(c);
if (pa_startswith(c, "L16/")) {
ss->format = PA_SAMPLE_S16BE;
@@ -119,15 +115,15 @@ static pa_sample_spec *parse_sdp_sample_spec(pa_sample_spec *ss, char *c) {
pa_sdp_info *pa_sdp_parse(const char *t, pa_sdp_info *i, int is_goodbye) {
uint16_t port = 0;
- int ss_valid = 0;
+ pa_bool_t ss_valid = FALSE;
+
+ pa_assert(t);
+ pa_assert(i);
- assert(t);
- assert(i);
-
i->origin = i->session_name = NULL;
i->salen = 0;
i->payload = 255;
-
+
if (!pa_startswith(t, PA_SDP_HEADER)) {
pa_log("Failed to parse SDP data: invalid header.");
goto fail;
@@ -154,7 +150,7 @@ pa_sdp_info *pa_sdp_parse(const char *t, pa_sdp_info *i, int is_goodbye) {
size_t k;
k = l-8 > sizeof(a) ? sizeof(a) : l-8;
-
+
pa_strlcpy(a, t+9, k);
a[strcspn(a, "/")] = 0;
@@ -171,7 +167,7 @@ pa_sdp_info *pa_sdp_parse(const char *t, pa_sdp_info *i, int is_goodbye) {
size_t k;
k = l-8 > sizeof(a) ? sizeof(a) : l-8;
-
+
pa_strlcpy(a, t+9, k);
a[strcspn(a, "/")] = 0;
@@ -187,7 +183,7 @@ pa_sdp_info *pa_sdp_parse(const char *t, pa_sdp_info *i, int is_goodbye) {
if (i->payload > 127) {
int _port, _payload;
-
+
if (sscanf(t+8, "%i RTP/AVP %i", &_port, &_payload) == 2) {
if (_port <= 0 || _port > 0xFFFF) {
@@ -204,7 +200,7 @@ pa_sdp_info *pa_sdp_parse(const char *t, pa_sdp_info *i, int is_goodbye) {
i->payload = (uint8_t) _payload;
if (pa_rtp_sample_spec_from_payload(i->payload, &i->sample_spec))
- ss_valid = 1;
+ ss_valid = TRUE;
}
}
} else if (pa_startswith(t, "a=rtpmap:")) {
@@ -222,16 +218,16 @@ pa_sdp_info *pa_sdp_parse(const char *t, pa_sdp_info *i, int is_goodbye) {
if (_payload == i->payload) {
c[strcspn(c, "\n")] = 0;
-
+
if (parse_sdp_sample_spec(&i->sample_spec, c))
- ss_valid = 1;
+ ss_valid = TRUE;
}
}
}
}
-
+
t += l;
-
+
if (*t == '\n')
t++;
}
@@ -245,7 +241,7 @@ pa_sdp_info *pa_sdp_parse(const char *t, pa_sdp_info *i, int is_goodbye) {
((struct sockaddr_in*) &i->sa)->sin_port = htons(port);
else
((struct sockaddr_in6*) &i->sa)->sin6_port = htons(port);
-
+
return i;
fail:
@@ -256,7 +252,7 @@ fail:
}
void pa_sdp_info_destroy(pa_sdp_info *i) {
- assert(i);
+ pa_assert(i);
pa_xfree(i->origin);
pa_xfree(i->session_name);
diff --git a/src/modules/rtp/sdp.h b/src/modules/rtp/sdp.h
index b95ca633..933a602b 100644
--- a/src/modules/rtp/sdp.h
+++ b/src/modules/rtp/sdp.h
@@ -1,21 +1,21 @@
#ifndef foosdphfoo
#define foosdphfoo
-/* $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
diff --git a/src/pulse/.gitignore b/src/pulse/.gitignore
new file mode 100644
index 00000000..67020331
--- /dev/null
+++ b/src/pulse/.gitignore
@@ -0,0 +1 @@
+version.h
diff --git a/src/pulse/browser.c b/src/pulse/browser.c
index dae8e3d5..1a3f657f 100644
--- a/src/pulse/browser.c
+++ b/src/pulse/browser.c
@@ -1,29 +1,28 @@
-/* $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
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
-#include <assert.h>
#include <string.h>
#include <avahi-client/lookup.h>
@@ -34,8 +33,9 @@
#include <pulsecore/log.h>
#include <pulsecore/core-util.h>
-
#include <pulsecore/avahi-wrap.h>
+#include <pulsecore/refcnt.h>
+#include <pulsecore/macro.h>
#include "browser.h"
@@ -44,7 +44,8 @@
#define SERVICE_TYPE_SERVER "_pulse-server._tcp."
struct pa_browser {
- int ref;
+ PA_REFCNT_DECLARE;
+
pa_mainloop_api *mainloop;
AvahiPoll* avahi_poll;
@@ -53,13 +54,14 @@ struct pa_browser {
pa_browser_error_cb_t error_callback;
void *error_userdata;
-
+
AvahiClient *client;
AvahiServiceBrowser *server_browser, *sink_browser, *source_browser;
-
+
};
static int map_to_opcode(const char *type, int new) {
+
if (avahi_domain_equal(type, SERVICE_TYPE_SINK))
return new ? PA_BROWSE_NEW_SINK : PA_BROWSE_REMOVE_SINK;
else if (avahi_domain_equal(type, SERVICE_TYPE_SOURCE))
@@ -84,7 +86,7 @@ static void resolve_callback(
AvahiStringList *txt,
AvahiLookupResultFlags flags,
void *userdata) {
-
+
pa_browser *b = userdata;
pa_browse_info i;
char ip[256], a[256];
@@ -94,35 +96,36 @@ static void resolve_callback(
pa_sample_spec ss;
int ss_valid = 0;
char *key = NULL, *value = NULL;
-
- assert(b);
+
+ pa_assert(b);
+ pa_assert(PA_REFCNT_VALUE(b) >= 1);
memset(&i, 0, sizeof(i));
i.name = name;
if (event != AVAHI_RESOLVER_FOUND)
goto fail;
-
+
if (!b->callback)
goto fail;
opcode = map_to_opcode(type, 1);
- assert(opcode >= 0);
+ pa_assert(opcode >= 0);
if (aa->proto == AVAHI_PROTO_INET)
- snprintf(a, sizeof(a), "tcp:%s:%u", avahi_address_snprint(ip, sizeof(ip), aa), port);
+ pa_snprintf(a, sizeof(a), "tcp:%s:%u", avahi_address_snprint(ip, sizeof(ip), aa), port);
else {
- assert(aa->proto == AVAHI_PROTO_INET6);
- snprintf(a, sizeof(a), "tcp6:%s:%u", avahi_address_snprint(ip, sizeof(ip), aa), port);
+ pa_assert(aa->proto == AVAHI_PROTO_INET6);
+ pa_snprintf(a, sizeof(a), "tcp6:%s:%u", avahi_address_snprint(ip, sizeof(ip), aa), port);
}
i.server = a;
while (txt) {
-
+
if (avahi_string_list_get_pair(txt, &key, &value, NULL) < 0)
break;
-
+
if (!strcmp(key, "device")) {
device_found = 1;
pa_xfree((char*) i.device);
@@ -138,20 +141,20 @@ static void resolve_callback(
value = NULL;
} else if (!strcmp(key, "fqdn")) {
size_t l;
-
+
pa_xfree((char*) i.fqdn);
i.fqdn = value;
value = NULL;
-
+
l = strlen(a);
- assert(l+1 <= sizeof(a));
+ pa_assert(l+1 <= sizeof(a));
strncat(a, " ", sizeof(a)-l-1);
strncat(a, i.fqdn, sizeof(a)-l-2);
} else if (!strcmp(key, "cookie")) {
if (pa_atou(value, &cookie) < 0)
goto fail;
-
+
i.cookie = &cookie;
} else if (!strcmp(key, "description")) {
pa_xfree((char*) i.description);
@@ -159,13 +162,13 @@ static void resolve_callback(
value = NULL;
} else if (!strcmp(key, "channels")) {
uint32_t ch;
-
+
if (pa_atou(value, &ch) < 0 || ch <= 0 || ch > 255)
goto fail;
-
+
ss.channels = (uint8_t) ch;
ss_valid |= 1;
-
+
} else if (!strcmp(key, "rate")) {
if (pa_atou(value, &ss.rate) < 0)
goto fail;
@@ -174,7 +177,7 @@ static void resolve_callback(
if ((ss.format = pa_parse_sample_format(value)) == PA_SAMPLE_INVALID)
goto fail;
-
+
ss_valid |= 4;
}
@@ -186,7 +189,7 @@ static void resolve_callback(
}
/* No device txt record was sent for a sink or source service */
- if (opcode != PA_BROWSE_NEW_SERVER && !device_found)
+ if (opcode != PA_BROWSE_NEW_SERVER && !device_found)
goto fail;
if (ss_valid == 7)
@@ -203,13 +206,15 @@ fail:
pa_xfree(key);
pa_xfree(value);
-
+
avahi_service_resolver_free(r);
}
static void handle_failure(pa_browser *b) {
const char *e = NULL;
- assert(b);
+
+ pa_assert(b);
+ pa_assert(PA_REFCNT_VALUE(b) >= 1);
if (b->sink_browser)
avahi_service_browser_free(b->sink_browser);
@@ -243,7 +248,9 @@ static void browse_callback(
void *userdata) {
pa_browser *b = userdata;
- assert(b);
+
+ pa_assert(b);
+ pa_assert(PA_REFCNT_VALUE(b) >= 1);
switch (event) {
case AVAHI_BROWSER_NEW: {
@@ -263,19 +270,19 @@ static void browse_callback(
break;
}
-
+
case AVAHI_BROWSER_REMOVE: {
if (b->callback) {
pa_browse_info i;
int opcode;
-
+
memset(&i, 0, sizeof(i));
i.name = name;
opcode = map_to_opcode(type, 0);
- assert(opcode >= 0);
-
+ pa_assert(opcode >= 0);
+
b->callback(b, opcode, &i, b->userdata);
}
break;
@@ -285,7 +292,7 @@ static void browse_callback(
handle_failure(b);
break;
}
-
+
default:
;
}
@@ -293,7 +300,10 @@ static void browse_callback(
static void client_callback(AvahiClient *s, AvahiClientState state, void *userdata) {
pa_browser *b = userdata;
- assert(s);
+
+ pa_assert(s);
+ pa_assert(b);
+ pa_assert(PA_REFCNT_VALUE(b) >= 1);
if (state == AVAHI_CLIENT_FAILURE)
handle_failure(b);
@@ -301,22 +311,27 @@ static void client_callback(AvahiClient *s, AvahiClientState state, void *userda
static void browser_free(pa_browser *b);
+
+PA_WARN_REFERENCE(pa_browser_new, "libpulse-browse is being phased out.");
+
pa_browser *pa_browser_new(pa_mainloop_api *mainloop) {
return pa_browser_new_full(mainloop, PA_BROWSE_FOR_SERVERS|PA_BROWSE_FOR_SINKS|PA_BROWSE_FOR_SOURCES, NULL);
}
+PA_WARN_REFERENCE(pa_browser_new_full, "libpulse-browse is being phased out.");
+
pa_browser *pa_browser_new_full(pa_mainloop_api *mainloop, pa_browse_flags_t flags, const char **error_string) {
pa_browser *b;
int error;
- assert(mainloop);
+ pa_assert(mainloop);
if (flags & ~(PA_BROWSE_FOR_SERVERS|PA_BROWSE_FOR_SINKS|PA_BROWSE_FOR_SOURCES) || flags == 0)
return NULL;
-
+
b = pa_xnew(pa_browser, 1);
b->mainloop = mainloop;
- b->ref = 1;
+ PA_REFCNT_INIT(b);
b->callback = NULL;
b->userdata = NULL;
b->error_callback = NULL;
@@ -335,7 +350,7 @@ pa_browser *pa_browser_new_full(pa_mainloop_api *mainloop, pa_browse_flags_t fla
!(b->server_browser = avahi_service_browser_new(
b->client,
AVAHI_IF_UNSPEC,
- AVAHI_PROTO_UNSPEC,
+ AVAHI_PROTO_INET,
SERVICE_TYPE_SERVER,
NULL,
0,
@@ -346,7 +361,7 @@ pa_browser *pa_browser_new_full(pa_mainloop_api *mainloop, pa_browse_flags_t fla
*error_string = avahi_strerror(avahi_client_errno(b->client));
goto fail;
}
-
+
if ((flags & PA_BROWSE_FOR_SINKS) &&
!(b->sink_browser = avahi_service_browser_new(
b->client,
@@ -378,18 +393,19 @@ pa_browser *pa_browser_new_full(pa_mainloop_api *mainloop, pa_browse_flags_t fla
*error_string = avahi_strerror(avahi_client_errno(b->client));
goto fail;
}
-
+
return b;
fail:
if (b)
browser_free(b);
-
+
return NULL;
}
static void browser_free(pa_browser *b) {
- assert(b && b->mainloop);
+ pa_assert(b);
+ pa_assert(b->mainloop);
if (b->sink_browser)
avahi_service_browser_free(b->sink_browser);
@@ -403,34 +419,45 @@ static void browser_free(pa_browser *b) {
if (b->avahi_poll)
pa_avahi_poll_free(b->avahi_poll);
-
+
pa_xfree(b);
}
+PA_WARN_REFERENCE(pa_browser_ref, "libpulse-browse is being phased out.");
+
pa_browser *pa_browser_ref(pa_browser *b) {
- assert(b);
- assert(b->ref >= 1);
- b->ref++;
+ pa_assert(b);
+ pa_assert(PA_REFCNT_VALUE(b) >= 1);
+
+ PA_REFCNT_INC(b);
return b;
}
+PA_WARN_REFERENCE(pa_browser_unref, "libpulse-browse is being phased out.");
+
void pa_browser_unref(pa_browser *b) {
- assert(b);
- assert(b->ref >= 1);
+ pa_assert(b);
+ pa_assert(PA_REFCNT_VALUE(b) >= 1);
- if ((-- (b->ref)) <= 0)
+ if (PA_REFCNT_DEC(b) <= 0)
browser_free(b);
}
+PA_WARN_REFERENCE(pa_browser_set_callback, "libpulse-browse is being phased out.");
+
void pa_browser_set_callback(pa_browser *b, pa_browse_cb_t cb, void *userdata) {
- assert(b);
+ pa_assert(b);
+ pa_assert(PA_REFCNT_VALUE(b) >= 1);
b->callback = cb;
b->userdata = userdata;
}
+PA_WARN_REFERENCE(pa_browser_set_error_callback, "libpulse-browse is being phased out.");
+
void pa_browser_set_error_callback(pa_browser *b, pa_browser_error_cb_t cb, void *userdata) {
- assert(b);
+ pa_assert(b);
+ pa_assert(PA_REFCNT_VALUE(b) >= 1);
b->error_callback = cb;
b->error_userdata = userdata;
diff --git a/src/pulse/browser.h b/src/pulse/browser.h
index fc57a4d5..c4e0a17e 100644
--- a/src/pulse/browser.h
+++ b/src/pulse/browser.h
@@ -1,21 +1,21 @@
#ifndef foobrowserhfoo
#define foobrowserhfoo
-/* $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
@@ -41,7 +41,7 @@ typedef enum pa_browse_opcode {
PA_BROWSE_NEW_SINK, /**< New sink found */
PA_BROWSE_NEW_SOURCE, /**< New source found */
PA_BROWSE_REMOVE_SERVER, /**< Server disappeared */
- PA_BROWSE_REMOVE_SINK, /**< Sink disappeared */
+ PA_BROWSE_REMOVE_SINK, /**< Sink disappeared */
PA_BROWSE_REMOVE_SOURCE /**< Source disappeared */
} pa_browse_opcode_t;
diff --git a/src/pulse/cdecl.h b/src/pulse/cdecl.h
index a3ec231c..8c5b2d0f 100644
--- a/src/pulse/cdecl.h
+++ b/src/pulse/cdecl.h
@@ -1,21 +1,21 @@
#ifndef foopulsecdeclhfoo
#define foopulsecdeclhfoo
-/* $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
diff --git a/src/pulse/channelmap.c b/src/pulse/channelmap.c
index 69b09089..7348b32e 100644
--- a/src/pulse/channelmap.c
+++ b/src/pulse/channelmap.c
@@ -1,18 +1,19 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
+ Copyright 2005-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
@@ -24,34 +25,34 @@
#endif
#include <stdlib.h>
-#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <pulse/xmalloc.h>
#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
#include "channelmap.h"
-const char *const table[] = {
+const char *const table[PA_CHANNEL_POSITION_MAX] = {
[PA_CHANNEL_POSITION_MONO] = "mono",
-
+
[PA_CHANNEL_POSITION_FRONT_CENTER] = "front-center",
[PA_CHANNEL_POSITION_FRONT_LEFT] = "front-left",
[PA_CHANNEL_POSITION_FRONT_RIGHT] = "front-right",
-
+
[PA_CHANNEL_POSITION_REAR_CENTER] = "rear-center",
[PA_CHANNEL_POSITION_REAR_LEFT] = "rear-left",
[PA_CHANNEL_POSITION_REAR_RIGHT] = "rear-right",
-
+
[PA_CHANNEL_POSITION_LFE] = "lfe",
-
+
[PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = "front-left-of-center",
[PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = "front-right-of-center",
-
+
[PA_CHANNEL_POSITION_SIDE_LEFT] = "side-left",
[PA_CHANNEL_POSITION_SIDE_RIGHT] = "side-right",
-
+
[PA_CHANNEL_POSITION_AUX0] = "aux0",
[PA_CHANNEL_POSITION_AUX1] = "aux1",
[PA_CHANNEL_POSITION_AUX2] = "aux2",
@@ -86,19 +87,82 @@ const char *const table[] = {
[PA_CHANNEL_POSITION_AUX31] = "aux31",
[PA_CHANNEL_POSITION_TOP_CENTER] = "top-center",
-
+
+ [PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = "top-front-center",
[PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = "top-front-left",
[PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = "top-front-right",
- [PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = "top-front-center",
+ [PA_CHANNEL_POSITION_TOP_REAR_CENTER] = "top-rear-center",
[PA_CHANNEL_POSITION_TOP_REAR_LEFT] = "top-rear-left",
- [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = "top-rear-right",
- [PA_CHANNEL_POSITION_TOP_REAR_CENTER] = "top-rear-center"
+ [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = "top-rear-right"
+};
+
+const char *const pretty_table[PA_CHANNEL_POSITION_MAX] = {
+ [PA_CHANNEL_POSITION_MONO] = "Mono",
+
+ [PA_CHANNEL_POSITION_FRONT_CENTER] = "Front Center",
+ [PA_CHANNEL_POSITION_FRONT_LEFT] = "Front Left",
+ [PA_CHANNEL_POSITION_FRONT_RIGHT] = "Front Right",
+
+ [PA_CHANNEL_POSITION_REAR_CENTER] = "Rear Center",
+ [PA_CHANNEL_POSITION_REAR_LEFT] = "Rear Left",
+ [PA_CHANNEL_POSITION_REAR_RIGHT] = "Rear Right",
+
+ [PA_CHANNEL_POSITION_LFE] = "Low Frequency Emmiter",
+
+ [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = "Front Left-of-center",
+ [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = "Front Right-of-center",
+
+ [PA_CHANNEL_POSITION_SIDE_LEFT] = "Side Left",
+ [PA_CHANNEL_POSITION_SIDE_RIGHT] = "Side Right",
+
+ [PA_CHANNEL_POSITION_AUX0] = "Auxiliary 0",
+ [PA_CHANNEL_POSITION_AUX1] = "Auxiliary 1",
+ [PA_CHANNEL_POSITION_AUX2] = "Auxiliary 2",
+ [PA_CHANNEL_POSITION_AUX3] = "Auxiliary 3",
+ [PA_CHANNEL_POSITION_AUX4] = "Auxiliary 4",
+ [PA_CHANNEL_POSITION_AUX5] = "Auxiliary 5",
+ [PA_CHANNEL_POSITION_AUX6] = "Auxiliary 6",
+ [PA_CHANNEL_POSITION_AUX7] = "Auxiliary 7",
+ [PA_CHANNEL_POSITION_AUX8] = "Auxiliary 8",
+ [PA_CHANNEL_POSITION_AUX9] = "Auxiliary 9",
+ [PA_CHANNEL_POSITION_AUX10] = "Auxiliary 10",
+ [PA_CHANNEL_POSITION_AUX11] = "Auxiliary 11",
+ [PA_CHANNEL_POSITION_AUX12] = "Auxiliary 12",
+ [PA_CHANNEL_POSITION_AUX13] = "Auxiliary 13",
+ [PA_CHANNEL_POSITION_AUX14] = "Auxiliary 14",
+ [PA_CHANNEL_POSITION_AUX15] = "Auxiliary 15",
+ [PA_CHANNEL_POSITION_AUX16] = "Auxiliary 16",
+ [PA_CHANNEL_POSITION_AUX17] = "Auxiliary 17",
+ [PA_CHANNEL_POSITION_AUX18] = "Auxiliary 18",
+ [PA_CHANNEL_POSITION_AUX19] = "Auxiliary 19",
+ [PA_CHANNEL_POSITION_AUX20] = "Auxiliary 20",
+ [PA_CHANNEL_POSITION_AUX21] = "Auxiliary 21",
+ [PA_CHANNEL_POSITION_AUX22] = "Auxiliary 22",
+ [PA_CHANNEL_POSITION_AUX23] = "Auxiliary 23",
+ [PA_CHANNEL_POSITION_AUX24] = "Auxiliary 24",
+ [PA_CHANNEL_POSITION_AUX25] = "Auxiliary 25",
+ [PA_CHANNEL_POSITION_AUX26] = "Auxiliary 26",
+ [PA_CHANNEL_POSITION_AUX27] = "Auxiliary 27",
+ [PA_CHANNEL_POSITION_AUX28] = "Auxiliary 28",
+ [PA_CHANNEL_POSITION_AUX29] = "Auxiliary 29",
+ [PA_CHANNEL_POSITION_AUX30] = "Auxiliary 30",
+ [PA_CHANNEL_POSITION_AUX31] = "Auxiliary 31",
+
+ [PA_CHANNEL_POSITION_TOP_CENTER] = "Top Center",
+
+ [PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = "Top Front Center",
+ [PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = "Top Front Left",
+ [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = "Top Front Right",
+
+ [PA_CHANNEL_POSITION_TOP_REAR_CENTER] = "Top Rear Center",
+ [PA_CHANNEL_POSITION_TOP_REAR_LEFT] = "Top Rear left",
+ [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = "Top Rear Right"
};
pa_channel_map* pa_channel_map_init(pa_channel_map *m) {
unsigned c;
- assert(m);
+ pa_assert(m);
m->channels = 0;
@@ -109,7 +173,7 @@ pa_channel_map* pa_channel_map_init(pa_channel_map *m) {
}
pa_channel_map* pa_channel_map_init_mono(pa_channel_map *m) {
- assert(m);
+ pa_assert(m);
pa_channel_map_init(m);
@@ -119,7 +183,7 @@ pa_channel_map* pa_channel_map_init_mono(pa_channel_map *m) {
}
pa_channel_map* pa_channel_map_init_stereo(pa_channel_map *m) {
- assert(m);
+ pa_assert(m);
pa_channel_map_init(m);
@@ -130,9 +194,9 @@ pa_channel_map* pa_channel_map_init_stereo(pa_channel_map *m) {
}
pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, pa_channel_map_def_t def) {
- assert(m);
- assert(channels > 0);
- assert(channels <= PA_CHANNELS_MAX);
+ pa_assert(m);
+ pa_assert(channels > 0);
+ pa_assert(channels <= PA_CHANNELS_MAX);
pa_channel_map_init(m);
@@ -140,14 +204,14 @@ pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, p
switch (def) {
case PA_CHANNEL_MAP_AIFF:
-
+
/* This is somewhat compatible with RFC3551 */
-
+
switch (channels) {
case 1:
m->map[0] = PA_CHANNEL_POSITION_MONO;
return m;
-
+
case 6:
m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
m->map[1] = PA_CHANNEL_POSITION_SIDE_LEFT;
@@ -156,31 +220,31 @@ pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, p
m->map[4] = PA_CHANNEL_POSITION_SIDE_RIGHT;
m->map[5] = PA_CHANNEL_POSITION_LFE;
return m;
-
+
case 5:
m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;
m->map[3] = PA_CHANNEL_POSITION_REAR_LEFT;
m->map[4] = PA_CHANNEL_POSITION_REAR_RIGHT;
/* Fall through */
-
+
case 2:
m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
return m;
-
+
case 3:
m->map[0] = PA_CHANNEL_POSITION_LEFT;
m->map[1] = PA_CHANNEL_POSITION_RIGHT;
m->map[2] = PA_CHANNEL_POSITION_CENTER;
return m;
-
+
case 4:
m->map[0] = PA_CHANNEL_POSITION_LEFT;
m->map[1] = PA_CHANNEL_POSITION_CENTER;
m->map[2] = PA_CHANNEL_POSITION_RIGHT;
m->map[3] = PA_CHANNEL_POSITION_LFE;
return m;
-
+
default:
return NULL;
}
@@ -191,43 +255,43 @@ pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, p
case 1:
m->map[0] = PA_CHANNEL_POSITION_MONO;
return m;
-
+
case 8:
m->map[6] = PA_CHANNEL_POSITION_SIDE_LEFT;
m->map[7] = PA_CHANNEL_POSITION_SIDE_RIGHT;
/* Fall through */
-
+
case 6:
m->map[5] = PA_CHANNEL_POSITION_LFE;
/* Fall through */
-
+
case 5:
m->map[4] = PA_CHANNEL_POSITION_FRONT_CENTER;
/* Fall through */
-
+
case 4:
m->map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
m->map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
/* Fall through */
-
+
case 2:
m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
return m;
-
+
default:
return NULL;
}
case PA_CHANNEL_MAP_AUX: {
unsigned i;
-
+
if (channels >= PA_CHANNELS_MAX)
return NULL;
for (i = 0; i < channels; i++)
m->map[i] = PA_CHANNEL_POSITION_AUX0 + i;
-
+
return m;
}
@@ -237,55 +301,55 @@ pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, p
case 1:
m->map[0] = PA_CHANNEL_POSITION_MONO;
return m;
-
+
case 18:
m->map[15] = PA_CHANNEL_POSITION_TOP_REAR_LEFT;
m->map[16] = PA_CHANNEL_POSITION_TOP_REAR_CENTER;
m->map[17] = PA_CHANNEL_POSITION_TOP_REAR_RIGHT;
/* Fall through */
-
+
case 15:
m->map[12] = PA_CHANNEL_POSITION_TOP_FRONT_LEFT;
m->map[13] = PA_CHANNEL_POSITION_TOP_FRONT_CENTER;
m->map[14] = PA_CHANNEL_POSITION_TOP_FRONT_RIGHT;
/* Fall through */
-
+
case 12:
m->map[11] = PA_CHANNEL_POSITION_TOP_CENTER;
/* Fall through */
-
+
case 11:
m->map[9] = PA_CHANNEL_POSITION_SIDE_LEFT;
m->map[10] = PA_CHANNEL_POSITION_SIDE_RIGHT;
/* Fall through */
-
+
case 9:
m->map[8] = PA_CHANNEL_POSITION_REAR_CENTER;
/* Fall through */
-
+
case 8:
m->map[6] = PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
m->map[7] = PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
/* Fall through */
-
+
case 6:
m->map[4] = PA_CHANNEL_POSITION_REAR_LEFT;
m->map[5] = PA_CHANNEL_POSITION_REAR_RIGHT;
/* Fall through */
-
+
case 4:
m->map[3] = PA_CHANNEL_POSITION_LFE;
/* Fall through */
-
+
case 3:
m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;
/* Fall through */
-
+
case 2:
m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
return m;
-
+
default:
return NULL;
}
@@ -296,12 +360,12 @@ pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, p
case 1:
m->map[0] = PA_CHANNEL_POSITION_MONO;
return m;
-
+
case 8:
m->map[6] = PA_CHANNEL_POSITION_REAR_LEFT;
m->map[7] = PA_CHANNEL_POSITION_REAR_RIGHT;
/* Fall through */
-
+
case 6:
m->map[4] = PA_CHANNEL_POSITION_SIDE_LEFT;
m->map[5] = PA_CHANNEL_POSITION_SIDE_RIGHT;
@@ -310,26 +374,54 @@ pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, p
case 4:
m->map[3] = PA_CHANNEL_POSITION_LFE;
/* Fall through */
-
+
case 3:
m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;
/* Fall through */
-
+
case 2:
m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
return m;
-
+
default:
return NULL;
}
-
+
default:
return NULL;
}
}
+pa_channel_map* pa_channel_map_init_extend(pa_channel_map *m, unsigned channels, pa_channel_map_def_t def) {
+ unsigned c;
+
+ pa_assert(m);
+ pa_assert(channels > 0);
+ pa_assert(channels <= PA_CHANNELS_MAX);
+
+ pa_channel_map_init(m);
+
+ for (c = channels; c > 0; c--) {
+
+ if (pa_channel_map_init_auto(m, c, def)) {
+ unsigned i = 0;
+
+ for (; c < channels; c++) {
+
+ m->map[c] = PA_CHANNEL_POSITION_AUX0 + i;
+ i++;
+ }
+
+ m->channels = channels;
+
+ return m;
+ }
+ }
+
+ return NULL;
+}
const char* pa_channel_position_to_string(pa_channel_position_t pos) {
@@ -339,15 +431,22 @@ const char* pa_channel_position_to_string(pa_channel_position_t pos) {
return table[pos];
}
+const char* pa_channel_position_to_pretty_string(pa_channel_position_t pos) {
+ if (pos < 0 || pos >= PA_CHANNEL_POSITION_MAX)
+ return NULL;
+
+ return pretty_table[pos];
+}
+
int pa_channel_map_equal(const pa_channel_map *a, const pa_channel_map *b) {
unsigned c;
-
- assert(a);
- assert(b);
+
+ pa_assert(a);
+ pa_assert(b);
if (a->channels != b->channels)
return 0;
-
+
for (c = 0; c < a->channels; c++)
if (a->map[c] != b->map[c])
return 0;
@@ -359,15 +458,15 @@ char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_map *map) {
unsigned channel;
int first = 1;
char *e;
-
- assert(s);
- assert(l > 0);
- assert(map);
+
+ pa_assert(s);
+ pa_assert(l > 0);
+ pa_assert(map);
*(e = s) = 0;
for (channel = 0; channel < map->channels && l > 1; channel++) {
- l -= snprintf(e, l, "%s%s",
+ l -= pa_snprintf(e, l, "%s%s",
first ? "" : ",",
pa_channel_position_to_string(map->map[channel]));
@@ -382,9 +481,9 @@ pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) {
const char *state;
pa_channel_map map;
char *p;
-
- assert(rmap);
- assert(s);
+
+ pa_assert(rmap);
+ pa_assert(s);
memset(&map, 0, sizeof(map));
@@ -397,14 +496,14 @@ pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) {
state = NULL;
map.channels = 0;
-
+
while ((p = pa_split(s, ",", &state))) {
if (map.channels >= PA_CHANNELS_MAX) {
pa_xfree(p);
return NULL;
}
-
+
/* Some special aliases */
if (strcmp(p, "left") == 0)
map.map[map.channels++] = PA_CHANNEL_POSITION_LEFT;
@@ -416,13 +515,13 @@ pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) {
map.map[map.channels++] = PA_CHANNEL_POSITION_SUBWOOFER;
else {
pa_channel_position_t i;
-
+
for (i = 0; i < PA_CHANNEL_POSITION_MAX; i++)
if (strcmp(p, table[i]) == 0) {
map.map[map.channels++] = i;
break;
}
-
+
if (i >= PA_CHANNEL_POSITION_MAX) {
pa_xfree(p);
return NULL;
@@ -433,24 +532,24 @@ pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) {
}
finish:
-
+
if (!pa_channel_map_valid(&map))
return NULL;
-
+
*rmap = map;
return rmap;
}
int pa_channel_map_valid(const pa_channel_map *map) {
unsigned c;
-
- assert(map);
+
+ pa_assert(map);
if (map->channels <= 0 || map->channels > PA_CHANNELS_MAX)
return 0;
for (c = 0; c < map->channels; c++) {
-
+
if (map->map[c] < 0 ||map->map[c] >= PA_CHANNEL_POSITION_MAX)
return 0;
@@ -458,4 +557,3 @@ int pa_channel_map_valid(const pa_channel_map *map) {
return 1;
}
-
diff --git a/src/pulse/channelmap.h b/src/pulse/channelmap.h
index 8a39ade8..2551eae9 100644
--- a/src/pulse/channelmap.h
+++ b/src/pulse/channelmap.h
@@ -1,21 +1,22 @@
#ifndef foochannelmaphfoo
#define foochannelmaphfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
+ Copyright 2005-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
@@ -24,6 +25,7 @@
#include <pulse/sample.h>
#include <pulse/cdecl.h>
+#include <pulse/gccmacro.h>
/** \page channelmap Channel Maps
*
@@ -72,7 +74,7 @@ typedef enum pa_channel_position {
PA_CHANNEL_POSITION_LEFT,
PA_CHANNEL_POSITION_RIGHT,
PA_CHANNEL_POSITION_CENTER,
-
+
PA_CHANNEL_POSITION_FRONT_LEFT = PA_CHANNEL_POSITION_LEFT,
PA_CHANNEL_POSITION_FRONT_RIGHT = PA_CHANNEL_POSITION_RIGHT,
PA_CHANNEL_POSITION_FRONT_CENTER = PA_CHANNEL_POSITION_CENTER,
@@ -80,13 +82,13 @@ typedef enum pa_channel_position {
PA_CHANNEL_POSITION_REAR_CENTER,
PA_CHANNEL_POSITION_REAR_LEFT,
PA_CHANNEL_POSITION_REAR_RIGHT,
-
+
PA_CHANNEL_POSITION_LFE,
PA_CHANNEL_POSITION_SUBWOOFER = PA_CHANNEL_POSITION_LFE,
-
+
PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER,
PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER,
-
+
PA_CHANNEL_POSITION_SIDE_LEFT,
PA_CHANNEL_POSITION_SIDE_RIGHT,
@@ -124,7 +126,7 @@ typedef enum pa_channel_position {
PA_CHANNEL_POSITION_AUX31,
PA_CHANNEL_POSITION_TOP_CENTER,
-
+
PA_CHANNEL_POSITION_TOP_FRONT_LEFT,
PA_CHANNEL_POSITION_TOP_FRONT_RIGHT,
PA_CHANNEL_POSITION_TOP_FRONT_CENTER,
@@ -132,7 +134,7 @@ typedef enum pa_channel_position {
PA_CHANNEL_POSITION_TOP_REAR_LEFT,
PA_CHANNEL_POSITION_TOP_REAR_RIGHT,
PA_CHANNEL_POSITION_TOP_REAR_CENTER,
-
+
PA_CHANNEL_POSITION_MAX
} pa_channel_position_t;
@@ -143,7 +145,7 @@ typedef enum pa_channel_map_def {
PA_CHANNEL_MAP_AUX, /**< Only aux channels */
PA_CHANNEL_MAP_WAVEEX, /**< Microsoft's WAVEFORMATEXTENSIBLE mapping */
PA_CHANNEL_MAP_OSS, /**< The default channel mapping used by OSS as defined in the OSS 4.0 API specs */
-
+
PA_CHANNEL_MAP_DEFAULT = PA_CHANNEL_MAP_AIFF /**< The default channel map */
} pa_channel_map_def_t;
@@ -164,12 +166,23 @@ pa_channel_map* pa_channel_map_init_mono(pa_channel_map *m);
/** Initialize the specified channel map for stereophonic audio and return a pointer to it */
pa_channel_map* pa_channel_map_init_stereo(pa_channel_map *m);
-/** Initialize the specified channel map for the specified number
- * of channels using default labels and return a pointer to it. */
+/** Initialize the specified channel map for the specified number of
+ * channels using default labels and return a pointer to it. This call
+ * will fail (return NULL) if there is no default channel map known for this
+ * specific number of channels and mapping. */
pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, pa_channel_map_def_t def);
+/** Similar to pa_channel_map_init_auto() but instead of failing if no
+ * default mapping is known with the specified parameters it will
+ * synthesize a mapping based on a known mapping with fewer channels
+ * and fill up the rest with AUX0...AUX31 channels \since 0.9.11 */
+pa_channel_map* pa_channel_map_init_extend(pa_channel_map *m, unsigned channels, pa_channel_map_def_t def);
+
/** Return a text label for the specified channel position */
-const char* pa_channel_position_to_string(pa_channel_position_t pos);
+const char* pa_channel_position_to_string(pa_channel_position_t pos) PA_GCC_PURE;
+
+/** Return a human readable text label for the specified channel position. \since 0.9.7 */
+const char* pa_channel_position_to_pretty_string(pa_channel_position_t pos);
/** The maximum length of strings returned by pa_channel_map_snprint() */
#define PA_CHANNEL_MAP_SNPRINT_MAX 336
@@ -177,14 +190,14 @@ const char* pa_channel_position_to_string(pa_channel_position_t pos);
/** Make a humand readable string from the specified channel map */
char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_map *map);
-/** Parse a channel position list into a channel map structure. \since 0.8.1 */
+/** Parse a channel position list into a channel map structure. */
pa_channel_map *pa_channel_map_parse(pa_channel_map *map, const char *s);
/** Compare two channel maps. Return 1 if both match. */
-int pa_channel_map_equal(const pa_channel_map *a, const pa_channel_map *b);
+int pa_channel_map_equal(const pa_channel_map *a, const pa_channel_map *b) PA_GCC_PURE;
/** Return non-zero of the specified channel map is considered valid */
-int pa_channel_map_valid(const pa_channel_map *map);
+int pa_channel_map_valid(const pa_channel_map *map) PA_GCC_PURE;
PA_C_DECL_END
diff --git a/src/pulse/client-conf-x11.c b/src/pulse/client-conf-x11.c
index 8cedc48b..393a7cd3 100644
--- a/src/pulse/client-conf-x11.c
+++ b/src/pulse/client-conf-x11.c
@@ -1,8 +1,8 @@
-/* $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,
@@ -24,7 +24,6 @@
#endif
#include <string.h>
-#include <assert.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
@@ -34,6 +33,7 @@
#include <pulsecore/x11prop.h>
#include <pulsecore/log.h>
#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
#include "client-conf-x11.h"
@@ -42,9 +42,14 @@ int pa_client_conf_from_x11(pa_client_conf *c, const char *dname) {
int ret = -1;
char t[1024];
- if (!dname && !getenv("DISPLAY"))
+ pa_assert(c);
+
+ if (!dname && !(dname = getenv("DISPLAY")))
goto finish;
-
+
+ if (*dname == 0)
+ goto finish;
+
if (!(d = XOpenDisplay(dname))) {
pa_log("XOpenDisplay() failed");
goto finish;
@@ -73,10 +78,10 @@ int pa_client_conf_from_x11(pa_client_conf *c, const char *dname) {
goto finish;
}
- assert(sizeof(cookie) == sizeof(c->cookie));
+ pa_assert(sizeof(cookie) == sizeof(c->cookie));
memcpy(c->cookie, cookie, sizeof(cookie));
- c->cookie_valid = 1;
+ c->cookie_valid = TRUE;
pa_xfree(c->cookie_file);
c->cookie_file = NULL;
@@ -89,5 +94,5 @@ finish:
XCloseDisplay(d);
return ret;
-
+
}
diff --git a/src/pulse/client-conf-x11.h b/src/pulse/client-conf-x11.h
index 02e808be..f2f40e03 100644
--- a/src/pulse/client-conf-x11.h
+++ b/src/pulse/client-conf-x11.h
@@ -1,11 +1,11 @@
#ifndef fooclientconfx11hfoo
#define fooclientconfx11hfoo
-/* $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,
diff --git a/src/pulse/client-conf.c b/src/pulse/client-conf.c
index 5cd7e3ed..2ead871f 100644
--- a/src/pulse/client-conf.c
+++ b/src/pulse/client-conf.c
@@ -1,8 +1,9 @@
-/* $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,
@@ -24,14 +25,14 @@
#endif
#include <stdlib.h>
-#include <assert.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
-#include <pulsecore/core-error.h>
#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-error.h>
#include <pulsecore/log.h>
#include <pulsecore/conf-parser.h>
#include <pulsecore/core-util.h>
@@ -39,13 +40,7 @@
#include "client-conf.h"
-#ifndef OS_IS_WIN32
-# define PATH_SEP "/"
-#else
-# define PATH_SEP "\\"
-#endif
-
-#define DEFAULT_CLIENT_CONFIG_FILE PA_DEFAULT_CONFIG_DIR PATH_SEP "client.conf"
+#define DEFAULT_CLIENT_CONFIG_FILE PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "client.conf"
#define DEFAULT_CLIENT_CONFIG_FILE_USER "client.conf"
#define ENV_CLIENT_CONFIG_FILE "PULSE_CLIENTCONFIG"
@@ -61,24 +56,24 @@ static const pa_client_conf default_conf = {
.default_sink = NULL,
.default_source = NULL,
.default_server = NULL,
- .autospawn = 0,
- .disable_shm = 0,
+ .autospawn = TRUE,
+ .disable_shm = FALSE,
.cookie_file = NULL,
- .cookie_valid = 0,
+ .cookie_valid = FALSE,
};
pa_client_conf *pa_client_conf_new(void) {
pa_client_conf *c = pa_xmemdup(&default_conf, sizeof(default_conf));
-
+
c->daemon_binary = pa_xstrdup(PA_BINARY);
c->extra_arguments = pa_xstrdup("--log-target=syslog --exit-idle-time=5");
c->cookie_file = pa_xstrdup(PA_NATIVE_COOKIE_FILE);
-
+
return c;
}
void pa_client_conf_free(pa_client_conf *c) {
- assert(c);
+ pa_assert(c);
pa_xfree(c->daemon_binary);
pa_xfree(c->extra_arguments);
pa_xfree(c->default_sink);
@@ -87,6 +82,7 @@ void pa_client_conf_free(pa_client_conf *c) {
pa_xfree(c->cookie_file);
pa_xfree(c);
}
+
int pa_client_conf_load(pa_client_conf *c, const char *filename) {
FILE *f = NULL;
char *fn = NULL;
@@ -114,33 +110,39 @@ int pa_client_conf_load(pa_client_conf *c, const char *filename) {
table[6].data = &c->cookie_file;
table[7].data = &c->disable_shm;
- f = filename ?
- fopen((fn = pa_xstrdup(filename)), "r") :
- pa_open_config_file(DEFAULT_CLIENT_CONFIG_FILE, DEFAULT_CLIENT_CONFIG_FILE_USER, ENV_CLIENT_CONFIG_FILE, &fn, "r");
+ if (filename) {
+
+ if (!(f = fopen(filename, "r"))) {
+ pa_log("Failed to open configuration file '%s': %s", fn, pa_cstrerror(errno));
+ goto finish;
+ }
- if (!f && errno != EINTR) {
- pa_log("WARNING: failed to open configuration file '%s': %s", fn, pa_cstrerror(errno));
- goto finish;
+ fn = pa_xstrdup(fn);
+
+ } else {
+
+ if (!(f = pa_open_config_file(DEFAULT_CLIENT_CONFIG_FILE, DEFAULT_CLIENT_CONFIG_FILE_USER, ENV_CLIENT_CONFIG_FILE, &fn)))
+ if (errno != ENOENT)
+ goto finish;
}
-
+
r = f ? pa_config_parse(fn, f, table, NULL) : 0;
if (!r)
r = pa_client_conf_load_cookie(c);
-
finish:
pa_xfree(fn);
if (f)
fclose(f);
-
+
return r;
}
int pa_client_conf_env(pa_client_conf *c) {
char *e;
-
+
if ((e = getenv(ENV_DEFAULT_SINK))) {
pa_xfree(c->default_sink);
c->default_sink = pa_xstrdup(e);
@@ -155,7 +157,7 @@ int pa_client_conf_env(pa_client_conf *c) {
pa_xfree(c->default_server);
c->default_server = pa_xstrdup(e);
}
-
+
if ((e = getenv(ENV_DAEMON_BINARY))) {
pa_xfree(c->daemon_binary);
c->daemon_binary = pa_xstrdup(e);
@@ -167,22 +169,21 @@ int pa_client_conf_env(pa_client_conf *c) {
return pa_client_conf_load_cookie(c);
}
-
+
return 0;
}
int pa_client_conf_load_cookie(pa_client_conf* c) {
- assert(c);
-
- c->cookie_valid = 0;
+ pa_assert(c);
if (!c->cookie_file)
return -1;
+ c->cookie_valid = FALSE;
+
if (pa_authkey_load_auto(c->cookie_file, c->cookie, sizeof(c->cookie)) < 0)
return -1;
- c->cookie_valid = 1;
+ c->cookie_valid = TRUE;
return 0;
}
-
diff --git a/src/pulse/client-conf.h b/src/pulse/client-conf.h
index 35728aeb..699279aa 100644
--- a/src/pulse/client-conf.h
+++ b/src/pulse/client-conf.h
@@ -1,11 +1,11 @@
#ifndef fooclientconfhfoo
#define fooclientconfhfoo
-/* $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,
@@ -28,9 +28,9 @@
typedef struct pa_client_conf {
char *daemon_binary, *extra_arguments, *default_sink, *default_source, *default_server, *cookie_file;
- int autospawn, disable_shm;
+ pa_bool_t autospawn, disable_shm;
uint8_t cookie[PA_NATIVE_COOKIE_LENGTH];
- int cookie_valid; /* non-zero, when cookie is valid */
+ pa_bool_t cookie_valid; /* non-zero, when cookie is valid */
} pa_client_conf;
/* Create a new configuration data object and reset it to defaults */
diff --git a/src/pulse/client.conf.in b/src/pulse/client.conf.in
index 3cfd9760..749e9688 100644
--- a/src/pulse/client.conf.in
+++ b/src/pulse/client.conf.in
@@ -1,5 +1,3 @@
-# $Id$
-#
# This file is part of PulseAudio.
#
# PulseAudio is free software; you can redistribute it and/or modify
@@ -17,29 +15,18 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA.
-## Configuration file for pulseaudio clients. Default values are
-## commented out. Use either ; or # for commenting
-
-## Path to the pulseaudio daemon to run when autospawning.
-; daemon-binary = @PA_BINARY@
-
-## Extra arguments to pass to the pulseaudio daemon
-; extra-arguments = --log-target=syslog --exit-idle-time=5
+## Configuration file for PulseAudio clients. See pulse-client.conf(5) for
+## more information. Default values a commented out. Use either ; or # for
+## commenting.
-## The default sink to connect to
-; default-sink =
-
-## The default source to connect to
+; default-sink =
; default-source =
-
-## The default sever to connect to
; default-server =
-## Autospawn daemons?
-; autospawn = 0
+; autospawn = yes
+; daemon-binary = @PA_BINARY@
+; extra-arguments = --log-target=syslog --exit-idle-time=5
-### Cookie file
-; cookie-file =
+; cookie-file =
-### Disable shared memory data transfer
-; disable-shm = 0
+; disable-shm = no
diff --git a/src/pulse/context.c b/src/pulse/context.c
index a458c6b1..f56cb241 100644
--- a/src/pulse/context.c
+++ b/src/pulse/context.c
@@ -1,18 +1,19 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
+ Copyright 2004-2008 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
@@ -24,7 +25,6 @@
#endif
#include <stdio.h>
-#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
@@ -33,6 +33,7 @@
#include <errno.h>
#include <signal.h>
#include <limits.h>
+#include <locale.h>
#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>
@@ -48,11 +49,13 @@
#include <netdb.h>
#endif
-#include "../pulsecore/winsock.h"
-
-#include <pulsecore/core-error.h>
#include <pulse/version.h>
#include <pulse/xmalloc.h>
+#include <pulse/utf8.h>
+#include <pulse/util.h>
+
+#include <pulsecore/winsock.h>
+#include <pulsecore/core-error.h>
#include <pulsecore/native-common.h>
#include <pulsecore/pdispatch.h>
@@ -64,6 +67,8 @@
#include <pulsecore/log.h>
#include <pulsecore/socket-util.h>
#include <pulsecore/creds.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/proplist-util.h>
#include "internal.h"
@@ -83,17 +88,26 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
[PA_COMMAND_UNDERFLOW] = pa_command_overflow_or_underflow,
[PA_COMMAND_PLAYBACK_STREAM_KILLED] = pa_command_stream_killed,
[PA_COMMAND_RECORD_STREAM_KILLED] = pa_command_stream_killed,
+ [PA_COMMAND_PLAYBACK_STREAM_MOVED] = pa_command_stream_moved,
+ [PA_COMMAND_RECORD_STREAM_MOVED] = pa_command_stream_moved,
+ [PA_COMMAND_PLAYBACK_STREAM_SUSPENDED] = pa_command_stream_suspended,
+ [PA_COMMAND_RECORD_STREAM_SUSPENDED] = pa_command_stream_suspended,
+ [PA_COMMAND_STARTED] = pa_command_stream_started,
[PA_COMMAND_SUBSCRIBE_EVENT] = pa_command_subscribe_event
};
static void unlock_autospawn_lock_file(pa_context *c) {
- assert(c);
-
+ pa_assert(c);
+
if (c->autospawn_lock_fd >= 0) {
- char lf[PATH_MAX];
- pa_runtime_path(AUTOSPAWN_LOCK, lf, sizeof(lf));
-
+ char *lf;
+
+ if (!(lf = pa_runtime_path(AUTOSPAWN_LOCK)))
+ pa_log_warn("Cannot unlock autospawn because runtime path is no more.");
+
pa_unlock_lockfile(lf, c->autospawn_lock_fd);
+ pa_xfree(lf);
+
c->autospawn_lock_fd = -1;
}
}
@@ -101,53 +115,72 @@ static void unlock_autospawn_lock_file(pa_context *c) {
static void context_free(pa_context *c);
pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name) {
+ return pa_context_new_with_proplist(mainloop, name, NULL);
+}
+
+static void reset_callbacks(pa_context *c) {
+ pa_assert(c);
+
+ c->state_callback = NULL;
+ c->state_userdata = NULL;
+
+ c->subscribe_callback = NULL;
+ c->subscribe_userdata = NULL;
+}
+
+pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char *name, pa_proplist *p) {
pa_context *c;
-
- assert(mainloop);
- assert(name);
-
+
+ pa_assert(mainloop);
+
+ if (!name && !pa_proplist_contains(p, PA_PROP_APPLICATION_NAME))
+ return NULL;
+
c = pa_xnew(pa_context, 1);
- c->ref = 1;
- c->name = pa_xstrdup(name);
+ PA_REFCNT_INIT(c);
+
+ c->proplist = p ? pa_proplist_copy(p) : pa_proplist_new();
+
+ if (name)
+ pa_proplist_sets(c->proplist, PA_PROP_APPLICATION_NAME, name);
+
c->mainloop = mainloop;
c->client = NULL;
c->pstream = NULL;
c->pdispatch = NULL;
c->playback_streams = pa_dynarray_new();
c->record_streams = pa_dynarray_new();
+ c->client_index = PA_INVALID_INDEX;
PA_LLIST_HEAD_INIT(pa_stream, c->streams);
PA_LLIST_HEAD_INIT(pa_operation, c->operations);
-
+
c->error = PA_OK;
c->state = PA_CONTEXT_UNCONNECTED;
c->ctag = 0;
c->csyncid = 0;
- c->state_callback = NULL;
- c->state_userdata = NULL;
-
- c->subscribe_callback = NULL;
- c->subscribe_userdata = NULL;
+ reset_callbacks(c);
- c->is_local = -1;
+ c->is_local = FALSE;
c->server_list = NULL;
c->server = NULL;
c->autospawn_lock_fd = -1;
memset(&c->spawn_api, 0, sizeof(c->spawn_api));
- c->do_autospawn = 0;
+ c->do_autospawn = FALSE;
+ c->do_shm = FALSE;
#ifndef MSG_NOSIGNAL
-#ifdef SIGPIPE
+#ifdef SIGPIPE
pa_check_signal_is_blocked(SIGPIPE);
#endif
#endif
c->conf = pa_client_conf_new();
- pa_client_conf_load(c->conf, NULL);
#ifdef HAVE_X11
pa_client_conf_from_x11(c->conf, NULL);
#endif
+ pa_client_conf_load(c->conf, NULL);
pa_client_conf_env(c->conf);
if (!(c->mempool = pa_mempool_new(!c->conf->disable_shm))) {
@@ -164,26 +197,48 @@ pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name) {
return c;
}
-static void context_free(pa_context *c) {
- assert(c);
+static void context_unlink(pa_context *c) {
+ pa_stream *s;
- unlock_autospawn_lock_file(c);
+ pa_assert(c);
+
+ s = c->streams ? pa_stream_ref(c->streams) : NULL;
+ while (s) {
+ pa_stream *n = s->next ? pa_stream_ref(s->next) : NULL;
+ pa_stream_set_state(s, c->state == PA_CONTEXT_FAILED ? PA_STREAM_FAILED : PA_STREAM_TERMINATED);
+ pa_stream_unref(s);
+ s = n;
+ }
while (c->operations)
pa_operation_cancel(c->operations);
- while (c->streams)
- pa_stream_set_state(c->streams, PA_STREAM_TERMINATED);
-
- if (c->client)
- pa_socket_client_unref(c->client);
- if (c->pdispatch)
+ if (c->pdispatch) {
pa_pdispatch_unref(c->pdispatch);
+ c->pdispatch = NULL;
+ }
+
if (c->pstream) {
- pa_pstream_close(c->pstream);
+ pa_pstream_unlink(c->pstream);
pa_pstream_unref(c->pstream);
+ c->pstream = NULL;
+ }
+
+ if (c->client) {
+ pa_socket_client_unref(c->client);
+ c->client = NULL;
}
-
+
+ reset_callbacks(c);
+}
+
+static void context_free(pa_context *c) {
+ pa_assert(c);
+
+ context_unlink(c);
+
+ unlock_autospawn_lock_file(c);
+
if (c->record_streams)
pa_dynarray_free(c->record_streams, NULL, NULL);
if (c->playback_streams)
@@ -196,81 +251,53 @@ static void context_free(pa_context *c) {
pa_client_conf_free(c->conf);
pa_strlist_free(c->server_list);
-
- pa_xfree(c->name);
+
+ if (c->proplist)
+ pa_proplist_free(c->proplist);
+
pa_xfree(c->server);
pa_xfree(c);
}
pa_context* pa_context_ref(pa_context *c) {
- assert(c);
- assert(c->ref >= 1);
-
- c->ref++;
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ PA_REFCNT_INC(c);
return c;
}
void pa_context_unref(pa_context *c) {
- assert(c);
- assert(c->ref >= 1);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
- if (--c->ref <= 0)
+ if (PA_REFCNT_DEC(c) <= 0)
context_free(c);
}
void pa_context_set_state(pa_context *c, pa_context_state_t st) {
- assert(c);
- assert(c->ref >= 1);
-
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
if (c->state == st)
return;
pa_context_ref(c);
c->state = st;
+
if (c->state_callback)
c->state_callback(c, c->state_userdata);
- if (st == PA_CONTEXT_FAILED || st == PA_CONTEXT_TERMINATED) {
- pa_stream *s;
-
- s = c->streams ? pa_stream_ref(c->streams) : NULL;
- while (s) {
- pa_stream *n = s->next ? pa_stream_ref(s->next) : NULL;
- pa_stream_set_state(s, st == PA_CONTEXT_FAILED ? PA_STREAM_FAILED : PA_STREAM_TERMINATED);
- pa_stream_unref(s);
- s = n;
- }
-
- if (c->pdispatch)
- pa_pdispatch_unref(c->pdispatch);
- c->pdispatch = NULL;
-
- if (c->pstream) {
- pa_pstream_close(c->pstream);
- pa_pstream_unref(c->pstream);
- }
- c->pstream = NULL;
-
- if (c->client)
- pa_socket_client_unref(c->client);
- c->client = NULL;
- }
+ if (st == PA_CONTEXT_FAILED || st == PA_CONTEXT_TERMINATED)
+ context_unlink(c);
pa_context_unref(c);
}
-void pa_context_fail(pa_context *c, int error) {
- assert(c);
- assert(c->ref >= 1);
-
- pa_context_set_error(c, error);
- pa_context_set_state(c, PA_CONTEXT_FAILED);
-}
-
int pa_context_set_error(pa_context *c, int error) {
- assert(error >= 0);
- assert(error < PA_ERR_MAX);
+ pa_assert(error >= 0);
+ pa_assert(error < PA_ERR_MAX);
if (c)
c->error = error;
@@ -278,24 +305,32 @@ int pa_context_set_error(pa_context *c, int error) {
return error;
}
+void pa_context_fail(pa_context *c, int error) {
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ pa_context_set_error(c, error);
+ pa_context_set_state(c, PA_CONTEXT_FAILED);
+}
+
static void pstream_die_callback(pa_pstream *p, void *userdata) {
pa_context *c = userdata;
- assert(p);
- assert(c);
-
+ pa_assert(p);
+ pa_assert(c);
+
pa_context_fail(c, PA_ERR_CONNECTIONTERMINATED);
}
static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, const pa_creds *creds, void *userdata) {
pa_context *c = userdata;
-
- assert(p);
- assert(packet);
- assert(c);
+
+ pa_assert(p);
+ pa_assert(packet);
+ pa_assert(c);
pa_context_ref(c);
-
+
if (pa_pdispatch_run(c->pdispatch, packet, creds, c) < 0)
pa_context_fail(c, PA_ERR_PROTOCOL);
@@ -305,23 +340,24 @@ static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, const pa_c
static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk, void *userdata) {
pa_context *c = userdata;
pa_stream *s;
-
- assert(p);
- assert(chunk);
- assert(chunk->memblock);
- assert(chunk->length);
- assert(c);
- assert(c->ref >= 1);
+
+ pa_assert(p);
+ pa_assert(chunk);
+ pa_assert(chunk->memblock);
+ pa_assert(chunk->length);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
pa_context_ref(c);
if ((s = pa_dynarray_get(c->record_streams, channel))) {
- assert(seek == PA_SEEK_RELATIVE && offset == 0);
+ pa_assert(seek == PA_SEEK_RELATIVE);
+ pa_assert(offset == 0);
pa_memblockq_seek(s->record_memblockq, offset, seek);
pa_memblockq_push_align(s->record_memblockq, chunk);
-
+
if (s->read_callback) {
size_t l;
@@ -333,49 +369,62 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o
pa_context_unref(c);
}
-int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t) {
- assert(c);
- assert(c->ref >= 1);
+int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t, pa_bool_t fail) {
+ uint32_t err;
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
if (command == PA_COMMAND_ERROR) {
- assert(t);
-
- if (pa_tagstruct_getu32(t, &c->error) < 0) {
+ pa_assert(t);
+
+ if (pa_tagstruct_getu32(t, &err) < 0) {
pa_context_fail(c, PA_ERR_PROTOCOL);
return -1;
-
}
+
} else if (command == PA_COMMAND_TIMEOUT)
- c->error = PA_ERR_TIMEOUT;
+ err = PA_ERR_TIMEOUT;
else {
pa_context_fail(c, PA_ERR_PROTOCOL);
return -1;
}
+ if (err == PA_OK) {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ return -1;
+ }
+
+ if (err >= PA_ERR_MAX)
+ err = PA_ERR_UNKNOWN;
+
+ if (fail) {
+ pa_context_fail(c, err);
+ return -1;
+ }
+
+ pa_context_set_error(c, err);
+
return 0;
}
static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_context *c = userdata;
-
- assert(pd);
- assert(c);
- assert(c->state == PA_CONTEXT_AUTHORIZING || c->state == PA_CONTEXT_SETTING_NAME);
+
+ pa_assert(pd);
+ pa_assert(c);
+ pa_assert(c->state == PA_CONTEXT_AUTHORIZING || c->state == PA_CONTEXT_SETTING_NAME);
pa_context_ref(c);
-
- if (command != PA_COMMAND_REPLY) {
-
- if (pa_context_handle_error(c, command, t) < 0)
- pa_context_fail(c, PA_ERR_PROTOCOL);
- pa_context_fail(c, c->error);
+ if (command != PA_COMMAND_REPLY) {
+ pa_context_handle_error(c, command, t, TRUE);
goto finish;
}
switch(c->state) {
case PA_CONTEXT_AUTHORIZING: {
pa_tagstruct *reply;
+ pa_bool_t shm_on_remote;
if (pa_tagstruct_getu32(t, &c->version) < 0 ||
!pa_tagstruct_eof(t)) {
@@ -389,25 +438,45 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
goto finish;
}
+ /* Starting with protocol version 13 the MSB of the version
+ tag reflects if shm is available for this connection or
+ not. */
+ if (c->version >= 13) {
+ shm_on_remote = !!(c->version & 0x80000000U);
+ c->version &= 0x7FFFFFFFU;
+ }
+
+ pa_log_debug("Protocol version: remote %u, local %u", c->version, PA_PROTOCOL_VERSION);
+
/* Enable shared memory support if possible */
- if (c->version >= 10 &&
- pa_mempool_is_shared(c->mempool) &&
- c->is_local) {
+ if (c->do_shm)
+ if (c->version < 10 || (c->version >= 13 && !shm_on_remote))
+ c->do_shm = FALSE;
+
+ if (c->do_shm) {
/* Only enable SHM if both sides are owned by the same
* user. This is a security measure because otherwise
* data private to the user might leak. */
-#ifdef HAVE_CREDS
+#ifdef HAVE_CREDS
const pa_creds *creds;
- if ((creds = pa_pdispatch_creds(pd)))
- if (getuid() == creds->uid)
- pa_pstream_use_shm(c->pstream, 1);
+ if (!(creds = pa_pdispatch_creds(pd)) || getuid() != creds->uid)
+ c->do_shm = FALSE;
#endif
}
+ pa_log_debug("Negotiated SHM: %s", pa_yes_no(c->do_shm));
+ pa_pstream_enable_shm(c->pstream, c->do_shm);
+
reply = pa_tagstruct_command(c, PA_COMMAND_SET_CLIENT_NAME, &tag);
- pa_tagstruct_puts(reply, c->name);
+
+ if (c->version >= 13) {
+ pa_init_proplist(c->proplist);
+ pa_tagstruct_put_proplist(reply, c->proplist);
+ } else
+ pa_tagstruct_puts(reply, pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME));
+
pa_pstream_send_tagstruct(c->pstream, reply);
pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, c, NULL);
@@ -416,11 +485,19 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
}
case PA_CONTEXT_SETTING_NAME :
+
+ if ((c->version >= 13 && (pa_tagstruct_getu32(t, &c->client_index) < 0 ||
+ c->client_index == PA_INVALID_INDEX)) ||
+ !pa_tagstruct_eof(t)) {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
pa_context_set_state(c, PA_CONTEXT_READY);
break;
-
+
default:
- assert(0);
+ pa_assert_not_reached();
}
finish:
@@ -430,27 +507,36 @@ finish:
static void setup_context(pa_context *c, pa_iochannel *io) {
pa_tagstruct *t;
uint32_t tag;
-
- assert(c);
- assert(io);
+
+ pa_assert(c);
+ pa_assert(io);
pa_context_ref(c);
-
- assert(!c->pstream);
+
+ pa_assert(!c->pstream);
c->pstream = pa_pstream_new(c->mainloop, io, c->mempool);
pa_pstream_set_die_callback(c->pstream, pstream_die_callback, c);
pa_pstream_set_recieve_packet_callback(c->pstream, pstream_packet_callback, c);
pa_pstream_set_recieve_memblock_callback(c->pstream, pstream_memblock_callback, c);
- assert(!c->pdispatch);
+ pa_assert(!c->pdispatch);
c->pdispatch = pa_pdispatch_new(c->mainloop, command_table, PA_COMMAND_MAX);
if (!c->conf->cookie_valid)
- pa_log_warn("No cookie loaded. Attempting to connect without.");
+ pa_log_info("No cookie loaded. Attempting to connect without.");
t = pa_tagstruct_command(c, PA_COMMAND_AUTH, &tag);
- pa_tagstruct_putu32(t, PA_PROTOCOL_VERSION);
+
+ c->do_shm =
+ pa_mempool_is_shared(c->mempool) &&
+ c->is_local;
+
+ pa_log_debug("SHM possible: %s", pa_yes_no(c->do_shm));
+
+ /* Starting with protocol version 13 we use the MSB of the version
+ * tag for informing the other side if we could do SHM or not */
+ pa_tagstruct_putu32(t, PA_PROTOCOL_VERSION | (c->do_shm ? 0x80000000U : 0));
pa_tagstruct_put_arbitrary(t, c->conf->cookie, sizeof(c->conf->cookie));
#ifdef HAVE_CREDS
@@ -462,13 +548,13 @@ static void setup_context(pa_context *c, pa_iochannel *io) {
ucred.uid = getuid();
ucred.gid = getgid();
-
+
pa_pstream_send_tagstruct_with_creds(c->pstream, t, &ucred);
}
#else
pa_pstream_send_tagstruct(c->pstream, t);
#endif
-
+
pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, c, NULL);
pa_context_set_state(c, PA_CONTEXT_AUTHORIZING);
@@ -486,29 +572,32 @@ static int context_connect_spawn(pa_context *c) {
int fds[2] = { -1, -1} ;
pa_iochannel *io;
+ if (getuid() == 0)
+ return -1;
+
pa_context_ref(c);
-
+
if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) {
- pa_log("socketpair(): %s", pa_cstrerror(errno));
+ pa_log_error("socketpair(): %s", pa_cstrerror(errno));
pa_context_fail(c, PA_ERR_INTERNAL);
goto fail;
}
- pa_fd_set_cloexec(fds[0], 1);
-
- pa_socket_low_delay(fds[0]);
- pa_socket_low_delay(fds[1]);
+ pa_make_fd_cloexec(fds[0]);
+
+ pa_make_socket_low_delay(fds[0]);
+ pa_make_socket_low_delay(fds[1]);
if (c->spawn_api.prefork)
c->spawn_api.prefork();
if ((pid = fork()) < 0) {
- pa_log("fork(): %s", pa_cstrerror(errno));
+ pa_log_error("fork(): %s", pa_cstrerror(errno));
pa_context_fail(c, PA_ERR_INTERNAL);
if (c->spawn_api.postfork)
c->spawn_api.postfork();
-
+
goto fail;
} else if (!pid) {
/* Child */
@@ -518,21 +607,25 @@ static int context_connect_spawn(pa_context *c) {
#define MAX_ARGS 64
const char * argv[MAX_ARGS+1];
int n;
+ char *f;
+
+ pa_close_all(fds[1], -1);
+
+ f = pa_sprintf_malloc("%i", fds[1]);
+ pa_set_env("PULSE_PASSED_FD", f);
+ pa_xfree(f);
- /* Not required, since fds[0] has CLOEXEC enabled anyway */
- close(fds[0]);
-
if (c->spawn_api.atfork)
c->spawn_api.atfork();
/* Setup argv */
n = 0;
-
+
argv[n++] = c->conf->daemon_binary;
argv[n++] = "--daemonize=yes";
-
- snprintf(t, sizeof(t), "-Lmodule-native-protocol-fd fd=%i", fds[1]);
+
+ pa_snprintf(t, sizeof(t), "-Lmodule-native-protocol-fd fd=%i", fds[1]);
argv[n++] = strdup(t);
while (n < MAX_ARGS) {
@@ -540,7 +633,7 @@ static int context_connect_spawn(pa_context *c) {
if (!(a = pa_split_spaces(c->conf->extra_arguments, &state)))
break;
-
+
argv[n++] = a;
}
@@ -549,15 +642,18 @@ static int context_connect_spawn(pa_context *c) {
execv(argv[0], (char * const *) argv);
_exit(1);
#undef MAX_ARGS
- }
+ }
/* Parent */
+ pa_assert_se(pa_close(fds[1]) == 0);
+ fds[1] = -1;
+
r = waitpid(pid, &status, 0);
if (c->spawn_api.postfork)
c->spawn_api.postfork();
-
+
if (r < 0) {
pa_log("waitpid(): %s", pa_cstrerror(errno));
pa_context_fail(c, PA_ERR_INTERNAL);
@@ -567,24 +663,19 @@ static int context_connect_spawn(pa_context *c) {
goto fail;
}
- close(fds[1]);
+ c->is_local = TRUE;
- c->is_local = 1;
-
- io = pa_iochannel_new(c->mainloop, fds[0], fds[0]);
+ unlock_autospawn_lock_file(c);
+ io = pa_iochannel_new(c->mainloop, fds[0], fds[0]);
setup_context(c, io);
- unlock_autospawn_lock_file(c);
pa_context_unref(c);
return 0;
fail:
- if (fds[0] != -1)
- close(fds[0]);
- if (fds[1] != -1)
- close(fds[1]);
+ pa_close_pipe(fds);
unlock_autospawn_lock_file(c);
@@ -598,16 +689,16 @@ fail:
static int try_next_connection(pa_context *c) {
char *u = NULL;
int r = -1;
-
- assert(c);
- assert(!c->client);
+
+ pa_assert(c);
+ pa_assert(!c->client);
for (;;) {
pa_xfree(u);
u = NULL;
-
+
c->server_list = pa_strlist_pop(c->server_list, &u);
-
+
if (!u) {
#ifndef OS_IS_WIN32
@@ -616,20 +707,20 @@ static int try_next_connection(pa_context *c) {
goto finish;
}
#endif
-
+
pa_context_fail(c, PA_ERR_CONNECTIONREFUSED);
goto finish;
}
-
- pa_log_debug("Trying to connect to %s...", u);
+
+ pa_log_debug("Trying to connect to %s...", u);
pa_xfree(c->server);
c->server = pa_xstrdup(u);
if (!(c->client = pa_socket_client_new_string(c->mainloop, u, PA_NATIVE_DEFAULT_PORT)))
continue;
-
- c->is_local = pa_socket_client_is_local(c->client);
+
+ c->is_local = !!pa_socket_client_is_local(c->client);
pa_socket_client_set_callback(c->client, on_connection, c);
break;
}
@@ -638,16 +729,17 @@ static int try_next_connection(pa_context *c) {
finish:
pa_xfree(u);
-
+
return r;
}
static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userdata) {
pa_context *c = userdata;
-
- assert(client);
- assert(c);
- assert(c->state == PA_CONTEXT_CONNECTING);
+ int saved_errno = errno;
+
+ pa_assert(client);
+ pa_assert(c);
+ pa_assert(c->state == PA_CONTEXT_CONNECTING);
pa_context_ref(c);
@@ -656,7 +748,9 @@ static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userd
if (!io) {
/* Try the item in the list */
- if (errno == ECONNREFUSED || errno == ETIMEDOUT || errno == EHOSTUNREACH) {
+ if (saved_errno == ECONNREFUSED ||
+ saved_errno == ETIMEDOUT ||
+ saved_errno == EHOSTUNREACH) {
try_next_connection(c);
goto finish;
}
@@ -672,16 +766,39 @@ finish:
pa_context_unref(c);
}
+
+static char *get_legacy_runtime_dir(void) {
+ char *p, u[128];
+ struct stat st;
+
+ if (!pa_get_user_name(u, sizeof(u)))
+ return NULL;
+
+ p = pa_sprintf_malloc("/tmp/pulse-%s", u);
+
+ if (stat(p, &st) < 0) {
+ pa_xfree(p);
+ return NULL;
+ }
+
+ if (st.st_uid != getuid()) {
+ pa_xfree(p);
+ return NULL;
+ }
+
+ return p;
+}
+
int pa_context_connect(
pa_context *c,
const char *server,
pa_context_flags_t flags,
const pa_spawn_api *api) {
-
+
int r = -1;
-
- assert(c);
- assert(c->ref >= 1);
+
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
PA_CHECK_VALIDITY(c, c->state == PA_CONTEXT_UNCONNECTED, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY(c, !(flags & ~PA_CONTEXT_NOAUTOSPAWN), PA_ERR_INVALID);
@@ -692,19 +809,19 @@ int pa_context_connect(
pa_context_ref(c);
- assert(!c->server_list);
-
+ pa_assert(!c->server_list);
+
if (server) {
if (!(c->server_list = pa_strlist_parse(server))) {
pa_context_fail(c, PA_ERR_INVALIDSERVER);
goto finish;
}
} else {
- char *d;
- char ufn[PATH_MAX];
+ char *d, *ufn;
+ static char *legacy_dir;
/* Prepend in reverse order */
-
+
if ((d = getenv("DISPLAY"))) {
char *e;
d = pa_xstrdup(d);
@@ -716,79 +833,94 @@ int pa_context_connect(
pa_xfree(d);
}
-
+
c->server_list = pa_strlist_prepend(c->server_list, "tcp6:localhost");
c->server_list = pa_strlist_prepend(c->server_list, "tcp4:localhost");
/* The system wide instance */
- c->server_list = pa_strlist_prepend(c->server_list, PA_SYSTEM_RUNTIME_PATH "/" PA_NATIVE_DEFAULT_UNIX_SOCKET);
+ c->server_list = pa_strlist_prepend(c->server_list, PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_NATIVE_DEFAULT_UNIX_SOCKET);
+
+ /* The old per-user instance path. This is supported only to ease upgrades */
+ if ((legacy_dir = get_legacy_runtime_dir())) {
+ char *p = pa_sprintf_malloc("%s" PA_PATH_SEP PA_NATIVE_DEFAULT_UNIX_SOCKET, legacy_dir);
+ c->server_list = pa_strlist_prepend(c->server_list, p);
+ pa_xfree(p);
+ pa_xfree(legacy_dir);
+ }
/* The per-user instance */
- c->server_list = pa_strlist_prepend(c->server_list, pa_runtime_path(PA_NATIVE_DEFAULT_UNIX_SOCKET, ufn, sizeof(ufn)));
+ if ((ufn = pa_runtime_path(PA_NATIVE_DEFAULT_UNIX_SOCKET))) {
+ c->server_list = pa_strlist_prepend(c->server_list, ufn);
+ pa_xfree(ufn);
+ }
/* Wrap the connection attempts in a single transaction for sane autospawn locking */
if (!(flags & PA_CONTEXT_NOAUTOSPAWN) && c->conf->autospawn) {
- char lf[PATH_MAX];
+ char *lf;
+
+ if (!(lf = pa_runtime_path(AUTOSPAWN_LOCK))) {
+ pa_context_fail(c, PA_ERR_ACCESS);
+ goto finish;
+ }
- pa_runtime_path(AUTOSPAWN_LOCK, lf, sizeof(lf));
- pa_make_secure_parent_dir(lf, 0700, (uid_t)-1, (gid_t)-1);
- assert(c->autospawn_lock_fd <= 0);
+ pa_assert(c->autospawn_lock_fd <= 0);
c->autospawn_lock_fd = pa_lock_lockfile(lf);
+ pa_xfree(lf);
if (api)
c->spawn_api = *api;
- c->do_autospawn = 1;
- }
+ c->do_autospawn = TRUE;
+ }
}
pa_context_set_state(c, PA_CONTEXT_CONNECTING);
r = try_next_connection(c);
-
+
finish:
pa_context_unref(c);
-
+
return r;
}
void pa_context_disconnect(pa_context *c) {
- assert(c);
- assert(c->ref >= 1);
-
- pa_context_set_state(c, PA_CONTEXT_TERMINATED);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ if (PA_CONTEXT_IS_GOOD(c->state))
+ pa_context_set_state(c, PA_CONTEXT_TERMINATED);
}
pa_context_state_t pa_context_get_state(pa_context *c) {
- assert(c);
- assert(c->ref >= 1);
-
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
return c->state;
}
int pa_context_errno(pa_context *c) {
- assert(c);
- assert(c->ref >= 1);
-
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
return c->error;
}
void pa_context_set_state_callback(pa_context *c, pa_context_notify_cb_t cb, void *userdata) {
- assert(c);
- assert(c->ref >= 1);
-
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ if (c->state == PA_CONTEXT_TERMINATED || c->state == PA_CONTEXT_FAILED)
+ return;
+
c->state_callback = cb;
c->state_userdata = userdata;
}
int pa_context_is_pending(pa_context *c) {
- assert(c);
- assert(c->ref >= 1);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
- PA_CHECK_VALIDITY(c,
- c->state == PA_CONTEXT_CONNECTING ||
- c->state == PA_CONTEXT_AUTHORIZING ||
- c->state == PA_CONTEXT_SETTING_NAME ||
- c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY(c, PA_CONTEXT_IS_GOOD(c->state), PA_ERR_BADSTATE);
return (c->pstream && pa_pstream_is_pending(c->pstream)) ||
(c->pdispatch && pa_pdispatch_is_pending(c->pdispatch)) ||
@@ -807,16 +939,16 @@ static void pstream_drain_callback(PA_GCC_UNUSED pa_pstream *s, void *userdata)
static void set_dispatch_callbacks(pa_operation *o) {
int done = 1;
-
- assert(o);
- assert(o->ref >= 1);
- assert(o->context);
- assert(o->context->ref >= 1);
- assert(o->context->state == PA_CONTEXT_READY);
+
+ pa_assert(o);
+ pa_assert(PA_REFCNT_VALUE(o) >= 1);
+ pa_assert(o->context);
+ pa_assert(PA_REFCNT_VALUE(o->context) >= 1);
+ pa_assert(o->context->state == PA_CONTEXT_READY);
pa_pstream_set_drain_callback(o->context->pstream, NULL, NULL);
pa_pdispatch_set_drain_callback(o->context->pdispatch, NULL, NULL);
-
+
if (pa_pdispatch_is_pending(o->context->pdispatch)) {
pa_pdispatch_set_drain_callback(o->context->pdispatch, pdispatch_drain_callback, o);
done = 0;
@@ -832,7 +964,7 @@ static void set_dispatch_callbacks(pa_operation *o) {
pa_context_notify_cb_t cb = (pa_context_notify_cb_t) o->callback;
cb(o->context, o->userdata);
}
-
+
pa_operation_done(o);
pa_operation_unref(o);
}
@@ -840,13 +972,13 @@ static void set_dispatch_callbacks(pa_operation *o) {
pa_operation* pa_context_drain(pa_context *c, pa_context_notify_cb_t cb, void *userdata) {
pa_operation *o;
-
- assert(c);
- assert(c->ref >= 1);
+
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(c, pa_context_is_pending(c), PA_ERR_BADSTATE);
-
+
o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
set_dispatch_callbacks(pa_operation_ref(o));
@@ -856,16 +988,16 @@ pa_operation* pa_context_drain(pa_context *c, pa_context_notify_cb_t cb, void *u
void pa_context_simple_ack_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_operation *o = userdata;
int success = 1;
-
- assert(pd);
- assert(o);
- assert(o->ref >= 1);
+
+ pa_assert(pd);
+ pa_assert(o);
+ pa_assert(PA_REFCNT_VALUE(o) >= 1);
if (!o->context)
goto finish;
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(o->context, command, t) < 0)
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
success = 0;
@@ -884,35 +1016,16 @@ finish:
pa_operation_unref(o);
}
-pa_operation* pa_context_exit_daemon(pa_context *c, pa_context_success_cb_t cb, void *userdata) {
+pa_operation* pa_context_send_simple_command(pa_context *c, uint32_t command, pa_pdispatch_cb_t internal_cb, pa_operation_cb_t cb, void *userdata) {
pa_tagstruct *t;
pa_operation *o;
uint32_t tag;
- assert(c);
- assert(c->ref >= 1);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
- o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
-
- t = pa_tagstruct_command(c, PA_COMMAND_EXIT, &tag);
- pa_pstream_send_tagstruct(c->pstream, t);
- pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
-
- return o;
-}
-
-pa_operation* pa_context_send_simple_command(pa_context *c, uint32_t command, pa_pdispatch_cb_t internal_cb, pa_operation_cb_t cb, void *userdata) {
- pa_tagstruct *t;
- pa_operation *o;
- uint32_t tag;
-
- assert(c);
- assert(c->ref >= 1);
-
- PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
-
o = pa_operation_new(c, NULL, cb, userdata);
t = pa_tagstruct_command(c, command, &tag);
@@ -922,18 +1035,24 @@ pa_operation* pa_context_send_simple_command(pa_context *c, uint32_t command, pa
return o;
}
+pa_operation* pa_context_exit_daemon(pa_context *c, pa_context_success_cb_t cb, void *userdata) {
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ return pa_context_send_simple_command(c, PA_COMMAND_EXIT, pa_context_simple_ack_callback, (pa_operation_cb_t) cb, userdata);
+}
+
pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata) {
pa_tagstruct *t;
pa_operation *o;
uint32_t tag;
-
- assert(c);
- assert(c->ref >= 1);
+
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
-
t = pa_tagstruct_command(c, PA_COMMAND_SET_DEFAULT_SINK, &tag);
pa_tagstruct_puts(t, name);
pa_pstream_send_tagstruct(c->pstream, t);
@@ -947,13 +1066,12 @@ pa_operation* pa_context_set_default_source(pa_context *c, const char *name, pa_
pa_operation *o;
uint32_t tag;
- assert(c);
- assert(c->ref >= 1);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
-
- o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
t = pa_tagstruct_command(c, PA_COMMAND_SET_DEFAULT_SOURCE, &tag);
pa_tagstruct_puts(t, name);
pa_pstream_send_tagstruct(c->pstream, t);
@@ -963,28 +1081,39 @@ pa_operation* pa_context_set_default_source(pa_context *c, const char *name, pa_
}
int pa_context_is_local(pa_context *c) {
- assert(c);
-
- return c->is_local;
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_ANY(c, PA_CONTEXT_IS_GOOD(c->state), PA_ERR_BADSTATE, -1);
+
+ return !!c->is_local;
}
pa_operation* pa_context_set_name(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata) {
- pa_tagstruct *t;
pa_operation *o;
- uint32_t tag;
- assert(c);
- assert(c->ref >= 1);
- assert(name);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+ pa_assert(name);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
-
- o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
- t = pa_tagstruct_command(c, PA_COMMAND_SET_CLIENT_NAME, &tag);
- pa_tagstruct_puts(t, name);
- pa_pstream_send_tagstruct(c->pstream, t);
- pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+ if (c->version >= 13) {
+ pa_proplist *p = pa_proplist_new();
+
+ pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, name);
+ o = pa_context_proplist_update(c, PA_UPDATE_REPLACE, p, cb, userdata);
+ pa_proplist_free(p);
+ } else {
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+ t = pa_tagstruct_command(c, PA_COMMAND_SET_CLIENT_NAME, &tag);
+ pa_tagstruct_puts(t, name);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+ }
return o;
}
@@ -994,17 +1123,17 @@ const char* pa_get_library_version(void) {
}
const char* pa_context_get_server(pa_context *c) {
- assert(c);
- assert(c->ref >= 1);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
if (!c->server)
return NULL;
-
+
if (*c->server == '{') {
char *e = strchr(c->server+1, '}');
return e ? e+1 : c->server;
}
-
+
return c->server;
}
@@ -1013,8 +1142,10 @@ uint32_t pa_context_get_protocol_version(PA_GCC_UNUSED pa_context *c) {
}
uint32_t pa_context_get_server_protocol_version(pa_context *c) {
- assert(c);
- assert(c->ref >= 1);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_ANY(c, PA_CONTEXT_IS_GOOD(c->state), PA_ERR_BADSTATE, PA_INVALID_INDEX);
return c->version;
}
@@ -1022,12 +1153,80 @@ uint32_t pa_context_get_server_protocol_version(pa_context *c) {
pa_tagstruct *pa_tagstruct_command(pa_context *c, uint32_t command, uint32_t *tag) {
pa_tagstruct *t;
- assert(c);
- assert(tag);
-
+ pa_assert(c);
+ pa_assert(tag);
+
t = pa_tagstruct_new(NULL, 0);
pa_tagstruct_putu32(t, command);
pa_tagstruct_putu32(t, *tag = c->ctag++);
return t;
}
+
+uint32_t pa_context_get_index(pa_context *c) {
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_ANY(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE, PA_INVALID_INDEX);
+ PA_CHECK_VALIDITY_RETURN_ANY(c, c->version >= 13, PA_ERR_NOTSUPPORTED, PA_INVALID_INDEX);
+
+ return c->client_index;
+}
+
+pa_operation *pa_context_proplist_update(pa_context *c, pa_update_mode_t mode, pa_proplist *p, pa_context_success_cb_t cb, void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, mode == PA_UPDATE_SET || mode == PA_UPDATE_MERGE || mode == PA_UPDATE_REPLACE, PA_ERR_INVALID);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 13, PA_ERR_NOTSUPPORTED);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_UPDATE_CLIENT_PROPLIST, &tag);
+ pa_tagstruct_putu32(t, (uint32_t) mode);
+ pa_tagstruct_put_proplist(t, p);
+
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ /* Please note that we don't update c->proplist here, because we
+ * don't export that field */
+
+ return o;
+}
+
+pa_operation *pa_context_proplist_remove(pa_context *c, const char *const keys[], pa_context_success_cb_t cb, void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+ const char * const *k;
+
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, keys && keys[0], PA_ERR_INVALID);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 13, PA_ERR_NOTSUPPORTED);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_REMOVE_CLIENT_PROPLIST, &tag);
+
+ for (k = keys; *k; k++)
+ pa_tagstruct_puts(t, *k);
+
+ pa_tagstruct_puts(t, NULL);
+
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ /* Please note that we don't update c->proplist here, because we
+ * don't export that field */
+
+ return o;
+}
diff --git a/src/pulse/context.h b/src/pulse/context.h
index 661ff617..8dff7642 100644
--- a/src/pulse/context.h
+++ b/src/pulse/context.h
@@ -1,21 +1,22 @@
#ifndef foocontexthfoo
#define foocontexthfoo
-/* $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
@@ -27,6 +28,7 @@
#include <pulse/mainloop-api.h>
#include <pulse/cdecl.h>
#include <pulse/operation.h>
+#include <pulse/proplist.h>
/** \page async Asynchronous API
*
@@ -50,7 +52,7 @@
* The abstraction is represented as a number of function pointers in the
* pa_mainloop_api structure.
*
- * To actually be able to use these functions, an implementation needs to
+ * To actually be able to use these functions, an implementation needs to
* be coupled to the abstraction. There are three of these shipped with
* PulseAudio, but any other can be used with a minimal ammount of work,
* provided it supports the three basic events listed above.
@@ -76,7 +78,7 @@
* and decrease their reference counts. Whenever an object's reference
* count reaches zero, that object gets destroy and any resources it uses
* get freed.
- *
+ *
* The benefit of this design is that an application need not worry about
* whether or not it needs to keep an object around in case the library is
* using it internally. If it is, then it has made sure it has its own
@@ -163,9 +165,15 @@ typedef void (*pa_context_notify_cb_t)(pa_context *c, void *userdata);
typedef void (*pa_context_success_cb_t) (pa_context *c, int success, void *userdata);
/** Instantiate a new connection context with an abstract mainloop API
- * and an application name */
+ * and an application name. It is recommended to use pa_context_new_with_proplist()
+ * instead and specify some initial properties.*/
pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name);
+/** Instantiate a new connection context with an abstract mainloop API
+ * and an application name, and specify the the initial client property
+ * list. \since 0.9.11 */
+pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char *name, pa_proplist *proplist);
+
/** Decrease the reference counter of the context by one */
void pa_context_unref(pa_context *c);
@@ -204,27 +212,42 @@ pa_operation* pa_context_drain(pa_context *c, pa_context_notify_cb_t cb, void *u
* returning a success notification */
pa_operation* pa_context_exit_daemon(pa_context *c, pa_context_success_cb_t cb, void *userdata);
-/** Set the name of the default sink. \since 0.4 */
+/** Set the name of the default sink. */
pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata);
-/** Set the name of the default source. \since 0.4 */
+/** Set the name of the default source. */
pa_operation* pa_context_set_default_source(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata);
-/** Returns 1 when the connection is to a local daemon. Returns negative when no connection has been made yet. \since 0.5 */
+/** Returns 1 when the connection is to a local daemon. Returns negative when no connection has been made yet. */
int pa_context_is_local(pa_context *c);
-/** Set a different application name for context on the server. \since 0.5 */
+/** Set a different application name for context on the server. */
pa_operation* pa_context_set_name(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata);
-/** Return the server name this context is connected to. \since 0.7 */
+/** Return the server name this context is connected to. */
const char* pa_context_get_server(pa_context *c);
-/** Return the protocol version of the library. \since 0.8 */
+/** Return the protocol version of the library. */
uint32_t pa_context_get_protocol_version(pa_context *c);
-/** Return the protocol version of the connected server. \since 0.8 */
+/** Return the protocol version of the connected server. */
uint32_t pa_context_get_server_protocol_version(pa_context *c);
+/* Update the property list of the client, adding new entries. Please
+ * note that it is highly recommended to set as much properties
+ * initially via pa_context_new_with_proplist() as possible instead a
+ * posteriori with this function, since that information may then be
+ * used to route streams of the client to the right device. \since 0.9.11 */
+pa_operation *pa_context_proplist_update(pa_context *c, pa_update_mode_t mode, pa_proplist *p, pa_context_success_cb_t cb, void *userdata);
+
+/* Update the property list of the client, remove entries. \since 0.9.11 */
+pa_operation *pa_context_proplist_remove(pa_context *c, const char *const keys[], pa_context_success_cb_t cb, void *userdata);
+
+/** Return the client index this context is
+ * identified in the server with. This is useful for usage with the
+ * introspection functions, such as pa_context_get_client_info(). \since 0.9.11 */
+uint32_t pa_context_get_index(pa_context *s);
+
PA_C_DECL_END
#endif
diff --git a/src/pulse/def.h b/src/pulse/def.h
index a22e3c19..a91c1031 100644
--- a/src/pulse/def.h
+++ b/src/pulse/def.h
@@ -1,21 +1,22 @@
#ifndef foodefhfoo
#define foodefhfoo
-/* $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.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
@@ -45,6 +46,15 @@ typedef enum pa_context_state {
PA_CONTEXT_TERMINATED /**< The connection was terminated cleanly */
} pa_context_state_t;
+/** Return non-zero if the passed state is one of the connected states */
+static inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x) {
+ return
+ x == PA_CONTEXT_CONNECTING ||
+ x == PA_CONTEXT_AUTHORIZING ||
+ x == PA_CONTEXT_SETTING_NAME ||
+ x == PA_CONTEXT_READY;
+}
+
/** The state of a stream */
typedef enum pa_stream_state {
PA_STREAM_UNCONNECTED, /**< The stream is not yet connected to any sink or source */
@@ -54,6 +64,13 @@ typedef enum pa_stream_state {
PA_STREAM_TERMINATED /**< The stream has been terminated cleanly */
} pa_stream_state_t;
+/** Return non-zero if the passed state is one of the connected states */
+static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x) {
+ return
+ x == PA_STREAM_CREATING ||
+ x == PA_STREAM_READY;
+}
+
/** The state of an operation */
typedef enum pa_operation_state {
PA_OPERATION_RUNNING, /**< The operation is still running */
@@ -64,12 +81,12 @@ typedef enum pa_operation_state {
/** An invalid index */
#define PA_INVALID_INDEX ((uint32_t) -1)
-/** Some special flags for contexts. \since 0.8 */
+/** Some special flags for contexts. */
typedef enum pa_context_flags {
PA_CONTEXT_NOAUTOSPAWN = 1 /**< Disabled autospawning of the PulseAudio daemon if required */
} pa_context_flags_t;
-/** The direction of a pa_stream object */
+/** The direction of a pa_stream object */
typedef enum pa_stream_direction {
PA_STREAM_NODIRECTION, /**< Invalid direction */
PA_STREAM_PLAYBACK, /**< Playback stream */
@@ -77,7 +94,7 @@ typedef enum pa_stream_direction {
PA_STREAM_UPLOAD /**< Sample upload stream */
} pa_stream_direction_t;
-/** Some special flags for stream connections. \since 0.6 */
+/** Some special flags for stream connections. */
typedef enum pa_stream_flags {
PA_STREAM_START_CORKED = 1, /**< Create the stream corked, requiring an explicit pa_stream_cork() call to uncork it. */
PA_STREAM_INTERPOLATE_TIMING = 2, /**< Interpolate the latency for
@@ -119,7 +136,7 @@ typedef enum pa_stream_flags {
* ahead can be corrected
* quickly, without the need to
* wait. */
- PA_STREAM_AUTO_TIMING_UPDATE = 8 /**< If set timing update requests
+ PA_STREAM_AUTO_TIMING_UPDATE = 8, /**< If set timing update requests
* are issued periodically
* automatically. Combined with
* PA_STREAM_INTERPOLATE_TIMING
@@ -129,15 +146,150 @@ typedef enum pa_stream_flags {
* pa_stream_get_latency() at
* all times without a packet
* round trip.*/
+ PA_STREAM_NO_REMAP_CHANNELS = 16, /**< Don't remap channels by
+ * their name, instead map them
+ * simply by their
+ * index. Implies
+ * PA_STREAM_NO_REMIX_CHANNELS. Only
+ * supported when the server is
+ * at least PA 0.9.8. It is
+ * ignored on older
+ * servers.\since 0.9.8 */
+ PA_STREAM_NO_REMIX_CHANNELS = 32, /**< When remapping channels by
+ * name, don't upmix or downmix
+ * them to related
+ * channels. Copy them into
+ * matching channels of the
+ * device 1:1. Only supported
+ * when the server is at least
+ * PA 0.9.8. It is ignored on
+ * older servers. \since
+ * 0.9.8 */
+ PA_STREAM_FIX_FORMAT = 64, /**< Use the sample format of the
+ * sink/device this stream is being
+ * connected to, and possibly ignore
+ * the format the sample spec contains
+ * -- but you still have to pass a
+ * valid value in it as a hint to
+ * PulseAudio what would suit your
+ * stream best. If this is used you
+ * should query the used sample format
+ * after creating the stream by using
+ * pa_stream_get_sample_spec(). Also,
+ * if you specified manual buffer
+ * metrics it is recommended to update
+ * them with
+ * pa_stream_set_buffer_attr() to
+ * compensate for the changed frame
+ * sizes. Only supported when the
+ * server is at least PA 0.9.8. It is
+ * ignored on older servers. \since
+ * 0.9.8 */
+
+ PA_STREAM_FIX_RATE = 128, /**< Use the sample rate of the sink,
+ * and possibly ignore the rate the
+ * sample spec contains. Usage similar
+ * to PA_STREAM_FIX_FORMAT.Only
+ * supported when the server is at least
+ * PA 0.9.8. It is ignored on older
+ * servers. \since 0.9.8 */
+
+ PA_STREAM_FIX_CHANNELS = 256, /**< Use the number of channels and
+ * the channel map of the sink, and
+ * possibly ignore the number of
+ * channels and the map the sample spec
+ * and the passed channel map
+ * contains. Usage similar to
+ * PA_STREAM_FIX_FORMAT. Only supported
+ * when the server is at least PA
+ * 0.9.8. It is ignored on older
+ * servers. \since 0.9.8 */
+ PA_STREAM_DONT_MOVE = 512, /**< Don't allow moving of this stream to
+ * another sink/device. Useful if you use
+ * any of the PA_STREAM_FIX_ flags and
+ * want to make sure that resampling
+ * never takes place -- which might
+ * happen if the stream is moved to
+ * another sink/source whith a different
+ * sample spec/channel map. Only
+ * supported when the server is at least
+ * PA 0.9.8. It is ignored on older
+ * servers. \since 0.9.8 */
+ PA_STREAM_VARIABLE_RATE = 1024, /**< Allow dynamic changing of the
+ * sampling rate during playback
+ * with
+ * pa_stream_update_sample_rate(). Only
+ * supported when the server is at
+ * least PA 0.9.8. It is ignored
+ * on older servers. \since
+ * 0.9.8 */
+ PA_STREAM_PEAK_DETECT = 2048, /**< Find peaks instead of
+ * resampling. \since 0.9.11 */
+
+ PA_STREAM_START_MUTED = 4096, /**< Create in muted state. \since 0.9.11 */
+
+
+ PA_STREAM_ADJUST_LATENCY = 8192, /**< Try to adjust the latency of
+ * the sink/source based on the
+ * requested buffer metrics and
+ * adjust buffer metrics
+ * accordingly. \since 0.9.11 */
} pa_stream_flags_t;
+
+/** English is an evil language \since 0.9.11 */
+#define PA_STREAM_NOT_MONOTONIC PA_STREAM_NOT_MONOTONOUS
+
/** Playback and record buffer metrics */
typedef struct pa_buffer_attr {
- uint32_t maxlength; /**< Maximum length of the buffer */
- uint32_t tlength; /**< Playback only: target length of the buffer. The server tries to assure that at least tlength bytes are always available in the buffer */
- uint32_t prebuf; /**< Playback only: pre-buffering. The server does not start with playback before at least prebug bytes are available in the buffer */
- uint32_t minreq; /**< Playback only: minimum request. The server does not request less than minreq bytes from the client, instead waints until the buffer is free enough to request more bytes at once */
- uint32_t fragsize; /**< Recording only: fragment size. The server sends data in blocks of fragsize bytes size. Large values deminish interactivity with other operations on the connection context but decrease control overhead. */
+ uint32_t maxlength; /**< Maximum length of the
+ * buffer. Setting this to 0 will
+ * initialize this to the maximum value
+ * supported by server, which is
+ * recommended. */
+ uint32_t tlength; /**< Playback only: target length of the
+ * buffer. The server tries to assure
+ * that at least tlength bytes are always
+ * available in the buffer. It is
+ * recommended to set this to 0, which
+ * will initialize this to a value that
+ * is deemed sensible by the
+ * server. However, this value will
+ * default to something like 2s, i.e. for
+ * applications that have specific
+ * latency requirements this value should
+ * be set to the maximum latency that the
+ * application can deal with. */
+ uint32_t prebuf; /**< Playback only: pre-buffering. The
+ * server does not start with playback
+ * before at least prebug bytes are
+ * available in the buffer. It is
+ * recommended to set this to 0, which
+ * will initialize this to the same value
+ * as tlength, whatever that may be. */
+ uint32_t minreq; /**< Playback only: minimum request. The
+ * server does not request less than
+ * minreq bytes from the client, instead
+ * waits until the buffer is free enough
+ * to request more bytes at once. It is
+ * recommended to set this to 0, which
+ * will initialize this to a value that
+ * is deemed sensible by the server. */
+ uint32_t fragsize; /**< Recording only: fragment size. The
+ * server sends data in blocks of
+ * fragsize bytes size. Large values
+ * deminish interactivity with other
+ * operations on the connection context
+ * but decrease control overhead. It is
+ * recommended to set this to 0, which
+ * will initialize this to a value that
+ * is deemed sensible by the
+ * server. However, this value will
+ * default to something like 2s, i.e. for
+ * applications that have specific
+ * latency requirements this value should
+ * be set to the maximum latency that the
+ * application can deal with. */
} pa_buffer_attr;
/** Error values as used by pa_context_errno(). Use pa_strerror() to convert these values to human readable strings */
@@ -149,7 +301,7 @@ enum {
PA_ERR_EXIST, /**< Entity exists */
PA_ERR_NOENTITY, /**< No such entity */
PA_ERR_CONNECTIONREFUSED, /**< Connection refused */
- PA_ERR_PROTOCOL, /**< Protocol error */
+ PA_ERR_PROTOCOL, /**< Protocol error */
PA_ERR_TIMEOUT, /**< Timeout */
PA_ERR_AUTHKEY, /**< No authorization key */
PA_ERR_INTERNAL, /**< Internal error */
@@ -159,9 +311,10 @@ enum {
PA_ERR_MODINITFAILED, /**< Module initialization failed */
PA_ERR_BADSTATE, /**< Bad state */
PA_ERR_NODATA, /**< No data */
- PA_ERR_VERSION, /**< Incompatible protocol version \since 0.8 */
- PA_ERR_TOOLARGE, /**< Data too large \since 0.8.1 */
+ PA_ERR_VERSION, /**< Incompatible protocol version */
+ PA_ERR_TOOLARGE, /**< Data too large */
PA_ERR_NOTSUPPORTED, /**< Operation not supported \since 0.9.5 */
+ PA_ERR_UNKNOWN, /**< The error code was unknown to the client */
PA_ERR_MAX /**< Not really an error but the first invalid error code */
};
@@ -175,9 +328,9 @@ typedef enum pa_subscription_mask {
PA_SUBSCRIPTION_MASK_MODULE = 16, /**< Module events */
PA_SUBSCRIPTION_MASK_CLIENT = 32, /**< Client events */
PA_SUBSCRIPTION_MASK_SAMPLE_CACHE = 64, /**< Sample cache events */
- PA_SUBSCRIPTION_MASK_SERVER = 128, /**< Other global server changes. \since 0.4 */
- PA_SUBSCRIPTION_MASK_AUTOLOAD = 256, /**< Autoload table events. \since 0.5 */
- PA_SUBSCRIPTION_MASK_ALL = 511 /**< Catch all events \since 0.8 */
+ PA_SUBSCRIPTION_MASK_SERVER = 128, /**< Other global server changes. */
+ PA_SUBSCRIPTION_MASK_AUTOLOAD = 256, /**< Autoload table events. */
+ PA_SUBSCRIPTION_MASK_ALL = 511 /**< Catch all events */
} pa_subscription_mask_t;
/** Subscription event types, as used by pa_context_subscribe() */
@@ -189,8 +342,8 @@ typedef enum pa_subscription_event_type {
PA_SUBSCRIPTION_EVENT_MODULE = 4, /**< Event type: Module */
PA_SUBSCRIPTION_EVENT_CLIENT = 5, /**< Event type: Client */
PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE = 6, /**< Event type: Sample cache item */
- PA_SUBSCRIPTION_EVENT_SERVER = 7, /**< Event type: Global server change, only occuring with PA_SUBSCRIPTION_EVENT_CHANGE. \since 0.4 */
- PA_SUBSCRIPTION_EVENT_AUTOLOAD = 8, /**< Event type: Autoload table changes. \since 0.5 */
+ PA_SUBSCRIPTION_EVENT_SERVER = 7, /**< Event type: Global server change, only occuring with PA_SUBSCRIPTION_EVENT_CHANGE. */
+ PA_SUBSCRIPTION_EVENT_AUTOLOAD = 8, /**< Event type: Autoload table changes. */
PA_SUBSCRIPTION_EVENT_FACILITY_MASK = 15, /**< A mask to extract the event type from an event value */
PA_SUBSCRIPTION_EVENT_NEW = 0, /**< A new object was created */
@@ -207,7 +360,7 @@ typedef enum pa_subscription_event_type {
* total output latency a sample that is written with
* pa_stream_write() takes to be played may be estimated by
* sink_usec+buffer_usec+transport_usec. (where buffer_usec is defined
- * as pa_bytes_to_usec(write_index-read_index)) The output buffer
+ * as pa_bytes_to_usec(write_index-read_index)) The output buffer
* which buffer_usec relates to may be manipulated freely (with
* pa_stream_write()'s seek argument, pa_stream_flush() and friends),
* the buffers sink_usec and source_usec relate to are first-in
@@ -217,7 +370,9 @@ typedef enum pa_subscription_event_type {
* source_usec+buffer_usec+transport_usec-sink_usec. (Take care of
* sign issues!) When connected to a monitor source sink_usec contains
* the latency of the owning sink. The two latency estimations
- * described here are implemented in pa_stream_get_latency().*/
+ * described here are implemented in pa_stream_get_latency(). Please
+ * note that this structure can be extended as part of evolutionary
+ * API updates at any time in any new release.*/
typedef struct pa_timing_info {
struct timeval timestamp; /**< The time when this timing info structure was current */
int synchronized_clocks; /**< Non-zero if the local and the
@@ -226,14 +381,21 @@ typedef struct pa_timing_info {
* detected transport_usec becomes much
* more reliable. However, the code that
* detects synchronized clocks is very
- * limited und unreliable itself. \since
- * 0.5 */
+ * limited und unreliable itself. */
pa_usec_t sink_usec; /**< Time in usecs a sample takes to be played on the sink. For playback streams and record streams connected to a monitor source. */
- pa_usec_t source_usec; /**< Time in usecs a sample takes from being recorded to being delivered to the application. Only for record streams. \since 0.5*/
- pa_usec_t transport_usec; /**< Estimated time in usecs a sample takes to be transferred to/from the daemon. For both playback and record streams. \since 0.5 */
+ pa_usec_t source_usec; /**< Time in usecs a sample takes from being recorded to being delivered to the application. Only for record streams. */
+ pa_usec_t transport_usec; /**< Estimated time in usecs a sample takes to be transferred to/from the daemon. For both playback and record streams. */
- int playing; /**< Non-zero when the stream is currently playing. Only for playback streams. */
+ int playing; /**< Non-zero when the stream is
+ * currently not underrun and data is
+ * being passed on to the device. Only
+ * for playback streams. This field does
+ * not say whether the data is actually
+ * already being played. To determine
+ * this check whether since_underrun
+ * (converted to usec) is larger than
+ * sink_usec.*/
int write_index_corrupt; /**< Non-zero if write_index is not
* up-to-date because a local write
@@ -242,28 +404,40 @@ typedef struct pa_timing_info {
* info was current . Only write
* commands with SEEK_RELATIVE_ON_READ
* and SEEK_RELATIVE_END can corrupt
- * write_index. \since 0.8 */
+ * write_index. */
int64_t write_index; /**< Current write index into the
* playback buffer in bytes. Think twice before
* using this for seeking purposes: it
* might be out of date a the time you
* want to use it. Consider using
- * PA_SEEK_RELATIVE instead. \since
- * 0.8 */
+ * PA_SEEK_RELATIVE instead. */
int read_index_corrupt; /**< Non-zero if read_index is not
* up-to-date because a local pause or
* flush request that corrupted it has
* been issued in the time since this
- * latency info was current. \since 0.8 */
-
+ * latency info was current. */
+
int64_t read_index; /**< Current read index into the
* playback buffer in bytes. Think twice before
* using this for seeking purposes: it
* might be out of date a the time you
* want to use it. Consider using
* PA_SEEK_RELATIVE_ON_READ
- * instead. \since 0.8 */
+ * instead. */
+
+ pa_usec_t configured_sink_usec; /**< The static configured latency for
+ * the sink. \since 0.9.11 */
+ pa_usec_t configured_source_usec; /**< The static configured latency for
+ * the source. \since 0.9.11 */
+
+ int64_t since_underrun; /**< Bytes that were handed to the sink
+ since the last underrun happened, or
+ since playback started again after
+ the last underrun. playing will tell
+ you which case it is. \since
+ 0.9.11 */
+
} pa_timing_info;
/** A structure for the spawn api. This may be used to integrate auto
@@ -272,7 +446,7 @@ typedef struct pa_timing_info {
* waitpid() is used on the child's PID. The spawn routine will not
* block or ignore SIGCHLD signals, since this cannot be done in a
* thread compatible way. You might have to do this in
- * prefork/postfork. \since 0.4 */
+ * prefork/postfork. */
typedef struct pa_spawn_api {
void (*prefork)(void); /**< Is called just before the fork in the parent process. May be NULL. */
void (*postfork)(void); /**< Is called immediately after the fork in the parent process. May be NULL.*/
@@ -285,26 +459,32 @@ typedef struct pa_spawn_api {
* passed to the new process. */
} pa_spawn_api;
-/** Seek type for pa_stream_write(). \since 0.8*/
+/** Seek type for pa_stream_write(). */
typedef enum pa_seek_mode {
PA_SEEK_RELATIVE = 0, /**< Seek relatively to the write index */
- PA_SEEK_ABSOLUTE = 1, /**< Seek relatively to the start of the buffer queue */
+ PA_SEEK_ABSOLUTE = 1, /**< Seek relatively to the start of the buffer queue */
PA_SEEK_RELATIVE_ON_READ = 2, /**< Seek relatively to the read index. */
PA_SEEK_RELATIVE_END = 3 /**< Seek relatively to the current end of the buffer queue. */
} pa_seek_mode_t;
-/** Special sink flags. \since 0.8 */
+/** Special sink flags. */
typedef enum pa_sink_flags {
PA_SINK_HW_VOLUME_CTRL = 1, /**< Supports hardware volume control */
PA_SINK_LATENCY = 2, /**< Supports latency querying */
- PA_SINK_HARDWARE = 4 /**< Is a hardware sink of some kind, in contrast to "virtual"/software sinks \since 0.9.3 */
+ PA_SINK_HARDWARE = 4, /**< Is a hardware sink of some kind, in contrast to "virtual"/software sinks \since 0.9.3 */
+ PA_SINK_NETWORK = 8, /**< Is a networked sink of some kind. \since 0.9.7 */
+ PA_SINK_HW_MUTE_CTRL = 16, /**< Supports hardware mute control \since 0.9.11 */
+ PA_SINK_DECIBEL_VOLUME = 32 /**< Volume can be translated to dB with pa_sw_volume_to_dB() \since 0.9.11 */
} pa_sink_flags_t;
-/** Special source flags. \since 0.8 */
+/** Special source flags. */
typedef enum pa_source_flags {
PA_SOURCE_HW_VOLUME_CTRL = 1, /**< Supports hardware volume control */
PA_SOURCE_LATENCY = 2, /**< Supports latency querying */
- PA_SOURCE_HARDWARE = 4 /**< Is a hardware source of some kind, in contrast to "virtual"/software source \since 0.9.3 */
+ PA_SOURCE_HARDWARE = 4, /**< Is a hardware source of some kind, in contrast to "virtual"/software source \since 0.9.3 */
+ PA_SOURCE_NETWORK = 8, /**< Is a networked sink of some kind. \since 0.9.7 */
+ PA_SOURCE_HW_MUTE_CTRL = 16, /**< Supports hardware mute control \since 0.9.11 */
+ PA_SOURCE_DECIBEL_VOLUME = 32 /**< Volume can be translated to dB with pa_sw_volume_to_dB() \since 0.9.11 */
} pa_source_flags_t;
/** A generic free() like callback prototype */
diff --git a/src/pulse/error.c b/src/pulse/error.c
index 7bd31ead..29690c89 100644
--- a/src/pulse/error.c
+++ b/src/pulse/error.c
@@ -1,18 +1,19 @@
-/* $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
diff --git a/src/pulse/error.h b/src/pulse/error.h
index bfce023c..9f9e3d33 100644
--- a/src/pulse/error.h
+++ b/src/pulse/error.h
@@ -1,21 +1,22 @@
#ifndef fooerrorhfoo
#define fooerrorhfoo
-/* $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
diff --git a/src/pulsecore/gccmacro.h b/src/pulse/gccmacro.h
index 8825700a..e4062033 100644
--- a/src/pulsecore/gccmacro.h
+++ b/src/pulse/gccmacro.h
@@ -1,21 +1,21 @@
#ifndef foopulsegccmacrohfoo
#define foopulsegccmacrohfoo
-/* $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
@@ -50,4 +50,47 @@
#define PA_GCC_UNUSED
#endif
+#ifdef __GNUC__
+#define PA_GCC_DESTRUCTOR __attribute__ ((destructor))
+#else
+/** Call this function when process terminates */
+#define PA_GCC_DESTRUCTOR
+#endif
+
+#ifndef PA_GCC_PURE
+#ifdef __GNUC__
+#define PA_GCC_PURE __attribute__ ((pure))
+#else
+/** This function's return value depends only the arguments list and global state **/
+#define PA_GCC_PURE
+#endif
+#endif
+
+#ifndef PA_GCC_CONST
+#ifdef __GNUC__
+#define PA_GCC_CONST __attribute__ ((const))
+#else
+/** This function's return value depends only the arguments list (stricter version of PA_GCC_PURE) **/
+#define PA_GCC_CONST
+#endif
+#endif
+
+#ifndef PA_GCC_DEPRECATED
+#ifdef __GNUC__
+#define PA_GCC_DEPRECATED __attribute__ ((deprecated))
+#else
+/** This function is deprecated **/
+#define PA_GCC_DEPRECATED
+#endif
+#endif
+
+#ifndef PA_GCC_PACKED
+#ifdef __GNUCC__
+#define PA_GCC_PACKED __attribute__ ((packed))
+#else
+/** Structure shall be packed in memory **/
+#define PA_GCC_PACKED
+#endif
+#endif
+
#endif
diff --git a/src/pulse/glib-mainloop.c b/src/pulse/glib-mainloop.c
index 201b6e23..6ddb0faa 100644
--- a/src/pulse/glib-mainloop.c
+++ b/src/pulse/glib-mainloop.c
@@ -1,18 +1,18 @@
-/* $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
@@ -23,8 +23,6 @@
#include <config.h>
#endif
-#include <assert.h>
-
#include <pulse/xmalloc.h>
#include <pulse/timeval.h>
@@ -69,7 +67,7 @@ struct pa_defer_event {
int dead;
int enabled;
-
+
pa_defer_event_cb_t callback;
void *userdata;
pa_defer_event_destroy_cb_t destroy_callback;
@@ -79,7 +77,7 @@ struct pa_defer_event {
struct pa_glib_mainloop {
GSource source;
-
+
pa_mainloop_api api;
GMainContext *context;
@@ -102,7 +100,7 @@ static void cleanup_io_events(pa_glib_mainloop *g, int force) {
if (!force && g->io_events_please_scan <= 0)
break;
-
+
if (force || e->dead) {
PA_LLIST_REMOVE(pa_io_event, g->io_events, e);
@@ -110,13 +108,13 @@ static void cleanup_io_events(pa_glib_mainloop *g, int force) {
g_assert(g->io_events_please_scan > 0);
g->io_events_please_scan--;
}
-
+
if (e->poll_fd_added)
g_source_remove_poll(&g->source, &e->poll_fd);
-
+
if (e->destroy_callback)
e->destroy_callback(&g->api, e, e->userdata);
-
+
pa_xfree(e);
}
@@ -135,7 +133,7 @@ static void cleanup_time_events(pa_glib_mainloop *g, int force) {
if (!force && g->time_events_please_scan <= 0)
break;
-
+
if (force || e->dead) {
PA_LLIST_REMOVE(pa_time_event, g->time_events, e);
@@ -148,10 +146,10 @@ static void cleanup_time_events(pa_glib_mainloop *g, int force) {
g_assert(g->n_enabled_time_events > 0);
g->n_enabled_time_events--;
}
-
+
if (e->destroy_callback)
e->destroy_callback(&g->api, e, e->userdata);
-
+
pa_xfree(e);
}
@@ -170,7 +168,7 @@ static void cleanup_defer_events(pa_glib_mainloop *g, int force) {
if (!force && g->defer_events_please_scan <= 0)
break;
-
+
if (force || e->dead) {
PA_LLIST_REMOVE(pa_defer_event, g->defer_events, e);
@@ -183,10 +181,10 @@ static void cleanup_defer_events(pa_glib_mainloop *g, int force) {
g_assert(g->n_enabled_defer_events > 0);
g->n_enabled_defer_events--;
}
-
+
if (e->destroy_callback)
e->destroy_callback(&g->api, e, e->userdata);
-
+
pa_xfree(e);
}
@@ -218,7 +216,7 @@ static pa_io_event* glib_io_new(
pa_io_event_flags_t f,
pa_io_event_cb_t cb,
void *userdata) {
-
+
pa_io_event *e;
pa_glib_mainloop *g;
@@ -226,7 +224,7 @@ static pa_io_event* glib_io_new(
g_assert(m->userdata);
g_assert(fd >= 0);
g_assert(cb);
-
+
g = m->userdata;
e = pa_xnew(pa_io_event, 1);
@@ -236,7 +234,7 @@ static pa_io_event* glib_io_new(
e->poll_fd.fd = fd;
e->poll_fd.events = map_flags_to_glib(f);
e->poll_fd.revents = 0;
-
+
e->callback = cb;
e->userdata = userdata;
e->destroy_callback = NULL;
@@ -245,7 +243,7 @@ static pa_io_event* glib_io_new(
g_source_add_poll(&g->source, &e->poll_fd);
e->poll_fd_added = 1;
-
+
return e;
}
@@ -272,7 +270,7 @@ static void glib_io_free(pa_io_event*e) {
static void glib_io_set_destroy(pa_io_event*e, pa_io_event_destroy_cb_t cb) {
g_assert(e);
g_assert(!e->dead);
-
+
e->destroy_callback = cb;
}
@@ -283,14 +281,14 @@ static pa_time_event* glib_time_new(
const struct timeval *tv,
pa_time_event_cb_t cb,
void *userdata) {
-
+
pa_glib_mainloop *g;
pa_time_event *e;
-
+
g_assert(m);
g_assert(m->userdata);
g_assert(cb);
-
+
g = m->userdata;
e = pa_xnew(pa_time_event, 1);
@@ -308,13 +306,13 @@ static pa_time_event* glib_time_new(
g->cached_next_time_event = e;
}
}
-
+
e->callback = cb;
e->userdata = userdata;
e->destroy_callback = NULL;
PA_LLIST_PREPEND(pa_time_event, g->time_events, e);
-
+
return e;
}
@@ -328,12 +326,12 @@ static void glib_time_restart(pa_time_event*e, const struct timeval *tv) {
} else if (!e->enabled && tv)
e->mainloop->n_enabled_time_events++;
- if ((e->enabled = !!tv))
+ if ((e->enabled = !!tv))
e->timeval = *tv;
if (e->mainloop->cached_next_time_event && e->enabled) {
g_assert(e->mainloop->cached_next_time_event->enabled);
-
+
if (pa_timeval_cmp(tv, &e->mainloop->cached_next_time_event->timeval) < 0)
e->mainloop->cached_next_time_event = e;
} else if (e->mainloop->cached_next_time_event == e)
@@ -357,7 +355,7 @@ static void glib_time_free(pa_time_event *e) {
static void glib_time_set_destroy(pa_time_event *e, pa_time_event_destroy_cb_t cb) {
g_assert(e);
g_assert(!e->dead);
-
+
e->destroy_callback = cb;
}
@@ -367,27 +365,27 @@ static pa_defer_event* glib_defer_new(
pa_mainloop_api*m,
pa_defer_event_cb_t cb,
void *userdata) {
-
+
pa_defer_event *e;
pa_glib_mainloop *g;
g_assert(m);
g_assert(m->userdata);
g_assert(cb);
-
+
g = m->userdata;
-
+
e = pa_xnew(pa_defer_event, 1);
e->mainloop = g;
e->dead = 0;
e->enabled = 1;
g->n_enabled_defer_events++;
-
+
e->callback = cb;
e->userdata = userdata;
e->destroy_callback = NULL;
-
+
PA_LLIST_PREPEND(pa_defer_event, g->defer_events, e);
return e;
}
@@ -430,7 +428,7 @@ static void glib_defer_set_destroy(pa_defer_event *e, pa_defer_event_destroy_cb_
static void glib_quit(pa_mainloop_api*a, PA_GCC_UNUSED int retval) {
g_warning("quit() ignored");
-
+
/* NOOP */
}
@@ -440,7 +438,7 @@ static pa_time_event* find_next_time_event(pa_glib_mainloop *g) {
if (g->cached_next_time_event)
return g->cached_next_time_event;
-
+
for (t = g->time_events; t; t = t->next) {
if (t->dead || !t->enabled)
@@ -461,7 +459,7 @@ static pa_time_event* find_next_time_event(pa_glib_mainloop *g) {
static void scan_dead(pa_glib_mainloop *g) {
g_assert(g);
-
+
if (g->io_events_please_scan)
cleanup_io_events(g, 0);
@@ -499,7 +497,7 @@ static gboolean prepare_func(GSource *source, gint *timeout) {
if (pa_timeval_cmp(&t->timeval, &tvnow) <= 0) {
*timeout = 0;
return TRUE;
- }
+ }
usec = pa_timeval_diff(&t->timeval, &tvnow);
*timeout = (gint) (usec / 1000);
} else
@@ -519,10 +517,10 @@ static gboolean check_func(GSource *source) {
pa_time_event *t;
GTimeVal now;
struct timeval tvnow;
-
+
t = find_next_time_event(g);
g_assert(t);
-
+
g_source_get_current_time(source, &now);
tvnow.tv_sec = now.tv_sec;
tvnow.tv_usec = now.tv_usec;
@@ -555,7 +553,7 @@ static gboolean dispatch_func(GSource *source, PA_GCC_UNUSED GSourceFunc callbac
}
g_assert(d);
-
+
d->callback(&g->api, d, d->userdata);
return TRUE;
}
@@ -567,7 +565,7 @@ static gboolean dispatch_func(GSource *source, PA_GCC_UNUSED GSourceFunc callbac
t = find_next_time_event(g);
g_assert(t);
-
+
g_source_get_current_time(source, &now);
tvnow.tv_sec = now.tv_sec;
tvnow.tv_usec = now.tv_usec;
@@ -576,7 +574,7 @@ static gboolean dispatch_func(GSource *source, PA_GCC_UNUSED GSourceFunc callbac
/* Disable time event */
glib_time_restart(t, NULL);
-
+
t->callback(&g->api, t, &t->timeval, t->userdata);
return TRUE;
}
@@ -604,12 +602,12 @@ static const pa_mainloop_api vtable = {
.time_restart = glib_time_restart,
.time_free = glib_time_free,
.time_set_destroy = glib_time_set_destroy,
-
+
.defer_new = glib_defer_new,
.defer_enable = glib_defer_enable,
.defer_free = glib_defer_free,
.defer_set_destroy = glib_defer_set_destroy,
-
+
.quit = glib_quit,
};
@@ -624,10 +622,10 @@ pa_glib_mainloop *pa_glib_mainloop_new(GMainContext *c) {
NULL,
NULL
};
-
+
g = (pa_glib_mainloop*) g_source_new(&source_funcs, sizeof(pa_glib_mainloop));
g_main_context_ref(g->context = c ? c : g_main_context_default());
-
+
g->api = vtable;
g->api.userdata = g;
@@ -639,10 +637,10 @@ pa_glib_mainloop *pa_glib_mainloop_new(GMainContext *c) {
g->io_events_please_scan = g->time_events_please_scan = g->defer_events_please_scan = 0;
g->cached_next_time_event = NULL;
-
+
g_source_attach(&g->source, g->context);
g_source_set_can_recurse(&g->source, FALSE);
-
+
return g;
}
@@ -660,6 +658,6 @@ void pa_glib_mainloop_free(pa_glib_mainloop* g) {
pa_mainloop_api* pa_glib_mainloop_get_api(pa_glib_mainloop *g) {
g_assert(g);
-
+
return &g->api;
}
diff --git a/src/pulse/glib-mainloop.h b/src/pulse/glib-mainloop.h
index af7cc0e9..60fd61a3 100644
--- a/src/pulse/glib-mainloop.h
+++ b/src/pulse/glib-mainloop.h
@@ -1,21 +1,22 @@
#ifndef fooglibmainloophfoo
#define fooglibmainloophfoo
-/* $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
diff --git a/src/pulse/internal.h b/src/pulse/internal.h
index 76d80d83..9ed541d1 100644
--- a/src/pulse/internal.h
+++ b/src/pulse/internal.h
@@ -1,21 +1,22 @@
#ifndef foointernalhfoo
#define foointernalhfoo
-/* $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
@@ -38,15 +39,17 @@
#include <pulsecore/mcalign.h>
#include <pulsecore/memblockq.h>
#include <pulsecore/hashmap.h>
+#include <pulsecore/refcnt.h>
+#include <pulsecore/time-smoother.h>
#include "client-conf.h"
#define DEFAULT_TIMEOUT (30)
struct pa_context {
- int ref;
-
- char *name;
+ PA_REFCNT_DECLARE;
+
+ pa_proplist *proplist;
pa_mainloop_api* mainloop;
pa_socket_client *client;
@@ -65,66 +68,80 @@ struct pa_context {
pa_context_notify_cb_t state_callback;
void *state_userdata;
-
pa_context_subscribe_cb_t subscribe_callback;
void *subscribe_userdata;
pa_mempool *mempool;
- int is_local;
- int do_autospawn;
+ pa_bool_t is_local:1;
+ pa_bool_t do_autospawn:1;
+ pa_bool_t do_shm:1;
int autospawn_lock_fd;
pa_spawn_api spawn_api;
-
+
pa_strlist *server_list;
char *server;
pa_client_conf *conf;
+
+ uint32_t client_index;
};
-#define PA_MAX_WRITE_INDEX_CORRECTIONS 10
+#define PA_MAX_WRITE_INDEX_CORRECTIONS 32
typedef struct pa_index_correction {
uint32_t tag;
- int valid;
int64_t value;
- int absolute, corrupt;
+ pa_bool_t valid:1;
+ pa_bool_t absolute:1;
+ pa_bool_t corrupt:1;
} pa_index_correction;
struct pa_stream {
- int ref;
+ PA_REFCNT_DECLARE;
+ PA_LLIST_FIELDS(pa_stream);
+
pa_context *context;
pa_mainloop_api *mainloop;
- PA_LLIST_FIELDS(pa_stream);
- char *name;
- pa_buffer_attr buffer_attr;
+ uint32_t direct_on_input;
+
+ pa_stream_direction_t direction;
+ pa_stream_state_t state;
+ pa_stream_flags_t flags;
+
pa_sample_spec sample_spec;
pa_channel_map channel_map;
- pa_stream_flags_t flags;
+
+ pa_proplist *proplist;
+
+ pa_bool_t channel_valid:1;
+ pa_bool_t suspended:1;
+ pa_bool_t corked:1;
+ pa_bool_t timing_info_valid:1;
+ pa_bool_t auto_timing_update_requested:1;
+
uint32_t channel;
uint32_t syncid;
- int channel_valid;
- uint32_t device_index;
- pa_stream_direction_t direction;
- pa_stream_state_t state;
-
+ uint32_t stream_index;
+
uint32_t requested_bytes;
+ pa_buffer_attr buffer_attr;
+
+ uint32_t device_index;
+ char *device_name;
pa_memchunk peek_memchunk;
void *peek_data;
pa_memblockq *record_memblockq;
- int corked;
-
/* Store latest latency info */
pa_timing_info timing_info;
- int timing_info_valid;
-
+
/* Use to make sure that time advances monotonically */
pa_usec_t previous_time;
-
+
/* time updates with tags older than these are invalid */
uint32_t write_index_not_before;
uint32_t read_index_not_before;
@@ -135,10 +152,8 @@ struct pa_stream {
/* Latency interpolation stuff */
pa_time_event *auto_timing_update_event;
- int auto_timing_update_requested;
-
- pa_usec_t cached_time;
- int cached_time_valid;
+
+ pa_smoother *smoother;
/* Callbacks */
pa_stream_notify_cb_t state_callback;
@@ -153,27 +168,38 @@ struct pa_stream {
void *underflow_userdata;
pa_stream_notify_cb_t latency_update_callback;
void *latency_update_userdata;
+ pa_stream_notify_cb_t moved_callback;
+ void *moved_userdata;
+ pa_stream_notify_cb_t suspended_callback;
+ void *suspended_userdata;
+ pa_stream_notify_cb_t started_callback;
+ void *started_userdata;
};
typedef void (*pa_operation_cb_t)(void);
struct pa_operation {
- int ref;
+ PA_REFCNT_DECLARE;
+
pa_context *context;
pa_stream *stream;
-
+
PA_LLIST_FIELDS(pa_operation);
pa_operation_state_t state;
void *userdata;
pa_operation_cb_t callback;
+
+ void *private; /* some operations might need this */
};
void pa_command_request(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
void pa_command_stream_killed(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
void pa_command_subscribe_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
void pa_command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
-
+void pa_command_stream_suspended(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+void pa_command_stream_moved(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+void pa_command_stream_started(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t callback, void *userdata);
void pa_operation_done(pa_operation *o);
@@ -185,7 +211,7 @@ void pa_stream_simple_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t
void pa_context_fail(pa_context *c, int error);
int pa_context_set_error(pa_context *c, int error);
void pa_context_set_state(pa_context *c, pa_context_state_t st);
-int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t);
+int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t, pa_bool_t fail);
pa_operation* pa_context_send_simple_command(pa_context *c, uint32_t command, void (*internal_callback)(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata), void (*cb)(void), void *userdata);
void pa_stream_set_state(pa_stream *s, pa_stream_state_t st);
@@ -207,5 +233,4 @@ pa_tagstruct *pa_tagstruct_command(pa_context *c, uint32_t command, uint32_t *ta
#define PA_CHECK_VALIDITY_RETURN_NULL(context, expression, error) PA_CHECK_VALIDITY_RETURN_ANY(context, expression, error, NULL)
-
#endif
diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c
index b926e4e4..4be2c62a 100644
--- a/src/pulse/introspect.c
+++ b/src/pulse/introspect.c
@@ -1,18 +1,19 @@
-/* $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
@@ -23,11 +24,10 @@
#include <config.h>
#endif
-#include <assert.h>
-
#include <pulse/context.h>
+#include <pulse/gccmacro.h>
-#include <pulsecore/gccmacro.h>
+#include <pulsecore/macro.h>
#include <pulsecore/pstream-util.h>
#include "internal.h"
@@ -39,16 +39,18 @@
static void context_stat_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_operation *o = userdata;
pa_stat_info i, *p = &i;
-
- assert(pd);
- assert(o);
- assert(o->ref >= 1);
+
+ pa_assert(pd);
+ pa_assert(o);
+ pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+ memset(&i, 0, sizeof(i));
if (!o->context)
goto finish;
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(o->context, command, t) < 0)
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
p = NULL;
@@ -56,8 +58,7 @@ static void context_stat_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNU
pa_tagstruct_getu32(t, &i.memblock_total_size) < 0 ||
pa_tagstruct_getu32(t, &i.memblock_allocated) < 0 ||
pa_tagstruct_getu32(t, &i.memblock_allocated_size) < 0 ||
- pa_tagstruct_getu32(t, &i.scache_size) < 0 ||
- !pa_tagstruct_eof(t)) {
+ pa_tagstruct_getu32(t, &i.scache_size) < 0) {
pa_context_fail(o->context, PA_ERR_PROTOCOL);
goto finish;
}
@@ -81,16 +82,18 @@ pa_operation* pa_context_stat(pa_context *c, pa_stat_info_cb_t cb, void *userdat
static void context_get_server_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_operation *o = userdata;
pa_server_info i, *p = &i;
-
- assert(pd);
- assert(o);
- assert(o->ref >= 1);
-
+
+ pa_assert(pd);
+ pa_assert(o);
+ pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+ memset(&i, 0, sizeof(i));
+
if (!o->context)
goto finish;
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(o->context, command, t) < 0)
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
p = NULL;
@@ -107,7 +110,7 @@ static void context_get_server_info_callback(pa_pdispatch *pd, uint32_t command,
pa_context_fail(o->context, PA_ERR_PROTOCOL);
goto finish;
}
-
+
if (o->callback) {
pa_server_info_cb_t cb = (pa_server_info_cb_t) o->callback;
cb(o->context, p, o->userdata);
@@ -127,25 +130,29 @@ pa_operation* pa_context_get_server_info(pa_context *c, pa_server_info_cb_t cb,
static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_operation *o = userdata;
int eol = 1;
-
- assert(pd);
- assert(o);
- assert(o->ref >= 1);
+
+ pa_assert(pd);
+ pa_assert(o);
+ pa_assert(PA_REFCNT_VALUE(o) >= 1);
if (!o->context)
goto finish;
-
+
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(o->context, command, t) < 0)
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
eol = -1;
} else {
uint32_t flags;
-
+
while (!pa_tagstruct_eof(t)) {
pa_sink_info i;
-
+ pa_bool_t mute = FALSE;
+
+ memset(&i, 0, sizeof(i));
+ i.proplist = pa_proplist_new();
+
if (pa_tagstruct_getu32(t, &i.index) < 0 ||
pa_tagstruct_gets(t, &i.name) < 0 ||
pa_tagstruct_gets(t, &i.description) < 0 ||
@@ -153,26 +160,33 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, P
pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 ||
pa_tagstruct_getu32(t, &i.owner_module) < 0 ||
pa_tagstruct_get_cvolume(t, &i.volume) < 0 ||
- pa_tagstruct_get_boolean(t, &i.mute) < 0 ||
+ pa_tagstruct_get_boolean(t, &mute) < 0 ||
pa_tagstruct_getu32(t, &i.monitor_source) < 0 ||
pa_tagstruct_gets(t, &i.monitor_source_name) < 0 ||
pa_tagstruct_get_usec(t, &i.latency) < 0 ||
pa_tagstruct_gets(t, &i.driver) < 0 ||
- pa_tagstruct_getu32(t, &flags) < 0) {
-
+ pa_tagstruct_getu32(t, &flags) < 0 ||
+ (o->context->version >= 13 &&
+ (pa_tagstruct_get_proplist(t, i.proplist) < 0 ||
+ pa_tagstruct_get_usec(t, &i.configured_latency) < 0))) {
+
pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ pa_proplist_free(i.proplist);
goto finish;
}
+ i.mute = (int) mute;
i.flags = (pa_sink_flags_t) flags;
if (o->callback) {
pa_sink_info_cb_t cb = (pa_sink_info_cb_t) o->callback;
cb(o->context, &i, 0, o->userdata);
}
+
+ pa_proplist_free(i.proplist);
}
}
-
+
if (o->callback) {
pa_sink_info_cb_t cb = (pa_sink_info_cb_t) o->callback;
cb(o->context, NULL, eol, o->userdata);
@@ -191,10 +205,10 @@ pa_operation* pa_context_get_sink_info_by_index(pa_context *c, uint32_t idx, pa_
pa_tagstruct *t;
pa_operation *o;
uint32_t tag;
-
- assert(c);
- assert(c->ref >= 1);
- assert(cb);
+
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+ pa_assert(cb);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
@@ -213,10 +227,10 @@ pa_operation* pa_context_get_sink_info_by_name(pa_context *c, const char *name,
pa_tagstruct *t;
pa_operation *o;
uint32_t tag;
-
- assert(c);
- assert(c->ref >= 1);
- assert(cb);
+
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+ pa_assert(cb);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID);
@@ -238,24 +252,28 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command,
pa_operation *o = userdata;
int eol = 1;
- assert(pd);
- assert(o);
- assert(o->ref >= 1);
+ pa_assert(pd);
+ pa_assert(o);
+ pa_assert(PA_REFCNT_VALUE(o) >= 1);
if (!o->context)
goto finish;
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(o->context, command, t) < 0)
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
eol = -1;
} else {
-
+
while (!pa_tagstruct_eof(t)) {
pa_source_info i;
uint32_t flags;
-
+ pa_bool_t mute = FALSE;
+
+ memset(&i, 0, sizeof(i));
+ i.proplist = pa_proplist_new();
+
if (pa_tagstruct_getu32(t, &i.index) < 0 ||
pa_tagstruct_gets(t, &i.name) < 0 ||
pa_tagstruct_gets(t, &i.description) < 0 ||
@@ -263,26 +281,33 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command,
pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 ||
pa_tagstruct_getu32(t, &i.owner_module) < 0 ||
pa_tagstruct_get_cvolume(t, &i.volume) < 0 ||
- pa_tagstruct_get_boolean(t, &i.mute) < 0 ||
+ pa_tagstruct_get_boolean(t, &mute) < 0 ||
pa_tagstruct_getu32(t, &i.monitor_of_sink) < 0 ||
pa_tagstruct_gets(t, &i.monitor_of_sink_name) < 0 ||
pa_tagstruct_get_usec(t, &i.latency) < 0 ||
pa_tagstruct_gets(t, &i.driver) < 0 ||
- pa_tagstruct_getu32(t, &flags) < 0) {
-
+ pa_tagstruct_getu32(t, &flags) < 0 ||
+ (o->context->version >= 13 &&
+ (pa_tagstruct_get_proplist(t, i.proplist) < 0 ||
+ pa_tagstruct_get_usec(t, &i.configured_latency) < 0))) {
+
pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ pa_proplist_free(i.proplist);
goto finish;
}
+ i.mute = (int) mute;
i.flags = (pa_source_flags_t) flags;
if (o->callback) {
pa_source_info_cb_t cb = (pa_source_info_cb_t) o->callback;
cb(o->context, &i, 0, o->userdata);
}
+
+ pa_proplist_free(i.proplist);
}
}
-
+
if (o->callback) {
pa_source_info_cb_t cb = (pa_source_info_cb_t) o->callback;
cb(o->context, NULL, eol, o->userdata);
@@ -302,14 +327,14 @@ pa_operation* pa_context_get_source_info_by_index(pa_context *c, uint32_t idx, p
pa_operation *o;
uint32_t tag;
- assert(c);
- assert(c->ref >= 1);
- assert(cb);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+ pa_assert(cb);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
-
+
t = pa_tagstruct_command(c, PA_COMMAND_GET_SOURCE_INFO, &tag);
pa_tagstruct_putu32(t, idx);
pa_tagstruct_puts(t, NULL);
@@ -324,9 +349,9 @@ pa_operation* pa_context_get_source_info_by_name(pa_context *c, const char *name
pa_operation *o;
uint32_t tag;
- assert(c);
- assert(c->ref >= 1);
- assert(cb);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+ pa_assert(cb);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID);
@@ -348,28 +373,34 @@ static void context_get_client_info_callback(pa_pdispatch *pd, uint32_t command,
pa_operation *o = userdata;
int eol = 1;
- assert(pd);
- assert(o);
- assert(o->ref >= 1);
+ pa_assert(pd);
+ pa_assert(o);
+ pa_assert(PA_REFCNT_VALUE(o) >= 1);
if (!o->context)
goto finish;
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(o->context, command, t) < 0)
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
eol = -1;
} else {
-
+
while (!pa_tagstruct_eof(t)) {
pa_client_info i;
-
+
+ memset(&i, 0, sizeof(i));
+ i.proplist = pa_proplist_new();
+
if (pa_tagstruct_getu32(t, &i.index) < 0 ||
pa_tagstruct_gets(t, &i.name) < 0 ||
pa_tagstruct_getu32(t, &i.owner_module) < 0 ||
- pa_tagstruct_gets(t, &i.driver) < 0 ) {
+ pa_tagstruct_gets(t, &i.driver) < 0 ||
+ (o->context->version >= 13 && pa_tagstruct_get_proplist(t, i.proplist) < 0)) {
+
pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ pa_proplist_free(i.proplist);
goto finish;
}
@@ -377,9 +408,11 @@ static void context_get_client_info_callback(pa_pdispatch *pd, uint32_t command,
pa_client_info_cb_t cb = (pa_client_info_cb_t) o->callback;
cb(o->context, &i, 0, o->userdata);
}
+
+ pa_proplist_free(i.proplist);
}
}
-
+
if (o->callback) {
pa_client_info_cb_t cb = (pa_client_info_cb_t) o->callback;
cb(o->context, NULL, eol, o->userdata);
@@ -395,9 +428,9 @@ pa_operation* pa_context_get_client_info(pa_context *c, uint32_t idx, pa_client_
pa_operation *o;
uint32_t tag;
- assert(c);
- assert(c->ref >= 1);
- assert(cb);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+ pa_assert(cb);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
@@ -422,39 +455,43 @@ static void context_get_module_info_callback(pa_pdispatch *pd, uint32_t command,
pa_operation *o = userdata;
int eol = 1;
- assert(pd);
- assert(o);
- assert(o->ref >= 1);
+ pa_assert(pd);
+ pa_assert(o);
+ pa_assert(PA_REFCNT_VALUE(o) >= 1);
if (!o->context)
goto finish;
-
+
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(o->context, command, t) < 0)
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
eol = -1;
} else {
-
+
while (!pa_tagstruct_eof(t)) {
pa_module_info i;
-
+ pa_bool_t auto_unload = FALSE;
+ memset(&i, 0, sizeof(i));
+
if (pa_tagstruct_getu32(t, &i.index) < 0 ||
pa_tagstruct_gets(t, &i.name) < 0 ||
pa_tagstruct_gets(t, &i.argument) < 0 ||
pa_tagstruct_getu32(t, &i.n_used) < 0 ||
- pa_tagstruct_get_boolean(t, &i.auto_unload) < 0) {
+ pa_tagstruct_get_boolean(t, &auto_unload) < 0) {
pa_context_fail(o->context, PA_ERR_PROTOCOL);
goto finish;
}
+ i.auto_unload = (int) auto_unload;
+
if (o->callback) {
pa_module_info_cb_t cb = (pa_module_info_cb_t) o->callback;
cb(o->context, &i, 0, o->userdata);
}
}
}
-
+
if (o->callback) {
pa_module_info_cb_t cb = (pa_module_info_cb_t) o->callback;
cb(o->context, NULL, eol, o->userdata);
@@ -470,13 +507,13 @@ pa_operation* pa_context_get_module_info(pa_context *c, uint32_t idx, pa_module_
pa_operation *o;
uint32_t tag;
- assert(c);
- assert(c->ref >= 1);
- assert(cb);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+ pa_assert(cb);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
-
+
o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
t = pa_tagstruct_command(c, PA_COMMAND_GET_MODULE_INFO, &tag);
@@ -497,23 +534,27 @@ static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t comm
pa_operation *o = userdata;
int eol = 1;
- assert(pd);
- assert(o);
- assert(o->ref >= 1);
+ pa_assert(pd);
+ pa_assert(o);
+ pa_assert(PA_REFCNT_VALUE(o) >= 1);
if (!o->context)
goto finish;
-
+
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(o->context, command, t) < 0)
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
eol = -1;
} else {
-
+
while (!pa_tagstruct_eof(t)) {
pa_sink_input_info i;
-
+ pa_bool_t mute = FALSE;
+
+ memset(&i, 0, sizeof(i));
+ i.proplist = pa_proplist_new();
+
if (pa_tagstruct_getu32(t, &i.index) < 0 ||
pa_tagstruct_gets(t, &i.name) < 0 ||
pa_tagstruct_getu32(t, &i.owner_module) < 0 ||
@@ -525,19 +566,26 @@ static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t comm
pa_tagstruct_get_usec(t, &i.buffer_usec) < 0 ||
pa_tagstruct_get_usec(t, &i.sink_usec) < 0 ||
pa_tagstruct_gets(t, &i.resample_method) < 0 ||
- pa_tagstruct_gets(t, &i.driver) < 0) {
-
+ pa_tagstruct_gets(t, &i.driver) < 0 ||
+ (o->context->version >= 11 && pa_tagstruct_get_boolean(t, &mute) < 0) ||
+ (o->context->version >= 13 && pa_tagstruct_get_proplist(t, i.proplist) < 0)) {
+
pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ pa_proplist_free(i.proplist);
goto finish;
}
+ i.mute = (int) mute;
+
if (o->callback) {
pa_sink_input_info_cb_t cb = (pa_sink_input_info_cb_t) o->callback;
cb(o->context, &i, 0, o->userdata);
}
+
+ pa_proplist_free(i.proplist);
}
}
-
+
if (o->callback) {
pa_sink_input_info_cb_t cb = (pa_sink_input_info_cb_t) o->callback;
cb(o->context, NULL, eol, o->userdata);
@@ -553,13 +601,13 @@ pa_operation* pa_context_get_sink_input_info(pa_context *c, uint32_t idx, pa_sin
pa_operation *o;
uint32_t tag;
- assert(c);
- assert(c->ref >= 1);
- assert(cb);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+ pa_assert(cb);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
-
+
o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
t = pa_tagstruct_command(c, PA_COMMAND_GET_SINK_INPUT_INFO, &tag);
@@ -580,23 +628,26 @@ static void context_get_source_output_info_callback(pa_pdispatch *pd, uint32_t c
pa_operation *o = userdata;
int eol = 1;
- assert(pd);
- assert(o);
- assert(o->ref >= 1);
+ pa_assert(pd);
+ pa_assert(o);
+ pa_assert(PA_REFCNT_VALUE(o) >= 1);
if (!o->context)
goto finish;
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(o->context, command, t) < 0)
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
eol = -1;
} else {
-
+
while (!pa_tagstruct_eof(t)) {
pa_source_output_info i;
-
+
+ memset(&i, 0, sizeof(i));
+ i.proplist = pa_proplist_new();
+
if (pa_tagstruct_getu32(t, &i.index) < 0 ||
pa_tagstruct_gets(t, &i.name) < 0 ||
pa_tagstruct_getu32(t, &i.owner_module) < 0 ||
@@ -607,9 +658,11 @@ static void context_get_source_output_info_callback(pa_pdispatch *pd, uint32_t c
pa_tagstruct_get_usec(t, &i.buffer_usec) < 0 ||
pa_tagstruct_get_usec(t, &i.source_usec) < 0 ||
pa_tagstruct_gets(t, &i.resample_method) < 0 ||
- pa_tagstruct_gets(t, &i.driver) < 0) {
-
+ pa_tagstruct_gets(t, &i.driver) < 0 ||
+ (o->context->version >= 13 && pa_tagstruct_get_proplist(t, i.proplist) < 0)) {
+
pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ pa_proplist_free(i.proplist);
goto finish;
}
@@ -617,9 +670,11 @@ static void context_get_source_output_info_callback(pa_pdispatch *pd, uint32_t c
pa_source_output_info_cb_t cb = (pa_source_output_info_cb_t) o->callback;
cb(o->context, &i, 0, o->userdata);
}
+
+ pa_proplist_free(i.proplist);
}
}
-
+
if (o->callback) {
pa_source_output_info_cb_t cb = (pa_source_output_info_cb_t) o->callback;
cb(o->context, NULL, eol, o->userdata);
@@ -635,13 +690,13 @@ pa_operation* pa_context_get_source_output_info(pa_context *c, uint32_t idx, pa_
pa_operation *o;
uint32_t tag;
- assert(c);
- assert(c->ref >= 1);
- assert(cb);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+ pa_assert(cb);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
-
+
o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
t = pa_tagstruct_command(c, PA_COMMAND_GET_SOURCE_OUTPUT_INFO, &tag);
@@ -663,9 +718,9 @@ pa_operation* pa_context_set_sink_volume_by_index(pa_context *c, uint32_t idx, c
pa_tagstruct *t;
uint32_t tag;
- assert(c);
- assert(c->ref >= 1);
- assert(volume);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+ pa_assert(volume);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(c, pa_cvolume_valid(volume), PA_ERR_INVALID);
@@ -687,15 +742,15 @@ pa_operation* pa_context_set_sink_volume_by_name(pa_context *c, const char *name
pa_tagstruct *t;
uint32_t tag;
- assert(c);
- assert(c->ref >= 1);
- assert(name);
- assert(volume);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+ pa_assert(name);
+ pa_assert(volume);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(c, pa_cvolume_valid(volume), PA_ERR_INVALID);
PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID);
-
+
o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
t = pa_tagstruct_command(c, PA_COMMAND_SET_SINK_VOLUME, &tag);
@@ -713,8 +768,8 @@ pa_operation* pa_context_set_sink_mute_by_index(pa_context *c, uint32_t idx, int
pa_tagstruct *t;
uint32_t tag;
- assert(c);
- assert(c->ref >= 1);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
@@ -735,13 +790,13 @@ pa_operation* pa_context_set_sink_mute_by_name(pa_context *c, const char *name,
pa_tagstruct *t;
uint32_t tag;
- assert(c);
- assert(c->ref >= 1);
- assert(name);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+ pa_assert(name);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID);
-
+
o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
t = pa_tagstruct_command(c, PA_COMMAND_SET_SINK_MUTE, &tag);
@@ -759,14 +814,14 @@ pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, cons
pa_tagstruct *t;
uint32_t tag;
- assert(c);
- assert(c->ref >= 1);
- assert(volume);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+ pa_assert(volume);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
PA_CHECK_VALIDITY_RETURN_NULL(c, pa_cvolume_valid(volume), PA_ERR_INVALID);
-
+
o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
t = pa_tagstruct_command(c, PA_COMMAND_SET_SINK_INPUT_VOLUME, &tag);
@@ -778,14 +833,37 @@ pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, cons
return o;
}
+pa_operation* pa_context_set_sink_input_mute(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 11, PA_ERR_NOTSUPPORTED);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_SET_SINK_INPUT_MUTE, &tag);
+ pa_tagstruct_putu32(t, idx);
+ pa_tagstruct_put_boolean(t, mute);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
pa_operation* pa_context_set_source_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) {
pa_operation *o;
pa_tagstruct *t;
uint32_t tag;
- assert(c);
- assert(c->ref >= 1);
- assert(volume);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+ pa_assert(volume);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(c, pa_cvolume_valid(volume), PA_ERR_INVALID);
@@ -807,15 +885,15 @@ pa_operation* pa_context_set_source_volume_by_name(pa_context *c, const char *na
pa_tagstruct *t;
uint32_t tag;
- assert(c);
- assert(c->ref >= 1);
- assert(name);
- assert(volume);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+ pa_assert(name);
+ pa_assert(volume);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(c, pa_cvolume_valid(volume), PA_ERR_INVALID);
PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID);
-
+
o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
t = pa_tagstruct_command(c, PA_COMMAND_SET_SOURCE_VOLUME, &tag);
@@ -833,8 +911,8 @@ pa_operation* pa_context_set_source_mute_by_index(pa_context *c, uint32_t idx, i
pa_tagstruct *t;
uint32_t tag;
- assert(c);
- assert(c->ref >= 1);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
@@ -855,13 +933,13 @@ pa_operation* pa_context_set_source_mute_by_name(pa_context *c, const char *name
pa_tagstruct *t;
uint32_t tag;
- assert(c);
- assert(c->ref >= 1);
- assert(name);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+ pa_assert(name);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID);
-
+
o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
t = pa_tagstruct_command(c, PA_COMMAND_SET_SOURCE_MUTE, &tag);
@@ -880,23 +958,27 @@ static void context_get_sample_info_callback(pa_pdispatch *pd, uint32_t command,
pa_operation *o = userdata;
int eol = 1;
- assert(pd);
- assert(o);
- assert(o->ref >= 1);
+ pa_assert(pd);
+ pa_assert(o);
+ pa_assert(PA_REFCNT_VALUE(o) >= 1);
if (!o->context)
goto finish;
-
+
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(o->context, command, t) < 0)
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
eol = -1;
} else {
-
+
while (!pa_tagstruct_eof(t)) {
pa_sample_info i;
-
+ pa_bool_t lazy = FALSE;
+
+ memset(&i, 0, sizeof(i));
+ i.proplist = pa_proplist_new();
+
if (pa_tagstruct_getu32(t, &i.index) < 0 ||
pa_tagstruct_gets(t, &i.name) < 0 ||
pa_tagstruct_get_cvolume(t, &i.volume) < 0 ||
@@ -904,20 +986,25 @@ static void context_get_sample_info_callback(pa_pdispatch *pd, uint32_t command,
pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 ||
pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 ||
pa_tagstruct_getu32(t, &i.bytes) < 0 ||
- pa_tagstruct_get_boolean(t, &i.lazy) < 0 ||
- pa_tagstruct_gets(t, &i.filename) < 0) {
-
+ pa_tagstruct_get_boolean(t, &lazy) < 0 ||
+ pa_tagstruct_gets(t, &i.filename) < 0 ||
+ (o->context->version >= 13 && pa_tagstruct_get_proplist(t, i.proplist) < 0)) {
+
pa_context_fail(o->context, PA_ERR_PROTOCOL);
goto finish;
}
+ i.lazy = (int) lazy;
+
if (o->callback) {
pa_sample_info_cb_t cb = (pa_sample_info_cb_t) o->callback;
cb(o->context, &i, 0, o->userdata);
}
+
+ pa_proplist_free(i.proplist);
}
}
-
+
if (o->callback) {
pa_sample_info_cb_t cb = (pa_sample_info_cb_t) o->callback;
cb(o->context, NULL, eol, o->userdata);
@@ -932,10 +1019,10 @@ pa_operation* pa_context_get_sample_info_by_name(pa_context *c, const char *name
pa_tagstruct *t;
pa_operation *o;
uint32_t tag;
-
- assert(c);
- assert(c->ref >= 1);
- assert(cb);
+
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+ pa_assert(cb);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID);
@@ -956,9 +1043,9 @@ pa_operation* pa_context_get_sample_info_by_index(pa_context *c, uint32_t idx, p
pa_operation *o;
uint32_t tag;
- assert(c);
- assert(c->ref >= 1);
- assert(cb);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+ pa_assert(cb);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
@@ -983,8 +1070,8 @@ static pa_operation* command_kill(pa_context *c, uint32_t command, uint32_t idx,
pa_tagstruct *t;
uint32_t tag;
- assert(c);
- assert(c->ref >= 1);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
@@ -1002,7 +1089,7 @@ static pa_operation* command_kill(pa_context *c, uint32_t command, uint32_t idx,
pa_operation* pa_context_kill_client(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata) {
return command_kill(c, PA_COMMAND_KILL_CLIENT, idx, cb, userdata);
}
-
+
pa_operation* pa_context_kill_sink_input(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata) {
return command_kill(c, PA_COMMAND_KILL_SINK_INPUT, idx, cb, userdata);
}
@@ -1015,15 +1102,15 @@ static void context_index_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UN
pa_operation *o = userdata;
uint32_t idx;
- assert(pd);
- assert(o);
- assert(o->ref >= 1);
+ pa_assert(pd);
+ pa_assert(o);
+ pa_assert(PA_REFCNT_VALUE(o) >= 1);
if (!o->context)
goto finish;
-
+
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(o->context, command, t) < 0)
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
idx = PA_INVALID_INDEX;
@@ -1048,9 +1135,9 @@ pa_operation* pa_context_load_module(pa_context *c, const char*name, const char
pa_operation *o;
pa_tagstruct *t;
uint32_t tag;
-
- assert(c);
- assert(c->ref >= 1);
+
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID);
@@ -1076,23 +1163,25 @@ static void context_get_autoload_info_callback(pa_pdispatch *pd, uint32_t comman
pa_operation *o = userdata;
int eol = 1;
- assert(pd);
- assert(o);
- assert(o->ref >= 1);
+ pa_assert(pd);
+ pa_assert(o);
+ pa_assert(PA_REFCNT_VALUE(o) >= 1);
if (!o->context)
goto finish;
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(o->context, command, t) < 0)
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
eol = -1;
} else {
-
+
while (!pa_tagstruct_eof(t)) {
pa_autoload_info i;
-
+
+ memset(&i, 0, sizeof(i));
+
if (pa_tagstruct_getu32(t, &i.index) < 0 ||
pa_tagstruct_gets(t, &i.name) < 0 ||
pa_tagstruct_getu32(t, &i.type) < 0 ||
@@ -1108,7 +1197,7 @@ static void context_get_autoload_info_callback(pa_pdispatch *pd, uint32_t comman
}
}
}
-
+
if (o->callback) {
pa_autoload_info_cb_t cb = (pa_autoload_info_cb_t) o->callback;
cb(o->context, NULL, eol, o->userdata);
@@ -1119,14 +1208,16 @@ finish:
pa_operation_unref(o);
}
+PA_WARN_REFERENCE(pa_context_get_autoload_info_by_name, "Autoload will no longer be implemented by future versions of the PulseAudio server.");
+
pa_operation* pa_context_get_autoload_info_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_autoload_info_cb_t cb, void *userdata) {
pa_tagstruct *t;
pa_operation *o;
uint32_t tag;
- assert(c);
- assert(c->ref >= 1);
- assert(cb);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+ pa_assert(cb);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID);
@@ -1143,14 +1234,16 @@ pa_operation* pa_context_get_autoload_info_by_name(pa_context *c, const char *na
return o;
}
+PA_WARN_REFERENCE(pa_context_get_autoload_info_by_index, "Autoload will no longer be implemented by future versions of the PulseAudio server.");
+
pa_operation* pa_context_get_autoload_info_by_index(pa_context *c, uint32_t idx, pa_autoload_info_cb_t cb, void *userdata) {
pa_tagstruct *t;
pa_operation *o;
uint32_t tag;
-
- assert(c);
- assert(c->ref >= 1);
- assert(cb);
+
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+ pa_assert(cb);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
@@ -1165,18 +1258,23 @@ pa_operation* pa_context_get_autoload_info_by_index(pa_context *c, uint32_t idx,
return o;
}
+
+PA_WARN_REFERENCE(pa_context_get_autoload_info_list, "Autoload will no longer be implemented by future versions of the PulseAudio server.");
+
pa_operation* pa_context_get_autoload_info_list(pa_context *c, pa_autoload_info_cb_t cb, void *userdata) {
return pa_context_send_simple_command(c, PA_COMMAND_GET_AUTOLOAD_INFO_LIST, context_get_autoload_info_callback, (pa_operation_cb_t) cb, userdata);
}
+PA_WARN_REFERENCE(pa_context_add_autoload, "Autoload will no longer be implemented by future versions of the PulseAudio server.");
+
pa_operation* pa_context_add_autoload(pa_context *c, const char *name, pa_autoload_type_t type, const char *module, const char*argument, pa_context_index_cb_t cb, void* userdata) {
pa_operation *o;
pa_tagstruct *t;
uint32_t tag;
- assert(c);
- assert(c->ref >= 1);
-
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID);
PA_CHECK_VALIDITY_RETURN_NULL(c, type == PA_AUTOLOAD_SINK || type == PA_AUTOLOAD_SOURCE, PA_ERR_INVALID);
@@ -1195,14 +1293,16 @@ pa_operation* pa_context_add_autoload(pa_context *c, const char *name, pa_autolo
return o;
}
+PA_WARN_REFERENCE(pa_context_remove_autoload_by_name, "Autoload will no longer be implemented by future versions of the PulseAudio server.");
+
pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_context_success_cb_t cb, void* userdata) {
pa_operation *o;
pa_tagstruct *t;
uint32_t tag;
- assert(c);
- assert(c->ref >= 1);
-
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID);
PA_CHECK_VALIDITY_RETURN_NULL(c, type == PA_AUTOLOAD_SINK || type == PA_AUTOLOAD_SOURCE, PA_ERR_INVALID);
@@ -1218,13 +1318,15 @@ pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name
return o;
}
+PA_WARN_REFERENCE(pa_context_remove_autoload_by_index, "Autoload will no longer be implemented by future versions of the PulseAudio server.");
+
pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void* userdata) {
pa_operation *o;
pa_tagstruct *t;
uint32_t tag;
- assert(c);
- assert(c->ref >= 1);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
@@ -1239,13 +1341,13 @@ pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, p
return o;
}
-pa_operation* pa_context_move_sink_input_by_name(pa_context *c, uint32_t idx, char *sink_name, pa_context_success_cb_t cb, void* userdata) {
+pa_operation* pa_context_move_sink_input_by_name(pa_context *c, uint32_t idx, const char *sink_name, pa_context_success_cb_t cb, void* userdata) {
pa_operation *o;
pa_tagstruct *t;
uint32_t tag;
- assert(c);
- assert(c->ref >= 1);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 10, PA_ERR_NOTSUPPORTED);
@@ -1269,8 +1371,8 @@ pa_operation* pa_context_move_sink_input_by_index(pa_context *c, uint32_t idx, u
pa_tagstruct *t;
uint32_t tag;
- assert(c);
- assert(c->ref >= 1);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 10, PA_ERR_NOTSUPPORTED);
@@ -1289,13 +1391,13 @@ pa_operation* pa_context_move_sink_input_by_index(pa_context *c, uint32_t idx, u
return o;
}
-pa_operation* pa_context_move_source_output_by_name(pa_context *c, uint32_t idx, char *source_name, pa_context_success_cb_t cb, void* userdata) {
+pa_operation* pa_context_move_source_output_by_name(pa_context *c, uint32_t idx, const char *source_name, pa_context_success_cb_t cb, void* userdata) {
pa_operation *o;
pa_tagstruct *t;
uint32_t tag;
- assert(c);
- assert(c->ref >= 1);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 10, PA_ERR_NOTSUPPORTED);
@@ -1319,8 +1421,8 @@ pa_operation* pa_context_move_source_output_by_index(pa_context *c, uint32_t idx
pa_tagstruct *t;
uint32_t tag;
- assert(c);
- assert(c->ref >= 1);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 10, PA_ERR_NOTSUPPORTED);
@@ -1338,3 +1440,97 @@ pa_operation* pa_context_move_source_output_by_index(pa_context *c, uint32_t idx
return o;
}
+
+pa_operation* pa_context_suspend_sink_by_name(pa_context *c, const char *sink_name, int suspend, pa_context_success_cb_t cb, void* userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 11, PA_ERR_NOTSUPPORTED);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, !sink_name || *sink_name, PA_ERR_INVALID);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_SUSPEND_SINK, &tag);
+ pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+ pa_tagstruct_puts(t, sink_name);
+ pa_tagstruct_put_boolean(t, suspend);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+pa_operation* pa_context_suspend_sink_by_index(pa_context *c, uint32_t idx, int suspend, pa_context_success_cb_t cb, void* userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 11, PA_ERR_NOTSUPPORTED);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_SUSPEND_SINK, &tag);
+ pa_tagstruct_putu32(t, idx);
+ pa_tagstruct_puts(t, idx == PA_INVALID_INDEX ? "" : NULL);
+ pa_tagstruct_put_boolean(t, suspend);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+pa_operation* pa_context_suspend_source_by_name(pa_context *c, const char *source_name, int suspend, pa_context_success_cb_t cb, void* userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 11, PA_ERR_NOTSUPPORTED);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, !source_name || *source_name, PA_ERR_INVALID);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_SUSPEND_SOURCE, &tag);
+ pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+ pa_tagstruct_puts(t, source_name);
+ pa_tagstruct_put_boolean(t, suspend);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+pa_operation* pa_context_suspend_source_by_index(pa_context *c, uint32_t idx, int suspend, pa_context_success_cb_t cb, void* userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 11, PA_ERR_NOTSUPPORTED);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_SUSPEND_SOURCE, &tag);
+ pa_tagstruct_putu32(t, idx);
+ pa_tagstruct_puts(t, idx == PA_INVALID_INDEX ? "" : NULL);
+ pa_tagstruct_put_boolean(t, suspend);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h
index 28d22cd7..c8c13a75 100644
--- a/src/pulse/introspect.h
+++ b/src/pulse/introspect.h
@@ -1,21 +1,22 @@
#ifndef foointrospecthfoo
#define foointrospecthfoo
-/* $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
@@ -27,8 +28,10 @@
#include <pulse/operation.h>
#include <pulse/context.h>
#include <pulse/cdecl.h>
+#include <pulse/gccmacro.h>
#include <pulse/channelmap.h>
#include <pulse/volume.h>
+#include <pulse/proplist.h>
/** \page introspect Server Query and Control
*
@@ -203,21 +206,32 @@
PA_C_DECL_BEGIN
-/** Stores information about sinks */
+#define PA_PORT_DIGITAL "spdif"
+#define PA_PORT_ANALOG_STEREO "analog-stereo"
+#define PA_PORT_ANALOG_5_1 "analog-5-1"
+#define PA_PORT_ANALOG_4_0 "analog-4-0"
+
+/** @{ \name Sinks */
+
+/** Stores information about sinks. Please note that this structure
+ * can be extended as part of evolutionary API updates at any time in
+ * any new release. */
typedef struct pa_sink_info {
const char *name; /**< Name of the sink */
- uint32_t index; /**< Index of the sink */
+ uint32_t index; /**< Index of the sink */
const char *description; /**< Description of this sink */
pa_sample_spec sample_spec; /**< Sample spec of this sink */
- pa_channel_map channel_map; /**< Channel map \since 0.8 */
+ pa_channel_map channel_map; /**< Channel map */
uint32_t owner_module; /**< Index of the owning module of this sink, or PA_INVALID_INDEX */
pa_cvolume volume; /**< Volume of the sink */
- int mute; /**< Mute switch of the sink \since 0.8 */
+ int mute; /**< Mute switch of the sink */
uint32_t monitor_source; /**< Index of the monitor source connected to this sink */
const char *monitor_source_name; /**< The name of the monitor source */
- pa_usec_t latency; /**< Length of filled playback buffer of this sink */
- const char *driver; /**< Driver name. \since 0.8 */
- pa_sink_flags_t flags; /**< Flags \since 0.8 */
+ pa_usec_t latency; /**< Length of queued audio in the output buffer. */
+ const char *driver; /**< Driver name. */
+ pa_sink_flags_t flags; /**< Flags */
+ pa_proplist *proplist; /**< Property list \since 0.9.11 */
+ pa_usec_t configured_latency; /**< The latency this device has been configured to. \since 0.9.11 */
} pa_sink_info;
/** Callback prototype for pa_context_get_sink_info_by_name() and friends */
@@ -232,21 +246,47 @@ pa_operation* pa_context_get_sink_info_by_index(pa_context *c, uint32_t id, pa_s
/** Get the complete sink list */
pa_operation* pa_context_get_sink_info_list(pa_context *c, pa_sink_info_cb_t cb, void *userdata);
-/** Stores information about sources */
-typedef struct pa_source_info {
- const char *name ; /**< Name of the source */
+/** Set the volume of a sink device specified by its index */
+pa_operation* pa_context_set_sink_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata);
+
+/** Set the volume of a sink device specified by its name */
+pa_operation* pa_context_set_sink_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata);
+
+/** Set the mute switch of a sink device specified by its index */
+pa_operation* pa_context_set_sink_mute_by_index(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata);
+
+/** Set the mute switch of a sink device specified by its name */
+pa_operation* pa_context_set_sink_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata);
+
+/** Suspend/Resume a sink. \since 0.9.7 */
+pa_operation* pa_context_suspend_sink_by_name(pa_context *c, const char *sink_name, int suspend, pa_context_success_cb_t cb, void* userdata);
+
+/** Suspend/Resume a sink. If idx is PA_INVALID_INDEX all sinks will be suspended. \since 0.9.7 */
+pa_operation* pa_context_suspend_sink_by_index(pa_context *c, uint32_t idx, int suspend, pa_context_success_cb_t cb, void* userdata);
+
+/** @} */
+
+/** @{ \name Sources */
+
+/** Stores information about sources. Please note that this structure
+ * can be extended as part of evolutionary API updates at any time in
+ * any new release. */
+typedef struct pa_source_info {
+ const char *name; /**< Name of the source */
uint32_t index; /**< Index of the source */
const char *description; /**< Description of this source */
pa_sample_spec sample_spec; /**< Sample spec of this source */
- pa_channel_map channel_map; /**< Channel map \since 0.8 */
+ pa_channel_map channel_map; /**< Channel map */
uint32_t owner_module; /**< Owning module index, or PA_INVALID_INDEX */
- pa_cvolume volume; /**< Volume of the source \since 0.8 */
- int mute; /**< Mute switch of the sink \since 0.8 */
+ pa_cvolume volume; /**< Volume of the source */
+ int mute; /**< Mute switch of the sink */
uint32_t monitor_of_sink; /**< If this is a monitor source the index of the owning sink, otherwise PA_INVALID_INDEX */
const char *monitor_of_sink_name; /**< Name of the owning sink, or PA_INVALID_INDEX */
- pa_usec_t latency; /**< Length of filled record buffer of this source. \since 0.5 */
- const char *driver; /**< Driver name \since 0.8 */
- pa_source_flags_t flags; /**< Flags \since 0.8 */
+ pa_usec_t latency; /**< Length of filled record buffer of this source. */
+ const char *driver; /**< Driver name */
+ pa_source_flags_t flags; /**< Flags */
+ pa_proplist *proplist; /**< Property list \since 0.9.11 */
+ pa_usec_t configured_latency; /**< The latency this device has been configured to. \since 0.9.11 */
} pa_source_info;
/** Callback prototype for pa_context_get_source_info_by_name() and friends */
@@ -261,16 +301,34 @@ pa_operation* pa_context_get_source_info_by_index(pa_context *c, uint32_t id, pa
/** Get the complete source list */
pa_operation* pa_context_get_source_info_list(pa_context *c, pa_source_info_cb_t cb, void *userdata);
-/** Server information */
+/** Set the volume of a source device specified by its index */
+pa_operation* pa_context_set_source_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata);
+
+/** Set the volume of a source device specified by its name */
+pa_operation* pa_context_set_source_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata);
+
+/** Set the mute switch of a source device specified by its index */
+pa_operation* pa_context_set_source_mute_by_index(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata);
+
+/** Set the mute switch of a source device specified by its name */
+pa_operation* pa_context_set_source_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata);
+
+/** @} */
+
+/** @{ \name Server */
+
+/** Server information. Please note that this structure can be
+ * extended as part of evolutionary API updates at any time in any new
+ * release. */
typedef struct pa_server_info {
const char *user_name; /**< User name of the daemon process */
const char *host_name; /**< Host name the daemon is running on */
const char *server_version; /**< Version string of the daemon */
const char *server_name; /**< Server package name (usually "pulseaudio") */
pa_sample_spec sample_spec; /**< Default sample specification */
- const char *default_sink_name; /**< Name of default sink. \since 0.4 */
- const char *default_source_name; /**< Name of default sink. \since 0.4*/
- uint32_t cookie; /**< A random cookie for identifying this instance of PulseAudio. \since 0.8 */
+ const char *default_sink_name; /**< Name of default sink. */
+ const char *default_source_name; /**< Name of default sink. */
+ uint32_t cookie; /**< A random cookie for identifying this instance of PulseAudio. */
} pa_server_info;
/** Callback prototype for pa_context_get_server_info() */
@@ -279,7 +337,13 @@ typedef void (*pa_server_info_cb_t) (pa_context *c, const pa_server_info*i, void
/** Get some information about the server */
pa_operation* pa_context_get_server_info(pa_context *c, pa_server_info_cb_t cb, void *userdata);
-/** Stores information about modules */
+/** @} */
+
+/** @{ \name Modules */
+
+/** Stores information about modules. Please note that this structure
+ * can be extended as part of evolutionary API updates at any time in
+ * any new release. */
typedef struct pa_module_info {
uint32_t index; /**< Index of the module */
const char*name, /**< Name of the module */
@@ -297,12 +361,28 @@ pa_operation* pa_context_get_module_info(pa_context *c, uint32_t idx, pa_module_
/** Get the complete list of currently loaded modules */
pa_operation* pa_context_get_module_info_list(pa_context *c, pa_module_info_cb_t cb, void *userdata);
-/** Stores information about clients */
+/** Callback prototype for pa_context_load_module() */
+typedef void (*pa_context_index_cb_t)(pa_context *c, uint32_t idx, void *userdata);
+
+/** Load a module. */
+pa_operation* pa_context_load_module(pa_context *c, const char*name, const char *argument, pa_context_index_cb_t cb, void *userdata);
+
+/** Unload a module. */
+pa_operation* pa_context_unload_module(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata);
+
+/** @} */
+
+/** @{ \name Clients */
+
+/** Stores information about clients. Please note that this structure
+ * can be extended as part of evolutionary API updates at any time in
+ * any new release. */
typedef struct pa_client_info {
uint32_t index; /**< Index of this client */
const char *name; /**< Name of this client */
uint32_t owner_module; /**< Index of the owning module, or PA_INVALID_INDEX */
- const char *driver; /**< Driver name \since 0.8 */
+ const char *driver; /**< Driver name */
+ pa_proplist *proplist; /**< Property list \since 0.9.11 */
} pa_client_info;
/** Callback prototype for pa_context_get_client_info() and firends*/
@@ -314,9 +394,18 @@ pa_operation* pa_context_get_client_info(pa_context *c, uint32_t idx, pa_client_
/** Get the complete client list */
pa_operation* pa_context_get_client_info_list(pa_context *c, pa_client_info_cb_t cb, void *userdata);
-/** Stores information about sink inputs */
+/** Kill a client. */
+pa_operation* pa_context_kill_client(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata);
+
+/** @} */
+
+/** @{ \name Sink Inputs */
+
+/** Stores information about sink inputs. Please note that this structure
+ * can be extended as part of evolutionary API updates at any time in
+ * any new release. */
typedef struct pa_sink_input_info {
- uint32_t index; /**< Index of the sink input */
+ uint32_t index; /**< Index of the sink input */
const char *name; /**< Name of the sink input */
uint32_t owner_module; /**< Index of the module this sink input belongs to, or PA_INVALID_INDEX when it does not belong to any module */
uint32_t client; /**< Index of the client this sink input belongs to, or PA_INVALID_INDEX when it does not belong to any client */
@@ -326,8 +415,10 @@ typedef struct pa_sink_input_info {
pa_cvolume volume; /**< The volume of this sink input */
pa_usec_t buffer_usec; /**< Latency due to buffering in sink input, see pa_latency_info for details */
pa_usec_t sink_usec; /**< Latency of the sink device, see pa_latency_info for details */
- const char *resample_method; /**< Thre resampling method used by this sink input. \since 0.7 */
- const char *driver; /**< Driver name \since 0.8 */
+ const char *resample_method; /**< Thre resampling method used by this sink input. */
+ const char *driver; /**< Driver name */
+ int mute; /**< Stream muted \since 0.9.7 */
+ pa_proplist *proplist; /**< Property list \since 0.9.11 */
} pa_sink_input_info;
/** Callback prototype for pa_context_get_sink_input_info() and firends*/
@@ -339,19 +430,41 @@ pa_operation* pa_context_get_sink_input_info(pa_context *c, uint32_t idx, pa_sin
/** Get the complete sink input list */
pa_operation* pa_context_get_sink_input_info_list(pa_context *c, pa_sink_input_info_cb_t cb, void *userdata);
-/** Stores information about source outputs */
+/** Move the specified sink input to a different sink. \since 0.9.5 */
+pa_operation* pa_context_move_sink_input_by_name(pa_context *c, uint32_t idx, const char *sink_name, pa_context_success_cb_t cb, void* userdata);
+
+/** Move the specified sink input to a different sink. \since 0.9.5 */
+pa_operation* pa_context_move_sink_input_by_index(pa_context *c, uint32_t idx, uint32_t sink_idx, pa_context_success_cb_t cb, void* userdata);
+
+/** Set the volume of a sink input stream */
+pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata);
+
+/** Set the mute switch of a sink input stream \since 0.9.7 */
+pa_operation* pa_context_set_sink_input_mute(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata);
+
+/** Kill a sink input. */
+pa_operation* pa_context_kill_sink_input(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata);
+
+/** @} */
+
+/** @{ \name Source Outputs */
+
+/** Stores information about source outputs. Please note that this structure
+ * can be extended as part of evolutionary API updates at any time in
+ * any new release. */
typedef struct pa_source_output_info {
- uint32_t index; /**< Index of the sink input */
+ uint32_t index; /**< Index of the sink input */
const char *name; /**< Name of the sink input */
- uint32_t owner_module; /**< Index of the module this sink input belongs to, or PA_INVALID_INDEX when it does not belong to any module */
- uint32_t client; /**< Index of the client this sink input belongs to, or PA_INVALID_INDEX when it does not belong to any client */
- uint32_t source; /**< Index of the connected source */
+ uint32_t owner_module; /**< Index of the module this sink input belongs to, or PA_INVALID_INDEX when it does not belong to any module */
+ uint32_t client; /**< Index of the client this sink input belongs to, or PA_INVALID_INDEX when it does not belong to any client */
+ uint32_t source; /**< Index of the connected source */
pa_sample_spec sample_spec; /**< The sample specification of the source output */
pa_channel_map channel_map; /**< Channel map */
- pa_usec_t buffer_usec; /**< Latency due to buffering in the source output, see pa_latency_info for details. \since 0.5 */
- pa_usec_t source_usec; /**< Latency of the source device, see pa_latency_info for details. \since 0.5 */
- const char *resample_method; /**< Thre resampling method used by this source output. \since 0.7 */
- const char *driver; /**< Driver name \since 0.8 */
+ pa_usec_t buffer_usec; /**< Latency due to buffering in the source output, see pa_latency_info for details. */
+ pa_usec_t source_usec; /**< Latency of the source device, see pa_latency_info for details. */
+ const char *resample_method; /**< Thre resampling method used by this source output. */
+ const char *driver; /**< Driver name */
+ pa_proplist *proplist; /**< Property list \since 0.9.11 */
} pa_source_output_info;
/** Callback prototype for pa_context_get_source_output_info() and firends*/
@@ -363,40 +476,34 @@ pa_operation* pa_context_get_source_output_info(pa_context *c, uint32_t idx, pa_
/** Get the complete list of source outputs */
pa_operation* pa_context_get_source_output_info_list(pa_context *c, pa_source_output_info_cb_t cb, void *userdata);
-/** Set the volume of a sink device specified by its index */
-pa_operation* pa_context_set_sink_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata);
-
-/** Set the volume of a sink device specified by its name */
-pa_operation* pa_context_set_sink_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata);
-
-/** Set the mute switch of a sink device specified by its index \since 0.8 */
-pa_operation* pa_context_set_sink_mute_by_index(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata);
+/** Move the specified source output to a different source. \since 0.9.5 */
+pa_operation* pa_context_move_source_output_by_name(pa_context *c, uint32_t idx, const char *source_name, pa_context_success_cb_t cb, void* userdata);
-/** Set the mute switch of a sink device specified by its name \since 0.8 */
-pa_operation* pa_context_set_sink_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata);
+/** Move the specified source output to a different source. \since 0.9.5 */
+pa_operation* pa_context_move_source_output_by_index(pa_context *c, uint32_t idx, uint32_t source_idx, pa_context_success_cb_t cb, void* userdata);
-/** Set the volume of a sink input stream */
-pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata);
+/** Suspend/Resume a source. \since 0.9.7 */
+pa_operation* pa_context_suspend_source_by_name(pa_context *c, const char *source_name, int suspend, pa_context_success_cb_t cb, void* userdata);
-/** Set the volume of a source device specified by its index \since 0.8 */
-pa_operation* pa_context_set_source_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata);
+/** Suspend/Resume a source. If idx is PA_INVALID_INDEX all sources will be suspended. \since 0.9.7 */
+pa_operation* pa_context_suspend_source_by_index(pa_context *c, uint32_t idx, int suspend, pa_context_success_cb_t cb, void* userdata);
-/** Set the volume of a source device specified by its name \since 0.8 */
-pa_operation* pa_context_set_source_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata);
+/** Kill a source output. */
+pa_operation* pa_context_kill_source_output(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata);
-/** Set the mute switch of a source device specified by its index \since 0.8 */
-pa_operation* pa_context_set_source_mute_by_index(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata);
+/** @} */
-/** Set the mute switch of a source device specified by its name \since 0.8 */
-pa_operation* pa_context_set_source_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata);
+/** @{ \name Statistics */
-/** Memory block statistics */
+/** Memory block statistics. Please note that this structure
+ * can be extended as part of evolutionary API updates at any time in
+ * any new release. */
typedef struct pa_stat_info {
uint32_t memblock_total; /**< Currently allocated memory blocks */
uint32_t memblock_total_size; /**< Currentl total size of allocated memory blocks */
uint32_t memblock_allocated; /**< Allocated memory blocks during the whole lifetime of the daemon */
uint32_t memblock_allocated_size; /**< Total size of all memory blocks allocated during the whole lifetime of the daemon */
- uint32_t scache_size; /**< Total size of all sample cache entries. \since 0.4 */
+ uint32_t scache_size; /**< Total size of all sample cache entries. */
} pa_stat_info;
/** Callback prototype for pa_context_stat() */
@@ -405,7 +512,13 @@ typedef void (*pa_stat_info_cb_t) (pa_context *c, const pa_stat_info *i, void *u
/** Get daemon memory block statistics */
pa_operation* pa_context_stat(pa_context *c, pa_stat_info_cb_t cb, void *userdata);
-/** Stores information about sample cache entries */
+/** @} */
+
+/** @{ \name Cached Samples */
+
+/** Stores information about sample cache entries. Please note that this structure
+ * can be extended as part of evolutionary API updates at any time in
+ * any new release. */
typedef struct pa_sample_info {
uint32_t index; /**< Index of this entry */
const char *name; /**< Name of this entry */
@@ -413,9 +526,10 @@ typedef struct pa_sample_info {
pa_sample_spec sample_spec; /**< Sample specification of the sample */
pa_channel_map channel_map; /**< The channel map */
pa_usec_t duration; /**< Duration of this entry */
- uint32_t bytes; /**< Length of this sample in bytes. \since 0.4 */
- int lazy; /**< Non-zero when this is a lazy cache entry. \since 0.5 */
- const char *filename; /**< In case this is a lazy cache entry, the filename for the sound file to be loaded on demand. \since 0.5 */
+ uint32_t bytes; /**< Length of this sample in bytes. */
+ int lazy; /**< Non-zero when this is a lazy cache entry. */
+ const char *filename; /**< In case this is a lazy cache entry, the filename for the sound file to be loaded on demand. */
+ pa_proplist *proplist; /**< Property list for this sample. \since 0.9.11 */
} pa_sample_info;
/** Callback prototype for pa_context_get_sample_info_by_name() and firends */
@@ -430,31 +544,21 @@ pa_operation* pa_context_get_sample_info_by_index(pa_context *c, uint32_t idx, p
/** Get the complete list of samples stored in the daemon. */
pa_operation* pa_context_get_sample_info_list(pa_context *c, pa_sample_info_cb_t cb, void *userdata);
-/** Kill a client. \since 0.5 */
-pa_operation* pa_context_kill_client(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata);
-
-/** Kill a sink input. \since 0.5 */
-pa_operation* pa_context_kill_sink_input(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata);
-
-/** Kill a source output. \since 0.5 */
-pa_operation* pa_context_kill_source_output(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata);
-
-/** Callback prototype for pa_context_load_module() and pa_context_add_autoload() */
-typedef void (*pa_context_index_cb_t)(pa_context *c, uint32_t idx, void *userdata);
+/** @} */
-/** Load a module. \since 0.5 */
-pa_operation* pa_context_load_module(pa_context *c, const char*name, const char *argument, pa_context_index_cb_t cb, void *userdata);
+/** \cond fulldocs */
-/** Unload a module. \since 0.5 */
-pa_operation* pa_context_unload_module(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata);
+/** @{ \name Autoload Entries */
-/** Type of an autoload entry. \since 0.5 */
+/** Type of an autoload entry. */
typedef enum pa_autoload_type {
PA_AUTOLOAD_SINK = 0,
PA_AUTOLOAD_SOURCE = 1
} pa_autoload_type_t;
-/** Stores information about autoload entries. \since 0.5 */
+/** Stores information about autoload entries. Please note that this structure
+ * can be extended as part of evolutionary API updates at any time in
+ * any new release. */
typedef struct pa_autoload_info {
uint32_t index; /**< Index of this autoload entry */
const char *name; /**< Name of the sink or source */
@@ -466,35 +570,27 @@ typedef struct pa_autoload_info {
/** Callback prototype for pa_context_get_autoload_info_by_name() and firends */
typedef void (*pa_autoload_info_cb_t)(pa_context *c, const pa_autoload_info *i, int eol, void *userdata);
-/** Get info about a specific autoload entry. \since 0.6 */
-pa_operation* pa_context_get_autoload_info_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_autoload_info_cb_t cb, void *userdata);
-
-/** Get info about a specific autoload entry. \since 0.6 */
-pa_operation* pa_context_get_autoload_info_by_index(pa_context *c, uint32_t idx, pa_autoload_info_cb_t cb, void *userdata);
-
-/** Get the complete list of autoload entries. \since 0.5 */
-pa_operation* pa_context_get_autoload_info_list(pa_context *c, pa_autoload_info_cb_t cb, void *userdata);
+/** Get info about a specific autoload entry. */
+pa_operation* pa_context_get_autoload_info_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_autoload_info_cb_t cb, void *userdata) PA_GCC_DEPRECATED;
-/** Add a new autoload entry. \since 0.5 */
-pa_operation* pa_context_add_autoload(pa_context *c, const char *name, pa_autoload_type_t type, const char *module, const char*argument, pa_context_index_cb_t, void* userdata);
+/** Get info about a specific autoload entry. */
+pa_operation* pa_context_get_autoload_info_by_index(pa_context *c, uint32_t idx, pa_autoload_info_cb_t cb, void *userdata) PA_GCC_DEPRECATED;
-/** Remove an autoload entry. \since 0.6 */
-pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_context_success_cb_t cb, void* userdata);
+/** Get the complete list of autoload entries. */
+pa_operation* pa_context_get_autoload_info_list(pa_context *c, pa_autoload_info_cb_t cb, void *userdata) PA_GCC_DEPRECATED;
-/** Remove an autoload entry. \since 0.6 */
-pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void* userdata);
+/** Add a new autoload entry. */
+pa_operation* pa_context_add_autoload(pa_context *c, const char *name, pa_autoload_type_t type, const char *module, const char*argument, pa_context_index_cb_t, void* userdata) PA_GCC_DEPRECATED;
-/** Move the specified sink input to a different sink. \since 0.9.5 */
-pa_operation* pa_context_move_sink_input_by_name(pa_context *c, uint32_t idx, char *sink_name, pa_context_success_cb_t cb, void* userdata);
+/** Remove an autoload entry. */
+pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_context_success_cb_t cb, void* userdata) PA_GCC_DEPRECATED;
-/** Move the specified sink input to a different sink. \since 0.9.5 */
-pa_operation* pa_context_move_sink_input_by_index(pa_context *c, uint32_t idx, uint32_t sink_idx, pa_context_success_cb_t cb, void* userdata);
+/** Remove an autoload entry. */
+pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void* userdata) PA_GCC_DEPRECATED;
-/** Move the specified source output to a different source. \since 0.9.5 */
-pa_operation* pa_context_move_source_output_by_name(pa_context *c, uint32_t idx, char *source_name, pa_context_success_cb_t cb, void* userdata);
+/** @} */
-/** Move the specified source output to a different source. \since 0.9.5 */
-pa_operation* pa_context_move_source_output_by_index(pa_context *c, uint32_t idx, uint32_t source_idx, pa_context_success_cb_t cb, void* userdata);
+/** \endcond */
PA_C_DECL_END
diff --git a/src/pulse/mainloop-api.c b/src/pulse/mainloop-api.c
index 2e20b446..90aff164 100644
--- a/src/pulse/mainloop-api.c
+++ b/src/pulse/mainloop-api.c
@@ -1,18 +1,18 @@
-/* $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.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
@@ -23,12 +23,12 @@
#include <config.h>
#endif
-#include <assert.h>
#include <stdlib.h>
#include <pulse/xmalloc.h>
+#include <pulse/gccmacro.h>
-#include <pulsecore/gccmacro.h>
+#include <pulsecore/macro.h>
#include "mainloop-api.h"
@@ -39,32 +39,37 @@ struct once_info {
static void once_callback(pa_mainloop_api *m, pa_defer_event *e, void *userdata) {
struct once_info *i = userdata;
- assert(m && i && i->callback);
+ pa_assert(m);
+ pa_assert(i);
+
+ pa_assert(i->callback);
i->callback(m, i->userdata);
- assert(m->defer_free);
+ pa_assert(m->defer_free);
m->defer_free(e);
}
static void free_callback(pa_mainloop_api *m, PA_GCC_UNUSED pa_defer_event *e, void *userdata) {
struct once_info *i = userdata;
- assert(m && i);
+
+ pa_assert(m);
+ pa_assert(i);
pa_xfree(i);
}
void pa_mainloop_api_once(pa_mainloop_api* m, void (*callback)(pa_mainloop_api *m, void *userdata), void *userdata) {
struct once_info *i;
pa_defer_event *e;
- assert(m && callback);
+
+ pa_assert(m);
+ pa_assert(callback);
i = pa_xnew(struct once_info, 1);
i->callback = callback;
i->userdata = userdata;
- assert(m->defer_new);
- e = m->defer_new(m, once_callback, i);
- assert(e);
+ pa_assert(m->defer_new);
+ pa_assert_se(e = m->defer_new(m, once_callback, i));
m->defer_set_destroy(e, free_callback);
}
-
diff --git a/src/pulse/mainloop-api.h b/src/pulse/mainloop-api.h
index 7b7075ae..53c7411e 100644
--- a/src/pulse/mainloop-api.h
+++ b/src/pulse/mainloop-api.h
@@ -1,21 +1,22 @@
#ifndef foomainloopapihfoo
#define foomainloopapihfoo
-/* $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.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
@@ -28,7 +29,7 @@
#include <pulse/cdecl.h>
/** \file
- *
+ *
* Main loop abstraction layer. Both the PulseAudio core and the
* PulseAudio client library use a main loop abstraction layer. Due to
* this it is possible to embed PulseAudio into other
diff --git a/src/pulse/mainloop-signal.c b/src/pulse/mainloop-signal.c
index d651462b..9161dec4 100644
--- a/src/pulse/mainloop-signal.c
+++ b/src/pulse/mainloop-signal.c
@@ -1,18 +1,19 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
+ Copyright 2004-2008 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
@@ -24,7 +25,6 @@
#endif
#include <stdio.h>
-#include <assert.h>
#include <signal.h>
#include <errno.h>
#include <stdlib.h>
@@ -36,12 +36,13 @@
#include <windows.h>
#endif
-#include <pulsecore/core-error.h>
#include <pulse/xmalloc.h>
+#include <pulse/gccmacro.h>
+#include <pulsecore/core-error.h>
#include <pulsecore/core-util.h>
#include <pulsecore/log.h>
-#include <pulsecore/gccmacro.h>
+#include <pulsecore/macro.h>
#include "mainloop-signal.h"
@@ -52,9 +53,9 @@ struct pa_signal_event {
#else
void (*saved_handler)(int sig);
#endif
- void (*callback) (pa_mainloop_api*a, pa_signal_event *e, int sig, void *userdata);
void *userdata;
- void (*destroy_callback) (pa_mainloop_api*a, pa_signal_event*e, void *userdata);
+ pa_signal_cb_t callback;
+ pa_signal_destroy_cb_t destroy_callback;
pa_signal_event *previous, *next;
};
@@ -64,18 +65,25 @@ static pa_io_event* io_event = NULL;
static pa_signal_event *signals = NULL;
static void signal_handler(int sig) {
+ int saved_errno;
+
+ saved_errno = errno;
+
#ifndef HAVE_SIGACTION
signal(sig, signal_handler);
#endif
+
pa_write(signal_pipe[1], &sig, sizeof(sig), NULL);
+
+ errno = saved_errno;
}
static void dispatch(pa_mainloop_api*a, int sig) {
- pa_signal_event*s;
+ pa_signal_event *s;
- for (s = signals; s; s = s->next)
+ for (s = signals; s; s = s->next)
if (s->sig == sig) {
- assert(s->callback);
+ pa_assert(s->callback);
s->callback(a, s, sig, s->userdata);
break;
}
@@ -84,7 +92,12 @@ static void dispatch(pa_mainloop_api*a, int sig) {
static void callback(pa_mainloop_api*a, pa_io_event*e, int fd, pa_io_event_flags_t f, PA_GCC_UNUSED void *userdata) {
ssize_t r;
int sig;
- assert(a && e && f == PA_IO_EVENT_INPUT && e == io_event && fd == signal_pipe[0]);
+
+ pa_assert(a);
+ pa_assert(e);
+ pa_assert(f == PA_IO_EVENT_INPUT);
+ pa_assert(e == io_event);
+ pa_assert(fd == signal_pipe[0]);
if ((r = pa_read(signal_pipe[0], &sig, sizeof(sig), NULL)) < 0) {
if (errno == EAGAIN)
@@ -93,7 +106,7 @@ static void callback(pa_mainloop_api*a, pa_io_event*e, int fd, pa_io_event_flags
pa_log("read(): %s", pa_cstrerror(errno));
return;
}
-
+
if (r != sizeof(sig)) {
pa_log("short read()");
return;
@@ -104,56 +117,59 @@ static void callback(pa_mainloop_api*a, pa_io_event*e, int fd, pa_io_event_flags
int pa_signal_init(pa_mainloop_api *a) {
- assert(!api && a && signal_pipe[0] == -1 && signal_pipe[1] == -1 && !io_event);
+ pa_assert(a);
+ pa_assert(!api);
+ pa_assert(signal_pipe[0] == -1);
+ pa_assert(signal_pipe[1] == -1);
+ pa_assert(!io_event);
if (pipe(signal_pipe) < 0) {
pa_log("pipe(): %s", pa_cstrerror(errno));
return -1;
}
- pa_make_nonblock_fd(signal_pipe[0]);
- pa_make_nonblock_fd(signal_pipe[1]);
- pa_fd_set_cloexec(signal_pipe[0], 1);
- pa_fd_set_cloexec(signal_pipe[1], 1);
+ pa_make_fd_nonblock(signal_pipe[0]);
+ pa_make_fd_nonblock(signal_pipe[1]);
+ pa_make_fd_cloexec(signal_pipe[0]);
+ pa_make_fd_cloexec(signal_pipe[1]);
api = a;
- io_event = api->io_new(api, signal_pipe[0], PA_IO_EVENT_INPUT, callback, NULL);
- assert(io_event);
+ pa_assert_se(io_event = api->io_new(api, signal_pipe[0], PA_IO_EVENT_INPUT, callback, NULL));
return 0;
}
void pa_signal_done(void) {
- assert(api && signal_pipe[0] >= 0 && signal_pipe[1] >= 0 && io_event);
-
while (signals)
pa_signal_free(signals);
-
- api->io_free(io_event);
- io_event = NULL;
- close(signal_pipe[0]);
- close(signal_pipe[1]);
- signal_pipe[0] = signal_pipe[1] = -1;
+ if (io_event) {
+ pa_assert(api);
+ api->io_free(io_event);
+ io_event = NULL;
+ }
+
+ pa_close_pipe(signal_pipe);
api = NULL;
}
-pa_signal_event* pa_signal_new(int sig, void (*_callback) (pa_mainloop_api *api, pa_signal_event*e, int sig, void *userdata), void *userdata) {
+pa_signal_event* pa_signal_new(int sig, pa_signal_cb_t _callback, void *userdata) {
pa_signal_event *e = NULL;
#ifdef HAVE_SIGACTION
struct sigaction sa;
#endif
- assert(sig > 0 && _callback);
-
+ pa_assert(sig > 0);
+ pa_assert(_callback);
+
for (e = signals; e; e = e->next)
if (e->sig == sig)
goto fail;
-
- e = pa_xmalloc(sizeof(pa_signal_event));
+
+ e = pa_xnew(pa_signal_event, 1);
e->sig = sig;
e->callback = _callback;
e->userdata = userdata;
@@ -164,7 +180,7 @@ pa_signal_event* pa_signal_new(int sig, void (*_callback) (pa_mainloop_api *api,
sa.sa_handler = signal_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
-
+
if (sigaction(sig, &sa, &e->saved_sigaction) < 0)
#else
if ((e->saved_handler = signal(sig, signal_handler)) == SIG_ERR)
@@ -183,7 +199,7 @@ fail:
}
void pa_signal_free(pa_signal_event *e) {
- assert(e);
+ pa_assert(e);
if (e->next)
e->next->previous = e->previous;
@@ -193,18 +209,19 @@ void pa_signal_free(pa_signal_event *e) {
signals = e->next;
#ifdef HAVE_SIGACTION
- sigaction(e->sig, &e->saved_sigaction, NULL);
+ pa_assert_se(sigaction(e->sig, &e->saved_sigaction, NULL) == 0);
#else
- signal(e->sig, e->saved_handler);
+ pa_assert_se(signal(e->sig, e->saved_handler) == signal_handler);
#endif
if (e->destroy_callback)
e->destroy_callback(api, e, e->userdata);
-
+
pa_xfree(e);
}
-void pa_signal_set_destroy(pa_signal_event *e, void (*_callback) (pa_mainloop_api *api, pa_signal_event*e, void *userdata)) {
- assert(e);
+void pa_signal_set_destroy(pa_signal_event *e, pa_signal_destroy_cb_t _callback) {
+ pa_assert(e);
+
e->destroy_callback = _callback;
}
diff --git a/src/pulse/mainloop-signal.h b/src/pulse/mainloop-signal.h
index 0721c1f5..a6c16f2f 100644
--- a/src/pulse/mainloop-signal.h
+++ b/src/pulse/mainloop-signal.h
@@ -1,21 +1,22 @@
#ifndef foomainloopsignalhfoo
#define foomainloopsignalhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
+ Copyright 2004-2008 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
@@ -36,23 +37,27 @@ PA_C_DECL_BEGIN
* signals. However, you may hook signal support into an abstract main loop via the routines defined herein.
*/
+/** An opaque UNIX signal event source object */
+typedef struct pa_signal_event pa_signal_event;
+
+typedef void (*pa_signal_cb_t) (pa_mainloop_api *api, pa_signal_event*e, int sig, void *userdata);
+
+typedef void (*pa_signal_destroy_cb_t) (pa_mainloop_api *api, pa_signal_event*e, void *userdata);
+
/** Initialize the UNIX signal subsystem and bind it to the specified main loop */
int pa_signal_init(pa_mainloop_api *api);
/** Cleanup the signal subsystem */
void pa_signal_done(void);
-/** An opaque UNIX signal event source object */
-typedef struct pa_signal_event pa_signal_event;
-
/** Create a new UNIX signal event source object */
-pa_signal_event* pa_signal_new(int sig, void (*callback) (pa_mainloop_api *api, pa_signal_event*e, int sig, void *userdata), void *userdata);
+pa_signal_event* pa_signal_new(int sig, pa_signal_cb_t callback, void *userdata);
/** Free a UNIX signal event source object */
void pa_signal_free(pa_signal_event *e);
/** Set a function that is called when the signal event source is destroyed. Use this to free the userdata argument if required */
-void pa_signal_set_destroy(pa_signal_event *e, void (*callback) (pa_mainloop_api *api, pa_signal_event*e, void *userdata));
+void pa_signal_set_destroy(pa_signal_event *e, pa_signal_destroy_cb_t callback);
PA_C_DECL_END
diff --git a/src/pulse/mainloop.c b/src/pulse/mainloop.c
index f7b15537..aaed3caf 100644
--- a/src/pulse/mainloop.c
+++ b/src/pulse/mainloop.c
@@ -1,18 +1,19 @@
-/* $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
@@ -28,40 +29,39 @@
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
-#include <assert.h>
#include <fcntl.h>
#include <errno.h>
-#ifdef HAVE_SYS_POLL_H
-#include <sys/poll.h>
+#ifdef HAVE_POLL_H
+#include <poll.h>
#else
-#include "../pulsecore/poll.h"
+#include <pulsecore/poll.h>
#endif
-#include "../pulsecore/winsock.h"
-
#ifndef HAVE_PIPE
-#include "../pulsecore/pipe.h"
+#include <pulsecore/pipe.h>
#endif
-#include <pulsecore/core-error.h>
#include <pulse/timeval.h>
#include <pulse/xmalloc.h>
#include <pulsecore/core-util.h>
#include <pulsecore/llist.h>
#include <pulsecore/log.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/winsock.h>
+#include <pulsecore/macro.h>
#include "mainloop.h"
struct pa_io_event {
pa_mainloop *mainloop;
int dead;
-
+
int fd;
pa_io_event_flags_t events;
struct pollfd *pollfd;
-
+
pa_io_event_cb_t callback;
void *userdata;
pa_io_event_destroy_cb_t destroy_callback;
@@ -154,17 +154,17 @@ static pa_io_event* mainloop_io_new(
pa_io_event_flags_t events,
pa_io_event_cb_t callback,
void *userdata) {
-
+
pa_mainloop *m;
pa_io_event *e;
- assert(a);
- assert(a->userdata);
- assert(fd >= 0);
- assert(callback);
-
+ pa_assert(a);
+ pa_assert(a->userdata);
+ pa_assert(fd >= 0);
+ pa_assert(callback);
+
m = a->userdata;
- assert(a == &m->api);
+ pa_assert(a == &m->api);
e = pa_xnew(pa_io_event, 1);
e->mainloop = m;
@@ -173,7 +173,7 @@ static pa_io_event* mainloop_io_new(
e->fd = fd;
e->events = events;
e->pollfd = NULL;
-
+
e->callback = callback;
e->userdata = userdata;
e->destroy_callback = NULL;
@@ -192,7 +192,7 @@ static pa_io_event* mainloop_io_new(
if ((select((SELECT_TYPE_ARG1) fd, NULL, NULL, SELECT_TYPE_ARG234 &xset,
SELECT_TYPE_ARG5 &tv) == -1) &&
(WSAGetLastError() == WSAENOTSOCK)) {
- pa_log_warn("WARNING: cannot monitor non-socket file descriptors.");
+ pa_log_warn("Cannot monitor non-socket file descriptors.");
e->dead = 1;
}
}
@@ -208,12 +208,12 @@ static pa_io_event* mainloop_io_new(
}
static void mainloop_io_enable(pa_io_event *e, pa_io_event_flags_t events) {
- assert(e);
- assert(!e->dead);
+ pa_assert(e);
+ pa_assert(!e->dead);
if (e->events == events)
return;
-
+
e->events = events;
if (e->pollfd)
@@ -225,8 +225,8 @@ static void mainloop_io_enable(pa_io_event *e, pa_io_event_flags_t events) {
}
static void mainloop_io_free(pa_io_event *e) {
- assert(e);
- assert(!e->dead);
+ pa_assert(e);
+ pa_assert(!e->dead);
e->dead = 1;
e->mainloop->io_events_please_scan ++;
@@ -238,8 +238,8 @@ static void mainloop_io_free(pa_io_event *e) {
}
static void mainloop_io_set_destroy(pa_io_event *e, pa_io_event_destroy_cb_t callback) {
- assert(e);
-
+ pa_assert(e);
+
e->destroy_callback = callback;
}
@@ -252,12 +252,12 @@ static pa_defer_event* mainloop_defer_new(
pa_mainloop *m;
pa_defer_event *e;
- assert(a);
- assert(a->userdata);
- assert(callback);
-
+ pa_assert(a);
+ pa_assert(a->userdata);
+ pa_assert(callback);
+
m = a->userdata;
- assert(a == &m->api);
+ pa_assert(a == &m->api);
e = pa_xnew(pa_defer_event, 1);
e->mainloop = m;
@@ -265,7 +265,7 @@ static pa_defer_event* mainloop_defer_new(
e->enabled = 1;
m->n_enabled_defer_events++;
-
+
e->callback = callback;
e->userdata = userdata;
e->destroy_callback = NULL;
@@ -278,36 +278,37 @@ static pa_defer_event* mainloop_defer_new(
}
static void mainloop_defer_enable(pa_defer_event *e, int b) {
- assert(e);
- assert(!e->dead);
+ pa_assert(e);
+ pa_assert(!e->dead);
if (e->enabled && !b) {
- assert(e->mainloop->n_enabled_defer_events > 0);
+ pa_assert(e->mainloop->n_enabled_defer_events > 0);
e->mainloop->n_enabled_defer_events--;
} else if (!e->enabled && b) {
e->mainloop->n_enabled_defer_events++;
pa_mainloop_wakeup(e->mainloop);
}
-
+
e->enabled = b;
}
static void mainloop_defer_free(pa_defer_event *e) {
- assert(e);
- assert(!e->dead);
+ pa_assert(e);
+ pa_assert(!e->dead);
e->dead = 1;
e->mainloop->defer_events_please_scan ++;
if (e->enabled) {
- assert(e->mainloop->n_enabled_defer_events > 0);
+ pa_assert(e->mainloop->n_enabled_defer_events > 0);
e->mainloop->n_enabled_defer_events--;
+ e->enabled = 0;
}
}
static void mainloop_defer_set_destroy(pa_defer_event *e, pa_defer_event_destroy_cb_t callback) {
- assert(e);
- assert(!e->dead);
+ pa_assert(e);
+ pa_assert(!e->dead);
e->destroy_callback = callback;
}
@@ -318,16 +319,16 @@ static pa_time_event* mainloop_time_new(
const struct timeval *tv,
pa_time_event_cb_t callback,
void *userdata) {
-
+
pa_mainloop *m;
pa_time_event *e;
- assert(a);
- assert(a->userdata);
- assert(callback);
-
+ pa_assert(a);
+ pa_assert(a->userdata);
+ pa_assert(callback);
+
m = a->userdata;
- assert(a == &m->api);
+ pa_assert(a == &m->api);
e = pa_xnew(pa_time_event, 1);
e->mainloop = m;
@@ -339,7 +340,7 @@ static pa_time_event* mainloop_time_new(
m->n_enabled_time_events++;
if (m->cached_next_time_event) {
- assert(m->cached_next_time_event->enabled);
+ pa_assert(m->cached_next_time_event->enabled);
if (pa_timeval_cmp(tv, &m->cached_next_time_event->timeval) < 0)
m->cached_next_time_event = e;
@@ -354,16 +355,16 @@ static pa_time_event* mainloop_time_new(
if (e->enabled)
pa_mainloop_wakeup(m);
-
+
return e;
}
static void mainloop_time_restart(pa_time_event *e, const struct timeval *tv) {
- assert(e);
- assert(!e->dead);
+ pa_assert(e);
+ pa_assert(!e->dead);
if (e->enabled && !tv) {
- assert(e->mainloop->n_enabled_time_events > 0);
+ pa_assert(e->mainloop->n_enabled_time_events > 0);
e->mainloop->n_enabled_time_events--;
} else if (!e->enabled && tv)
e->mainloop->n_enabled_time_events++;
@@ -374,8 +375,8 @@ static void mainloop_time_restart(pa_time_event *e, const struct timeval *tv) {
}
if (e->mainloop->cached_next_time_event && e->enabled) {
- assert(e->mainloop->cached_next_time_event->enabled);
-
+ pa_assert(e->mainloop->cached_next_time_event->enabled);
+
if (pa_timeval_cmp(tv, &e->mainloop->cached_next_time_event->timeval) < 0)
e->mainloop->cached_next_time_event = e;
} else if (e->mainloop->cached_next_time_event == e)
@@ -383,26 +384,27 @@ static void mainloop_time_restart(pa_time_event *e, const struct timeval *tv) {
}
static void mainloop_time_free(pa_time_event *e) {
- assert(e);
- assert(!e->dead);
+ pa_assert(e);
+ pa_assert(!e->dead);
e->dead = 1;
e->mainloop->time_events_please_scan ++;
if (e->enabled) {
- assert(e->mainloop->n_enabled_time_events > 0);
+ pa_assert(e->mainloop->n_enabled_time_events > 0);
e->mainloop->n_enabled_time_events--;
+ e->enabled = 0;
}
if (e->mainloop->cached_next_time_event == e)
e->mainloop->cached_next_time_event = NULL;
-
+
/* no wakeup needed here. Think about it! */
}
static void mainloop_time_set_destroy(pa_time_event *e, pa_time_event_destroy_cb_t callback) {
- assert(e);
- assert(!e->dead);
+ pa_assert(e);
+ pa_assert(!e->dead);
e->destroy_callback = callback;
}
@@ -411,15 +413,15 @@ static void mainloop_time_set_destroy(pa_time_event *e, pa_time_event_destroy_cb
static void mainloop_quit(pa_mainloop_api*a, int retval) {
pa_mainloop *m;
-
- assert(a);
- assert(a->userdata);
+
+ pa_assert(a);
+ pa_assert(a->userdata);
m = a->userdata;
- assert(a == &m->api);
+ pa_assert(a == &m->api);
pa_mainloop_quit(m, retval);
}
-
+
static const pa_mainloop_api vtable = {
.userdata = NULL,
@@ -432,12 +434,12 @@ static const pa_mainloop_api vtable = {
.time_restart = mainloop_time_restart,
.time_free = mainloop_time_free,
.time_set_destroy = mainloop_time_set_destroy,
-
+
.defer_new = mainloop_defer_new,
.defer_enable = mainloop_defer_enable,
.defer_free = mainloop_defer_free,
.defer_set_destroy = mainloop_defer_set_destroy,
-
+
.quit = mainloop_quit,
};
@@ -453,8 +455,10 @@ pa_mainloop *pa_mainloop_new(void) {
return NULL;
}
- pa_make_nonblock_fd(m->wakeup_pipe[0]);
- pa_make_nonblock_fd(m->wakeup_pipe[1]);
+ pa_make_fd_nonblock(m->wakeup_pipe[0]);
+ pa_make_fd_nonblock(m->wakeup_pipe[1]);
+ pa_make_fd_cloexec(m->wakeup_pipe[0]);
+ pa_make_fd_cloexec(m->wakeup_pipe[1]);
m->wakeup_requested = 0;
PA_LLIST_HEAD_INIT(pa_io_event, m->io_events);
@@ -466,7 +470,7 @@ pa_mainloop *pa_mainloop_new(void) {
m->cached_next_time_event = NULL;
m->prepared_timeout = 0;
-
+
m->pollfds = NULL;
m->max_pollfds = m->n_pollfds = 0;
m->rebuild_pollfds = 1;
@@ -481,7 +485,7 @@ pa_mainloop *pa_mainloop_new(void) {
m->poll_func = NULL;
m->poll_func_userdata = NULL;
m->poll_func_ret = -1;
-
+
return m;
}
@@ -494,18 +498,18 @@ static void cleanup_io_events(pa_mainloop *m, int force) {
if (!force && m->io_events_please_scan <= 0)
break;
-
+
if (force || e->dead) {
PA_LLIST_REMOVE(pa_io_event, m->io_events, e);
if (e->dead) {
- assert(m->io_events_please_scan > 0);
+ pa_assert(m->io_events_please_scan > 0);
m->io_events_please_scan--;
}
-
+
if (e->destroy_callback)
e->destroy_callback(&m->api, e, e->userdata);
-
+
pa_xfree(e);
m->rebuild_pollfds = 1;
@@ -514,7 +518,7 @@ static void cleanup_io_events(pa_mainloop *m, int force) {
e = n;
}
- assert(m->io_events_please_scan == 0);
+ pa_assert(m->io_events_please_scan == 0);
}
static void cleanup_time_events(pa_mainloop *m, int force) {
@@ -526,30 +530,31 @@ static void cleanup_time_events(pa_mainloop *m, int force) {
if (!force && m->time_events_please_scan <= 0)
break;
-
+
if (force || e->dead) {
PA_LLIST_REMOVE(pa_time_event, m->time_events, e);
if (e->dead) {
- assert(m->time_events_please_scan > 0);
+ pa_assert(m->time_events_please_scan > 0);
m->time_events_please_scan--;
}
if (!e->dead && e->enabled) {
- assert(m->n_enabled_time_events > 0);
+ pa_assert(m->n_enabled_time_events > 0);
m->n_enabled_time_events--;
+ e->enabled = 0;
}
-
+
if (e->destroy_callback)
e->destroy_callback(&m->api, e, e->userdata);
-
+
pa_xfree(e);
}
e = n;
}
- assert(m->time_events_please_scan == 0);
+ pa_assert(m->time_events_please_scan == 0);
}
static void cleanup_defer_events(pa_mainloop *m, int force) {
@@ -561,35 +566,36 @@ static void cleanup_defer_events(pa_mainloop *m, int force) {
if (!force && m->defer_events_please_scan <= 0)
break;
-
+
if (force || e->dead) {
PA_LLIST_REMOVE(pa_defer_event, m->defer_events, e);
if (e->dead) {
- assert(m->defer_events_please_scan > 0);
+ pa_assert(m->defer_events_please_scan > 0);
m->defer_events_please_scan--;
}
if (!e->dead && e->enabled) {
- assert(m->n_enabled_defer_events > 0);
+ pa_assert(m->n_enabled_defer_events > 0);
m->n_enabled_defer_events--;
+ e->enabled = 0;
}
-
+
if (e->destroy_callback)
e->destroy_callback(&m->api, e, e->userdata);
-
+
pa_xfree(e);
}
e = n;
}
- assert(m->defer_events_please_scan == 0);
+ pa_assert(m->defer_events_please_scan == 0);
}
void pa_mainloop_free(pa_mainloop* m) {
- assert(m);
+ pa_assert(m);
cleanup_io_events(m, 1);
cleanup_defer_events(m, 1);
@@ -597,16 +603,13 @@ void pa_mainloop_free(pa_mainloop* m) {
pa_xfree(m->pollfds);
- if (m->wakeup_pipe[0] >= 0)
- close(m->wakeup_pipe[0]);
- if (m->wakeup_pipe[1] >= 0)
- close(m->wakeup_pipe[1]);
+ pa_close_pipe(m->wakeup_pipe);
pa_xfree(m);
}
static void scan_dead(pa_mainloop *m) {
- assert(m);
+ pa_assert(m);
if (m->io_events_please_scan)
cleanup_io_events(m, 0);
@@ -663,13 +666,14 @@ static int dispatch_pollfds(pa_mainloop *m) {
pa_io_event *e;
int r = 0, k;
- assert(m->poll_func_ret > 0);
-
+ pa_assert(m->poll_func_ret > 0);
+
for (e = m->io_events, k = m->poll_func_ret; e && !m->quit && k > 0; e = e->next) {
if (e->dead || !e->pollfd || !e->pollfd->revents)
continue;
-
- assert(e->pollfd->fd == e->fd && e->callback);
+
+ pa_assert(e->pollfd->fd == e->fd);
+ pa_assert(e->callback);
e->callback(&m->api, e, e->fd, map_flags_from_libc(e->pollfd->revents), e->userdata);
e->pollfd->revents = 0;
r++;
@@ -690,8 +694,8 @@ static int dispatch_defer(pa_mainloop *m) {
for (e = m->defer_events; e && !m->quit; e = e->next) {
if (e->dead || !e->enabled)
continue;
-
- assert(e->callback);
+
+ pa_assert(e->callback);
e->callback(&m->api, e, e->userdata);
r++;
}
@@ -701,11 +705,11 @@ static int dispatch_defer(pa_mainloop *m) {
static pa_time_event* find_next_time_event(pa_mainloop *m) {
pa_time_event *t, *n = NULL;
- assert(m);
+ pa_assert(m);
if (m->cached_next_time_event)
return m->cached_next_time_event;
-
+
for (t = m->time_events; t; t = t->next) {
if (t->dead || !t->enabled)
@@ -733,11 +737,11 @@ static int calc_next_timeout(pa_mainloop *m) {
return -1;
t = find_next_time_event(m);
- assert(t);
+ pa_assert(t);
if (t->timeval.tv_sec <= 0)
return 0;
-
+
pa_gettimeofday(&now);
if (pa_timeval_cmp(&t->timeval, &now) <= 0)
@@ -751,7 +755,7 @@ static int dispatch_timeout(pa_mainloop *m) {
pa_time_event *e;
struct timeval now;
int r = 0;
- assert(m);
+ pa_assert(m);
if (m->n_enabled_time_events <= 0)
return 0;
@@ -759,12 +763,12 @@ static int dispatch_timeout(pa_mainloop *m) {
pa_gettimeofday(&now);
for (e = m->time_events; e && !m->quit; e = e->next) {
-
+
if (e->dead || !e->enabled)
continue;
if (pa_timeval_cmp(&e->timeval, &now) <= 0) {
- assert(e->callback);
+ pa_assert(e->callback);
/* Disable time event */
mainloop_time_restart(e, NULL);
@@ -780,7 +784,7 @@ static int dispatch_timeout(pa_mainloop *m) {
void pa_mainloop_wakeup(pa_mainloop *m) {
char c = 'W';
- assert(m);
+ pa_assert(m);
if (m->wakeup_pipe[1] >= 0 && m->state == STATE_POLLING) {
pa_write(m->wakeup_pipe[1], &c, sizeof(c), &m->wakeup_pipe_type);
@@ -791,7 +795,7 @@ void pa_mainloop_wakeup(pa_mainloop *m) {
static void clear_wakeup(pa_mainloop *m) {
char c[10];
- assert(m);
+ pa_assert(m);
if (m->wakeup_pipe[0] < 0)
return;
@@ -803,8 +807,8 @@ static void clear_wakeup(pa_mainloop *m) {
}
int pa_mainloop_prepare(pa_mainloop *m, int timeout) {
- assert(m);
- assert(m->state == STATE_PASSIVE);
+ pa_assert(m);
+ pa_assert(m->state == STATE_PASSIVE);
clear_wakeup(m);
scan_dead(m);
@@ -815,7 +819,7 @@ int pa_mainloop_prepare(pa_mainloop *m, int timeout) {
if (m->n_enabled_defer_events <= 0) {
if (m->rebuild_pollfds)
rebuild_pollfds(m);
-
+
m->prepared_timeout = calc_next_timeout(m);
if (timeout >= 0 && (timeout < m->prepared_timeout || m->prepared_timeout < 0))
m->prepared_timeout = timeout;
@@ -830,8 +834,8 @@ quit:
}
int pa_mainloop_poll(pa_mainloop *m) {
- assert(m);
- assert(m->state == STATE_PREPARED);
+ pa_assert(m);
+ pa_assert(m->state == STATE_PREPARED);
if (m->quit)
goto quit;
@@ -841,8 +845,8 @@ int pa_mainloop_poll(pa_mainloop *m) {
if (m->n_enabled_defer_events )
m->poll_func_ret = 0;
else {
- assert(!m->rebuild_pollfds);
-
+ pa_assert(!m->rebuild_pollfds);
+
if (m->poll_func)
m->poll_func_ret = m->poll_func(m->pollfds, m->n_pollfds, m->prepared_timeout, m->poll_func_userdata);
else
@@ -867,28 +871,28 @@ quit:
int pa_mainloop_dispatch(pa_mainloop *m) {
int dispatched = 0;
- assert(m);
- assert(m->state == STATE_POLLED);
+ pa_assert(m);
+ pa_assert(m->state == STATE_POLLED);
if (m->quit)
goto quit;
-
+
if (m->n_enabled_defer_events)
dispatched += dispatch_defer(m);
else {
- if (m->n_enabled_time_events)
+ if (m->n_enabled_time_events)
dispatched += dispatch_timeout(m);
-
+
if (m->quit)
goto quit;
if (m->poll_func_ret > 0)
dispatched += dispatch_pollfds(m);
}
-
+
if (m->quit)
goto quit;
-
+
m->state = STATE_PASSIVE;
return dispatched;
@@ -899,13 +903,13 @@ quit:
}
int pa_mainloop_get_retval(pa_mainloop *m) {
- assert(m);
+ pa_assert(m);
return m->retval;
}
int pa_mainloop_iterate(pa_mainloop *m, int block, int *retval) {
int r;
- assert(m);
+ pa_assert(m);
if ((r = pa_mainloop_prepare(m, block ? -1 : 0)) < 0)
goto quit;
@@ -919,7 +923,7 @@ int pa_mainloop_iterate(pa_mainloop *m, int block, int *retval) {
return r;
quit:
-
+
if ((r == -2) && retval)
*retval = pa_mainloop_get_retval(m);
return r;
@@ -927,7 +931,7 @@ quit:
int pa_mainloop_run(pa_mainloop *m, int *retval) {
int r;
-
+
while ((r = pa_mainloop_iterate(m, 1, retval)) >= 0);
if (r == -2)
@@ -939,7 +943,7 @@ int pa_mainloop_run(pa_mainloop *m, int *retval) {
}
void pa_mainloop_quit(pa_mainloop *m, int retval) {
- assert(m);
+ pa_assert(m);
m->quit = 1;
m->retval = retval;
@@ -947,12 +951,12 @@ void pa_mainloop_quit(pa_mainloop *m, int retval) {
}
pa_mainloop_api* pa_mainloop_get_api(pa_mainloop*m) {
- assert(m);
+ pa_assert(m);
return &m->api;
}
void pa_mainloop_set_poll_func(pa_mainloop *m, pa_poll_func poll_func, void *userdata) {
- assert(m);
+ pa_assert(m);
m->poll_func = poll_func;
m->poll_func_userdata = userdata;
diff --git a/src/pulse/mainloop.h b/src/pulse/mainloop.h
index 8abd8fe4..907e94a7 100644
--- a/src/pulse/mainloop.h
+++ b/src/pulse/mainloop.h
@@ -1,21 +1,22 @@
#ifndef foomainloophfoo
#define foomainloophfoo
-/* $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
@@ -65,7 +66,7 @@ struct pollfd;
*/
/** \file
- *
+ *
* A minimal main loop implementation based on the C library's poll()
* function. Using the routines defined herein you may create a simple
* main loop supporting the generic main loop abstraction layer as
diff --git a/src/pulse/operation.c b/src/pulse/operation.c
index 8d896d7d..13b470a8 100644
--- a/src/pulse/operation.c
+++ b/src/pulse/operation.c
@@ -1,18 +1,18 @@
-/* $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
@@ -23,21 +23,26 @@
#include <config.h>
#endif
-#include <assert.h>
-
#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/flist.h>
#include "internal.h"
#include "operation.h"
+PA_STATIC_FLIST_DECLARE(operations, 0, pa_xfree);
+
pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t cb, void *userdata) {
pa_operation *o;
- assert(c);
+ pa_assert(c);
+
+ if (!(o = pa_flist_pop(PA_STATIC_FLIST_GET(operations))))
+ o = pa_xnew(pa_operation, 1);
- o = pa_xnew(pa_operation, 1);
- o->ref = 1;
+ PA_REFCNT_INIT(o);
o->context = c;
o->stream = s;
+ o->private = NULL;
o->state = PA_OPERATION_RUNNING;
o->callback = cb;
@@ -46,32 +51,50 @@ pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t cb
/* Refcounting is strictly one-way: from the "bigger" to the "smaller" object. */
PA_LLIST_PREPEND(pa_operation, c->operations, o);
pa_operation_ref(o);
-
+
return o;
}
pa_operation *pa_operation_ref(pa_operation *o) {
- assert(o);
- assert(o->ref >= 1);
-
- o->ref++;
+ pa_assert(o);
+ pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+ PA_REFCNT_INC(o);
return o;
}
-
void pa_operation_unref(pa_operation *o) {
- assert(o);
- assert(o->ref >= 1);
+ pa_assert(o);
+ pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+ if (PA_REFCNT_DEC(o) <= 0) {
+ pa_assert(!o->context);
+ pa_assert(!o->stream);
- if ((--(o->ref)) == 0) {
- assert(!o->context);
- assert(!o->stream);
- pa_xfree(o);
+ if (pa_flist_push(PA_STATIC_FLIST_GET(operations), o) < 0)
+ pa_xfree(o);
}
}
+static void operation_unlink(pa_operation *o) {
+ pa_assert(o);
+
+ if (o->context) {
+ pa_assert(PA_REFCNT_VALUE(o) >= 2);
+
+ PA_LLIST_REMOVE(pa_operation, o->context->operations, o);
+ pa_operation_unref(o);
+
+ o->context = NULL;
+ }
+
+ o->stream = NULL;
+ o->callback = NULL;
+ o->userdata = NULL;
+}
+
static void operation_set_state(pa_operation *o, pa_operation_state_t st) {
- assert(o);
- assert(o->ref >= 1);
+ pa_assert(o);
+ pa_assert(PA_REFCNT_VALUE(o) >= 1);
if (st == o->state)
return;
@@ -80,41 +103,29 @@ static void operation_set_state(pa_operation *o, pa_operation_state_t st) {
o->state = st;
- if ((o->state == PA_OPERATION_DONE) || (o->state == PA_OPERATION_CANCELED)) {
-
- if (o->context) {
- assert(o->ref >= 2);
-
- PA_LLIST_REMOVE(pa_operation, o->context->operations, o);
- pa_operation_unref(o);
- }
-
- o->context = NULL;
- o->stream = NULL;
- o->callback = NULL;
- o->userdata = NULL;
- }
+ if ((o->state == PA_OPERATION_DONE) || (o->state == PA_OPERATION_CANCELED))
+ operation_unlink(o);
pa_operation_unref(o);
}
void pa_operation_cancel(pa_operation *o) {
- assert(o);
- assert(o->ref >= 1);
-
+ pa_assert(o);
+ pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
operation_set_state(o, PA_OPERATION_CANCELED);
}
void pa_operation_done(pa_operation *o) {
- assert(o);
- assert(o->ref >= 1);
-
+ pa_assert(o);
+ pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
operation_set_state(o, PA_OPERATION_DONE);
}
pa_operation_state_t pa_operation_get_state(pa_operation *o) {
- assert(o);
- assert(o->ref >= 1);
+ pa_assert(o);
+ pa_assert(PA_REFCNT_VALUE(o) >= 1);
return o->state;
}
diff --git a/src/pulse/operation.h b/src/pulse/operation.h
index b544e08e..188e2cb9 100644
--- a/src/pulse/operation.h
+++ b/src/pulse/operation.h
@@ -1,21 +1,21 @@
#ifndef foooperationhfoo
#define foooperationhfoo
-/* $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
diff --git a/src/pulse/proplist.c b/src/pulse/proplist.c
new file mode 100644
index 00000000..74aea20a
--- /dev/null
+++ b/src/pulse/proplist.c
@@ -0,0 +1,321 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2007 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/utf8.h>
+
+#include <pulsecore/hashmap.h>
+#include <pulsecore/strbuf.h>
+#include <pulsecore/core-util.h>
+
+#include "proplist.h"
+
+struct property {
+ char *key;
+ void *value;
+ size_t nbytes;
+};
+
+#define MAKE_HASHMAP(p) ((pa_hashmap*) (p))
+#define MAKE_PROPLIST(p) ((pa_proplist*) (p))
+
+static pa_bool_t property_name_valid(const char *key) {
+
+ if (!pa_utf8_valid(key))
+ return FALSE;
+
+ if (strlen(key) <= 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static void property_free(struct property *prop) {
+ pa_assert(prop);
+
+ pa_xfree(prop->key);
+ pa_xfree(prop->value);
+ pa_xfree(prop);
+}
+
+pa_proplist* pa_proplist_new(void) {
+ return MAKE_PROPLIST(pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func));
+}
+
+void pa_proplist_free(pa_proplist* p) {
+ pa_assert(p);
+
+ pa_proplist_clear(p);
+ pa_hashmap_free(MAKE_HASHMAP(p), NULL, NULL);
+}
+
+/** Will accept only valid UTF-8 */
+int pa_proplist_sets(pa_proplist *p, const char *key, const char *value) {
+ struct property *prop;
+ pa_bool_t add = FALSE;
+
+ pa_assert(p);
+ pa_assert(key);
+
+ if (!property_name_valid(key) || !pa_utf8_valid(value))
+ return -1;
+
+ if (!(prop = pa_hashmap_get(MAKE_HASHMAP(p), key))) {
+ prop = pa_xnew(struct property, 1);
+ prop->key = pa_xstrdup(key);
+ add = TRUE;
+ } else
+ pa_xfree(prop->value);
+
+ prop->value = pa_xstrdup(value);
+ prop->nbytes = strlen(value)+1;
+
+ if (add)
+ pa_hashmap_put(MAKE_HASHMAP(p), prop->key, prop);
+
+ return 0;
+}
+
+/** Will accept only valid UTF-8 */
+int pa_proplist_setf(pa_proplist *p, const char *key, const char *format, ...) {
+ va_list ap;
+ int r;
+ char *t;
+
+ pa_assert(p);
+ pa_assert(key);
+
+ if (!property_name_valid(key) || !pa_utf8_valid(format))
+ return -1;
+
+ va_start(ap, format);
+ t = pa_vsprintf_malloc(format, ap);
+ va_end(ap);
+
+ r = pa_proplist_sets(p, key, t);
+
+ pa_xfree(t);
+ return r;
+}
+
+int pa_proplist_set(pa_proplist *p, const char *key, const void *data, size_t nbytes) {
+ struct property *prop;
+ pa_bool_t add = FALSE;
+
+ pa_assert(p);
+ pa_assert(key);
+
+ if (!property_name_valid(key))
+ return -1;
+
+ if (!(prop = pa_hashmap_get(MAKE_HASHMAP(p), key))) {
+ prop = pa_xnew(struct property, 1);
+ prop->key = pa_xstrdup(key);
+ add = TRUE;
+ } else
+ pa_xfree(prop->value);
+
+ prop->value = pa_xmemdup(data, nbytes);
+ prop->nbytes = nbytes;
+
+ if (add)
+ pa_hashmap_put(MAKE_HASHMAP(p), prop->key, prop);
+
+ return 0;
+}
+
+const char *pa_proplist_gets(pa_proplist *p, const char *key) {
+ struct property *prop;
+
+ pa_assert(p);
+ pa_assert(key);
+
+ if (!property_name_valid(key))
+ return NULL;
+
+ if (!(prop = pa_hashmap_get(MAKE_HASHMAP(p), key)))
+ return NULL;
+
+ if (prop->nbytes <= 0)
+ return NULL;
+
+ if (((char*) prop->value)[prop->nbytes-1] != 0)
+ return NULL;
+
+ if (strlen((char*) prop->value) != prop->nbytes-1)
+ return NULL;
+
+ if (!pa_utf8_valid((char*) prop->value))
+ return NULL;
+
+ return (char*) prop->value;
+}
+
+int pa_proplist_get(pa_proplist *p, const char *key, const void **data, size_t *nbytes) {
+ struct property *prop;
+
+ pa_assert(p);
+ pa_assert(key);
+
+ if (!property_name_valid(key))
+ return -1;
+
+ if (!(prop = pa_hashmap_get(MAKE_HASHMAP(p), key)))
+ return -1;
+
+ *data = prop->value;
+ *nbytes = prop->nbytes;
+
+ return 0;
+}
+
+void pa_proplist_update(pa_proplist *p, pa_update_mode_t mode, pa_proplist *other) {
+ struct property *prop;
+ void *state = NULL;
+
+ pa_assert(p);
+ pa_assert(mode == PA_UPDATE_SET || mode == PA_UPDATE_MERGE || mode == PA_UPDATE_REPLACE);
+ pa_assert(other);
+
+ if (mode == PA_UPDATE_SET)
+ pa_proplist_clear(p);
+
+ while ((prop = pa_hashmap_iterate(MAKE_HASHMAP(other), &state, NULL))) {
+
+ if (mode == PA_UPDATE_MERGE && pa_proplist_contains(p, prop->key))
+ continue;
+
+ pa_assert_se(pa_proplist_set(p, prop->key, prop->value, prop->nbytes) == 0);
+ }
+}
+
+int pa_proplist_unset(pa_proplist *p, const char *key) {
+ struct property *prop;
+
+ pa_assert(p);
+ pa_assert(key);
+
+ if (!property_name_valid(key))
+ return -1;
+
+ if (!(prop = pa_hashmap_remove(MAKE_HASHMAP(p), key)))
+ return -2;
+
+ property_free(prop);
+ return 0;
+}
+
+int pa_proplist_unset_many(pa_proplist *p, const char * const keys[]) {
+ const char * const * k;
+ int n = 0;
+
+ pa_assert(p);
+ pa_assert(keys);
+
+ for (k = keys; *k; k++)
+ if (!property_name_valid(*k))
+ return -1;
+
+ for (k = keys; *k; k++)
+ if (pa_proplist_unset(p, *k) >= 0)
+ n++;
+
+ return n;
+}
+
+const char *pa_proplist_iterate(pa_proplist *p, void **state) {
+ struct property *prop;
+
+ if (!(prop = pa_hashmap_iterate(MAKE_HASHMAP(p), state, NULL)))
+ return NULL;
+
+ return prop->key;
+}
+
+char *pa_proplist_to_string(pa_proplist *p) {
+ const char *key;
+ void *state = NULL;
+ pa_strbuf *buf;
+
+ pa_assert(p);
+
+ buf = pa_strbuf_new();
+
+ while ((key = pa_proplist_iterate(p, &state))) {
+
+ const char *v;
+
+ if ((v = pa_proplist_gets(p, key)))
+ pa_strbuf_printf(buf, "%s = \"%s\"\n", key, v);
+ else {
+ const void *value;
+ size_t nbytes;
+ char *c;
+
+ pa_assert_se(pa_proplist_get(p, key, &value, &nbytes) == 0);
+ c = pa_xnew(char, nbytes*2+1);
+ pa_hexstr((const uint8_t*) value, nbytes, c, nbytes*2+1);
+
+ pa_strbuf_printf(buf, "%s = hex:%s\n", key, c);
+ pa_xfree(c);
+ }
+ }
+
+ return pa_strbuf_tostring_free(buf);
+}
+
+int pa_proplist_contains(pa_proplist *p, const char *key) {
+ pa_assert(p);
+ pa_assert(key);
+
+ if (!property_name_valid(key))
+ return -1;
+
+ if (!(pa_hashmap_get(MAKE_HASHMAP(p), key)))
+ return 0;
+
+ return 1;
+}
+
+void pa_proplist_clear(pa_proplist *p) {
+ struct property *prop;
+ pa_assert(p);
+
+ while ((prop = pa_hashmap_steal_first(MAKE_HASHMAP(p))))
+ property_free(prop);
+}
+
+pa_proplist* pa_proplist_copy(pa_proplist *template) {
+ pa_proplist *p;
+
+ pa_assert_se(p = pa_proplist_new());
+
+ if (template)
+ pa_proplist_update(p, PA_UPDATE_REPLACE, template);
+
+ return p;
+}
diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h
new file mode 100644
index 00000000..f75cca54
--- /dev/null
+++ b/src/pulse/proplist.h
@@ -0,0 +1,217 @@
+#ifndef foopulseproplisthfoo
+#define foopulseproplisthfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2007 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <sys/types.h>
+
+#include <pulse/cdecl.h>
+#include <pulse/gccmacro.h>
+
+PA_C_DECL_BEGIN
+
+/* Defined properties:
+ *
+ * media.name "Guns'N'Roses: Civil War"
+ * media.title "Civil War"
+ * media.artist "Guns'N'Roses"
+ * media.language "de_DE"
+ * media.filename
+ * media.icon
+ * media.icon_name
+ * media.role video, music, game, event, phone, production, filter, abstract, stream
+ * event.id button-click, session-login
+ * event.mouse.x
+ * event.mouse.y
+ * event.mouse.hpos
+ * event.mouse.vpos
+ * event.mouse.button
+ * window.name
+ * window.id
+ * window.icon
+ * window.icon_name
+ * window.x11.display
+ * window.x11.screen
+ * window.x11.monitor
+ * window.x11.xid
+ * application.name "Rhythmbox Media Player"
+ * application.id "org.gnome.rhythmbox"
+ * application.version
+ * application.icon
+ * application.icon_name
+ * application.language
+ * application.process.id
+ * application.process.binary
+ * application.process.user
+ * application.process.host
+ * device.string
+ * device.api oss, alsa, sunaudio
+ * device.description
+ * device.bus_path
+ * device.serial
+ * device.vendor_product_id
+ * device.class sound, modem, monitor, filter, abstract
+ * device.form_factor laptop-speakers, external-speakers, telephone, tv-capture, webcam-capture, microphone-capture, headset
+ * device.connector isa, pci, usb, firewire, bluetooth
+ * device.access_mode mmap, mmap_rewrite, serial
+ * device.master_device
+ * device.bufferin.buffer_size
+ * device.bufferin.fragment_size
+ */
+#define PA_PROP_MEDIA_NAME "media.name"
+#define PA_PROP_MEDIA_TITLE "media.title"
+#define PA_PROP_MEDIA_ARTIST "media.artist"
+#define PA_PROP_MEDIA_LANGUAGE "media.language"
+#define PA_PROP_MEDIA_FILENAME "media.filename"
+#define PA_PROP_MEDIA_ICON "media.icon"
+#define PA_PROP_MEDIA_ICON_NAME "media.icon_name"
+#define PA_PROP_MEDIA_ROLE "media.role"
+#define PA_PROP_EVENT_ID "event.id"
+#define PA_PROP_EVENT_MOUSE_X "event.mouse.x"
+#define PA_PROP_EVENT_MOUSE_Y "event.mouse.y"
+#define PA_PROP_EVENT_MOUSE_HPOS "event.mouse.hpos"
+#define PA_PROP_EVENT_MOUSE_VPOS "event.mouse.vpos"
+#define PA_PROP_EVENT_MOUSE_BUTTON "event.mouse.button"
+#define PA_PROP_WINDOW_NAME "window.name"
+#define PA_PROP_WINDOW_ID "window.id"
+#define PA_PROP_WINDOW_ICON "window.icon"
+#define PA_PROP_WINDOW_ICON_NAME "window.icon_name"
+#define PA_PROP_WINDOW_X11_DISPLAY "window.x11.display"
+#define PA_PROP_WINDOW_X11_SCREEN "window.x11.screen"
+#define PA_PROP_WINDOW_X11_MONITOR "window.x11.monitor"
+#define PA_PROP_WINDOW_X11_XID "window.x11.xid"
+#define PA_PROP_APPLICATION_NAME "application.name"
+#define PA_PROP_APPLICATION_ID "application.id"
+#define PA_PROP_APPLICATION_VERSION "application.version"
+#define PA_PROP_APPLICATION_ICON "application.icon"
+#define PA_PROP_APPLICATION_ICON_NAME "application.icon_name"
+#define PA_PROP_APPLICATION_LANGUAGE "application.language"
+#define PA_PROP_APPLICATION_PROCESS_ID "application.process.id"
+#define PA_PROP_APPLICATION_PROCESS_BINARY "application.process.binary"
+#define PA_PROP_APPLICATION_PROCESS_USER "application.process.user"
+#define PA_PROP_APPLICATION_PROCESS_HOST "application.process.host"
+#define PA_PROP_DEVICE_STRING "device.string"
+#define PA_PROP_DEVICE_API "device.api"
+#define PA_PROP_DEVICE_DESCRIPTION "device.description"
+#define PA_PROP_DEVICE_BUS_PATH "device.bus_path"
+#define PA_PROP_DEVICE_SERIAL "device.serial"
+#define PA_PROP_DEVICE_VENDOR_PRODUCT_ID "device.vendor_product_id"
+#define PA_PROP_DEVICE_CLASS "device.class"
+#define PA_PROP_DEVICE_FORM_FACTOR "device.form_factor"
+#define PA_PROP_DEVICE_CONNECTOR "device.connector"
+#define PA_PROP_DEVICE_ACCESS_MODE "device.access_mode"
+#define PA_PROP_DEVICE_MASTER_DEVICE "device.master_device"
+#define PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE "device.buffering.buffer_size"
+#define PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE "device.buffering.fragment_size"
+
+/** A property list object. Basically a dictionary with UTF-8 strings
+ * as keys and arbitrary data as values. \since 0.9.11 */
+typedef struct pa_proplist pa_proplist;
+
+/** Allocate a property list. \since 0.9.11 */
+pa_proplist* pa_proplist_new(void);
+
+/** Free the property list. \since 0.9.11 */
+void pa_proplist_free(pa_proplist* p);
+
+/** Append a new string entry to the property list, possibly
+ * overwriting an already existing entry with the same key. An
+ * internal copy of the data passed is made. Will accept only valid
+ * UTF-8. \since 0.9.11 */
+int pa_proplist_sets(pa_proplist *p, const char *key, const char *value);
+
+/** Append a new string entry to the property list, possibly
+ * overwriting an already existing entry with the same key. An
+ * internal copy of the data passed is made. Will accept only valid
+ * UTF-8. The data can be passed as printf()-style format string with
+ * arguments. \since 0.9.11 */
+int pa_proplist_setf(pa_proplist *p, const char *key, const char *format, ...) PA_GCC_PRINTF_ATTR(3,4);
+
+/** Append a new arbitrary data entry to the property list, possibly
+ * overwriting an already existing entry with the same key. An
+ * internal copy of the data passed is made. \since 0.9.11 */
+int pa_proplist_set(pa_proplist *p, const char *key, const void *data, size_t nbytes);
+
+/* Return a string entry for the specified key. Will return NULL if
+ * the data is not valid UTF-8. Will return a NUL-terminated string in
+ * an internally allocated buffer. The caller should make a copy of
+ * the data before accessing the property list again. \since 0.9.11 */
+const char *pa_proplist_gets(pa_proplist *p, const char *key);
+
+/** Return the the value for the specified key. Will return a
+ * NUL-terminated string for string entries. The pointer returned will
+ * point to an internally allocated buffer. The caller should make a
+ * copy of the data before the property list is accessed again. \since
+ * 0.9.11 */
+int pa_proplist_get(pa_proplist *p, const char *key, const void **data, size_t *nbytes);
+
+/** Update mode enum for pa_proplist_update(). \since 0.9.11 */
+typedef enum pa_update_mode {
+ PA_UPDATE_SET, /*< Replace the entirey property list with the new one. Don't keep any of the old data around */
+ PA_UPDATE_MERGE, /*< Merge new property list into the existing one, not replacing any old entries if they share a common key with the new property list. */
+ PA_UPDATE_REPLACE /*< Merge new property list into the existing one, replacing all old entries that share a common key with the new property list. */
+} pa_update_mode_t;
+
+/** Merge property list "other" into "p", adhering the merge mode as
+ * specified in "mode". \since 0.9.11 */
+void pa_proplist_update(pa_proplist *p, pa_update_mode_t mode, pa_proplist *other);
+
+/** Removes a single entry from the property list, identified be the
+ * specified key name. \since 0.9.11 */
+int pa_proplist_unset(pa_proplist *p, const char *key);
+
+/** Similar to pa_proplist_remove() but takes an array of keys to
+ * remove. The array should be terminated by a NULL pointer. Return -1
+ * on failure, otherwise the number of entries actually removed (which
+ * might even be 0, if there where no matching entries to
+ * remove). \since 0.9.11 */
+int pa_proplist_unset_many(pa_proplist *p, const char * const keys[]);
+
+/** Iterate through the property list. The user should allocate a
+ * state variable of type void* and initialize it with NULL. A pointer
+ * to this variable should then be passed to pa_proplist_iterate()
+ * which should be called in a loop until it returns NULL which
+ * signifies EOL. The property list should not be modified during
+ * iteration through the list -- except for deleting the current
+ * looked at entry. On each invication this function will return the
+ * key string for the next entry. The keys in the property list do not
+ * have any particular order. \since 0.9.11 */
+const char *pa_proplist_iterate(pa_proplist *p, void **state);
+
+/** Format the property list nicely as a human readable string. Call pa_xfree() on the result. \since
+ * 0.9.11 */
+char *pa_proplist_to_string(pa_proplist *p);
+
+/** Returns 1 if an entry for the specified key is existant in the
+ * property list. \since 0.9.11 */
+int pa_proplist_contains(pa_proplist *p, const char *key);
+
+/** Remove all entries from the property list object. \since 0.9.11 */
+void pa_proplist_clear(pa_proplist *p);
+
+/** Allocate a new property list and copy over every single entry from
+ * the specific list. \since 0.9.11 */
+pa_proplist* pa_proplist_copy(pa_proplist *t);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/pulseaudio.h b/src/pulse/pulseaudio.h
index 88cc326b..ee8a4bb8 100644
--- a/src/pulse/pulseaudio.h
+++ b/src/pulse/pulseaudio.h
@@ -1,21 +1,22 @@
#ifndef foopulseaudiohfoo
#define foopulseaudiohfoo
-/* $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.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
@@ -55,11 +56,11 @@
/** \mainpage
*
* \section intro_sec Introduction
- *
+ *
* This document describes the client API for the PulseAudio sound
* server. The API comes in two flavours to accomodate different styles
* of applications and different needs in complexity:
- *
+ *
* \li The complete but somewhat complicated to use asynchronous API
* \li The simplified, easy to use, but limited synchronous API
*
@@ -67,7 +68,7 @@
* locale. Some functions will filter invalid sequences from the string, some
* will simply fail. To ensure reliable behaviour, make sure everything you
* pass to the API is already in UTF-8.
-
+
* \section simple_sec Simple API
*
* Use this if you develop your program in synchronous style and just
diff --git a/src/pulse/sample.c b/src/pulse/sample.c
index 7ca418e1..4aef5bb0 100644
--- a/src/pulse/sample.c
+++ b/src/pulse/sample.c
@@ -1,18 +1,19 @@
-/* $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
@@ -24,59 +25,64 @@
#endif
#include <stdio.h>
-#include <assert.h>
#include <math.h>
#include <string.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+#include <pulse/timeval.h>
+
#include "sample.h"
size_t pa_sample_size(const pa_sample_spec *spec) {
- assert(spec);
-
- switch (spec->format) {
- case PA_SAMPLE_U8:
- case PA_SAMPLE_ULAW:
- case PA_SAMPLE_ALAW:
- return 1;
- case PA_SAMPLE_S16LE:
- case PA_SAMPLE_S16BE:
- return 2;
- case PA_SAMPLE_FLOAT32LE:
- case PA_SAMPLE_FLOAT32BE:
- return 4;
- default:
- assert(0);
- return 0;
- }
+
+ static const size_t table[] = {
+ [PA_SAMPLE_U8] = 1,
+ [PA_SAMPLE_ULAW] = 1,
+ [PA_SAMPLE_ALAW] = 1,
+ [PA_SAMPLE_S16LE] = 2,
+ [PA_SAMPLE_S16BE] = 2,
+ [PA_SAMPLE_FLOAT32LE] = 4,
+ [PA_SAMPLE_FLOAT32BE] = 4,
+ [PA_SAMPLE_S32LE] = 4,
+ [PA_SAMPLE_S32BE] = 4,
+ };
+
+ pa_assert(spec);
+ pa_assert(spec->format >= 0);
+ pa_assert(spec->format < PA_SAMPLE_MAX);
+
+ return table[spec->format];
}
size_t pa_frame_size(const pa_sample_spec *spec) {
- assert(spec);
+ pa_assert(spec);
return pa_sample_size(spec) * spec->channels;
}
size_t pa_bytes_per_second(const pa_sample_spec *spec) {
- assert(spec);
+ pa_assert(spec);
return spec->rate*pa_frame_size(spec);
}
pa_usec_t pa_bytes_to_usec(uint64_t length, const pa_sample_spec *spec) {
- assert(spec);
+ pa_assert(spec);
- return (pa_usec_t) (((double) length/pa_frame_size(spec)*1000000)/spec->rate);
+ return (((pa_usec_t) (length / pa_frame_size(spec)) * PA_USEC_PER_SEC) / spec->rate);
}
size_t pa_usec_to_bytes(pa_usec_t t, const pa_sample_spec *spec) {
- assert(spec);
+ pa_assert(spec);
- return (size_t) (((double) t * spec->rate / 1000000))*pa_frame_size(spec);
+ return (size_t) (((t * spec->rate) / PA_USEC_PER_SEC)) * pa_frame_size(spec);
}
int pa_sample_spec_valid(const pa_sample_spec *spec) {
- assert(spec);
+ pa_assert(spec);
if (spec->rate <= 0 ||
+ spec->rate > PA_RATE_MAX ||
spec->channels <= 0 ||
spec->channels > PA_CHANNELS_MAX ||
spec->format >= PA_SAMPLE_MAX ||
@@ -87,9 +93,13 @@ int pa_sample_spec_valid(const pa_sample_spec *spec) {
}
int pa_sample_spec_equal(const pa_sample_spec*a, const pa_sample_spec*b) {
- assert(a && b);
+ pa_assert(a);
+ pa_assert(b);
- return (a->format == b->format) && (a->rate == b->rate) && (a->channels == b->channels);
+ return
+ (a->format == b->format) &&
+ (a->rate == b->rate) &&
+ (a->channels == b->channels);
}
const char *pa_sample_format_to_string(pa_sample_format_t f) {
@@ -101,58 +111,77 @@ const char *pa_sample_format_to_string(pa_sample_format_t f) {
[PA_SAMPLE_S16BE] = "s16be",
[PA_SAMPLE_FLOAT32LE] = "float32le",
[PA_SAMPLE_FLOAT32BE] = "float32be",
+ [PA_SAMPLE_S32LE] = "s32le",
+ [PA_SAMPLE_S32BE] = "s32be",
};
- if (f >= PA_SAMPLE_MAX)
+ if (f < 0 || f >= PA_SAMPLE_MAX)
return NULL;
return table[f];
}
char *pa_sample_spec_snprint(char *s, size_t l, const pa_sample_spec *spec) {
- assert(s && l && spec);
-
+ pa_assert(s);
+ pa_assert(l);
+ pa_assert(spec);
+
if (!pa_sample_spec_valid(spec))
- snprintf(s, l, "Invalid");
+ pa_snprintf(s, l, "Invalid");
else
- snprintf(s, l, "%s %uch %uHz", pa_sample_format_to_string(spec->format), spec->channels, spec->rate);
+ pa_snprintf(s, l, "%s %uch %uHz", pa_sample_format_to_string(spec->format), spec->channels, spec->rate);
return s;
}
char* pa_bytes_snprint(char *s, size_t l, unsigned v) {
+ pa_assert(s);
+
if (v >= ((unsigned) 1024)*1024*1024)
- snprintf(s, l, "%0.1f GiB", ((double) v)/1024/1024/1024);
+ pa_snprintf(s, l, "%0.1f GiB", ((double) v)/1024/1024/1024);
else if (v >= ((unsigned) 1024)*1024)
- snprintf(s, l, "%0.1f MiB", ((double) v)/1024/1024);
+ pa_snprintf(s, l, "%0.1f MiB", ((double) v)/1024/1024);
else if (v >= (unsigned) 1024)
- snprintf(s, l, "%0.1f KiB", ((double) v)/1024);
+ pa_snprintf(s, l, "%0.1f KiB", ((double) v)/1024);
else
- snprintf(s, l, "%u B", (unsigned) v);
+ pa_snprintf(s, l, "%u B", (unsigned) v);
return s;
}
pa_sample_format_t pa_parse_sample_format(const char *format) {
-
+ pa_assert(format);
+
if (strcasecmp(format, "s16le") == 0)
return PA_SAMPLE_S16LE;
else if (strcasecmp(format, "s16be") == 0)
return PA_SAMPLE_S16BE;
else if (strcasecmp(format, "s16ne") == 0 || strcasecmp(format, "s16") == 0 || strcasecmp(format, "16") == 0)
return PA_SAMPLE_S16NE;
+ else if (strcasecmp(format, "s16re") == 0)
+ return PA_SAMPLE_S16RE;
else if (strcasecmp(format, "u8") == 0 || strcasecmp(format, "8") == 0)
return PA_SAMPLE_U8;
- else if (strcasecmp(format, "float32") == 0 || strcasecmp(format, "float32ne") == 0)
- return PA_SAMPLE_FLOAT32;
+ else if (strcasecmp(format, "float32") == 0 || strcasecmp(format, "float32ne") == 0 || strcasecmp(format, "float") == 0)
+ return PA_SAMPLE_FLOAT32NE;
+ else if (strcasecmp(format, "float32re") == 0)
+ return PA_SAMPLE_FLOAT32RE;
else if (strcasecmp(format, "float32le") == 0)
return PA_SAMPLE_FLOAT32LE;
else if (strcasecmp(format, "float32be") == 0)
return PA_SAMPLE_FLOAT32BE;
- else if (strcasecmp(format, "ulaw") == 0)
+ else if (strcasecmp(format, "ulaw") == 0 || strcasecmp(format, "mulaw") == 0)
return PA_SAMPLE_ULAW;
else if (strcasecmp(format, "alaw") == 0)
return PA_SAMPLE_ALAW;
+ else if (strcasecmp(format, "s32le") == 0)
+ return PA_SAMPLE_S32LE;
+ else if (strcasecmp(format, "s32be") == 0)
+ return PA_SAMPLE_S32BE;
+ else if (strcasecmp(format, "s32ne") == 0 || strcasecmp(format, "s32") == 0 || strcasecmp(format, "32") == 0)
+ return PA_SAMPLE_S32NE;
+ else if (strcasecmp(format, "s32re") == 0)
+ return PA_SAMPLE_S32RE;
return -1;
}
diff --git a/src/pulse/sample.h b/src/pulse/sample.h
index da32fdf0..1ba3f871 100644
--- a/src/pulse/sample.h
+++ b/src/pulse/sample.h
@@ -1,21 +1,22 @@
#ifndef foosamplehfoo
#define foosamplehfoo
-/* $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
@@ -24,8 +25,10 @@
#include <inttypes.h>
#include <sys/types.h>
+#include <sys/param.h>
#include <math.h>
+#include <pulse/gccmacro.h>
#include <pulse/cdecl.h>
/** \page sample Sample Format Specifications
@@ -39,13 +42,15 @@
*
* PulseAudio supports the following sample formats:
*
- * \li PA_SAMPLE_U8 - Unsigned 8 bit PCM.
- * \li PA_SAMPLE_S16LE - Signed 16 bit PCM, little endian.
- * \li PA_SAMPLE_S16BE - Signed 16 bit PCM, big endian.
+ * \li PA_SAMPLE_U8 - Unsigned 8 bit integer PCM.
+ * \li PA_SAMPLE_S16LE - Signed 16 integer bit PCM, little endian.
+ * \li PA_SAMPLE_S16BE - Signed 16 integer bit PCM, big endian.
* \li PA_SAMPLE_FLOAT32LE - 32 bit IEEE floating point PCM, little endian.
* \li PA_SAMPLE_FLOAT32BE - 32 bit IEEE floating point PCM, big endian.
* \li PA_SAMPLE_ALAW - 8 bit a-Law.
* \li PA_SAMPLE_ULAW - 8 bit mu-Law.
+ * \li PA_SAMPLE_S32LE - Signed 32 bit integer PCM, little endian.
+ * \li PA_SAMPLE_S32BE - Signed 32 bit integer PCM, big endian.
*
* The floating point sample formats have the range from -1 to 1.
*
@@ -99,8 +104,19 @@
PA_C_DECL_BEGIN
+#if !defined(WORDS_BIGENDIAN)
+#if defined(__BYTE_ORDER)
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define WORDS_BIGENDIAN
+#endif
+#endif
+#endif
+
/** Maximum number of allowed channels */
-#define PA_CHANNELS_MAX 32
+#define PA_CHANNELS_MAX 32U
+
+/** Maximum allowed sample rate */
+#define PA_RATE_MAX (48000U*4U)
/** Sample format */
typedef enum pa_sample_format {
@@ -111,6 +127,8 @@ typedef enum pa_sample_format {
PA_SAMPLE_S16BE, /**< Signed 16 Bit PCM, big endian */
PA_SAMPLE_FLOAT32LE, /**< 32 Bit IEEE floating point, little endian, range -1 to 1 */
PA_SAMPLE_FLOAT32BE, /**< 32 Bit IEEE floating point, big endian, range -1 to 1 */
+ PA_SAMPLE_S32LE, /**< Signed 32 Bit PCM, little endian (PC) */
+ PA_SAMPLE_S32BE, /**< Signed 32 Bit PCM, big endian (PC) */
PA_SAMPLE_MAX, /**< Upper limit of valid sample types */
PA_SAMPLE_INVALID = -1 /**< An invalid value */
} pa_sample_format_t;
@@ -120,19 +138,27 @@ typedef enum pa_sample_format {
#define PA_SAMPLE_S16NE PA_SAMPLE_S16BE
/** 32 Bit IEEE floating point, native endian */
#define PA_SAMPLE_FLOAT32NE PA_SAMPLE_FLOAT32BE
+/** Signed 32 Bit PCM, native endian */
+#define PA_SAMPLE_S32NE PA_SAMPLE_S32BE
/** Signed 16 Bit PCM reverse endian */
#define PA_SAMPLE_S16RE PA_SAMPLE_S16LE
/** 32 Bit IEEE floating point, reverse endian */
#define PA_SAMPLE_FLOAT32RE PA_SAMPLE_FLOAT32LE
+/** Signed 32 Bit PCM reverse endian */
+#define PA_SAMPLE_S32RE PA_SAMPLE_S32LE
#else
/** Signed 16 Bit PCM, native endian */
#define PA_SAMPLE_S16NE PA_SAMPLE_S16LE
/** 32 Bit IEEE floating point, native endian */
#define PA_SAMPLE_FLOAT32NE PA_SAMPLE_FLOAT32LE
+/** Signed 32 Bit PCM, native endian */
+#define PA_SAMPLE_S32NE PA_SAMPLE_S32LE
/** Signed 16 Bit PCM reverse endian */
#define PA_SAMPLE_S16RE PA_SAMPLE_S16BE
/** 32 Bit IEEE floating point, reverse endian */
#define PA_SAMPLE_FLOAT32RE PA_SAMPLE_FLOAT32BE
+/** Signed 32 Bit PCM reverse endian */
+#define PA_SAMPLE_S32RE PA_SAMPLE_S32BE
#endif
/** A Shortcut for PA_SAMPLE_FLOAT32NE */
@@ -145,35 +171,35 @@ typedef struct pa_sample_spec {
uint8_t channels; /**< Audio channels. (1 for mono, 2 for stereo, ...) */
} pa_sample_spec;
-/** Type for usec specifications (unsigned). May be either 32 or 64 bit, depending on the architecture */
+/** Type for usec specifications (unsigned). Always 64 bit. */
typedef uint64_t pa_usec_t;
/** Return the amount of bytes playback of a second of audio with the specified sample type takes */
-size_t pa_bytes_per_second(const pa_sample_spec *spec);
+size_t pa_bytes_per_second(const pa_sample_spec *spec) PA_GCC_PURE;
/** Return the size of a frame with the specific sample type */
-size_t pa_frame_size(const pa_sample_spec *spec);
+size_t pa_frame_size(const pa_sample_spec *spec) PA_GCC_PURE;
/** Return the size of a sample with the specific sample type */
-size_t pa_sample_size(const pa_sample_spec *spec);
+size_t pa_sample_size(const pa_sample_spec *spec) PA_GCC_PURE;
/** Calculate the time the specified bytes take to play with the specified sample type */
-pa_usec_t pa_bytes_to_usec(uint64_t length, const pa_sample_spec *spec);
+pa_usec_t pa_bytes_to_usec(uint64_t length, const pa_sample_spec *spec) PA_GCC_PURE;
/** Calculates the number of bytes that are required for the specified time. \since 0.9 */
-size_t pa_usec_to_bytes(pa_usec_t t, const pa_sample_spec *spec);
+size_t pa_usec_to_bytes(pa_usec_t t, const pa_sample_spec *spec) PA_GCC_PURE;
/** Return non-zero when the sample type specification is valid */
-int pa_sample_spec_valid(const pa_sample_spec *spec);
+int pa_sample_spec_valid(const pa_sample_spec *spec) PA_GCC_PURE;
/** Return non-zero when the two sample type specifications match */
-int pa_sample_spec_equal(const pa_sample_spec*a, const pa_sample_spec*b);
+int pa_sample_spec_equal(const pa_sample_spec*a, const pa_sample_spec*b) PA_GCC_PURE;
/** Return a descriptive string for the specified sample format. \since 0.8 */
-const char *pa_sample_format_to_string(pa_sample_format_t f);
+const char *pa_sample_format_to_string(pa_sample_format_t f) PA_GCC_PURE;
/** Parse a sample format text. Inverse of pa_sample_format_to_string() */
-pa_sample_format_t pa_parse_sample_format(const char *format);
+pa_sample_format_t pa_parse_sample_format(const char *format) PA_GCC_PURE;
/** Maximum required string length for pa_sample_spec_snprint() */
#define PA_SAMPLE_SPEC_SNPRINT_MAX 32
diff --git a/src/pulse/scache.c b/src/pulse/scache.c
index 5d29c5b3..5e31e7af 100644
--- a/src/pulse/scache.c
+++ b/src/pulse/scache.c
@@ -1,18 +1,18 @@
-/* $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
@@ -23,12 +23,15 @@
#include <config.h>
#endif
-#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
+#include <pulse/utf8.h>
+
#include <pulsecore/pstream-util.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/proplist-util.h>
#include "internal.h"
@@ -37,26 +40,41 @@
int pa_stream_connect_upload(pa_stream *s, size_t length) {
pa_tagstruct *t;
uint32_t tag;
-
- assert(s);
+ const char *name;
+
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_UNCONNECTED, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY(s->context, length > 0, PA_ERR_INVALID);
-
+
+ if (!(name = pa_proplist_gets(s->proplist, PA_PROP_EVENT_ID)))
+ name = pa_proplist_gets(s->proplist, PA_PROP_MEDIA_NAME);
+
+ PA_CHECK_VALIDITY(s->context, name && *name && pa_utf8_valid(name), PA_ERR_INVALID);
+
pa_stream_ref(s);
-
+
s->direction = PA_STREAM_UPLOAD;
+ s->flags = 0;
t = pa_tagstruct_command(s->context, PA_COMMAND_CREATE_UPLOAD_STREAM, &tag);
- pa_tagstruct_puts(t, s->name);
+
+ pa_tagstruct_puts(t, name);
pa_tagstruct_put_sample_spec(t, &s->sample_spec);
pa_tagstruct_put_channel_map(t, &s->channel_map);
pa_tagstruct_putu32(t, length);
+
+ if (s->context->version >= 13) {
+ pa_init_proplist(s->proplist);
+ pa_tagstruct_put_proplist(t, s->proplist);
+ }
+
pa_pstream_send_tagstruct(s->context->pstream, t);
pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_create_stream_callback, s, NULL);
pa_stream_set_state(s, PA_STREAM_CREATING);
-
+
pa_stream_unref(s);
return 0;
}
@@ -64,7 +82,9 @@ int pa_stream_connect_upload(pa_stream *s, size_t length) {
int pa_stream_finish_upload(pa_stream *s) {
pa_tagstruct *t;
uint32_t tag;
- assert(s);
+
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
PA_CHECK_VALIDITY(s->context, s->channel_valid, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY(s->context, s->context->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
@@ -80,30 +100,136 @@ int pa_stream_finish_upload(pa_stream *s) {
return 0;
}
+static void play_sample_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_operation *o = userdata;
+ int success = 1;
+ uint32_t idx = PA_INVALID_INDEX;
+
+ pa_assert(pd);
+ pa_assert(o);
+ pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+ if (!o->context)
+ goto finish;
+
+ if (command != PA_COMMAND_REPLY) {
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
+ goto finish;
+
+ success = 0;
+ } else if ((o->context->version >= 13 && pa_tagstruct_getu32(t, &idx) < 0) ||
+ !pa_tagstruct_eof(t)) {
+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ goto finish;
+ } else if (o->context->version >= 13 && idx == PA_INVALID_INDEX)
+ success = 0;
+
+ if (o->callback) {
+ pa_context_success_cb_t cb = (pa_context_success_cb_t) o->callback;
+ cb(o->context, success, o->userdata);
+ }
+
+finish:
+ pa_operation_done(o);
+ pa_operation_unref(o);
+}
+
+static void play_sample_with_proplist_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_operation *o = userdata;
+ uint32_t idx;
+
+ pa_assert(pd);
+ pa_assert(o);
+ pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+ if (!o->context)
+ goto finish;
+
+ if (command != PA_COMMAND_REPLY) {
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
+ goto finish;
+
+ idx = PA_INVALID_INDEX;
+ } else if (pa_tagstruct_getu32(t, &idx) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (o->callback) {
+ pa_context_play_sample_cb_t cb = (pa_context_play_sample_cb_t) o->callback;
+ cb(o->context, idx, o->userdata);
+ }
+
+finish:
+ pa_operation_done(o);
+ pa_operation_unref(o);
+}
+
+
pa_operation *pa_context_play_sample(pa_context *c, const char *name, const char *dev, pa_volume_t volume, pa_context_success_cb_t cb, void *userdata) {
pa_operation *o;
pa_tagstruct *t;
uint32_t tag;
- assert(c);
- assert(c->ref >= 1);
-
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID);
PA_CHECK_VALIDITY_RETURN_NULL(c, !dev || *dev, PA_ERR_INVALID);
-
+
o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
if (!dev)
dev = c->conf->default_sink;
-
+
t = pa_tagstruct_command(c, PA_COMMAND_PLAY_SAMPLE, &tag);
pa_tagstruct_putu32(t, PA_INVALID_INDEX);
pa_tagstruct_puts(t, dev);
pa_tagstruct_putu32(t, volume);
pa_tagstruct_puts(t, name);
+
+ if (c->version >= 13) {
+ pa_proplist *p = pa_proplist_new();
+ pa_tagstruct_put_proplist(t, p);
+ pa_proplist_free(p);
+ }
+
pa_pstream_send_tagstruct(c->pstream, t);
- pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, play_sample_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+pa_operation *pa_context_play_sample_with_proplist(pa_context *c, const char *name, const char *dev, pa_volume_t volume, pa_proplist *p, pa_context_play_sample_cb_t cb, void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, !dev || *dev, PA_ERR_INVALID);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, p, PA_ERR_INVALID);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 13, PA_ERR_NOTSUPPORTED);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ if (!dev)
+ dev = c->conf->default_sink;
+
+ t = pa_tagstruct_command(c, PA_COMMAND_PLAY_SAMPLE, &tag);
+ pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+ pa_tagstruct_puts(t, dev);
+ pa_tagstruct_putu32(t, volume);
+ pa_tagstruct_puts(t, name);
+ pa_tagstruct_put_proplist(t, p);
+
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, play_sample_with_proplist_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
return o;
}
@@ -113,19 +239,19 @@ pa_operation* pa_context_remove_sample(pa_context *c, const char *name, pa_conte
pa_tagstruct *t;
uint32_t tag;
- assert(c);
- assert(c->ref >= 1);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID);
-
+
o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
-
+
t = pa_tagstruct_command(c, PA_COMMAND_REMOVE_SAMPLE, &tag);
pa_tagstruct_puts(t, name);
+
pa_pstream_send_tagstruct(c->pstream, t);
pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
return o;
}
-
diff --git a/src/pulse/scache.h b/src/pulse/scache.h
index e32703d4..f380b4e8 100644
--- a/src/pulse/scache.h
+++ b/src/pulse/scache.h
@@ -1,21 +1,22 @@
#ifndef fooscachehfoo
#define fooscachehfoo
-/* $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
@@ -76,14 +77,25 @@
PA_C_DECL_BEGIN
+/** Callback prototype for pa_context_play_sample_with_proplist(). The
+ * idx value is the index of the sink input object, or
+ * PA_INVALID_INDEX on failure. \since 0.9.11 */
+typedef void (*pa_context_play_sample_cb_t)(pa_context *c, uint32_t idx, void *userdata);
+
/** Make this stream a sample upload stream */
int pa_stream_connect_upload(pa_stream *s, size_t length);
-/** Finish the sample upload, the stream name will become the sample name. You cancel a samp
- * le upload by issuing pa_stream_disconnect() */
+/** Finish the sample upload, the stream name will become the sample
+ * name. You cancel a sample upload by issuing
+ * pa_stream_disconnect() */
int pa_stream_finish_upload(pa_stream *s);
-/** Play a sample from the sample cache to the specified device. If the latter is NULL use the default sink. Returns an operation object */
+/** Remove a sample from the sample cache. Returns an operation object which may be used to cancel the operation while it is running */
+pa_operation* pa_context_remove_sample(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata);
+
+/** Play a sample from the sample cache to the specified device. If
+ * the latter is NULL use the default sink. Returns an operation
+ * object */
pa_operation* pa_context_play_sample(
pa_context *c /**< Context */,
const char *name /**< Name of the sample to play */,
@@ -92,8 +104,18 @@ pa_operation* pa_context_play_sample(
pa_context_success_cb_t cb /**< Call this function after successfully starting playback, or NULL */,
void *userdata /**< Userdata to pass to the callback */);
-/** Remove a sample from the sample cache. Returns an operation object which may be used to cancel the operation while it is running */
-pa_operation* pa_context_remove_sample(pa_context *c, const char *name, pa_context_success_cb_t, void *userdata);
+/** Play a sample from the sample cache to the specified device,
+ * allowing specification of a property list for the playback
+ * stream. If the latter is NULL use the default sink. Returns an
+ * operation object. \since 0.9.11 */
+pa_operation* pa_context_play_sample_with_proplist(
+ pa_context *c /**< Context */,
+ const char *name /**< Name of the sample to play */,
+ const char *dev /**< Sink to play this sample on */,
+ pa_volume_t volume /**< Volume to play this sample with */ ,
+ pa_proplist *proplist /**< Property list for this sound. The property list of the cached entry will be merged into this property list */,
+ pa_context_play_sample_cb_t cb /**< Call this function after successfully starting playback, or NULL */,
+ void *userdata /**< Userdata to pass to the callback */);
PA_C_DECL_END
diff --git a/src/pulse/simple.c b/src/pulse/simple.c
index a41881bb..70396835 100644
--- a/src/pulse/simple.c
+++ b/src/pulse/simple.c
@@ -1,18 +1,19 @@
-/* $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
@@ -25,7 +26,6 @@
#include <stdio.h>
#include <string.h>
-#include <assert.h>
#include <stdlib.h>
#include <pulse/pulseaudio.h>
@@ -34,6 +34,7 @@
#include <pulsecore/native-common.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
#include "simple.h"
@@ -64,7 +65,7 @@ if (!(expression)) { \
goto label; \
} \
} while(0);
-
+
#define CHECK_DEAD_GOTO(p, rerror, label) do { \
if (!(p)->context || pa_context_get_state((p)->context) != PA_CONTEXT_READY || \
!(p)->stream || pa_stream_get_state((p)->stream) != PA_STREAM_READY) { \
@@ -81,8 +82,8 @@ if (!(p)->context || pa_context_get_state((p)->context) != PA_CONTEXT_READY || \
static void context_state_cb(pa_context *c, void *userdata) {
pa_simple *p = userdata;
- assert(c);
- assert(p);
+ pa_assert(c);
+ pa_assert(p);
switch (pa_context_get_state(c)) {
case PA_CONTEXT_READY:
@@ -101,8 +102,8 @@ static void context_state_cb(pa_context *c, void *userdata) {
static void stream_state_cb(pa_stream *s, void * userdata) {
pa_simple *p = userdata;
- assert(s);
- assert(p);
+ pa_assert(s);
+ pa_assert(p);
switch (pa_stream_get_state(s)) {
@@ -120,7 +121,7 @@ static void stream_state_cb(pa_stream *s, void * userdata) {
static void stream_request_cb(pa_stream *s, size_t length, void *userdata) {
pa_simple *p = userdata;
- assert(p);
+ pa_assert(p);
pa_threaded_mainloop_signal(p->mainloop, 0);
}
@@ -128,22 +129,22 @@ static void stream_request_cb(pa_stream *s, size_t length, void *userdata) {
static void stream_latency_update_cb(pa_stream *s, void *userdata) {
pa_simple *p = userdata;
- assert(p);
+ pa_assert(p);
pa_threaded_mainloop_signal(p->mainloop, 0);
}
pa_simple* pa_simple_new(
- const char *server,
- const char *name,
- pa_stream_direction_t dir,
- const char *dev,
- const char *stream_name,
- const pa_sample_spec *ss,
- const pa_channel_map *map,
- const pa_buffer_attr *attr,
- int *rerror) {
-
+ const char *server,
+ const char *name,
+ pa_stream_direction_t dir,
+ const char *dev,
+ const char *stream_name,
+ const pa_sample_spec *ss,
+ const pa_channel_map *map,
+ const pa_buffer_attr *attr,
+ int *rerror) {
+
pa_simple *p;
int error = PA_ERR_INTERNAL, r;
@@ -162,12 +163,12 @@ pa_simple* pa_simple_new(
if (!(p->mainloop = pa_threaded_mainloop_new()))
goto fail;
-
+
if (!(p->context = pa_context_new(pa_threaded_mainloop_get_api(p->mainloop), name)))
goto fail;
pa_context_set_state_callback(p->context, context_state_cb, p);
-
+
if (pa_context_connect(p->context, server, 0, NULL) < 0) {
error = pa_context_errno(p->context);
goto fail;
@@ -180,7 +181,7 @@ pa_simple* pa_simple_new(
/* Wait until the context is ready */
pa_threaded_mainloop_wait(p->mainloop);
-
+
if (pa_context_get_state(p->context) != PA_CONTEXT_READY) {
error = pa_context_errno(p->context);
goto unlock_and_fail;
@@ -216,12 +217,12 @@ pa_simple* pa_simple_new(
}
pa_threaded_mainloop_unlock(p->mainloop);
-
+
return p;
unlock_and_fail:
pa_threaded_mainloop_unlock(p->mainloop);
-
+
fail:
if (rerror)
*rerror = error;
@@ -230,14 +231,14 @@ fail:
}
void pa_simple_free(pa_simple *s) {
- assert(s);
+ pa_assert(s);
if (s->mainloop)
pa_threaded_mainloop_stop(s->mainloop);
-
+
if (s->stream)
pa_stream_unref(s->stream);
-
+
if (s->context)
pa_context_unref(s->context);
@@ -248,60 +249,60 @@ void pa_simple_free(pa_simple *s) {
}
int pa_simple_write(pa_simple *p, const void*data, size_t length, int *rerror) {
- assert(p);
-
+ pa_assert(p);
+
CHECK_VALIDITY_RETURN_ANY(rerror, p->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE, -1);
CHECK_VALIDITY_RETURN_ANY(rerror, data && length, PA_ERR_INVALID, -1);
pa_threaded_mainloop_lock(p->mainloop);
-
+
CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
while (length > 0) {
size_t l;
int r;
-
+
while (!(l = pa_stream_writable_size(p->stream))) {
pa_threaded_mainloop_wait(p->mainloop);
CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
}
CHECK_SUCCESS_GOTO(p, rerror, l != (size_t) -1, unlock_and_fail);
-
+
if (l > length)
l = length;
r = pa_stream_write(p->stream, data, l, NULL, 0, PA_SEEK_RELATIVE);
CHECK_SUCCESS_GOTO(p, rerror, r >= 0, unlock_and_fail);
-
+
data = (const uint8_t*) data + l;
length -= l;
}
pa_threaded_mainloop_unlock(p->mainloop);
return 0;
-
+
unlock_and_fail:
pa_threaded_mainloop_unlock(p->mainloop);
return -1;
}
int pa_simple_read(pa_simple *p, void*data, size_t length, int *rerror) {
- assert(p);
+ pa_assert(p);
CHECK_VALIDITY_RETURN_ANY(rerror, p->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE, -1);
CHECK_VALIDITY_RETURN_ANY(rerror, data && length, PA_ERR_INVALID, -1);
pa_threaded_mainloop_lock(p->mainloop);
-
+
CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
while (length > 0) {
size_t l;
-
+
while (!p->read_data) {
int r;
-
+
r = pa_stream_peek(p->stream, &p->read_data, &p->read_length);
CHECK_SUCCESS_GOTO(p, rerror, r == 0, unlock_and_fail);
@@ -311,31 +312,31 @@ int pa_simple_read(pa_simple *p, void*data, size_t length, int *rerror) {
} else
p->read_index = 0;
}
-
+
l = p->read_length < length ? p->read_length : length;
memcpy(data, (const uint8_t*) p->read_data+p->read_index, l);
data = (uint8_t*) data + l;
length -= l;
-
+
p->read_index += l;
p->read_length -= l;
if (!p->read_length) {
int r;
-
+
r = pa_stream_drop(p->stream);
p->read_data = NULL;
p->read_length = 0;
p->read_index = 0;
-
+
CHECK_SUCCESS_GOTO(p, rerror, r == 0, unlock_and_fail);
}
}
pa_threaded_mainloop_unlock(p->mainloop);
return 0;
-
+
unlock_and_fail:
pa_threaded_mainloop_unlock(p->mainloop);
return -1;
@@ -344,8 +345,8 @@ unlock_and_fail:
static void success_cb(pa_stream *s, int success, void *userdata) {
pa_simple *p = userdata;
- assert(s);
- assert(p);
+ pa_assert(s);
+ pa_assert(p);
p->operation_success = success;
pa_threaded_mainloop_signal(p->mainloop, 0);
@@ -353,8 +354,8 @@ static void success_cb(pa_stream *s, int success, void *userdata) {
int pa_simple_drain(pa_simple *p, int *rerror) {
pa_operation *o = NULL;
-
- assert(p);
+
+ pa_assert(p);
CHECK_VALIDITY_RETURN_ANY(rerror, p->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE, -1);
@@ -370,7 +371,7 @@ int pa_simple_drain(pa_simple *p, int *rerror) {
CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
}
CHECK_SUCCESS_GOTO(p, rerror, p->operation_success, unlock_and_fail);
-
+
pa_operation_unref(o);
pa_threaded_mainloop_unlock(p->mainloop);
@@ -389,8 +390,8 @@ unlock_and_fail:
int pa_simple_flush(pa_simple *p, int *rerror) {
pa_operation *o = NULL;
-
- assert(p);
+
+ pa_assert(p);
CHECK_VALIDITY_RETURN_ANY(rerror, p->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE, -1);
@@ -399,7 +400,7 @@ int pa_simple_flush(pa_simple *p, int *rerror) {
o = pa_stream_flush(p->stream, success_cb, p);
CHECK_SUCCESS_GOTO(p, rerror, o, unlock_and_fail);
-
+
p->operation_success = 0;
while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
pa_threaded_mainloop_wait(p->mainloop);
@@ -426,14 +427,14 @@ unlock_and_fail:
pa_usec_t pa_simple_get_latency(pa_simple *p, int *rerror) {
pa_usec_t t;
int negative;
-
- assert(p);
-
+
+ pa_assert(p);
+
pa_threaded_mainloop_lock(p->mainloop);
for (;;) {
CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
-
+
if (pa_stream_get_latency(p->stream, &t, &negative) >= 0)
break;
@@ -442,7 +443,7 @@ pa_usec_t pa_simple_get_latency(pa_simple *p, int *rerror) {
/* Wait until latency data is available again */
pa_threaded_mainloop_wait(p->mainloop);
}
-
+
pa_threaded_mainloop_unlock(p->mainloop);
return negative ? 0 : t;
diff --git a/src/pulse/simple.h b/src/pulse/simple.h
index 0438d319..a1380a0a 100644
--- a/src/pulse/simple.h
+++ b/src/pulse/simple.h
@@ -1,21 +1,22 @@
#ifndef foosimplehfoo
#define foosimplehfoo
-/* $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
@@ -48,7 +49,7 @@
* pa_simple *s;
* pa_sample_spec ss;
*
- * ss.format = PA_SAMPLE_S16_NE;
+ * ss.format = PA_SAMPLE_S16NE;
* ss.channels = 2;
* ss.rate = 44100;
*
@@ -127,18 +128,18 @@ pa_simple* pa_simple_new(
void pa_simple_free(pa_simple *s);
/** Write some data to the server */
-int pa_simple_write(pa_simple *s, const void*data, size_t length, int *error);
+int pa_simple_write(pa_simple *s, const void*data, size_t bytes, int *error);
/** Wait until all data already written is played by the daemon */
int pa_simple_drain(pa_simple *s, int *error);
/** Read some data from the server */
-int pa_simple_read(pa_simple *s, void*data, size_t length, int *error);
+int pa_simple_read(pa_simple *s, void*data, size_t bytes, int *error);
-/** Return the playback latency. \since 0.5 */
+/** Return the playback latency. */
pa_usec_t pa_simple_get_latency(pa_simple *s, int *error);
-/** Flush the playback buffer. \since 0.5 */
+/** Flush the playback buffer. */
int pa_simple_flush(pa_simple *s, int *error);
PA_C_DECL_END
diff --git a/src/pulse/stream.c b/src/pulse/stream.c
index d31127d8..3bee7a05 100644
--- a/src/pulse/stream.c
+++ b/src/pulse/stream.c
@@ -1,18 +1,19 @@
-/* $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
@@ -23,7 +24,6 @@
#include <config.h>
#endif
-#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <string.h>
@@ -35,25 +35,22 @@
#include <pulsecore/pstream-util.h>
#include <pulsecore/log.h>
#include <pulsecore/hashmap.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/rtclock.h>
#include "internal.h"
-#define LATENCY_IPOL_INTERVAL_USEC (100000L)
-
-pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map) {
- pa_stream *s;
- int i;
-
- assert(c);
+#define LATENCY_IPOL_INTERVAL_USEC (333*PA_USEC_PER_MSEC)
- PA_CHECK_VALIDITY_RETURN_NULL(c, ss && pa_sample_spec_valid(ss), PA_ERR_INVALID);
- PA_CHECK_VALIDITY_RETURN_NULL(c, !map || (pa_channel_map_valid(map) && map->channels == ss->channels), PA_ERR_INVALID);
+#define SMOOTHER_ADJUST_TIME (1000*PA_USEC_PER_MSEC)
+#define SMOOTHER_HISTORY_TIME (5000*PA_USEC_PER_MSEC)
+#define SMOOTHER_MIN_HISTORY (4)
- s = pa_xnew(pa_stream, 1);
- s->ref = 1;
- s->context = c;
- s->mainloop = c->mainloop;
+pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map) {
+ return pa_stream_new_with_proplist(c, name, ss, map, NULL);
+}
+static void reset_callbacks(pa_stream *s) {
s->read_callback = NULL;
s->read_userdata = NULL;
s->write_callback = NULL;
@@ -66,47 +63,90 @@ pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *
s->underflow_userdata = NULL;
s->latency_update_callback = NULL;
s->latency_update_userdata = NULL;
+ s->moved_callback = NULL;
+ s->moved_userdata = NULL;
+ s->suspended_callback = NULL;
+ s->suspended_userdata = NULL;
+ s->started_callback = NULL;
+ s->started_userdata = NULL;
+}
+
+pa_stream *pa_stream_new_with_proplist(
+ pa_context *c,
+ const char *name,
+ const pa_sample_spec *ss,
+ const pa_channel_map *map,
+ pa_proplist *p) {
+
+ pa_stream *s;
+ int i;
+ pa_channel_map tmap;
+
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, ss && pa_sample_spec_valid(ss), PA_ERR_INVALID);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 12 || (ss->format != PA_SAMPLE_S32LE || ss->format != PA_SAMPLE_S32NE), PA_ERR_NOTSUPPORTED);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, !map || (pa_channel_map_valid(map) && map->channels == ss->channels), PA_ERR_INVALID);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, name || (p && pa_proplist_contains(p, PA_PROP_MEDIA_NAME)), PA_ERR_INVALID);
+
+ if (!map)
+ PA_CHECK_VALIDITY_RETURN_NULL(c, map = pa_channel_map_init_auto(&tmap, ss->channels, PA_CHANNEL_MAP_DEFAULT), PA_ERR_INVALID);
+
+ s = pa_xnew(pa_stream, 1);
+ PA_REFCNT_INIT(s);
+ s->context = c;
+ s->mainloop = c->mainloop;
s->direction = PA_STREAM_NODIRECTION;
- s->name = pa_xstrdup(name);
- s->sample_spec = *ss;
+ s->state = PA_STREAM_UNCONNECTED;
s->flags = 0;
- if (map)
- s->channel_map = *map;
- else
- pa_channel_map_init_auto(&s->channel_map, ss->channels, PA_CHANNEL_MAP_DEFAULT);
-
+ s->sample_spec = *ss;
+ s->channel_map = *map;
+
+ s->direct_on_input = PA_INVALID_INDEX;
+
+ s->proplist = p ? pa_proplist_copy(p) : pa_proplist_new();
+ if (name)
+ pa_proplist_sets(s->proplist, PA_PROP_MEDIA_NAME, name);
+
s->channel = 0;
- s->channel_valid = 0;
+ s->channel_valid = FALSE;
s->syncid = c->csyncid++;
- s->device_index = PA_INVALID_INDEX;
+ s->stream_index = PA_INVALID_INDEX;
+
s->requested_bytes = 0;
- s->state = PA_STREAM_UNCONNECTED;
memset(&s->buffer_attr, 0, sizeof(s->buffer_attr));
- s->peek_memchunk.index = 0;
- s->peek_memchunk.length = 0;
- s->peek_memchunk.memblock = NULL;
+ s->device_index = PA_INVALID_INDEX;
+ s->device_name = NULL;
+ s->suspended = FALSE;
+
+ pa_memchunk_reset(&s->peek_memchunk);
s->peek_data = NULL;
s->record_memblockq = NULL;
+ s->corked = FALSE;
+
+ memset(&s->timing_info, 0, sizeof(s->timing_info));
+ s->timing_info_valid = FALSE;
+
s->previous_time = 0;
- s->timing_info_valid = 0;
+
s->read_index_not_before = 0;
s->write_index_not_before = 0;
-
for (i = 0; i < PA_MAX_WRITE_INDEX_CORRECTIONS; i++)
s->write_index_corrections[i].valid = 0;
s->current_write_index_correction = 0;
-
- s->corked = 0;
- s->cached_time_valid = 0;
-
s->auto_timing_update_event = NULL;
- s->auto_timing_update_requested = 0;
+ s->auto_timing_update_requested = FALSE;
+
+ reset_callbacks(s);
+
+ s->smoother = NULL;
/* Refcounting is strictly one-way: from the "bigger" to the "smaller" object. */
PA_LLIST_PREPEND(pa_stream, c->streams, s);
@@ -115,14 +155,51 @@ pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *
return s;
}
-static void stream_free(pa_stream *s) {
- assert(s && !s->context && !s->channel_valid);
+static void stream_unlink(pa_stream *s) {
+ pa_operation *o, *n;
+ pa_assert(s);
+
+ if (!s->context)
+ return;
+
+ /* Detach from context */
+
+ /* Unref all operatio object that point to us */
+ for (o = s->context->operations; o; o = n) {
+ n = o->next;
+
+ if (o->stream == s)
+ pa_operation_cancel(o);
+ }
+
+ /* Drop all outstanding replies for this stream */
+ if (s->context->pdispatch)
+ pa_pdispatch_unregister_reply(s->context->pdispatch, s);
+
+ if (s->channel_valid) {
+ pa_dynarray_put((s->direction == PA_STREAM_PLAYBACK) ? s->context->playback_streams : s->context->record_streams, s->channel, NULL);
+ s->channel = 0;
+ s->channel_valid = FALSE;
+ }
+
+ PA_LLIST_REMOVE(pa_stream, s->context->streams, s);
+ pa_stream_unref(s);
+
+ s->context = NULL;
if (s->auto_timing_update_event) {
- assert(s->mainloop);
+ pa_assert(s->mainloop);
s->mainloop->time_free(s->auto_timing_update_event);
}
+ reset_callbacks(s);
+}
+
+static void stream_free(pa_stream *s) {
+ pa_assert(s);
+
+ stream_unlink(s);
+
if (s->peek_memchunk.memblock) {
if (s->peek_data)
pa_memblock_release(s->peek_memchunk.memblock);
@@ -132,115 +209,127 @@ static void stream_free(pa_stream *s) {
if (s->record_memblockq)
pa_memblockq_free(s->record_memblockq);
- pa_xfree(s->name);
+ if (s->proplist)
+ pa_proplist_free(s->proplist);
+
+ if (s->smoother)
+ pa_smoother_free(s->smoother);
+
+ pa_xfree(s->device_name);
pa_xfree(s);
}
void pa_stream_unref(pa_stream *s) {
- assert(s);
- assert(s->ref >= 1);
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
- if (--(s->ref) == 0)
+ if (PA_REFCNT_DEC(s) <= 0)
stream_free(s);
}
pa_stream* pa_stream_ref(pa_stream *s) {
- assert(s);
- assert(s->ref >= 1);
-
- s->ref++;
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+ PA_REFCNT_INC(s);
return s;
}
pa_stream_state_t pa_stream_get_state(pa_stream *s) {
- assert(s);
- assert(s->ref >= 1);
-
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
return s->state;
}
pa_context* pa_stream_get_context(pa_stream *s) {
- assert(s);
- assert(s->ref >= 1);
-
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
return s->context;
}
uint32_t pa_stream_get_index(pa_stream *s) {
- assert(s);
- assert(s->ref >= 1);
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE, PA_INVALID_INDEX);
-
- return s->device_index;
+
+ return s->stream_index;
}
-
+
void pa_stream_set_state(pa_stream *s, pa_stream_state_t st) {
- assert(s);
- assert(s->ref >= 1);
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
if (s->state == st)
return;
-
+
pa_stream_ref(s);
s->state = st;
+
if (s->state_callback)
s->state_callback(s, s->state_userdata);
- if ((st == PA_STREAM_FAILED || st == PA_STREAM_TERMINATED) && s->context) {
+ if ((st == PA_STREAM_FAILED || st == PA_STREAM_TERMINATED))
+ stream_unlink(s);
- /* Detach from context */
- pa_operation *o, *n;
+ pa_stream_unref(s);
+}
- /* Unref all operatio object that point to us */
- for (o = s->context->operations; o; o = n) {
- n = o->next;
-
- if (o->stream == s)
- pa_operation_cancel(o);
- }
-
- /* Drop all outstanding replies for this stream */
- if (s->context->pdispatch)
- pa_pdispatch_unregister_reply(s->context->pdispatch, s);
+static void request_auto_timing_update(pa_stream *s, pa_bool_t force) {
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
- if (s->channel_valid)
- pa_dynarray_put((s->direction == PA_STREAM_PLAYBACK) ? s->context->playback_streams : s->context->record_streams, s->channel, NULL);
-
- PA_LLIST_REMOVE(pa_stream, s->context->streams, s);
- pa_stream_unref(s);
+ if (!(s->flags & PA_STREAM_AUTO_TIMING_UPDATE))
+ return;
- s->channel = 0;
- s->channel_valid = 0;
-
- s->context = NULL;
+ if (s->state == PA_STREAM_READY &&
+ (force || !s->auto_timing_update_requested)) {
+ pa_operation *o;
+
+/* pa_log("automatically requesting new timing data"); */
+
+ if ((o = pa_stream_update_timing_info(s, NULL, NULL))) {
+ pa_operation_unref(o);
+ s->auto_timing_update_requested = TRUE;
+ }
}
- pa_stream_unref(s);
+ if (s->auto_timing_update_event) {
+ struct timeval next;
+ pa_gettimeofday(&next);
+ pa_timeval_add(&next, LATENCY_IPOL_INTERVAL_USEC);
+ s->mainloop->time_restart(s->auto_timing_update_event, &next);
+ }
}
void pa_command_stream_killed(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_context *c = userdata;
pa_stream *s;
uint32_t channel;
-
- assert(pd);
- assert(command == PA_COMMAND_PLAYBACK_STREAM_KILLED || command == PA_COMMAND_RECORD_STREAM_KILLED);
- assert(t);
- assert(c);
+
+ pa_assert(pd);
+ pa_assert(command == PA_COMMAND_PLAYBACK_STREAM_KILLED || command == PA_COMMAND_RECORD_STREAM_KILLED);
+ pa_assert(t);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
pa_context_ref(c);
-
+
if (pa_tagstruct_getu32(t, &channel) < 0 ||
!pa_tagstruct_eof(t)) {
pa_context_fail(c, PA_ERR_PROTOCOL);
goto finish;
}
-
+
if (!(s = pa_dynarray_get(command == PA_COMMAND_PLAYBACK_STREAM_KILLED ? c->playback_streams : c->record_streams, channel)))
goto finish;
+ if (s->state != PA_STREAM_READY)
+ goto finish;
+
pa_context_set_error(c, PA_ERR_KILLED);
pa_stream_set_state(s, PA_STREAM_FAILED);
@@ -248,34 +337,226 @@ finish:
pa_context_unref(c);
}
+void pa_command_stream_moved(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_context *c = userdata;
+ pa_stream *s;
+ uint32_t channel;
+ const char *dn;
+ pa_bool_t suspended;
+ uint32_t di;
+ pa_usec_t usec;
+ uint32_t maxlength = 0, fragsize = 0, minreq = 0, tlength = 0, prebuf = 0;
+
+ pa_assert(pd);
+ pa_assert(command == PA_COMMAND_PLAYBACK_STREAM_MOVED || command == PA_COMMAND_RECORD_STREAM_MOVED);
+ pa_assert(t);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ pa_context_ref(c);
+
+ if (c->version < 12) {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (pa_tagstruct_getu32(t, &channel) < 0 ||
+ pa_tagstruct_getu32(t, &di) < 0 ||
+ pa_tagstruct_gets(t, &dn) < 0 ||
+ pa_tagstruct_get_boolean(t, &suspended) < 0) {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (c->version >= 13) {
+
+ if (command == PA_COMMAND_RECORD_STREAM_MOVED) {
+ if (pa_tagstruct_getu32(t, &maxlength) < 0 ||
+ pa_tagstruct_getu32(t, &fragsize) < 0 ||
+ pa_tagstruct_get_usec(t, &usec) < 0) {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+ } else {
+ if (pa_tagstruct_getu32(t, &maxlength) < 0 ||
+ pa_tagstruct_getu32(t, &tlength) < 0 ||
+ pa_tagstruct_getu32(t, &prebuf) < 0 ||
+ pa_tagstruct_getu32(t, &minreq) < 0 ||
+ pa_tagstruct_get_usec(t, &usec) < 0) {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+ }
+ }
+
+ if (!pa_tagstruct_eof(t)) {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (!dn || di == PA_INVALID_INDEX) {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (!(s = pa_dynarray_get(command == PA_COMMAND_PLAYBACK_STREAM_MOVED ? c->playback_streams : c->record_streams, channel)))
+ goto finish;
+
+ if (s->state != PA_STREAM_READY)
+ goto finish;
+
+ if (c->version >= 13) {
+ if (s->direction == PA_STREAM_RECORD)
+ s->timing_info.configured_source_usec = usec;
+ else
+ s->timing_info.configured_sink_usec = usec;
+
+ s->buffer_attr.maxlength = maxlength;
+ s->buffer_attr.fragsize = fragsize;
+ s->buffer_attr.tlength = tlength;
+ s->buffer_attr.prebuf = prebuf;
+ s->buffer_attr.minreq = minreq;
+ }
+
+ pa_xfree(s->device_name);
+ s->device_name = pa_xstrdup(dn);
+ s->device_index = di;
+
+ s->suspended = suspended;
+
+ request_auto_timing_update(s, TRUE);
+
+ if (s->moved_callback)
+ s->moved_callback(s, s->moved_userdata);
+
+finish:
+ pa_context_unref(c);
+}
+
+void pa_command_stream_suspended(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_context *c = userdata;
+ pa_stream *s;
+ uint32_t channel;
+ pa_bool_t suspended;
+
+ pa_assert(pd);
+ pa_assert(command == PA_COMMAND_PLAYBACK_STREAM_SUSPENDED || command == PA_COMMAND_RECORD_STREAM_SUSPENDED);
+ pa_assert(t);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ pa_context_ref(c);
+
+ if (c->version < 12) {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (pa_tagstruct_getu32(t, &channel) < 0 ||
+ pa_tagstruct_get_boolean(t, &suspended) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (!(s = pa_dynarray_get(command == PA_COMMAND_PLAYBACK_STREAM_SUSPENDED ? c->playback_streams : c->record_streams, channel)))
+ goto finish;
+
+ if (s->state != PA_STREAM_READY)
+ goto finish;
+
+ s->suspended = suspended;
+
+ if (s->smoother) {
+ pa_usec_t x = pa_rtclock_usec();
+
+ if (s->timing_info_valid)
+ x -= s->timing_info.transport_usec;
+
+ if (s->suspended || s->corked)
+ pa_smoother_pause(s->smoother, x);
+ else
+ pa_smoother_resume(s->smoother, x);
+ }
+
+ request_auto_timing_update(s, TRUE);
+
+ if (s->suspended_callback)
+ s->suspended_callback(s, s->suspended_userdata);
+
+finish:
+ pa_context_unref(c);
+}
+
+void pa_command_stream_started(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_context *c = userdata;
+ pa_stream *s;
+ uint32_t channel;
+
+ pa_assert(pd);
+ pa_assert(command == PA_COMMAND_STARTED);
+ pa_assert(t);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ pa_context_ref(c);
+
+ if (c->version < 13) {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (pa_tagstruct_getu32(t, &channel) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (!(s = pa_dynarray_get(c->playback_streams, channel)))
+ goto finish;
+
+ if (s->state != PA_STREAM_READY)
+ goto finish;
+
+ request_auto_timing_update(s, TRUE);
+
+ if (s->started_callback)
+ s->started_callback(s, s->suspended_userdata);
+
+finish:
+ pa_context_unref(c);
+}
+
void pa_command_request(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_stream *s;
pa_context *c = userdata;
uint32_t bytes, channel;
-
- assert(pd);
- assert(command == PA_COMMAND_REQUEST);
- assert(t);
- assert(c);
+
+ pa_assert(pd);
+ pa_assert(command == PA_COMMAND_REQUEST);
+ pa_assert(t);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
pa_context_ref(c);
-
+
if (pa_tagstruct_getu32(t, &channel) < 0 ||
pa_tagstruct_getu32(t, &bytes) < 0 ||
!pa_tagstruct_eof(t)) {
pa_context_fail(c, PA_ERR_PROTOCOL);
goto finish;
}
-
+
if (!(s = pa_dynarray_get(c->playback_streams, channel)))
goto finish;
- if (s->state == PA_STREAM_READY) {
- s->requested_bytes += bytes;
-
- if (s->requested_bytes > 0 && s->write_callback)
- s->write_callback(s, s->requested_bytes, s->write_userdata);
- }
+ if (s->state != PA_STREAM_READY)
+ goto finish;
+
+ s->requested_bytes += bytes;
+
+ if (s->requested_bytes > 0 && s->write_callback)
+ s->write_callback(s, s->requested_bytes, s->write_userdata);
finish:
pa_context_unref(c);
@@ -286,22 +567,38 @@ void pa_command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, PA_GCC
pa_context *c = userdata;
uint32_t channel;
- assert(pd);
- assert(command == PA_COMMAND_OVERFLOW || command == PA_COMMAND_UNDERFLOW);
- assert(t);
- assert(c);
+ pa_assert(pd);
+ pa_assert(command == PA_COMMAND_OVERFLOW || command == PA_COMMAND_UNDERFLOW);
+ pa_assert(t);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
pa_context_ref(c);
-
+
if (pa_tagstruct_getu32(t, &channel) < 0 ||
!pa_tagstruct_eof(t)) {
pa_context_fail(c, PA_ERR_PROTOCOL);
goto finish;
}
-
+
if (!(s = pa_dynarray_get(c->playback_streams, channel)))
goto finish;
+ if (s->state != PA_STREAM_READY)
+ goto finish;
+
+ if (s->smoother)
+ if (s->direction == PA_STREAM_PLAYBACK && s->buffer_attr.prebuf > 0) {
+ pa_usec_t x = pa_rtclock_usec();
+
+ if (s->timing_info_valid)
+ x -= s->timing_info.transport_usec;
+
+ pa_smoother_pause(s->smoother, x);
+ }
+
+ request_auto_timing_update(s, TRUE);
+
if (s->state == PA_STREAM_READY) {
if (command == PA_COMMAND_OVERFLOW) {
@@ -317,98 +614,122 @@ void pa_command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, PA_GCC
pa_context_unref(c);
}
-static void request_auto_timing_update(pa_stream *s, int force) {
- struct timeval next;
- assert(s);
-
- if (!(s->flags & PA_STREAM_AUTO_TIMING_UPDATE))
- return;
-
- if (s->state == PA_STREAM_READY &&
- (force || !s->auto_timing_update_requested)) {
- pa_operation *o;
-
-/* pa_log("automatically requesting new timing data"); */
-
- if ((o = pa_stream_update_timing_info(s, NULL, NULL))) {
- pa_operation_unref(o);
- s->auto_timing_update_requested = 1;
- }
- }
-
- pa_gettimeofday(&next);
- pa_timeval_add(&next, LATENCY_IPOL_INTERVAL_USEC);
- s->mainloop->time_restart(s->auto_timing_update_event, &next);
-}
-
-static void invalidate_indexes(pa_stream *s, int r, int w) {
- assert(s);
+static void invalidate_indexes(pa_stream *s, pa_bool_t r, pa_bool_t w) {
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
/* pa_log("invalidate r:%u w:%u tag:%u", r, w, s->context->ctag); */
-
+
if (s->state != PA_STREAM_READY)
return;
if (w) {
s->write_index_not_before = s->context->ctag;
-
+
if (s->timing_info_valid)
- s->timing_info.write_index_corrupt = 1;
-
+ s->timing_info.write_index_corrupt = TRUE;
+
/* pa_log("write_index invalidated"); */
}
-
+
if (r) {
s->read_index_not_before = s->context->ctag;
-
+
if (s->timing_info_valid)
- s->timing_info.read_index_corrupt = 1;
-
+ s->timing_info.read_index_corrupt = TRUE;
+
/* pa_log("read_index invalidated"); */
}
-
- if ((s->direction == PA_STREAM_PLAYBACK && r) ||
- (s->direction == PA_STREAM_RECORD && w))
- s->cached_time_valid = 0;
-
- request_auto_timing_update(s, 1);
+
+ request_auto_timing_update(s, TRUE);
}
static void auto_timing_update_callback(PA_GCC_UNUSED pa_mainloop_api *m, PA_GCC_UNUSED pa_time_event *e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
pa_stream *s = userdata;
-/* pa_log("time event"); */
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
pa_stream_ref(s);
- request_auto_timing_update(s, 0);
+ request_auto_timing_update(s, FALSE);
pa_stream_unref(s);
}
+static void create_stream_complete(pa_stream *s) {
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+ pa_assert(s->state == PA_STREAM_CREATING);
+
+ pa_stream_set_state(s, PA_STREAM_READY);
+
+ if (s->requested_bytes > 0 && s->write_callback)
+ s->write_callback(s, s->requested_bytes, s->write_userdata);
+
+ if (s->flags & PA_STREAM_AUTO_TIMING_UPDATE) {
+ struct timeval tv;
+ pa_gettimeofday(&tv);
+ tv.tv_usec += LATENCY_IPOL_INTERVAL_USEC; /* every 100 ms */
+ pa_assert(!s->auto_timing_update_event);
+ s->auto_timing_update_event = s->mainloop->time_new(s->mainloop, &tv, &auto_timing_update_callback, s);
+
+ request_auto_timing_update(s, TRUE);
+ }
+}
+
+static void automatic_buffer_attr(pa_stream *s, pa_buffer_attr *attr, const pa_sample_spec *ss) {
+ pa_assert(s);
+ pa_assert(attr);
+ pa_assert(ss);
+
+ if (s->context->version >= 13)
+ return;
+
+ /* Version older than 0.9.10 didn't do server side buffer_attr
+ * selection, hence we have to fake it on the client side */
+
+ if (!attr->maxlength <= 0)
+ attr->maxlength = 4*1024*1024; /* 4MB is the maximum queue length PulseAudio <= 0.9.9 supported. */
+
+ if (!attr->tlength <= 0)
+ attr->tlength = pa_bytes_per_second(ss)*2; /* 2s of buffering */
+
+ if (!attr->minreq <= 0)
+ attr->minreq = (2*attr->tlength)/10; /* Ask for more data when there are only 200ms left in the playback buffer */
+
+ if (!attr->prebuf)
+ attr->prebuf = attr->tlength; /* Start to play only when the playback is fully filled up once */
+
+ if (!attr->fragsize)
+ attr->fragsize = attr->tlength; /* Pass data to the app only when the buffer is filled up once */
+}
+
void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_stream *s = userdata;
-
- assert(pd);
- assert(s);
- assert(s->state == PA_STREAM_CREATING);
-
+
+ pa_assert(pd);
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+ pa_assert(s->state == PA_STREAM_CREATING);
+
pa_stream_ref(s);
-
+
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(s->context, command, t) < 0)
+ if (pa_context_handle_error(s->context, command, t, FALSE) < 0)
goto finish;
-
+
pa_stream_set_state(s, PA_STREAM_FAILED);
goto finish;
}
if (pa_tagstruct_getu32(t, &s->channel) < 0 ||
- ((s->direction != PA_STREAM_UPLOAD) && pa_tagstruct_getu32(t, &s->device_index) < 0) ||
+ s->channel == PA_INVALID_INDEX ||
+ ((s->direction != PA_STREAM_UPLOAD) && (pa_tagstruct_getu32(t, &s->stream_index) < 0 || s->stream_index == PA_INVALID_INDEX)) ||
((s->direction != PA_STREAM_RECORD) && pa_tagstruct_getu32(t, &s->requested_bytes) < 0)) {
pa_context_fail(s->context, PA_ERR_PROTOCOL);
goto finish;
}
- if (pa_context_get_server_protocol_version(s->context) >= 9) {
+ if (s->context->version >= 9) {
if (s->direction == PA_STREAM_PLAYBACK) {
if (pa_tagstruct_getu32(t, &s->buffer_attr.maxlength) < 0 ||
pa_tagstruct_getu32(t, &s->buffer_attr.tlength) < 0 ||
@@ -426,44 +747,77 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED
}
}
+ if (s->context->version >= 12 && s->direction != PA_STREAM_UPLOAD) {
+ pa_sample_spec ss;
+ pa_channel_map cm;
+ const char *dn = NULL;
+ pa_bool_t suspended;
+
+ if (pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
+ pa_tagstruct_get_channel_map(t, &cm) < 0 ||
+ pa_tagstruct_getu32(t, &s->device_index) < 0 ||
+ pa_tagstruct_gets(t, &dn) < 0 ||
+ pa_tagstruct_get_boolean(t, &suspended) < 0) {
+ pa_context_fail(s->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (!dn || s->device_index == PA_INVALID_INDEX ||
+ ss.channels != cm.channels ||
+ !pa_channel_map_valid(&cm) ||
+ !pa_sample_spec_valid(&ss) ||
+ (!(s->flags & PA_STREAM_FIX_FORMAT) && ss.format != s->sample_spec.format) ||
+ (!(s->flags & PA_STREAM_FIX_RATE) && ss.rate != s->sample_spec.rate) ||
+ (!(s->flags & PA_STREAM_FIX_CHANNELS) && !pa_channel_map_equal(&cm, &s->channel_map))) {
+ pa_context_fail(s->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ pa_xfree(s->device_name);
+ s->device_name = pa_xstrdup(dn);
+ s->suspended = suspended;
+
+ s->channel_map = cm;
+ s->sample_spec = ss;
+ }
+
+ if (s->context->version >= 13 && s->direction != PA_STREAM_UPLOAD) {
+ pa_usec_t usec;
+
+ if (pa_tagstruct_get_usec(t, &usec) < 0) {
+ pa_context_fail(s->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (s->direction == PA_STREAM_RECORD)
+ s->timing_info.configured_source_usec = usec;
+ else
+ s->timing_info.configured_sink_usec = usec;
+ }
+
if (!pa_tagstruct_eof(t)) {
pa_context_fail(s->context, PA_ERR_PROTOCOL);
goto finish;
}
if (s->direction == PA_STREAM_RECORD) {
- assert(!s->record_memblockq);
-
+ pa_assert(!s->record_memblockq);
+
s->record_memblockq = pa_memblockq_new(
- 0,
+ 0,
s->buffer_attr.maxlength,
0,
pa_frame_size(&s->sample_spec),
1,
0,
+ 0,
NULL);
}
- s->channel_valid = 1;
+ s->channel_valid = TRUE;
pa_dynarray_put((s->direction == PA_STREAM_RECORD) ? s->context->record_streams : s->context->playback_streams, s->channel, s);
- pa_stream_set_state(s, PA_STREAM_READY);
-
- if (s->direction != PA_STREAM_UPLOAD &&
- s->flags & PA_STREAM_AUTO_TIMING_UPDATE) {
- struct timeval tv;
-
- pa_gettimeofday(&tv);
- tv.tv_usec += LATENCY_IPOL_INTERVAL_USEC; /* every 100 ms */
-
- assert(!s->auto_timing_update_event);
- s->auto_timing_update_event = s->mainloop->time_new(s->mainloop, &tv, &auto_timing_update_callback, s);
-
- request_auto_timing_update(s, 1);
- }
-
- if (s->requested_bytes > 0 && s->ref > 1 && s->write_callback)
- s->write_callback(s, s->requested_bytes, s->write_userdata);
+ create_stream_complete(s);
finish:
pa_stream_unref(s);
@@ -477,19 +831,40 @@ static int create_stream(
pa_stream_flags_t flags,
const pa_cvolume *volume,
pa_stream *sync_stream) {
-
+
pa_tagstruct *t;
uint32_t tag;
-
- assert(s);
- assert(s->ref >= 1);
-
+
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+ pa_assert(direction == PA_STREAM_PLAYBACK || direction == PA_STREAM_RECORD);
+
PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_UNCONNECTED, PA_ERR_BADSTATE);
- PA_CHECK_VALIDITY(s->context, !(flags & ~((direction != PA_STREAM_UPLOAD ?
- PA_STREAM_START_CORKED|
- PA_STREAM_INTERPOLATE_TIMING|
- PA_STREAM_NOT_MONOTONOUS|
- PA_STREAM_AUTO_TIMING_UPDATE : 0))), PA_ERR_INVALID);
+ PA_CHECK_VALIDITY(s->context, s->direct_on_input == PA_INVALID_INDEX || direction == PA_STREAM_RECORD, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY(s->context, !(flags & ~(PA_STREAM_START_CORKED|
+ PA_STREAM_INTERPOLATE_TIMING|
+ PA_STREAM_NOT_MONOTONOUS|
+ PA_STREAM_AUTO_TIMING_UPDATE|
+ PA_STREAM_NO_REMAP_CHANNELS|
+ PA_STREAM_NO_REMIX_CHANNELS|
+ PA_STREAM_FIX_FORMAT|
+ PA_STREAM_FIX_RATE|
+ PA_STREAM_FIX_CHANNELS|
+ PA_STREAM_DONT_MOVE|
+ PA_STREAM_VARIABLE_RATE|
+ PA_STREAM_PEAK_DETECT|
+ PA_STREAM_START_MUTED|
+ PA_STREAM_ADJUST_LATENCY)), PA_ERR_INVALID);
+
+ PA_CHECK_VALIDITY(s->context, s->context->version >= 12 || !(flags & PA_STREAM_VARIABLE_RATE), PA_ERR_NOTSUPPORTED);
+ PA_CHECK_VALIDITY(s->context, s->context->version >= 13 || !(flags & PA_STREAM_PEAK_DETECT), PA_ERR_NOTSUPPORTED);
+ /* Althought some of the other flags are not supported on older
+ * version, we don't check for them here, because it doesn't hurt
+ * when they are passed but actually not supported. This makes
+ * client development easier */
+
+ PA_CHECK_VALIDITY(s->context, direction == PA_STREAM_PLAYBACK || !(flags & (PA_STREAM_START_MUTED)), PA_ERR_INVALID);
+ PA_CHECK_VALIDITY(s->context, direction == PA_STREAM_RECORD || !(flags & (PA_STREAM_PEAK_DETECT)), PA_ERR_INVALID);
PA_CHECK_VALIDITY(s->context, !volume || volume->channels == s->sample_spec.channels, PA_ERR_INVALID);
PA_CHECK_VALIDITY(s->context, !sync_stream || (direction == PA_STREAM_PLAYBACK && sync_stream->direction == PA_STREAM_PLAYBACK), PA_ERR_INVALID);
@@ -497,19 +872,25 @@ static int create_stream(
s->direction = direction;
s->flags = flags;
-
+
if (sync_stream)
s->syncid = sync_stream->syncid;
-
+
if (attr)
s->buffer_attr = *attr;
- else {
- /* half a second */
- s->buffer_attr.tlength = pa_bytes_per_second(&s->sample_spec)/2;
- s->buffer_attr.maxlength = (s->buffer_attr.tlength*3)/2;
- s->buffer_attr.minreq = s->buffer_attr.tlength/100;
- s->buffer_attr.prebuf = s->buffer_attr.tlength - s->buffer_attr.minreq;
- s->buffer_attr.fragsize = s->buffer_attr.tlength/100;
+ automatic_buffer_attr(s, &s->buffer_attr, &s->sample_spec);
+
+ if (flags & PA_STREAM_INTERPOLATE_TIMING) {
+ pa_usec_t x;
+
+ if (s->smoother)
+ pa_smoother_free(s->smoother);
+
+ s->smoother = pa_smoother_new(SMOOTHER_ADJUST_TIME, SMOOTHER_HISTORY_TIME, !(flags & PA_STREAM_NOT_MONOTONOUS), SMOOTHER_MIN_HISTORY);
+
+ x = pa_rtclock_usec();
+ pa_smoother_set_time_offset(s->smoother, x);
+ pa_smoother_pause(s->smoother, x);
}
if (!dev)
@@ -519,10 +900,12 @@ static int create_stream(
s->context,
s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_CREATE_PLAYBACK_STREAM : PA_COMMAND_CREATE_RECORD_STREAM,
&tag);
-
+
+ if (s->context->version < 13)
+ pa_tagstruct_puts(t, pa_proplist_gets(s->proplist, PA_PROP_MEDIA_NAME));
+
pa_tagstruct_put(
t,
- PA_TAG_STRING, s->name,
PA_TAG_SAMPLE_SPEC, &s->sample_spec,
PA_TAG_CHANNEL_MAP, &s->channel_map,
PA_TAG_U32, PA_INVALID_INDEX,
@@ -530,7 +913,7 @@ static int create_stream(
PA_TAG_U32, s->buffer_attr.maxlength,
PA_TAG_BOOLEAN, !!(flags & PA_STREAM_START_CORKED),
PA_TAG_INVALID);
-
+
if (s->direction == PA_STREAM_PLAYBACK) {
pa_cvolume cv;
@@ -544,16 +927,46 @@ static int create_stream(
if (!volume)
volume = pa_cvolume_reset(&cv, s->sample_spec.channels);
-
+
pa_tagstruct_put_cvolume(t, volume);
} else
pa_tagstruct_putu32(t, s->buffer_attr.fragsize);
+ if (s->context->version >= 12) {
+ pa_tagstruct_put(
+ t,
+ PA_TAG_BOOLEAN, flags & PA_STREAM_NO_REMAP_CHANNELS,
+ PA_TAG_BOOLEAN, flags & PA_STREAM_NO_REMIX_CHANNELS,
+ PA_TAG_BOOLEAN, flags & PA_STREAM_FIX_FORMAT,
+ PA_TAG_BOOLEAN, flags & PA_STREAM_FIX_RATE,
+ PA_TAG_BOOLEAN, flags & PA_STREAM_FIX_CHANNELS,
+ PA_TAG_BOOLEAN, flags & PA_STREAM_DONT_MOVE,
+ PA_TAG_BOOLEAN, flags & PA_STREAM_VARIABLE_RATE,
+ PA_TAG_INVALID);
+ }
+
+ if (s->context->version >= 13) {
+
+ if (s->direction == PA_STREAM_PLAYBACK)
+ pa_tagstruct_put_boolean(t, flags & PA_STREAM_START_MUTED);
+ else
+ pa_tagstruct_put_boolean(t, flags & PA_STREAM_PEAK_DETECT);
+
+ pa_tagstruct_put(
+ t,
+ PA_TAG_BOOLEAN, flags & PA_STREAM_ADJUST_LATENCY,
+ PA_TAG_PROPLIST, s->proplist,
+ PA_TAG_INVALID);
+
+ if (s->direction == PA_STREAM_RECORD)
+ pa_tagstruct_putu32(t, s->direct_on_input);
+ }
+
pa_pstream_send_tagstruct(s->context->pstream, t);
pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_create_stream_callback, s, NULL);
pa_stream_set_state(s, PA_STREAM_CREATING);
-
+
pa_stream_unref(s);
return 0;
}
@@ -565,10 +978,10 @@ int pa_stream_connect_playback(
pa_stream_flags_t flags,
pa_cvolume *volume,
pa_stream *sync_stream) {
-
- assert(s);
- assert(s->ref >= 1);
-
+
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
return create_stream(PA_STREAM_PLAYBACK, s, dev, attr, flags, volume, sync_stream);
}
@@ -577,9 +990,9 @@ int pa_stream_connect_record(
const char *dev,
const pa_buffer_attr *attr,
pa_stream_flags_t flags) {
-
- assert(s);
- assert(s->ref >= 1);
+
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
return create_stream(PA_STREAM_RECORD, s, dev, attr, flags, NULL, NULL);
}
@@ -591,12 +1004,16 @@ int pa_stream_write(
void (*free_cb)(void *p),
int64_t offset,
pa_seek_mode_t seek) {
-
+
pa_memchunk chunk;
-
- assert(s);
- assert(s->ref >= 1);
- assert(data);
+ pa_seek_mode_t t_seek;
+ int64_t t_offset;
+ size_t t_length;
+ const void *t_data;
+
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+ pa_assert(data);
PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_PLAYBACK || s->direction == PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
@@ -606,72 +1023,93 @@ int pa_stream_write(
if (length <= 0)
return 0;
- if (free_cb)
- chunk.memblock = pa_memblock_new_user(s->context->mempool, (void*) data, length, free_cb, 1);
- else {
- void *tdata;
- chunk.memblock = pa_memblock_new(s->context->mempool, length);
- tdata = pa_memblock_acquire(chunk.memblock);
- memcpy(tdata, data, length);
- pa_memblock_release(chunk.memblock);
+ t_seek = seek;
+ t_offset = offset;
+ t_length = length;
+ t_data = data;
+
+ while (t_length > 0) {
+
+ chunk.index = 0;
+
+ if (free_cb && !pa_pstream_get_shm(s->context->pstream)) {
+ chunk.memblock = pa_memblock_new_user(s->context->mempool, (void*) t_data, t_length, free_cb, 1);
+ chunk.length = t_length;
+ } else {
+ void *d;
+
+ chunk.length = PA_MIN(t_length, pa_mempool_block_size_max(s->context->mempool));
+ chunk.memblock = pa_memblock_new(s->context->mempool, chunk.length);
+
+ d = pa_memblock_acquire(chunk.memblock);
+ memcpy(d, t_data, chunk.length);
+ pa_memblock_release(chunk.memblock);
+ }
+
+ pa_pstream_send_memblock(s->context->pstream, s->channel, t_offset, t_seek, &chunk);
+
+ t_offset = 0;
+ t_seek = PA_SEEK_RELATIVE;
+
+ t_data = (const uint8_t*) t_data + chunk.length;
+ t_length -= chunk.length;
+
+ pa_memblock_unref(chunk.memblock);
}
-
- chunk.index = 0;
- chunk.length = length;
- pa_pstream_send_memblock(s->context->pstream, s->channel, offset, seek, &chunk);
- pa_memblock_unref(chunk.memblock);
-
+ if (free_cb && pa_pstream_get_shm(s->context->pstream))
+ free_cb((void*) data);
+
if (length < s->requested_bytes)
s->requested_bytes -= length;
else
s->requested_bytes = 0;
if (s->direction == PA_STREAM_PLAYBACK) {
-
+
/* Update latency request correction */
if (s->write_index_corrections[s->current_write_index_correction].valid) {
-
+
if (seek == PA_SEEK_ABSOLUTE) {
- s->write_index_corrections[s->current_write_index_correction].corrupt = 0;
- s->write_index_corrections[s->current_write_index_correction].absolute = 1;
+ s->write_index_corrections[s->current_write_index_correction].corrupt = FALSE;
+ s->write_index_corrections[s->current_write_index_correction].absolute = TRUE;
s->write_index_corrections[s->current_write_index_correction].value = offset + length;
} else if (seek == PA_SEEK_RELATIVE) {
if (!s->write_index_corrections[s->current_write_index_correction].corrupt)
s->write_index_corrections[s->current_write_index_correction].value += offset + length;
} else
- s->write_index_corrections[s->current_write_index_correction].corrupt = 1;
+ s->write_index_corrections[s->current_write_index_correction].corrupt = TRUE;
}
-
+
/* Update the write index in the already available latency data */
if (s->timing_info_valid) {
-
+
if (seek == PA_SEEK_ABSOLUTE) {
- s->timing_info.write_index_corrupt = 0;
+ s->timing_info.write_index_corrupt = FALSE;
s->timing_info.write_index = offset + length;
} else if (seek == PA_SEEK_RELATIVE) {
if (!s->timing_info.write_index_corrupt)
s->timing_info.write_index += offset + length;
} else
- s->timing_info.write_index_corrupt = 1;
+ s->timing_info.write_index_corrupt = TRUE;
}
if (!s->timing_info_valid || s->timing_info.write_index_corrupt)
- request_auto_timing_update(s, 1);
+ request_auto_timing_update(s, TRUE);
}
-
+
return 0;
}
int pa_stream_peek(pa_stream *s, const void **data, size_t *length) {
- assert(s);
- assert(s->ref >= 1);
- assert(data);
- assert(length);
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+ pa_assert(data);
+ pa_assert(length);
PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE);
-
+
if (!s->peek_memchunk.memblock) {
if (pa_memblockq_peek(s->record_memblockq, &s->peek_memchunk) < 0) {
@@ -683,49 +1121,47 @@ int pa_stream_peek(pa_stream *s, const void **data, size_t *length) {
s->peek_data = pa_memblock_acquire(s->peek_memchunk.memblock);
}
- assert(s->peek_data);
+ pa_assert(s->peek_data);
*data = (uint8_t*) s->peek_data + s->peek_memchunk.index;
*length = s->peek_memchunk.length;
return 0;
}
int pa_stream_drop(pa_stream *s) {
- assert(s);
- assert(s->ref >= 1);
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY(s->context, s->peek_memchunk.memblock, PA_ERR_BADSTATE);
-
- pa_memblockq_drop(s->record_memblockq, &s->peek_memchunk, s->peek_memchunk.length);
+
+ pa_memblockq_drop(s->record_memblockq, s->peek_memchunk.length);
/* Fix the simulated local read index */
if (s->timing_info_valid && !s->timing_info.read_index_corrupt)
s->timing_info.read_index += s->peek_memchunk.length;
- assert(s->peek_data);
+ pa_assert(s->peek_data);
pa_memblock_release(s->peek_memchunk.memblock);
pa_memblock_unref(s->peek_memchunk.memblock);
- s->peek_memchunk.length = 0;
- s->peek_memchunk.index = 0;
- s->peek_memchunk.memblock = NULL;
+ pa_memchunk_reset(&s->peek_memchunk);
return 0;
}
size_t pa_stream_writable_size(pa_stream *s) {
- assert(s);
- assert(s->ref >= 1);
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE, (size_t) -1);
PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direction != PA_STREAM_RECORD, PA_ERR_BADSTATE, (size_t) -1);
-
+
return s->requested_bytes;
}
size_t pa_stream_readable_size(pa_stream *s) {
- assert(s);
- assert(s->ref >= 1);
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE, (size_t) -1);
PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE, (size_t) -1);
@@ -737,9 +1173,9 @@ pa_operation * pa_stream_drain(pa_stream *s, pa_stream_success_cb_t cb, void *us
pa_operation *o;
pa_tagstruct *t;
uint32_t tag;
-
- assert(s);
- assert(s->ref >= 1);
+
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE);
@@ -754,13 +1190,75 @@ pa_operation * pa_stream_drain(pa_stream *s, pa_stream_success_cb_t cb, void *us
return o;
}
+static pa_usec_t calc_time(pa_stream *s, pa_bool_t ignore_transport) {
+ pa_usec_t usec;
+
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+ pa_assert(s->state == PA_STREAM_READY);
+ pa_assert(s->direction != PA_STREAM_UPLOAD);
+ pa_assert(s->timing_info_valid);
+ pa_assert(s->direction != PA_STREAM_PLAYBACK || !s->timing_info.read_index_corrupt);
+ pa_assert(s->direction != PA_STREAM_RECORD || !s->timing_info.write_index_corrupt);
+
+ if (s->direction == PA_STREAM_PLAYBACK) {
+ /* The last byte that was written into the output device
+ * had this time value associated */
+ usec = pa_bytes_to_usec(s->timing_info.read_index < 0 ? 0 : (uint64_t) s->timing_info.read_index, &s->sample_spec);
+
+ if (!s->corked && !s->suspended) {
+
+ if (!ignore_transport)
+ /* Because the latency info took a little time to come
+ * to us, we assume that the real output time is actually
+ * a little ahead */
+ usec += s->timing_info.transport_usec;
+
+ /* However, the output device usually maintains a buffer
+ too, hence the real sample currently played is a little
+ back */
+ if (s->timing_info.sink_usec >= usec)
+ usec = 0;
+ else
+ usec -= s->timing_info.sink_usec;
+ }
+
+ } else if (s->direction == PA_STREAM_RECORD) {
+ /* The last byte written into the server side queue had
+ * this time value associated */
+ usec = pa_bytes_to_usec(s->timing_info.write_index < 0 ? 0 : (uint64_t) s->timing_info.write_index, &s->sample_spec);
+
+ if (!s->corked && !s->suspended) {
+
+ if (!ignore_transport)
+ /* Add transport latency */
+ usec += s->timing_info.transport_usec;
+
+ /* Add latency of data in device buffer */
+ usec += s->timing_info.source_usec;
+
+ /* If this is a monitor source, we need to correct the
+ * time by the playback device buffer */
+ if (s->timing_info.sink_usec >= usec)
+ usec = 0;
+ else
+ usec -= s->timing_info.sink_usec;
+ }
+ }
+
+ return usec;
+}
+
static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_operation *o = userdata;
struct timeval local, remote, now;
pa_timing_info *i;
-
- assert(pd);
- assert(o);
+ pa_bool_t playing = FALSE;
+ uint64_t underrun_for = 0, playing_for = 0;
+
+ pa_assert(pd);
+ pa_assert(o);
+ pa_assert(PA_REFCNT_VALUE(o) >= 1);
if (!o->context || !o->stream)
goto finish;
@@ -768,58 +1266,77 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command,
i = &o->stream->timing_info;
/* pa_log("pre corrupt w:%u r:%u\n", !o->stream->timing_info_valid || i->write_index_corrupt,!o->stream->timing_info_valid || i->read_index_corrupt); */
-
- o->stream->timing_info_valid = 0;
- i->write_index_corrupt = 0;
- i->read_index_corrupt = 0;
+
+ o->stream->timing_info_valid = FALSE;
+ i->write_index_corrupt = FALSE;
+ i->read_index_corrupt = FALSE;
/* pa_log("timing update %u\n", tag); */
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(o->context, command, t) < 0)
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
- } else if (pa_tagstruct_get_usec(t, &i->sink_usec) < 0 ||
- pa_tagstruct_get_usec(t, &i->source_usec) < 0 ||
- pa_tagstruct_get_boolean(t, &i->playing) < 0 ||
- pa_tagstruct_get_timeval(t, &local) < 0 ||
- pa_tagstruct_get_timeval(t, &remote) < 0 ||
- pa_tagstruct_gets64(t, &i->write_index) < 0 ||
- pa_tagstruct_gets64(t, &i->read_index) < 0 ||
- !pa_tagstruct_eof(t)) {
- pa_context_fail(o->context, PA_ERR_PROTOCOL);
- goto finish;
-
} else {
- o->stream->timing_info_valid = 1;
+
+ if (pa_tagstruct_get_usec(t, &i->sink_usec) < 0 ||
+ pa_tagstruct_get_usec(t, &i->source_usec) < 0 ||
+ pa_tagstruct_get_boolean(t, &playing) < 0 ||
+ pa_tagstruct_get_timeval(t, &local) < 0 ||
+ pa_tagstruct_get_timeval(t, &remote) < 0 ||
+ pa_tagstruct_gets64(t, &i->write_index) < 0 ||
+ pa_tagstruct_gets64(t, &i->read_index) < 0) {
+
+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (o->context->version >= 13 &&
+ o->stream->direction == PA_STREAM_PLAYBACK)
+ if (pa_tagstruct_getu64(t, &underrun_for) < 0 ||
+ pa_tagstruct_getu64(t, &playing_for) < 0) {
+
+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+
+ if (!pa_tagstruct_eof(t)) {
+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ o->stream->timing_info_valid = TRUE;
+ i->playing = (int) playing;
+ i->since_underrun = playing ? playing_for : underrun_for;
pa_gettimeofday(&now);
-
+
/* Calculcate timestamps */
if (pa_timeval_cmp(&local, &remote) <= 0 && pa_timeval_cmp(&remote, &now) <= 0) {
/* local and remote seem to have synchronized clocks */
-
+
if (o->stream->direction == PA_STREAM_PLAYBACK)
i->transport_usec = pa_timeval_diff(&remote, &local);
else
i->transport_usec = pa_timeval_diff(&now, &remote);
-
- i->synchronized_clocks = 1;
+
+ i->synchronized_clocks = TRUE;
i->timestamp = remote;
} else {
/* clocks are not synchronized, let's estimate latency then */
i->transport_usec = pa_timeval_diff(&now, &local)/2;
- i->synchronized_clocks = 0;
+ i->synchronized_clocks = FALSE;
i->timestamp = local;
pa_timeval_add(&i->timestamp, i->transport_usec);
}
/* Invalidate read and write indexes if necessary */
if (tag < o->stream->read_index_not_before)
- i->read_index_corrupt = 1;
+ i->read_index_corrupt = TRUE;
if (tag < o->stream->write_index_not_before)
- i->write_index_corrupt = 1;
+ i->write_index_corrupt = TRUE;
if (o->stream->direction == PA_STREAM_PLAYBACK) {
/* Write index correction */
@@ -828,7 +1345,7 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command,
uint32_t ctag = tag;
/* Go through the saved correction values and add up the total correction.*/
-
+
for (n = 0, j = o->stream->current_write_index_correction+1;
n < PA_MAX_WRITE_INDEX_CORRECTIONS;
n++, j = (j + 1) % PA_MAX_WRITE_INDEX_CORRECTIONS) {
@@ -845,11 +1362,11 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command,
if (o->stream->write_index_corrections[j].corrupt) {
/* A corrupting seek was made */
i->write_index = 0;
- i->write_index_corrupt = 1;
+ i->write_index_corrupt = TRUE;
} else if (o->stream->write_index_corrections[j].absolute) {
/* An absolute seek was made */
i->write_index = o->stream->write_index_corrections[j].value;
- i->write_index_corrupt = 0;
+ i->write_index_corrupt = FALSE;
} else if (!i->write_index_corrupt) {
/* A relative seek was made */
i->write_index += o->stream->write_index_corrections[j].value;
@@ -863,34 +1380,66 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command,
if (!i->read_index_corrupt)
i->read_index -= pa_memblockq_get_length(o->stream->record_memblockq);
}
-
- o->stream->cached_time_valid = 0;
- }
- o->stream->auto_timing_update_requested = 0;
/* pa_log("post corrupt w:%u r:%u\n", i->write_index_corrupt || !o->stream->timing_info_valid, i->read_index_corrupt || !o->stream->timing_info_valid); */
-
- /* Clear old correction entries */
- if (o->stream->direction == PA_STREAM_PLAYBACK) {
- int n;
-
- for (n = 0; n < PA_MAX_WRITE_INDEX_CORRECTIONS; n++) {
- if (!o->stream->write_index_corrections[n].valid)
- continue;
-
- if (o->stream->write_index_corrections[n].tag <= tag)
- o->stream->write_index_corrections[n].valid = 0;
+
+ /* Clear old correction entries */
+ if (o->stream->direction == PA_STREAM_PLAYBACK) {
+ int n;
+
+ for (n = 0; n < PA_MAX_WRITE_INDEX_CORRECTIONS; n++) {
+ if (!o->stream->write_index_corrections[n].valid)
+ continue;
+
+ if (o->stream->write_index_corrections[n].tag <= tag)
+ o->stream->write_index_corrections[n].valid = FALSE;
+ }
+ }
+
+ /* Update smoother */
+ if (o->stream->smoother) {
+ pa_usec_t u, x;
+
+ u = x = pa_rtclock_usec() - i->transport_usec;
+
+ if (o->stream->direction == PA_STREAM_PLAYBACK &&
+ o->context->version >= 13) {
+ pa_usec_t su;
+
+ /* If we weren't playing then it will take some time
+ * until the audio will actually come out through the
+ * speakers. Since we follow that timing here, we need
+ * to try to fix this up */
+
+ su = pa_bytes_to_usec(i->since_underrun, &o->stream->sample_spec);
+
+ if (su < i->sink_usec)
+ x += i->sink_usec - su;
+ }
+
+ if (!i->playing)
+ pa_smoother_pause(o->stream->smoother, x);
+
+ /* Update the smoother */
+ if ((o->stream->direction == PA_STREAM_PLAYBACK && !i->read_index_corrupt) ||
+ (o->stream->direction == PA_STREAM_RECORD && !i->write_index_corrupt))
+ pa_smoother_put(o->stream->smoother, u, calc_time(o->stream, TRUE));
+
+ if (i->playing)
+ pa_smoother_resume(o->stream->smoother, x);
}
}
+ o->stream->auto_timing_update_requested = FALSE;
+
if (o->stream->latency_update_callback)
o->stream->latency_update_callback(o->stream, o->stream->latency_update_userdata);
-
+
if (o->callback && o->stream && o->stream->state == PA_STREAM_READY) {
pa_stream_success_cb_t cb = (pa_stream_success_cb_t) o->callback;
cb(o->stream, o->stream->timing_info_valid, o->userdata);
}
-
+
finish:
pa_operation_done(o);
@@ -903,58 +1452,58 @@ pa_operation* pa_stream_update_timing_info(pa_stream *s, pa_stream_success_cb_t
pa_tagstruct *t;
struct timeval now;
int cidx = 0;
-
- assert(s);
- assert(s->ref >= 1);
+
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
-
+
if (s->direction == PA_STREAM_PLAYBACK) {
/* Find a place to store the write_index correction data for this entry */
cidx = (s->current_write_index_correction + 1) % PA_MAX_WRITE_INDEX_CORRECTIONS;
-
+
/* Check if we could allocate a correction slot. If not, there are too many outstanding queries */
PA_CHECK_VALIDITY_RETURN_NULL(s->context, !s->write_index_corrections[cidx].valid, PA_ERR_INTERNAL);
}
o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
-
+
t = pa_tagstruct_command(
s->context,
s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_GET_PLAYBACK_LATENCY : PA_COMMAND_GET_RECORD_LATENCY,
&tag);
pa_tagstruct_putu32(t, s->channel);
pa_tagstruct_put_timeval(t, pa_gettimeofday(&now));
-
+
pa_pstream_send_tagstruct(s->context->pstream, t);
pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, stream_get_timing_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
if (s->direction == PA_STREAM_PLAYBACK) {
/* Fill in initial correction data */
- o->stream->current_write_index_correction = cidx;
- o->stream->write_index_corrections[cidx].valid = 1;
- o->stream->write_index_corrections[cidx].tag = tag;
- o->stream->write_index_corrections[cidx].absolute = 0;
- o->stream->write_index_corrections[cidx].value = 0;
- o->stream->write_index_corrections[cidx].corrupt = 0;
+
+ s->current_write_index_correction = cidx;
+
+ s->write_index_corrections[cidx].valid = TRUE;
+ s->write_index_corrections[cidx].absolute = FALSE;
+ s->write_index_corrections[cidx].corrupt = FALSE;
+ s->write_index_corrections[cidx].tag = tag;
+ s->write_index_corrections[cidx].value = 0;
}
-
-/* pa_log("requesting update %u\n", tag); */
-
+
return o;
}
void pa_stream_disconnect_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_stream *s = userdata;
-
- assert(pd);
- assert(s);
- assert(s->ref >= 1);
+
+ pa_assert(pd);
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
pa_stream_ref(s);
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(s->context, command, t) < 0)
+ if (pa_context_handle_error(s->context, command, t, FALSE) < 0)
goto finish;
pa_stream_set_state(s, PA_STREAM_FAILED);
@@ -973,9 +1522,9 @@ finish:
int pa_stream_disconnect(pa_stream *s) {
pa_tagstruct *t;
uint32_t tag;
-
- assert(s);
- assert(s->ref >= 1);
+
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
PA_CHECK_VALIDITY(s->context, s->channel_valid, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY(s->context, s->context->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
@@ -996,66 +1545,117 @@ int pa_stream_disconnect(pa_stream *s) {
}
void pa_stream_set_read_callback(pa_stream *s, pa_stream_request_cb_t cb, void *userdata) {
- assert(s);
- assert(s->ref >= 1);
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+ if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+ return;
s->read_callback = cb;
s->read_userdata = userdata;
}
void pa_stream_set_write_callback(pa_stream *s, pa_stream_request_cb_t cb, void *userdata) {
- assert(s);
- assert(s->ref >= 1);
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+ if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+ return;
s->write_callback = cb;
s->write_userdata = userdata;
}
void pa_stream_set_state_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) {
- assert(s);
- assert(s->ref >= 1);
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+ if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+ return;
s->state_callback = cb;
s->state_userdata = userdata;
}
void pa_stream_set_overflow_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) {
- assert(s);
- assert(s->ref >= 1);
-
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+ if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+ return;
+
s->overflow_callback = cb;
s->overflow_userdata = userdata;
}
void pa_stream_set_underflow_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) {
- assert(s);
- assert(s->ref >= 1);
-
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+ if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+ return;
+
s->underflow_callback = cb;
s->underflow_userdata = userdata;
}
void pa_stream_set_latency_update_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) {
- assert(s);
- assert(s->ref >= 1);
-
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+ if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+ return;
+
s->latency_update_callback = cb;
s->latency_update_userdata = userdata;
}
+void pa_stream_set_moved_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) {
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+ if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+ return;
+
+ s->moved_callback = cb;
+ s->moved_userdata = userdata;
+}
+
+void pa_stream_set_suspended_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) {
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+ if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+ return;
+
+ s->suspended_callback = cb;
+ s->suspended_userdata = userdata;
+}
+
+void pa_stream_set_started_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) {
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+ if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+ return;
+
+ s->started_callback = cb;
+ s->started_userdata = userdata;
+}
+
void pa_stream_simple_ack_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_operation *o = userdata;
int success = 1;
-
- assert(pd);
- assert(o);
- assert(o->ref >= 1);
+
+ pa_assert(pd);
+ pa_assert(o);
+ pa_assert(PA_REFCNT_VALUE(o) >= 1);
if (!o->context)
goto finish;
-
+
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(o->context, command, t) < 0)
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
success = 0;
@@ -1078,15 +1678,15 @@ pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, voi
pa_operation *o;
pa_tagstruct *t;
uint32_t tag;
-
- assert(s);
- assert(s->ref >= 1);
-
+
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
s->corked = b;
-
+
o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
t = pa_tagstruct_command(
@@ -1098,8 +1698,18 @@ pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, voi
pa_pstream_send_tagstruct(s->context->pstream, t);
pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+ if (s->smoother) {
+ pa_usec_t x = pa_rtclock_usec();
+
+ if (s->timing_info_valid)
+ x += s->timing_info.transport_usec;
+
+ if (s->suspended || s->corked)
+ pa_smoother_pause(s->smoother, x);
+ }
+
if (s->direction == PA_STREAM_PLAYBACK)
- invalidate_indexes(s, 1, 0);
+ invalidate_indexes(s, TRUE, FALSE);
return o;
}
@@ -1108,12 +1718,12 @@ static pa_operation* stream_send_simple_command(pa_stream *s, uint32_t command,
pa_tagstruct *t;
pa_operation *o;
uint32_t tag;
-
- assert(s);
- assert(s->ref >= 1);
+
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
-
+
o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
t = pa_tagstruct_command(s->context, command, &tag);
@@ -1126,84 +1736,114 @@ static pa_operation* stream_send_simple_command(pa_stream *s, uint32_t command,
pa_operation* pa_stream_flush(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) {
pa_operation *o;
-
+
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
-
+
if ((o = stream_send_simple_command(s, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_FLUSH_PLAYBACK_STREAM : PA_COMMAND_FLUSH_RECORD_STREAM, cb, userdata))) {
if (s->direction == PA_STREAM_PLAYBACK) {
if (s->write_index_corrections[s->current_write_index_correction].valid)
- s->write_index_corrections[s->current_write_index_correction].corrupt = 1;
-
+ s->write_index_corrections[s->current_write_index_correction].corrupt = TRUE;
+
if (s->timing_info_valid)
- s->timing_info.write_index_corrupt = 1;
+ s->timing_info.write_index_corrupt = TRUE;
- if (s->buffer_attr.prebuf > 0)
- invalidate_indexes(s, 1, 0);
+ if (s->buffer_attr.prebuf > 0)
+ invalidate_indexes(s, TRUE, FALSE);
else
- request_auto_timing_update(s, 1);
+ request_auto_timing_update(s, TRUE);
+
+ if (s->smoother && s->buffer_attr.prebuf > 0) {
+ pa_usec_t x = pa_rtclock_usec();
+
+ if (s->timing_info_valid)
+ x += s->timing_info.transport_usec;
+
+ pa_smoother_pause(s->smoother, x);
+ }
+
} else
- invalidate_indexes(s, 0, 1);
+ invalidate_indexes(s, FALSE, TRUE);
}
-
+
return o;
}
pa_operation* pa_stream_prebuf(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) {
pa_operation *o;
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->buffer_attr.prebuf > 0, PA_ERR_BADSTATE);
if ((o = stream_send_simple_command(s, PA_COMMAND_PREBUF_PLAYBACK_STREAM, cb, userdata)))
- invalidate_indexes(s, 1, 0);
+ invalidate_indexes(s, TRUE, FALSE);
return o;
}
pa_operation* pa_stream_trigger(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) {
pa_operation *o;
-
+
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->buffer_attr.prebuf > 0, PA_ERR_BADSTATE);
if ((o = stream_send_simple_command(s, PA_COMMAND_TRIGGER_PLAYBACK_STREAM, cb, userdata)))
- invalidate_indexes(s, 1, 0);
-
+ invalidate_indexes(s, TRUE, FALSE);
+
return o;
}
pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_success_cb_t cb, void *userdata) {
pa_operation *o;
- pa_tagstruct *t;
- uint32_t tag;
-
- assert(s);
- assert(s->ref >= 1);
- assert(name);
+
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+ pa_assert(name);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
- o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
+ if (s->context->version >= 13) {
+ pa_proplist *p = pa_proplist_new();
- t = pa_tagstruct_command(
- s->context,
- s->direction == PA_STREAM_RECORD ? PA_COMMAND_SET_RECORD_STREAM_NAME : PA_COMMAND_SET_PLAYBACK_STREAM_NAME,
- &tag);
- pa_tagstruct_putu32(t, s->channel);
- pa_tagstruct_puts(t, name);
- pa_pstream_send_tagstruct(s->context->pstream, t);
- pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+ pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, name);
+ o = pa_stream_proplist_update(s, PA_UPDATE_REPLACE, p, cb, userdata);
+ pa_proplist_free(p);
+ } else {
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
+ t = pa_tagstruct_command(
+ s->context,
+ s->direction == PA_STREAM_RECORD ? PA_COMMAND_SET_RECORD_STREAM_NAME : PA_COMMAND_SET_PLAYBACK_STREAM_NAME,
+ &tag);
+ pa_tagstruct_putu32(t, s->channel);
+ pa_tagstruct_puts(t, name);
+ pa_pstream_send_tagstruct(s->context->pstream, t);
+ pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+ }
return o;
}
int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) {
- pa_usec_t usec = 0;
+ pa_usec_t usec;
- assert(s);
- assert(s->ref >= 1);
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
@@ -1211,65 +1851,10 @@ int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) {
PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_PLAYBACK || !s->timing_info.read_index_corrupt, PA_ERR_NODATA);
PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_RECORD || !s->timing_info.write_index_corrupt, PA_ERR_NODATA);
- if (s->cached_time_valid)
- /* We alredy calculated the time value for this timing info, so let's reuse it */
- usec = s->cached_time;
- else {
- if (s->direction == PA_STREAM_PLAYBACK) {
- /* The last byte that was written into the output device
- * had this time value associated */
- usec = pa_bytes_to_usec(s->timing_info.read_index < 0 ? 0 : (uint64_t) s->timing_info.read_index, &s->sample_spec);
-
- if (!s->corked) {
- /* Because the latency info took a little time to come
- * to us, we assume that the real output time is actually
- * a little ahead */
- usec += s->timing_info.transport_usec;
-
- /* However, the output device usually maintains a buffer
- too, hence the real sample currently played is a little
- back */
- if (s->timing_info.sink_usec >= usec)
- usec = 0;
- else
- usec -= s->timing_info.sink_usec;
- }
-
- } else if (s->direction == PA_STREAM_RECORD) {
- /* The last byte written into the server side queue had
- * this time value associated */
- usec = pa_bytes_to_usec(s->timing_info.write_index < 0 ? 0 : (uint64_t) s->timing_info.write_index, &s->sample_spec);
-
- if (!s->corked) {
- /* Add transport latency */
- usec += s->timing_info.transport_usec;
-
- /* Add latency of data in device buffer */
- usec += s->timing_info.source_usec;
-
- /* If this is a monitor source, we need to correct the
- * time by the playback device buffer */
- if (s->timing_info.sink_usec >= usec)
- usec = 0;
- else
- usec -= s->timing_info.sink_usec;
- }
- }
-
- s->cached_time = usec;
- s->cached_time_valid = 1;
- }
-
- /* Interpolate if requested */
- if (s->flags & PA_STREAM_INTERPOLATE_TIMING) {
-
- /* We just add the time that passed since the latency info was
- * current */
- if (!s->corked && s->timing_info.playing) {
- struct timeval now;
- usec += pa_timeval_diff(pa_gettimeofday(&now), &s->timing_info.timestamp);
- }
- }
+ if (s->smoother)
+ usec = pa_smoother_get(s->smoother, pa_rtclock_usec());
+ else
+ usec = calc_time(s, FALSE);
/* Make sure the time runs monotonically */
if (!(s->flags & PA_STREAM_NOT_MONOTONOUS)) {
@@ -1281,18 +1866,18 @@ int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) {
if (r_usec)
*r_usec = usec;
-
+
return 0;
}
static pa_usec_t time_counter_diff(pa_stream *s, pa_usec_t a, pa_usec_t b, int *negative) {
- assert(s);
- assert(s->ref >= 1);
-
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
if (negative)
*negative = 0;
- if (a >= b)
+ if (a >= b)
return a-b;
else {
if (negative && s->direction == PA_STREAM_RECORD) {
@@ -1308,16 +1893,16 @@ int pa_stream_get_latency(pa_stream *s, pa_usec_t *r_usec, int *negative) {
int r;
int64_t cindex;
- assert(s);
- assert(s->ref >= 1);
- assert(r_usec);
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+ pa_assert(r_usec);
PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY(s->context, s->timing_info_valid, PA_ERR_NODATA);
PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_PLAYBACK || !s->timing_info.write_index_corrupt, PA_ERR_NODATA);
PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_RECORD || !s->timing_info.read_index_corrupt, PA_ERR_NODATA);
-
+
if ((r = pa_stream_get_time(s, &t)) < 0)
return r;
@@ -1328,7 +1913,7 @@ int pa_stream_get_latency(pa_stream *s, pa_usec_t *r_usec, int *negative) {
if (cindex < 0)
cindex = 0;
-
+
c = pa_bytes_to_usec(cindex, &s->sample_spec);
if (s->direction == PA_STREAM_PLAYBACK)
@@ -1340,8 +1925,8 @@ int pa_stream_get_latency(pa_stream *s, pa_usec_t *r_usec, int *negative) {
}
const pa_timing_info* pa_stream_get_timing_info(pa_stream *s) {
- assert(s);
- assert(s->ref >= 1);
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
@@ -1351,27 +1936,323 @@ const pa_timing_info* pa_stream_get_timing_info(pa_stream *s) {
}
const pa_sample_spec* pa_stream_get_sample_spec(pa_stream *s) {
- assert(s);
- assert(s->ref >= 1);
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
return &s->sample_spec;
}
const pa_channel_map* pa_stream_get_channel_map(pa_stream *s) {
- assert(s);
- assert(s->ref >= 1);
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
return &s->channel_map;
}
const pa_buffer_attr* pa_stream_get_buffer_attr(pa_stream *s) {
- assert(s);
- assert(s->ref >= 1);
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
- PA_CHECK_VALIDITY_RETURN_NULL(s->context,
- pa_context_get_server_protocol_version(s->context) >= 9, PA_ERR_NODATA);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->context->version >= 9, PA_ERR_NOTSUPPORTED);
return &s->buffer_attr;
}
+
+static void stream_set_buffer_attr_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_operation *o = userdata;
+ int success = 1;
+
+ pa_assert(pd);
+ pa_assert(o);
+ pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+ if (!o->context)
+ goto finish;
+
+ if (command != PA_COMMAND_REPLY) {
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
+ goto finish;
+
+ success = 0;
+ } else {
+
+ if (o->stream->direction == PA_STREAM_PLAYBACK) {
+ if (pa_tagstruct_getu32(t, &o->stream->buffer_attr.maxlength) < 0 ||
+ pa_tagstruct_getu32(t, &o->stream->buffer_attr.tlength) < 0 ||
+ pa_tagstruct_getu32(t, &o->stream->buffer_attr.prebuf) < 0 ||
+ pa_tagstruct_getu32(t, &o->stream->buffer_attr.minreq) < 0) {
+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+ } else if (o->stream->direction == PA_STREAM_RECORD) {
+ if (pa_tagstruct_getu32(t, &o->stream->buffer_attr.maxlength) < 0 ||
+ pa_tagstruct_getu32(t, &o->stream->buffer_attr.fragsize) < 0) {
+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+ }
+
+ if (!pa_tagstruct_eof(t)) {
+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+ }
+
+ if (o->callback) {
+ pa_stream_success_cb_t cb = (pa_stream_success_cb_t) o->callback;
+ cb(o->stream, success, o->userdata);
+ }
+
+finish:
+ pa_operation_done(o);
+ pa_operation_unref(o);
+}
+
+
+pa_operation* pa_stream_set_buffer_attr(pa_stream *s, const pa_buffer_attr *attr, pa_stream_success_cb_t cb, void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+ pa_assert(attr);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->context->version >= 12, PA_ERR_NOTSUPPORTED);
+
+ o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(
+ s->context,
+ s->direction == PA_STREAM_RECORD ? PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR : PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR,
+ &tag);
+ pa_tagstruct_putu32(t, s->channel);
+
+ pa_tagstruct_putu32(t, attr->maxlength);
+
+ if (s->direction == PA_STREAM_PLAYBACK)
+ pa_tagstruct_put(
+ t,
+ PA_TAG_U32, attr->tlength,
+ PA_TAG_U32, attr->prebuf,
+ PA_TAG_U32, attr->minreq,
+ PA_TAG_INVALID);
+ else
+ pa_tagstruct_putu32(t, attr->fragsize);
+
+ if (s->context->version >= 13)
+ pa_tagstruct_put_boolean(t, !!(s->flags & PA_STREAM_ADJUST_LATENCY));
+
+ pa_pstream_send_tagstruct(s->context->pstream, t);
+ pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, stream_set_buffer_attr_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+uint32_t pa_stream_get_device_index(pa_stream *s) {
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE, PA_INVALID_INDEX);
+ PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE, PA_INVALID_INDEX);
+ PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->context->version >= 12, PA_ERR_NOTSUPPORTED, PA_INVALID_INDEX);
+ PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->device_index != PA_INVALID_INDEX, PA_ERR_BADSTATE, PA_INVALID_INDEX);
+
+ return s->device_index;
+}
+
+const char *pa_stream_get_device_name(pa_stream *s) {
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->context->version >= 12, PA_ERR_NOTSUPPORTED);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->device_name, PA_ERR_BADSTATE);
+
+ return s->device_name;
+}
+
+int pa_stream_is_suspended(pa_stream *s) {
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+ PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY(s->context, s->context->version >= 12, PA_ERR_NOTSUPPORTED);
+
+ return s->suspended;
+}
+
+int pa_stream_is_corked(pa_stream *s) {
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+ PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+
+ return s->corked;
+}
+
+static void stream_update_sample_rate_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_operation *o = userdata;
+ int success = 1;
+
+ pa_assert(pd);
+ pa_assert(o);
+ pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+ if (!o->context)
+ goto finish;
+
+ if (command != PA_COMMAND_REPLY) {
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
+ goto finish;
+
+ success = 0;
+ } else {
+
+ if (!pa_tagstruct_eof(t)) {
+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+ }
+
+ o->stream->sample_spec.rate = PA_PTR_TO_UINT(o->private);
+ pa_assert(pa_sample_spec_valid(&o->stream->sample_spec));
+
+ if (o->callback) {
+ pa_stream_success_cb_t cb = (pa_stream_success_cb_t) o->callback;
+ cb(o->stream, success, o->userdata);
+ }
+
+finish:
+ pa_operation_done(o);
+ pa_operation_unref(o);
+}
+
+
+pa_operation *pa_stream_update_sample_rate(pa_stream *s, uint32_t rate, pa_stream_success_cb_t cb, void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, rate > 0 && rate <= PA_RATE_MAX, PA_ERR_INVALID);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->flags & PA_STREAM_VARIABLE_RATE, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->context->version >= 12, PA_ERR_NOTSUPPORTED);
+
+ o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
+ o->private = PA_UINT_TO_PTR(rate);
+
+ t = pa_tagstruct_command(
+ s->context,
+ s->direction == PA_STREAM_RECORD ? PA_COMMAND_UPDATE_RECORD_STREAM_SAMPLE_RATE : PA_COMMAND_UPDATE_PLAYBACK_STREAM_SAMPLE_RATE,
+ &tag);
+ pa_tagstruct_putu32(t, s->channel);
+ pa_tagstruct_putu32(t, rate);
+
+ pa_pstream_send_tagstruct(s->context->pstream, t);
+ pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, stream_update_sample_rate_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+pa_operation *pa_stream_proplist_update(pa_stream *s, pa_update_mode_t mode, pa_proplist *p, pa_stream_success_cb_t cb, void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, mode == PA_UPDATE_SET || mode == PA_UPDATE_MERGE || mode == PA_UPDATE_REPLACE, PA_ERR_INVALID);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->context->version >= 13, PA_ERR_NOTSUPPORTED);
+
+ o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(
+ s->context,
+ s->direction == PA_STREAM_RECORD ? PA_COMMAND_UPDATE_RECORD_STREAM_PROPLIST : PA_COMMAND_UPDATE_PLAYBACK_STREAM_PROPLIST,
+ &tag);
+ pa_tagstruct_putu32(t, s->channel);
+ pa_tagstruct_putu32(t, (uint32_t) mode);
+ pa_tagstruct_put_proplist(t, p);
+
+ pa_pstream_send_tagstruct(s->context->pstream, t);
+ pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ /* Please note that we don't update s->proplist here, because we
+ * don't export that field */
+
+ return o;
+}
+
+pa_operation *pa_stream_proplist_remove(pa_stream *s, const char *const keys[], pa_stream_success_cb_t cb, void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+ const char * const*k;
+
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, keys && keys[0], PA_ERR_INVALID);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->context->version >= 13, PA_ERR_NOTSUPPORTED);
+
+ o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(
+ s->context,
+ s->direction == PA_STREAM_RECORD ? PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST : PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST,
+ &tag);
+ pa_tagstruct_putu32(t, s->channel);
+
+ for (k = keys; *k; k++)
+ pa_tagstruct_puts(t, *k);
+
+ pa_tagstruct_puts(t, NULL);
+
+ pa_pstream_send_tagstruct(s->context->pstream, t);
+ pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ /* Please note that we don't update s->proplist here, because we
+ * don't export that field */
+
+ return o;
+}
+
+int pa_stream_set_monitor_stream(pa_stream *s, uint32_t sink_input_idx) {
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+ PA_CHECK_VALIDITY(s->context, sink_input_idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+ PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_UNCONNECTED, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY(s->context, s->context->version >= 13, PA_ERR_NOTSUPPORTED);
+
+ s->direct_on_input = sink_input_idx;
+
+ return 0;
+}
+
+uint32_t pa_stream_get_monitor_stream(pa_stream *s) {
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direct_on_input != PA_INVALID_INDEX, PA_ERR_BADSTATE, PA_INVALID_INDEX);
+ PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->context->version >= 13, PA_ERR_NOTSUPPORTED, PA_INVALID_INDEX);
+
+ return s->direct_on_input;
+}
diff --git a/src/pulse/stream.h b/src/pulse/stream.h
index ad15125a..55f36b7f 100644
--- a/src/pulse/stream.h
+++ b/src/pulse/stream.h
@@ -1,21 +1,22 @@
#ifndef foostreamhfoo
#define foostreamhfoo
-/* $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
@@ -113,7 +114,7 @@
* read index may be larger than the write index, in which case
* silence is played. If the application writes data to indexes lower
* than the read index, the data is immediately lost.
- *
+ *
* \section transfer_sec Transferring Data
*
* Once the stream is up, data can start flowing between the client and the
@@ -159,13 +160,13 @@
* \li PA_SEEK_ABSOLUTE - seek relative to the beginning of the playback buffer, (i.e. the first that was ever played in the stream)
* \li PA_SEEK_RELATIVE_ON_READ - seek relative to the current read index. Use this to write data to the output buffer that should be played as soon as possible
* \li PA_SEEK_RELATIVE_END - seek relative to the last byte ever written.
- *
+ *
* If an application just wants to append some data to the output
* buffer, PA_SEEK_RELATIVE and an offset of 0 should be used.
*
* After a call to pa_stream_write() the write index will be left at
* the position right after the last byte of the written data.
- *
+ *
* \section latency_sec Latency
*
* A major problem with networked audio is the increased latency caused by
@@ -217,7 +218,7 @@
* this option with PA_STREAM_AUTO_TIMING_UPDATE, which will enable
* you to monitor the current playback time/latency very precisely and
* very frequently without requiring a network round trip every time.
- *
+ *
* \section flow_sec Overflow and underflow
*
* Even with the best precautions, buffers will sometime over - or
@@ -268,18 +269,30 @@ typedef struct pa_stream pa_stream;
typedef void (*pa_stream_success_cb_t) (pa_stream*s, int success, void *userdata);
/** A generic request callback */
-typedef void (*pa_stream_request_cb_t)(pa_stream *p, size_t length, void *userdata);
+typedef void (*pa_stream_request_cb_t)(pa_stream *p, size_t bytes, void *userdata);
/** A generic notification callback */
typedef void (*pa_stream_notify_cb_t)(pa_stream *p, void *userdata);
-/** Create a new, unconnected stream with the specified name and sample type */
+/** Create a new, unconnected stream with the specified name and
+ * sample type. It is recommended to use pa_stream_new_with_proplist()
+ * instead and specify some initial properties. */
pa_stream* pa_stream_new(
- pa_context *c /**< The context to create this stream in */,
+ pa_context *c /**< The context to create this stream in */,
const char *name /**< A name for this stream */,
const pa_sample_spec *ss /**< The desired sample format */,
const pa_channel_map *map /**< The desired channel map, or NULL for default */);
+/** Create a new, unconnected stream with the specified name and
+ * sample type, and specify the the initial stream property
+ * list. \since 0.9.11 */
+pa_stream* pa_stream_new_with_proplist(
+ pa_context *c /**< The context to create this stream in */,
+ const char *name /**< A name for this stream */,
+ const pa_sample_spec *ss /**< The desired sample format */,
+ const pa_channel_map *map /**< The desired channel map, or NULL for default */,
+ pa_proplist *p /**< The initial property list */);
+
/** Decrease the reference counter by one */
void pa_stream_unref(pa_stream *s);
@@ -292,9 +305,42 @@ pa_stream_state_t pa_stream_get_state(pa_stream *p);
/** Return the context this stream is attached to */
pa_context* pa_stream_get_context(pa_stream *p);
-/** Return the device (sink input or source output) index this stream is connected to */
+/** Return the sink input resp. source output index this stream is
+ * identified in the server with. This is useful for usage with the
+ * introspection functions, such as pa_context_get_sink_input_info()
+ * resp. pa_context_get_source_output_info(). */
uint32_t pa_stream_get_index(pa_stream *s);
+/** Return the index of the sink or source this stream is connected to
+ * in the server. This is useful for usage with the introspection
+ * functions, such as pa_context_get_sink_info_by_index()
+ * resp. pa_context_get_source_info_by_index(). Please note that
+ * streams may be moved between sinks/sources and thus it is
+ * recommended to use pa_stream_set_moved_callback() to be notified
+ * about this. This function will return with PA_ERR_NOTSUPPORTED when the
+ * server is older than 0.9.8. \since 0.9.8 */
+uint32_t pa_stream_get_device_index(pa_stream *s);
+
+/** Return the name of the sink or source this stream is connected to
+ * in the server. This is useful for usage with the introspection
+ * functions, such as pa_context_get_sink_info_by_name()
+ * resp. pa_context_get_source_info_by_name(). Please note that
+ * streams may be moved between sinks/sources and thus it is
+ * recommended to use pa_stream_set_moved_callback() to be notified
+ * about this. This function will return with PA_ERR_NOTSUPPORTED when the
+ * server is older than 0.9.8. \since 0.9.8 */
+const char *pa_stream_get_device_name(pa_stream *s);
+
+/** Return 1 if the sink or source this stream is connected to has
+ * been suspended. This will return 0 if not, and negative on
+ * error. This function will return with PA_ERR_NOTSUPPORTED when the
+ * server is older than 0.9.8. \since 0.9.8 */
+int pa_stream_is_suspended(pa_stream *s);
+
+/** Return 1 if the this stream has been corked. This will return 0 if
+ * not, and negative on error. \since 0.9.11 */
+int pa_stream_is_corked(pa_stream *s);
+
/** Connect the stream to a sink */
int pa_stream_connect_playback(
pa_stream *s /**< The stream to connect to a sink */,
@@ -320,11 +366,11 @@ int pa_stream_disconnect(pa_stream *s);
* is not copied. If NULL, the data is copied into an internal
* buffer. The client my freely seek around in the output buffer. For
* most applications passing 0 and PA_SEEK_RELATIVE as arguments for
- * offset and seek should be useful.*/
+ * offset and seek should be useful.*/
int pa_stream_write(
pa_stream *p /**< The stream to use */,
const void *data /**< The data to write */,
- size_t length /**< The length of the data to write */,
+ size_t nbytes /**< The length of the data to write in bytes*/,
pa_free_cb_t free_cb /**< A cleanup routine for the data or NULL to request an internal copy */,
int64_t offset, /**< Offset for seeking, must be 0 for upload streams */
pa_seek_mode_t seek /**< Seek mode, must be PA_SEEK_RELATIVE for upload streams */);
@@ -333,20 +379,20 @@ int pa_stream_write(
* data will point to the actual data and length will contain the size
* of the data in bytes (which can be less than a complete framgnet).
* Use pa_stream_drop() to actually remove the data from the
- * buffer. If no data is available will return a NULL pointer \since 0.8 */
+ * buffer. If no data is available will return a NULL pointer */
int pa_stream_peek(
pa_stream *p /**< The stream to use */,
const void **data /**< Pointer to pointer that will point to data */,
- size_t *length /**< The length of the data read */);
+ size_t *nbytes /**< The length of the data read in bytes */);
/** Remove the current fragment on record streams. It is invalid to do this without first
- * calling pa_stream_peek(). \since 0.8 */
+ * calling pa_stream_peek(). */
int pa_stream_drop(pa_stream *p);
-/** Return the nember of bytes that may be written using pa_stream_write() */
+/** Return the number of bytes that may be written using pa_stream_write() */
size_t pa_stream_writable_size(pa_stream *p);
-/** Return the number of bytes that may be read using pa_stream_read() \since 0.8 */
+/** Return the number of bytes that may be read using pa_stream_read()*/
size_t pa_stream_readable_size(pa_stream *p);
/** Drain a playback stream. Use this for notification when the buffer is empty */
@@ -366,36 +412,63 @@ void pa_stream_set_state_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *
void pa_stream_set_write_callback(pa_stream *p, pa_stream_request_cb_t cb, void *userdata);
/** Set the callback function that is called when new data is available from the stream.
- * Return the number of bytes read. \since 0.8 */
+ * Return the number of bytes read.*/
void pa_stream_set_read_callback(pa_stream *p, pa_stream_request_cb_t cb, void *userdata);
-/** Set the callback function that is called when a buffer overflow happens. (Only for playback streams) \since 0.8 */
+/** Set the callback function that is called when a buffer overflow happens. (Only for playback streams) */
void pa_stream_set_overflow_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);
-/** Set the callback function that is called when a buffer underflow happens. (Only for playback streams) \since 0.8 */
+/** Set the callback function that is called when a buffer underflow happens. (Only for playback streams) */
void pa_stream_set_underflow_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);
-/** Set the callback function that is called whenever a latency information update happens. Useful on PA_STREAM_AUTO_TIMING_UPDATE streams only. (Only for playback streams) \since 0.8.2 */
+/** Set the callback function that is called when a the server starts
+ * playback after an underrun or on initial startup. This only informs
+ * that audio is flowing again, it is no indication that audio startet
+ * to reach the speakers already. (Only for playback streams). \since
+ * 0.9.11 */
+void pa_stream_set_started_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);
+
+/** Set the callback function that is called whenever a latency
+ * information update happens. Useful on PA_STREAM_AUTO_TIMING_UPDATE
+ * streams only. (Only for playback streams) */
void pa_stream_set_latency_update_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);
-/** Pause (or resume) playback of this stream temporarily. Available on both playback and recording streams. \since 0.3 */
+/** Set the callback function that is called whenever the stream is
+ * moved to a different sink/source. Use pa_stream_get_device_name()or
+ * pa_stream_get_device_index() to query the new sink/source. This
+ * notification is only generated when the server is at least
+ * 0.9.8. \since 0.9.8 */
+void pa_stream_set_moved_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);
+
+/** Set the callback function that is called whenever the sink/source
+ * this stream is connected to is suspended or resumed. Use
+ * pa_stream_is_suspended() to query the new suspend status. Please
+ * note that the suspend status might also change when the stream is
+ * moved between devices. Thus if you call this function you very
+ * likely want to call pa_stream_set_moved_callback, too. This
+ * notification is only generated when the server is at least
+ * 0.9.8. \since 0.9.8 */
+void pa_stream_set_suspended_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);
+
+/** Pause (or resume) playback of this stream temporarily. Available on both playback and recording streams. */
pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, void *userdata);
/** Flush the playback buffer of this stream. Most of the time you're
- * better off using the parameter delta of pa_stream_write() instead of this
- * function. Available on both playback and recording streams. \since 0.3 */
+ * better off using the parameter delta of pa_stream_write() instead
+ * of this function. Available on both playback and recording
+ * streams. */
pa_operation* pa_stream_flush(pa_stream *s, pa_stream_success_cb_t cb, void *userdata);
/** Reenable prebuffering as specified in the pa_buffer_attr
- * structure. Available for playback streams only. \since 0.6 */
+ * structure. Available for playback streams only. */
pa_operation* pa_stream_prebuf(pa_stream *s, pa_stream_success_cb_t cb, void *userdata);
/** Request immediate start of playback on this stream. This disables
- * prebuffering as specified in the pa_buffer_attr
- * structure, temporarily. Available for playback streams only. \since 0.3 */
+ * prebuffering as specified in the pa_buffer_attr structure,
+ * temporarily. Available for playback streams only. */
pa_operation* pa_stream_trigger(pa_stream *s, pa_stream_success_cb_t cb, void *userdata);
-/** Rename the stream. \since 0.5 */
+/** Rename the stream. */
pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_success_cb_t cb, void *userdata);
/** Return the current playback/recording time. This is based on the
@@ -412,13 +485,13 @@ pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_succe
* be disabled by using PA_STREAM_NOT_MONOTONOUS. This may be
* desirable to deal better with bad estimations of transport
* latencies, but may have strange effects if the application is not
- * able to deal with time going 'backwards'. \since 0.6 */
+ * able to deal with time going 'backwards'. */
int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec);
/** Return the total stream latency. This function is based on
* pa_stream_get_time(). In case the stream is a monitoring stream the
* result can be negative, i.e. the captured samples are not yet
- * played. In this case *negative is set to 1. \since 0.6 */
+ * played. In this case *negative is set to 1. */
int pa_stream_get_latency(pa_stream *s, pa_usec_t *r_usec, int *negative);
/** Return the latest raw timing data structure. The returned pointer
@@ -430,13 +503,13 @@ int pa_stream_get_latency(pa_stream *s, pa_usec_t *r_usec, int *negative);
* function will fail with PA_ERR_NODATA. Please note that the
* write_index member field (and only this field) is updated on each
* pa_stream_write() call, not just when a timing update has been
- * recieved. \since 0.8 */
+ * recieved. */
const pa_timing_info* pa_stream_get_timing_info(pa_stream *s);
-/** Return a pointer to the stream's sample specification. \since 0.6 */
+/** Return a pointer to the stream's sample specification. */
const pa_sample_spec* pa_stream_get_sample_spec(pa_stream *s);
-/** Return a pointer to the stream's channel map. \since 0.8 */
+/** Return a pointer to the stream's channel map. */
const pa_channel_map* pa_stream_get_channel_map(pa_stream *s);
/** Return the buffer metrics of the stream. Only valid after the
@@ -444,6 +517,43 @@ const pa_channel_map* pa_stream_get_channel_map(pa_stream *s);
* PulseAudio 0.9. \since 0.9.0 */
const pa_buffer_attr* pa_stream_get_buffer_attr(pa_stream *s);
+/** Change the buffer metrics of the stream during playback. The
+ * server might have chosen different buffer metrics then
+ * requested. The selected metrics may be queried with
+ * pa_stream_get_buffer_attr() as soon as the callback is called. Only
+ * valid after the stream has been connected successfully and if the
+ * server is at least PulseAudio 0.9.8. \since 0.9.8 */
+pa_operation *pa_stream_set_buffer_attr(pa_stream *s, const pa_buffer_attr *attr, pa_stream_success_cb_t cb, void *userdata);
+
+/** Change the stream sampling rate during playback. You need to pass
+ * PA_STREAM_VARIABLE_RATE in the flags parameter of
+ * pa_stream_connect() if you plan to use this function. Only valid
+ * after the stream has been connected successfully and if the server
+ * is at least PulseAudio 0.9.8. \since 0.9.8 */
+pa_operation *pa_stream_update_sample_rate(pa_stream *s, uint32_t rate, pa_stream_success_cb_t cb, void *userdata);
+
+/** Update the property list of the sink input/source output of this
+ * stream, adding new entries. Please note that it is highly
+ * recommended to set as much properties initially via
+ * pa_stream_new_with_proplist() as possible instead a posteriori with
+ * this function, since that information may then be used to route
+ * this stream to the right device. \since 0.9.11 */
+pa_operation *pa_stream_proplist_update(pa_stream *s, pa_update_mode_t mode, pa_proplist *p, pa_stream_success_cb_t cb, void *userdata);
+
+/** Update the property list of the sink input/source output of this
+ * stream, remove entries. \since 0.9.11 */
+pa_operation *pa_stream_proplist_remove(pa_stream *s, const char *const keys[], pa_stream_success_cb_t cb, void *userdata);
+
+/** For record streams connected to a monitor source: monitor only a
+ * very specific sink input of the sink. Thus function needs to be
+ * called before pa_stream_connect_record() is called. \since
+ * 0.9.11 */
+int pa_stream_set_monitor_stream(pa_stream *s, uint32_t sink_input_idx);
+
+/** Return what has been set with pa_stream_set_monitor_stream()
+ * ebfore. \since 0.9.11 */
+uint32_t pa_stream_get_monitor_stream(pa_stream *s);
+
PA_C_DECL_END
#endif
diff --git a/src/pulse/subscribe.c b/src/pulse/subscribe.c
index c1d88912..d9c06b7e 100644
--- a/src/pulse/subscribe.c
+++ b/src/pulse/subscribe.c
@@ -1,18 +1,18 @@
-/* $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
@@ -23,10 +23,11 @@
#include <config.h>
#endif
-#include <assert.h>
#include <stdio.h>
-#include <pulsecore/gccmacro.h>
+#include <pulse/gccmacro.h>
+
+#include <pulsecore/macro.h>
#include <pulsecore/pstream-util.h>
#include "internal.h"
@@ -38,10 +39,11 @@ void pa_command_subscribe_event(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSE
pa_subscription_event_type_t e;
uint32_t idx;
- assert(pd);
- assert(command == PA_COMMAND_SUBSCRIBE_EVENT);
- assert(t);
- assert(c);
+ pa_assert(pd);
+ pa_assert(command == PA_COMMAND_SUBSCRIBE_EVENT);
+ pa_assert(t);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
pa_context_ref(c);
@@ -65,11 +67,11 @@ pa_operation* pa_context_subscribe(pa_context *c, pa_subscription_mask_t m, pa_c
pa_tagstruct *t;
uint32_t tag;
- assert(c);
- assert(c->ref >= 1);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
-
+
o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
t = pa_tagstruct_command(c, PA_COMMAND_SUBSCRIBE, &tag);
@@ -81,9 +83,12 @@ pa_operation* pa_context_subscribe(pa_context *c, pa_subscription_mask_t m, pa_c
}
void pa_context_set_subscribe_callback(pa_context *c, pa_context_subscribe_cb_t cb, void *userdata) {
- assert(c);
- assert(c->ref >= 1);
-
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ if (c->state == PA_CONTEXT_TERMINATED || c->state == PA_CONTEXT_FAILED)
+ return;
+
c->subscribe_callback = cb;
c->subscribe_userdata = userdata;
}
diff --git a/src/pulse/subscribe.h b/src/pulse/subscribe.h
index adbea680..0e4be8c3 100644
--- a/src/pulse/subscribe.h
+++ b/src/pulse/subscribe.h
@@ -1,21 +1,22 @@
#ifndef foosubscribehfoo
#define foosubscribehfoo
-/* $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
diff --git a/src/pulse/thread-mainloop.c b/src/pulse/thread-mainloop.c
index 7e1ccfaa..6b66696c 100644
--- a/src/pulse/thread-mainloop.c
+++ b/src/pulse/thread-mainloop.c
@@ -1,18 +1,19 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
+ Copyright 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
@@ -23,24 +24,24 @@
#include <config.h>
#endif
-#include <assert.h>
#include <signal.h>
#include <stdio.h>
-#ifdef HAVE_SYS_POLL_H
-#include <sys/poll.h>
+#ifdef HAVE_POLL_H
+#include <poll.h>
#else
-#include "../pulsecore/poll.h"
+#include <pulsecore/poll.h>
#endif
#include <pulse/xmalloc.h>
+#include <pulse/mainloop.h>
#include <pulsecore/log.h>
#include <pulsecore/hashmap.h>
#include <pulsecore/thread.h>
#include <pulsecore/mutex.h>
+#include <pulsecore/macro.h>
-#include "mainloop.h"
#include "thread-mainloop.h"
struct pa_threaded_mainloop {
@@ -60,7 +61,7 @@ static int poll_func(struct pollfd *ufds, unsigned long nfds, int timeout, void
pa_mutex *mutex = userdata;
int r;
- assert(mutex);
+ pa_assert(mutex);
/* Before entering poll() we unlock the mutex, so that
* avahi_simple_poll_quit() can succeed from another thread. */
@@ -100,7 +101,7 @@ pa_threaded_mainloop *pa_threaded_mainloop_new(void) {
return NULL;
}
- m->mutex = pa_mutex_new(1);
+ m->mutex = pa_mutex_new(TRUE, TRUE);
m->cond = pa_cond_new();
m->accept_cond = pa_cond_new();
m->thread = NULL;
@@ -113,10 +114,10 @@ pa_threaded_mainloop *pa_threaded_mainloop_new(void) {
}
void pa_threaded_mainloop_free(pa_threaded_mainloop* m) {
- assert(m);
+ pa_assert(m);
/* Make sure that this function is not called from the helper thread */
- assert((m->thread && !pa_thread_is_running(m->thread)) || !in_worker(m));
+ pa_assert((m->thread && !pa_thread_is_running(m->thread)) || !in_worker(m));
pa_threaded_mainloop_stop(m);
@@ -128,14 +129,14 @@ void pa_threaded_mainloop_free(pa_threaded_mainloop* m) {
pa_mutex_free(m->mutex);
pa_cond_free(m->cond);
pa_cond_free(m->accept_cond);
-
+
pa_xfree(m);
}
int pa_threaded_mainloop_start(pa_threaded_mainloop *m) {
- assert(m);
+ pa_assert(m);
- assert(!m->thread || !pa_thread_is_running(m->thread));
+ pa_assert(!m->thread || !pa_thread_is_running(m->thread));
if (!(m->thread = pa_thread_new(thread, m)))
return -1;
@@ -144,13 +145,13 @@ int pa_threaded_mainloop_start(pa_threaded_mainloop *m) {
}
void pa_threaded_mainloop_stop(pa_threaded_mainloop *m) {
- assert(m);
+ pa_assert(m);
if (!m->thread || !pa_thread_is_running(m->thread))
return;
/* Make sure that this function is not called from the helper thread */
- assert(!in_worker(m));
+ pa_assert(!in_worker(m));
pa_mutex_lock(m->mutex);
pa_mainloop_quit(m->real_mainloop, 0);
@@ -160,25 +161,25 @@ void pa_threaded_mainloop_stop(pa_threaded_mainloop *m) {
}
void pa_threaded_mainloop_lock(pa_threaded_mainloop *m) {
- assert(m);
-
+ pa_assert(m);
+
/* Make sure that this function is not called from the helper thread */
- assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m));
+ pa_assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m));
pa_mutex_lock(m->mutex);
}
void pa_threaded_mainloop_unlock(pa_threaded_mainloop *m) {
- assert(m);
-
+ pa_assert(m);
+
/* Make sure that this function is not called from the helper thread */
- assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m));
+ pa_assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m));
pa_mutex_unlock(m->mutex);
}
void pa_threaded_mainloop_signal(pa_threaded_mainloop *m, int wait_for_accept) {
- assert(m);
+ pa_assert(m);
pa_cond_signal(m->cond, 1);
@@ -187,36 +188,42 @@ void pa_threaded_mainloop_signal(pa_threaded_mainloop *m, int wait_for_accept) {
}
void pa_threaded_mainloop_wait(pa_threaded_mainloop *m) {
- assert(m);
-
+ pa_assert(m);
+
/* Make sure that this function is not called from the helper thread */
- assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m));
+ pa_assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m));
m->n_waiting ++;
pa_cond_wait(m->cond, m->mutex);
- assert(m->n_waiting > 0);
+ pa_assert(m->n_waiting > 0);
m->n_waiting --;
}
void pa_threaded_mainloop_accept(pa_threaded_mainloop *m) {
- assert(m);
-
+ pa_assert(m);
+
/* Make sure that this function is not called from the helper thread */
- assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m));
+ pa_assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m));
pa_cond_signal(m->accept_cond, 0);
}
int pa_threaded_mainloop_get_retval(pa_threaded_mainloop *m) {
- assert(m);
+ pa_assert(m);
return pa_mainloop_get_retval(m->real_mainloop);
}
pa_mainloop_api* pa_threaded_mainloop_get_api(pa_threaded_mainloop*m) {
- assert(m);
+ pa_assert(m);
return pa_mainloop_get_api(m->real_mainloop);
}
+
+int pa_threaded_mainloop_in_thread(pa_threaded_mainloop *m) {
+ pa_assert(m);
+
+ return m->thread && pa_thread_self() == m->thread;
+}
diff --git a/src/pulse/thread-mainloop.h b/src/pulse/thread-mainloop.h
index 44eff5a3..521e29b0 100644
--- a/src/pulse/thread-mainloop.h
+++ b/src/pulse/thread-mainloop.h
@@ -1,21 +1,22 @@
#ifndef foothreadmainloophfoo
#define foothreadmainloophfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
+ Copyright 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
@@ -149,7 +150,7 @@ PA_C_DECL_BEGIN
* deal with that.
*
* The functions will not dead lock because the wait function will release
- * the lock before waiting and then regrab it once it has been signaled.
+ * the lock before waiting and then regrab it once it has been signaled.
* For those of you familiar with threads, the behaviour is that of a
* condition variable.
*
@@ -229,7 +230,7 @@ PA_C_DECL_BEGIN
*/
/** \file
- *
+ *
* A thread based event loop implementation based on pa_mainloop. The
* event loop is run in a helper thread in the background. A few
* synchronization primitives are available to access the objects
@@ -294,6 +295,9 @@ int pa_threaded_mainloop_get_retval(pa_threaded_mainloop *m);
/** Return the abstract main loop abstraction layer vtable for this main loop. */
pa_mainloop_api* pa_threaded_mainloop_get_api(pa_threaded_mainloop*m);
+/** Returns non-zero when called from withing the event loop thread. \since 0.9.7 */
+int pa_threaded_mainloop_in_thread(pa_threaded_mainloop *m);
+
PA_C_DECL_END
#endif
diff --git a/src/pulse/timeval.c b/src/pulse/timeval.c
index 11285230..9708a735 100644
--- a/src/pulse/timeval.c
+++ b/src/pulse/timeval.c
@@ -1,18 +1,19 @@
-/* $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.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
@@ -23,7 +24,6 @@
#include <config.h>
#endif
-#include <assert.h>
#include <stddef.h>
#include <sys/time.h>
@@ -31,15 +31,17 @@
#include <windows.h>
#endif
-#include "../pulsecore/winsock.h"
+#include <pulsecore/winsock.h>
+#include <pulsecore/macro.h>
#include "timeval.h"
struct timeval *pa_gettimeofday(struct timeval *tv) {
#ifdef HAVE_GETTIMEOFDAY
- assert(tv);
-
- return gettimeofday(tv, NULL) < 0 ? NULL : tv;
+ pa_assert(tv);
+
+ pa_assert_se(gettimeofday(tv, NULL) == 0);
+ return tv;
#elif defined(OS_IS_WIN32)
/*
* Copied from implementation by Steven Edwards (LGPL).
@@ -56,7 +58,7 @@ struct timeval *pa_gettimeofday(struct timeval *tv) {
LARGE_INTEGER li;
__int64 t;
- assert(tv);
+ pa_assert(tv);
GetSystemTimeAsFileTime(&ft);
li.LowPart = ft.dwLowDateTime;
@@ -64,8 +66,8 @@ struct timeval *pa_gettimeofday(struct timeval *tv) {
t = li.QuadPart; /* In 100-nanosecond intervals */
t -= EPOCHFILETIME; /* Offset to the Epoch time */
t /= 10; /* In microseconds */
- tv->tv_sec = (long)(t / 1000000);
- tv->tv_usec = (long)(t % 1000000);
+ tv->tv_sec = (time_t) (t / PA_USEC_PER_SEC);
+ tv->tv_usec = (suseconds_t) (t % PA_USEC_PER_SEC);
return tv;
#else
@@ -75,9 +77,11 @@ struct timeval *pa_gettimeofday(struct timeval *tv) {
pa_usec_t pa_timeval_diff(const struct timeval *a, const struct timeval *b) {
pa_usec_t r;
- assert(a && b);
- /* Check which whan is the earlier time and swap the two arguments if reuqired. */
+ pa_assert(a);
+ pa_assert(b);
+
+ /* Check which whan is the earlier time and swap the two arguments if required. */
if (pa_timeval_cmp(a, b) < 0) {
const struct timeval *c;
c = a;
@@ -86,7 +90,7 @@ pa_usec_t pa_timeval_diff(const struct timeval *a, const struct timeval *b) {
}
/* Calculate the second difference*/
- r = ((pa_usec_t) a->tv_sec - b->tv_sec)* 1000000;
+ r = ((pa_usec_t) a->tv_sec - b->tv_sec) * PA_USEC_PER_SEC;
/* Calculate the microsecond difference */
if (a->tv_usec > b->tv_usec)
@@ -98,7 +102,8 @@ pa_usec_t pa_timeval_diff(const struct timeval *a, const struct timeval *b) {
}
int pa_timeval_cmp(const struct timeval *a, const struct timeval *b) {
- assert(a && b);
+ pa_assert(a);
+ pa_assert(b);
if (a->tv_sec < b->tv_sec)
return -1;
@@ -117,26 +122,61 @@ int pa_timeval_cmp(const struct timeval *a, const struct timeval *b) {
pa_usec_t pa_timeval_age(const struct timeval *tv) {
struct timeval now;
- assert(tv);
-
+ pa_assert(tv);
+
return pa_timeval_diff(pa_gettimeofday(&now), tv);
}
struct timeval* pa_timeval_add(struct timeval *tv, pa_usec_t v) {
unsigned long secs;
- assert(tv);
-
- secs = (v/1000000);
- tv->tv_sec += (unsigned long) secs;
- v -= secs*1000000;
+ pa_assert(tv);
+
+ secs = (unsigned long) (v/PA_USEC_PER_SEC);
+ tv->tv_sec += secs;
+ v -= ((pa_usec_t) secs) * PA_USEC_PER_SEC;
- tv->tv_usec += v;
+ tv->tv_usec += (suseconds_t) v;
/* Normalize */
- while (tv->tv_usec >= 1000000) {
+ while ((unsigned) tv->tv_usec >= PA_USEC_PER_SEC) {
tv->tv_sec++;
- tv->tv_usec -= 1000000;
+ tv->tv_usec -= PA_USEC_PER_SEC;
+ }
+
+ return tv;
+}
+
+struct timeval* pa_timeval_sub(struct timeval *tv, pa_usec_t v) {
+ unsigned long secs;
+ pa_assert(tv);
+
+ secs = (unsigned long) (v/PA_USEC_PER_SEC);
+ tv->tv_sec -= secs;
+ v -= ((pa_usec_t) secs) * PA_USEC_PER_SEC;
+
+ if (tv->tv_usec >= (suseconds_t) v)
+ tv->tv_usec -= (suseconds_t) v;
+ else {
+ tv->tv_sec --;
+ tv->tv_usec = tv->tv_usec + PA_USEC_PER_SEC - v;
}
return tv;
}
+
+struct timeval* pa_timeval_store(struct timeval *tv, pa_usec_t v) {
+ pa_assert(tv);
+
+ tv->tv_sec = v / PA_USEC_PER_SEC;
+ tv->tv_usec = v % PA_USEC_PER_SEC;
+
+ return tv;
+}
+
+pa_usec_t pa_timeval_load(const struct timeval *tv) {
+ pa_assert(tv);
+
+ return
+ (pa_usec_t) tv->tv_sec * PA_USEC_PER_SEC +
+ (pa_usec_t) tv->tv_usec;
+}
diff --git a/src/pulse/timeval.h b/src/pulse/timeval.h
index e2dbbadb..ee398296 100644
--- a/src/pulse/timeval.h
+++ b/src/pulse/timeval.h
@@ -1,21 +1,22 @@
#ifndef footimevalhfoo
#define footimevalhfoo
-/* $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.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
@@ -23,6 +24,7 @@
***/
#include <pulse/cdecl.h>
+#include <pulse/gccmacro.h>
#include <pulse/sample.h>
/** \file
@@ -30,6 +32,13 @@
PA_C_DECL_BEGIN
+#define PA_MSEC_PER_SEC ((pa_usec_t) 1000ULL)
+#define PA_USEC_PER_SEC ((pa_usec_t) 1000000ULL)
+#define PA_NSEC_PER_SEC ((pa_usec_t) 1000000000ULL)
+#define PA_USEC_PER_MSEC ((pa_usec_t) 1000ULL)
+#define PA_NSEC_PER_MSEC ((pa_usec_t) 1000000ULL)
+#define PA_NSEC_PER_USEC ((pa_usec_t) 1000ULL)
+
struct timeval;
/** Return the current timestamp, just like UNIX gettimeofday() */
@@ -37,10 +46,10 @@ struct timeval *pa_gettimeofday(struct timeval *tv);
/** Calculate the difference between the two specified timeval
* structs. */
-pa_usec_t pa_timeval_diff(const struct timeval *a, const struct timeval *b);
+pa_usec_t pa_timeval_diff(const struct timeval *a, const struct timeval *b) PA_GCC_PURE;
/** Compare the two timeval structs and return 0 when equal, negative when a < b, positive otherwse */
-int pa_timeval_cmp(const struct timeval *a, const struct timeval *b);
+int pa_timeval_cmp(const struct timeval *a, const struct timeval *b) PA_GCC_PURE;
/** Return the time difference between now and the specified timestamp */
pa_usec_t pa_timeval_age(const struct timeval *tv);
@@ -48,6 +57,15 @@ pa_usec_t pa_timeval_age(const struct timeval *tv);
/** Add the specified time inmicroseconds to the specified timeval structure */
struct timeval* pa_timeval_add(struct timeval *tv, pa_usec_t v);
+/** Subtract the specified time inmicroseconds to the specified timeval structure. \since 0.9.11 */
+struct timeval* pa_timeval_sub(struct timeval *tv, pa_usec_t v);
+
+/** Store the specified uec value in the timeval struct. \since 0.9.7 */
+struct timeval* pa_timeval_store(struct timeval *tv, pa_usec_t v);
+
+/** Load the specified tv value and return it in usec. \since 0.9.7 */
+pa_usec_t pa_timeval_load(const struct timeval *tv);
+
PA_C_DECL_END
#endif
diff --git a/src/pulse/utf8.c b/src/pulse/utf8.c
index 33fa7214..119be542 100644
--- a/src/pulse/utf8.c
+++ b/src/pulse/utf8.c
@@ -1,4 +1,24 @@
-/* $Id$ */
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2006 Lennart Poettering
+ Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
/* This file is based on the GLIB utf8 validation functions. The
* original license text follows. */
@@ -15,7 +35,7 @@
*
* 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
+ * 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
@@ -28,7 +48,6 @@
#include <config.h>
#endif
-#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <inttypes.h>
@@ -38,12 +57,15 @@
#include <iconv.h>
#endif
+#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+
#include "utf8.h"
-#include "xmalloc.h"
#define FILTER_CHAR '_'
static inline int is_unicode_valid(uint32_t ch) {
+
if (ch >= 0x110000) /* End of unicode space */
return 0;
if ((ch & 0xFFFFF800) == 0xD800) /* Reserved area for UTF-16 */
@@ -52,6 +74,7 @@ static inline int is_unicode_valid(uint32_t ch) {
return 0;
if ((ch & 0xFFFE) == 0xFFFE) /* BOM (Byte Order Mark) */
return 0;
+
return 1;
}
@@ -73,6 +96,8 @@ static char* utf8_validate(const char *str, char *output) {
int size;
uint8_t *o;
+ pa_assert(str);
+
o = (uint8_t*) output;
for (p = (const uint8_t*) str; *p; p++) {
if (*p < 128) {
@@ -130,7 +155,7 @@ ONE_REMAINING:
if (o)
o++;
-
+
continue;
error:
@@ -156,15 +181,15 @@ failure:
return NULL;
}
-const char* pa_utf8_valid (const char *str) {
+char* pa_utf8_valid (const char *str) {
return utf8_validate(str, NULL);
}
char* pa_utf8_filter (const char *str) {
char *new_str;
+ pa_assert(str);
new_str = pa_xnew(char, strlen(str) + 1);
-
return utf8_validate(str, new_str);
}
@@ -173,22 +198,24 @@ char* pa_utf8_filter (const char *str) {
static char* iconv_simple(const char *str, const char *to, const char *from) {
char *new_str;
size_t len, inlen;
-
iconv_t cd;
ICONV_CONST char *inbuf;
char *outbuf;
size_t res, inbytes, outbytes;
+ pa_assert(str);
+ pa_assert(to);
+ pa_assert(from);
+
cd = iconv_open(to, from);
if (cd == (iconv_t)-1)
return NULL;
inlen = len = strlen(str) + 1;
- new_str = pa_xmalloc(len);
- assert(new_str);
+ new_str = pa_xnew(char, len);
- while (1) {
- inbuf = (ICONV_CONST char*)str; /* Brain dead prototype for iconv() */
+ for (;;) {
+ inbuf = (ICONV_CONST char*) str; /* Brain dead prototype for iconv() */
inbytes = inlen;
outbuf = new_str;
outbytes = len;
@@ -204,11 +231,10 @@ static char* iconv_simple(const char *str, const char *to, const char *from) {
break;
}
- assert(inbytes != 0);
+ pa_assert(inbytes != 0);
len += inbytes;
new_str = pa_xrealloc(new_str, len);
- assert(new_str);
}
iconv_close(cd);
@@ -227,10 +253,12 @@ char* pa_locale_to_utf8 (const char *str) {
#else
char* pa_utf8_to_locale (const char *str) {
+ pa_assert(str);
return NULL;
}
char* pa_locale_to_utf8 (const char *str) {
+ pa_assert(str);
return NULL;
}
diff --git a/src/pulse/utf8.h b/src/pulse/utf8.h
index 2eac724d..6c7e7a5b 100644
--- a/src/pulse/utf8.h
+++ b/src/pulse/utf8.h
@@ -1,21 +1,22 @@
#ifndef fooutf8hfoo
#define fooutf8hfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
+ Copyright 2006 Lennart Poettering
+ Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
@@ -23,6 +24,7 @@
***/
#include <pulse/cdecl.h>
+#include <pulse/gccmacro.h>
/** \file
* UTF8 Validation functions
@@ -31,7 +33,7 @@
PA_C_DECL_BEGIN
/** Test if the specified strings qualifies as valid UTF8. Return the string if so, otherwise NULL */
-const char *pa_utf8_valid(const char *str);
+char *pa_utf8_valid(const char *str) PA_GCC_PURE;
/** Filter all invalid UTF8 characters from the specified string, returning a new fully UTF8 valid string. Don't forget to free the returned string with pa_xfree() */
char *pa_utf8_filter(const char *str);
diff --git a/src/pulse/util.c b/src/pulse/util.c
index c13951da..c0911b51 100644
--- a/src/pulse/util.c
+++ b/src/pulse/util.c
@@ -1,18 +1,19 @@
-/* $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.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
@@ -23,7 +24,6 @@
#include <config.h>
#endif
-#include <assert.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
@@ -53,33 +53,32 @@
#include <sys/prctl.h>
#endif
-#include "../pulsecore/winsock.h"
-
+#include <pulse/xmalloc.h>
+#include <pulsecore/winsock.h>
#include <pulsecore/core-error.h>
#include <pulsecore/log.h>
#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
#include "util.h"
-#ifndef OS_IS_WIN32
-#define PATH_SEP '/'
-#else
-#define PATH_SEP '\\'
-#endif
-
char *pa_get_user_name(char *s, size_t l) {
- char *p;
+ const char *p;
char buf[1024];
#ifdef HAVE_PWD_H
struct passwd pw, *r;
#endif
- assert(s && l > 0);
+ pa_assert(s);
+ pa_assert(l > 0);
- if (!(p = getenv("USER")) && !(p = getenv("LOGNAME")) && !(p = getenv("USERNAME"))) {
+ if (!(p = (getuid() == 0 ? "root" : NULL)) &&
+ !(p = getenv("USER")) &&
+ !(p = getenv("LOGNAME")) &&
+ !(p = getenv("USERNAME"))) {
#ifdef HAVE_PWD_H
-
+
#ifdef HAVE_GETPWUID_R
if (getpwuid_r(getuid(), &pw, buf, sizeof(buf), &r) != 0 || !r) {
#else
@@ -87,10 +86,10 @@ char *pa_get_user_name(char *s, size_t l) {
* that do not support getpwuid_r. */
if ((r = getpwuid(getuid())) == NULL) {
#endif
- snprintf(s, l, "%lu", (unsigned long) getuid());
+ pa_snprintf(s, l, "%lu", (unsigned long) getuid());
return s;
}
-
+
p = r->pw_name;
#elif defined(OS_IS_WIN32) /* HAVE_PWD_H */
@@ -110,11 +109,15 @@ char *pa_get_user_name(char *s, size_t l) {
}
char *pa_get_host_name(char *s, size_t l) {
- assert(s && l > 0);
+
+ pa_assert(s);
+ pa_assert(l > 0);
+
if (gethostname(s, l) < 0) {
pa_log("gethostname(): %s", pa_cstrerror(errno));
return NULL;
}
+
s[l-1] = 0;
return s;
}
@@ -127,7 +130,8 @@ char *pa_get_home_dir(char *s, size_t l) {
struct passwd pw, *r;
#endif
- assert(s && l);
+ pa_assert(s);
+ pa_assert(l > 0);
if ((e = getenv("HOME")))
return pa_strlcpy(s, e, l);
@@ -156,32 +160,32 @@ char *pa_get_home_dir(char *s, size_t l) {
char *pa_get_binary_name(char *s, size_t l) {
- assert(s);
- assert(l);
+ pa_assert(s);
+ pa_assert(l > 0);
#if defined(OS_IS_WIN32)
{
char path[PATH_MAX];
-
+
if (GetModuleFileName(NULL, path, PATH_MAX))
return pa_strlcpy(s, pa_path_get_filename(path), l);
}
#endif
-
-#ifdef HAVE_READLINK
+
+#ifdef __linux__
{
- int i;
- char path[PATH_MAX];
+ char *rp;
/* This works on Linux only */
-
- if ((i = readlink("/proc/self/exe", path, sizeof(path)-1)) >= 0) {
- path[i] = 0;
- return pa_strlcpy(s, pa_path_get_filename(path), l);
+
+ if ((rp = pa_readlink("/proc/self/exe"))) {
+ pa_strlcpy(s, pa_path_get_filename(rp), l);
+ pa_xfree(rp);
+ return s;
}
}
-
+
#endif
-
+
#if defined(HAVE_SYS_PRCTL_H) && defined(PR_GET_NAME)
{
@@ -192,32 +196,37 @@ char *pa_get_binary_name(char *s, size_t l) {
char tcomm[TASK_COMM_LEN+1];
memset(tcomm, 0, sizeof(tcomm));
-
+
/* This works on Linux only */
if (prctl(PR_GET_NAME, (unsigned long) tcomm, 0, 0, 0) == 0)
return pa_strlcpy(s, tcomm, l);
-
+
}
#endif
-
+
return NULL;
}
-const char *pa_path_get_filename(const char *p) {
+char *pa_path_get_filename(const char *p) {
char *fn;
- if ((fn = strrchr(p, PATH_SEP)))
+ pa_assert(p);
+
+ if ((fn = strrchr(p, PA_PATH_SEP_CHAR)))
return fn+1;
- return (const char*) p;
+ return (char*) p;
}
char *pa_get_fqdn(char *s, size_t l) {
char hn[256];
-#ifdef HAVE_GETADDRINFO
+#ifdef HAVE_GETADDRINFO
struct addrinfo *a, hints;
#endif
+ pa_assert(s);
+ pa_assert(l > 0);
+
if (!pa_get_host_name(hn, sizeof(hn)))
return NULL;
@@ -225,7 +234,7 @@ char *pa_get_fqdn(char *s, size_t l) {
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_flags = AI_CANONNAME;
-
+
if (getaddrinfo(hn, NULL, &hints, &a) < 0 || !a || !a->ai_canonname || !*a->ai_canonname)
return pa_strlcpy(s, hn, l);
diff --git a/src/pulse/util.h b/src/pulse/util.h
index 5c03b0a9..cf06d4fd 100644
--- a/src/pulse/util.h
+++ b/src/pulse/util.h
@@ -1,21 +1,22 @@
#ifndef fooutilhfoo
#define fooutilhfoo
-/* $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.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
@@ -25,6 +26,7 @@
#include <stddef.h>
#include <pulse/cdecl.h>
+#include <pulse/gccmacro.h>
/** \file
* Assorted utility functions */
@@ -49,7 +51,7 @@ char *pa_get_binary_name(char *s, size_t l);
/** Return a pointer to the filename inside a path (which is the last
* component). */
-const char *pa_path_get_filename(const char *p);
+char *pa_path_get_filename(const char *p);
/** Wait t milliseconds */
int pa_msleep(unsigned long t);
diff --git a/src/pulse/version.h.in b/src/pulse/version.h.in
index 748541a1..e6226c44 100644
--- a/src/pulse/version.h.in
+++ b/src/pulse/version.h.in
@@ -1,21 +1,22 @@
#ifndef fooversionhfoo /*-*-C-*-*/
#define fooversionhfoo
-/* $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
@@ -36,7 +37,8 @@ a macro and not a function, so it is impossible to get the pointer of
it. */
#define pa_get_headers_version() ("@PACKAGE_VERSION@")
-/** Return the version of the library the current application is linked to. */
+/** Return the version of the library the current application is
+ * linked to. */
const char* pa_get_library_version(void);
/** The current API version. Version 6 relates to Polypaudio
@@ -44,8 +46,8 @@ const char* pa_get_library_version(void);
* PA_API_VERSION undefined. */
#define PA_API_VERSION @PA_API_VERSION@
-/** The current protocol version. Version 8 relates to Polypaudio 0.8/PulseAudio 0.9.
- * \since 0.8 */
+/** The current protocol version. Version 8 relates to Polypaudio
+ * 0.8/PulseAudio 0.9. */
#define PA_PROTOCOL_VERSION @PA_PROTOCOL_VERSION@
PA_C_DECL_END
diff --git a/src/pulse/volume.c b/src/pulse/volume.c
index 530814e0..70d6f86a 100644
--- a/src/pulse/volume.c
+++ b/src/pulse/volume.c
@@ -1,18 +1,18 @@
-/* $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
@@ -23,20 +23,22 @@
#include <config.h>
#endif
-#include <assert.h>
#include <stdio.h>
#include <string.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+
#include "volume.h"
int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b) {
int i;
- assert(a);
- assert(b);
+ pa_assert(a);
+ pa_assert(b);
if (a->channels != b->channels)
return 0;
-
+
for (i = 0; i < a->channels; i++)
if (a->values[i] != b->values[i])
return 0;
@@ -46,10 +48,10 @@ int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b) {
pa_cvolume* pa_cvolume_set(pa_cvolume *a, unsigned channels, pa_volume_t v) {
int i;
-
- assert(a);
- assert(channels > 0);
- assert(channels <= PA_CHANNELS_MAX);
+
+ pa_assert(a);
+ pa_assert(channels > 0);
+ pa_assert(channels <= PA_CHANNELS_MAX);
a->channels = channels;
@@ -62,7 +64,7 @@ pa_cvolume* pa_cvolume_set(pa_cvolume *a, unsigned channels, pa_volume_t v) {
pa_volume_t pa_cvolume_avg(const pa_cvolume *a) {
uint64_t sum = 0;
int i;
- assert(a);
+ pa_assert(a);
for (i = 0; i < a->channels; i++)
sum += a->values[i];
@@ -76,10 +78,10 @@ pa_volume_t pa_sw_volume_multiply(pa_volume_t a, pa_volume_t b) {
return pa_sw_volume_from_linear(pa_sw_volume_to_linear(a)* pa_sw_volume_to_linear(b));
}
-#define USER_DECIBEL_RANGE 30
+#define USER_DECIBEL_RANGE 60
pa_volume_t pa_sw_volume_from_dB(double dB) {
- if (dB <= -USER_DECIBEL_RANGE)
+ if (isinf(dB) < 0 || dB <= -USER_DECIBEL_RANGE)
return PA_VOLUME_MUTED;
return (pa_volume_t) ((dB/USER_DECIBEL_RANGE+1)*PA_VOLUME_NORM);
@@ -115,15 +117,15 @@ char *pa_cvolume_snprint(char *s, size_t l, const pa_cvolume *c) {
unsigned channel;
int first = 1;
char *e;
-
- assert(s);
- assert(l > 0);
- assert(c);
+
+ pa_assert(s);
+ pa_assert(l > 0);
+ pa_assert(c);
*(e = s) = 0;
for (channel = 0; channel < c->channels && l > 1; channel++) {
- l -= snprintf(e, l, "%s%u: %3u%%",
+ l -= pa_snprintf(e, l, "%s%u: %3u%%",
first ? "" : " ",
channel,
(c->values[channel]*100)/PA_VOLUME_NORM);
@@ -138,7 +140,7 @@ char *pa_cvolume_snprint(char *s, size_t l, const pa_cvolume *c) {
/** Return non-zero if the volume of all channels is equal to the specified value */
int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v) {
unsigned c;
- assert(a);
+ pa_assert(a);
for (c = 0; c < a->channels; c++)
if (a->values[c] != v)
@@ -149,10 +151,10 @@ int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v) {
pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b) {
unsigned i;
-
- assert(dest);
- assert(a);
- assert(b);
+
+ pa_assert(dest);
+ pa_assert(a);
+ pa_assert(b);
for (i = 0; i < a->channels && i < b->channels && i < PA_CHANNELS_MAX; i++) {
@@ -167,7 +169,7 @@ pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const
}
int pa_cvolume_valid(const pa_cvolume *v) {
- assert(v);
+ pa_assert(v);
if (v->channels <= 0 || v->channels > PA_CHANNELS_MAX)
return 0;
diff --git a/src/pulse/volume.h b/src/pulse/volume.h
index 6c60223a..3befb1da 100644
--- a/src/pulse/volume.h
+++ b/src/pulse/volume.h
@@ -1,21 +1,22 @@
#ifndef foovolumehfoo
#define foovolumehfoo
-/* $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
@@ -23,7 +24,9 @@
***/
#include <inttypes.h>
+
#include <pulse/cdecl.h>
+#include <pulse/gccmacro.h>
#include <pulse/sample.h>
/** \page volume Volume Control
@@ -98,10 +101,10 @@ PA_C_DECL_BEGIN
typedef uint32_t pa_volume_t;
/** Normal volume (100%) */
-#define PA_VOLUME_NORM (0x10000)
+#define PA_VOLUME_NORM ((pa_volume_t) 0x10000U)
/** Muted volume (0%) */
-#define PA_VOLUME_MUTED (0)
+#define PA_VOLUME_MUTED ((pa_volume_t) 0U)
/** A structure encapsulating a per-channel volume */
typedef struct pa_cvolume {
@@ -110,7 +113,7 @@ typedef struct pa_cvolume {
} pa_cvolume;
/** Return non-zero when *a == *b */
-int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b);
+int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b) PA_GCC_PURE;
/** Set the volume of all channels to PA_VOLUME_NORM */
#define pa_cvolume_reset(a, n) pa_cvolume_set((a), (n), PA_VOLUME_NORM)
@@ -128,13 +131,13 @@ pa_cvolume* pa_cvolume_set(pa_cvolume *a, unsigned channels, pa_volume_t v);
char *pa_cvolume_snprint(char *s, size_t l, const pa_cvolume *c);
/** Return the average volume of all channels */
-pa_volume_t pa_cvolume_avg(const pa_cvolume *a);
+pa_volume_t pa_cvolume_avg(const pa_cvolume *a) PA_GCC_PURE;
/** Return TRUE when the passed cvolume structure is valid, FALSE otherwise */
-int pa_cvolume_valid(const pa_cvolume *v);
+int pa_cvolume_valid(const pa_cvolume *v) PA_GCC_PURE;
/** Return non-zero if the volume of all channels is equal to the specified value */
-int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v);
+int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v) PA_GCC_PURE;
/** Return 1 if the specified volume has all channels muted */
#define pa_cvolume_is_muted(a) pa_cvolume_channels_equal_to((a), PA_VOLUME_MUTED)
@@ -143,28 +146,28 @@ int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v);
#define pa_cvolume_is_norm(a) pa_cvolume_channels_equal_to((a), PA_VOLUME_NORM)
/** Multiply two volumes specifications, return the result. This uses PA_VOLUME_NORM as neutral element of multiplication. This is only valid for software volumes! */
-pa_volume_t pa_sw_volume_multiply(pa_volume_t a, pa_volume_t b);
+pa_volume_t pa_sw_volume_multiply(pa_volume_t a, pa_volume_t b) PA_GCC_CONST;
/** Multiply to per-channel volumes and return the result in *dest. This is only valid for software volumes! */
pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b);
-/** Convert a decibel value to a volume. This is only valid for software volumes! \since 0.4 */
-pa_volume_t pa_sw_volume_from_dB(double f);
+/** Convert a decibel value to a volume. This is only valid for software volumes! */
+pa_volume_t pa_sw_volume_from_dB(double f) PA_GCC_CONST;
-/** Convert a volume to a decibel value. This is only valid for software volumes! \since 0.4 */
-double pa_sw_volume_to_dB(pa_volume_t v);
+/** Convert a volume to a decibel value. This is only valid for software volumes! */
+double pa_sw_volume_to_dB(pa_volume_t v) PA_GCC_CONST;
-/** Convert a linear factor to a volume. This is only valid for software volumes! \since 0.8 */
-pa_volume_t pa_sw_volume_from_linear(double v);
+/** Convert a linear factor to a volume. This is only valid for software volumes! */
+pa_volume_t pa_sw_volume_from_linear(double v) PA_GCC_CONST;
-/** Convert a volume to a linear factor. This is only valid for software volumes! \since 0.8 */
-double pa_sw_volume_to_linear(pa_volume_t v);
+/** Convert a volume to a linear factor. This is only valid for software volumes! */
+double pa_sw_volume_to_linear(pa_volume_t v) PA_GCC_CONST;
#ifdef INFINITY
-#define PA_DECIBEL_MININFTY (-INFINITY)
+#define PA_DECIBEL_MININFTY ((double) -INFINITY)
#else
-/** This value is used as minus infinity when using pa_volume_{to,from}_dB(). \since 0.4 */
-#define PA_DECIBEL_MININFTY (-200)
+/** This value is used as minus infinity when using pa_volume_{to,from}_dB(). */
+#define PA_DECIBEL_MININFTY ((double) -200.0)
#endif
PA_C_DECL_END
diff --git a/src/pulse/xmalloc.c b/src/pulse/xmalloc.c
index 36755166..90237013 100644
--- a/src/pulse/xmalloc.c
+++ b/src/pulse/xmalloc.c
@@ -1,18 +1,18 @@
-/* $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
@@ -25,12 +25,13 @@
#include <stdlib.h>
#include <signal.h>
-#include <assert.h>
#include <unistd.h>
#include <string.h>
+#include <errno.h>
+#include <pulse/gccmacro.h>
#include <pulsecore/core-util.h>
-#include <pulsecore/gccmacro.h>
+#include <pulsecore/macro.h>
#include "xmalloc.h"
@@ -58,31 +59,31 @@ static void oom(void) {
void* pa_xmalloc(size_t size) {
void *p;
- assert(size > 0);
- assert(size < MAX_ALLOC_SIZE);
-
+ pa_assert(size > 0);
+ pa_assert(size < MAX_ALLOC_SIZE);
+
if (!(p = malloc(size)))
oom();
-
+
return p;
}
void* pa_xmalloc0(size_t size) {
void *p;
- assert(size > 0);
- assert(size < MAX_ALLOC_SIZE);
-
+ pa_assert(size > 0);
+ pa_assert(size < MAX_ALLOC_SIZE);
+
if (!(p = calloc(1, size)))
oom();
-
+
return p;
}
-
+
void *pa_xrealloc(void *ptr, size_t size) {
void *p;
- assert(size > 0);
- assert(size < MAX_ALLOC_SIZE);
-
+ pa_assert(size > 0);
+ pa_assert(size < MAX_ALLOC_SIZE);
+
if (!(p = realloc(ptr, size)))
oom();
return p;
@@ -107,7 +108,7 @@ char *pa_xstrdup(const char *s) {
char *pa_xstrndup(const char *s, size_t l) {
char *e, *r;
-
+
if (!s)
return NULL;
@@ -121,8 +122,12 @@ char *pa_xstrndup(const char *s, size_t l) {
}
void pa_xfree(void *p) {
+ int saved_errno;
+
if (!p)
return;
+ saved_errno = errno;
free(p);
+ errno = saved_errno;
}
diff --git a/src/pulse/xmalloc.h b/src/pulse/xmalloc.h
index 126c495c..c453138b 100644
--- a/src/pulse/xmalloc.h
+++ b/src/pulse/xmalloc.h
@@ -1,21 +1,21 @@
#ifndef foomemoryhfoo
#define foomemoryhfoo
-/* $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
@@ -73,6 +73,15 @@ static inline void* pa_xnew0_internal(unsigned n, size_t k) {
/** Same as pa_xnew() but set the memory to zero */
#define pa_xnew0(type, n) ((type*) pa_xnew0_internal((n), sizeof(type)))
+/** Internal helper for pa_xnew0() */
+static inline void* pa_xnewdup_internal(const void *p, unsigned n, size_t k) {
+ assert(n < INT_MAX/k);
+ return pa_xmemdup(p, n*k);
+}
+
+/** Same as pa_xnew() but set the memory to zero */
+#define pa_xnewdup(type, p, n) ((type*) pa_xnewdup_internal((p), (n), sizeof(type)))
+
PA_C_DECL_END
#endif
diff --git a/src/pulsecore/anotify.c b/src/pulsecore/anotify.c
deleted file mode 100644
index a61f8442..00000000
--- a/src/pulsecore/anotify.c
+++ /dev/null
@@ -1,143 +0,0 @@
-/* $Id$ */
-
-/***
- This file is part of PulseAudio.
-
- PulseAudio is free software; you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as
- published by the Free Software Foundation; either version 2.1 of the
- License, or (at your option) any later version.
-
- PulseAudio is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with PulseAudio; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <assert.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-#include <pulse/xmalloc.h>
-
-#include "anotify.h"
-
-#define EVENTS_MAX 16
-
-struct pa_anotify {
- pa_mainloop_api *api;
- pa_anotify_cb_t callback;
- void *userdata;
- int fds[2];
- pa_io_event *io_event;
- pa_defer_event *defer_event;
-
- uint8_t queued_events[EVENTS_MAX];
- unsigned n_queued_events, queue_index;
-};
-
-static void dispatch_event(pa_anotify *a) {
- assert(a);
- assert(a->queue_index < a->n_queued_events);
-
- a->callback(a->queued_events[a->queue_index++], a->userdata);
-
- if (a->queue_index >= a->n_queued_events) {
- a->n_queued_events = 0;
- a->queue_index = 0;
-
- a->api->io_enable(a->io_event, PA_IO_EVENT_INPUT);
- a->api->defer_enable(a->defer_event, 0);
- } else {
- a->api->io_enable(a->io_event, 0);
- a->api->defer_enable(a->defer_event, 1);
- }
-}
-
-static void io_callback(
- pa_mainloop_api *api,
- pa_io_event *e,
- int fd,
- pa_io_event_flags_t events,
- void *userdata) {
-
- pa_anotify *a = userdata;
- ssize_t r;
-
- assert(a);
- assert(events == PA_IO_EVENT_INPUT);
- assert(a->n_queued_events == 0);
-
- r = read(fd, a->queued_events, sizeof(a->queued_events));
- assert(r > 0);
-
- a->n_queued_events = (unsigned) r;
- a->queue_index = 0;
-
- /* Only dispatch a single event */
- dispatch_event(a);
-}
-
-static void defer_callback(pa_mainloop_api *api, pa_defer_event *e, void *userdata) {
- pa_anotify *a = userdata;
- assert(a);
-
- dispatch_event(a);
-}
-
-pa_anotify *pa_anotify_new(pa_mainloop_api*api, pa_anotify_cb_t cb, void *userdata) {
- pa_anotify *a;
-
- assert(api);
- assert(cb);
-
- a = pa_xnew(pa_anotify, 1);
-
- if (pipe(a->fds) < 0) {
- pa_xfree(a);
- return NULL;
- }
-
- a->api = api;
- a->callback = cb;
- a->userdata = userdata;
-
- a->io_event = api->io_new(api, a->fds[0], PA_IO_EVENT_INPUT, io_callback, a);
- a->defer_event = api->defer_new(api, defer_callback, a);
- a->api->defer_enable(a->defer_event, 0);
-
- a->n_queued_events = 0;
-
- return a;
-}
-
-void pa_anotify_free(pa_anotify *a) {
- assert(a);
-
- a->api->io_free(a->io_event);
- a->api->defer_free(a->defer_event);
-
- if (a->fds[0] >= 0)
- close(a->fds[0]);
- if (a->fds[1] >= 0)
- close(a->fds[1]);
-
- pa_xfree(a);
-}
-
-int pa_anotify_signal(pa_anotify *a, uint8_t event) {
- ssize_t r;
- assert(a);
-
- r = write(a->fds[1], &event, 1);
- return r != 1 ? -1 : 0;
-}
diff --git a/src/pulsecore/asyncmsgq.c b/src/pulsecore/asyncmsgq.c
new file mode 100644
index 00000000..5c7af2a8
--- /dev/null
+++ b/src/pulsecore/asyncmsgq.c
@@ -0,0 +1,319 @@
+/***
+ 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.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <errno.h>
+
+#include <pulsecore/atomic.h>
+#include <pulsecore/log.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/semaphore.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/flist.h>
+#include <pulse/xmalloc.h>
+
+#include "asyncmsgq.h"
+
+PA_STATIC_FLIST_DECLARE(asyncmsgq, 0, pa_xfree);
+PA_STATIC_FLIST_DECLARE(semaphores, 0, (void(*)(void*)) pa_semaphore_free);
+
+struct asyncmsgq_item {
+ int code;
+ pa_msgobject *object;
+ void *userdata;
+ pa_free_cb_t free_cb;
+ int64_t offset;
+ pa_memchunk memchunk;
+ pa_semaphore *semaphore;
+ int ret;
+};
+
+struct pa_asyncmsgq {
+ PA_REFCNT_DECLARE;
+ pa_asyncq *asyncq;
+ pa_mutex *mutex; /* only for the writer side */
+
+ struct asyncmsgq_item *current;
+};
+
+pa_asyncmsgq *pa_asyncmsgq_new(unsigned size) {
+ pa_asyncmsgq *a;
+
+ a = pa_xnew(pa_asyncmsgq, 1);
+
+ PA_REFCNT_INIT(a);
+ pa_assert_se(a->asyncq = pa_asyncq_new(size));
+ pa_assert_se(a->mutex = pa_mutex_new(FALSE, TRUE));
+ a->current = NULL;
+
+ return a;
+}
+
+static void asyncmsgq_free(pa_asyncmsgq *a) {
+ struct asyncmsgq_item *i;
+ pa_assert(a);
+
+ while ((i = pa_asyncq_pop(a->asyncq, 0))) {
+
+ pa_assert(!i->semaphore);
+
+ if (i->object)
+ pa_msgobject_unref(i->object);
+
+ if (i->memchunk.memblock)
+ pa_memblock_unref(i->memchunk.memblock);
+
+ if (i->free_cb)
+ i->free_cb(i->userdata);
+
+ if (pa_flist_push(PA_STATIC_FLIST_GET(asyncmsgq), i) < 0)
+ pa_xfree(i);
+ }
+
+ pa_asyncq_free(a->asyncq, NULL);
+ pa_mutex_free(a->mutex);
+ pa_xfree(a);
+}
+
+pa_asyncmsgq* pa_asyncmsgq_ref(pa_asyncmsgq *q) {
+ pa_assert(PA_REFCNT_VALUE(q) > 0);
+
+ PA_REFCNT_INC(q);
+ return q;
+}
+
+void pa_asyncmsgq_unref(pa_asyncmsgq* q) {
+ pa_assert(PA_REFCNT_VALUE(q) > 0);
+
+ if (PA_REFCNT_DEC(q) <= 0)
+ asyncmsgq_free(q);
+}
+
+void pa_asyncmsgq_post(pa_asyncmsgq *a, pa_msgobject *object, int code, const void *userdata, int64_t offset, const pa_memchunk *chunk, pa_free_cb_t free_cb) {
+ struct asyncmsgq_item *i;
+ pa_assert(PA_REFCNT_VALUE(a) > 0);
+
+ if (!(i = pa_flist_pop(PA_STATIC_FLIST_GET(asyncmsgq))))
+ i = pa_xnew(struct asyncmsgq_item, 1);
+
+ i->code = code;
+ i->object = object ? pa_msgobject_ref(object) : NULL;
+ i->userdata = (void*) userdata;
+ i->free_cb = free_cb;
+ i->offset = offset;
+ if (chunk) {
+ pa_assert(chunk->memblock);
+ i->memchunk = *chunk;
+ pa_memblock_ref(i->memchunk.memblock);
+ } else
+ pa_memchunk_reset(&i->memchunk);
+ i->semaphore = NULL;
+
+ /* This mutex makes the queue multiple-writer safe. This lock is only used on the writing side */
+ pa_mutex_lock(a->mutex);
+ pa_asyncq_post(a->asyncq, i);
+ pa_mutex_unlock(a->mutex);
+}
+
+int pa_asyncmsgq_send(pa_asyncmsgq *a, pa_msgobject *object, int code, const void *userdata, int64_t offset, const pa_memchunk *chunk) {
+ struct asyncmsgq_item i;
+ pa_assert(PA_REFCNT_VALUE(a) > 0);
+
+ i.code = code;
+ i.object = object;
+ i.userdata = (void*) userdata;
+ i.free_cb = NULL;
+ i.ret = -1;
+ i.offset = offset;
+ if (chunk) {
+ pa_assert(chunk->memblock);
+ i.memchunk = *chunk;
+ } else
+ pa_memchunk_reset(&i.memchunk);
+
+ if (!(i.semaphore = pa_flist_pop(PA_STATIC_FLIST_GET(semaphores))))
+ i.semaphore = pa_semaphore_new(0);
+
+ pa_assert_se(i.semaphore);
+
+ /* Thus mutex makes the queue multiple-writer safe. This lock is only used on the writing side */
+ pa_mutex_lock(a->mutex);
+ pa_assert_se(pa_asyncq_push(a->asyncq, &i, TRUE) == 0);
+ pa_mutex_unlock(a->mutex);
+
+ pa_semaphore_wait(i.semaphore);
+
+ if (pa_flist_push(PA_STATIC_FLIST_GET(semaphores), i.semaphore) < 0)
+ pa_semaphore_free(i.semaphore);
+
+ return i.ret;
+}
+
+int pa_asyncmsgq_get(pa_asyncmsgq *a, pa_msgobject **object, int *code, void **userdata, int64_t *offset, pa_memchunk *chunk, pa_bool_t wait) {
+ pa_assert(PA_REFCNT_VALUE(a) > 0);
+ pa_assert(!a->current);
+
+ if (!(a->current = pa_asyncq_pop(a->asyncq, wait))) {
+/* pa_log("failure"); */
+ return -1;
+ }
+
+/* pa_log("success"); */
+
+ if (code)
+ *code = a->current->code;
+ if (userdata)
+ *userdata = a->current->userdata;
+ if (offset)
+ *offset = a->current->offset;
+ if (object) {
+ if ((*object = a->current->object))
+ pa_msgobject_assert_ref(*object);
+ }
+ if (chunk)
+ *chunk = a->current->memchunk;
+
+/* pa_log_debug("Get q=%p object=%p (%s) code=%i data=%p chunk.length=%lu", (void*) a, (void*) a->current->object, a->current->object ? a->current->object->parent.type_name : NULL, a->current->code, (void*) a->current->userdata, (unsigned long) a->current->memchunk.length); */
+
+ return 0;
+}
+
+void pa_asyncmsgq_done(pa_asyncmsgq *a, int ret) {
+ pa_assert(PA_REFCNT_VALUE(a) > 0);
+ pa_assert(a);
+ pa_assert(a->current);
+
+ if (a->current->semaphore) {
+ a->current->ret = ret;
+ pa_semaphore_post(a->current->semaphore);
+ } else {
+
+ if (a->current->free_cb)
+ a->current->free_cb(a->current->userdata);
+
+ if (a->current->object)
+ pa_msgobject_unref(a->current->object);
+
+ if (a->current->memchunk.memblock)
+ pa_memblock_unref(a->current->memchunk.memblock);
+
+ if (pa_flist_push(PA_STATIC_FLIST_GET(asyncmsgq), a->current) < 0)
+ pa_xfree(a->current);
+ }
+
+ a->current = NULL;
+}
+
+int pa_asyncmsgq_wait_for(pa_asyncmsgq *a, int code) {
+ int c;
+ pa_assert(PA_REFCNT_VALUE(a) > 0);
+
+ pa_asyncmsgq_ref(a);
+
+ do {
+ pa_msgobject *o;
+ void *data;
+ int64_t offset;
+ pa_memchunk chunk;
+ int ret;
+
+ if (pa_asyncmsgq_get(a, &o, &c, &data, &offset, &chunk, 1) < 0)
+ return -1;
+
+ ret = pa_asyncmsgq_dispatch(o, c, data, offset, &chunk);
+ pa_asyncmsgq_done(a, ret);
+
+ } while (c != code);
+
+ pa_asyncmsgq_unref(a);
+
+ return 0;
+}
+
+int pa_asyncmsgq_process_one(pa_asyncmsgq *a) {
+ pa_msgobject *object;
+ int code;
+ void *data;
+ pa_memchunk chunk;
+ int64_t offset;
+ int ret;
+
+ pa_assert(PA_REFCNT_VALUE(a) > 0);
+
+ if (pa_asyncmsgq_get(a, &object, &code, &data, &offset, &chunk, 0) < 0)
+ return 0;
+
+ pa_asyncmsgq_ref(a);
+ ret = pa_asyncmsgq_dispatch(object, code, data, offset, &chunk);
+ pa_asyncmsgq_done(a, ret);
+ pa_asyncmsgq_unref(a);
+
+ return 1;
+}
+
+int pa_asyncmsgq_read_fd(pa_asyncmsgq *a) {
+ pa_assert(PA_REFCNT_VALUE(a) > 0);
+
+ return pa_asyncq_read_fd(a->asyncq);
+}
+
+int pa_asyncmsgq_read_before_poll(pa_asyncmsgq *a) {
+ pa_assert(PA_REFCNT_VALUE(a) > 0);
+
+ return pa_asyncq_read_before_poll(a->asyncq);
+}
+
+void pa_asyncmsgq_read_after_poll(pa_asyncmsgq *a) {
+ pa_assert(PA_REFCNT_VALUE(a) > 0);
+
+ pa_asyncq_read_after_poll(a->asyncq);
+}
+
+int pa_asyncmsgq_write_fd(pa_asyncmsgq *a) {
+ pa_assert(PA_REFCNT_VALUE(a) > 0);
+
+ return pa_asyncq_write_fd(a->asyncq);
+}
+
+void pa_asyncmsgq_write_before_poll(pa_asyncmsgq *a) {
+ pa_assert(PA_REFCNT_VALUE(a) > 0);
+
+ pa_asyncq_write_before_poll(a->asyncq);
+}
+
+void pa_asyncmsgq_write_after_poll(pa_asyncmsgq *a) {
+ pa_assert(PA_REFCNT_VALUE(a) > 0);
+
+ pa_asyncq_write_after_poll(a->asyncq);
+}
+
+int pa_asyncmsgq_dispatch(pa_msgobject *object, int code, void *userdata, int64_t offset, pa_memchunk *memchunk) {
+
+ if (object)
+ return object->process_msg(object, code, userdata, offset, memchunk);
+
+ return 0;
+}
diff --git a/src/pulsecore/asyncmsgq.h b/src/pulsecore/asyncmsgq.h
new file mode 100644
index 00000000..1f38207a
--- /dev/null
+++ b/src/pulsecore/asyncmsgq.h
@@ -0,0 +1,79 @@
+#ifndef foopulseasyncmsgqhfoo
+#define foopulseasyncmsgqhfoo
+
+/***
+ 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.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <sys/types.h>
+
+#include <pulsecore/asyncq.h>
+#include <pulsecore/memchunk.h>
+#include <pulsecore/msgobject.h>
+
+/* A simple asynchronous message queue, based on pa_asyncq. In
+ * contrast to pa_asyncq this one is multiple-writer safe, though
+ * still not multiple-reader safe. This queue is intended to be used
+ * for controlling real-time threads from normal-priority
+ * threads. Multiple-writer-safety is accomplished by using a mutex on
+ * the writer side. This queue is thus not useful for communication
+ * between several real-time threads.
+ *
+ * The queue takes messages consisting of:
+ * "Object" for which this messages is intended (may be NULL)
+ * A numeric message code
+ * Arbitrary userdata pointer (may be NULL)
+ * A memchunk (may be NULL)
+ *
+ * There are two functions for submitting messages: _post and
+ * _send. The former just enqueues the message asynchronously, the
+ * latter waits for completion, synchronously. */
+
+enum {
+ PA_MESSAGE_SHUTDOWN = -1/* A generic message to inform the handler of this queue to quit */
+};
+
+typedef struct pa_asyncmsgq pa_asyncmsgq;
+
+pa_asyncmsgq* pa_asyncmsgq_new(unsigned size);
+pa_asyncmsgq* pa_asyncmsgq_ref(pa_asyncmsgq *q);
+
+void pa_asyncmsgq_unref(pa_asyncmsgq* q);
+
+void pa_asyncmsgq_post(pa_asyncmsgq *q, pa_msgobject *object, int code, const void *userdata, int64_t offset, const pa_memchunk *memchunk, pa_free_cb_t userdata_free_cb);
+int pa_asyncmsgq_send(pa_asyncmsgq *q, pa_msgobject *object, int code, const void *userdata, int64_t offset, const pa_memchunk *memchunk);
+
+int pa_asyncmsgq_get(pa_asyncmsgq *q, pa_msgobject **object, int *code, void **userdata, int64_t *offset, pa_memchunk *memchunk, pa_bool_t wait);
+int pa_asyncmsgq_dispatch(pa_msgobject *object, int code, void *userdata, int64_t offset, pa_memchunk *memchunk);
+void pa_asyncmsgq_done(pa_asyncmsgq *q, int ret);
+int pa_asyncmsgq_wait_for(pa_asyncmsgq *a, int code);
+int pa_asyncmsgq_process_one(pa_asyncmsgq *a);
+
+/* For the reading side */
+int pa_asyncmsgq_read_fd(pa_asyncmsgq *q);
+int pa_asyncmsgq_read_before_poll(pa_asyncmsgq *a);
+void pa_asyncmsgq_read_after_poll(pa_asyncmsgq *a);
+
+/* For the write side */
+int pa_asyncmsgq_write_fd(pa_asyncmsgq *q);
+void pa_asyncmsgq_write_before_poll(pa_asyncmsgq *a);
+void pa_asyncmsgq_write_after_poll(pa_asyncmsgq *a);
+
+#endif
diff --git a/src/pulsecore/asyncq.c b/src/pulsecore/asyncq.c
new file mode 100644
index 00000000..03e9f0df
--- /dev/null
+++ b/src/pulsecore/asyncq.c
@@ -0,0 +1,321 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2006-2008 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <errno.h>
+
+#include <pulsecore/atomic.h>
+#include <pulsecore/log.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/llist.h>
+#include <pulsecore/flist.h>
+#include <pulse/xmalloc.h>
+
+#include "asyncq.h"
+#include "fdsem.h"
+
+#define ASYNCQ_SIZE 256
+
+/* For debugging purposes we can define _Y to put an extra thread
+ * yield between each operation. */
+
+/* #define PROFILE */
+
+#ifdef PROFILE
+#define _Y pa_thread_yield()
+#else
+#define _Y do { } while(0)
+#endif
+
+struct localq {
+ void *data;
+ PA_LLIST_FIELDS(struct localq);
+};
+
+struct pa_asyncq {
+ unsigned size;
+ unsigned read_idx;
+ unsigned write_idx;
+ pa_fdsem *read_fdsem, *write_fdsem;
+
+ PA_LLIST_HEAD(struct localq, localq);
+ struct localq *last_localq;
+ pa_bool_t waiting_for_post;
+};
+
+PA_STATIC_FLIST_DECLARE(localq, 0, pa_xfree);
+
+#define PA_ASYNCQ_CELLS(x) ((pa_atomic_ptr_t*) ((uint8_t*) (x) + PA_ALIGN(sizeof(struct pa_asyncq))))
+
+static int reduce(pa_asyncq *l, int value) {
+ return value & (unsigned) (l->size - 1);
+}
+
+pa_asyncq *pa_asyncq_new(unsigned size) {
+ pa_asyncq *l;
+
+ if (!size)
+ size = ASYNCQ_SIZE;
+
+ pa_assert(pa_is_power_of_two(size));
+
+ l = pa_xmalloc0(PA_ALIGN(sizeof(pa_asyncq)) + (sizeof(pa_atomic_ptr_t) * size));
+
+ l->size = size;
+
+ PA_LLIST_HEAD_INIT(struct localq, l->localq);
+ l->last_localq = NULL;
+ l->waiting_for_post = FALSE;
+
+ if (!(l->read_fdsem = pa_fdsem_new())) {
+ pa_xfree(l);
+ return NULL;
+ }
+
+ if (!(l->write_fdsem = pa_fdsem_new())) {
+ pa_fdsem_free(l->read_fdsem);
+ pa_xfree(l);
+ return NULL;
+ }
+
+ return l;
+}
+
+void pa_asyncq_free(pa_asyncq *l, pa_free_cb_t free_cb) {
+ struct localq *q;
+ pa_assert(l);
+
+ if (free_cb) {
+ void *p;
+
+ while ((p = pa_asyncq_pop(l, 0)))
+ free_cb(p);
+ }
+
+ while ((q = l->localq)) {
+ if (free_cb)
+ free_cb(q->data);
+
+ PA_LLIST_REMOVE(struct localq, l->localq, q);
+
+ if (pa_flist_push(PA_STATIC_FLIST_GET(localq), q) < 0)
+ pa_xfree(q);
+ }
+
+ pa_fdsem_free(l->read_fdsem);
+ pa_fdsem_free(l->write_fdsem);
+ pa_xfree(l);
+}
+
+static int push(pa_asyncq*l, void *p, pa_bool_t wait) {
+ int idx;
+ pa_atomic_ptr_t *cells;
+
+ pa_assert(l);
+ pa_assert(p);
+
+ cells = PA_ASYNCQ_CELLS(l);
+
+ _Y;
+ idx = reduce(l, l->write_idx);
+
+ if (!pa_atomic_ptr_cmpxchg(&cells[idx], NULL, p)) {
+
+ if (!wait)
+ return -1;
+
+/* pa_log("sleeping on push"); */
+
+ do {
+ pa_fdsem_wait(l->read_fdsem);
+ } while (!pa_atomic_ptr_cmpxchg(&cells[idx], NULL, p));
+ }
+
+ _Y;
+ l->write_idx++;
+
+ pa_fdsem_post(l->write_fdsem);
+
+ return 0;
+}
+
+static pa_bool_t flush_postq(pa_asyncq *l) {
+ struct localq *q;
+
+ pa_assert(l);
+
+ while ((q = l->last_localq)) {
+
+ if (push(l, q->data, FALSE) < 0)
+ return FALSE;
+
+ l->last_localq = q->prev;
+
+ PA_LLIST_REMOVE(struct localq, l->localq, q);
+
+ if (pa_flist_push(PA_STATIC_FLIST_GET(localq), q) < 0)
+ pa_xfree(q);
+ }
+
+ return TRUE;
+}
+
+int pa_asyncq_push(pa_asyncq*l, void *p, pa_bool_t wait) {
+ pa_assert(l);
+
+ if (!flush_postq(l))
+ return -1;
+
+ return push(l, p, wait);
+}
+
+void pa_asyncq_post(pa_asyncq*l, void *p) {
+ struct localq *q;
+
+ pa_assert(l);
+ pa_assert(p);
+
+ if (pa_asyncq_push(l, p, FALSE) >= 0)
+ return;
+
+ /* OK, we couldn't push anything in the queue. So let's queue it
+ * locally and push it later */
+
+ pa_log("q overrun, queuing locally");
+
+ if (!(q = pa_flist_pop(PA_STATIC_FLIST_GET(localq))))
+ q = pa_xnew(struct localq, 1);
+
+ q->data = p;
+ PA_LLIST_PREPEND(struct localq, l->localq, q);
+
+ if (!l->last_localq)
+ l->last_localq = q;
+
+ return;
+}
+
+void* pa_asyncq_pop(pa_asyncq*l, pa_bool_t wait) {
+ int idx;
+ void *ret;
+ pa_atomic_ptr_t *cells;
+
+ pa_assert(l);
+
+ cells = PA_ASYNCQ_CELLS(l);
+
+ _Y;
+ idx = reduce(l, l->read_idx);
+
+ if (!(ret = pa_atomic_ptr_load(&cells[idx]))) {
+
+ if (!wait)
+ return NULL;
+
+/* pa_log("sleeping on pop"); */
+
+ do {
+ pa_fdsem_wait(l->write_fdsem);
+ } while (!(ret = pa_atomic_ptr_load(&cells[idx])));
+ }
+
+ pa_assert(ret);
+
+ /* Guaranteed to succeed if we only have a single reader */
+ pa_assert_se(pa_atomic_ptr_cmpxchg(&cells[idx], ret, NULL));
+
+ _Y;
+ l->read_idx++;
+
+ pa_fdsem_post(l->read_fdsem);
+
+ return ret;
+}
+
+int pa_asyncq_read_fd(pa_asyncq *q) {
+ pa_assert(q);
+
+ return pa_fdsem_get(q->write_fdsem);
+}
+
+int pa_asyncq_read_before_poll(pa_asyncq *l) {
+ int idx;
+ pa_atomic_ptr_t *cells;
+
+ pa_assert(l);
+
+ cells = PA_ASYNCQ_CELLS(l);
+
+ _Y;
+ idx = reduce(l, l->read_idx);
+
+ for (;;) {
+ if (pa_atomic_ptr_load(&cells[idx]))
+ return -1;
+
+ if (pa_fdsem_before_poll(l->write_fdsem) >= 0)
+ return 0;
+ }
+
+ return 0;
+}
+
+void pa_asyncq_read_after_poll(pa_asyncq *l) {
+ pa_assert(l);
+
+ pa_fdsem_after_poll(l->write_fdsem);
+}
+
+int pa_asyncq_write_fd(pa_asyncq *q) {
+ pa_assert(q);
+
+ return pa_fdsem_get(q->read_fdsem);
+}
+
+void pa_asyncq_write_before_poll(pa_asyncq *l) {
+ pa_assert(l);
+
+ for (;;) {
+
+ if (flush_postq(l))
+ break;
+
+ if (pa_fdsem_before_poll(l->read_fdsem) >= 0) {
+ l->waiting_for_post = TRUE;
+ break;
+ }
+ }
+}
+
+void pa_asyncq_write_after_poll(pa_asyncq *l) {
+ pa_assert(l);
+
+ if (l->waiting_for_post) {
+ pa_fdsem_after_poll(l->read_fdsem);
+ l->waiting_for_post = FALSE;
+ }
+}
diff --git a/src/pulsecore/asyncq.h b/src/pulsecore/asyncq.h
new file mode 100644
index 00000000..e6847ab8
--- /dev/null
+++ b/src/pulsecore/asyncq.h
@@ -0,0 +1,65 @@
+#ifndef foopulseasyncqhfoo
+#define foopulseasyncqhfoo
+
+/***
+ 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.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <sys/types.h>
+#include <pulse/def.h>
+#include <pulsecore/macro.h>
+
+/* A simple, asynchronous, lock-free (if requested also wait-free)
+ * queue. Not multiple-reader/multiple-writer safe. If that is
+ * required both sides can be protected by a mutex each. --- Which is
+ * not a bad thing in most cases, since this queue is intended for
+ * communication between a normal thread and a single real-time
+ * thread. Only the real-time side needs to be lock-free/wait-free.
+ *
+ * If the queue is full and another entry shall be pushed, or when the
+ * queue is empty and another entry shall be popped and the "wait"
+ * argument is non-zero, the queue will block on a UNIX FIFO object --
+ * that will probably require locking on the kernel side -- which
+ * however is probably not problematic, because we do it only on
+ * starvation or overload in which case we have to block anyway. */
+
+typedef struct pa_asyncq pa_asyncq;
+
+pa_asyncq* pa_asyncq_new(unsigned size);
+void pa_asyncq_free(pa_asyncq* q, pa_free_cb_t free_cb);
+
+void* pa_asyncq_pop(pa_asyncq *q, pa_bool_t wait);
+int pa_asyncq_push(pa_asyncq *q, void *p, pa_bool_t wait);
+
+/* Similar to pa_asyncq_push(), but if the queue is full, postpone it
+ * locally and delay until pa_asyncq_before_poll_post() */
+void pa_asyncq_post(pa_asyncq*l, void *p);
+
+/* For the reading side */
+int pa_asyncq_read_fd(pa_asyncq *q);
+int pa_asyncq_read_before_poll(pa_asyncq *a);
+void pa_asyncq_read_after_poll(pa_asyncq *a);
+
+/* For the writing side */
+int pa_asyncq_write_fd(pa_asyncq *q);
+void pa_asyncq_write_before_poll(pa_asyncq *a);
+void pa_asyncq_write_after_poll(pa_asyncq *a);
+
+#endif
diff --git a/src/pulsecore/atomic.h b/src/pulsecore/atomic.h
index 8d608b5b..a91c4d56 100644
--- a/src/pulsecore/atomic.h
+++ b/src/pulsecore/atomic.h
@@ -1,65 +1,452 @@
#ifndef foopulseatomichfoo
#define foopulseatomichfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
+ Copyright 2006-2008 Lennart Poettering
+ Copyright 2008 Nokia Corporation
+
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.
***/
-#include <atomic_ops.h>
-
-/* atomic_ops guarantees us that sizeof(AO_t) == sizeof(void*).
+/*
+ * atomic_ops guarantees us that sizeof(AO_t) == sizeof(void*). It is
+ * not guaranteed however, that sizeof(AO_t) == sizeof(size_t).
+ * however very likely.
+ *
+ * For now we do only full memory barriers. Eventually we might want
+ * to support more elaborate memory barriers, in which case we will add
+ * suffixes to the function names.
*
- * It is not guaranteed however, that sizeof(AO_t) == sizeof(size_t).
- * however very likely. */
+ * On gcc >= 4.1 we use the builtin atomic functions. otherwise we use
+ * libatomic_ops
+ */
+#
+#ifndef PACKAGE
+#error "Please include config.h before including this file!"
+#endif
-typedef struct pa_atomic_int {
- volatile AO_t value;
-} pa_atomic_int_t;
+#ifdef HAVE_ATOMIC_BUILTINS
+
+/* __sync based implementation */
+
+typedef struct pa_atomic {
+ volatile int value;
+} pa_atomic_t;
#define PA_ATOMIC_INIT(v) { .value = (v) }
-/* For now we do only full memory barriers. Eventually we might want
- * to support more elaborate memory barriers, in which case we will add
- * suffixes to the function names */
+static inline int pa_atomic_load(const pa_atomic_t *a) {
+ __sync_synchronize();
+ return a->value;
+}
+
+static inline void pa_atomic_store(pa_atomic_t *a, int i) {
+ a->value = i;
+ __sync_synchronize();
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_add(pa_atomic_t *a, int i) {
+ return __sync_fetch_and_add(&a->value, i);
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_sub(pa_atomic_t *a, int i) {
+ return __sync_fetch_and_sub(&a->value, i);
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_inc(pa_atomic_t *a) {
+ return pa_atomic_add(a, 1);
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_dec(pa_atomic_t *a) {
+ return pa_atomic_sub(a, 1);
+}
+
+/* Returns non-zero when the operation was successful. */
+static inline int pa_atomic_cmpxchg(pa_atomic_t *a, int old_i, int new_i) {
+ return __sync_bool_compare_and_swap(&a->value, old_i, new_i);
+}
+
+typedef struct pa_atomic_ptr {
+ volatile unsigned long value;
+} pa_atomic_ptr_t;
+
+#define PA_ATOMIC_PTR_INIT(v) { .value = (long) (v) }
+
+static inline void* pa_atomic_ptr_load(const pa_atomic_ptr_t *a) {
+ __sync_synchronize();
+ return (void*) a->value;
+}
+
+static inline void pa_atomic_ptr_store(pa_atomic_ptr_t *a, void *p) {
+ a->value = (unsigned long) p;
+ __sync_synchronize();
+}
+
+static inline int pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) {
+ return __sync_bool_compare_and_swap(&a->value, (long) old_p, (long) new_p);
+}
+
+#elif defined(__GNUC__) && (defined(__amd64__) || defined(__x86_64__))
+
+#error "The native atomic operations implementation for AMD64 has not been tested. libatomic_ops is known to not work properly on AMD64 and your gcc version is too old for the gcc-builtin atomic ops support. You have three options now: make the native atomic operations implementation for AMD64 work, fix libatomic_ops, or upgrade your GCC."
+
+/* Addapted from glibc */
+
+typedef struct pa_atomic {
+ volatile int value;
+} pa_atomic_t;
+
+#define PA_ATOMIC_INIT(v) { .value = (v) }
+
+static inline int pa_atomic_load(const pa_atomic_t *a) {
+ return a->value;
+}
+
+static inline void pa_atomic_store(pa_atomic_t *a, int i) {
+ a->value = i;
+}
+
+static inline int pa_atomic_add(pa_atomic_t *a, int i) {
+ int result;
+
+ __asm __volatile ("lock; xaddl %0, %1"
+ : "=r" (result), "=m" (a->value)
+ : "0" (i), "m" (a->value));
+
+ return result;
+}
+
+static inline int pa_atomic_sub(pa_atomic_t *a, int i) {
+ return pa_atomic_add(a, -i);
+}
+
+static inline int pa_atomic_inc(pa_atomic_t *a) {
+ return pa_atomic_add(a, 1);
+}
+
+static inline int pa_atomic_dec(pa_atomic_t *a) {
+ return pa_atomic_sub(a, 1);
+}
+
+static inline int pa_atomic_cmpxchg(pa_atomic_t *a, int old_i, int new_i) {
+ int result;
+
+ __asm__ __volatile__ ("lock; cmpxchgl %2, %1"
+ : "=a" (result), "=m" (a->value)
+ : "r" (new_i), "m" (a->value), "0" (old_i));
+
+ return result == oldval;
+}
+
+typedef struct pa_atomic_ptr {
+ volatile unsigned long value;
+} pa_atomic_ptr_t;
+
+#define PA_ATOMIC_PTR_INIT(v) { .value = (long) (v) }
+
+static inline void* pa_atomic_ptr_load(const pa_atomic_ptr_t *a) {
+ return (void*) a->value;
+}
+
+static inline void pa_atomic_ptr_store(pa_atomic_ptr_t *a, void *p) {
+ a->value = (unsigned long) p;
+}
+
+static inline int pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) {
+ void *result;
+
+ __asm__ __volatile__ ("lock; cmpxchgq %q2, %1"
+ : "=a" (result), "=m" (a->value)
+ : "r" (new_p), "m" (a->value), "0" (old_p));
+
+ return result;
+}
+
+#elif defined(ATOMIC_ARM_INLINE_ASM)
+
+/*
+ These should only be enabled if we have ARMv6 or better.
+*/
+
+typedef struct pa_atomic {
+ volatile int value;
+} pa_atomic_t;
+
+#define PA_ATOMIC_INIT(v) { .value = (v) }
+
+static inline void pa_memory_barrier(void) {
+#ifdef ATOMIC_ARM_MEMORY_BARRIER_ENABLED
+ asm volatile ("mcr p15, 0, r0, c7, c10, 5 @ dmb");
+#endif
+}
+
+static inline int pa_atomic_load(const pa_atomic_t *a) {
+ pa_memory_barrier();
+ return a->value;
+}
+
+static inline void pa_atomic_store(pa_atomic_t *a, int i) {
+ a->value = i;
+ pa_memory_barrier();
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_add(pa_atomic_t *a, int i) {
+ unsigned long not_exclusive;
+ int new_val, old_val;
+
+ pa_memory_barrier();
+ do {
+ asm volatile ("ldrex %0, [%3]\n"
+ "add %2, %0, %4\n"
+ "strex %1, %2, [%3]\n"
+ : "=&r" (old_val), "=&r" (not_exclusive), "=&r" (new_val)
+ : "r" (&a->value), "Ir" (i)
+ : "cc");
+ } while(not_exclusive);
+ pa_memory_barrier();
+
+ return old_val;
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_sub(pa_atomic_t *a, int i) {
+ unsigned long not_exclusive;
+ int new_val, old_val;
+
+ pa_memory_barrier();
+ do {
+ asm volatile ("ldrex %0, [%3]\n"
+ "sub %2, %0, %4\n"
+ "strex %1, %2, [%3]\n"
+ : "=&r" (old_val), "=&r" (not_exclusive), "=&r" (new_val)
+ : "r" (&a->value), "Ir" (i)
+ : "cc");
+ } while(not_exclusive);
+ pa_memory_barrier();
+
+ return old_val;
+}
+
+static inline int pa_atomic_inc(pa_atomic_t *a) {
+ return pa_atomic_add(a, 1);
+}
+
+static inline int pa_atomic_dec(pa_atomic_t *a) {
+ return pa_atomic_sub(a, 1);
+}
+
+static inline int pa_atomic_cmpxchg(pa_atomic_t *a, int old_i, int new_i) {
+ unsigned long not_equal, not_exclusive;
+
+ pa_memory_barrier();
+ do {
+ asm volatile ("ldrex %0, [%2]\n"
+ "subs %0, %0, %3\n"
+ "mov %1, %0\n"
+ "strexeq %0, %4, [%2]\n"
+ : "=&r" (not_exclusive), "=&r" (not_equal)
+ : "r" (&a->value), "Ir" (old_i), "r" (new_i)
+ : "cc");
+ } while(not_exclusive && !not_equal);
+ pa_memory_barrier();
+
+ return !not_equal;
+}
+
+typedef struct pa_atomic_ptr {
+ volatile unsigned long value;
+} pa_atomic_ptr_t;
+
+#define PA_ATOMIC_PTR_INIT(v) { .value = (long) (v) }
+
+static inline void* pa_atomic_ptr_load(const pa_atomic_ptr_t *a) {
+ pa_memory_barrier();
+ return (void*) a->value;
+}
+
+static inline void pa_atomic_ptr_store(pa_atomic_ptr_t *a, void *p) {
+ a->value = (unsigned long) p;
+ pa_memory_barrier();
+}
+
+static inline int pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) {
+ unsigned long not_equal, not_exclusive;
+
+ pa_memory_barrier();
+ do {
+ asm volatile ("ldrex %0, [%2]\n"
+ "subs %0, %0, %3\n"
+ "mov %1, %0\n"
+ "strexeq %0, %4, [%2]\n"
+ : "=&r" (not_exclusive), "=&r" (not_equal)
+ : "r" (&a->value), "Ir" (old_p), "r" (new_p)
+ : "cc");
+ } while(not_exclusive && !not_equal);
+ pa_memory_barrier();
+
+ return !not_equal;
+}
+
+#elif defined(ATOMIC_ARM_LINUX_HELPERS)
+
+/* See file arch/arm/kernel/entry-armv.S in your kernel sources for more
+ information about these functions. The arm kernel helper functions first
+ appeared in 2.6.16.
+ Apply --disable-atomic-arm-linux-helpers flag to confugure if you prefere
+ inline asm implementation or you have an obsolete Linux kernel.
+*/
+/* Memory barrier */
+typedef void (__kernel_dmb_t)(void);
+#define __kernel_dmb (*(__kernel_dmb_t *)0xffff0fa0)
+
+static inline void pa_memory_barrier(void) {
+#ifndef ATOMIC_ARM_MEMORY_BARRIER_ENABLED
+ __kernel_dmb();
+#endif
+}
+
+/* Atomic exchange (__kernel_cmpxchg_t contains memory barriers if needed) */
+typedef int (__kernel_cmpxchg_t)(int oldval, int newval, volatile int *ptr);
+#define __kernel_cmpxchg (*(__kernel_cmpxchg_t *)0xffff0fc0)
+
+/* This is just to get rid of all warnings */
+typedef int (__kernel_cmpxchg_u_t)(unsigned long oldval, unsigned long newval, volatile unsigned long *ptr);
+#define __kernel_cmpxchg_u (*(__kernel_cmpxchg_u_t *)0xffff0fc0)
-static inline int pa_atomic_load(const pa_atomic_int_t *a) {
+typedef struct pa_atomic {
+ volatile int value;
+} pa_atomic_t;
+
+#define PA_ATOMIC_INIT(v) { .value = (v) }
+
+static inline int pa_atomic_load(const pa_atomic_t *a) {
+ pa_memory_barrier();
+ return a->value;
+}
+
+static inline void pa_atomic_store(pa_atomic_t *a, int i) {
+ a->value = i;
+ pa_memory_barrier();
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_add(pa_atomic_t *a, int i) {
+ int old_val;
+ do {
+ old_val = a->value;
+ } while(__kernel_cmpxchg(old_val, old_val + i, &a->value));
+ return old_val;
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_sub(pa_atomic_t *a, int i) {
+ int old_val;
+ do {
+ old_val = a->value;
+ } while(__kernel_cmpxchg(old_val, old_val - i, &a->value));
+ return old_val;
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_inc(pa_atomic_t *a) {
+ return pa_atomic_add(a, 1);
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_dec(pa_atomic_t *a) {
+ return pa_atomic_sub(a, 1);
+}
+
+/* Returns non-zero when the operation was successful. */
+static inline int pa_atomic_cmpxchg(pa_atomic_t *a, int old_i, int new_i) {
+ pa_bool_t failed;
+ do {
+ failed = !!__kernel_cmpxchg(old_i, new_i, &a->value);
+ } while(failed && a->value == old_i);
+ return !failed;
+}
+
+typedef struct pa_atomic_ptr {
+ volatile unsigned long value;
+} pa_atomic_ptr_t;
+
+#define PA_ATOMIC_PTR_INIT(v) { .value = (unsigned long) (v) }
+
+static inline void* pa_atomic_ptr_load(const pa_atomic_ptr_t *a) {
+ pa_memory_barrier();
+ return (void*) a->value;
+}
+
+static inline void pa_atomic_ptr_store(pa_atomic_ptr_t *a, void *p) {
+ a->value = (unsigned long) p;
+ pa_memory_barrier();
+}
+
+static inline int pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) {
+ pa_bool_t failed;
+ do {
+ failed = !!__kernel_cmpxchg_u((unsigned long) old_p, (unsigned long) new_p, &a->value);
+ } while(failed && a->value == (unsigned long) old_p);
+ return !failed;
+}
+
+#else
+
+/* libatomic_ops based implementation */
+
+#include <atomic_ops.h>
+
+typedef struct pa_atomic {
+ volatile AO_t value;
+} pa_atomic_t;
+
+#define PA_ATOMIC_INIT(v) { .value = (v) }
+
+static inline int pa_atomic_load(const pa_atomic_t *a) {
return (int) AO_load_full((AO_t*) &a->value);
}
-static inline void pa_atomic_store(pa_atomic_int_t *a, int i) {
+static inline void pa_atomic_store(pa_atomic_t *a, int i) {
AO_store_full(&a->value, (AO_t) i);
}
-static inline int pa_atomic_add(pa_atomic_int_t *a, int i) {
+static inline int pa_atomic_add(pa_atomic_t *a, int i) {
return AO_fetch_and_add_full(&a->value, (AO_t) i);
}
-static inline int pa_atomic_inc(pa_atomic_int_t *a) {
+static inline int pa_atomic_sub(pa_atomic_t *a, int i) {
+ return AO_fetch_and_add_full(&a->value, (AO_t) -i);
+}
+
+static inline int pa_atomic_inc(pa_atomic_t *a) {
return AO_fetch_and_add1_full(&a->value);
}
-static inline int pa_atomic_dec(pa_atomic_int_t *a) {
+static inline int pa_atomic_dec(pa_atomic_t *a) {
return AO_fetch_and_sub1_full(&a->value);
}
-static inline int pa_atomic_cmpxchg(pa_atomic_int_t *a, int old_i, int new_i) {
+static inline int pa_atomic_cmpxchg(pa_atomic_t *a, int old_i, int new_i) {
return AO_compare_and_swap_full(&a->value, old_i, new_i);
}
@@ -67,6 +454,8 @@ typedef struct pa_atomic_ptr {
volatile AO_t value;
} pa_atomic_ptr_t;
+#define PA_ATOMIC_PTR_INIT(v) { .value = (AO_t) (v) }
+
static inline void* pa_atomic_ptr_load(const pa_atomic_ptr_t *a) {
return (void*) AO_load_full((AO_t*) &a->value);
}
@@ -80,3 +469,5 @@ static inline int pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* n
}
#endif
+
+#endif
diff --git a/src/pulsecore/authkey-prop.c b/src/pulsecore/authkey-prop.c
index 7eda1e49..a953bf7d 100644
--- a/src/pulsecore/authkey-prop.c
+++ b/src/pulsecore/authkey-prop.c
@@ -1,86 +1,103 @@
-/* $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.
***/
-#include <assert.h>
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
#include <string.h>
#include <pulse/xmalloc.h>
#include <pulsecore/props.h>
+#include <pulsecore/macro.h>
#include <pulsecore/log.h>
+#include <pulsecore/refcnt.h>
#include "authkey-prop.h"
struct authkey_data {
- int ref;
+ PA_REFCNT_DECLARE;
size_t length;
};
int pa_authkey_prop_get(pa_core *c, const char *name, void *data, size_t len) {
struct authkey_data *a;
- assert(c && name && data && len > 0);
-
+
+ pa_assert(c);
+ pa_assert(name);
+ pa_assert(data);
+ pa_assert(len > 0);
+
if (!(a = pa_property_get(c, name)))
return -1;
- assert(a->length == len);
- memcpy(data, a+1, len);
+ pa_assert(a->length == len);
+ memcpy(data, (uint8_t*) a + PA_ALIGN(sizeof(struct authkey_data)), len);
+
return 0;
}
int pa_authkey_prop_put(pa_core *c, const char *name, const void *data, size_t len) {
struct authkey_data *a;
- assert(c && name);
+
+ pa_assert(c);
+ pa_assert(name);
if (pa_property_get(c, name))
return -1;
- a = pa_xmalloc(sizeof(struct authkey_data) + len);
- a->ref = 1;
+ a = pa_xmalloc(PA_ALIGN(sizeof(struct authkey_data)) + len);
+ PA_REFCNT_INIT(a);
a->length = len;
- memcpy(a+1, data, len);
+ memcpy((uint8_t*) a + PA_ALIGN(sizeof(struct authkey_data)), data, len);
pa_property_set(c, name, a);
-
+
return 0;
}
void pa_authkey_prop_ref(pa_core *c, const char *name) {
struct authkey_data *a;
- assert(c && name);
- a = pa_property_get(c, name);
- assert(a && a->ref >= 1);
+ pa_assert(c);
+ pa_assert(name);
- a->ref++;
+ a = pa_property_get(c, name);
+ pa_assert(a);
+ pa_assert(PA_REFCNT_VALUE(a) >= 1);
+ PA_REFCNT_INC(a);
}
void pa_authkey_prop_unref(pa_core *c, const char *name) {
struct authkey_data *a;
- assert(c && name);
+
+ pa_assert(c);
+ pa_assert(name);
a = pa_property_get(c, name);
- assert(a && a->ref >= 1);
+ pa_assert(a);
+ pa_assert(PA_REFCNT_VALUE(a) >= 1);
- if (!(--a->ref)) {
+ if (PA_REFCNT_DEC(a) <= 0) {
pa_property_remove(c, name);
pa_xfree(a);
}
diff --git a/src/pulsecore/authkey-prop.h b/src/pulsecore/authkey-prop.h
index b1da28be..de02d2aa 100644
--- a/src/pulsecore/authkey-prop.h
+++ b/src/pulsecore/authkey-prop.h
@@ -1,21 +1,21 @@
#ifndef fooauthkeyprophfoo
#define fooauthkeyprophfoo
-/* $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
diff --git a/src/pulsecore/authkey.c b/src/pulsecore/authkey.c
index 87631ca5..f3f40f80 100644
--- a/src/pulsecore/authkey.c
+++ b/src/pulsecore/authkey.c
@@ -1,18 +1,19 @@
-/* $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.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
@@ -23,7 +24,6 @@
#include <config.h>
#endif
-#include <assert.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
@@ -40,21 +40,25 @@
#include <pulsecore/core-util.h>
#include <pulsecore/log.h>
#include <pulsecore/random.h>
+#include <pulsecore/macro.h>
#include "authkey.h"
/* Generate a new authorization key, store it in file fd and return it in *data */
static int generate(int fd, void *ret_data, size_t length) {
ssize_t r;
- assert(fd >= 0 && ret_data && length);
+
+ pa_assert(fd >= 0);
+ pa_assert(ret_data);
+ pa_assert(length > 0);
pa_random(ret_data, length);
lseek(fd, 0, SEEK_SET);
- ftruncate(fd, 0);
+ (void) ftruncate(fd, 0);
if ((r = pa_loop_write(fd, ret_data, length, NULL)) < 0 || (size_t) r != length) {
- pa_log("failed to write cookie file: %s", pa_cstrerror(errno));
+ pa_log("Failed to write cookie file: %s", pa_cstrerror(errno));
return -1;
}
@@ -65,6 +69,10 @@ static int generate(int fd, void *ret_data, size_t length) {
#define O_BINARY 0
#endif
+#ifndef O_NOCTTY
+#define O_NOCTTY 0
+#endif
+
/* Load an euthorization cookie from file fn and store it in data. If
* the cookie file doesn't exist, create it */
static int load(const char *fn, void *data, size_t length) {
@@ -72,11 +80,15 @@ static int load(const char *fn, void *data, size_t length) {
int writable = 1;
int unlock = 0, ret = -1;
ssize_t r;
- assert(fn && data && length);
- if ((fd = open(fn, O_RDWR|O_CREAT|O_BINARY, S_IRUSR|S_IWUSR)) < 0) {
- if (errno != EACCES || (fd = open(fn, O_RDONLY|O_BINARY)) < 0) {
- pa_log("failed to open cookie file '%s': %s", fn, pa_cstrerror(errno));
+ pa_assert(fn);
+ pa_assert(data);
+ pa_assert(length > 0);
+
+ if ((fd = open(fn, O_RDWR|O_CREAT|O_BINARY|O_NOCTTY, S_IRUSR|S_IWUSR)) < 0) {
+
+ if (errno != EACCES || (fd = open(fn, O_RDONLY|O_BINARY|O_NOCTTY)) < 0) {
+ pa_log("Failed to open cookie file '%s': %s", fn, pa_cstrerror(errno));
goto finish;
} else
writable = 0;
@@ -85,32 +97,35 @@ static int load(const char *fn, void *data, size_t length) {
unlock = pa_lock_fd(fd, 1) >= 0;
if ((r = pa_loop_read(fd, data, length, NULL)) < 0) {
- pa_log("failed to read cookie file '%s': %s", fn, pa_cstrerror(errno));
+ pa_log("Failed to read cookie file '%s': %s", fn, pa_cstrerror(errno));
goto finish;
}
if ((size_t) r != length) {
- pa_log_debug("got %d bytes from cookie file '%s', expected %d", (int)r, fn, (int)length);
-
+ pa_log_debug("Got %d bytes from cookie file '%s', expected %d", (int) r, fn, (int) length);
+
if (!writable) {
- pa_log("unable to write cookie to read only file");
+ pa_log("Unable to write cookie to read only file");
goto finish;
}
-
+
if (generate(fd, data, length) < 0)
goto finish;
}
ret = 0;
-
+
finish:
if (fd >= 0) {
-
+
if (unlock)
pa_lock_fd(fd, 0);
-
- close(fd);
+
+ if (pa_close(fd) < 0) {
+ pa_log_warn("Failed to close cookie file: %s", pa_cstrerror(errno));
+ ret = -1;
+ }
}
return ret;
@@ -120,13 +135,12 @@ finish:
int pa_authkey_load(const char *path, void *data, size_t length) {
int ret;
- assert(path && data && length);
+ pa_assert(path);
+ pa_assert(data);
+ pa_assert(length > 0);
- ret = load(path, data, length);
-
- if (ret < 0)
- pa_log("Failed to load authorization key '%s': %s", path,
- (ret == -1) ? pa_cstrerror(errno) : "file corrupt");
+ if ((ret = load(path, data, length)) < 0)
+ pa_log("Failed to load authorization key '%s': %s", path, (ret < 0) ? pa_cstrerror(errno) : "File corrupt");
return ret;
}
@@ -134,7 +148,10 @@ int pa_authkey_load(const char *path, void *data, size_t length) {
/* If the specified file path starts with / return it, otherwise
* return path prepended with home directory */
static const char *normalize_path(const char *fn, char *s, size_t l) {
- assert(fn && s && l > 0);
+
+ pa_assert(fn);
+ pa_assert(s);
+ pa_assert(l > 0);
#ifndef OS_IS_WIN32
if (fn[0] != '/') {
@@ -142,13 +159,14 @@ static const char *normalize_path(const char *fn, char *s, size_t l) {
if (strlen(fn) < 3 || !isalpha(fn[0]) || fn[1] != ':' || fn[2] != '\\') {
#endif
char homedir[PATH_MAX];
+
if (!pa_get_home_dir(homedir, sizeof(homedir)))
return NULL;
-
+
#ifndef OS_IS_WIN32
- snprintf(s, l, "%s/%s", homedir, fn);
+ pa_snprintf(s, l, "%s/%s", homedir, fn);
#else
- snprintf(s, l, "%s\\%s", homedir, fn);
+ pa_snprintf(s, l, "%s\\%s", homedir, fn);
#endif
return s;
}
@@ -161,11 +179,14 @@ static const char *normalize_path(const char *fn, char *s, size_t l) {
int pa_authkey_load_auto(const char *fn, void *data, size_t length) {
char path[PATH_MAX];
const char *p;
- assert(fn && data && length);
+
+ pa_assert(fn);
+ pa_assert(data);
+ pa_assert(length > 0);
if (!(p = normalize_path(fn, path, sizeof(path))))
return -2;
-
+
return pa_authkey_load(p, data, length);
}
@@ -176,33 +197,39 @@ int pa_authkey_save(const char *fn, const void *data, size_t length) {
ssize_t r;
char path[PATH_MAX];
const char *p;
- assert(fn && data && length);
+
+ pa_assert(fn);
+ pa_assert(data);
+ pa_assert(length > 0);
if (!(p = normalize_path(fn, path, sizeof(path))))
return -2;
- if ((fd = open(p, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR)) < 0) {
- pa_log("failed to open cookie file '%s': %s", fn, pa_cstrerror(errno));
+ if ((fd = open(p, O_RDWR|O_CREAT|O_NOCTTY, S_IRUSR|S_IWUSR)) < 0) {
+ pa_log("Failed to open cookie file '%s': %s", fn, pa_cstrerror(errno));
goto finish;
}
unlock = pa_lock_fd(fd, 1) >= 0;
if ((r = pa_loop_write(fd, data, length, NULL)) < 0 || (size_t) r != length) {
- pa_log("failed to read cookie file '%s': %s", fn, pa_cstrerror(errno));
+ pa_log("Failed to read cookie file '%s': %s", fn, pa_cstrerror(errno));
goto finish;
}
ret = 0;
-
+
finish:
if (fd >= 0) {
-
+
if (unlock)
pa_lock_fd(fd, 0);
-
- close(fd);
+
+ if (pa_close(fd) < 0) {
+ pa_log_warn("Failed to close cookie file: %s", pa_cstrerror(errno));
+ ret = -1;
+ }
}
return ret;
diff --git a/src/pulsecore/authkey.h b/src/pulsecore/authkey.h
index cc8a565c..8301db1e 100644
--- a/src/pulsecore/authkey.h
+++ b/src/pulsecore/authkey.h
@@ -1,21 +1,21 @@
#ifndef fooauthkeyhfoo
#define fooauthkeyhfoo
-/* $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.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
diff --git a/src/pulsecore/autoload.c b/src/pulsecore/autoload.c
index f6869097..26c294b2 100644
--- a/src/pulsecore/autoload.c
+++ b/src/pulsecore/autoload.c
@@ -1,18 +1,19 @@
-/* $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
@@ -23,7 +24,6 @@
#include <config.h>
#endif
-#include <assert.h>
#include <stdlib.h>
#include <string.h>
@@ -33,13 +33,14 @@
#include <pulsecore/memchunk.h>
#include <pulsecore/sound-file.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
#include <pulsecore/core-scache.h>
#include <pulsecore/core-subscribe.h>
#include "autoload.h"
static void entry_free(pa_autoload_entry *e) {
- assert(e);
+ pa_assert(e);
pa_subscription_post(e->core, PA_SUBSCRIPTION_EVENT_AUTOLOAD|PA_SUBSCRIPTION_EVENT_REMOVE, PA_INVALID_INDEX);
pa_xfree(e->name);
pa_xfree(e->module);
@@ -48,7 +49,8 @@ static void entry_free(pa_autoload_entry *e) {
}
static void entry_remove_and_free(pa_autoload_entry *e) {
- assert(e && e->core);
+ pa_assert(e);
+ pa_assert(e->core);
pa_idxset_remove_by_data(e->core->autoload_idxset, e, NULL);
pa_hashmap_remove(e->core->autoload_hashmap, e->name);
@@ -57,21 +59,23 @@ static void entry_remove_and_free(pa_autoload_entry *e) {
static pa_autoload_entry* entry_new(pa_core *c, const char *name) {
pa_autoload_entry *e = NULL;
- assert(c && name);
-
+
+ pa_core_assert_ref(c);
+ pa_assert(name);
+
if (c->autoload_hashmap && (e = pa_hashmap_get(c->autoload_hashmap, name)))
return NULL;
-
- e = pa_xmalloc(sizeof(pa_autoload_entry));
+
+ e = pa_xnew(pa_autoload_entry, 1);
e->core = c;
e->name = pa_xstrdup(name);
e->module = e->argument = NULL;
e->in_action = 0;
-
+
if (!c->autoload_hashmap)
c->autoload_hashmap = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
- assert(c->autoload_hashmap);
-
+ pa_assert(c->autoload_hashmap);
+
pa_hashmap_put(c->autoload_hashmap, e->name, e);
if (!c->autoload_idxset)
@@ -79,30 +83,37 @@ static pa_autoload_entry* entry_new(pa_core *c, const char *name) {
pa_idxset_put(c->autoload_idxset, e, &e->index);
pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_AUTOLOAD|PA_SUBSCRIPTION_EVENT_NEW, e->index);
-
+
return e;
}
int pa_autoload_add(pa_core *c, const char*name, pa_namereg_type_t type, const char*module, const char *argument, uint32_t *idx) {
pa_autoload_entry *e = NULL;
- assert(c && name && module && (type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE));
-
+
+ pa_assert(c);
+ pa_assert(name);
+ pa_assert(module);
+ pa_assert(type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE);
+
if (!(e = entry_new(c, name)))
return -1;
-
+
e->module = pa_xstrdup(module);
e->argument = pa_xstrdup(argument);
e->type = type;
if (idx)
*idx = e->index;
-
+
return 0;
}
int pa_autoload_remove_by_name(pa_core *c, const char*name, pa_namereg_type_t type) {
pa_autoload_entry *e;
- assert(c && name && type);
+
+ pa_assert(c);
+ pa_assert(name);
+ pa_assert(type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE);
if (!c->autoload_hashmap || !(e = pa_hashmap_get(c->autoload_hashmap, name)) || e->type != type)
return -1;
@@ -113,7 +124,9 @@ int pa_autoload_remove_by_name(pa_core *c, const char*name, pa_namereg_type_t ty
int pa_autoload_remove_by_index(pa_core *c, uint32_t idx) {
pa_autoload_entry *e;
- assert(c && idx != PA_IDXSET_INVALID);
+
+ pa_assert(c);
+ pa_assert(idx != PA_IDXSET_INVALID);
if (!c->autoload_idxset || !(e = pa_idxset_get_by_index(c->autoload_idxset, idx)))
return -1;
@@ -125,7 +138,9 @@ int pa_autoload_remove_by_index(pa_core *c, uint32_t idx) {
void pa_autoload_request(pa_core *c, const char *name, pa_namereg_type_t type) {
pa_autoload_entry *e;
pa_module *m;
- assert(c && name);
+
+ pa_assert(c);
+ pa_assert(name);
if (!c->autoload_hashmap || !(e = pa_hashmap_get(c->autoload_hashmap, name)) || (e->type != type))
return;
@@ -139,7 +154,7 @@ void pa_autoload_request(pa_core *c, const char *name, pa_namereg_type_t type) {
if ((m = pa_module_load(c, e->module, e->argument)))
m->auto_unload = 1;
}
-
+
e->in_action = 0;
}
@@ -150,11 +165,12 @@ static void free_func(void *p, PA_GCC_UNUSED void *userdata) {
}
void pa_autoload_free(pa_core *c) {
+
if (c->autoload_hashmap) {
pa_hashmap_free(c->autoload_hashmap, free_func, NULL);
c->autoload_hashmap = NULL;
}
-
+
if (c->autoload_idxset) {
pa_idxset_free(c->autoload_idxset, NULL, NULL);
c->autoload_idxset = NULL;
@@ -163,8 +179,10 @@ void pa_autoload_free(pa_core *c) {
const pa_autoload_entry* pa_autoload_get_by_name(pa_core *c, const char*name, pa_namereg_type_t type) {
pa_autoload_entry *e;
- assert(c && name);
-
+
+ pa_core_assert_ref(c);
+ pa_assert(name);
+
if (!c->autoload_hashmap || !(e = pa_hashmap_get(c->autoload_hashmap, name)) || e->type != type)
return NULL;
@@ -173,8 +191,10 @@ const pa_autoload_entry* pa_autoload_get_by_name(pa_core *c, const char*name, pa
const pa_autoload_entry* pa_autoload_get_by_index(pa_core *c, uint32_t idx) {
pa_autoload_entry *e;
- assert(c && idx != PA_IDXSET_INVALID);
-
+
+ pa_core_assert_ref(c);
+ pa_assert(idx != PA_IDXSET_INVALID);
+
if (!c->autoload_idxset || !(e = pa_idxset_get_by_index(c->autoload_idxset, idx)))
return NULL;
diff --git a/src/pulsecore/autoload.h b/src/pulsecore/autoload.h
index 65bdd6da..3926351f 100644
--- a/src/pulsecore/autoload.h
+++ b/src/pulsecore/autoload.h
@@ -1,21 +1,21 @@
#ifndef fooautoloadhfoo
#define fooautoloadhfoo
-/* $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
@@ -35,8 +35,8 @@ typedef struct pa_autoload_entry {
uint32_t index;
char *name;
pa_namereg_type_t type; /* Type of the autoload entry */
- int in_action; /* Currently loaded */
- char *module, *argument;
+ int in_action; /* The module is currently being loaded */
+ char *module, *argument;
} pa_autoload_entry;
/* Add a new autoload entry of the given time, with the speicified
diff --git a/src/pulsecore/avahi-wrap.c b/src/pulsecore/avahi-wrap.c
index 80256a12..d5f40d83 100644
--- a/src/pulsecore/avahi-wrap.c
+++ b/src/pulsecore/avahi-wrap.c
@@ -1,29 +1,32 @@
-/* $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.
***/
-#include <assert.h>
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
#include <pulse/xmalloc.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
#include "avahi-wrap.h"
@@ -58,10 +61,10 @@ static pa_io_event_flags_t translate_io_flags(AvahiWatchEvent e) {
static void watch_callback(pa_mainloop_api*a, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata) {
AvahiWatch *w = userdata;
-
- assert(a);
- assert(e);
- assert(w);
+
+ pa_assert(a);
+ pa_assert(e);
+ pa_assert(w);
w->current_event = translate_io_flags_back(events);
w->callback(w, fd, w->current_event, w->userdata);
@@ -72,12 +75,10 @@ static AvahiWatch* watch_new(const AvahiPoll *api, int fd, AvahiWatchEvent event
pa_avahi_poll *p;
AvahiWatch *w;
- assert(api);
- assert(fd >= 0);
- assert(callback);
-
- p = api->userdata;
- assert(p);
+ pa_assert(api);
+ pa_assert(fd >= 0);
+ pa_assert(callback);
+ pa_assert_se(p = api->userdata);
w = pa_xnew(AvahiWatch, 1);
w->avahi_poll = p;
@@ -88,21 +89,21 @@ static AvahiWatch* watch_new(const AvahiPoll *api, int fd, AvahiWatchEvent event
return w;
}
-
+
static void watch_update(AvahiWatch *w, AvahiWatchEvent event) {
- assert(w);
+ pa_assert(w);
w->avahi_poll->mainloop->io_enable(w->io_event, translate_io_flags(event));
}
-
+
static AvahiWatchEvent watch_get_events(AvahiWatch *w) {
- assert(w);
+ pa_assert(w);
return w->current_event;
}
-
+
static void watch_free(AvahiWatch *w) {
- assert(w);
+ pa_assert(w);
w->avahi_poll->mainloop->io_free(w->io_event);
pa_xfree(w);
@@ -117,10 +118,10 @@ struct AvahiTimeout {
static void timeout_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) {
AvahiTimeout *t = userdata;
-
- assert(a);
- assert(e);
- assert(t);
+
+ pa_assert(a);
+ pa_assert(e);
+ pa_assert(t);
t->callback(t, t->userdata);
}
@@ -129,24 +130,22 @@ static AvahiTimeout* timeout_new(const AvahiPoll *api, const struct timeval *tv,
pa_avahi_poll *p;
AvahiTimeout *t;
- assert(api);
- assert(callback);
-
- p = api->userdata;
- assert(p);
+ pa_assert(api);
+ pa_assert(callback);
+ pa_assert_se(p = api->userdata);
t = pa_xnew(AvahiTimeout, 1);
t->avahi_poll = p;
t->callback = callback;
t->userdata = userdata;
-
+
t->time_event = tv ? p->mainloop->time_new(p->mainloop, tv, timeout_callback, t) : NULL;
return t;
}
-
+
static void timeout_update(AvahiTimeout *t, const struct timeval *tv) {
- assert(t);
+ pa_assert(t);
if (t->time_event && tv)
t->avahi_poll->mainloop->time_restart(t->time_event, tv);
@@ -157,9 +156,9 @@ static void timeout_update(AvahiTimeout *t, const struct timeval *tv) {
t->time_event = NULL;
}
}
-
+
static void timeout_free(AvahiTimeout *t) {
- assert(t);
+ pa_assert(t);
if (t->time_event)
t->avahi_poll->mainloop->time_free(t->time_event);
@@ -169,10 +168,10 @@ static void timeout_free(AvahiTimeout *t) {
AvahiPoll* pa_avahi_poll_new(pa_mainloop_api *m) {
pa_avahi_poll *p;
- assert(m);
-
+ pa_assert(m);
+
p = pa_xnew(pa_avahi_poll, 1);
-
+
p->api.userdata = p;
p->api.watch_new = watch_new;
p->api.watch_update = watch_update;
@@ -182,16 +181,15 @@ AvahiPoll* pa_avahi_poll_new(pa_mainloop_api *m) {
p->api.timeout_update = timeout_update;
p->api.timeout_free = timeout_free;
p->mainloop = m;
-
+
return &p->api;
}
void pa_avahi_poll_free(AvahiPoll *api) {
pa_avahi_poll *p;
- assert(api);
- p = api->userdata;
- assert(p);
-
+ pa_assert(api);
+ pa_assert_se(p = api->userdata);
+
pa_xfree(p);
}
diff --git a/src/pulsecore/avahi-wrap.h b/src/pulsecore/avahi-wrap.h
index d868fed4..7d8995bb 100644
--- a/src/pulsecore/avahi-wrap.h
+++ b/src/pulsecore/avahi-wrap.h
@@ -1,21 +1,21 @@
#ifndef fooavahiwrapperhfoo
#define fooavahiwrapperhfoo
-/* $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
diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c
index d7e4a75c..cbdb602a 100644
--- a/src/pulsecore/cli-command.c
+++ b/src/pulsecore/cli-command.c
@@ -1,18 +1,19 @@
-/* $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
@@ -25,9 +26,10 @@
#include <stdio.h>
#include <string.h>
-#include <assert.h>
#include <stdlib.h>
#include <errno.h>
+#include <unistd.h>
+#include <ltdl.h>
#include <pulse/xmalloc.h>
@@ -55,52 +57,66 @@
struct command {
const char *name;
- int (*proc) (pa_core *c, pa_tokenizer*t, pa_strbuf *buf, int *fail);
+ int (*proc) (pa_core *c, pa_tokenizer*t, pa_strbuf *buf, pa_bool_t *fail);
const char *help;
unsigned args;
};
-#define INCLUDE_META ".include"
-#define FAIL_META ".fail"
-#define NOFAIL_META ".nofail"
+#define META_INCLUDE ".include"
+#define META_FAIL ".fail"
+#define META_NOFAIL ".nofail"
+#define META_IFEXISTS ".ifexists"
+#define META_ELSE ".else"
+#define META_ENDIF ".endif"
+
+enum {
+ IFSTATE_NONE = -1,
+ IFSTATE_FALSE = 0,
+ IFSTATE_TRUE = 1,
+};
/* Prototypes for all available commands */
-static int pa_cli_command_exit(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_help(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_modules(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_clients(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_sinks(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_sources(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_sink_inputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_source_outputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_kill_client(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_kill_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_kill_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_scache_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_scache_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_scache_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_scache_load_dir(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_autoload_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_autoload_add(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_autoload_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_list_props(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_move_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_move_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_vacuum(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
+static int pa_cli_command_exit(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_help(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_modules(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_clients(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_sinks(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_sources(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_sink_inputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_source_outputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_describe(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_sink_input_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_kill_client(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_kill_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_kill_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_scache_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_scache_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_scache_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_scache_load_dir(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_autoload_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_autoload_add(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_autoload_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_list_props(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_move_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_move_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_vacuum(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_suspend_sink(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_suspend_source(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_suspend(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
/* A method table for all available commands */
@@ -117,13 +133,15 @@ static const struct command commands[] = {
{ "info", pa_cli_command_info, "Show comprehensive status", 1 },
{ "ls", pa_cli_command_info, NULL, 1 },
{ "list", pa_cli_command_info, NULL, 1 },
- { "load-module", pa_cli_command_load, "Load a module (args: name, arguments)", 3},
- { "unload-module", pa_cli_command_unload, "Unload a module (args: index)", 2},
- { "set-sink-volume", pa_cli_command_sink_volume, "Set the volume of a sink (args: index|name, volume)", 3},
- { "set-sink-input-volume", pa_cli_command_sink_input_volume, "Set the volume of a sink input (args: index|name, volume)", 3},
+ { "load-module", pa_cli_command_load, "Load a module (args: name, arguments)", 3},
+ { "unload-module", pa_cli_command_unload, "Unload a module (args: index)", 2},
+ { "describe-module", pa_cli_command_describe, "Describe a module (arg: name)", 2},
+ { "set-sink-volume", pa_cli_command_sink_volume, "Set the volume of a sink (args: index|name, volume)", 3},
+ { "set-sink-input-volume", pa_cli_command_sink_input_volume, "Set the volume of a sink input (args: index, volume)", 3},
{ "set-source-volume", pa_cli_command_source_volume, "Set the volume of a source (args: index|name, volume)", 3},
- { "set-sink-mute", pa_cli_command_sink_mute, "Set the mute switch of a sink (args: index|name, mute)", 3},
- { "set-source-mute", pa_cli_command_source_mute, "Set the mute switch of a source (args: index|name, mute)", 3},
+ { "set-sink-mute", pa_cli_command_sink_mute, "Set the mute switch of a sink (args: index|name, bool)", 3},
+ { "set-sink-input-mute", pa_cli_command_sink_input_mute, "Set the mute switch of a sink input (args: index, bool)", 3},
+ { "set-source-mute", pa_cli_command_source_mute, "Set the mute switch of a source (args: index|name, bool)", 3},
{ "set-default-sink", pa_cli_command_sink_default, "Set the default sink (args: index|name)", 2},
{ "set-default-source", pa_cli_command_source_default, "Set the default source (args: index|name)", 2},
{ "kill-client", pa_cli_command_kill_client, "Kill a client (args: index)", 2},
@@ -137,15 +155,18 @@ static const struct command commands[] = {
{ "load-sample-dir-lazy", pa_cli_command_scache_load_dir, "Lazily load all files in a directory into the sample cache (args: pathname)", 2},
{ "play-file", pa_cli_command_play_file, "Play a sound file (args: filename, sink|index)", 3},
{ "list-autoload", pa_cli_command_autoload_list, "List autoload entries", 1},
- { "add-autoload-sink", pa_cli_command_autoload_add, "Add autoload entry for a sink (args: sink, module name, arguments)", 4},
- { "add-autoload-source", pa_cli_command_autoload_add, "Add autoload entry for a source (args: source, module name, arguments)", 4},
- { "remove-autoload-sink", pa_cli_command_autoload_remove, "Remove autoload entry for a sink (args: name)", 2},
- { "remove-autoload-source", pa_cli_command_autoload_remove, "Remove autoload entry for a source (args: name)", 2},
+ { "add-autoload-sink", pa_cli_command_autoload_add, NULL /*"Add autoload entry for a sink (args: sink, module name, arguments)"*/, 4},
+ { "add-autoload-source", pa_cli_command_autoload_add, NULL /*"Add autoload entry for a source (args: source, module name, arguments)"*/, 4},
+ { "remove-autoload-sink", pa_cli_command_autoload_remove, NULL /*"Remove autoload entry for a sink (args: name)"*/, 2},
+ { "remove-autoload-source", pa_cli_command_autoload_remove, NULL /*"Remove autoload entry for a source (args: name)"*/, 2},
{ "dump", pa_cli_command_dump, "Dump daemon configuration", 1},
{ "list-props", pa_cli_command_list_props, NULL, 1},
{ "move-sink-input", pa_cli_command_move_sink_input, "Move sink input to another sink (args: index, sink)", 3},
{ "move-source-output", pa_cli_command_move_source_output, "Move source output to another source (args: index, source)", 3},
{ "vacuum", pa_cli_command_vacuum, NULL, 1},
+ { "suspend-sink", pa_cli_command_suspend_sink, "Suspend sink (args: index|name, bool)", 3},
+ { "suspend-source", pa_cli_command_suspend_source, "Suspend source (args: index|name, bool)", 3},
+ { "suspend", pa_cli_command_suspend, "Suspend all sinks and all sources (args: bool)", 2},
{ NULL, NULL, NULL, 0 }
};
@@ -161,88 +182,121 @@ static uint32_t parse_index(const char *n) {
return idx;
}
-static int pa_cli_command_exit(pa_core *c, pa_tokenizer *t, PA_GCC_UNUSED pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
- assert(c && c->mainloop && t);
+static int pa_cli_command_exit(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
c->mainloop->quit(c->mainloop, 0);
return 0;
}
-static int pa_cli_command_help(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+static int pa_cli_command_help(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const struct command*command;
- assert(c && t && buf);
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
pa_strbuf_puts(buf, "Available commands:\n");
-
+
for (command = commands; command->name; command++)
if (command->help)
pa_strbuf_printf(buf, " %-25s %s\n", command->name, command->help);
return 0;
}
-static int pa_cli_command_modules(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+static int pa_cli_command_modules(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
char *s;
- assert(c && t);
- s = pa_module_list_to_string(c);
- assert(s);
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
+ pa_assert_se(s = pa_module_list_to_string(c));
pa_strbuf_puts(buf, s);
pa_xfree(s);
return 0;
}
-static int pa_cli_command_clients(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+static int pa_cli_command_clients(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
char *s;
- assert(c && t);
- s = pa_client_list_to_string(c);
- assert(s);
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
+ pa_assert_se(s = pa_client_list_to_string(c));
pa_strbuf_puts(buf, s);
pa_xfree(s);
return 0;
}
-static int pa_cli_command_sinks(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+static int pa_cli_command_sinks(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
char *s;
- assert(c && t);
- s = pa_sink_list_to_string(c);
- assert(s);
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
+ pa_assert_se(s = pa_sink_list_to_string(c));
pa_strbuf_puts(buf, s);
pa_xfree(s);
return 0;
}
-static int pa_cli_command_sources(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+static int pa_cli_command_sources(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
char *s;
- assert(c && t);
- s = pa_source_list_to_string(c);
- assert(s);
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
+ pa_assert_se(s = pa_source_list_to_string(c));
pa_strbuf_puts(buf, s);
pa_xfree(s);
return 0;
}
-static int pa_cli_command_sink_inputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+static int pa_cli_command_sink_inputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
char *s;
- assert(c && t);
- s = pa_sink_input_list_to_string(c);
- assert(s);
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
+ pa_assert_se(s = pa_sink_input_list_to_string(c));
pa_strbuf_puts(buf, s);
pa_xfree(s);
return 0;
}
-static int pa_cli_command_source_outputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+static int pa_cli_command_source_outputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
char *s;
- assert(c && t);
- s = pa_source_output_list_to_string(c);
- assert(s);
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
+ pa_assert_se(s = pa_source_output_list_to_string(c));
pa_strbuf_puts(buf, s);
pa_xfree(s);
return 0;
}
-static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
char s[256];
const pa_mempool_stat *stat;
unsigned k;
+ const char *def_sink, *def_source;
static const char* const type_table[PA_MEMBLOCK_TYPE_MAX] = {
[PA_MEMBLOCK_POOL] = "POOL",
@@ -252,12 +306,14 @@ static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_G
[PA_MEMBLOCK_FIXED] = "FIXED",
[PA_MEMBLOCK_IMPORTED] = "IMPORTED",
};
-
- assert(c);
- assert(t);
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
stat = pa_mempool_get_stat(c->mempool);
-
+
pa_strbuf_printf(buf, "Memory blocks currently allocated: %u, size: %s.\n",
(unsigned) pa_atomic_load(&stat->n_allocated),
pa_bytes_snprint(s, sizeof(s), (size_t) pa_atomic_load(&stat->allocated_size)));
@@ -280,10 +336,12 @@ static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_G
pa_strbuf_printf(buf, "Default sample spec: %s\n",
pa_sample_spec_snprint(s, sizeof(s), &c->default_sample_spec));
+ def_sink = pa_namereg_get_default_sink_name(c);
+ def_source = pa_namereg_get_default_source_name(c);
pa_strbuf_printf(buf, "Default sink name: %s\n"
"Default source name: %s\n",
- pa_namereg_get_default_sink_name(c),
- pa_namereg_get_default_source_name(c));
+ def_sink ? def_sink : "none",
+ def_source ? def_source : "none");
for (k = 0; k < PA_MEMBLOCK_TYPE_MAX; k++)
pa_strbuf_printf(buf,
@@ -291,12 +349,16 @@ static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_G
type_table[k],
(unsigned) pa_atomic_load(&stat->n_allocated_by_type[k]),
(unsigned) pa_atomic_load(&stat->n_accumulated_by_type[k]));
-
+
return 0;
}
-static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
- assert(c && t);
+static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
pa_cli_command_stat(c, t, buf, fail);
pa_cli_command_modules(c, t, buf, fail);
pa_cli_command_sinks(c, t, buf, fail);
@@ -305,20 +367,24 @@ static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int
pa_cli_command_sink_inputs(c, t, buf, fail);
pa_cli_command_source_outputs(c, t, buf, fail);
pa_cli_command_scache_list(c, t, buf, fail);
- pa_cli_command_autoload_list(c, t, buf, fail);
+/* pa_cli_command_autoload_list(c, t, buf, fail); */
return 0;
}
-static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
pa_module *m;
const char *name;
- assert(c && t);
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
if (!(name = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify the module name and optionally arguments.\n");
return -1;
}
-
+
if (!(m = pa_module_load(c, name, pa_tokenizer_get(t, 2)))) {
pa_strbuf_puts(buf, "Module load failed.\n");
return -1;
@@ -327,12 +393,16 @@ static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_G
return 0;
}
-static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
pa_module *m;
uint32_t idx;
const char *i;
char *e;
- assert(c && t);
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
if (!(i = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify the module index.\n");
@@ -349,19 +419,63 @@ static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA
return 0;
}
-static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+static int pa_cli_command_describe(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+ const char *name;
+ pa_modinfo *i;
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
+ if (!(name = pa_tokenizer_get(t, 1))) {
+ pa_strbuf_puts(buf, "You need to specify the module name.\n");
+ return -1;
+ }
+
+ if ((i = pa_modinfo_get_by_name(name))) {
+
+ pa_strbuf_printf(buf, "Name: %s\n", name);
+
+ if (!i->description && !i->version && !i->author && !i->usage)
+ pa_strbuf_printf(buf, "No module information available\n");
+ else {
+ if (i->version)
+ pa_strbuf_printf(buf, "Version: %s\n", i->version);
+ if (i->description)
+ pa_strbuf_printf(buf, "Description: %s\n", i->description);
+ if (i->author)
+ pa_strbuf_printf(buf, "Author: %s\n", i->author);
+ if (i->usage)
+ pa_strbuf_printf(buf, "Usage: %s\n", i->usage);
+ pa_strbuf_printf(buf, "Load Once: %s\n", pa_yes_no(i->load_once));
+ }
+
+ pa_modinfo_free(i);
+ } else
+ pa_strbuf_puts(buf, "Failed to open module.\n");
+
+ return 0;
+}
+
+static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n, *v;
pa_sink *sink;
uint32_t volume;
pa_cvolume cvolume;
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n");
return -1;
}
if (!(v = pa_tokenizer_get(t, 2))) {
- pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x100 is normal volume)\n");
+ pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x10000 is normal volume)\n");
return -1;
}
@@ -376,17 +490,22 @@ static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
}
pa_cvolume_set(&cvolume, sink->sample_spec.channels, volume);
- pa_sink_set_volume(sink, PA_MIXER_HARDWARE, &cvolume);
+ pa_sink_set_volume(sink, &cvolume);
return 0;
}
-static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n, *v;
pa_sink_input *si;
pa_volume_t volume;
pa_cvolume cvolume;
uint32_t idx;
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n");
return -1;
@@ -398,7 +517,7 @@ static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strb
}
if (!(v = pa_tokenizer_get(t, 2))) {
- pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x100 is normal volume)\n");
+ pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x10000 is normal volume)\n");
return -1;
}
@@ -417,19 +536,24 @@ static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strb
return 0;
}
-static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n, *v;
pa_source *source;
uint32_t volume;
pa_cvolume cvolume;
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n");
return -1;
}
if (!(v = pa_tokenizer_get(t, 2))) {
- pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x100 is normal volume)\n");
+ pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x10000 is normal volume)\n");
return -1;
}
@@ -444,15 +568,20 @@ static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *
}
pa_cvolume_set(&cvolume, source->sample_spec.channels, volume);
- pa_source_set_volume(source, PA_MIXER_HARDWARE, &cvolume);
+ pa_source_set_volume(source, &cvolume);
return 0;
}
-static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n, *m;
pa_sink *sink;
int mute;
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n");
return -1;
@@ -463,7 +592,7 @@ static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf,
return -1;
}
- if (pa_atoi(m, &mute) < 0) {
+ if ((mute = pa_parse_boolean(m)) < 0) {
pa_strbuf_puts(buf, "Failed to parse mute switch.\n");
return -1;
}
@@ -473,15 +602,20 @@ static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf,
return -1;
}
- pa_sink_set_mute(sink, PA_MIXER_HARDWARE, mute);
+ pa_sink_set_mute(sink, mute);
return 0;
}
-static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n, *m;
pa_source *source;
int mute;
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n");
return -1;
@@ -492,7 +626,7 @@ static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
return -1;
}
- if (pa_atoi(m, &mute) < 0) {
+ if ((mute = pa_parse_boolean(m)) < 0) {
pa_strbuf_puts(buf, "Failed to parse mute switch.\n");
return -1;
}
@@ -502,13 +636,57 @@ static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
return -1;
}
- pa_source_set_mute(source, PA_MIXER_HARDWARE, mute);
+ pa_source_set_mute(source, mute);
+ return 0;
+}
+
+static int pa_cli_command_sink_input_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+ const char *n, *v;
+ pa_sink_input *si;
+ uint32_t idx;
+ int mute;
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
+ if (!(n = pa_tokenizer_get(t, 1))) {
+ pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n");
+ return -1;
+ }
+
+ if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
+ pa_strbuf_puts(buf, "Failed to parse index.\n");
+ return -1;
+ }
+
+ if (!(v = pa_tokenizer_get(t, 2))) {
+ pa_strbuf_puts(buf, "You need to specify a mute switch setting (0/1).\n");
+ return -1;
+ }
+
+ if ((mute = pa_parse_boolean(v)) < 0) {
+ pa_strbuf_puts(buf, "Failed to parse mute switch.\n");
+ return -1;
+ }
+
+ if (!(si = pa_idxset_get_by_index(c->sink_inputs, (uint32_t) idx))) {
+ pa_strbuf_puts(buf, "No sink input found with this index.\n");
+ return -1;
+ }
+
+ pa_sink_input_set_mute(si, mute);
return 0;
}
-static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n;
- assert(c && t);
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n");
@@ -519,9 +697,13 @@ static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *b
return 0;
}
-static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n;
- assert(c && t);
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n");
@@ -532,11 +714,15 @@ static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf
return 0;
}
-static int pa_cli_command_kill_client(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+static int pa_cli_command_kill_client(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n;
pa_client *client;
uint32_t idx;
- assert(c && t);
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a client by its index.\n");
@@ -557,11 +743,15 @@ static int pa_cli_command_kill_client(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
return 0;
}
-static int pa_cli_command_kill_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+static int pa_cli_command_kill_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n;
pa_sink_input *sink_input;
uint32_t idx;
- assert(c && t);
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n");
@@ -582,11 +772,15 @@ static int pa_cli_command_kill_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf
return 0;
}
-static int pa_cli_command_kill_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+static int pa_cli_command_kill_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n;
pa_source_output *source_output;
uint32_t idx;
- assert(c && t);
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a source output by its index.\n");
@@ -607,20 +801,30 @@ static int pa_cli_command_kill_source_output(pa_core *c, pa_tokenizer *t, pa_str
return 0;
}
-static int pa_cli_command_scache_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+static int pa_cli_command_scache_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
char *s;
- assert(c && t);
- s = pa_scache_list_to_string(c);
- assert(s);
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
+ pa_assert_se(s = pa_scache_list_to_string(c));
pa_strbuf_puts(buf, s);
pa_xfree(s);
+
return 0;
}
-static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n, *sink_name;
pa_sink *sink;
- assert(c && t && buf && fail);
+ uint32_t idx;
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
if (!(n = pa_tokenizer_get(t, 1)) || !(sink_name = pa_tokenizer_get(t, 2))) {
pa_strbuf_puts(buf, "You need to specify a sample name and a sink name.\n");
@@ -632,17 +836,23 @@ static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
return -1;
}
- if (pa_scache_play_item(c, n, sink, PA_VOLUME_NORM) < 0) {
+ if (pa_scache_play_item(c, n, sink, PA_VOLUME_NORM, NULL, &idx) < 0) {
pa_strbuf_puts(buf, "Failed to play sample.\n");
return -1;
}
+ pa_strbuf_printf(buf, "Playing on sink input #%i\n", idx);
+
return 0;
}
-static int pa_cli_command_scache_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_scache_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n;
- assert(c && t && buf && fail);
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a sample name.\n");
@@ -657,10 +867,14 @@ static int pa_cli_command_scache_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *
return 0;
}
-static int pa_cli_command_scache_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_scache_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *fname, *n;
int r;
- assert(c && t && buf && fail);
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
if (!(fname = pa_tokenizer_get(t, 2)) || !(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a file name and a sample name.\n");
@@ -678,9 +892,13 @@ static int pa_cli_command_scache_load(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
return 0;
}
-static int pa_cli_command_scache_load_dir(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_scache_load_dir(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *pname;
- assert(c && t && buf && fail);
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
if (!(pname = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a path name.\n");
@@ -695,10 +913,14 @@ static int pa_cli_command_scache_load_dir(pa_core *c, pa_tokenizer *t, pa_strbuf
return 0;
}
-static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *fname, *sink_name;
pa_sink *sink;
- assert(c && t && buf && fail);
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
if (!(fname = pa_tokenizer_get(t, 1)) || !(sink_name = pa_tokenizer_get(t, 2))) {
pa_strbuf_puts(buf, "You need to specify a file name and a sink name.\n");
@@ -714,9 +936,15 @@ static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf,
return pa_play_file(sink, fname, NULL);
}
-static int pa_cli_command_autoload_add(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_autoload_add(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *a, *b;
- assert(c && t && buf && fail);
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
+ pa_log_warn("Autoload will no longer be implemented by future versions of the PulseAudio server.");
if (!(a = pa_tokenizer_get(t, 1)) || !(b = pa_tokenizer_get(t, 2))) {
pa_strbuf_puts(buf, "You need to specify a device name, a filename or a module name and optionally module arguments\n");
@@ -724,14 +952,20 @@ static int pa_cli_command_autoload_add(pa_core *c, pa_tokenizer *t, pa_strbuf *b
}
pa_autoload_add(c, a, strstr(pa_tokenizer_get(t, 0), "sink") ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE, b, pa_tokenizer_get(t, 3), NULL);
-
+
return 0;
}
-static int pa_cli_command_autoload_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_autoload_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *name;
- assert(c && t && buf && fail);
-
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
+ pa_log_warn("Autoload will no longer be implemented by future versions of the PulseAudio server.");
+
if (!(name = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a device name\n");
return -1;
@@ -742,40 +976,58 @@ static int pa_cli_command_autoload_remove(pa_core *c, pa_tokenizer *t, pa_strbuf
return -1;
}
- return 0;
+ return 0;
}
-static int pa_cli_command_autoload_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+static int pa_cli_command_autoload_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
char *s;
- assert(c && t);
- s = pa_autoload_list_to_string(c);
- assert(s);
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
+ pa_log_warn("Autoload will no longer be implemented by future versions of the PulseAudio server.");
+
+ pa_assert_se(s = pa_autoload_list_to_string(c));
pa_strbuf_puts(buf, s);
pa_xfree(s);
+
return 0;
}
-static int pa_cli_command_list_props(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
- assert(c && t);
+static int pa_cli_command_list_props(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
pa_property_dump(c, buf);
return 0;
}
-static int pa_cli_command_vacuum(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
- assert(c);
- assert(t);
+static int pa_cli_command_vacuum(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
pa_mempool_vacuum(c->mempool);
-
+
return 0;
}
-static int pa_cli_command_move_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_move_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n, *k;
pa_sink_input *si;
pa_sink *sink;
uint32_t idx;
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n");
return -1;
@@ -801,19 +1053,24 @@ static int pa_cli_command_move_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf
return -1;
}
- if (pa_sink_input_move_to(si, sink, 0) < 0) {
+ if (pa_sink_input_move_to(si, sink) < 0) {
pa_strbuf_puts(buf, "Moved failed.\n");
return -1;
}
return 0;
}
-static int pa_cli_command_move_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_move_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n, *k;
pa_source_output *so;
pa_source *source;
uint32_t idx;
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a source output by its index.\n");
return -1;
@@ -846,7 +1103,105 @@ static int pa_cli_command_move_source_output(pa_core *c, pa_tokenizer *t, pa_str
return 0;
}
-static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+static int pa_cli_command_suspend_sink(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+ const char *n, *m;
+ pa_sink *sink;
+ int suspend;
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
+ if (!(n = pa_tokenizer_get(t, 1))) {
+ pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n");
+ return -1;
+ }
+
+ if (!(m = pa_tokenizer_get(t, 2))) {
+ pa_strbuf_puts(buf, "You need to specify a suspend switch setting (0/1).\n");
+ return -1;
+ }
+
+ if ((suspend = pa_parse_boolean(m)) < 0) {
+ pa_strbuf_puts(buf, "Failed to parse suspend switch.\n");
+ return -1;
+ }
+
+ if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK, 1))) {
+ pa_strbuf_puts(buf, "No sink found by this name or index.\n");
+ return -1;
+ }
+
+ pa_sink_suspend(sink, suspend);
+ return 0;
+}
+
+static int pa_cli_command_suspend_source(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+ const char *n, *m;
+ pa_source *source;
+ int suspend;
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
+ if (!(n = pa_tokenizer_get(t, 1))) {
+ pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n");
+ return -1;
+ }
+
+ if (!(m = pa_tokenizer_get(t, 2))) {
+ pa_strbuf_puts(buf, "You need to specify a suspend switch setting (0/1).\n");
+ return -1;
+ }
+
+ if ((suspend = pa_parse_boolean(m)) < 0) {
+ pa_strbuf_puts(buf, "Failed to parse suspend switch.\n");
+ return -1;
+ }
+
+ if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE, 1))) {
+ pa_strbuf_puts(buf, "No source found by this name or index.\n");
+ return -1;
+ }
+
+ pa_source_suspend(source, suspend);
+ return 0;
+}
+
+static int pa_cli_command_suspend(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+ const char *m;
+ int suspend;
+ int ret;
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
+ if (!(m = pa_tokenizer_get(t, 1))) {
+ pa_strbuf_puts(buf, "You need to specify a suspend switch setting (0/1).\n");
+ return -1;
+ }
+
+ if ((suspend = pa_parse_boolean(m)) < 0) {
+ pa_strbuf_puts(buf, "Failed to parse suspend switch.\n");
+ return -1;
+ }
+
+ ret = - (pa_sink_suspend_all(c, suspend) < 0);
+ if (pa_source_suspend_all(c, suspend) < 0)
+ ret = -1;
+
+ if (ret < 0)
+ pa_strbuf_puts(buf, "Failed to resume/suspend all sinks/sources.\n");
+
+ return 0;
+}
+
+static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
pa_module *m;
pa_sink *sink;
pa_source *source;
@@ -857,8 +1212,11 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_G
time_t now;
void *i;
pa_autoload_entry *a;
-
- assert(c && t);
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
time(&now);
@@ -868,7 +1226,6 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_G
pa_strbuf_printf(buf, "### Configuration dump generated at %s\n", ctime(&now));
#endif
-
for (m = pa_idxset_first(c->modules, &idx); m; m = pa_idxset_next(c->modules, &idx)) {
if (m->auto_unload)
continue;
@@ -884,7 +1241,7 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_G
nl = 0;
for (sink = pa_idxset_first(c->sinks, &idx); sink; sink = pa_idxset_next(c->sinks, &idx)) {
- if (sink->owner && sink->owner->auto_unload)
+ if (sink->module && sink->module->auto_unload)
continue;
if (!nl) {
@@ -892,12 +1249,13 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_G
nl = 1;
}
- pa_strbuf_printf(buf, "set-sink-volume %s 0x%03x\n", sink->name, pa_cvolume_avg(pa_sink_get_volume(sink, PA_MIXER_HARDWARE)));
- pa_strbuf_printf(buf, "set-sink-mute %s %d\n", sink->name, pa_sink_get_mute(sink, PA_MIXER_HARDWARE));
+ pa_strbuf_printf(buf, "set-sink-volume %s 0x%03x\n", sink->name, pa_cvolume_avg(pa_sink_get_volume(sink)));
+ pa_strbuf_printf(buf, "set-sink-mute %s %s\n", sink->name, pa_yes_no(pa_sink_get_mute(sink)));
+ pa_strbuf_printf(buf, "suspend-sink %s %s\n", sink->name, pa_yes_no(pa_sink_get_state(sink) == PA_SINK_SUSPENDED));
}
for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx)) {
- if (source->owner && source->owner->auto_unload)
+ if (source->module && source->module->auto_unload)
continue;
if (!nl) {
@@ -905,14 +1263,15 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_G
nl = 1;
}
- pa_strbuf_printf(buf, "set-source-volume %s 0x%03x\n", source->name, pa_cvolume_avg(pa_source_get_volume(source, PA_MIXER_HARDWARE)));
- pa_strbuf_printf(buf, "set-source-mute %s %d\n", source->name, pa_source_get_mute(source, PA_MIXER_HARDWARE));
+ pa_strbuf_printf(buf, "set-source-volume %s 0x%03x\n", source->name, pa_cvolume_avg(pa_source_get_volume(source)));
+ pa_strbuf_printf(buf, "set-source-mute %s %s\n", source->name, pa_yes_no(pa_source_get_mute(source)));
+ pa_strbuf_printf(buf, "suspend-source %s %s\n", source->name, pa_yes_no(pa_source_get_state(source) == PA_SOURCE_SUSPENDED));
}
if (c->autoload_hashmap) {
nl = 0;
-
+
i = NULL;
while ((a = pa_hashmap_iterate(c->autoload_hashmap, &i, NULL))) {
@@ -920,18 +1279,18 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_G
pa_strbuf_puts(buf, "\n");
nl = 1;
}
-
+
pa_strbuf_printf(buf, "add-autoload-%s %s %s", a->type == PA_NAMEREG_SINK ? "sink" : "source", a->name, a->module);
-
+
if (a->argument)
pa_strbuf_printf(buf, " %s", a->argument);
-
+
pa_strbuf_puts(buf, "\n");
}
}
nl = 0;
-
+
if ((p = pa_namereg_get_default_sink_name(c))) {
if (!nl) {
pa_strbuf_puts(buf, "\n");
@@ -953,27 +1312,90 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_G
return 0;
}
-int pa_cli_command_execute_line(pa_core *c, const char *s, pa_strbuf *buf, int *fail) {
+int pa_cli_command_execute_line_stateful(pa_core *c, const char *s, pa_strbuf *buf, pa_bool_t *fail, int *ifstate) {
const char *cs;
-
+
+ pa_assert(c);
+ pa_assert(s);
+ pa_assert(buf);
+
cs = s+strspn(s, whitespace);
if (*cs == '#' || !*cs)
return 0;
else if (*cs == '.') {
- if (!strcmp(cs, FAIL_META))
- *fail = 1;
- else if (!strcmp(cs, NOFAIL_META))
- *fail = 0;
+ if (!strcmp(cs, META_ELSE)) {
+ if (!ifstate || *ifstate == IFSTATE_NONE) {
+ pa_strbuf_printf(buf, "Meta command %s is not valid in this context\n", cs);
+ return -1;
+ } else if (*ifstate == IFSTATE_TRUE)
+ *ifstate = IFSTATE_FALSE;
+ else
+ *ifstate = IFSTATE_TRUE;
+ return 0;
+ } else if (!strcmp(cs, META_ENDIF)) {
+ if (!ifstate || *ifstate == IFSTATE_NONE) {
+ pa_strbuf_printf(buf, "Meta command %s is not valid in this context\n", cs);
+ return -1;
+ } else
+ *ifstate = IFSTATE_NONE;
+ return 0;
+ }
+ if (ifstate && *ifstate == IFSTATE_FALSE)
+ return 0;
+ if (!strcmp(cs, META_FAIL))
+ *fail = TRUE;
+ else if (!strcmp(cs, META_NOFAIL))
+ *fail = FALSE;
else {
size_t l;
l = strcspn(cs, whitespace);
- if (l == sizeof(INCLUDE_META)-1 && !strncmp(cs, INCLUDE_META, l)) {
+ if (l == sizeof(META_INCLUDE)-1 && !strncmp(cs, META_INCLUDE, l)) {
const char *filename = cs+l+strspn(cs+l, whitespace);
-
if (pa_cli_command_execute_file(c, filename, buf, fail) < 0)
- if (*fail) return -1;
+ if (*fail)
+ return -1;
+ } else if (l == sizeof(META_IFEXISTS)-1 && !strncmp(cs, META_IFEXISTS, l)) {
+ if (!ifstate) {
+ pa_strbuf_printf(buf, "Meta command %s is not valid in this context\n", cs);
+ return -1;
+ } else if (*ifstate != IFSTATE_NONE) {
+ pa_strbuf_printf(buf, "Nested %s commands not supported\n", cs);
+ return -1;
+ } else {
+ const char *filename = cs+l+strspn(cs+l, whitespace);
+
+ /* Search DL_SEARCH_PATH unless the filename is absolute */
+ if (filename[0] == PA_PATH_SEP_CHAR) {
+
+ *ifstate = access(filename, F_OK) == 0 ? IFSTATE_TRUE : IFSTATE_FALSE;
+ pa_log_debug("Checking for existance of '%s': %s", filename, *ifstate == IFSTATE_TRUE ? "success" : "failure");
+
+ } else {
+ const char *paths, *state = NULL;
+ char *p;
+
+ if (!(paths = lt_dlgetsearchpath()))
+ return -1;
+
+ while ((p = pa_split(paths, ":", &state))) {
+ char *pathname;
+
+ pathname = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", p, filename);
+ pa_xfree(p);
+
+ *ifstate = access(pathname, F_OK) == 0 ? IFSTATE_TRUE : IFSTATE_FALSE;
+ pa_log_debug("Checking for existance of '%s': %s", pathname, *ifstate == IFSTATE_TRUE ? "success" : "failure");
+
+ pa_xfree(pathname);
+
+ if (*ifstate == IFSTATE_TRUE)
+ break;
+ }
+ }
+
+ }
} else {
pa_strbuf_printf(buf, "Invalid meta command: %s\n", cs);
if (*fail) return -1;
@@ -983,21 +1405,24 @@ int pa_cli_command_execute_line(pa_core *c, const char *s, pa_strbuf *buf, int *
const struct command*command;
int unknown = 1;
size_t l;
-
+
+ if (ifstate && *ifstate == IFSTATE_FALSE)
+ return 0;
+
l = strcspn(cs, whitespace);
- for (command = commands; command->name; command++)
+ for (command = commands; command->name; command++)
if (strlen(command->name) == l && !strncmp(cs, command->name, l)) {
int ret;
pa_tokenizer *t = pa_tokenizer_new(cs, command->args);
- assert(t);
+ pa_assert(t);
ret = command->proc(c, t, buf, fail);
pa_tokenizer_free(t);
unknown = 0;
if (ret < 0 && *fail)
return -1;
-
+
break;
}
@@ -1011,11 +1436,48 @@ int pa_cli_command_execute_line(pa_core *c, const char *s, pa_strbuf *buf, int *
return 0;
}
-int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, int *fail) {
- char line[256];
+int pa_cli_command_execute_line(pa_core *c, const char *s, pa_strbuf *buf, pa_bool_t *fail) {
+ return pa_cli_command_execute_line_stateful(c, s, buf, fail, NULL);
+}
+
+int pa_cli_command_execute_file_stream(pa_core *c, FILE *f, pa_strbuf *buf, pa_bool_t *fail) {
+ char line[1024];
+ int ifstate = IFSTATE_NONE;
+ int ret = -1;
+ pa_bool_t _fail = TRUE;
+
+ pa_assert(c);
+ pa_assert(f);
+ pa_assert(buf);
+
+ if (!fail)
+ fail = &_fail;
+
+ while (fgets(line, sizeof(line), f)) {
+ pa_strip_nl(line);
+
+ if (pa_cli_command_execute_line_stateful(c, line, buf, fail, &ifstate) < 0 && *fail)
+ goto fail;
+ }
+
+ ret = 0;
+
+fail:
+
+ return ret;
+}
+
+int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, pa_bool_t *fail) {
FILE *f = NULL;
int ret = -1;
- assert(c && fn && buf);
+ pa_bool_t _fail = TRUE;
+
+ pa_assert(c);
+ pa_assert(fn);
+ pa_assert(buf);
+
+ if (!fail)
+ fail = &_fail;
if (!(f = fopen(fn, "r"))) {
pa_strbuf_printf(buf, "open('%s') failed: %s\n", fn, pa_cstrerror(errno));
@@ -1024,13 +1486,7 @@ int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, int
goto fail;
}
- while (fgets(line, sizeof(line), f)) {
- char *e = line + strcspn(line, linebreak);
- *e = 0;
-
- if (pa_cli_command_execute_line(c, line, buf, fail) < 0 && *fail)
- goto fail;
- }
+ ret = pa_cli_command_execute_file_stream(c, f, buf, fail);
ret = 0;
@@ -1041,16 +1497,24 @@ fail:
return ret;
}
-int pa_cli_command_execute(pa_core *c, const char *s, pa_strbuf *buf, int *fail) {
+int pa_cli_command_execute(pa_core *c, const char *s, pa_strbuf *buf, pa_bool_t *fail) {
const char *p;
- assert(c && s && buf && fail);
+ int ifstate = IFSTATE_NONE;
+ pa_bool_t _fail = TRUE;
+
+ pa_assert(c);
+ pa_assert(s);
+ pa_assert(buf);
+
+ if (!fail)
+ fail = &_fail;
p = s;
while (*p) {
size_t l = strcspn(p, linebreak);
char *line = pa_xstrndup(p, l);
-
- if (pa_cli_command_execute_line(c, line, buf, fail) < 0&& *fail) {
+
+ if (pa_cli_command_execute_line_stateful(c, line, buf, fail, &ifstate) < 0 && *fail) {
pa_xfree(line);
return -1;
}
diff --git a/src/pulsecore/cli-command.h b/src/pulsecore/cli-command.h
index c56c3ca0..9bf35dc3 100644
--- a/src/pulsecore/cli-command.h
+++ b/src/pulsecore/cli-command.h
@@ -1,21 +1,21 @@
#ifndef fooclicommandhfoo
#define fooclicommandhfoo
-/* $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
@@ -29,12 +29,18 @@
* buffer *buf. If *fail is non-zero the function will return -1 when
* one or more of the executed commands failed. *fail
* may be modified by the function call. */
-int pa_cli_command_execute_line(pa_core *c, const char *s, pa_strbuf *buf, int *fail);
+int pa_cli_command_execute_line(pa_core *c, const char *s, pa_strbuf *buf, pa_bool_t *fail);
+
+/* Execute a whole file of CLI commands */
+int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, pa_bool_t *fail);
/* Execute a whole file of CLI commands */
-int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, int *fail);
+int pa_cli_command_execute_file_stream(pa_core *c, FILE *f, pa_strbuf *buf, pa_bool_t *fail);
/* Split the specified string into lines and run pa_cli_command_execute_line() for each. */
-int pa_cli_command_execute(pa_core *c, const char *s, pa_strbuf *buf, int *fail);
+int pa_cli_command_execute(pa_core *c, const char *s, pa_strbuf *buf, pa_bool_t *fail);
+
+/* Same as pa_cli_command_execute_line() but also take ifstate var. */
+int pa_cli_command_execute_line_stateful(pa_core *c, const char *s, pa_strbuf *buf, pa_bool_t *fail, int *ifstate);
#endif
diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c
index 49934c07..c92fca20 100644
--- a/src/pulsecore/cli-text.c
+++ b/src/pulsecore/cli-text.c
@@ -1,18 +1,18 @@
-/* $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
@@ -23,11 +23,11 @@
#include <config.h>
#endif
-#include <assert.h>
#include <string.h>
#include <pulse/volume.h>
#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
#include <pulsecore/module.h>
#include <pulsecore/client.h>
@@ -39,6 +39,8 @@
#include <pulsecore/sample-util.h>
#include <pulsecore/core-scache.h>
#include <pulsecore/autoload.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
#include "cli-text.h"
@@ -46,16 +48,22 @@ char *pa_module_list_to_string(pa_core *c) {
pa_strbuf *s;
pa_module *m;
uint32_t idx = PA_IDXSET_INVALID;
- assert(c);
+ pa_assert(c);
s = pa_strbuf_new();
- assert(s);
pa_strbuf_printf(s, "%u module(s) loaded.\n", pa_idxset_size(c->modules));
-
- for (m = pa_idxset_first(c->modules, &idx); m; m = pa_idxset_next(c->modules, &idx))
- pa_strbuf_printf(s, " index: %u\n\tname: <%s>\n\targument: <%s>\n\tused: %i\n\tauto unload: %s\n", m->index, m->name, m->argument, m->n_used, m->auto_unload ? "yes" : "no");
-
+
+ for (m = pa_idxset_first(c->modules, &idx); m; m = pa_idxset_next(c->modules, &idx)) {
+ pa_strbuf_printf(s, " index: %u\n"
+ "\tname: <%s>\n"
+ "\targument: <%s>\n"
+ "\tused: %i\n"
+ "\tauto unload: %s\n",
+ m->index, m->name, m->argument ? m->argument : "", m->n_used,
+ pa_yes_no(m->auto_unload));
+ }
+
return pa_strbuf_tostring_free(s);
}
@@ -63,20 +71,29 @@ char *pa_client_list_to_string(pa_core *c) {
pa_strbuf *s;
pa_client *client;
uint32_t idx = PA_IDXSET_INVALID;
- assert(c);
+ pa_assert(c);
s = pa_strbuf_new();
- assert(s);
pa_strbuf_printf(s, "%u client(s) logged in.\n", pa_idxset_size(c->clients));
-
+
for (client = pa_idxset_first(c->clients, &idx); client; client = pa_idxset_next(c->clients, &idx)) {
- pa_strbuf_printf(s, " index: %u\n\tname: <%s>\n\tdriver: <%s>\n", client->index, client->name, client->driver);
+ char *t;
+ pa_strbuf_printf(
+ s,
+ " index: %u\n"
+ "\tdriver: <%s>\n",
+ client->index,
+ client->driver);
+
+ if (client->module)
+ pa_strbuf_printf(s, "\towner module: %u\n", client->module->index);
- if (client->owner)
- pa_strbuf_printf(s, "\towner module: <%u>\n", client->owner->index);
+ t = pa_proplist_to_string(client->proplist);
+ pa_strbuf_printf(s, "\tproperties:\n%s", t);
+ pa_xfree(t);
}
-
+
return pa_strbuf_tostring_free(s);
}
@@ -84,41 +101,74 @@ char *pa_sink_list_to_string(pa_core *c) {
pa_strbuf *s;
pa_sink *sink;
uint32_t idx = PA_IDXSET_INVALID;
- assert(c);
+ static const char* const state_table[] = {
+ [PA_SINK_INIT] = "INIT",
+ [PA_SINK_RUNNING] = "RUNNING",
+ [PA_SINK_SUSPENDED] = "SUSPENDED",
+ [PA_SINK_IDLE] = "IDLE",
+ [PA_SINK_UNLINKED] = "UNLINKED"
+ };
+ pa_assert(c);
s = pa_strbuf_new();
- assert(s);
pa_strbuf_printf(s, "%u sink(s) available.\n", pa_idxset_size(c->sinks));
for (sink = pa_idxset_first(c->sinks, &idx); sink; sink = pa_idxset_next(c->sinks, &idx)) {
- char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
-
+ char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t;
+ pa_usec_t min_latency, max_latency;
+
+ pa_sink_get_latency_range(sink, &min_latency, &max_latency);
+
pa_strbuf_printf(
s,
" %c index: %u\n"
"\tname: <%s>\n"
"\tdriver: <%s>\n"
- "\tvolume: <%s>\n"
- "\tlatency: <%0.0f usec>\n"
- "\tmonitor_source: <%u>\n"
- "\tsample spec: <%s>\n"
- "\tchannel map: <%s>\n",
+ "\tflags: %s%s%s%s%s%s\n"
+ "\tstate: %s\n"
+ "\tvolume: %s\n"
+ "\tmuted: %s\n"
+ "\tcurrent latency: %0.2f ms\n"
+ "\tconfigured latency: %0.2f ms; range is %0.2f .. %0.2f ms\n"
+ "\tmax request: %lu KiB\n"
+ "\tmax rewind: %lu KiB\n"
+ "\tmonitor source: %u\n"
+ "\tsample spec: %s\n"
+ "\tchannel map: %s\n"
+ "\tused by: %u\n"
+ "\tlinked by: %u\n",
c->default_sink_name && !strcmp(sink->name, c->default_sink_name) ? '*' : ' ',
- sink->index, sink->name,
+ sink->index,
+ sink->name,
sink->driver,
- pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink, PA_MIXER_HARDWARE)),
- (double) pa_sink_get_latency(sink),
+ sink->flags & PA_SINK_HARDWARE ? "HARDWARE " : "",
+ sink->flags & PA_SINK_NETWORK ? "NETWORK " : "",
+ sink->flags & PA_SINK_HW_MUTE_CTRL ? "HW_MUTE_CTRL " : "",
+ sink->flags & PA_SINK_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
+ sink->flags & PA_SINK_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "",
+ sink->flags & PA_SINK_LATENCY ? "LATENCY " : "",
+ state_table[pa_sink_get_state(sink)],
+ pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink)),
+ pa_yes_no(pa_sink_get_mute(sink)),
+ (double) pa_sink_get_latency(sink) / PA_USEC_PER_MSEC,
+ (double) pa_sink_get_requested_latency(sink) / PA_USEC_PER_MSEC, (double) min_latency / PA_USEC_PER_MSEC, (double) max_latency / PA_USEC_PER_MSEC,
+ (unsigned long) pa_sink_get_max_request(sink) / 1024,
+ (unsigned long) pa_sink_get_max_rewind(sink) / 1024,
sink->monitor_source ? sink->monitor_source->index : PA_INVALID_INDEX,
pa_sample_spec_snprint(ss, sizeof(ss), &sink->sample_spec),
- pa_channel_map_snprint(cm, sizeof(cm), &sink->channel_map));
+ pa_channel_map_snprint(cm, sizeof(cm), &sink->channel_map),
+ pa_sink_used_by(sink),
+ pa_sink_linked_by(sink));
- if (sink->owner)
- pa_strbuf_printf(s, "\towner module: <%u>\n", sink->owner->index);
- if (sink->description)
- pa_strbuf_printf(s, "\tdescription: <%s>\n", sink->description);
+ if (sink->module)
+ pa_strbuf_printf(s, "\tmodule: %u\n", sink->module->index);
+
+ t = pa_proplist_to_string(sink->proplist);
+ pa_strbuf_printf(s, "\tproperties:\n%s", t);
+ pa_xfree(t);
}
-
+
return pa_strbuf_tostring_free(s);
}
@@ -126,41 +176,72 @@ char *pa_source_list_to_string(pa_core *c) {
pa_strbuf *s;
pa_source *source;
uint32_t idx = PA_IDXSET_INVALID;
- assert(c);
+ static const char* const state_table[] = {
+ [PA_SOURCE_INIT] = "INIT",
+ [PA_SOURCE_RUNNING] = "RUNNING",
+ [PA_SOURCE_SUSPENDED] = "SUSPENDED",
+ [PA_SOURCE_IDLE] = "IDLE",
+ [PA_SOURCE_UNLINKED] = "UNLINKED"
+ };
+ pa_assert(c);
s = pa_strbuf_new();
- assert(s);
pa_strbuf_printf(s, "%u source(s) available.\n", pa_idxset_size(c->sources));
for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx)) {
- char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
-
-
+ char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], *t;
+ pa_usec_t min_latency, max_latency;
+
+ pa_source_get_latency_range(source, &min_latency, &max_latency);
+
pa_strbuf_printf(
s,
" %c index: %u\n"
"\tname: <%s>\n"
"\tdriver: <%s>\n"
- "\tlatency: <%0.0f usec>\n"
- "\tsample spec: <%s>\n"
- "\tchannel map: <%s>\n",
+ "\tflags: %s%s%s%s%s%s\n"
+ "\tstate: %s\n"
+ "\tvolume: %s\n"
+ "\tmuted: %s\n"
+ "\tcurrent latency: %0.2f ms\n"
+ "\tconfigured latency: %0.2f ms; range is %0.2f .. %0.2f ms\n"
+ "\tmax rewind: %lu KiB\n"
+ "\tsample spec: %s\n"
+ "\tchannel map: %s\n"
+ "\tused by: %u\n"
+ "\tlinked by: %u\n",
c->default_source_name && !strcmp(source->name, c->default_source_name) ? '*' : ' ',
source->index,
source->name,
source->driver,
- (double) pa_source_get_latency(source),
+ source->flags & PA_SOURCE_HARDWARE ? "HARDWARE " : "",
+ source->flags & PA_SOURCE_NETWORK ? "NETWORK " : "",
+ source->flags & PA_SOURCE_HW_MUTE_CTRL ? "HW_MUTE_CTRL " : "",
+ source->flags & PA_SOURCE_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
+ source->flags & PA_SOURCE_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "",
+ source->flags & PA_SOURCE_LATENCY ? "LATENCY " : "",
+ state_table[pa_source_get_state(source)],
+ pa_cvolume_snprint(cv, sizeof(cv), pa_source_get_volume(source)),
+ pa_yes_no(pa_source_get_mute(source)),
+ (double) pa_source_get_latency(source) / PA_USEC_PER_MSEC,
+ (double) pa_source_get_requested_latency(source) / PA_USEC_PER_MSEC, (double) min_latency / PA_USEC_PER_MSEC, (double) max_latency / PA_USEC_PER_MSEC,
+ (unsigned long) pa_source_get_max_rewind(source) / 1024,
pa_sample_spec_snprint(ss, sizeof(ss), &source->sample_spec),
- pa_channel_map_snprint(cm, sizeof(cm), &source->channel_map));
-
- if (source->monitor_of)
- pa_strbuf_printf(s, "\tmonitor_of: <%u>\n", source->monitor_of->index);
- if (source->owner)
- pa_strbuf_printf(s, "\towner module: <%u>\n", source->owner->index);
- if (source->description)
- pa_strbuf_printf(s, "\tdescription: <%s>\n", source->description);
+ pa_channel_map_snprint(cm, sizeof(cm), &source->channel_map),
+ pa_source_used_by(source),
+ pa_source_linked_by(source));
+
+ if (source->monitor_of)
+ pa_strbuf_printf(s, "\tmonitor_of: %u\n", source->monitor_of->index);
+ if (source->module)
+ pa_strbuf_printf(s, "\tmodule: %u\n", source->module->index);
+
+ t = pa_proplist_to_string(source->proplist);
+ pa_strbuf_printf(s, "\tproperties:\n%s", t);
+ pa_xfree(t);
}
-
+
return pa_strbuf_tostring_free(s);
}
@@ -170,46 +251,69 @@ char *pa_source_output_list_to_string(pa_core *c) {
pa_source_output *o;
uint32_t idx = PA_IDXSET_INVALID;
static const char* const state_table[] = {
- "RUNNING",
- "CORKED",
- "DISCONNECTED"
+ [PA_SOURCE_OUTPUT_INIT] = "INIT",
+ [PA_SOURCE_OUTPUT_RUNNING] = "RUNNING",
+ [PA_SOURCE_OUTPUT_CORKED] = "CORKED",
+ [PA_SOURCE_OUTPUT_UNLINKED] = "UNLINKED"
};
- assert(c);
+ pa_assert(c);
s = pa_strbuf_new();
- assert(s);
pa_strbuf_printf(s, "%u source outputs(s) available.\n", pa_idxset_size(c->source_outputs));
for (o = pa_idxset_first(c->source_outputs, &idx); o; o = pa_idxset_next(c->source_outputs, &idx)) {
- char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
-
- assert(o->source);
-
+ char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t, clt[28];
+ pa_usec_t cl;
+
+ if ((cl = pa_source_output_get_requested_latency(o)) == (pa_usec_t) -1)
+ pa_snprintf(clt, sizeof(clt), "n/a");
+ else
+ pa_snprintf(clt, sizeof(clt), "%0.2f ms", (double) cl / PA_USEC_PER_MSEC);
+
+ pa_assert(o->source);
+
pa_strbuf_printf(
s,
" index: %u\n"
- "\tname: '%s'\n"
"\tdriver: <%s>\n"
+ "\tflags: %s%s%s%s%s%s%s%s\n"
"\tstate: %s\n"
- "\tsource: <%u> '%s'\n"
- "\tsample spec: <%s>\n"
- "\tchannel map: <%s>\n"
+ "\tsource: %u <%s>\n"
+ "\tcurrent latency: %0.2f ms\n"
+ "\trequested latency: %s\n"
+ "\tsample spec: %s\n"
+ "\tchannel map: %s\n"
"\tresample method: %s\n",
o->index,
- o->name,
o->driver,
- state_table[o->state],
+ o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE ? "VARIABLE_RATE " : "",
+ o->flags & PA_SOURCE_OUTPUT_DONT_MOVE ? "DONT_MOVE " : "",
+ o->flags & PA_SOURCE_OUTPUT_START_CORKED ? "START_CORKED " : "",
+ o->flags & PA_SOURCE_OUTPUT_NO_REMAP ? "NO_REMAP " : "",
+ o->flags & PA_SOURCE_OUTPUT_NO_REMIX ? "NO_REMIX " : "",
+ o->flags & PA_SOURCE_OUTPUT_FIX_FORMAT ? "FIX_FORMAT " : "",
+ o->flags & PA_SOURCE_OUTPUT_FIX_RATE ? "FIX_RATE " : "",
+ o->flags & PA_SOURCE_OUTPUT_FIX_CHANNELS ? "FIX_CHANNELS " : "",
+ state_table[pa_source_output_get_state(o)],
o->source->index, o->source->name,
+ (double) pa_source_output_get_latency(o, NULL) / PA_USEC_PER_MSEC,
+ clt,
pa_sample_spec_snprint(ss, sizeof(ss), &o->sample_spec),
pa_channel_map_snprint(cm, sizeof(cm), &o->channel_map),
pa_resample_method_to_string(pa_source_output_get_resample_method(o)));
if (o->module)
- pa_strbuf_printf(s, "\towner module: <%u>\n", o->module->index);
+ pa_strbuf_printf(s, "\towner module: %u\n", o->module->index);
if (o->client)
- pa_strbuf_printf(s, "\tclient: <%u> '%s'\n", o->client->index, o->client->name);
+ pa_strbuf_printf(s, "\tclient: %u <%s>\n", o->client->index, pa_strnull(pa_proplist_gets(o->client->proplist, PA_PROP_APPLICATION_NAME)));
+ if (o->direct_on_input)
+ pa_strbuf_printf(s, "\tdirect on input: %u\n", o->direct_on_input->index);
+
+ t = pa_proplist_to_string(o->proplist);
+ pa_strbuf_printf(s, "\tproperties:\n%s", t);
+ pa_xfree(t);
}
-
+
return pa_strbuf_tostring_free(s);
}
@@ -218,60 +322,81 @@ char *pa_sink_input_list_to_string(pa_core *c) {
pa_sink_input *i;
uint32_t idx = PA_IDXSET_INVALID;
static const char* const state_table[] = {
- "RUNNING",
- "CORKED",
- "DISCONNECTED"
+ [PA_SINK_INPUT_INIT] = "INIT",
+ [PA_SINK_INPUT_RUNNING] = "RUNNING",
+ [PA_SINK_INPUT_DRAINED] = "DRAINED",
+ [PA_SINK_INPUT_CORKED] = "CORKED",
+ [PA_SINK_INPUT_UNLINKED] = "UNLINKED"
};
- assert(c);
+ pa_assert(c);
s = pa_strbuf_new();
- assert(s);
pa_strbuf_printf(s, "%u sink input(s) available.\n", pa_idxset_size(c->sink_inputs));
for (i = pa_idxset_first(c->sink_inputs, &idx); i; i = pa_idxset_next(c->sink_inputs, &idx)) {
- char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+ char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t, clt[28];
+ pa_usec_t cl;
+
+ if ((cl = pa_sink_input_get_requested_latency(i)) == (pa_usec_t) -1)
+ pa_snprintf(clt, sizeof(clt), "n/a");
+ else
+ pa_snprintf(clt, sizeof(clt), "%0.2f ms", (double) cl / PA_USEC_PER_MSEC);
+
+ pa_assert(i->sink);
- assert(i->sink);
-
pa_strbuf_printf(
s,
" index: %u\n"
- "\tname: <%s>\n"
"\tdriver: <%s>\n"
+ "\tflags: %s%s%s%s%s%s%s%s\n"
"\tstate: %s\n"
- "\tsink: <%u> '%s'\n"
- "\tvolume: <%s>\n"
- "\tlatency: <%0.0f usec>\n"
- "\tsample spec: <%s>\n"
- "\tchannel map: <%s>\n"
+ "\tsink: %u <%s>\n"
+ "\tvolume: %s\n"
+ "\tmuted: %s\n"
+ "\tcurrent latency: %0.2f ms\n"
+ "\trequested latency: %s\n"
+ "\tsample spec: %s\n"
+ "\tchannel map: %s\n"
"\tresample method: %s\n",
i->index,
- i->name,
i->driver,
- state_table[i->state],
+ i->flags & PA_SINK_INPUT_VARIABLE_RATE ? "VARIABLE_RATE " : "",
+ i->flags & PA_SINK_INPUT_DONT_MOVE ? "DONT_MOVE " : "",
+ i->flags & PA_SINK_INPUT_START_CORKED ? "START_CORKED " : "",
+ i->flags & PA_SINK_INPUT_NO_REMAP ? "NO_REMAP " : "",
+ i->flags & PA_SINK_INPUT_NO_REMIX ? "NO_REMIX " : "",
+ i->flags & PA_SINK_INPUT_FIX_FORMAT ? "FIX_FORMAT " : "",
+ i->flags & PA_SINK_INPUT_FIX_RATE ? "FIX_RATE " : "",
+ i->flags & PA_SINK_INPUT_FIX_CHANNELS ? "FIX_CHANNELS " : "",
+ state_table[pa_sink_input_get_state(i)],
i->sink->index, i->sink->name,
pa_cvolume_snprint(cv, sizeof(cv), pa_sink_input_get_volume(i)),
- (double) pa_sink_input_get_latency(i),
+ pa_yes_no(pa_sink_input_get_mute(i)),
+ (double) pa_sink_input_get_latency(i, NULL) / PA_USEC_PER_MSEC,
+ clt,
pa_sample_spec_snprint(ss, sizeof(ss), &i->sample_spec),
pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
pa_resample_method_to_string(pa_sink_input_get_resample_method(i)));
if (i->module)
- pa_strbuf_printf(s, "\towner module: <%u>\n", i->module->index);
+ pa_strbuf_printf(s, "\tmodule: %u\n", i->module->index);
if (i->client)
- pa_strbuf_printf(s, "\tclient: <%u> '%s'\n", i->client->index, i->client->name);
+ pa_strbuf_printf(s, "\tclient: %u <%s>\n", i->client->index, pa_strnull(pa_proplist_gets(i->client->proplist, PA_PROP_APPLICATION_NAME)));
+
+ t = pa_proplist_to_string(i->proplist);
+ pa_strbuf_printf(s, "\tproperties:\n%s", t);
+ pa_xfree(t);
}
-
+
return pa_strbuf_tostring_free(s);
}
char *pa_scache_list_to_string(pa_core *c) {
pa_strbuf *s;
- assert(c);
+ pa_assert(c);
s = pa_strbuf_new();
- assert(s);
pa_strbuf_printf(s, "%u cache entries available.\n", c->scache ? pa_idxset_size(c->scache) : 0);
@@ -281,25 +406,25 @@ char *pa_scache_list_to_string(pa_core *c) {
for (e = pa_idxset_first(c->scache, &idx); e; e = pa_idxset_next(c->scache, &idx)) {
double l = 0;
- char ss[PA_SAMPLE_SPEC_SNPRINT_MAX] = "n/a", cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX] = "n/a";
-
+ char ss[PA_SAMPLE_SPEC_SNPRINT_MAX] = "n/a", cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX] = "n/a", *t;
+
if (e->memchunk.memblock) {
pa_sample_spec_snprint(ss, sizeof(ss), &e->sample_spec);
pa_channel_map_snprint(cm, sizeof(cm), &e->channel_map);
l = (double) e->memchunk.length / pa_bytes_per_second(&e->sample_spec);
}
-
+
pa_strbuf_printf(
s,
" name: <%s>\n"
- "\tindex: <%u>\n"
- "\tsample spec: <%s>\n"
- "\tchannel map: <%s>\n"
- "\tlength: <%lu>\n"
- "\tduration: <%0.1fs>\n"
- "\tvolume: <%s>\n"
+ "\tindex: %u\n"
+ "\tsample spec: %s\n"
+ "\tchannel map: %s\n"
+ "\tlength: %lu\n"
+ "\tduration: %0.1f s\n"
+ "\tvolume: %s\n"
"\tlazy: %s\n"
- "\tfilename: %s\n",
+ "\tfilename: <%s>\n",
e->name,
e->index,
ss,
@@ -307,8 +432,12 @@ char *pa_scache_list_to_string(pa_core *c) {
(long unsigned)(e->memchunk.memblock ? e->memchunk.length : 0),
l,
pa_cvolume_snprint(cv, sizeof(cv), &e->volume),
- e->lazy ? "yes" : "no",
+ pa_yes_no(e->lazy),
e->filename ? e->filename : "n/a");
+
+ t = pa_proplist_to_string(e->proplist);
+ pa_strbuf_printf(s, "\tproperties:\n%s", t);
+ pa_xfree(t);
}
}
@@ -317,10 +446,9 @@ char *pa_scache_list_to_string(pa_core *c) {
char *pa_autoload_list_to_string(pa_core *c) {
pa_strbuf *s;
- assert(c);
+ pa_assert(c);
s = pa_strbuf_new();
- assert(s);
pa_strbuf_printf(s, "%u autoload entries available.\n", c->autoload_hashmap ? pa_hashmap_size(c->autoload_hashmap) : 0);
@@ -330,12 +458,17 @@ char *pa_autoload_list_to_string(pa_core *c) {
while ((e = pa_hashmap_iterate(c->autoload_hashmap, &state, NULL))) {
pa_strbuf_printf(
- s, " name: <%s>\n\ttype: <%s>\n\tindex: <%u>\n\tmodule_name: <%s>\n\targuments: <%s>\n",
+ s,
+ " name: <%s>\n"
+ "\ttype: %s\n"
+ "\tindex: %u\n"
+ "\tmodule_name: <%s>\n"
+ "\targuments: <%s>\n",
e->name,
e->type == PA_NAMEREG_SOURCE ? "source" : "sink",
e->index,
e->module,
- e->argument);
+ e->argument ? e->argument : "");
}
}
@@ -351,9 +484,9 @@ char *pa_full_status_string(pa_core *c) {
for (i = 0; i < 8; i++) {
char *t = NULL;
-
+
switch (i) {
- case 0:
+ case 0:
t = pa_sink_list_to_string(c);
break;
case 1:
@@ -365,7 +498,7 @@ char *pa_full_status_string(pa_core *c) {
case 3:
t = pa_source_output_list_to_string(c);
break;
- case 4:
+ case 4:
t = pa_client_list_to_string(c);
break;
case 5:
diff --git a/src/pulsecore/cli-text.h b/src/pulsecore/cli-text.h
index cd3acdee..f4cb97a5 100644
--- a/src/pulsecore/cli-text.h
+++ b/src/pulsecore/cli-text.h
@@ -1,21 +1,21 @@
#ifndef fooclitexthfoo
#define fooclitexthfoo
-/* $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
diff --git a/src/pulsecore/cli.c b/src/pulsecore/cli.c
index e3fc2e4c..b3c639f8 100644
--- a/src/pulsecore/cli.c
+++ b/src/pulsecore/cli.c
@@ -1,18 +1,18 @@
-/* $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
@@ -25,7 +25,6 @@
#include <stdio.h>
#include <string.h>
-#include <assert.h>
#include <stdlib.h>
#include <pulse/xmalloc.h>
@@ -43,6 +42,7 @@
#include <pulsecore/cli-text.h>
#include <pulsecore/cli-command.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
#include "cli.h"
@@ -57,7 +57,8 @@ struct pa_cli {
pa_client *client;
- int fail, kill_requested, defer_kill;
+ pa_bool_t fail, kill_requested;
+ int defer_kill;
};
static void line_callback(pa_ioline *line, const char *s, void *userdata);
@@ -66,33 +67,33 @@ static void client_kill(pa_client *c);
pa_cli* pa_cli_new(pa_core *core, pa_iochannel *io, pa_module *m) {
char cname[256];
pa_cli *c;
- assert(io);
+ pa_assert(io);
- c = pa_xmalloc(sizeof(pa_cli));
+ c = pa_xnew(pa_cli, 1);
c->core = core;
- c->line = pa_ioline_new(io);
- assert(c->line);
+ pa_assert_se(c->line = pa_ioline_new(io));
c->userdata = NULL;
c->eof_callback = NULL;
pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname));
- c->client = pa_client_new(core, __FILE__, cname);
- assert(c->client);
+ pa_assert_se(c->client = pa_client_new(core, __FILE__, cname));
c->client->kill = client_kill;
c->client->userdata = c;
- c->client->owner = m;
-
+ c->client->module = m;
+
pa_ioline_set_callback(c->line, line_callback, c);
pa_ioline_puts(c->line, "Welcome to PulseAudio! Use \"help\" for usage information.\n"PROMPT);
- c->fail = c->kill_requested = c->defer_kill = 0;
-
+ c->fail = c->kill_requested = FALSE;
+ c->defer_kill = 0;
+
return c;
}
void pa_cli_free(pa_cli *c) {
- assert(c);
+ pa_assert(c);
+
pa_ioline_close(c->line);
pa_ioline_unref(c->line);
pa_client_free(c->client);
@@ -101,12 +102,13 @@ void pa_cli_free(pa_cli *c) {
static void client_kill(pa_client *client) {
pa_cli *c;
- assert(client && client->userdata);
- c = client->userdata;
-
+
+ pa_assert(client);
+ pa_assert_se(c = client->userdata);
+
pa_log_debug("CLI client killed.");
if (c->defer_kill)
- c->kill_requested = 1;
+ c->kill_requested = TRUE;
else {
if (c->eof_callback)
c->eof_callback(c, c->userdata);
@@ -117,7 +119,9 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
pa_strbuf *buf;
pa_cli *c = userdata;
char *p;
- assert(line && c);
+
+ pa_assert(line);
+ pa_assert(c);
if (!s) {
pa_log_debug("CLI got EOF from user.");
@@ -127,8 +131,7 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
return;
}
- buf = pa_strbuf_new();
- assert(buf);
+ pa_assert_se(buf = pa_strbuf_new());
c->defer_kill++;
pa_cli_command_execute_line(c->core, s, buf, &c->fail);
c->defer_kill--;
@@ -138,12 +141,13 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
if (c->kill_requested) {
if (c->eof_callback)
c->eof_callback(c, c->userdata);
- } else
+ } else
pa_ioline_puts(line, PROMPT);
}
void pa_cli_set_eof_callback(pa_cli *c, void (*cb)(pa_cli*c, void *userdata), void *userdata) {
- assert(c);
+ pa_assert(c);
+
c->eof_callback = cb;
c->userdata = userdata;
}
diff --git a/src/pulsecore/cli.h b/src/pulsecore/cli.h
index 639fa952..6077a8e8 100644
--- a/src/pulsecore/cli.h
+++ b/src/pulsecore/cli.h
@@ -1,21 +1,21 @@
#ifndef fooclihfoo
#define fooclihfoo
-/* $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
diff --git a/src/pulsecore/client.c b/src/pulsecore/client.c
index c34bf149..0ffd2330 100644
--- a/src/pulsecore/client.c
+++ b/src/pulsecore/client.c
@@ -1,18 +1,19 @@
-/* $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
@@ -24,7 +25,6 @@
#endif
#include <stdio.h>
-#include <assert.h>
#include <stdlib.h>
#include <string.h>
@@ -32,50 +32,58 @@
#include <pulsecore/core-subscribe.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
#include "client.h"
pa_client *pa_client_new(pa_core *core, const char *driver, const char *name) {
pa_client *c;
- int r;
- assert(core);
- c = pa_xmalloc(sizeof(pa_client));
- c->name = pa_xstrdup(name);
- c->driver = pa_xstrdup(driver);
- c->owner = NULL;
+ pa_core_assert_ref(core);
+
+ c = pa_xnew(pa_client, 1);
c->core = core;
+ c->proplist = pa_proplist_new();
+ if (name)
+ pa_proplist_sets(c->proplist, PA_PROP_APPLICATION_NAME, name);
+ c->driver = pa_xstrdup(driver);
+ c->module = NULL;
c->kill = NULL;
c->userdata = NULL;
- r = pa_idxset_put(core->clients, c, &c->index);
- assert(c->index != PA_IDXSET_INVALID && r >= 0);
+ pa_assert_se(pa_idxset_put(core->clients, c, &c->index) >= 0);
- pa_log_info("created %u \"%s\"", c->index, c->name);
+ pa_log_info("Created %u \"%s\"", c->index, pa_strnull(name));
pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_NEW, c->index);
pa_core_check_quit(core);
-
+
return c;
}
void pa_client_free(pa_client *c) {
- assert(c && c->core);
+ pa_core *core;
- pa_idxset_remove_by_data(c->core->clients, c, NULL);
+ pa_assert(c);
+ pa_assert(c->core);
- pa_core_check_quit(c->core);
+ core = c->core;
+ pa_idxset_remove_by_data(c->core->clients, c, NULL);
- pa_log_info("freed %u \"%s\"", c->index, c->name);
+ pa_log_info("Freed %u \"%s\"", c->index, pa_strnull(pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME)));
pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_REMOVE, c->index);
- pa_xfree(c->name);
+ pa_proplist_free(c->proplist);
pa_xfree(c->driver);
pa_xfree(c);
+
+ pa_core_check_quit(core);
}
void pa_client_kill(pa_client *c) {
- assert(c);
+ pa_assert(c);
+
if (!c->kill) {
pa_log_warn("kill() operation not implemented for client %u", c->index);
return;
@@ -85,12 +93,9 @@ void pa_client_kill(pa_client *c) {
}
void pa_client_set_name(pa_client *c, const char *name) {
- assert(c);
-
- pa_log_info("client %u changed name from \"%s\" to \"%s\"", c->index, c->name, name);
-
- pa_xfree(c->name);
- c->name = pa_xstrdup(name);
+ pa_assert(c);
+ pa_log_info("Client %u changed name from \"%s\" to \"%s\"", c->index, pa_strnull(pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME)), name);
+ pa_proplist_sets(c->proplist, PA_PROP_APPLICATION_NAME, name);
pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->index);
}
diff --git a/src/pulsecore/client.h b/src/pulsecore/client.h
index b28065e5..28d1fe5f 100644
--- a/src/pulsecore/client.h
+++ b/src/pulsecore/client.h
@@ -1,21 +1,21 @@
#ifndef foopulseclienthfoo
#define foopulseclienthfoo
-/* $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
@@ -26,6 +26,7 @@
typedef struct pa_client pa_client;
+#include <pulse/proplist.h>
#include <pulsecore/core.h>
#include <pulsecore/module.h>
@@ -35,11 +36,12 @@ typedef struct pa_client pa_client;
struct pa_client {
uint32_t index;
-
- pa_module *owner;
- char *name, *driver;
pa_core *core;
+ pa_proplist *proplist;
+ pa_module *module;
+ char *driver;
+
void (*kill)(pa_client *c);
void *userdata;
};
diff --git a/src/pulsecore/conf-parser.c b/src/pulsecore/conf-parser.c
index db1e3719..4aec45d7 100644
--- a/src/pulsecore/conf-parser.c
+++ b/src/pulsecore/conf-parser.c
@@ -1,8 +1,8 @@
-/* $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,
@@ -23,7 +23,6 @@
#include <config.h>
#endif
-#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
@@ -33,6 +32,7 @@
#include <pulsecore/core-error.h>
#include <pulsecore/log.h>
#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
#include "conf-parser.h"
@@ -41,25 +41,28 @@
/* Run the user supplied parser for an assignment */
static int next_assignment(const char *filename, unsigned line, const pa_config_item *t, const char *lvalue, const char *rvalue, void *userdata) {
- assert(filename && t && lvalue && rvalue);
-
+ pa_assert(filename);
+ pa_assert(t);
+ pa_assert(lvalue);
+ pa_assert(rvalue);
+
for (; t->parse; t++)
if (!strcmp(lvalue, t->lvalue))
return t->parse(filename, line, lvalue, rvalue, t->data, userdata);
pa_log("[%s:%u] Unknown lvalue '%s'.", filename, line, lvalue);
-
+
return -1;
}
/* Returns non-zero when c is contained in s */
static int in_string(char c, const char *s) {
- assert(s);
-
+ pa_assert(s);
+
for (; *s; s++)
if (*s == c)
return 1;
-
+
return 0;
}
@@ -85,7 +88,7 @@ static int parse_line(const char *filename, unsigned line, const pa_config_item
if ((c = strpbrk(b, COMMENTS)))
*c = 0;
-
+
if (!*b)
return 0;
@@ -105,15 +108,17 @@ int pa_config_parse(const char *filename, FILE *f, const pa_config_item *t, void
int r = -1;
unsigned line = 0;
int do_close = !f;
- assert(filename && t);
-
+
+ pa_assert(filename);
+ pa_assert(t);
+
if (!f && !(f = fopen(filename, "r"))) {
if (errno == ENOENT) {
r = 0;
goto finish;
}
-
- pa_log_warn("WARNING: failed to open configuration file '%s': %s",
+
+ pa_log_warn("Failed to open configuration file '%s': %s",
filename, pa_cstrerror(errno));
goto finish;
}
@@ -123,57 +128,70 @@ int pa_config_parse(const char *filename, FILE *f, const pa_config_item *t, void
if (!fgets(l, sizeof(l), f)) {
if (feof(f))
break;
-
- pa_log_warn("WARNING: failed to read configuration file '%s': %s",
+
+ pa_log_warn("Failed to read configuration file '%s': %s",
filename, pa_cstrerror(errno));
goto finish;
}
-
+
if (parse_line(filename, ++line, t, l, userdata) < 0)
goto finish;
}
-
+
r = 0;
-
+
finish:
if (do_close && f)
fclose(f);
-
+
return r;
}
int pa_config_parse_int(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
int *i = data;
int32_t k;
- assert(filename && lvalue && rvalue && data);
+
+ pa_assert(filename);
+ pa_assert(lvalue);
+ pa_assert(rvalue);
+ pa_assert(data);
if (pa_atoi(rvalue, &k) < 0) {
pa_log("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
return -1;
}
-
+
*i = (int) k;
- return 0;
+ return 0;
}
int pa_config_parse_bool(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
- int *b = data, k;
- assert(filename && lvalue && rvalue && data);
-
+ int k;
+ pa_bool_t *b = data;
+
+ pa_assert(filename);
+ pa_assert(lvalue);
+ pa_assert(rvalue);
+ pa_assert(data);
+
if ((k = pa_parse_boolean(rvalue)) < 0) {
pa_log("[%s:%u] Failed to parse boolean value: %s", filename, line, rvalue);
return -1;
}
-
- *b = k;
-
+
+ *b = !!k;
+
return 0;
}
int pa_config_parse_string(const char *filename, PA_GCC_UNUSED unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
char **s = data;
- assert(filename && lvalue && rvalue && data);
+
+ pa_assert(filename);
+ pa_assert(lvalue);
+ pa_assert(rvalue);
+ pa_assert(data);
pa_xfree(*s);
*s = *rvalue ? pa_xstrdup(rvalue) : NULL;
diff --git a/src/pulsecore/conf-parser.h b/src/pulsecore/conf-parser.h
index 9c1a697a..7eb1fae2 100644
--- a/src/pulsecore/conf-parser.h
+++ b/src/pulsecore/conf-parser.h
@@ -1,11 +1,11 @@
#ifndef fooconfparserhfoo
#define fooconfparserhfoo
-/* $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,
diff --git a/src/pulsecore/core-error.c b/src/pulsecore/core-error.c
index 61878c9e..3d6c2c3b 100644
--- a/src/pulsecore/core-error.c
+++ b/src/pulsecore/core-error.c
@@ -1,18 +1,19 @@
-/* $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
@@ -28,178 +29,50 @@
#include <stdlib.h>
#include <string.h>
-#ifdef HAVE_PTHREAD
-#include <pthread.h>
-#endif
-
-#ifdef HAVE_WINDOWS_H
-#include <windows.h>
-#endif
-
#include <pulse/utf8.h>
#include <pulse/xmalloc.h>
#include <pulsecore/core-util.h>
#include <pulsecore/native-common.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/log.h>
#include "core-error.h"
-#ifdef HAVE_PTHREAD
-
-static pthread_once_t cstrerror_once = PTHREAD_ONCE_INIT;
-static pthread_key_t tlsstr_key;
-
-static void inittls(void) {
- int ret;
-
- ret = pthread_key_create(&tlsstr_key, pa_xfree);
- if (ret) {
- fprintf(stderr, __FILE__ ": CRITICAL: Unable to allocate TLS key (%d)\n", errno);
- exit(-1);
- }
-}
-
-#elif HAVE_WINDOWS_H
-
-static DWORD tlsstr_key = TLS_OUT_OF_INDEXES;
-static DWORD monitor_key = TLS_OUT_OF_INDEXES;
-
-static void inittls(void) {
- HANDLE mutex;
- char name[64];
-
- sprintf(name, "pulse%d", (int)GetCurrentProcessId());
-
- mutex = CreateMutex(NULL, FALSE, name);
- if (!mutex) {
- fprintf(stderr, __FILE__ ": CRITICAL: Unable to create named mutex (%d)\n", (int)GetLastError());
- exit(-1);
- }
-
- WaitForSingleObject(mutex, INFINITE);
-
- if (tlsstr_key == TLS_OUT_OF_INDEXES) {
- tlsstr_key = TlsAlloc();
- monitor_key = TlsAlloc();
- if ((tlsstr_key == TLS_OUT_OF_INDEXES) || (monitor_key == TLS_OUT_OF_INDEXES)) {
- fprintf(stderr, __FILE__ ": CRITICAL: Unable to allocate TLS key (%d)\n", (int)GetLastError());
- exit(-1);
- }
- }
-
- ReleaseMutex(mutex);
-
- CloseHandle(mutex);
-}
-
-/*
- * This is incredibly brain dead, but this is necessary when dealing with
- * the hell that is Win32.
- */
-struct monitor_data {
- HANDLE thread;
- void *data;
-};
-
-static DWORD WINAPI monitor_thread(LPVOID param) {
- struct monitor_data *data;
-
- data = (struct monitor_data*)param;
- assert(data);
-
- WaitForSingleObject(data->thread, INFINITE);
-
- CloseHandle(data->thread);
- pa_xfree(data->data);
- pa_xfree(data);
-
- return 0;
-}
-
-static void start_monitor(void) {
- HANDLE thread;
- struct monitor_data *data;
-
- data = pa_xnew(struct monitor_data, 1);
- assert(data);
-
- DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),
- GetCurrentProcess(), &data->thread, 0, FALSE, DUPLICATE_SAME_ACCESS);
-
- thread = CreateThread(NULL, 0, monitor_thread, data, 0, NULL);
- assert(thread);
-
- TlsSetValue(monitor_key, data);
-
- CloseHandle(thread);
-}
-
-#else
-
-/* Unsafe, but we have no choice */
-static char *tlsstr;
-
-#endif
+PA_STATIC_TLS_DECLARE(cstrerror, pa_xfree);
const char* pa_cstrerror(int errnum) {
- const char *origbuf;
-
-#ifdef HAVE_STRERROR_R
+ const char *original = NULL;
+ char *translated, *t;
char errbuf[128];
-#endif
-
-#ifdef HAVE_PTHREAD
- char *tlsstr;
-
- pthread_once(&cstrerror_once, inittls);
-
- tlsstr = pthread_getspecific(tlsstr_key);
-#elif defined(HAVE_WINDOWS_H)
- char *tlsstr;
- struct monitor_data *data;
-
- inittls();
-
- tlsstr = TlsGetValue(tlsstr_key);
- if (!tlsstr)
- start_monitor();
- data = TlsGetValue(monitor_key);
-#endif
-
- if (tlsstr)
- pa_xfree(tlsstr);
-#ifdef HAVE_STRERROR_R
+ if ((t = PA_STATIC_TLS_GET(cstrerror)))
+ pa_xfree(t);
-#ifdef __GLIBC__
- origbuf = strerror_r(errnum, errbuf, sizeof(errbuf));
- if (origbuf == NULL)
- origbuf = "";
-#else
+#if defined(HAVE_STRERROR_R) && defined(__GLIBC__)
+ original = strerror_r(errnum, errbuf, sizeof(errbuf));
+#elif defined(HAVE_STRERROR_R)
if (strerror_r(errnum, errbuf, sizeof(errbuf)) == 0) {
- origbuf = errbuf;
- errbuf[sizeof(errbuf) - 1] = '\0';
- } else
- origbuf = "";
-#endif
-
+ errbuf[sizeof(errbuf) - 1] = 0;
+ original = errbuf;
+ }
#else
/* This might not be thread safe, but we hope for the best */
- origbuf = strerror(errnum);
+ original = strerror(errnum);
#endif
- tlsstr = pa_locale_to_utf8(origbuf);
- if (!tlsstr) {
- fprintf(stderr, "Unable to convert, filtering\n");
- tlsstr = pa_utf8_filter(origbuf);
+ if (!original) {
+ pa_snprintf(errbuf, sizeof(errbuf), "Unknown error %i", errnum);
+ original = errbuf;
}
-#ifdef HAVE_PTHREAD
- pthread_setspecific(tlsstr_key, tlsstr);
-#elif defined(HAVE_WINDOWS_H)
- TlsSetValue(tlsstr_key, tlsstr);
- data->data = tlsstr;
-#endif
+ if (!(translated = pa_locale_to_utf8(original))) {
+ pa_log_warn("Unable to convert error string to locale, filtering.");
+ translated = pa_utf8_filter(original);
+ }
+
+ PA_STATIC_TLS_SET(cstrerror, translated);
- return tlsstr;
+ return translated;
}
diff --git a/src/pulsecore/core-error.h b/src/pulsecore/core-error.h
index 32da8bf2..b0c306c7 100644
--- a/src/pulsecore/core-error.h
+++ b/src/pulsecore/core-error.h
@@ -1,21 +1,22 @@
#ifndef foocoreerrorhfoo
#define foocoreerrorhfoo
-/* $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
diff --git a/src/pulsecore/core-scache.c b/src/pulsecore/core-scache.c
index e3bf3ca2..75fa2ff1 100644
--- a/src/pulsecore/core-scache.c
+++ b/src/pulsecore/core-scache.c
@@ -1,18 +1,19 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
+ Copyright 2004-2008 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
@@ -23,7 +24,6 @@
#include <config.h>
#endif
-#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
@@ -57,15 +57,19 @@
#include <pulsecore/core-util.h>
#include <pulsecore/log.h>
#include <pulsecore/core-error.h>
+#include <pulsecore/macro.h>
#include "core-scache.h"
-#define UNLOAD_POLL_TIME 2
+#define UNLOAD_POLL_TIME 60
static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
pa_core *c = userdata;
struct timeval ntv;
- assert(c && c->mainloop == m && c->scache_auto_unload_event == e);
+
+ pa_assert(c);
+ pa_assert(c->mainloop == m);
+ pa_assert(c->scache_auto_unload_event == e);
pa_scache_unload_unused(c);
@@ -75,31 +79,37 @@ static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED
}
static void free_entry(pa_scache_entry *e) {
- assert(e);
+ pa_assert(e);
+
pa_namereg_unregister(e->core, e->name);
pa_subscription_post(e->core, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_REMOVE, e->index);
pa_xfree(e->name);
pa_xfree(e->filename);
if (e->memchunk.memblock)
pa_memblock_unref(e->memchunk.memblock);
+ if (e->proplist)
+ pa_proplist_free(e->proplist);
pa_xfree(e);
}
static pa_scache_entry* scache_add_item(pa_core *c, const char *name) {
pa_scache_entry *e;
- assert(c && name);
+
+ pa_assert(c);
+ pa_assert(name);
if ((e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 0))) {
if (e->memchunk.memblock)
pa_memblock_unref(e->memchunk.memblock);
pa_xfree(e->filename);
-
- assert(e->core == c);
+ pa_proplist_clear(e->proplist);
+
+ pa_assert(e->core == c);
pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index);
} else {
- e = pa_xmalloc(sizeof(pa_scache_entry));
+ e = pa_xnew(pa_scache_entry, 1);
if (!pa_namereg_register(c, name, PA_NAMEREG_SAMPLE, e, 1)) {
pa_xfree(e);
@@ -108,11 +118,10 @@ static pa_scache_entry* scache_add_item(pa_core *c, const char *name) {
e->name = pa_xstrdup(name);
e->core = c;
+ e->proplist = pa_proplist_new();
- if (!c->scache) {
+ if (!c->scache)
c->scache = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
- assert(c->scache);
- }
pa_idxset_put(c->scache, e, &e->index);
@@ -120,22 +129,40 @@ static pa_scache_entry* scache_add_item(pa_core *c, const char *name) {
}
e->last_used_time = 0;
- e->memchunk.memblock = NULL;
- e->memchunk.index = e->memchunk.length = 0;
+ pa_memchunk_reset(&e->memchunk);
e->filename = NULL;
- e->lazy = 0;
+ e->lazy = FALSE;
e->last_used_time = 0;
memset(&e->sample_spec, 0, sizeof(e->sample_spec));
pa_channel_map_init(&e->channel_map);
pa_cvolume_reset(&e->volume, PA_CHANNELS_MAX);
+ pa_proplist_sets(e->proplist, PA_PROP_MEDIA_ROLE, "event");
+
return e;
}
-int pa_scache_add_item(pa_core *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map, const pa_memchunk *chunk, uint32_t *idx) {
+int pa_scache_add_item(
+ pa_core *c,
+ const char *name,
+ const pa_sample_spec *ss,
+ const pa_channel_map *map,
+ const pa_memchunk *chunk,
+ pa_proplist *p,
+ uint32_t *idx) {
+
pa_scache_entry *e;
- assert(c && name);
+ char st[PA_SAMPLE_SPEC_SNPRINT_MAX];
+ pa_channel_map tmap;
+
+ pa_assert(c);
+ pa_assert(name);
+ pa_assert(!ss || pa_sample_spec_valid(ss));
+ pa_assert(!map || (pa_channel_map_valid(map) && ss && ss->channels == map->channels));
+
+ if (ss && !map)
+ pa_channel_map_init_extend(&tmap, ss->channels, PA_CHANNEL_MAP_DEFAULT);
if (chunk && chunk->length > PA_SCACHE_ENTRY_SIZE_MAX)
return -1;
@@ -143,9 +170,11 @@ int pa_scache_add_item(pa_core *c, const char *name, const pa_sample_spec *ss, c
if (!(e = scache_add_item(c, name)))
return -1;
+ memset(&e->sample_spec, 0, sizeof(e->sample_spec));
+ pa_channel_map_init(&e->channel_map);
+
if (ss) {
e->sample_spec = *ss;
- pa_channel_map_init_auto(&e->channel_map, ss->channels, PA_CHANNEL_MAP_DEFAULT);
e->volume.channels = e->sample_spec.channels;
}
@@ -157,9 +186,16 @@ int pa_scache_add_item(pa_core *c, const char *name, const pa_sample_spec *ss, c
pa_memblock_ref(e->memchunk.memblock);
}
+ if (p)
+ pa_proplist_update(e->proplist, PA_UPDATE_REPLACE, p);
+
if (idx)
*idx = e->index;
+ pa_log_debug("Created sample \"%s\" (#%d), %lu bytes with sample spec %s",
+ name, e->index, (unsigned long) e->memchunk.length,
+ pa_sample_spec_snprint(st, sizeof(st), &e->sample_spec));
+
return 0;
}
@@ -168,6 +204,7 @@ int pa_scache_add_file(pa_core *c, const char *name, const char *filename, uint3
pa_channel_map map;
pa_memchunk chunk;
int r;
+ pa_proplist *p;
#ifdef OS_IS_WIN32
char buf[MAX_PATH];
@@ -176,11 +213,18 @@ int pa_scache_add_file(pa_core *c, const char *name, const char *filename, uint3
filename = buf;
#endif
+ pa_assert(c);
+ pa_assert(name);
+ pa_assert(filename);
+
if (pa_sound_file_load(c->mempool, filename, &ss, &map, &chunk) < 0)
return -1;
-
- r = pa_scache_add_item(c, name, &ss, &map, &chunk, idx);
+
+ p = pa_proplist_new();
+ pa_proplist_sets(p, PA_PROP_MEDIA_FILENAME, filename);
+ r = pa_scache_add_item(c, name, &ss, &map, &chunk, p, idx);
pa_memblock_unref(chunk.memblock);
+ pa_proplist_free(p);
return r;
}
@@ -195,14 +239,18 @@ int pa_scache_add_file_lazy(pa_core *c, const char *name, const char *filename,
filename = buf;
#endif
- assert(c && name);
+ pa_assert(c);
+ pa_assert(name);
+ pa_assert(filename);
if (!(e = scache_add_item(c, name)))
return -1;
- e->lazy = 1;
+ e->lazy = TRUE;
e->filename = pa_xstrdup(filename);
-
+
+ pa_proplist_sets(e->proplist, PA_PROP_MEDIA_FILENAME, filename);
+
if (!c->scache_auto_unload_event) {
struct timeval ntv;
pa_gettimeofday(&ntv);
@@ -218,26 +266,31 @@ int pa_scache_add_file_lazy(pa_core *c, const char *name, const char *filename,
int pa_scache_remove_item(pa_core *c, const char *name) {
pa_scache_entry *e;
- assert(c && name);
+
+ pa_assert(c);
+ pa_assert(name);
if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 0)))
return -1;
- if (pa_idxset_remove_by_data(c->scache, e, NULL) != e)
- assert(0);
+ pa_assert_se(pa_idxset_remove_by_data(c->scache, e, NULL) == e);
+
+ pa_log_debug("Removed sample \"%s\"", name);
free_entry(e);
+
return 0;
}
static void free_cb(void *p, PA_GCC_UNUSED void *userdata) {
pa_scache_entry *e = p;
- assert(e);
+ pa_assert(e);
+
free_entry(e);
}
void pa_scache_free(pa_core *c) {
- assert(c);
+ pa_assert(c);
if (c->scache) {
pa_idxset_free(c->scache, free_cb, NULL);
@@ -248,14 +301,14 @@ void pa_scache_free(pa_core *c) {
c->mainloop->time_free(c->scache_auto_unload_event);
}
-int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t volume) {
+int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx) {
pa_scache_entry *e;
- char *t;
pa_cvolume r;
-
- assert(c);
- assert(name);
- assert(sink);
+ pa_proplist *merged;
+
+ pa_assert(c);
+ pa_assert(name);
+ pa_assert(sink);
if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 1)))
return -1;
@@ -269,31 +322,54 @@ int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t
if (e->volume.channels > e->sample_spec.channels)
e->volume.channels = e->sample_spec.channels;
}
-
+
if (!e->memchunk.memblock)
return -1;
- t = pa_sprintf_malloc("sample:%s", name);
+ pa_log_debug("Playing sample \"%s\" on \"%s\"", name, sink->name);
pa_cvolume_set(&r, e->volume.channels, volume);
pa_sw_cvolume_multiply(&r, &r, &e->volume);
- if (pa_play_memchunk(sink, t, &e->sample_spec, &e->channel_map, &e->memchunk, &r) < 0) {
- pa_xfree(t);
+ merged = pa_proplist_new();
+
+ pa_proplist_setf(merged, PA_PROP_MEDIA_NAME, "Sample %s", name);
+
+ pa_proplist_update(merged, PA_UPDATE_REPLACE, e->proplist);
+
+ if (p)
+ pa_proplist_update(merged, PA_UPDATE_REPLACE, p);
+
+ if (pa_play_memchunk(sink, &e->sample_spec, &e->channel_map, &e->memchunk, &r, merged, sink_input_idx) < 0) {
+ pa_proplist_free(merged);
return -1;
}
- pa_xfree(t);
+ pa_proplist_free(merged);
if (e->lazy)
time(&e->last_used_time);
-
+
return 0;
}
-const char * pa_scache_get_name_by_id(pa_core *c, uint32_t id) {
+int pa_scache_play_item_by_name(pa_core *c, const char *name, const char*sink_name, pa_bool_t autoload, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx) {
+ pa_sink *sink;
+
+ pa_assert(c);
+ pa_assert(name);
+
+ if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK, autoload)))
+ return -1;
+
+ return pa_scache_play_item(c, name, sink, volume, p, sink_input_idx);
+}
+
+const char *pa_scache_get_name_by_id(pa_core *c, uint32_t id) {
pa_scache_entry *e;
- assert(c && id != PA_IDXSET_INVALID);
+
+ pa_assert(c);
+ pa_assert(id != PA_IDXSET_INVALID);
if (!c->scache || !(e = pa_idxset_get_by_index(c->scache, id)))
return NULL;
@@ -303,7 +379,9 @@ const char * pa_scache_get_name_by_id(pa_core *c, uint32_t id) {
uint32_t pa_scache_get_id_by_name(pa_core *c, const char *name) {
pa_scache_entry *e;
- assert(c && name);
+
+ pa_assert(c);
+ pa_assert(name);
if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 0)))
return PA_IDXSET_INVALID;
@@ -311,14 +389,16 @@ uint32_t pa_scache_get_id_by_name(pa_core *c, const char *name) {
return e->index;
}
-uint32_t pa_scache_total_size(pa_core *c) {
+size_t pa_scache_total_size(pa_core *c) {
pa_scache_entry *e;
- uint32_t idx, sum = 0;
- assert(c);
+ uint32_t idx;
+ size_t sum = 0;
+
+ pa_assert(c);
if (!c->scache || !pa_idxset_size(c->scache))
return 0;
-
+
for (e = pa_idxset_first(c->scache, &idx); e; e = pa_idxset_next(c->scache, &idx))
if (e->memchunk.memblock)
sum += e->memchunk.length;
@@ -330,11 +410,12 @@ void pa_scache_unload_unused(pa_core *c) {
pa_scache_entry *e;
time_t now;
uint32_t idx;
- assert(c);
+
+ pa_assert(c);
if (!c->scache || !pa_idxset_size(c->scache))
return;
-
+
time(&now);
for (e = pa_idxset_first(c->scache, &idx); e; e = pa_idxset_next(c->scache, &idx)) {
@@ -344,10 +425,9 @@ void pa_scache_unload_unused(pa_core *c) {
if (e->last_used_time + c->scache_idle_time > now)
continue;
-
+
pa_memblock_unref(e->memchunk.memblock);
- e->memchunk.memblock = NULL;
- e->memchunk.index = e->memchunk.length = 0;
+ pa_memchunk_reset(&e->memchunk);
pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index);
}
@@ -357,8 +437,11 @@ static void add_file(pa_core *c, const char *pathname) {
struct stat st;
const char *e;
+ pa_core_assert_ref(c);
+ pa_assert(pathname);
+
e = pa_path_get_filename(pathname);
-
+
if (stat(pathname, &st) < 0) {
pa_log("stat('%s'): %s", pathname, pa_cstrerror(errno));
return;
@@ -372,7 +455,9 @@ static void add_file(pa_core *c, const char *pathname) {
int pa_scache_add_directory_lazy(pa_core *c, const char *pathname) {
DIR *dir;
- assert(c && pathname);
+
+ pa_core_assert_ref(c);
+ pa_assert(pathname);
/* First try to open this as directory */
if (!(dir = opendir(pathname))) {
@@ -388,7 +473,7 @@ int pa_scache_add_directory_lazy(pa_core *c, const char *pathname) {
for (i = 0; i < p.gl_pathc; i++)
add_file(c, p.gl_pathv[i]);
-
+
globfree(&p);
#else
return -1;
@@ -402,11 +487,12 @@ int pa_scache_add_directory_lazy(pa_core *c, const char *pathname) {
if (e->d_name[0] == '.')
continue;
- snprintf(p, sizeof(p), "%s/%s", pathname, e->d_name);
+ pa_snprintf(p, sizeof(p), "%s/%s", pathname, e->d_name);
add_file(c, p);
}
+
+ closedir(dir);
}
- closedir(dir);
return 0;
}
diff --git a/src/pulsecore/core-scache.h b/src/pulsecore/core-scache.h
index d01aae9b..80e0fd04 100644
--- a/src/pulsecore/core-scache.h
+++ b/src/pulsecore/core-scache.h
@@ -1,21 +1,22 @@
#ifndef foocorescachehfoo
#define foocorescachehfoo
-/* $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
@@ -26,38 +27,42 @@
#include <pulsecore/memchunk.h>
#include <pulsecore/sink.h>
-#define PA_SCACHE_ENTRY_SIZE_MAX (1024*1024*2)
+#define PA_SCACHE_ENTRY_SIZE_MAX (1024*1024*16)
typedef struct pa_scache_entry {
- pa_core *core;
uint32_t index;
+ pa_core *core;
+
char *name;
-
+
pa_cvolume volume;
pa_sample_spec sample_spec;
pa_channel_map channel_map;
pa_memchunk memchunk;
char *filename;
-
- int lazy;
+
+ pa_bool_t lazy;
time_t last_used_time;
+
+ pa_proplist *proplist;
} pa_scache_entry;
-int pa_scache_add_item(pa_core *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map, const pa_memchunk *chunk, uint32_t *idx);
+int pa_scache_add_item(pa_core *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map, const pa_memchunk *chunk, pa_proplist *p, uint32_t *idx);
int pa_scache_add_file(pa_core *c, const char *name, const char *filename, uint32_t *idx);
int pa_scache_add_file_lazy(pa_core *c, const char *name, const char *filename, uint32_t *idx);
int pa_scache_add_directory_lazy(pa_core *c, const char *pathname);
int pa_scache_remove_item(pa_core *c, const char *name);
-int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t volume);
+int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx);
+int pa_scache_play_item_by_name(pa_core *c, const char *name, const char*sink_name, pa_bool_t autoload, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx);
void pa_scache_free(pa_core *c);
const char *pa_scache_get_name_by_id(pa_core *c, uint32_t id);
uint32_t pa_scache_get_id_by_name(pa_core *c, const char *name);
-uint32_t pa_scache_total_size(pa_core *c);
+size_t pa_scache_total_size(pa_core *c);
void pa_scache_unload_unused(pa_core *c);
diff --git a/src/pulsecore/core-subscribe.c b/src/pulsecore/core-subscribe.c
index 4df1d511..6107002b 100644
--- a/src/pulsecore/core-subscribe.c
+++ b/src/pulsecore/core-subscribe.c
@@ -1,18 +1,18 @@
-/* $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
@@ -24,12 +24,12 @@
#endif
#include <stdio.h>
-#include <assert.h>
#include <pulse/xmalloc.h>
#include <pulsecore/queue.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
#include "core-subscribe.h"
@@ -65,10 +65,10 @@ static void sched_event(pa_core *c);
/* Allocate a new subscription object for the given subscription mask. Use the specified callback function and user data */
pa_subscription* pa_subscription_new(pa_core *c, pa_subscription_mask_t m, pa_subscription_cb_t callback, void *userdata) {
pa_subscription *s;
-
- assert(c);
- assert(m);
- assert(callback);
+
+ pa_assert(c);
+ pa_assert(m);
+ pa_assert(callback);
s = pa_xnew(pa_subscription, 1);
s->core = c;
@@ -83,36 +83,36 @@ pa_subscription* pa_subscription_new(pa_core *c, pa_subscription_mask_t m, pa_su
/* Free a subscription object, effectively marking it for deletion */
void pa_subscription_free(pa_subscription*s) {
- assert(s);
- assert(!s->dead);
-
+ pa_assert(s);
+ pa_assert(!s->dead);
+
s->dead = 1;
sched_event(s->core);
}
static void free_subscription(pa_subscription *s) {
- assert(s);
- assert(s->core);
+ pa_assert(s);
+ pa_assert(s->core);
PA_LLIST_REMOVE(pa_subscription, s->core->subscriptions, s);
pa_xfree(s);
}
static void free_event(pa_subscription_event *s) {
- assert(s);
- assert(s->core);
+ pa_assert(s);
+ pa_assert(s->core);
if (!s->next)
s->core->subscription_event_last = s->prev;
-
+
PA_LLIST_REMOVE(pa_subscription_event, s->core->subscription_event_queue, s);
pa_xfree(s);
}
/* Free all subscription objects */
void pa_subscription_free_all(pa_core *c) {
- assert(c);
-
+ pa_assert(c);
+
while (c->subscriptions)
free_subscription(c->subscriptions);
@@ -157,10 +157,10 @@ static void dump_event(const char * prefix, pa_subscription_event*e) {
static void defer_cb(pa_mainloop_api *m, pa_defer_event *de, void *userdata) {
pa_core *c = userdata;
pa_subscription *s;
-
- assert(c->mainloop == m);
- assert(c);
- assert(c->subscription_defer_event == de);
+
+ pa_assert(c->mainloop == m);
+ pa_assert(c);
+ pa_assert(c->subscription_defer_event == de);
c->mainloop->defer_enable(c->subscription_defer_event, 0);
@@ -170,7 +170,7 @@ static void defer_cb(pa_mainloop_api *m, pa_defer_event *de, void *userdata) {
pa_subscription_event *e = c->subscription_event_queue;
for (s = c->subscriptions; s; s = s->next) {
-
+
if (!s->dead && pa_subscription_match_flags(s->mask, e->type))
s->callback(c, e->type, e->index, s->userdata);
}
@@ -182,7 +182,7 @@ static void defer_cb(pa_mainloop_api *m, pa_defer_event *de, void *userdata) {
}
/* Remove dead subscriptions */
-
+
s = c->subscriptions;
while (s) {
pa_subscription *n = s->next;
@@ -194,38 +194,38 @@ static void defer_cb(pa_mainloop_api *m, pa_defer_event *de, void *userdata) {
/* Schedule an mainloop event so that a pending subscription event is dispatched */
static void sched_event(pa_core *c) {
- assert(c);
+ pa_assert(c);
if (!c->subscription_defer_event) {
c->subscription_defer_event = c->mainloop->defer_new(c->mainloop, defer_cb, c);
- assert(c->subscription_defer_event);
+ pa_assert(c->subscription_defer_event);
}
-
+
c->mainloop->defer_enable(c->subscription_defer_event, 1);
}
/* Append a new subscription event to the subscription event queue and schedule a main loop event */
-void pa_subscription_post(pa_core *c, pa_subscription_event_type_t t, uint32_t index) {
+void pa_subscription_post(pa_core *c, pa_subscription_event_type_t t, uint32_t idx) {
pa_subscription_event *e;
- assert(c);
+ pa_assert(c);
/* No need for queuing subscriptions of noone is listening */
if (!c->subscriptions)
return;
-
+
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_NEW) {
pa_subscription_event *i, *n;
-
+
/* Check for duplicates */
for (i = c->subscription_event_last; i; i = n) {
n = i->prev;
-
+
/* not the same object type */
if (((t ^ i->type) & PA_SUBSCRIPTION_EVENT_FACILITY_MASK))
continue;
-
+
/* not the same object */
- if (i->index != index)
+ if (i->index != idx)
continue;
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
@@ -251,9 +251,9 @@ void pa_subscription_post(pa_core *c, pa_subscription_event_type_t t, uint32_t i
e = pa_xnew(pa_subscription_event, 1);
e->core = c;
e->type = t;
- e->index = index;
+ e->index = idx;
- PA_LLIST_INSERT_AFTER(pa_subscription_event, c->subscription_event_queue, c->subscription_event_last, e);
+ PA_LLIST_INSERT_AFTER(pa_subscription_event, c->subscription_event_queue, c->subscription_event_last, e);
c->subscription_event_last = e;
#ifdef DEBUG
diff --git a/src/pulsecore/core-subscribe.h b/src/pulsecore/core-subscribe.h
index 6e3b646e..2f9730d9 100644
--- a/src/pulsecore/core-subscribe.h
+++ b/src/pulsecore/core-subscribe.h
@@ -1,21 +1,21 @@
#ifndef foocoresubscribehfoo
#define foocoresubscribehfoo
-/* $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
diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c
index 5f72b342..d259fb16 100644
--- a/src/pulsecore/core-util.c
+++ b/src/pulsecore/core-util.c
@@ -1,18 +1,20 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
+ Copyright 2004-2006 Lennart Poettering
+ Copyright 2004 Joe Marcus Clarke
+ Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
@@ -27,7 +29,6 @@
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
-#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
@@ -38,6 +39,11 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
+#include <dirent.h>
+
+#ifdef HAVE_STRTOF_L
+#include <locale.h>
+#endif
#ifdef HAVE_SCHED_H
#include <sched.h>
@@ -47,6 +53,14 @@
#include <sys/resource.h>
#endif
+#ifdef HAVE_SYS_CAPABILITY_H
+#include <sys/capability.h>
+#endif
+
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+
#ifdef HAVE_PTHREAD
#include <pthread.h>
#endif
@@ -67,14 +81,19 @@
#include <grp.h>
#endif
+#ifdef HAVE_LIBSAMPLERATE
#include <samplerate.h>
+#endif
#include <pulse/xmalloc.h>
#include <pulse/util.h>
+#include <pulse/utf8.h>
#include <pulsecore/core-error.h>
#include <pulsecore/winsock.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/thread.h>
#include "core-util.h"
@@ -83,14 +102,6 @@
#define MSG_NOSIGNAL 0
#endif
-#ifndef OS_IS_WIN32
-#define PA_USER_RUNTIME_PATH_PREFIX "/tmp/pulse-"
-#define PATH_SEP '/'
-#else
-#define PA_USER_RUNTIME_PATH_PREFIX "%TEMP%\\pulse-"
-#define PATH_SEP '\\'
-#endif
-
#ifdef OS_IS_WIN32
#define PULSE_ROOTENV "PULSE_ROOT"
@@ -103,7 +114,7 @@ int pa_set_root(HANDLE handle) {
if (!GetModuleFileName(handle, library_path + sizeof(PULSE_ROOTENV), MAX_PATH))
return 0;
- sep = strrchr(library_path, '\\');
+ sep = strrchr(library_path, PA_PATH_SEP_CHAR);
if (sep)
*sep = '\0';
@@ -116,31 +127,50 @@ int pa_set_root(HANDLE handle) {
#endif
/** Make a file descriptor nonblock. Doesn't do any error checking */
-void pa_make_nonblock_fd(int fd) {
+void pa_make_fd_nonblock(int fd) {
+
#ifdef O_NONBLOCK
int v;
- assert(fd >= 0);
+ pa_assert(fd >= 0);
+
+ pa_assert_se((v = fcntl(fd, F_GETFL)) >= 0);
+
+ if (!(v & O_NONBLOCK))
+ pa_assert_se(fcntl(fd, F_SETFL, v|O_NONBLOCK) >= 0);
- if ((v = fcntl(fd, F_GETFL)) >= 0)
- if (!(v & O_NONBLOCK))
- fcntl(fd, F_SETFL, v|O_NONBLOCK);
#elif defined(OS_IS_WIN32)
u_long arg = 1;
if (ioctlsocket(fd, FIONBIO, &arg) < 0) {
- if (WSAGetLastError() == WSAENOTSOCK)
- pa_log_warn("WARNING: Only sockets can be made non-blocking!");
+ pa_assert_se(WSAGetLastError() == WSAENOTSOCK);
+ pa_log_warn("Only sockets can be made non-blocking!");
}
#else
- pa_log_warn("WARNING: Non-blocking I/O not supported.!");
+ pa_log_warn("Non-blocking I/O not supported.!");
+#endif
+
+}
+
+/* Set the FD_CLOEXEC flag for a fd */
+void pa_make_fd_cloexec(int fd) {
+
+#ifdef FD_CLOEXEC
+ int v;
+ pa_assert(fd >= 0);
+
+ pa_assert_se((v = fcntl(fd, F_GETFD, 0)) >= 0);
+
+ if (!(v & FD_CLOEXEC))
+ pa_assert_se(fcntl(fd, F_SETFD, v|FD_CLOEXEC) >= 0);
#endif
+
}
/** Creates a directory securely */
int pa_make_secure_dir(const char* dir, mode_t m, uid_t uid, gid_t gid) {
struct stat st;
int r;
-
- assert(dir);
+
+ pa_assert(dir);
#ifdef OS_IS_WIN32
r = mkdir(dir);
@@ -152,7 +182,7 @@ int pa_make_secure_dir(const char* dir, mode_t m, uid_t uid, gid_t gid) {
umask(u);
}
#endif
-
+
if (r < 0 && errno != EEXIST)
return -1;
@@ -161,20 +191,20 @@ int pa_make_secure_dir(const char* dir, mode_t m, uid_t uid, gid_t gid) {
uid = getuid();
if (gid == (gid_t)-1)
gid = getgid();
- chown(dir, uid, gid);
+ (void) chown(dir, uid, gid);
#endif
-
+
#ifdef HAVE_CHMOD
chmod(dir, m);
#endif
-
+
#ifdef HAVE_LSTAT
if (lstat(dir, &st) < 0)
#else
if (stat(dir, &st) < 0)
#endif
goto fail;
-
+
#ifndef OS_IS_WIN32
if (!S_ISDIR(st.st_mode) ||
(st.st_uid != uid) ||
@@ -184,11 +214,11 @@ int pa_make_secure_dir(const char* dir, mode_t m, uid_t uid, gid_t gid) {
goto fail;
}
#else
- pa_log_warn("secure directory creation not supported on Win32.");
+ pa_log_warn("Secure directory creation not supported on Win32.");
#endif
-
+
return 0;
-
+
fail:
rmdir(dir);
return -1;
@@ -214,12 +244,12 @@ int pa_make_secure_parent_dir(const char *fn, mode_t m, uid_t uid, gid_t gid) {
if (!(dir = pa_parent_dir(fn)))
goto finish;
-
+
if (pa_make_secure_dir(dir, m, uid, gid) < 0)
goto finish;
ret = 0;
-
+
finish:
pa_xfree(dir);
return ret;
@@ -237,7 +267,7 @@ ssize_t pa_read(int fd, void *buf, size_t count, int *type) {
if (!type || *type == 0) {
ssize_t r;
-
+
if ((r = recv(fd, buf, count, 0)) >= 0)
return r;
@@ -251,7 +281,7 @@ ssize_t pa_read(int fd, void *buf, size_t count, int *type) {
}
#endif
-
+
return read(fd, buf, count);
}
@@ -263,7 +293,7 @@ ssize_t pa_write(int fd, const void *buf, size_t count, int *type) {
if ((r = send(fd, buf, count, MSG_NOSIGNAL)) >= 0)
return r;
-
+
#ifdef OS_IS_WIN32
if (WSAGetLastError() != WSAENOTSOCK) {
errno = WSAGetLastError();
@@ -286,10 +316,10 @@ ssize_t pa_write(int fd, const void *buf, size_t count, int *type) {
ssize_t pa_loop_read(int fd, void*data, size_t size, int *type) {
ssize_t ret = 0;
int _type;
-
- assert(fd >= 0);
- assert(data);
- assert(size);
+
+ pa_assert(fd >= 0);
+ pa_assert(data);
+ pa_assert(size);
if (!type) {
_type = 0;
@@ -304,7 +334,7 @@ ssize_t pa_loop_read(int fd, void*data, size_t size, int *type) {
if (r == 0)
break;
-
+
ret += r;
data = (uint8_t*) data + r;
size -= r;
@@ -318,9 +348,9 @@ ssize_t pa_loop_write(int fd, const void*data, size_t size, int *type) {
ssize_t ret = 0;
int _type;
- assert(fd >= 0);
- assert(data);
- assert(size);
+ pa_assert(fd >= 0);
+ pa_assert(data);
+ pa_assert(size);
if (!type) {
_type = 0;
@@ -335,7 +365,7 @@ ssize_t pa_loop_write(int fd, const void*data, size_t size, int *type) {
if (r == 0)
break;
-
+
ret += r;
data = (const uint8_t*) data + r;
size -= r;
@@ -344,6 +374,25 @@ ssize_t pa_loop_write(int fd, const void*data, size_t size, int *type) {
return ret;
}
+/** Platform independent read function. Necessary since not all
+ * systems treat all file descriptors equal. */
+int pa_close(int fd) {
+
+#ifdef OS_IS_WIN32
+ int ret;
+
+ if ((ret = closesocket(fd)) == 0)
+ return 0;
+
+ if (WSAGetLastError() != WSAENOTSOCK) {
+ errno = WSAGetLastError();
+ return ret;
+ }
+#endif
+
+ return close(fd);
+}
+
/* Print a warning messages in case that the given signal is not
* blocked or trapped */
void pa_check_signal_is_blocked(int sig) {
@@ -354,8 +403,8 @@ void pa_check_signal_is_blocked(int sig) {
/* If POSIX threads are supported use thread-aware
* pthread_sigmask() function, to check if the signal is
* blocked. Otherwise fall back to sigprocmask() */
-
-#ifdef HAVE_PTHREAD
+
+#ifdef HAVE_PTHREAD
if (pthread_sigmask(SIG_SETMASK, NULL, &set) < 0) {
#endif
if (sigprocmask(SIG_SETMASK, NULL, &set) < 0) {
@@ -370,18 +419,18 @@ void pa_check_signal_is_blocked(int sig) {
return;
/* Check whether the signal is trapped */
-
+
if (sigaction(sig, NULL, &sa) < 0) {
pa_log("sigaction(): %s", pa_cstrerror(errno));
return;
}
-
+
if (sa.sa_handler != SIG_DFL)
return;
-
- pa_log("WARNING: %s is not trapped. This might cause malfunction!", pa_strsignal(sig));
+
+ pa_log_warn("%s is not trapped. This might cause malfunction!", pa_sig2str(sig));
#else /* HAVE_SIGACTION */
- pa_log("WARNING: %s might not be trapped. This might cause malfunction!", pa_strsignal(sig));
+ pa_log_warn("%s might not be trapped. This might cause malfunction!", pa_sig2str(sig));
#endif
}
@@ -390,9 +439,9 @@ void pa_check_signal_is_blocked(int sig) {
char *pa_sprintf_malloc(const char *format, ...) {
int size = 100;
char *c = NULL;
-
- assert(format);
-
+
+ pa_assert(format);
+
for(;;) {
int r;
va_list ap;
@@ -402,12 +451,14 @@ char *pa_sprintf_malloc(const char *format, ...) {
va_start(ap, format);
r = vsnprintf(c, size, format, ap);
va_end(ap);
-
+
+ c[size-1] = 0;
+
if (r > -1 && r < size)
return c;
if (r > -1) /* glibc 2.1 */
- size = r+1;
+ size = r+1;
else /* glibc 2.0 */
size *= 2;
}
@@ -418,25 +469,26 @@ char *pa_sprintf_malloc(const char *format, ...) {
char *pa_vsprintf_malloc(const char *format, va_list ap) {
int size = 100;
char *c = NULL;
-
- assert(format);
-
+
+ pa_assert(format);
+
for(;;) {
int r;
va_list aq;
- va_copy(aq, ap);
-
c = pa_xrealloc(c, size);
- r = vsnprintf(c, size, format, aq);
+ va_copy(aq, ap);
+ r = vsnprintf(c, size, format, aq);
va_end(aq);
-
+
+ c[size-1] = 0;
+
if (r > -1 && r < size)
return c;
if (r > -1) /* glibc 2.1 */
- size = r+1;
+ size = r+1;
else /* glibc 2.0 */
size *= 2;
}
@@ -444,96 +496,193 @@ char *pa_vsprintf_malloc(const char *format, va_list ap) {
/* Similar to OpenBSD's strlcpy() function */
char *pa_strlcpy(char *b, const char *s, size_t l) {
- assert(b && s && l > 0);
+ pa_assert(b);
+ pa_assert(s);
+ pa_assert(l > 0);
strncpy(b, s, l);
b[l-1] = 0;
return b;
}
-#define NICE_LEVEL (-15)
+/* Make the current thread a realtime thread, and acquire the highest
+ * rtprio we can get that is less or equal the specified parameter. If
+ * the thread is already realtime, don't do anything. */
+int pa_make_realtime(int rtprio) {
-/* Raise the priority of the current process as much as possible and
-sensible: set the nice level to -15 and enable realtime scheduling if
-supported.*/
-void pa_raise_priority(void) {
-
-#ifdef HAVE_SYS_RESOURCE_H
- if (setpriority(PRIO_PROCESS, 0, NICE_LEVEL) < 0)
- pa_log_warn("setpriority(): %s", pa_cstrerror(errno));
- else
- pa_log_info("Successfully gained nice level %i.", NICE_LEVEL);
-#endif
-
#ifdef _POSIX_PRIORITY_SCHEDULING
- {
- struct sched_param sp;
+ struct sched_param sp;
+ int r, policy;
- if (sched_getparam(0, &sp) < 0) {
- pa_log("sched_getparam(): %s", pa_cstrerror(errno));
- return;
- }
-
- sp.sched_priority = 1;
- if (sched_setscheduler(0, SCHED_FIFO, &sp) < 0) {
- pa_log_warn("sched_setscheduler(): %s", pa_cstrerror(errno));
- return;
+ memset(&sp, 0, sizeof(sp));
+ policy = 0;
+
+ if ((r = pthread_getschedparam(pthread_self(), &policy, &sp)) != 0) {
+ pa_log("pthread_getschedgetparam(): %s", pa_cstrerror(r));
+ return -1;
+ }
+
+ if (policy == SCHED_FIFO && sp.sched_priority >= rtprio) {
+ pa_log_info("Thread already being scheduled with SCHED_FIFO with priority %i.", sp.sched_priority);
+ return 0;
+ }
+
+ sp.sched_priority = rtprio;
+ if ((r = pthread_setschedparam(pthread_self(), SCHED_FIFO, &sp)) != 0) {
+
+ while (sp.sched_priority > 1) {
+ sp.sched_priority --;
+
+ if ((r = pthread_setschedparam(pthread_self(), SCHED_FIFO, &sp)) == 0) {
+ pa_log_info("Successfully enabled SCHED_FIFO scheduling for thread, with priority %i, which is lower than the requested %i.", sp.sched_priority, rtprio);
+ return 0;
+ }
}
- pa_log_info("Successfully enabled SCHED_FIFO scheduling.");
+ pa_log_warn("pthread_setschedparam(): %s", pa_cstrerror(r));
+ return -1;
}
-#endif
-#ifdef OS_IS_WIN32
- if (!SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS))
- pa_log_warn("SetPriorityClass() failed: 0x%08X", GetLastError());
- else
- pa_log_info("Successfully gained high priority class.");
+ pa_log_info("Successfully enabled SCHED_FIFO scheduling for thread, with priority %i.", sp.sched_priority);
+ return 0;
+#else
+ return -1;
#endif
}
-/* Reset the priority to normal, inverting the changes made by pa_raise_priority() */
-void pa_reset_priority(void) {
-#ifdef OS_IS_WIN32
- SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
+/* This is merely used for giving the user a hint. This is not correct
+ * for anything security related */
+pa_bool_t pa_can_realtime(void) {
+
+ if (geteuid() == 0)
+ return TRUE;
+
+#if defined(HAVE_SYS_RESOURCE_H) && defined(RLIMIT_RTPRIO)
+ {
+ struct rlimit rl;
+
+ if (getrlimit(RLIMIT_RTPRIO, &rl) >= 0)
+ if (rl.rlim_cur > 0 || rl.rlim_cur == RLIM_INFINITY)
+ return TRUE;
+ }
#endif
-#ifdef _POSIX_PRIORITY_SCHEDULING
+#if defined(HAVE_SYS_CAPABILITY_H) && defined(CAP_SYS_NICE)
{
- struct sched_param sp;
- sched_getparam(0, &sp);
- sp.sched_priority = 0;
- sched_setscheduler(0, SCHED_OTHER, &sp);
+ cap_t cap;
+
+ if ((cap = cap_get_proc())) {
+ cap_flag_value_t flag = CAP_CLEAR;
+
+ if (cap_get_flag(cap, CAP_SYS_NICE, CAP_EFFECTIVE, &flag) >= 0)
+ if (flag == CAP_SET) {
+ cap_free(cap);
+ return TRUE;
+ }
+
+ cap_free(cap);
+ }
}
#endif
-#ifdef HAVE_SYS_RESOURCE_H
- setpriority(PRIO_PROCESS, 0, 0);
+ return FALSE;
+}
+
+/* This is merely used for giving the user a hint. This is not correct
+ * for anything security related */
+pa_bool_t pa_can_high_priority(void) {
+
+ if (geteuid() == 0)
+ return TRUE;
+
+#if defined(HAVE_SYS_RESOURCE_H) && defined(RLIMIT_RTPRIO)
+ {
+ struct rlimit rl;
+
+ if (getrlimit(RLIMIT_NICE, &rl) >= 0)
+ if (rl.rlim_cur >= 21 || rl.rlim_cur == RLIM_INFINITY)
+ return TRUE;
+ }
#endif
+
+#if defined(HAVE_SYS_CAPABILITY_H) && defined(CAP_SYS_NICE)
+ {
+ cap_t cap;
+
+ if ((cap = cap_get_proc())) {
+ cap_flag_value_t flag = CAP_CLEAR;
+
+ if (cap_get_flag(cap, CAP_SYS_NICE, CAP_EFFECTIVE, &flag) >= 0)
+ if (flag == CAP_SET) {
+ cap_free(cap);
+ return TRUE;
+ }
+
+ cap_free(cap);
+ }
+ }
+#endif
+
+ return FALSE;
}
-/* Set the FD_CLOEXEC flag for a fd */
-int pa_fd_set_cloexec(int fd, int b) {
+/* Raise the priority of the current process as much as possible that
+ * is <= the specified nice level..*/
+int pa_raise_priority(int nice_level) {
-#ifdef FD_CLOEXEC
- int v;
- assert(fd >= 0);
+#ifdef HAVE_SYS_RESOURCE_H
+ if (setpriority(PRIO_PROCESS, 0, nice_level) < 0) {
+ int n;
- if ((v = fcntl(fd, F_GETFD, 0)) < 0)
- return -1;
-
- v = (v & ~FD_CLOEXEC) | (b ? FD_CLOEXEC : 0);
-
- if (fcntl(fd, F_SETFD, v) < 0)
+ for (n = nice_level+1; n < 0; n++) {
+
+ if (setpriority(PRIO_PROCESS, 0, n) == 0) {
+ pa_log_info("Successfully acquired nice level %i, which is lower than the requested %i.", n, nice_level);
+ return 0;
+ }
+ }
+
+ pa_log_warn("setpriority(): %s", pa_cstrerror(errno));
return -1;
-#endif
+ }
+
+ pa_log_info("Successfully gained nice level %i.", nice_level);
+#endif
+
+#ifdef OS_IS_WIN32
+ if (nice_level < 0) {
+ if (!SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS)) {
+ pa_log_warn("SetPriorityClass() failed: 0x%08X", GetLastError());
+ return .-1;
+ } else
+ pa_log_info("Successfully gained high priority class.");
+ }
+#endif
return 0;
}
+/* Reset the priority to normal, inverting the changes made by
+ * pa_raise_priority() and pa_make_realtime()*/
+void pa_reset_priority(void) {
+#ifdef HAVE_SYS_RESOURCE_H
+ struct sched_param sp;
+
+ setpriority(PRIO_PROCESS, 0, 0);
+
+ memset(&sp, 0, sizeof(sp));
+ pa_assert_se(pthread_setschedparam(pthread_self(), SCHED_OTHER, &sp) == 0);
+#endif
+
+#ifdef OS_IS_WIN32
+ SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
+#endif
+}
+
/* Try to parse a boolean string value.*/
int pa_parse_boolean(const char *v) {
-
+ pa_assert(v);
+
if (!strcmp(v, "1") || v[0] == 'y' || v[0] == 'Y' || v[0] == 't' || v[0] == 'T' || !strcasecmp(v, "on"))
return 1;
else if (!strcmp(v, "0") || v[0] == 'n' || v[0] == 'N' || v[0] == 'f' || v[0] == 'F' || !strcasecmp(v, "off"))
@@ -552,7 +701,7 @@ char *pa_split(const char *c, const char *delimiter, const char**state) {
if (!*current)
return NULL;
-
+
l = strcspn(current, delimiter);
*state = current+l;
@@ -581,31 +730,134 @@ char *pa_split_spaces(const char *c, const char **state) {
return pa_xstrndup(current, l);
}
-/* Return the name of an UNIX signal. Similar to GNU's strsignal() */
-const char *pa_strsignal(int sig) {
+PA_STATIC_TLS_DECLARE(signame, pa_xfree);
+
+/* Return the name of an UNIX signal. Similar to Solaris sig2str() */
+const char *pa_sig2str(int sig) {
+ char *t;
+
+ if (sig <= 0)
+ goto fail;
+
+#ifdef NSIG
+ if (sig >= NSIG)
+ goto fail;
+#endif
+
+#ifdef HAVE_SIG2STR
+ {
+ char buf[SIG2STR_MAX];
+
+ if (sig2str(sig, buf) == 0) {
+ pa_xfree(PA_STATIC_TLS_GET(signame));
+ t = pa_sprintf_malloc("SIG%s", buf);
+ PA_STATIC_TLS_SET(signame, t);
+ return t;
+ }
+ }
+#else
+
switch(sig) {
- case SIGINT: return "SIGINT";
- case SIGTERM: return "SIGTERM";
+#ifdef SIGHUP
+ case SIGHUP: return "SIGHUP";
+#endif
+ case SIGINT: return "SIGINT";
+#ifdef SIGQUIT
+ case SIGQUIT: return "SIGQUIT";
+#endif
+ case SIGILL: return "SIGULL";
+#ifdef SIGTRAP
+ case SIGTRAP: return "SIGTRAP";
+#endif
+ case SIGABRT: return "SIGABRT";
+#ifdef SIGBUS
+ case SIGBUS: return "SIGBUS";
+#endif
+ case SIGFPE: return "SIGFPE";
+#ifdef SIGKILL
+ case SIGKILL: return "SIGKILL";
+#endif
#ifdef SIGUSR1
- case SIGUSR1: return "SIGUSR1";
+ case SIGUSR1: return "SIGUSR1";
#endif
+ case SIGSEGV: return "SIGSEGV";
#ifdef SIGUSR2
- case SIGUSR2: return "SIGUSR2";
-#endif
-#ifdef SIGXCPU
- case SIGXCPU: return "SIGXCPU";
+ case SIGUSR2: return "SIGUSR2";
#endif
#ifdef SIGPIPE
- case SIGPIPE: return "SIGPIPE";
+ case SIGPIPE: return "SIGPIPE";
+#endif
+#ifdef SIGALRM
+ case SIGALRM: return "SIGALRM";
+#endif
+ case SIGTERM: return "SIGTERM";
+#ifdef SIGSTKFLT
+ case SIGSTKFLT: return "SIGSTKFLT";
#endif
#ifdef SIGCHLD
- case SIGCHLD: return "SIGCHLD";
+ case SIGCHLD: return "SIGCHLD";
#endif
-#ifdef SIGHUP
- case SIGHUP: return "SIGHUP";
+#ifdef SIGCONT
+ case SIGCONT: return "SIGCONT";
+#endif
+#ifdef SIGSTOP
+ case SIGSTOP: return "SIGSTOP";
+#endif
+#ifdef SIGTSTP
+ case SIGTSTP: return "SIGTSTP";
+#endif
+#ifdef SIGTTIN
+ case SIGTTIN: return "SIGTTIN";
+#endif
+#ifdef SIGTTOU
+ case SIGTTOU: return "SIGTTOU";
+#endif
+#ifdef SIGURG
+ case SIGURG: return "SIGURG";
+#endif
+#ifdef SIGXCPU
+ case SIGXCPU: return "SIGXCPU";
+#endif
+#ifdef SIGXFSZ
+ case SIGXFSZ: return "SIGXFSZ";
+#endif
+#ifdef SIGVTALRM
+ case SIGVTALRM: return "SIGVTALRM";
+#endif
+#ifdef SIGPROF
+ case SIGPROF: return "SIGPROF";
+#endif
+#ifdef SIGWINCH
+ case SIGWINCH: return "SIGWINCH";
#endif
- default: return "UNKNOWN SIGNAL";
+#ifdef SIGIO
+ case SIGIO: return "SIGIO";
+#endif
+#ifdef SIGPWR
+ case SIGPWR: return "SIGPWR";
+#endif
+#ifdef SIGSYS
+ case SIGSYS: return "SIGSYS";
+#endif
+ }
+
+#ifdef SIGRTMIN
+ if (sig >= SIGRTMIN && sig <= SIGRTMAX) {
+ pa_xfree(PA_STATIC_TLS_GET(signame));
+ t = pa_sprintf_malloc("SIGRTMIN+%i", sig - SIGRTMIN);
+ PA_STATIC_TLS_SET(signame, t);
+ return t;
}
+#endif
+
+#endif
+
+fail:
+
+ pa_xfree(PA_STATIC_TLS_GET(signame));
+ t = pa_sprintf_malloc("SIG%i", sig);
+ PA_STATIC_TLS_SET(signame, t);
+ return t;
}
#ifdef HAVE_GRP_H
@@ -632,7 +884,7 @@ static int is_group(gid_t gid, const char *name) {
}
r = strcmp(name, result->gr_name) == 0;
-
+
finish:
pa_xfree(data);
#else
@@ -647,7 +899,7 @@ finish:
finish:
#endif
-
+
return r;
}
@@ -657,10 +909,10 @@ int pa_own_uid_in_group(const char *name, gid_t *gid) {
int n = sysconf(_SC_NGROUPS_MAX);
int r = -1, i;
- assert(n > 0);
-
+ pa_assert(n > 0);
+
gids = pa_xmalloc(sizeof(GETGROUPS_T)*n);
-
+
if ((n = getgroups(n, gids)) < 0) {
pa_log("getgroups(): %s", pa_cstrerror(errno));
goto finish;
@@ -681,7 +933,7 @@ int pa_own_uid_in_group(const char *name, gid_t *gid) {
}
r = 0;
-
+
finish:
pa_xfree(gids);
@@ -695,20 +947,20 @@ int pa_uid_in_group(uid_t uid, const char *name) {
struct group grbuf, *gr;
char **i;
int r = -1;
-
+
g_n = sysconf(_SC_GETGR_R_SIZE_MAX);
g_buf = pa_xmalloc(g_n);
p_n = sysconf(_SC_GETPW_R_SIZE_MAX);
p_buf = pa_xmalloc(p_n);
-
+
if (getgrnam_r(name, &grbuf, g_buf, (size_t) g_n, &gr) != 0 || !gr)
goto finish;
r = 0;
for (i = gr->gr_mem; *i; i++) {
struct passwd pwbuf, *pw;
-
+
if (getpwnam_r(*i, &pwbuf, p_buf, (size_t) p_n, &pw) != 0 || !pw)
continue;
@@ -763,7 +1015,7 @@ int pa_check_in_group(gid_t g) {
int pa_own_uid_in_group(const char *name, gid_t *gid) {
return -1;
-
+
}
int pa_uid_in_group(uid_t uid, const char *name) {
@@ -787,7 +1039,7 @@ int pa_lock_fd(int fd, int b) {
struct flock flock;
/* Try a R/W lock first */
-
+
flock.l_type = b ? F_WRLCK : F_UNLCK;
flock.l_whence = SEEK_SET;
flock.l_start = 0;
@@ -802,9 +1054,8 @@ int pa_lock_fd(int fd, int b) {
if (fcntl(fd, F_SETLKW, &flock) >= 0)
return 0;
}
-
- pa_log("%slock: %s", !b? "un" : "",
- pa_cstrerror(errno));
+
+ pa_log("%slock: %s", !b? "un" : "", pa_cstrerror(errno));
#endif
#ifdef OS_IS_WIN32
@@ -823,7 +1074,7 @@ int pa_lock_fd(int fd, int b) {
/* Remove trailing newlines from a string */
char* pa_strip_nl(char *s) {
- assert(s);
+ pa_assert(s);
s[strcspn(s, "\r\n")] = 0;
return s;
@@ -832,50 +1083,58 @@ char* pa_strip_nl(char *s) {
/* Create a temporary lock file and lock it. */
int pa_lock_lockfile(const char *fn) {
int fd = -1;
- assert(fn);
+ pa_assert(fn);
for (;;) {
struct stat st;
-
- if ((fd = open(fn, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR)) < 0) {
- pa_log("failed to create lock file '%s': %s", fn,
- pa_cstrerror(errno));
+
+ if ((fd = open(fn, O_CREAT|O_RDWR
+#ifdef O_NOCTTY
+ |O_NOCTTY
+#endif
+#ifdef O_NOFOLLOW
+ |O_NOFOLLOW
+#endif
+ , S_IRUSR|S_IWUSR)) < 0) {
+ pa_log_warn("Failed to create lock file '%s': %s", fn, pa_cstrerror(errno));
goto fail;
}
-
+
if (pa_lock_fd(fd, 1) < 0) {
- pa_log("failed to lock file '%s'.", fn);
+ pa_log_warn("Failed to lock file '%s'.", fn);
goto fail;
}
-
+
if (fstat(fd, &st) < 0) {
- pa_log("failed to fstat() file '%s'.", fn);
+ pa_log_warn("Failed to fstat() file '%s': %s", fn, pa_cstrerror(errno));
goto fail;
}
- /* Check wheter the file has been removed meanwhile. When yes, restart this loop, otherwise, we're done */
+ /* Check wheter the file has been removed meanwhile. When yes,
+ * restart this loop, otherwise, we're done */
if (st.st_nlink >= 1)
break;
-
+
if (pa_lock_fd(fd, 0) < 0) {
- pa_log("failed to unlock file '%s'.", fn);
+ pa_log_warn("Failed to unlock file '%s'.", fn);
goto fail;
}
-
- if (close(fd) < 0) {
- pa_log("failed to close file '%s'.", fn);
+
+ if (pa_close(fd) < 0) {
+ pa_log_warn("Failed to close file '%s': %s", fn, pa_cstrerror(errno));
+ fd = -1;
goto fail;
}
fd = -1;
}
-
+
return fd;
fail:
if (fd >= 0)
- close(fd);
+ pa_close(fd);
return -1;
}
@@ -883,38 +1142,80 @@ fail:
/* Unlock a temporary lcok file */
int pa_unlock_lockfile(const char *fn, int fd) {
int r = 0;
- assert(fn && fd >= 0);
+ pa_assert(fd >= 0);
- if (unlink(fn) < 0) {
- pa_log_warn("WARNING: unable to remove lock file '%s': %s",
- fn, pa_cstrerror(errno));
- r = -1;
+ if (fn) {
+ if (unlink(fn) < 0) {
+ pa_log_warn("Unable to remove lock file '%s': %s", fn, pa_cstrerror(errno));
+ r = -1;
+ }
}
-
+
if (pa_lock_fd(fd, 0) < 0) {
- pa_log_warn("WARNING: failed to unlock file '%s'.", fn);
+ pa_log_warn("Failed to unlock file '%s'.", fn);
r = -1;
}
- if (close(fd) < 0) {
- pa_log_warn("WARNING: failed to close lock file '%s': %s",
- fn, pa_cstrerror(errno));
+ if (pa_close(fd) < 0) {
+ pa_log_warn("Failed to close '%s': %s", fn, pa_cstrerror(errno));
r = -1;
}
return r;
}
+static char *get_dir(mode_t m, const char *env_name) {
+ const char *e;
+ char *d;
+
+ if ((e = getenv(env_name)))
+ d = pa_xstrdup(e);
+ else {
+ char h[PATH_MAX];
+ struct stat st;
+
+ if (!pa_get_home_dir(h, sizeof(h))) {
+ pa_log_error("Failed to get home directory.");
+ return NULL;
+ }
+
+ if (stat(h, &st) < 0) {
+ pa_log_error("Failed to stat home directory %s: %s", h, pa_cstrerror(errno));
+ return NULL;
+ }
+
+ if (st.st_uid != getuid()) {
+ pa_log_error("Home directory %s not ours.", d);
+ return NULL;
+ }
+
+ d = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse", h);
+ }
+
+ if (pa_make_secure_dir(d, m, (pid_t) -1, (pid_t) -1) < 0) {
+ pa_log_error("Failed to create secure directory: %s", pa_cstrerror(errno));
+ return NULL;
+ }
+
+ return d;
+}
+
+char *pa_get_runtime_dir(void) {
+ return get_dir(pa_in_system_mode() ? 0755 : 0700, "PULSE_RUNTIME_PATH");
+}
+
+char *pa_get_state_dir(void) {
+ return get_dir(0700, "PULSE_STATE_PATH");
+}
+
/* Try to open a configuration file. If "env" is specified, open the
* value of the specified environment variable. Otherwise look for a
* file "local" in the home directory or a file "global" in global
* file system. If "result" is non-NULL, a pointer to a newly
* allocated buffer containing the used configuration file is
* stored there.*/
-FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result, const char *mode) {
+FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result) {
const char *fn;
- char h[PATH_MAX];
-
#ifdef OS_IS_WIN32
char buf[PATH_MAX];
@@ -923,71 +1224,162 @@ FILE *pa_open_config_file(const char *global, const char *local, const char *env
#endif
if (env && (fn = getenv(env))) {
+ FILE *f;
+
#ifdef OS_IS_WIN32
if (!ExpandEnvironmentStrings(fn, buf, PATH_MAX))
return NULL;
fn = buf;
#endif
- if (result)
- *result = pa_xstrdup(fn);
+ if ((f = fopen(fn, "r"))) {
+ if (result)
+ *result = pa_xstrdup(fn);
+
+ return f;
+ }
- return fopen(fn, mode);
+ pa_log_warn("Failed to open configuration file '%s': %s", fn, pa_cstrerror(errno));
+ return NULL;
}
if (local) {
const char *e;
- char *lfn = NULL;
+ char *lfn;
+ char h[PATH_MAX];
+ FILE *f;
if ((e = getenv("PULSE_CONFIG_PATH")))
- fn = lfn = pa_sprintf_malloc("%s/%s", e, local);
+ fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", e, local);
else if (pa_get_home_dir(h, sizeof(h)))
- fn = lfn = pa_sprintf_malloc("%s/.pulse/%s", h, local);
+ fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse" PA_PATH_SEP "%s", h, local);
- if (lfn) {
- FILE *f;
-
#ifdef OS_IS_WIN32
- if (!ExpandEnvironmentStrings(lfn, buf, PATH_MAX))
- return NULL;
- fn = buf;
-#endif
-
- if ((f = fopen(fn, mode)) || errno != ENOENT) {
- if (result)
- *result = pa_xstrdup(fn);
- pa_xfree(lfn);
- return f;
- }
-
+ if (!ExpandEnvironmentStrings(lfn, buf, PATH_MAX)) {
pa_xfree(lfn);
+ return NULL;
}
+ fn = buf;
+#endif
+
+ if ((f = fopen(fn, "r"))) {
+ if (result)
+ *result = pa_xstrdup(fn);
+
+ pa_xfree(lfn);
+ return f;
+ }
+
+ if (errno != ENOENT) {
+ pa_log_warn("Failed to open configuration file '%s': %s", fn, pa_cstrerror(errno));
+ pa_xfree(lfn);
+ return NULL;
+ }
+
+ pa_xfree(lfn);
}
- if (!global) {
- if (result)
- *result = NULL;
+ if (global) {
+ FILE *f;
+
+#ifdef OS_IS_WIN32
+ if (!ExpandEnvironmentStrings(global, buf, PATH_MAX))
+ return NULL;
+ global = buf;
+#endif
+
+ if ((f = fopen(global, "r"))) {
+
+ if (result)
+ *result = pa_xstrdup(global);
+
+ return f;
+ }
+ } else
errno = ENOENT;
+
+ return NULL;
+}
+
+char *pa_find_config_file(const char *global, const char *local, const char *env) {
+ const char *fn;
+#ifdef OS_IS_WIN32
+ char buf[PATH_MAX];
+
+ if (!getenv(PULSE_ROOTENV))
+ pa_set_root(NULL);
+#endif
+
+ if (env && (fn = getenv(env))) {
+#ifdef OS_IS_WIN32
+ if (!ExpandEnvironmentStrings(fn, buf, PATH_MAX))
+ return NULL;
+ fn = buf;
+#endif
+
+ if (access(fn, R_OK) == 0)
+ return pa_xstrdup(fn);
+
+ pa_log_warn("Failed to access configuration file '%s': %s", fn, pa_cstrerror(errno));
return NULL;
}
+ if (local) {
+ const char *e;
+ char *lfn;
+ char h[PATH_MAX];
+
+ if ((e = getenv("PULSE_CONFIG_PATH")))
+ fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", e, local);
+ else if (pa_get_home_dir(h, sizeof(h)))
+ fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse" PA_PATH_SEP "%s", h, local);
+
#ifdef OS_IS_WIN32
- if (!ExpandEnvironmentStrings(global, buf, PATH_MAX))
- return NULL;
- global = buf;
+ if (!ExpandEnvironmentStrings(lfn, buf, PATH_MAX)) {
+ pa_xfree(lfn);
+ return NULL;
+ }
+ fn = buf;
#endif
- if (result)
- *result = pa_xstrdup(global);
-
- return fopen(global, mode);
+ if (access(fn, R_OK) == 0) {
+ char *r = pa_xstrdup(fn);
+ pa_xfree(lfn);
+ return r;
+ }
+
+ if (errno != ENOENT) {
+ pa_log_warn("Failed to access configuration file '%s': %s", fn, pa_cstrerror(errno));
+ pa_xfree(lfn);
+ return NULL;
+ }
+
+ pa_xfree(lfn);
+ }
+
+ if (global) {
+#ifdef OS_IS_WIN32
+ if (!ExpandEnvironmentStrings(global, buf, PATH_MAX))
+ return NULL;
+ global = buf;
+#endif
+
+ if (access(fn, R_OK) == 0)
+ return pa_xstrdup(global);
+ } else
+ errno = ENOENT;
+
+ return NULL;
}
-
+
/* Format the specified data as a hexademical string */
char *pa_hexstr(const uint8_t* d, size_t dlength, char *s, size_t slength) {
size_t i = 0, j = 0;
const char hex[] = "0123456789abcdef";
- assert(d && s && slength > 0);
+
+ pa_assert(d);
+ pa_assert(s);
+ pa_assert(slength > 0);
while (i < dlength && j+3 <= slength) {
s[j++] = hex[*d >> 4];
@@ -1018,14 +1410,16 @@ static int hexc(char c) {
/* Parse a hexadecimal string as created by pa_hexstr() to a BLOB */
size_t pa_parsehex(const char *p, uint8_t *d, size_t dlength) {
size_t j = 0;
- assert(p && d);
+
+ pa_assert(p);
+ pa_assert(d);
while (j < dlength && *p) {
int b;
if ((b = hexc(*(p++))) < 0)
return (size_t) -1;
-
+
d[j] = (uint8_t) (b << 4);
if (!*p)
@@ -1042,84 +1436,107 @@ size_t pa_parsehex(const char *p, uint8_t *d, size_t dlength) {
}
/* Returns nonzero when *s starts with *pfx */
-int pa_startswith(const char *s, const char *pfx) {
+pa_bool_t pa_startswith(const char *s, const char *pfx) {
size_t l;
-
- assert(s);
- assert(pfx);
-
+
+ pa_assert(s);
+ pa_assert(pfx);
+
l = strlen(pfx);
return strlen(s) >= l && strncmp(s, pfx, l) == 0;
}
/* Returns nonzero when *s ends with *sfx */
-int pa_endswith(const char *s, const char *sfx) {
+pa_bool_t pa_endswith(const char *s, const char *sfx) {
size_t l1, l2;
-
- assert(s);
- assert(sfx);
-
+
+ pa_assert(s);
+ pa_assert(sfx);
+
l1 = strlen(s);
l2 = strlen(sfx);
return l1 >= l2 && strcmp(s+l1-l2, sfx) == 0;
}
-/* if fn is null return the PulseAudio run time path in s (/tmp/pulse)
- * if fn is non-null and starts with / return fn in s
- * otherwise append fn to the run time path and return it in s */
-char *pa_runtime_path(const char *fn, char *s, size_t l) {
- const char *e;
+pa_bool_t pa_is_path_absolute(const char *fn) {
+ pa_assert(fn);
#ifndef OS_IS_WIN32
- if (fn && *fn == '/')
+ return *fn == '/';
#else
- if (fn && strlen(fn) >= 3 && isalpha(fn[0]) && fn[1] == ':' && fn[2] == '\\')
-#endif
- return pa_strlcpy(s, fn, l);
-
- if ((e = getenv("PULSE_RUNTIME_PATH"))) {
-
- if (fn)
- snprintf(s, l, "%s%c%s", e, PATH_SEP, fn);
- else
- snprintf(s, l, "%s", e);
-
- } else {
- char u[256];
-
- if (fn)
- snprintf(s, l, "%s%s%c%s", PA_USER_RUNTIME_PATH_PREFIX, pa_get_user_name(u, sizeof(u)), PATH_SEP, fn);
- else
- snprintf(s, l, "%s%s", PA_USER_RUNTIME_PATH_PREFIX, pa_get_user_name(u, sizeof(u)));
- }
-
-
-#ifdef OS_IS_WIN32
- {
- char buf[l];
- strcpy(buf, s);
- ExpandEnvironmentStrings(buf, s, l);
- }
+ return strlen(fn) >= 3 && isalpha(fn[0]) && fn[1] == ':' && fn[2] == '\\';
#endif
+}
- return s;
+char *pa_make_path_absolute(const char *p) {
+ char *r;
+ char *cwd;
+
+ pa_assert(p);
+
+ if (pa_is_path_absolute(p))
+ return pa_xstrdup(p);
+
+ if (!(cwd = pa_getcwd()))
+ return pa_xstrdup(p);
+
+ r = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", cwd, p);
+ pa_xfree(cwd);
+ return r;
+}
+
+/* if fn is null return the PulseAudio run time path in s (~/.pulse)
+ * if fn is non-null and starts with / return fn
+ * otherwise append fn to the run time path and return it */
+static char *get_path(const char *fn, pa_bool_t rt) {
+ char *rtp;
+
+ if (pa_is_path_absolute(fn))
+ return pa_xstrdup(fn);
+
+ rtp = rt ? pa_get_runtime_dir() : pa_get_state_dir();
+
+ if (!rtp)
+ return NULL;
+
+ if (fn) {
+ char *r;
+ r = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", rtp, fn);
+ pa_xfree(rtp);
+ return r;
+ } else
+ return rtp;
+}
+
+char *pa_runtime_path(const char *fn) {
+ return get_path(fn, 1);
+}
+
+char *pa_state_path(const char *fn) {
+ return get_path(fn, 0);
}
/* Convert the string s to a signed integer in *ret_i */
int pa_atoi(const char *s, int32_t *ret_i) {
char *x = NULL;
long l;
- assert(s && ret_i);
+ pa_assert(s);
+ pa_assert(ret_i);
+
+ errno = 0;
l = strtol(s, &x, 0);
- if (!x || *x)
+ if (!x || *x || errno != 0)
+ return -1;
+
+ if ((int32_t) l != l)
return -1;
*ret_i = (int32_t) l;
-
+
return 0;
}
@@ -1127,14 +1544,502 @@ int pa_atoi(const char *s, int32_t *ret_i) {
int pa_atou(const char *s, uint32_t *ret_u) {
char *x = NULL;
unsigned long l;
- assert(s && ret_u);
+ pa_assert(s);
+ pa_assert(ret_u);
+
+ errno = 0;
l = strtoul(s, &x, 0);
- if (!x || *x)
+ if (!x || *x || errno != 0)
+ return -1;
+
+ if ((uint32_t) l != l)
return -1;
*ret_u = (uint32_t) l;
-
+
+ return 0;
+}
+
+#ifdef HAVE_STRTOF_L
+static locale_t c_locale = NULL;
+
+static void c_locale_destroy(void) {
+ freelocale(c_locale);
+}
+#endif
+
+int pa_atod(const char *s, double *ret_d) {
+ char *x = NULL;
+ double f;
+ int r = 0;
+
+ pa_assert(s);
+ pa_assert(ret_d);
+
+ /* This should be locale independent */
+
+#ifdef HAVE_STRTOF_L
+
+ PA_ONCE_BEGIN {
+
+ if ((c_locale = newlocale(LC_ALL_MASK, "C", NULL)))
+ atexit(c_locale_destroy);
+
+ } PA_ONCE_END;
+
+ if (c_locale) {
+ errno = 0;
+ f = strtod_l(s, &x, c_locale);
+ } else
+#endif
+ {
+ errno = 0;
+ f = strtod(s, &x);
+ }
+
+ if (!x || *x || errno != 0)
+ r = -1;
+ else
+ *ret_d = f;
+
+ return r;
+}
+
+/* Same as snprintf, but guarantees NUL-termination on every platform */
+int pa_snprintf(char *str, size_t size, const char *format, ...) {
+ int ret;
+ va_list ap;
+
+ pa_assert(str);
+ pa_assert(size > 0);
+ pa_assert(format);
+
+ va_start(ap, format);
+ ret = pa_vsnprintf(str, size, format, ap);
+ va_end(ap);
+
+ return ret;
+}
+
+/* Same as vsnprintf, but guarantees NUL-termination on every platform */
+int pa_vsnprintf(char *str, size_t size, const char *format, va_list ap) {
+ int ret;
+
+ pa_assert(str);
+ pa_assert(size > 0);
+ pa_assert(format);
+
+ ret = vsnprintf(str, size, format, ap);
+
+ str[size-1] = 0;
+
+ if (ret < 0)
+ ret = strlen(str);
+
+ return PA_MIN((int) size-1, ret);
+}
+
+/* Truncate the specified string, but guarantee that the string
+ * returned still validates as UTF8 */
+char *pa_truncate_utf8(char *c, size_t l) {
+ pa_assert(c);
+ pa_assert(pa_utf8_valid(c));
+
+ if (strlen(c) <= l)
+ return c;
+
+ c[l] = 0;
+
+ while (l > 0 && !pa_utf8_valid(c))
+ c[--l] = 0;
+
+ return c;
+}
+
+char *pa_getcwd(void) {
+ size_t l = 128;
+
+ for (;;) {
+ char *p = pa_xnew(char, l);
+ if (getcwd(p, l))
+ return p;
+
+ if (errno != ERANGE)
+ return NULL;
+
+ pa_xfree(p);
+ l *= 2;
+ }
+}
+
+void *pa_will_need(const void *p, size_t l) {
+#ifdef RLIMIT_MEMLOCK
+ struct rlimit rlim;
+#endif
+ const void *a;
+ size_t size;
+ int r;
+ size_t bs;
+
+ pa_assert(p);
+ pa_assert(l > 0);
+
+ a = PA_PAGE_ALIGN_PTR(p);
+ size = (const uint8_t*) p + l - (const uint8_t*) a;
+
+#ifdef HAVE_POSIX_MADVISE
+ if ((r = posix_madvise((void*) a, size, POSIX_MADV_WILLNEED)) == 0) {
+ pa_log_debug("posix_madvise() worked fine!");
+ return (void*) p;
+ }
+#endif
+
+ /* Most likely the memory was not mmap()ed from a file and thus
+ * madvise() didn't work, so let's misuse mlock() do page this
+ * stuff back into RAM. Yeah, let's fuck with the MM! It's so
+ * inviting, the man page of mlock() tells us: "All pages that
+ * contain a part of the specified address range are guaranteed to
+ * be resident in RAM when the call returns successfully." */
+
+#ifdef RLIMIT_MEMLOCK
+ pa_assert_se(getrlimit(RLIMIT_MEMLOCK, &rlim) == 0);
+
+ if (rlim.rlim_cur < PA_PAGE_SIZE) {
+ pa_log_debug("posix_madvise() failed (or doesn't exist), resource limits don't allow mlock(), can't page in data: %s", pa_cstrerror(r));
+ return (void*) p;
+ }
+
+ bs = PA_PAGE_ALIGN(rlim.rlim_cur);
+#else
+ bs = PA_PAGE_SIZE*4;
+#endif
+
+ pa_log_debug("posix_madvise() failed (or doesn't exist), trying mlock(): %s", pa_cstrerror(r));
+
+#ifdef HAVE_MLOCK
+ while (size > 0 && bs > 0) {
+
+ if (bs > size)
+ bs = size;
+
+ if (mlock(a, bs) < 0) {
+ bs = PA_PAGE_ALIGN(bs / 2);
+ continue;
+ }
+
+ pa_assert_se(munlock(a, bs) == 0);
+
+ a = (const uint8_t*) a + bs;
+ size -= bs;
+ }
+#endif
+
+ if (bs <= 0)
+ pa_log_debug("mlock() failed too (or doesn't exist), giving up: %s", pa_cstrerror(errno));
+ else
+ pa_log_debug("mlock() worked fine!");
+
+ return (void*) p;
+}
+
+void pa_close_pipe(int fds[2]) {
+ pa_assert(fds);
+
+ if (fds[0] >= 0)
+ pa_assert_se(pa_close(fds[0]) == 0);
+
+ if (fds[1] >= 0)
+ pa_assert_se(pa_close(fds[1]) == 0);
+
+ fds[0] = fds[1] = -1;
+}
+
+char *pa_readlink(const char *p) {
+ size_t l = 100;
+
+ for (;;) {
+ char *c;
+ ssize_t n;
+
+ c = pa_xnew(char, l);
+
+ if ((n = readlink(p, c, l-1)) < 0) {
+ pa_xfree(c);
+ return NULL;
+ }
+
+ if ((size_t) n < l-1) {
+ c[n] = 0;
+ return c;
+ }
+
+ pa_xfree(c);
+ l *= 2;
+ }
+}
+
+int pa_close_all(int except_fd, ...) {
+ va_list ap;
+ int n = 0, i, r;
+ int *p;
+
+ va_start(ap, except_fd);
+
+ if (except_fd >= 0)
+ for (n = 1; va_arg(ap, int) >= 0; n++)
+ ;
+
+ va_end(ap);
+
+ p = pa_xnew(int, n+1);
+
+ va_start(ap, except_fd);
+
+ i = 0;
+ if (except_fd >= 0) {
+ int fd;
+ p[i++] = except_fd;
+
+ while ((fd = va_arg(ap, int)) >= 0)
+ p[i++] = fd;
+ }
+ p[i] = -1;
+
+ va_end(ap);
+
+ r = pa_close_allv(p);
+ free(p);
+
+ return r;
+}
+
+int pa_close_allv(const int except_fds[]) {
+ struct rlimit rl;
+ int fd;
+ int saved_errno;
+
+#ifdef __linux__
+
+ DIR *d;
+
+ if ((d = opendir("/proc/self/fd"))) {
+
+ struct dirent *de;
+
+ while ((de = readdir(d))) {
+ pa_bool_t found;
+ long l;
+ char *e = NULL;
+ int i;
+
+ if (de->d_name[0] == '.')
+ continue;
+
+ errno = 0;
+ l = strtol(de->d_name, &e, 10);
+ if (errno != 0 || !e || *e) {
+ closedir(d);
+ errno = EINVAL;
+ return -1;
+ }
+
+ fd = (int) l;
+
+ if ((long) fd != l) {
+ closedir(d);
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (fd < 3)
+ continue;
+
+ if (fd == dirfd(d))
+ continue;
+
+ found = FALSE;
+ for (i = 0; except_fds[i] >= 0; i++)
+ if (except_fds[i] == fd) {
+ found = TRUE;
+ break;
+ }
+
+ if (found)
+ continue;
+
+ if (pa_close(fd) < 0) {
+ saved_errno = errno;
+ closedir(d);
+ errno = saved_errno;
+
+ return -1;
+ }
+ }
+
+ closedir(d);
+ return 0;
+ }
+
+#endif
+
+ if (getrlimit(RLIMIT_NOFILE, &rl) < 0)
+ return -1;
+
+ for (fd = 0; fd < (int) rl.rlim_max; fd++) {
+ int i;
+
+ if (fd <= 3)
+ continue;
+
+ for (i = 0; except_fds[i] >= 0; i++)
+ if (except_fds[i] == fd)
+ continue;
+
+ if (close(fd) < 0 && errno != EBADF)
+ return -1;
+ }
+
return 0;
}
+
+int pa_unblock_sigs(int except, ...) {
+ va_list ap;
+ int n = 0, i, r;
+ int *p;
+
+ va_start(ap, except);
+
+ if (except >= 1)
+ for (n = 1; va_arg(ap, int) >= 0; n++)
+ ;
+
+ va_end(ap);
+
+ p = pa_xnew(int, n+1);
+
+ va_start(ap, except);
+
+ i = 0;
+ if (except >= 1) {
+ int sig;
+ p[i++] = except;
+
+ while ((sig = va_arg(ap, int)) >= 0)
+ p[i++] = sig;
+ }
+ p[i] = -1;
+
+ va_end(ap);
+
+ r = pa_unblock_sigsv(p);
+ pa_xfree(p);
+
+ return r;
+}
+
+int pa_unblock_sigsv(const int except[]) {
+ int i;
+ sigset_t ss;
+
+ if (sigemptyset(&ss) < 0)
+ return -1;
+
+ for (i = 0; except[i] > 0; i++)
+ if (sigaddset(&ss, except[i]) < 0)
+ return -1;
+
+ return sigprocmask(SIG_SETMASK, &ss, NULL);
+}
+
+int pa_reset_sigs(int except, ...) {
+ va_list ap;
+ int n = 0, i, r;
+ int *p;
+
+ va_start(ap, except);
+
+ if (except >= 1)
+ for (n = 1; va_arg(ap, int) >= 0; n++)
+ ;
+
+ va_end(ap);
+
+ p = pa_xnew(int, n+1);
+
+ va_start(ap, except);
+
+ i = 0;
+ if (except >= 1) {
+ p[i++] = except;
+
+ while ((p[i++] = va_arg(ap, int)) >= 0)
+ ;
+ }
+ p[i] = -1;
+
+ va_end(ap);
+
+ r = pa_reset_sigsv(p);
+ pa_xfree(p);
+
+ return r;
+}
+
+int pa_reset_sigsv(const int except[]) {
+ int sig;
+
+ for (sig = 1; sig < _NSIG; sig++) {
+ pa_bool_t reset = TRUE;
+
+ switch (sig) {
+ case SIGKILL:
+ case SIGSTOP:
+ reset = FALSE;
+ break;
+
+ default: {
+ int i;
+
+ for (i = 0; except[i] > 0; i++) {
+ if (sig == except[i]) {
+ reset = FALSE;
+ break;
+ }
+ }
+ }
+ }
+
+ if (reset) {
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = SIG_DFL;
+
+ /* On Linux the first two RT signals are reserved by
+ * glibc, and sigaction() will return EINVAL for them. */
+ if ((sigaction(sig, &sa, NULL) < 0))
+ if (errno != EINVAL)
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+void pa_set_env(const char *key, const char *value) {
+ pa_assert(key);
+ pa_assert(value);
+
+ putenv(pa_sprintf_malloc("%s=%s", key, value));
+}
+
+pa_bool_t pa_in_system_mode(void) {
+ const char *e;
+
+ if (!(e = getenv("PULSE_SYSTEM")))
+ return FALSE;
+
+ return !!atoi(e);
+}
diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h
index ba325968..2ed81fc5 100644
--- a/src/pulsecore/core-util.h
+++ b/src/pulsecore/core-util.h
@@ -1,21 +1,22 @@
#ifndef foocoreutilhfoo
#define foocoreutilhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
+ Copyright 2004-2006 Lennart Poettering
+ Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
@@ -27,11 +28,29 @@
#include <stdarg.h>
#include <stdio.h>
-#include <pulsecore/gccmacro.h>
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
+
+#include <pulse/gccmacro.h>
+#include <pulsecore/macro.h>
struct timeval;
-void pa_make_nonblock_fd(int fd);
+/* These resource limits are pretty new on Linux, let's define them
+ * here manually, in case the kernel is newer than the glibc */
+#if !defined(RLIMIT_NICE) && defined(__linux__)
+#define RLIMIT_NICE 13
+#endif
+#if !defined(RLIMIT_RTPRIO) && defined(__linux__)
+#define RLIMIT_RTPRIO 14
+#endif
+#if !defined(RLIMIT_RTTIME) && defined(__linux__)
+#define RLIMIT_RTTIME 15
+#endif
+
+void pa_make_fd_nonblock(int fd);
+void pa_make_fd_cloexec(int fd);
int pa_make_secure_dir(const char* dir, mode_t m, uid_t uid, gid_t gid);
int pa_make_secure_parent_dir(const char *fn, mode_t, uid_t uid, gid_t gid);
@@ -41,6 +60,8 @@ ssize_t pa_write(int fd, const void *buf, size_t count, int *type);
ssize_t pa_loop_read(int fd, void*data, size_t size, int *type);
ssize_t pa_loop_write(int fd, const void*data, size_t size, int *type);
+int pa_close(int fd);
+
void pa_check_signal_is_blocked(int sig);
char *pa_sprintf_malloc(const char *format, ...) PA_GCC_PRINTF_ATTR(1,2);
@@ -50,19 +71,33 @@ char *pa_strlcpy(char *b, const char *s, size_t l);
char *pa_parent_dir(const char *fn);
-void pa_raise_priority(void);
+int pa_make_realtime(int rtprio);
+int pa_raise_priority(int nice_level);
void pa_reset_priority(void);
-int pa_fd_set_cloexec(int fd, int b);
+pa_bool_t pa_can_realtime(void);
+pa_bool_t pa_can_high_priority(void);
+
+int pa_parse_boolean(const char *s) PA_GCC_PURE;
+
+static inline const char *pa_yes_no(pa_bool_t b) {
+ return b ? "yes" : "no";
+}
-int pa_parse_boolean(const char *s);
+static inline const char *pa_strnull(const char *x) {
+ return x ? x : "(null)";
+}
+
+static inline const char *pa_strempty(const char *x) {
+ return x ? x : "";
+}
char *pa_split(const char *c, const char*delimiters, const char **state);
char *pa_split_spaces(const char *c, const char **state);
char *pa_strip_nl(char *s);
-const char *pa_strsignal(int sig);
+const char *pa_sig2str(int sig) PA_GCC_PURE;
int pa_own_uid_in_group(const char *name, gid_t *gid);
int pa_uid_in_group(uid_t uid, const char *name);
@@ -74,17 +109,79 @@ int pa_lock_fd(int fd, int b);
int pa_lock_lockfile(const char *fn);
int pa_unlock_lockfile(const char *fn, int fd);
-FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result, const char *mode);
-
char *pa_hexstr(const uint8_t* d, size_t dlength, char *s, size_t slength);
size_t pa_parsehex(const char *p, uint8_t *d, size_t dlength);
-int pa_startswith(const char *s, const char *pfx);
-int pa_endswith(const char *s, const char *sfx);
+pa_bool_t pa_startswith(const char *s, const char *pfx) PA_GCC_PURE;
+pa_bool_t pa_endswith(const char *s, const char *sfx) PA_GCC_PURE;
-char *pa_runtime_path(const char *fn, char *s, size_t l);
+FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result);
+char* pa_find_config_file(const char *global, const char *local, const char *env);
+
+char *pa_get_runtime_dir(void);
+char *pa_get_state_dir(void);
+char *pa_runtime_path(const char *fn);
+char *pa_state_path(const char *fn);
int pa_atoi(const char *s, int32_t *ret_i);
int pa_atou(const char *s, uint32_t *ret_u);
+int pa_atod(const char *s, double *ret_d);
+
+int pa_snprintf(char *str, size_t size, const char *format, ...);
+int pa_vsnprintf(char *str, size_t size, const char *format, va_list ap);
+
+char *pa_truncate_utf8(char *c, size_t l);
+
+char *pa_getcwd(void);
+char *pa_make_path_absolute(const char *p);
+pa_bool_t pa_is_path_absolute(const char *p);
+
+void *pa_will_need(const void *p, size_t l);
+
+static inline int pa_is_power_of_two(unsigned n) {
+ return !(n & (n - 1));
+}
+
+static inline unsigned pa_make_power_of_two(unsigned n) {
+ unsigned j = n;
+
+ if (pa_is_power_of_two(n))
+ return n;
+
+ while (j) {
+ j = j >> 1;
+ n = n | j;
+ }
+
+ return n + 1;
+}
+
+static inline unsigned pa_ulog2(unsigned n) {
+ unsigned r = 0;
+
+ while (n) {
+ r++;
+ n = n >> 1;
+ }
+
+ return r;
+}
+
+void pa_close_pipe(int fds[2]);
+
+char *pa_readlink(const char *p);
+
+int pa_close_all(int except_fd, ...);
+int pa_close_allv(const int except_fds[]);
+int pa_unblock_sigs(int except, ...);
+int pa_unblock_sigsv(const int except[]);
+int pa_reset_sigs(int except, ...);
+int pa_reset_sigsv(const int except[]);
+
+void pa_set_env(const char *key, const char *value);
+
+pa_bool_t pa_in_system_mode(void);
+
+#define pa_streq(a,b) (!strcmp((a),(b)))
#endif
diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c
index 63ee60ca..b2638b10 100644
--- a/src/pulsecore/core.c
+++ b/src/pulsecore/core.c
@@ -1,18 +1,19 @@
-/* $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
@@ -24,7 +25,6 @@
#endif
#include <stdlib.h>
-#include <assert.h>
#include <stdio.h>
#include <signal.h>
@@ -42,12 +42,36 @@
#include <pulsecore/props.h>
#include <pulsecore/random.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
#include "core.h"
+static PA_DEFINE_CHECK_TYPE(pa_core, pa_msgobject);
+
+static int core_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
+ pa_core *c = PA_CORE(o);
+
+ pa_core_assert_ref(c);
+
+ switch (code) {
+
+ case PA_CORE_MESSAGE_UNLOAD_MODULE:
+ pa_module_unload(c, userdata);
+ return 0;
+
+ default:
+ return -1;
+ }
+}
+
+static void core_free(pa_object *o);
+
pa_core* pa_core_new(pa_mainloop_api *m, int shared) {
pa_core* c;
pa_mempool *pool;
+ int j;
+
+ pa_assert(m);
if (shared) {
if (!(pool = pa_mempool_new(shared))) {
@@ -63,7 +87,9 @@ pa_core* pa_core_new(pa_mainloop_api *m, int shared) {
}
}
- c = pa_xnew(pa_core, 1);
+ c = pa_msgobject_new(pa_core);
+ c->parent.parent.free = core_free;
+ c->parent.process_msg = core_process_msg;
c->mainloop = m;
c->clients = pa_idxset_new(NULL, NULL);
@@ -79,11 +105,13 @@ pa_core* pa_core_new(pa_mainloop_api *m, int shared) {
c->scache = NULL;
c->autoload_idxset = NULL;
c->autoload_hashmap = NULL;
- c->running_as_daemon = 0;
+ c->running_as_daemon = FALSE;
c->default_sample_spec.format = PA_SAMPLE_S16NE;
c->default_sample_spec.rate = 44100;
c->default_sample_spec.channels = 2;
+ c->default_n_fragments = 4;
+ c->default_fragment_size_msec = 25;
c->module_auto_unload_event = NULL;
c->module_defer_unload_event = NULL;
@@ -95,8 +123,7 @@ pa_core* pa_core_new(pa_mainloop_api *m, int shared) {
c->subscription_event_last = NULL;
c->mempool = pool;
-
- c->disallow_module_loading = 0;
+ pa_silence_cache_init(&c->silence_cache);
c->quit_event = NULL;
@@ -104,44 +131,48 @@ pa_core* pa_core_new(pa_mainloop_api *m, int shared) {
c->module_idle_time = 20;
c->scache_idle_time = 20;
- c->resample_method = PA_RESAMPLER_SRC_SINC_FASTEST;
+ c->resample_method = PA_RESAMPLER_SPEEX_FLOAT_BASE + 3;
- c->is_system_instance = 0;
+ c->disallow_module_loading = FALSE;
+ c->realtime_scheduling = FALSE;
+ c->realtime_priority = 5;
+ c->disable_remixing = FALSE;
- pa_hook_init(&c->hook_sink_input_new, c);
- pa_hook_init(&c->hook_sink_disconnect, c);
- pa_hook_init(&c->hook_source_output_new, c);
- pa_hook_init(&c->hook_source_disconnect, c);
+ for (j = 0; j < PA_CORE_HOOK_MAX; j++)
+ pa_hook_init(&c->hooks[j], c);
pa_property_init(c);
pa_random(&c->cookie, sizeof(c->cookie));
-
+
#ifdef SIGPIPE
pa_check_signal_is_blocked(SIGPIPE);
#endif
+
return c;
}
-void pa_core_free(pa_core *c) {
- assert(c);
+static void core_free(pa_object *o) {
+ pa_core *c = PA_CORE(o);
+ int j;
+ pa_assert(c);
pa_module_unload_all(c);
- assert(!c->modules);
+ pa_assert(!c->modules);
- assert(pa_idxset_isempty(c->clients));
+ pa_assert(pa_idxset_isempty(c->clients));
pa_idxset_free(c->clients, NULL, NULL);
-
- assert(pa_idxset_isempty(c->sinks));
+
+ pa_assert(pa_idxset_isempty(c->sinks));
pa_idxset_free(c->sinks, NULL, NULL);
- assert(pa_idxset_isempty(c->sources));
+ pa_assert(pa_idxset_isempty(c->sources));
pa_idxset_free(c->sources, NULL, NULL);
-
- assert(pa_idxset_isempty(c->source_outputs));
+
+ pa_assert(pa_idxset_isempty(c->source_outputs));
pa_idxset_free(c->source_outputs, NULL, NULL);
-
- assert(pa_idxset_isempty(c->sink_inputs));
+
+ pa_assert(pa_idxset_isempty(c->sink_inputs));
pa_idxset_free(c->sink_inputs, NULL, NULL);
pa_scache_free(c);
@@ -155,36 +186,39 @@ void pa_core_free(pa_core *c) {
pa_xfree(c->default_source_name);
pa_xfree(c->default_sink_name);
+ pa_silence_cache_done(&c->silence_cache);
pa_mempool_free(c->mempool);
pa_property_cleanup(c);
- pa_hook_free(&c->hook_sink_input_new);
- pa_hook_free(&c->hook_sink_disconnect);
- pa_hook_free(&c->hook_source_output_new);
- pa_hook_free(&c->hook_source_disconnect);
-
- pa_xfree(c);
+ for (j = 0; j < PA_CORE_HOOK_MAX; j++)
+ pa_hook_free(&c->hooks[j]);
+
+ pa_xfree(c);
}
static void quit_callback(pa_mainloop_api*m, pa_time_event *e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
pa_core *c = userdata;
- assert(c->quit_event = e);
+ pa_assert(c->quit_event == e);
m->quit(m, 0);
}
void pa_core_check_quit(pa_core *c) {
- assert(c);
+ pa_assert(c);
+
+ if (!c->quit_event &&
+ c->exit_idle_time >= 0 &&
+ pa_idxset_size(c->clients) == 0) {
- if (!c->quit_event && c->exit_idle_time >= 0 && pa_idxset_size(c->clients) == 0) {
struct timeval tv;
pa_gettimeofday(&tv);
tv.tv_sec+= c->exit_idle_time;
+
c->quit_event = c->mainloop->time_new(c->mainloop, &tv, quit_callback, c);
+
} else if (c->quit_event && pa_idxset_size(c->clients) > 0) {
c->mainloop->time_free(c->quit_event);
c->quit_event = NULL;
}
}
-
diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
index 3a34d297..d9ed46f6 100644
--- a/src/pulsecore/core.h
+++ b/src/pulsecore/core.h
@@ -1,21 +1,21 @@
#ifndef foocorehfoo
#define foocorehfoo
-/* $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
@@ -32,21 +32,62 @@
#include <pulsecore/queue.h>
#include <pulsecore/llist.h>
#include <pulsecore/hook-list.h>
+#include <pulsecore/asyncmsgq.h>
+#include <pulsecore/sample-util.h>
typedef struct pa_core pa_core;
#include <pulsecore/core-subscribe.h>
#include <pulsecore/sink-input.h>
+#include <pulsecore/msgobject.h>
+
+typedef enum pa_core_hook {
+ PA_CORE_HOOK_SINK_NEW,
+ PA_CORE_HOOK_SINK_FIXATE,
+ PA_CORE_HOOK_SINK_PUT,
+ PA_CORE_HOOK_SINK_UNLINK,
+ PA_CORE_HOOK_SINK_UNLINK_POST,
+ PA_CORE_HOOK_SINK_STATE_CHANGED,
+ PA_CORE_HOOK_SINK_PROPLIST_CHANGED,
+ PA_CORE_HOOK_SOURCE_NEW,
+ PA_CORE_HOOK_SOURCE_FIXATE,
+ PA_CORE_HOOK_SOURCE_PUT,
+ PA_CORE_HOOK_SOURCE_UNLINK,
+ PA_CORE_HOOK_SOURCE_UNLINK_POST,
+ PA_CORE_HOOK_SOURCE_STATE_CHANGED,
+ PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED,
+ PA_CORE_HOOK_SINK_INPUT_NEW,
+ PA_CORE_HOOK_SINK_INPUT_FIXATE,
+ PA_CORE_HOOK_SINK_INPUT_PUT,
+ PA_CORE_HOOK_SINK_INPUT_UNLINK,
+ PA_CORE_HOOK_SINK_INPUT_UNLINK_POST,
+ PA_CORE_HOOK_SINK_INPUT_MOVE,
+ PA_CORE_HOOK_SINK_INPUT_MOVE_POST,
+ PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED,
+ PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED,
+ PA_CORE_HOOK_SOURCE_OUTPUT_NEW,
+ PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE,
+ PA_CORE_HOOK_SOURCE_OUTPUT_PUT,
+ PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK,
+ PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST,
+ PA_CORE_HOOK_SOURCE_OUTPUT_MOVE,
+ PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_POST,
+ PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED,
+ PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED,
+ PA_CORE_HOOK_MAX
+} pa_core_hook_t;
/* The core structure of PulseAudio. Every PulseAudio daemon contains
* exactly one of these. It is used for storing kind of global
* variables for the daemon. */
struct pa_core {
+ pa_msgobject parent;
+
/* A random value which may be used to identify this instance of
* PulseAudio. Not cryptographically secure in any way. */
uint32_t cookie;
-
+
pa_mainloop_api *mainloop;
/* idxset of all kinds of entities */
@@ -59,6 +100,8 @@ struct pa_core {
char *default_source_name, *default_sink_name;
pa_sample_spec default_sample_spec;
+ unsigned default_n_fragments, default_fragment_size_msec;
+
pa_time_event *module_auto_unload_event;
pa_defer_event *module_defer_unload_event;
@@ -68,28 +111,33 @@ struct pa_core {
pa_subscription_event *subscription_event_last;
pa_mempool *mempool;
+ pa_silence_cache silence_cache;
- int disallow_module_loading, running_as_daemon;
int exit_idle_time, module_idle_time, scache_idle_time;
pa_time_event *quit_event;
pa_time_event *scache_auto_unload_event;
+ pa_bool_t disallow_module_loading, running_as_daemon;
pa_resample_method_t resample_method;
-
- int is_system_instance;
+ pa_bool_t realtime_scheduling;
+ int realtime_priority;
+ pa_bool_t disable_remixing;
/* hooks */
- pa_hook
- hook_sink_input_new,
- hook_sink_disconnect,
- hook_source_output_new,
- hook_source_disconnect;
+ pa_hook hooks[PA_CORE_HOOK_MAX];
+};
+
+PA_DECLARE_CLASS(pa_core);
+#define PA_CORE(o) pa_core_cast(o)
+
+enum {
+ PA_CORE_MESSAGE_UNLOAD_MODULE,
+ PA_CORE_MESSAGE_MAX
};
pa_core* pa_core_new(pa_mainloop_api *m, int shared);
-void pa_core_free(pa_core*c);
/* Check whether noone is connected to this core */
void pa_core_check_quit(pa_core *c);
diff --git a/src/pulsecore/creds.h b/src/pulsecore/creds.h
index d92ce598..c15c469b 100644
--- a/src/pulsecore/creds.h
+++ b/src/pulsecore/creds.h
@@ -1,21 +1,21 @@
#ifndef foocredshfoo
#define foocredshfoo
-/* $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.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
@@ -24,7 +24,9 @@
#include <sys/types.h>
-/* config.h must be included before this file */
+#ifndef PACKAGE
+#error "Please include config.h before including this file!"
+#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
diff --git a/src/pulsecore/dllmain.c b/src/pulsecore/dllmain.c
index b86bf04f..269de604 100644
--- a/src/pulsecore/dllmain.c
+++ b/src/pulsecore/dllmain.c
@@ -1,8 +1,8 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
+ 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,
diff --git a/src/pulsecore/dynarray.c b/src/pulsecore/dynarray.c
index cd1fcb0f..a1fcd8a3 100644
--- a/src/pulsecore/dynarray.c
+++ b/src/pulsecore/dynarray.c
@@ -1,18 +1,18 @@
-/* $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.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
@@ -24,10 +24,10 @@
#endif
#include <string.h>
-#include <assert.h>
#include <stdlib.h>
#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
#include "dynarray.h"
@@ -50,7 +50,7 @@ pa_dynarray* pa_dynarray_new(void) {
void pa_dynarray_free(pa_dynarray* a, void (*func)(void *p, void *userdata), void *userdata) {
unsigned i;
- assert(a);
+ pa_assert(a);
if (func)
for (i = 0; i < a->n_entries; i++)
@@ -62,7 +62,7 @@ void pa_dynarray_free(pa_dynarray* a, void (*func)(void *p, void *userdata), voi
}
void pa_dynarray_put(pa_dynarray*a, unsigned i, void *p) {
- assert(a);
+ pa_assert(a);
if (i >= a->n_allocated) {
unsigned n;
@@ -83,21 +83,27 @@ void pa_dynarray_put(pa_dynarray*a, unsigned i, void *p) {
}
unsigned pa_dynarray_append(pa_dynarray*a, void *p) {
- unsigned i = a->n_entries;
+ unsigned i;
+
+ pa_assert(a);
+
+ i = a->n_entries;
pa_dynarray_put(a, i, p);
return i;
}
void *pa_dynarray_get(pa_dynarray*a, unsigned i) {
- assert(a);
+ pa_assert(a);
+
if (i >= a->n_entries)
return NULL;
- assert(a->data);
+ pa_assert(a->data);
return a->data[i];
}
unsigned pa_dynarray_size(pa_dynarray*a) {
- assert(a);
+ pa_assert(a);
+
return a->n_entries;
}
diff --git a/src/pulsecore/dynarray.h b/src/pulsecore/dynarray.h
index 4ddb526c..82b42082 100644
--- a/src/pulsecore/dynarray.h
+++ b/src/pulsecore/dynarray.h
@@ -1,21 +1,21 @@
#ifndef foodynarrayhfoo
#define foodynarrayhfoo
-/* $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.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
diff --git a/src/pulsecore/endianmacros.h b/src/pulsecore/endianmacros.h
index 65db3feb..26336918 100644
--- a/src/pulsecore/endianmacros.h
+++ b/src/pulsecore/endianmacros.h
@@ -1,21 +1,22 @@
#ifndef fooendianmacroshfoo
#define fooendianmacroshfoo
-/* $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
@@ -24,54 +25,94 @@
#include <inttypes.h>
-#ifdef HAVE_CONFIG_H
-#include <config.h>
+#ifndef PACKAGE
+#error "Please include config.h before including this file!"
+#endif
+
+#ifdef HAVE_BYTESWAP_H
+#include <byteswap.h>
+#endif
+
+#ifdef HAVE_BYTESWAP_H
+#define PA_INT16_SWAP(x) ((int16_t) bswap_16((uint16_t) x))
+#define PA_UINT16_SWAP(x) ((uint16_t) bswap_16((uint16_t) x))
+#define PA_INT32_SWAP(x) ((int32_t) bswap_32((uint32_t) x))
+#define PA_UINT32_SWAP(x) ((uint32_t) bswap_32((uint32_t) x))
+#else
+#define PA_INT16_SWAP(x) ( (int16_t) ( ((uint16_t) x >> 8) | ((uint16_t) x << 8) ) )
+#define PA_UINT16_SWAP(x) ( (uint16_t) ( ((uint16_t) x >> 8) | ((uint16_t) x << 8) ) )
+#define PA_INT32_SWAP(x) ( (int32_t) ( ((uint32_t) x >> 24) | ((uint32_t) x << 24) | (((uint32_t) x & 0xFF00) << 8) | ((((uint32_t) x) >> 8) & 0xFF00) ) )
+#define PA_UINT32_SWAP(x) ( (uint32_t) ( ((uint32_t) x >> 24) | ((uint32_t) x << 24) | (((uint32_t) x & 0xFF00) << 8) | ((((uint32_t) x) >> 8) & 0xFF00) ) )
#endif
-#define INT16_SWAP(x) ( (int16_t) ( ((uint16_t) x >> 8) | ((uint16_t) x << 8) ) )
-#define UINT16_SWAP(x) ( (uint16_t) ( ((uint16_t) x >> 8) | ((uint16_t) x << 8) ) )
-#define INT32_SWAP(x) ( (int32_t) ( ((uint32_t) x >> 24) | ((uint32_t) x << 24) | (((uint32_t) x & 0xFF00) << 8) | ((((uint32_t) x) >> 8) & 0xFF00) ) )
-#define UINT32_SWAP(x) ( (uint32_t) ( ((uint32_t) x >> 24) | ((uint32_t) x << 24) | (((uint32_t) x & 0xFF00) << 8) | ((((uint32_t) x) >> 8) & 0xFF00) ) )
+static inline float PA_FLOAT32_SWAP(float x) {
+ uint32_t i = *(uint32_t*) &x;
+ i = PA_UINT32_SWAP(i);
+ return *(float*) &i;
+}
+
+#define PA_MAYBE_INT16_SWAP(c,x) ((c) ? PA_INT32_SWAP(x) : x)
+#define PA_MAYBE_UINT16_SWAP(c,x) ((c) ? PA_UINT32_SWAP(x) : x)
+
+#define PA_MAYBE_INT32_SWAP(c,x) ((c) ? PA_INT32_SWAP(x) : x)
+#define PA_MAYBE_UINT32_SWAP(c,x) ((c) ? PA_UINT32_SWAP(x) : x)
-#define MAYBE_INT32_SWAP(c,x) ((c) ? INT32_SWAP(x) : x)
-#define MAYBE_UINT32_SWAP(c,x) ((c) ? UINT32_SWAP(x) : x)
+#define PA_MAYBE_FLOAT32_SWAP(c,x) ((c) ? PA_FLOAT32_SWAP(x) : x)
#ifdef WORDS_BIGENDIAN
- #define INT16_FROM_LE(x) INT16_SWAP(x)
- #define INT16_FROM_BE(x) ((int16_t)(x))
+ #define PA_INT16_FROM_LE(x) PA_INT16_SWAP(x)
+ #define PA_INT16_FROM_BE(x) ((int16_t)(x))
+
+ #define PA_INT16_TO_LE(x) PA_INT16_SWAP(x)
+ #define PA_INT16_TO_BE(x) ((int16_t)(x))
+
+ #define PA_UINT16_FROM_LE(x) PA_UINT16_SWAP(x)
+ #define PA_UINT16_FROM_BE(x) ((uint16_t)(x))
- #define INT16_TO_LE(x) INT16_SWAP(x)
- #define INT16_TO_BE(x) ((int16_t)(x))
+ #define PA_UINT16_TO_LE(x) PA_UINT16_SWAP(x)
+ #define PA_UINT16_TO_BE(x) ((uint16_t)(x))
- #define UINT16_FROM_LE(x) UINT16_SWAP(x)
- #define UINT16_FROM_BE(x) ((uint16_t)(x))
+ #define PA_INT32_FROM_LE(x) PA_INT32_SWAP(x)
+ #define PA_INT32_FROM_BE(x) ((int32_t)(x))
- #define INT32_FROM_LE(x) INT32_SWAP(x)
- #define INT32_FROM_BE(x) ((int32_t)(x))
+ #define PA_INT32_TO_LE(x) PA_INT32_SWAP(x)
+ #define PA_INT32_TO_BE(x) ((int32_t)(x))
- #define UINT32_FROM_LE(x) UINT32_SWAP(x)
- #define UINT32_FROM_BE(x) ((uint32_t)(x))
+ #define PA_UINT32_FROM_LE(x) PA_UINT32_SWAP(x)
+ #define PA_UINT32_FROM_BE(x) ((uint32_t)(x))
- #define UINT32_TO_LE(x) UINT32_SWAP(x)
- #define UINT32_TO_BE(x) ((uint32_t)(x))
+ #define PA_UINT32_TO_LE(x) PA_UINT32_SWAP(x)
+ #define PA_UINT32_TO_BE(x) ((uint32_t)(x))
+
+ #define PA_FLOAT32_TO_LE(x) PA_FLOAT32_SWAP(x)
+ #define PA_FLOAT32_TO_BE(x) ((float) (x))
#else
- #define INT16_FROM_LE(x) ((int16_t)(x))
- #define INT16_FROM_BE(x) INT16_SWAP(x)
+ #define PA_INT16_FROM_LE(x) ((int16_t)(x))
+ #define PA_INT16_FROM_BE(x) PA_INT16_SWAP(x)
+
+ #define PA_INT16_TO_LE(x) ((int16_t)(x))
+ #define PA_INT16_TO_BE(x) PA_INT16_SWAP(x)
+
+ #define PA_UINT16_FROM_LE(x) ((uint16_t)(x))
+ #define PA_UINT16_FROM_BE(x) PA_UINT16_SWAP(x)
+
+ #define PA_UINT16_TO_LE(x) ((uint16_t)(x))
+ #define PA_UINT16_TO_BE(x) PA_UINT16_SWAP(x)
- #define INT16_TO_LE(x) ((int16_t)(x))
- #define INT16_TO_BE(x) INT16_SWAP(x)
+ #define PA_INT32_FROM_LE(x) ((int32_t)(x))
+ #define PA_INT32_FROM_BE(x) PA_INT32_SWAP(x)
- #define UINT16_FROM_LE(x) ((uint16_t)(x))
- #define UINT16_FROM_BE(x) UINT16_SWAP(x)
+ #define PA_INT32_TO_LE(x) ((int32_t)(x))
+ #define PA_INT32_TO_BE(x) PA_INT32_SWAP(x)
- #define INT32_FROM_LE(x) ((int32_t)(x))
- #define INT32_FROM_BE(x) INT32_SWAP(x)
+ #define PA_UINT32_FROM_LE(x) ((uint32_t)(x))
+ #define PA_UINT32_FROM_BE(x) PA_UINT32_SWAP(x)
- #define UINT32_FROM_LE(x) ((uint32_t)(x))
- #define UINT32_FROM_BE(x) UINT32_SWAP(x)
+ #define PA_UINT32_TO_LE(x) ((uint32_t)(x))
+ #define PA_UINT32_TO_BE(x) PA_UINT32_SWAP(x)
- #define UINT32_TO_LE(x) ((uint32_t)(x))
- #define UINT32_TO_BE(x) UINT32_SWAP(x)
+ #define PA_FLOAT32_TO_LE(x) ((float) (x))
+ #define PA_FLOAT32_TO_BE(x) PA_FLOAT32_SWAP(x)
#endif
#endif
diff --git a/src/pulsecore/envelope.c b/src/pulsecore/envelope.c
new file mode 100644
index 00000000..e2691611
--- /dev/null
+++ b/src/pulsecore/envelope.c
@@ -0,0 +1,781 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2007 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+
+#include <pulse/sample.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/endianmacros.h>
+#include <pulsecore/memchunk.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/flist.h>
+#include <pulsecore/semaphore.h>
+#include <pulsecore/g711.h>
+
+#include "envelope.h"
+
+/*
+ Envelope subsystem for applying linear interpolated volume
+ envelopes on audio data. If multiple enevelopes shall be applied
+ at the same time, the "minimum" envelope is determined and
+ applied.
+
+ Envelopes are defined in a statically allocated constant structure
+ pa_envelope_def. It may be activated using pa_envelope_add(). And
+ already active envelope may be replaced with pa_envelope_replace()
+ and removed with pa_envelope_remove().The combined "minimum"
+ envelope can be applied to audio data with pa_envelope_apply().
+
+ _apply() on one hand and _add()/_replace()/_remove() on the other
+ can be executed in seperate threads, in which case no locking is
+ used.
+*/
+
+PA_STATIC_FLIST_DECLARE(items, 0, pa_xfree);
+
+struct pa_envelope_item {
+ PA_LLIST_FIELDS(pa_envelope_item);
+ const pa_envelope_def *def;
+ pa_usec_t start_x;
+ union {
+ int32_t i;
+ float f;
+ } start_y;
+ unsigned j;
+};
+
+enum envelope_state {
+ STATE_VALID0,
+ STATE_VALID1,
+ STATE_READ0,
+ STATE_READ1,
+ STATE_WAIT0,
+ STATE_WAIT1,
+ STATE_WRITE0,
+ STATE_WRITE1
+};
+
+struct pa_envelope {
+ pa_sample_spec sample_spec;
+
+ PA_LLIST_HEAD(pa_envelope_item, items);
+
+ pa_atomic_t state;
+
+ size_t x;
+
+ struct {
+ unsigned n_points, n_allocated, n_current;
+
+ size_t *x;
+ union {
+ int32_t *i;
+ float *f;
+ } y;
+
+ size_t cached_dx;
+ int32_t cached_dy_i;
+ float cached_dy_dx;
+ pa_bool_t cached_valid;
+ } points[2];
+
+ pa_bool_t is_float;
+
+ pa_semaphore *semaphore;
+};
+
+pa_envelope *pa_envelope_new(const pa_sample_spec *ss) {
+ pa_envelope *e;
+ pa_assert(ss);
+
+ e = pa_xnew(pa_envelope, 1);
+
+ e->sample_spec = *ss;
+ PA_LLIST_HEAD_INIT(pa_envelope_item, e->items);
+
+ e->x = 0;
+
+ e->points[0].n_points = e->points[1].n_points = 0;
+ e->points[0].n_allocated = e->points[1].n_allocated = 0;
+ e->points[0].n_current = e->points[1].n_current = 0;
+ e->points[0].x = e->points[1].x = NULL;
+ e->points[0].y.i = e->points[1].y.i = NULL;
+ e->points[0].cached_valid = e->points[1].cached_valid = FALSE;
+
+ pa_atomic_store(&e->state, STATE_VALID0);
+
+ e->is_float =
+ ss->format == PA_SAMPLE_FLOAT32LE ||
+ ss->format == PA_SAMPLE_FLOAT32BE;
+
+ e->semaphore = pa_semaphore_new(0);
+
+ return e;
+}
+
+void pa_envelope_free(pa_envelope *e) {
+ pa_assert(e);
+
+ while (e->items)
+ pa_envelope_remove(e, e->items);
+
+ pa_xfree(e->points[0].x);
+ pa_xfree(e->points[1].x);
+ pa_xfree(e->points[0].y.i);
+ pa_xfree(e->points[1].y.i);
+
+ pa_semaphore_free(e->semaphore);
+
+ pa_xfree(e);
+}
+
+static int32_t linear_interpolate_int(pa_usec_t x1, int32_t _y1, pa_usec_t x2, int32_t y2, pa_usec_t x3) {
+ return (int32_t) (_y1 + (x3 - x1) * (float) (y2 - _y1) / (float) (x2 - x1));
+}
+
+static float linear_interpolate_float(pa_usec_t x1, float _y1, pa_usec_t x2, float y2, pa_usec_t x3) {
+ return _y1 + (x3 - x1) * (y2 - _y1) / (x2 - x1);
+}
+
+static int32_t item_get_int(pa_envelope_item *i, pa_usec_t x) {
+ pa_assert(i);
+
+ if (x <= i->start_x)
+ return i->start_y.i;
+
+ x -= i->start_x;
+
+ if (x <= i->def->points_x[0])
+ return linear_interpolate_int(0, i->start_y.i,
+ i->def->points_x[0], i->def->points_y.i[0], x);
+
+ if (x >= i->def->points_x[i->def->n_points-1])
+ return i->def->points_y.i[i->def->n_points-1];
+
+ pa_assert(i->j > 0);
+ pa_assert(i->def->points_x[i->j-1] <= x);
+ pa_assert(x < i->def->points_x[i->j]);
+
+ return linear_interpolate_int(i->def->points_x[i->j-1], i->def->points_y.i[i->j-1],
+ i->def->points_x[i->j], i->def->points_y.i[i->j], x);
+}
+
+static float item_get_float(pa_envelope_item *i, pa_usec_t x) {
+ pa_assert(i);
+
+ if (x <= i->start_x)
+ return i->start_y.f;
+
+ x -= i->start_x;
+
+ if (x <= i->def->points_x[0])
+ return linear_interpolate_float(0, i->start_y.f,
+ i->def->points_x[0], i->def->points_y.f[0], x);
+
+ if (x >= i->def->points_x[i->def->n_points-1])
+ return i->def->points_y.f[i->def->n_points-1];
+
+ pa_assert(i->j > 0);
+ pa_assert(i->def->points_x[i->j-1] <= x);
+ pa_assert(x < i->def->points_x[i->j]);
+
+ return linear_interpolate_float(i->def->points_x[i->j-1], i->def->points_y.f[i->j-1],
+ i->def->points_x[i->j], i->def->points_y.f[i->j], x);
+}
+
+static void envelope_begin_write(pa_envelope *e, int *v) {
+ enum envelope_state new_state, old_state;
+ pa_bool_t wait_sem;
+
+ pa_assert(e);
+ pa_assert(v);
+
+ for (;;) {
+ do {
+ wait_sem = FALSE;
+ old_state = pa_atomic_load(&e->state);
+
+ switch (old_state) {
+ case STATE_VALID0:
+ *v = 1;
+ new_state = STATE_WRITE0;
+ break;
+ case STATE_VALID1:
+ *v = 0;
+ new_state = STATE_WRITE1;
+ break;
+ case STATE_READ0:
+ new_state = STATE_WAIT0;
+ wait_sem = TRUE;
+ break;
+ case STATE_READ1:
+ new_state = STATE_WAIT1;
+ wait_sem = TRUE;
+ break;
+ default:
+ pa_assert_not_reached();
+ }
+ } while (!pa_atomic_cmpxchg(&e->state, old_state, new_state));
+
+ if (!wait_sem)
+ break;
+
+ pa_semaphore_wait(e->semaphore);
+ }
+}
+
+static pa_bool_t envelope_commit_write(pa_envelope *e, int v) {
+ enum envelope_state new_state, old_state;
+
+ pa_assert(e);
+
+ do {
+ old_state = pa_atomic_load(&e->state);
+
+ switch (old_state) {
+ case STATE_WRITE0:
+ pa_assert(v == 1);
+ new_state = STATE_VALID1;
+ break;
+ case STATE_WRITE1:
+ pa_assert(v == 0);
+ new_state = STATE_VALID0;
+ break;
+ case STATE_VALID0:
+ case STATE_VALID1:
+ case STATE_READ0:
+ case STATE_READ1:
+ return FALSE;
+ default:
+ pa_assert_not_reached();
+ }
+ } while (!pa_atomic_cmpxchg(&e->state, old_state, new_state));
+
+ return TRUE;
+}
+
+static void envelope_begin_read(pa_envelope *e, int *v) {
+ enum envelope_state new_state, old_state;
+ pa_assert(e);
+ pa_assert(v);
+
+ do {
+ old_state = pa_atomic_load(&e->state);
+
+ switch (old_state) {
+ case STATE_VALID0:
+ case STATE_WRITE0:
+ *v = 0;
+ new_state = STATE_READ0;
+ break;
+ case STATE_VALID1:
+ case STATE_WRITE1:
+ *v = 1;
+ new_state = STATE_READ1;
+ break;
+ default:
+ pa_assert_not_reached();
+ }
+ } while (!pa_atomic_cmpxchg(&e->state, old_state, new_state));
+}
+
+static void envelope_commit_read(pa_envelope *e, int v) {
+ enum envelope_state new_state, old_state;
+ pa_bool_t post_sem;
+
+ pa_assert(e);
+
+ do {
+ post_sem = FALSE;
+ old_state = pa_atomic_load(&e->state);
+
+ switch (old_state) {
+ case STATE_READ0:
+ pa_assert(v == 0);
+ new_state = STATE_VALID0;
+ break;
+ case STATE_READ1:
+ pa_assert(v == 1);
+ new_state = STATE_VALID1;
+ break;
+ case STATE_WAIT0:
+ pa_assert(v == 0);
+ new_state = STATE_VALID0;
+ post_sem = TRUE;
+ break;
+ case STATE_WAIT1:
+ pa_assert(v == 1);
+ new_state = STATE_VALID1;
+ post_sem = TRUE;
+ break;
+ default:
+ pa_assert_not_reached();
+ }
+ } while (!pa_atomic_cmpxchg(&e->state, old_state, new_state));
+
+ if (post_sem)
+ pa_semaphore_post(e->semaphore);
+}
+
+static void envelope_merge(pa_envelope *e, int v) {
+
+ e->points[v].n_points = 0;
+
+ if (e->items) {
+ pa_envelope_item *i;
+ pa_usec_t x = (pa_usec_t) -1;
+
+ for (i = e->items; i; i = i->next)
+ i->j = 0;
+
+ for (;;) {
+ pa_bool_t min_is_set;
+ pa_envelope_item *s = NULL;
+
+ /* Let's find the next spot on the X axis to analyze */
+ for (i = e->items; i; i = i->next) {
+
+ for (;;) {
+
+ if (i->j >= i->def->n_points)
+ break;
+
+ if ((x != (pa_usec_t) -1) && i->start_x + i->def->points_x[i->j] <= x) {
+ i->j++;
+ continue;
+ }
+
+ if (!s || (i->start_x + i->def->points_x[i->j] < s->start_x + s->def->points_x[s->j]))
+ s = i;
+
+ break;
+ }
+ }
+
+ if (!s)
+ break;
+
+ if (e->points[v].n_points >= e->points[v].n_allocated) {
+ e->points[v].n_allocated = PA_MAX(e->points[v].n_points*2, PA_ENVELOPE_POINTS_MAX);
+
+ e->points[v].x = pa_xrealloc(e->points[v].x, sizeof(size_t) * e->points[v].n_allocated);
+ e->points[v].y.i = pa_xrealloc(e->points[v].y.i, sizeof(int32_t) * e->points[v].n_allocated);
+ }
+
+ x = s->start_x + s->def->points_x[s->j];
+ e->points[v].x[e->points[v].n_points] = pa_usec_to_bytes(x, &e->sample_spec);
+
+ min_is_set = FALSE;
+
+ /* Now let's find the lowest value */
+ if (e->is_float) {
+ float min_f;
+
+ for (i = e->items; i; i = i->next) {
+ float f = item_get_float(i, x);
+ if (!min_is_set || f < min_f) {
+ min_f = f;
+ min_is_set = TRUE;
+ }
+ }
+
+ e->points[v].y.f[e->points[v].n_points] = min_f;
+ } else {
+ int32_t min_k;
+
+ for (i = e->items; i; i = i->next) {
+ int32_t k = item_get_int(i, x);
+ if (!min_is_set || k < min_k) {
+ min_k = k;
+ min_is_set = TRUE;
+ }
+ }
+
+ e->points[v].y.i[e->points[v].n_points] = min_k;
+ }
+
+ pa_assert_se(min_is_set);
+ e->points[v].n_points++;
+ }
+ }
+
+ e->points[v].n_current = 0;
+ e->points[v].cached_valid = FALSE;
+}
+
+pa_envelope_item *pa_envelope_add(pa_envelope *e, const pa_envelope_def *def) {
+ pa_envelope_item *i;
+ int v;
+
+ pa_assert(e);
+ pa_assert(def);
+ pa_assert(def->n_points > 0);
+
+ if (!(i = pa_flist_pop(PA_STATIC_FLIST_GET(items))))
+ i = pa_xnew(pa_envelope_item, 1);
+
+ i->def = def;
+
+ if (e->is_float)
+ i->start_y.f = def->points_y.f[0];
+ else
+ i->start_y.i = def->points_y.i[0];
+
+ PA_LLIST_PREPEND(pa_envelope_item, e->items, i);
+
+ envelope_begin_write(e, &v);
+
+ do {
+
+ i->start_x = pa_bytes_to_usec(e->x, &e->sample_spec);
+ envelope_merge(e, v);
+
+ } while (!envelope_commit_write(e, v));
+
+ return i;
+}
+
+pa_envelope_item *pa_envelope_replace(pa_envelope *e, pa_envelope_item *i, const pa_envelope_def *def) {
+ pa_usec_t x;
+ int v;
+
+ pa_assert(e);
+ pa_assert(i);
+ pa_assert(def->n_points > 0);
+
+ envelope_begin_write(e, &v);
+
+ for (;;) {
+ float saved_f;
+ int32_t saved_i;
+ uint64_t saved_start_x;
+ const pa_envelope_def *saved_def;
+
+ x = pa_bytes_to_usec(e->x, &e->sample_spec);
+
+ if (e->is_float) {
+ saved_f = i->start_y.f;
+ i->start_y.f = item_get_float(i, x);
+ } else {
+ saved_i = i->start_y.i;
+ i->start_y.i = item_get_int(i, x);
+ }
+
+ saved_start_x = i->start_x;
+ saved_def = i->def;
+
+ i->start_x = x;
+ i->def = def;
+
+ envelope_merge(e, v);
+
+ if (envelope_commit_write(e, v))
+ break;
+
+ i->start_x = saved_start_x;
+ i->def = saved_def;
+
+ if (e->is_float)
+ i->start_y.f = saved_f;
+ else
+ i->start_y.i = saved_i;
+ }
+
+ return i;
+}
+
+void pa_envelope_remove(pa_envelope *e, pa_envelope_item *i) {
+ int v;
+
+ pa_assert(e);
+ pa_assert(i);
+
+ PA_LLIST_REMOVE(pa_envelope_item, e->items, i);
+
+ if (pa_flist_push(PA_STATIC_FLIST_GET(items), i) < 0)
+ pa_xfree(i);
+
+ envelope_begin_write(e, &v);
+ do {
+ envelope_merge(e, v);
+ } while (!envelope_commit_write(e, v));
+}
+
+static int32_t linear_get_int(pa_envelope *e, int v) {
+ pa_assert(e);
+
+ /* The repeated division could be replaced by Bresenham, as an
+ * optimization */
+
+ if (e->x < e->points[v].x[0])
+ return e->points[v].y.i[0];
+
+ for (;;) {
+ if (e->points[v].n_current+1 >= e->points[v].n_points)
+ return e->points[v].y.i[e->points[v].n_points-1];
+
+ if (e->x < e->points[v].x[e->points[v].n_current+1])
+ break;
+
+ e->points[v].n_current++;
+ e->points[v].cached_valid = FALSE;
+ }
+
+ if (!e->points[v].cached_valid) {
+ e->points[v].cached_dx = e->points[v].x[e->points[v].n_current+1] - e->points[v].x[e->points[v].n_current];
+ e->points[v].cached_dy_i = e->points[v].y.i[e->points[v].n_current+1] - e->points[v].y.i[e->points[v].n_current];
+ e->points[v].cached_valid = TRUE;
+ }
+
+ return e->points[v].y.i[e->points[v].n_current] + (e->points[v].cached_dy_i * (int32_t) (e->x - e->points[v].x[e->points[v].n_current])) / (int32_t) e->points[v].cached_dx;
+}
+
+static float linear_get_float(pa_envelope *e, int v) {
+ pa_assert(e);
+
+ if (e->x < e->points[v].x[0])
+ return e->points[v].y.f[0];
+
+ for (;;) {
+ if (e->points[v].n_current+1 >= e->points[v].n_points)
+ return e->points[v].y.f[e->points[v].n_points-1];
+
+ if (e->x < e->points[v].x[e->points[v].n_current+1])
+ break;
+
+ e->points[v].n_current++;
+ e->points[v].cached_valid = FALSE;
+ }
+
+ if (!e->points[v].cached_valid) {
+ e->points[v].cached_dy_dx =
+ (e->points[v].y.f[e->points[v].n_current+1] - e->points[v].y.f[e->points[v].n_current]) /
+ (e->points[v].x[e->points[v].n_current+1] - e->points[v].x[e->points[v].n_current]);
+ e->points[v].cached_valid = TRUE;
+ }
+
+ return e->points[v].y.f[e->points[v].n_current] + (e->x - e->points[v].x[e->points[v].n_current]) * e->points[v].cached_dy_dx;
+}
+
+void pa_envelope_apply(pa_envelope *e, pa_memchunk *chunk) {
+ int v;
+
+ pa_assert(e);
+ pa_assert(chunk);
+
+ envelope_begin_read(e, &v);
+
+ if (e->points[v].n_points > 0) {
+ void *p;
+ size_t fs, n;
+
+ pa_memchunk_make_writable(chunk, 0);
+ p = (uint8_t*) pa_memblock_acquire(chunk->memblock) + chunk->index;
+ fs = pa_frame_size(&e->sample_spec);
+ n = chunk->length;
+
+ switch (e->sample_spec.format) {
+
+
+
+ case PA_SAMPLE_U8: {
+ uint8_t *t;
+
+ for (t = p; n > 0; n -= fs) {
+ int16_t factor = linear_get_int(e, v);
+ unsigned c;
+ e->x += fs;
+
+ for (c = 0; c < e->sample_spec.channels; c++, t++)
+ *t = (uint8_t) (((factor * ((int16_t) *t - 0x80)) / 0x10000) + 0x80);
+ }
+
+ break;
+ }
+
+ case PA_SAMPLE_ULAW: {
+ uint8_t *t;
+
+ for (t = p; n > 0; n -= fs) {
+ int16_t factor = linear_get_int(e, v);
+ unsigned c;
+ e->x += fs;
+
+ for (c = 0; c < e->sample_spec.channels; c++, t++) {
+ int16_t k = st_ulaw2linear16(*t);
+ *t = (uint8_t) st_14linear2ulaw(((factor * k) / 0x10000) >> 2);
+ }
+ }
+
+ break;
+ }
+
+ case PA_SAMPLE_ALAW: {
+ uint8_t *t;
+
+ for (t = p; n > 0; n -= fs) {
+ int16_t factor = linear_get_int(e, v);
+ unsigned c;
+ e->x += fs;
+
+ for (c = 0; c < e->sample_spec.channels; c++, t++) {
+ int16_t k = st_alaw2linear16(*t);
+ *t = (uint8_t) st_13linear2alaw(((factor * k) / 0x10000) >> 3);
+ }
+ }
+
+ break;
+ }
+
+ case PA_SAMPLE_S16NE: {
+ int16_t *t;
+
+ for (t = p; n > 0; n -= fs) {
+ int32_t factor = linear_get_int(e, v);
+ unsigned c;
+ e->x += fs;
+
+ for (c = 0; c < e->sample_spec.channels; c++, t++)
+ *t = (factor * *t) / 0x10000;
+ }
+
+ break;
+ }
+
+ case PA_SAMPLE_S16RE: {
+ int16_t *t;
+
+ for (t = p; n > 0; n -= fs) {
+ int32_t factor = linear_get_int(e, v);
+ unsigned c;
+ e->x += fs;
+
+ for (c = 0; c < e->sample_spec.channels; c++, t++) {
+ int16_t r = (factor * PA_INT16_SWAP(*t)) / 0x10000;
+ *t = PA_INT16_SWAP(r);
+ }
+ }
+
+ break;
+ }
+
+ case PA_SAMPLE_S32NE: {
+ int32_t *t;
+
+ for (t = p; n > 0; n -= fs) {
+ int32_t factor = linear_get_int(e, v);
+ unsigned c;
+ e->x += fs;
+
+ for (c = 0; c < e->sample_spec.channels; c++, t++)
+ *t = (int32_t) (((int64_t) factor * (int64_t) *t) / 0x10000);
+ }
+
+ break;
+ }
+
+ case PA_SAMPLE_S32RE: {
+ int32_t *t;
+
+ for (t = p; n > 0; n -= fs) {
+ int32_t factor = linear_get_int(e, v);
+ unsigned c;
+ e->x += fs;
+
+ for (c = 0; c < e->sample_spec.channels; c++, t++) {
+ int32_t r = (int32_t) (((int64_t) factor * (int64_t) PA_INT32_SWAP(*t)) / 0x10000);
+ *t = PA_INT32_SWAP(r);
+ }
+ }
+
+ break;
+ }
+
+ case PA_SAMPLE_FLOAT32NE: {
+ float *t;
+
+ for (t = p; n > 0; n -= fs) {
+ float factor = linear_get_float(e, v);
+ unsigned c;
+ e->x += fs;
+
+ for (c = 0; c < e->sample_spec.channels; c++, t++)
+ *t = *t * factor;
+ }
+
+ break;
+ }
+
+ case PA_SAMPLE_FLOAT32RE: {
+ float *t;
+
+ for (t = p; n > 0; n -= fs) {
+ float factor = linear_get_float(e, v);
+ unsigned c;
+ e->x += fs;
+
+ for (c = 0; c < e->sample_spec.channels; c++, t++) {
+ float r = PA_FLOAT32_SWAP(*t) * factor;
+ *t = PA_FLOAT32_SWAP(r);
+ }
+ }
+
+ break;
+ }
+
+ case PA_SAMPLE_MAX:
+ case PA_SAMPLE_INVALID:
+ pa_assert_not_reached();
+ }
+
+ pa_memblock_release(chunk->memblock);
+
+ e->x += chunk->length;
+ } else {
+ /* When we have no envelope to apply we reset our origin */
+ e->x = 0;
+ }
+
+ envelope_commit_read(e, v);
+}
+
+void pa_envelope_rewind(pa_envelope *e, size_t n_bytes) {
+ int v;
+
+ pa_assert(e);
+
+ envelope_begin_read(e, &v);
+
+ if (n_bytes < e->x)
+ e->x -= n_bytes;
+ else
+ e->x = 0;
+
+ e->points[v].n_current = 0;
+ e->points[v].cached_valid = FALSE;
+
+ envelope_commit_read(e, v);
+}
diff --git a/src/pulsecore/envelope.h b/src/pulsecore/envelope.h
new file mode 100644
index 00000000..5296415a
--- /dev/null
+++ b/src/pulsecore/envelope.h
@@ -0,0 +1,53 @@
+#ifndef foopulseenvelopehfoo
+#define foopulseenvelopehfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2007 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <pulsecore/macro.h>
+#include <pulsecore/memchunk.h>
+
+#include <pulse/sample.h>
+
+#define PA_ENVELOPE_POINTS_MAX 4U
+
+typedef struct pa_envelope pa_envelope;
+typedef struct pa_envelope_item pa_envelope_item;
+
+typedef struct pa_envelope_def {
+ unsigned n_points;
+
+ pa_usec_t points_x[PA_ENVELOPE_POINTS_MAX];
+ struct {
+ int32_t i[PA_ENVELOPE_POINTS_MAX];
+ float f[PA_ENVELOPE_POINTS_MAX];
+ } points_y;
+} pa_envelope_def;
+
+pa_envelope *pa_envelope_new(const pa_sample_spec *ss);
+void pa_envelope_free(pa_envelope *e);
+pa_envelope_item *pa_envelope_add(pa_envelope *e, const pa_envelope_def *def);
+pa_envelope_item *pa_envelope_replace(pa_envelope *e, pa_envelope_item *i, const pa_envelope_def *def);
+void pa_envelope_remove(pa_envelope *e, pa_envelope_item *i);
+void pa_envelope_apply(pa_envelope *e, pa_memchunk *chunk);
+void pa_envelope_rewind(pa_envelope *e, size_t n_bytes);
+
+#endif
diff --git a/src/pulsecore/esound.h b/src/pulsecore/esound.h
index 9d44f65c..79322ae4 100644
--- a/src/pulsecore/esound.h
+++ b/src/pulsecore/esound.h
@@ -1,21 +1,21 @@
#ifndef fooesoundhfoo
#define fooesoundhfoo
-/* $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
@@ -203,7 +203,7 @@ typedef int esd_client_state_t;
/* the endian key is transferred in binary, if it's read into int, */
/* and matches ESD_ENDIAN_KEY (ENDN), then the endianness of the */
/* server and the client match; if it's SWAP_ENDIAN_KEY, swap data */
-#define ESD_SWAP_ENDIAN_KEY (UINT32_SWAP(ESD_ENDIAN_KEY))
+#define ESD_SWAP_ENDIAN_KEY (PA_UINT32_SWAP(ESD_ENDIAN_KEY))
#endif
diff --git a/src/pulsecore/fdsem.c b/src/pulsecore/fdsem.c
new file mode 100644
index 00000000..1531e3db
--- /dev/null
+++ b/src/pulsecore/fdsem.c
@@ -0,0 +1,324 @@
+/***
+ 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.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_SYS_SYSCALL_H
+#include <sys/syscall.h>
+#endif
+
+#include <unistd.h>
+#include <errno.h>
+
+#include <pulsecore/atomic.h>
+#include <pulsecore/log.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulse/xmalloc.h>
+
+#ifndef HAVE_PIPE
+#include <pulsecore/pipe.h>
+#endif
+
+#ifdef __linux__
+
+#if !defined(__NR_eventfd) && defined(__i386__)
+#define __NR_eventfd 323
+#endif
+
+#if !defined(__NR_eventfd) && defined(__x86_64__)
+#define __NR_eventfd 284
+#endif
+
+#if !defined(__NR_eventfd) && defined(__arm__)
+#define __NR_eventfd (__NR_SYSCALL_BASE+351)
+#endif
+
+#if !defined(SYS_eventfd) && defined(__NR_eventfd)
+#define SYS_eventfd __NR_eventfd
+#endif
+
+#ifdef SYS_eventfd
+#define HAVE_EVENTFD
+
+static inline long eventfd(unsigned count) {
+ return syscall(SYS_eventfd, count);
+}
+
+#endif
+#endif
+
+#include "fdsem.h"
+
+struct pa_fdsem {
+ int fds[2];
+#ifdef HAVE_EVENTFD
+ int efd;
+#endif
+
+ pa_fdsem_data *data;
+};
+
+pa_fdsem *pa_fdsem_new(void) {
+ pa_fdsem *f;
+
+ f = pa_xmalloc(PA_ALIGN(sizeof(pa_fdsem)) + PA_ALIGN(sizeof(pa_fdsem_data)));
+
+#ifdef HAVE_EVENTFD
+ if ((f->efd = eventfd(0)) >= 0) {
+ pa_make_fd_cloexec(f->efd);
+ f->fds[0] = f->fds[1] = -1;
+ } else
+#endif
+ {
+ if (pipe(f->fds) < 0) {
+ pa_xfree(f);
+ return NULL;
+ }
+
+ pa_make_fd_cloexec(f->fds[0]);
+ pa_make_fd_cloexec(f->fds[1]);
+ }
+
+ f->data = (pa_fdsem_data*) ((uint8_t*) f + PA_ALIGN(sizeof(pa_fdsem)));
+
+ pa_atomic_store(&f->data->waiting, 0);
+ pa_atomic_store(&f->data->signalled, 0);
+ pa_atomic_store(&f->data->in_pipe, 0);
+
+ return f;
+}
+
+pa_fdsem *pa_fdsem_open_shm(pa_fdsem_data *data, int event_fd) {
+ pa_fdsem *f = NULL;
+
+ pa_assert(data);
+ pa_assert(event_fd >= 0);
+
+#ifdef HAVE_EVENTFD
+ f = pa_xnew(pa_fdsem, 1);
+
+ f->efd = event_fd;
+ pa_make_fd_cloexec(f->efd);
+ f->fds[0] = f->fds[1] = -1;
+ f->data = data;
+#endif
+
+ return f;
+}
+
+pa_fdsem *pa_fdsem_new_shm(pa_fdsem_data *data, int* event_fd) {
+ pa_fdsem *f = NULL;
+
+ pa_assert(data);
+ pa_assert(event_fd);
+
+#ifdef HAVE_EVENTFD
+
+ f = pa_xnew(pa_fdsem, 1);
+
+ if ((f->efd = eventfd(0)) < 0) {
+ pa_xfree(f);
+ return NULL;
+ }
+
+ pa_make_fd_cloexec(f->efd);
+ f->fds[0] = f->fds[1] = -1;
+ f->data = data;
+
+ pa_atomic_store(&f->data->waiting, 0);
+ pa_atomic_store(&f->data->signalled, 0);
+ pa_atomic_store(&f->data->in_pipe, 0);
+
+#endif
+
+ return f;
+}
+
+void pa_fdsem_free(pa_fdsem *f) {
+ pa_assert(f);
+
+#ifdef HAVE_EVENTFD
+ if (f->efd >= 0)
+ pa_close(f->efd);
+#endif
+ pa_close_pipe(f->fds);
+
+ pa_xfree(f);
+}
+
+static void flush(pa_fdsem *f) {
+ ssize_t r;
+ pa_assert(f);
+
+ if (pa_atomic_load(&f->data->in_pipe) <= 0)
+ return;
+
+ do {
+ char x[10];
+
+#ifdef HAVE_EVENTFD
+ if (f->efd >= 0) {
+ uint64_t u;
+
+ if ((r = read(f->efd, &u, sizeof(u))) != sizeof(u)) {
+ pa_assert(r < 0 && errno == EINTR);
+ continue;
+ }
+ r = (ssize_t) u;
+ } else
+#endif
+
+ if ((r = read(f->fds[0], &x, sizeof(x))) <= 0) {
+ pa_assert(r < 0 && errno == EINTR);
+ continue;
+ }
+
+ } while (pa_atomic_sub(&f->data->in_pipe, r) > r);
+}
+
+void pa_fdsem_post(pa_fdsem *f) {
+ pa_assert(f);
+
+ if (pa_atomic_cmpxchg(&f->data->signalled, 0, 1)) {
+
+ if (pa_atomic_load(&f->data->waiting)) {
+ ssize_t r;
+ char x = 'x';
+
+ pa_atomic_inc(&f->data->in_pipe);
+
+ for (;;) {
+
+#ifdef HAVE_EVENTFD
+ if (f->efd >= 0) {
+ uint64_t u = 1;
+
+ if ((r = write(f->efd, &u, sizeof(u))) != sizeof(u)) {
+ pa_assert(r < 0 && errno == EINTR);
+ continue;
+ }
+ } else
+#endif
+
+ if ((r = write(f->fds[1], &x, 1)) != 1) {
+ pa_assert(r < 0 && errno == EINTR);
+ continue;
+ }
+
+ break;
+ }
+ }
+ }
+}
+
+void pa_fdsem_wait(pa_fdsem *f) {
+ pa_assert(f);
+
+ flush(f);
+
+ if (pa_atomic_cmpxchg(&f->data->signalled, 1, 0))
+ return;
+
+ pa_atomic_inc(&f->data->waiting);
+
+ while (!pa_atomic_cmpxchg(&f->data->signalled, 1, 0)) {
+ char x[10];
+ ssize_t r;
+
+#ifdef HAVE_EVENTFD
+ if (f->efd >= 0) {
+ uint64_t u;
+
+ if ((r = read(f->efd, &u, sizeof(u))) != sizeof(u)) {
+ pa_assert(r < 0 && errno == EINTR);
+ continue;
+ }
+
+ r = (ssize_t) u;
+ } else
+#endif
+
+ if ((r = read(f->fds[0], &x, sizeof(x))) <= 0) {
+ pa_assert(r < 0 && errno == EINTR);
+ continue;
+ }
+
+ pa_atomic_sub(&f->data->in_pipe, r);
+ }
+
+ pa_assert_se(pa_atomic_dec(&f->data->waiting) >= 1);
+}
+
+int pa_fdsem_try(pa_fdsem *f) {
+ pa_assert(f);
+
+ flush(f);
+
+ if (pa_atomic_cmpxchg(&f->data->signalled, 1, 0))
+ return 1;
+
+ return 0;
+}
+
+int pa_fdsem_get(pa_fdsem *f) {
+ pa_assert(f);
+
+#ifdef HAVE_EVENTFD
+ if (f->efd >= 0)
+ return f->efd;
+#endif
+
+ return f->fds[0];
+}
+
+int pa_fdsem_before_poll(pa_fdsem *f) {
+ pa_assert(f);
+
+ flush(f);
+
+ if (pa_atomic_cmpxchg(&f->data->signalled, 1, 0))
+ return -1;
+
+ pa_atomic_inc(&f->data->waiting);
+
+ if (pa_atomic_cmpxchg(&f->data->signalled, 1, 0)) {
+ pa_assert_se(pa_atomic_dec(&f->data->waiting) >= 1);
+ return -1;
+ }
+ return 0;
+}
+
+int pa_fdsem_after_poll(pa_fdsem *f) {
+ pa_assert(f);
+
+ pa_assert_se(pa_atomic_dec(&f->data->waiting) >= 1);
+
+ flush(f);
+
+ if (pa_atomic_cmpxchg(&f->data->signalled, 1, 0))
+ return 1;
+
+ return 0;
+}
diff --git a/src/pulsecore/fdsem.h b/src/pulsecore/fdsem.h
new file mode 100644
index 00000000..48a77c49
--- /dev/null
+++ b/src/pulsecore/fdsem.h
@@ -0,0 +1,55 @@
+#ifndef foopulsefdsemhfoo
+#define foopulsefdsemhfoo
+
+/***
+ 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.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <sys/types.h>
+#include <pulse/def.h>
+
+/* A simple, asynchronous semaphore which uses fds for sleeping. In
+ * the best case all functions are lock-free unless sleeping is
+ * required. */
+
+typedef struct pa_fdsem pa_fdsem;
+
+typedef struct pa_fdsem_data {
+ pa_atomic_t waiting;
+ pa_atomic_t signalled;
+ pa_atomic_t in_pipe;
+} pa_fdsem_data;
+
+pa_fdsem *pa_fdsem_new(void);
+pa_fdsem *pa_fdsem_open_shm(pa_fdsem_data *data, int event_fd);
+pa_fdsem *pa_fdsem_new_shm(pa_fdsem_data *data, int* event_fd);
+void pa_fdsem_free(pa_fdsem *f);
+
+void pa_fdsem_post(pa_fdsem *f);
+void pa_fdsem_wait(pa_fdsem *f);
+int pa_fdsem_try(pa_fdsem *f);
+
+int pa_fdsem_get(pa_fdsem *f);
+
+int pa_fdsem_before_poll(pa_fdsem *f);
+int pa_fdsem_after_poll(pa_fdsem *f);
+
+
+#endif
diff --git a/src/pulsecore/ffmpeg/Makefile b/src/pulsecore/ffmpeg/Makefile
new file mode 100644
index 00000000..316beb72
--- /dev/null
+++ b/src/pulsecore/ffmpeg/Makefile
@@ -0,0 +1,13 @@
+# This is a dirty trick just to ease compilation with emacs
+#
+# This file is not intended to be distributed or anything
+#
+# So: don't touch it, even better ignore it!
+
+all:
+ $(MAKE) -C ../..
+
+clean:
+ $(MAKE) -C ../.. clean
+
+.PHONY: all clean
diff --git a/src/pulsecore/ffmpeg/avcodec.h b/src/pulsecore/ffmpeg/avcodec.h
new file mode 100644
index 00000000..696fc986
--- /dev/null
+++ b/src/pulsecore/ffmpeg/avcodec.h
@@ -0,0 +1,82 @@
+/*
+ * copyright (c) 2001 Fabrice Bellard
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVCODEC_H
+#define AVCODEC_H
+
+/* Just a heavily bastardized version of the original file from
+ * ffmpeg, just enough to get resample2.c to compile without
+ * modification -- Lennart */
+
+#if !defined(PACKAGE) && defined(HAVE_CONFIG_H)
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <inttypes.h>
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#define av_mallocz(l) calloc(1, (l))
+#define av_malloc(l) malloc(l)
+#define av_realloc(p,l) realloc((p),(l))
+#define av_free(p) free(p)
+
+static inline void av_freep(void *k) {
+ void **p = k;
+
+ if (p) {
+ free(*p);
+ *p = NULL;
+ }
+}
+
+static inline int av_clip(int a, int amin, int amax)
+{
+ if (a < amin) return amin;
+ else if (a > amax) return amax;
+ else return a;
+}
+
+#define av_log(a,b,c)
+
+#define FFABS(a) ((a) >= 0 ? (a) : (-(a)))
+#define FFSIGN(a) ((a) > 0 ? 1 : -1)
+
+#define FFMAX(a,b) ((a) > (b) ? (a) : (b))
+#define FFMIN(a,b) ((a) > (b) ? (b) : (a))
+
+struct AVResampleContext;
+struct AVResampleContext *av_resample_init(int out_rate, int in_rate, int filter_length, int log2_phase_count, int linear, double cutoff);
+int av_resample(struct AVResampleContext *c, short *dst, short *src, int *consumed, int src_size, int dst_size, int update_ctx);
+void av_resample_compensate(struct AVResampleContext *c, int sample_delta, int compensation_distance);
+void av_resample_close(struct AVResampleContext *c);
+void av_build_filter(int16_t *filter, double factor, int tap_count, int phase_count, int scale, int type);
+
+/*
+ * crude lrintf for non-C99 systems.
+ */
+#ifndef HAVE_LRINTF
+#define lrintf(x) ((long int)(x))
+#endif
+
+#endif /* AVCODEC_H */
diff --git a/src/pulsecore/ffmpeg/dsputil.h b/src/pulsecore/ffmpeg/dsputil.h
new file mode 100644
index 00000000..8da742d0
--- /dev/null
+++ b/src/pulsecore/ffmpeg/dsputil.h
@@ -0,0 +1 @@
+/* empty file, just here to allow us to compile an unmodified resampler2.c */
diff --git a/src/pulsecore/ffmpeg/resample2.c b/src/pulsecore/ffmpeg/resample2.c
new file mode 100644
index 00000000..da1443d9
--- /dev/null
+++ b/src/pulsecore/ffmpeg/resample2.c
@@ -0,0 +1,324 @@
+/*
+ * audio resampling
+ * Copyright (c) 2004 Michael Niedermayer <michaelni@gmx.at>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file resample2.c
+ * audio resampling
+ * @author Michael Niedermayer <michaelni@gmx.at>
+ */
+
+#include "avcodec.h"
+#include "dsputil.h"
+
+#ifndef CONFIG_RESAMPLE_HP
+#define FILTER_SHIFT 15
+
+#define FELEM int16_t
+#define FELEM2 int32_t
+#define FELEML int64_t
+#define FELEM_MAX INT16_MAX
+#define FELEM_MIN INT16_MIN
+#define WINDOW_TYPE 9
+#elif !defined(CONFIG_RESAMPLE_AUDIOPHILE_KIDDY_MODE)
+#define FILTER_SHIFT 30
+
+#define FELEM int32_t
+#define FELEM2 int64_t
+#define FELEML int64_t
+#define FELEM_MAX INT32_MAX
+#define FELEM_MIN INT32_MIN
+#define WINDOW_TYPE 12
+#else
+#define FILTER_SHIFT 0
+
+#define FELEM double
+#define FELEM2 double
+#define FELEML double
+#define WINDOW_TYPE 24
+#endif
+
+
+typedef struct AVResampleContext{
+ FELEM *filter_bank;
+ int filter_length;
+ int ideal_dst_incr;
+ int dst_incr;
+ int index;
+ int frac;
+ int src_incr;
+ int compensation_distance;
+ int phase_shift;
+ int phase_mask;
+ int linear;
+}AVResampleContext;
+
+/**
+ * 0th order modified bessel function of the first kind.
+ */
+static double bessel(double x){
+ double v=1;
+ double t=1;
+ int i;
+
+ x= x*x/4;
+ for(i=1; i<50; i++){
+ t *= x/(i*i);
+ v += t;
+ }
+ return v;
+}
+
+/**
+ * builds a polyphase filterbank.
+ * @param factor resampling factor
+ * @param scale wanted sum of coefficients for each filter
+ * @param type 0->cubic, 1->blackman nuttall windowed sinc, 2..16->kaiser windowed sinc beta=2..16
+ */
+void av_build_filter(FELEM *filter, double factor, int tap_count, int phase_count, int scale, int type){
+ int ph, i;
+ double x, y, w, tab[tap_count];
+ const int center= (tap_count-1)/2;
+
+ /* if upsampling, only need to interpolate, no filter */
+ if (factor > 1.0)
+ factor = 1.0;
+
+ for(ph=0;ph<phase_count;ph++) {
+ double norm = 0;
+ for(i=0;i<tap_count;i++) {
+ x = M_PI * ((double)(i - center) - (double)ph / phase_count) * factor;
+ if (x == 0) y = 1.0;
+ else y = sin(x) / x;
+ switch(type){
+ case 0:{
+ const float d= -0.5; //first order derivative = -0.5
+ x = fabs(((double)(i - center) - (double)ph / phase_count) * factor);
+ if(x<1.0) y= 1 - 3*x*x + 2*x*x*x + d*( -x*x + x*x*x);
+ else y= d*(-4 + 8*x - 5*x*x + x*x*x);
+ break;}
+ case 1:
+ w = 2.0*x / (factor*tap_count) + M_PI;
+ y *= 0.3635819 - 0.4891775 * cos(w) + 0.1365995 * cos(2*w) - 0.0106411 * cos(3*w);
+ break;
+ default:
+ w = 2.0*x / (factor*tap_count*M_PI);
+ y *= bessel(type*sqrt(FFMAX(1-w*w, 0)));
+ break;
+ }
+
+ tab[i] = y;
+ norm += y;
+ }
+
+ /* normalize so that an uniform color remains the same */
+ for(i=0;i<tap_count;i++) {
+#ifdef CONFIG_RESAMPLE_AUDIOPHILE_KIDDY_MODE
+ filter[ph * tap_count + i] = tab[i] / norm;
+#else
+ filter[ph * tap_count + i] = av_clip(lrintf(tab[i] * scale / norm), FELEM_MIN, FELEM_MAX);
+#endif
+ }
+ }
+#if 0
+ {
+#define LEN 1024
+ int j,k;
+ double sine[LEN + tap_count];
+ double filtered[LEN];
+ double maxff=-2, minff=2, maxsf=-2, minsf=2;
+ for(i=0; i<LEN; i++){
+ double ss=0, sf=0, ff=0;
+ for(j=0; j<LEN+tap_count; j++)
+ sine[j]= cos(i*j*M_PI/LEN);
+ for(j=0; j<LEN; j++){
+ double sum=0;
+ ph=0;
+ for(k=0; k<tap_count; k++)
+ sum += filter[ph * tap_count + k] * sine[k+j];
+ filtered[j]= sum / (1<<FILTER_SHIFT);
+ ss+= sine[j + center] * sine[j + center];
+ ff+= filtered[j] * filtered[j];
+ sf+= sine[j + center] * filtered[j];
+ }
+ ss= sqrt(2*ss/LEN);
+ ff= sqrt(2*ff/LEN);
+ sf= 2*sf/LEN;
+ maxff= FFMAX(maxff, ff);
+ minff= FFMIN(minff, ff);
+ maxsf= FFMAX(maxsf, sf);
+ minsf= FFMIN(minsf, sf);
+ if(i%11==0){
+ av_log(NULL, AV_LOG_ERROR, "i:%4d ss:%f ff:%13.6e-%13.6e sf:%13.6e-%13.6e\n", i, ss, maxff, minff, maxsf, minsf);
+ minff=minsf= 2;
+ maxff=maxsf= -2;
+ }
+ }
+ }
+#endif
+}
+
+/**
+ * Initializes an audio resampler.
+ * Note, if either rate is not an integer then simply scale both rates up so they are.
+ */
+AVResampleContext *av_resample_init(int out_rate, int in_rate, int filter_size, int phase_shift, int linear, double cutoff){
+ AVResampleContext *c= av_mallocz(sizeof(AVResampleContext));
+ double factor= FFMIN(out_rate * cutoff / in_rate, 1.0);
+ int phase_count= 1<<phase_shift;
+
+ c->phase_shift= phase_shift;
+ c->phase_mask= phase_count-1;
+ c->linear= linear;
+
+ c->filter_length= FFMAX((int)ceil(filter_size/factor), 1);
+ c->filter_bank= av_mallocz(c->filter_length*(phase_count+1)*sizeof(FELEM));
+ av_build_filter(c->filter_bank, factor, c->filter_length, phase_count, 1<<FILTER_SHIFT, WINDOW_TYPE);
+ memcpy(&c->filter_bank[c->filter_length*phase_count+1], c->filter_bank, (c->filter_length-1)*sizeof(FELEM));
+ c->filter_bank[c->filter_length*phase_count]= c->filter_bank[c->filter_length - 1];
+
+ c->src_incr= out_rate;
+ c->ideal_dst_incr= c->dst_incr= in_rate * phase_count;
+ c->index= -phase_count*((c->filter_length-1)/2);
+
+ return c;
+}
+
+void av_resample_close(AVResampleContext *c){
+ av_freep(&c->filter_bank);
+ av_freep(&c);
+}
+
+/**
+ * Compensates samplerate/timestamp drift. The compensation is done by changing
+ * the resampler parameters, so no audible clicks or similar distortions ocur
+ * @param compensation_distance distance in output samples over which the compensation should be performed
+ * @param sample_delta number of output samples which should be output less
+ *
+ * example: av_resample_compensate(c, 10, 500)
+ * here instead of 510 samples only 500 samples would be output
+ *
+ * note, due to rounding the actual compensation might be slightly different,
+ * especially if the compensation_distance is large and the in_rate used during init is small
+ */
+void av_resample_compensate(AVResampleContext *c, int sample_delta, int compensation_distance){
+// sample_delta += (c->ideal_dst_incr - c->dst_incr)*(int64_t)c->compensation_distance / c->ideal_dst_incr;
+ c->compensation_distance= compensation_distance;
+ c->dst_incr = c->ideal_dst_incr - c->ideal_dst_incr * (int64_t)sample_delta / compensation_distance;
+}
+
+/**
+ * resamples.
+ * @param src an array of unconsumed samples
+ * @param consumed the number of samples of src which have been consumed are returned here
+ * @param src_size the number of unconsumed samples available
+ * @param dst_size the amount of space in samples available in dst
+ * @param update_ctx if this is 0 then the context wont be modified, that way several channels can be resampled with the same context
+ * @return the number of samples written in dst or -1 if an error occured
+ */
+int av_resample(AVResampleContext *c, short *dst, short *src, int *consumed, int src_size, int dst_size, int update_ctx){
+ int dst_index, i;
+ int index= c->index;
+ int frac= c->frac;
+ int dst_incr_frac= c->dst_incr % c->src_incr;
+ int dst_incr= c->dst_incr / c->src_incr;
+ int compensation_distance= c->compensation_distance;
+
+ if(compensation_distance == 0 && c->filter_length == 1 && c->phase_shift==0){
+ int64_t index2= ((int64_t)index)<<32;
+ int64_t incr= (1LL<<32) * c->dst_incr / c->src_incr;
+ dst_size= FFMIN(dst_size, (src_size-1-index) * (int64_t)c->src_incr / c->dst_incr);
+
+ for(dst_index=0; dst_index < dst_size; dst_index++){
+ dst[dst_index] = src[index2>>32];
+ index2 += incr;
+ }
+ frac += dst_index * dst_incr_frac;
+ index += dst_index * dst_incr;
+ index += frac / c->src_incr;
+ frac %= c->src_incr;
+ }else{
+ for(dst_index=0; dst_index < dst_size; dst_index++){
+ FELEM *filter= c->filter_bank + c->filter_length*(index & c->phase_mask);
+ int sample_index= index >> c->phase_shift;
+ FELEM2 val=0;
+
+ if(sample_index < 0){
+ for(i=0; i<c->filter_length; i++)
+ val += src[FFABS(sample_index + i) % src_size] * filter[i];
+ }else if(sample_index + c->filter_length > src_size){
+ break;
+ }else if(c->linear){
+ FELEM2 v2=0;
+ for(i=0; i<c->filter_length; i++){
+ val += src[sample_index + i] * (FELEM2)filter[i];
+ v2 += src[sample_index + i] * (FELEM2)filter[i + c->filter_length];
+ }
+ val+=(v2-val)*(FELEML)frac / c->src_incr;
+ }else{
+ for(i=0; i<c->filter_length; i++){
+ val += src[sample_index + i] * (FELEM2)filter[i];
+ }
+ }
+
+#ifdef CONFIG_RESAMPLE_AUDIOPHILE_KIDDY_MODE
+ dst[dst_index] = av_clip_int16(lrintf(val));
+#else
+ val = (val + (1<<(FILTER_SHIFT-1)))>>FILTER_SHIFT;
+ dst[dst_index] = (unsigned)(val + 32768) > 65535 ? (val>>31) ^ 32767 : val;
+#endif
+
+ frac += dst_incr_frac;
+ index += dst_incr;
+ if(frac >= c->src_incr){
+ frac -= c->src_incr;
+ index++;
+ }
+
+ if(dst_index + 1 == compensation_distance){
+ compensation_distance= 0;
+ dst_incr_frac= c->ideal_dst_incr % c->src_incr;
+ dst_incr= c->ideal_dst_incr / c->src_incr;
+ }
+ }
+ }
+ *consumed= FFMAX(index, 0) >> c->phase_shift;
+ if(index>=0) index &= c->phase_mask;
+
+ if(compensation_distance){
+ compensation_distance -= dst_index;
+ assert(compensation_distance > 0);
+ }
+ if(update_ctx){
+ c->frac= frac;
+ c->index= index;
+ c->dst_incr= dst_incr_frac + c->src_incr*dst_incr;
+ c->compensation_distance= compensation_distance;
+ }
+#if 0
+ if(update_ctx && !c->compensation_distance){
+#undef rand
+ av_resample_compensate(c, rand() % (8000*2) - 8000, 8000*2);
+av_log(NULL, AV_LOG_DEBUG, "%d %d %d\n", c->dst_incr, c->ideal_dst_incr, c->compensation_distance);
+ }
+#endif
+
+ return dst_index;
+}
diff --git a/src/pulsecore/flist.c b/src/pulsecore/flist.c
index cfeeac22..f166ee33 100644
--- a/src/pulsecore/flist.c
+++ b/src/pulsecore/flist.c
@@ -1,18 +1,18 @@
-/* $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.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
@@ -23,12 +23,14 @@
#include <config.h>
#endif
-#include <assert.h>
+#include <pulse/xmalloc.h>
#include <pulsecore/atomic.h>
#include <pulsecore/log.h>
#include <pulsecore/thread.h>
-#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
#include "flist.h"
@@ -88,21 +90,18 @@ enum {
};
struct cell {
- pa_atomic_int_t state;
+ pa_atomic_t state;
void *data;
};
struct pa_flist {
- struct cell *cells;
unsigned size;
- pa_atomic_int_t length;
- pa_atomic_int_t read_idx;
- pa_atomic_int_t write_idx;
+ pa_atomic_t length;
+ pa_atomic_t read_idx;
+ pa_atomic_t write_idx;
};
-static int is_power_of_two(unsigned size) {
- return !(size & (size - 1));
-}
+#define PA_FLIST_CELLS(x) ((struct cell*) ((uint8_t*) (x) + PA_ALIGN(sizeof(struct pa_flist))))
pa_flist *pa_flist_new(unsigned size) {
pa_flist *l;
@@ -110,13 +109,12 @@ pa_flist *pa_flist_new(unsigned size) {
if (!size)
size = FLIST_SIZE;
- assert(is_power_of_two(size));
-
- l = pa_xnew(pa_flist, 1);
+ pa_assert(pa_is_power_of_two(size));
+
+ l = pa_xmalloc0(PA_ALIGN(sizeof(pa_flist)) + (sizeof(struct cell) * size));
l->size = size;
- l->cells = pa_xnew0(struct cell, size);
-
+
pa_atomic_store(&l->read_idx, 0);
pa_atomic_store(&l->write_idx, 0);
pa_atomic_store(&l->length, 0);
@@ -129,32 +127,37 @@ static int reduce(pa_flist *l, int value) {
}
void pa_flist_free(pa_flist *l, pa_free_cb_t free_cb) {
- assert(l);
+ pa_assert(l);
if (free_cb) {
+ struct cell *cells;
int len, idx;
-
+
+ cells = PA_FLIST_CELLS(l);
+
idx = reduce(l, pa_atomic_load(&l->read_idx));
len = pa_atomic_load(&l->length);
-
+
for (; len > 0; len--) {
- if (pa_atomic_load(&l->cells[idx].state) == STATE_USED)
- free_cb(l->cells[idx].data);
+ if (pa_atomic_load(&cells[idx].state) == STATE_USED)
+ free_cb(cells[idx].data);
idx = reduce(l, idx + 1);
}
}
- pa_xfree(l->cells);
pa_xfree(l);
}
int pa_flist_push(pa_flist*l, void *p) {
int idx, len, n;
-
- assert(l);
- assert(p);
+ struct cell *cells;
+
+ pa_assert(l);
+ pa_assert(p);
+
+ cells = PA_FLIST_CELLS(l);
n = len = (int) l->size - pa_atomic_load(&l->length) + N_EXTRA_SCAN;
_Y;
@@ -163,13 +166,13 @@ int pa_flist_push(pa_flist*l, void *p) {
for (; n > 0 ; n--) {
_Y;
- if (pa_atomic_cmpxchg(&l->cells[idx].state, STATE_UNUSED, STATE_BUSY)) {
+ if (pa_atomic_cmpxchg(&cells[idx].state, STATE_UNUSED, STATE_BUSY)) {
_Y;
pa_atomic_inc(&l->write_idx);
_Y;
- l->cells[idx].data = p;
+ cells[idx].data = p;
_Y;
- pa_atomic_store(&l->cells[idx].state, STATE_USED);
+ pa_atomic_store(&cells[idx].state, STATE_USED);
_Y;
pa_atomic_inc(&l->length);
return 0;
@@ -181,16 +184,19 @@ int pa_flist_push(pa_flist*l, void *p) {
#ifdef PROFILE
if (len > N_EXTRA_SCAN)
- pa_log("WARNING: Didn't find free cell after %u iterations.", len);
+ pa_log_warn("Didn't find free cell after %u iterations.", len);
#endif
-
+
return -1;
}
void* pa_flist_pop(pa_flist*l) {
int idx, len, n;
-
- assert(l);
+ struct cell *cells;
+
+ pa_assert(l);
+
+ cells = PA_FLIST_CELLS(l);
n = len = pa_atomic_load(&l->length) + N_EXTRA_SCAN;
_Y;
@@ -199,14 +205,14 @@ void* pa_flist_pop(pa_flist*l) {
for (; n > 0 ; n--) {
_Y;
- if (pa_atomic_cmpxchg(&l->cells[idx].state, STATE_USED, STATE_BUSY)) {
+ if (pa_atomic_cmpxchg(&cells[idx].state, STATE_USED, STATE_BUSY)) {
void *p;
_Y;
pa_atomic_inc(&l->read_idx);
_Y;
- p = l->cells[idx].data;
+ p = cells[idx].data;
_Y;
- pa_atomic_store(&l->cells[idx].state, STATE_UNUSED);
+ pa_atomic_store(&cells[idx].state, STATE_UNUSED);
_Y;
pa_atomic_dec(&l->length);
@@ -219,8 +225,8 @@ void* pa_flist_pop(pa_flist*l) {
#ifdef PROFILE
if (len > N_EXTRA_SCAN)
- pa_log("WARNING: Didn't find used cell after %u iterations.", len);
+ pa_log_warn("Didn't find used cell after %u iterations.", len);
#endif
-
+
return NULL;
}
diff --git a/src/pulsecore/flist.h b/src/pulsecore/flist.h
index 57c9598b..c040667d 100644
--- a/src/pulsecore/flist.h
+++ b/src/pulsecore/flist.h
@@ -1,21 +1,21 @@
#ifndef foopulseflisthfoo
#define foopulseflisthfoo
-/* $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
@@ -23,6 +23,9 @@
***/
#include <pulse/def.h>
+#include <pulse/gccmacro.h>
+
+#include <pulsecore/once.h>
/* A multiple-reader multipler-write lock-free free list implementation */
@@ -36,4 +39,28 @@ void pa_flist_free(pa_flist *l, pa_free_cb_t free_cb);
int pa_flist_push(pa_flist*l, void *p);
void* pa_flist_pop(pa_flist*l);
+/* Please not that the destructor stuff is not really necesary, we do
+ * this just to make valgrind output more useful. */
+
+#define PA_STATIC_FLIST_DECLARE(name, size, free_cb) \
+ static struct { \
+ pa_flist *flist; \
+ pa_once once; \
+ } name##_flist = { NULL, PA_ONCE_INIT }; \
+ static void name##_flist_init(void) { \
+ name##_flist.flist = pa_flist_new(size); \
+ } \
+ static inline pa_flist* name##_flist_get(void) { \
+ pa_run_once(&name##_flist.once, name##_flist_init); \
+ return name##_flist.flist; \
+ } \
+ static void name##_flist_destructor(void) PA_GCC_DESTRUCTOR; \
+ static void name##_flist_destructor(void) { \
+ if (name##_flist.flist) \
+ pa_flist_free(name##_flist.flist, (free_cb)); \
+ } \
+ struct __stupid_useless_struct_to_allow_trailing_semicolon
+
+#define PA_STATIC_FLIST_GET(name) (name##_flist_get())
+
#endif
diff --git a/src/pulsecore/g711.c b/src/pulsecore/g711.c
index 55a82396..aa2d703a 100644
--- a/src/pulsecore/g711.c
+++ b/src/pulsecore/g711.c
@@ -1,2531 +1,2531 @@
-/*
- * This source code is a product of Sun Microsystems, Inc. and is provided
- * for unrestricted use. Users may copy or modify this source code without
- * charge.
- *
- * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
- * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
- *
- * Sun source code is provided with no support and without any obligation on
- * the part of Sun Microsystems, Inc. to assist in its use, correction,
- * modification or enhancement.
- *
- * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
- * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE
- * OR ANY PART THEREOF.
- *
- * In no event will Sun Microsystems, Inc. be liable for any lost revenue
- * or profits or other special, indirect and consequential damages, even if
- * Sun has been advised of the possibility of such damages.
- *
- * Sun Microsystems, Inc.
- * 2550 Garcia Avenue
- * Mountain View, California 94043
- */
-
-/*
- * g711.c
- *
- * u-law, A-law and linear PCM conversions.
- */
-
-/*
- * December 30, 1994:
- * Functions linear2alaw, linear2ulaw have been updated to correctly
- * convert unquantized 16 bit values.
- * Tables for direct u- to A-law and A- to u-law conversions have been
- * corrected.
- * Borge Lindberg, Center for PersonKommunikation, Aalborg University.
- * bli@cpk.auc.dk
- *
- */
-
-#include "g711.h"
-
-#define SIGN_BIT (0x80) /* Sign bit for a A-law byte. */
-#define QUANT_MASK (0xf) /* Quantization field mask. */
-#define NSEGS (8) /* Number of A-law segments. */
-#define SEG_SHIFT (4) /* Left shift for segment number. */
-#define SEG_MASK (0x70) /* Segment field mask. */
-
-#if !defined(FAST_ALAW_CONVERSION) || !defined(FAST_ULAW_CONVERSION)
-static int16_t seg_aend[8] = {0x1F, 0x3F, 0x7F, 0xFF,
- 0x1FF, 0x3FF, 0x7FF, 0xFFF};
-static int16_t seg_uend[8] = {0x3F, 0x7F, 0xFF, 0x1FF,
- 0x3FF, 0x7FF, 0xFFF, 0x1FFF};
-
-static int16_t search(
- int16_t val,
- int16_t *table,
- int size)
-{
- int i;
-
- for (i = 0; i < size; i++) {
- if (val <= *table++)
- return (i);
- }
- return (size);
-}
-#endif /* !FAST_*_CONVERSION */
-
-#ifndef FAST_ALAW_CONVERSION
-/*
- * linear2alaw() accepts an 13-bit signed integer and encodes it as A-law data
- * stored in a unsigned char. This function should only be called with
- * the data shifted such that it only contains information in the lower
- * 13-bits.
- *
- * Linear Input Code Compressed Code
- * ------------------------ ---------------
- * 0000000wxyza 000wxyz
- * 0000001wxyza 001wxyz
- * 000001wxyzab 010wxyz
- * 00001wxyzabc 011wxyz
- * 0001wxyzabcd 100wxyz
- * 001wxyzabcde 101wxyz
- * 01wxyzabcdef 110wxyz
- * 1wxyzabcdefg 111wxyz
- *
- * For further information see John C. Bellamy's Digital Telephony, 1982,
- * John Wiley & Sons, pps 98-111 and 472-476.
- */
-unsigned char st_13linear2alaw(
- int16_t pcm_val) /* 2's complement (13-bit range) */
-{
- int16_t mask;
- short seg;
- unsigned char aval;
-
- /* Have calling software do it since its already doing a shift
- * from 32-bits down to 16-bits.
- */
- /* pcm_val = pcm_val >> 3; */
-
- /* A-law using even bit inversion */
- if (pcm_val >= 0) {
- mask = 0xD5; /* sign (7th) bit = 1 */
- } else {
- mask = 0x55; /* sign bit = 0 */
- pcm_val = -pcm_val - 1;
- }
-
- /* Convert the scaled magnitude to segment number. */
- seg = search(pcm_val, seg_aend, 8);
-
- /* Combine the sign, segment, and quantization bits. */
-
- if (seg >= 8) /* out of range, return maximum value. */
- return (unsigned char) (0x7F ^ mask);
- else {
- aval = (unsigned char) seg << SEG_SHIFT;
- if (seg < 2)
- aval |= (pcm_val >> 1) & QUANT_MASK;
- else
- aval |= (pcm_val >> seg) & QUANT_MASK;
- return (aval ^ mask);
- }
-}
-
-/*
- * alaw2linear() - Convert an A-law value to 16-bit signed linear PCM
- *
- */
-int16_t st_alaw2linear16(
- unsigned char a_val)
-{
- int16_t t;
- int16_t seg;
-
- a_val ^= 0x55;
-
- t = (a_val & QUANT_MASK) << 4;
- seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT;
- switch (seg) {
- case 0:
- t += 8;
- break;
- case 1:
- t += 0x108;
- break;
- default:
- t += 0x108;
- t <<= seg - 1;
- }
- return ((a_val & SIGN_BIT) ? t : -t);
-}
-#endif /* !FAST_ALAW_CONVERSION */
-
-#define BIAS (0x84) /* Bias for linear code. */
-#define CLIP 8159
-
-#ifndef FAST_ULAW_CONVERSION
-/*
- * linear2ulaw() accepts a 14-bit signed integer and encodes it as u-law data
- * stored in a unsigned char. This function should only be called with
- * the data shifted such that it only contains information in the lower
- * 14-bits.
- *
- * In order to simplify the encoding process, the original linear magnitude
- * is biased by adding 33 which shifts the encoding range from (0 - 8158) to
- * (33 - 8191). The result can be seen in the following encoding table:
- *
- * Biased Linear Input Code Compressed Code
- * ------------------------ ---------------
- * 00000001wxyza 000wxyz
- * 0000001wxyzab 001wxyz
- * 000001wxyzabc 010wxyz
- * 00001wxyzabcd 011wxyz
- * 0001wxyzabcde 100wxyz
- * 001wxyzabcdef 101wxyz
- * 01wxyzabcdefg 110wxyz
- * 1wxyzabcdefgh 111wxyz
- *
- * Each biased linear code has a leading 1 which identifies the segment
- * number. The value of the segment number is equal to 7 minus the number
- * of leading 0's. The quantization interval is directly available as the
- * four bits wxyz. * The trailing bits (a - h) are ignored.
- *
- * Ordinarily the complement of the resulting code word is used for
- * transmission, and so the code word is complemented before it is returned.
- *
- * For further information see John C. Bellamy's Digital Telephony, 1982,
- * John Wiley & Sons, pps 98-111 and 472-476.
- */
-unsigned char st_14linear2ulaw(
- int16_t pcm_val) /* 2's complement (14-bit range) */
-{
- int16_t mask;
- int16_t seg;
- unsigned char uval;
-
- /* Have calling software do it since its already doing a shift
- * from 32-bits down to 16-bits.
- */
- /* pcm_val = pcm_val >> 2; */
-
- /* u-law inverts all bits */
- /* Get the sign and the magnitude of the value. */
- if (pcm_val < 0) {
- pcm_val = -pcm_val;
- mask = 0x7F;
- } else {
- mask = 0xFF;
- }
- if ( pcm_val > CLIP ) pcm_val = CLIP; /* clip the magnitude */
- pcm_val += (BIAS >> 2);
-
- /* Convert the scaled magnitude to segment number. */
- seg = search(pcm_val, seg_uend, 8);
-
- /*
- * Combine the sign, segment, quantization bits;
- * and complement the code word.
- */
- if (seg >= 8) /* out of range, return maximum value. */
- return (unsigned char) (0x7F ^ mask);
- else {
- uval = (unsigned char) (seg << 4) | ((pcm_val >> (seg + 1)) & 0xF);
- return (uval ^ mask);
- }
-
-}
-
-/*
- * ulaw2linear() - Convert a u-law value to 16-bit linear PCM
- *
- * First, a biased linear code is derived from the code word. An unbiased
- * output can then be obtained by subtracting 33 from the biased code.
- *
- * Note that this function expects to be passed the complement of the
- * original code word. This is in keeping with ISDN conventions.
- */
-int16_t st_ulaw2linear16(
- unsigned char u_val)
-{
- int16_t t;
-
- /* Complement to obtain normal u-law value. */
- u_val = ~u_val;
-
- /*
- * Extract and bias the quantization bits. Then
- * shift up by the segment number and subtract out the bias.
- */
- t = ((u_val & QUANT_MASK) << 3) + BIAS;
- t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT;
-
- return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS));
-}
-#endif /* !FAST_ULAW_CONVERSION */
-
-#ifdef FAST_ALAW_CONVERSION
-
-int16_t _st_alaw2linear16[256] = {
- -5504, -5248, -6016, -5760, -4480, -4224, -4992,
- -4736, -7552, -7296, -8064, -7808, -6528, -6272,
- -7040, -6784, -2752, -2624, -3008, -2880, -2240,
- -2112, -2496, -2368, -3776, -3648, -4032, -3904,
- -3264, -3136, -3520, -3392, -22016, -20992, -24064,
- -23040, -17920, -16896, -19968, -18944, -30208, -29184,
- -32256, -31232, -26112, -25088, -28160, -27136, -11008,
- -10496, -12032, -11520, -8960, -8448, -9984, -9472,
- -15104, -14592, -16128, -15616, -13056, -12544, -14080,
- -13568, -344, -328, -376, -360, -280, -264,
- -312, -296, -472, -456, -504, -488, -408,
- -392, -440, -424, -88, -72, -120, -104,
- -24, -8, -56, -40, -216, -200, -248,
- -232, -152, -136, -184, -168, -1376, -1312,
- -1504, -1440, -1120, -1056, -1248, -1184, -1888,
- -1824, -2016, -1952, -1632, -1568, -1760, -1696,
- -688, -656, -752, -720, -560, -528, -624,
- -592, -944, -912, -1008, -976, -816, -784,
- -880, -848, 5504, 5248, 6016, 5760, 4480,
- 4224, 4992, 4736, 7552, 7296, 8064, 7808,
- 6528, 6272, 7040, 6784, 2752, 2624, 3008,
- 2880, 2240, 2112, 2496, 2368, 3776, 3648,
- 4032, 3904, 3264, 3136, 3520, 3392, 22016,
- 20992, 24064, 23040, 17920, 16896, 19968, 18944,
- 30208, 29184, 32256, 31232, 26112, 25088, 28160,
- 27136, 11008, 10496, 12032, 11520, 8960, 8448,
- 9984, 9472, 15104, 14592, 16128, 15616, 13056,
- 12544, 14080, 13568, 344, 328, 376, 360,
- 280, 264, 312, 296, 472, 456, 504,
- 488, 408, 392, 440, 424, 88, 72,
- 120, 104, 24, 8, 56, 40, 216,
- 200, 248, 232, 152, 136, 184, 168,
- 1376, 1312, 1504, 1440, 1120, 1056, 1248,
- 1184, 1888, 1824, 2016, 1952, 1632, 1568,
- 1760, 1696, 688, 656, 752, 720, 560,
- 528, 624, 592, 944, 912, 1008, 976,
- 816, 784, 880, 848
-};
-
-uint8_t _st_13linear2alaw[0x2000] = {
- 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
- 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
- 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
- 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
- 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
- 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
- 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
- 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
- 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
- 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
- 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2b, 0x2b, 0x2b, 0x2b,
- 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
- 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
- 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
- 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
- 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
- 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
- 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
- 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
- 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
- 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
- 0x2b, 0x2b, 0x2b, 0x2b, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
- 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
- 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
- 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
- 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
- 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
- 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
- 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
- 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
- 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
- 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
- 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
- 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
- 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
- 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
- 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
- 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
- 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
- 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
- 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
- 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
- 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x2e, 0x2e, 0x2e, 0x2e,
- 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
- 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
- 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
- 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
- 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
- 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
- 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
- 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
- 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
- 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
- 0x2e, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
- 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
- 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
- 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
- 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
- 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
- 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
- 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
- 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
- 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
- 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
- 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
- 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
- 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
- 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
- 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
- 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
- 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
- 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
- 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
- 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
- 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2d, 0x2d, 0x2d, 0x2d,
- 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
- 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
- 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
- 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
- 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
- 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
- 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
- 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
- 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
- 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
- 0x2d, 0x2d, 0x2d, 0x2d, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
- 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
- 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
- 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
- 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
- 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
- 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
- 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
- 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
- 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
- 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
- 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
- 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
- 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
- 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
- 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
- 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
- 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
- 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
- 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
- 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
- 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
- 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
- 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
- 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
- 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
- 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
- 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
- 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
- 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
- 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
- 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
- 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
- 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
- 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
- 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
- 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
- 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
- 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
- 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
- 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
- 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
- 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x27, 0x27, 0x27, 0x27,
- 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
- 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
- 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
- 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
- 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
- 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
- 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
- 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
- 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
- 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
- 0x27, 0x27, 0x27, 0x27, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
- 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
- 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
- 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
- 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
- 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
- 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
- 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
- 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
- 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
- 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
- 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
- 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
- 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
- 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
- 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
- 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
- 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
- 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
- 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
- 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
- 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x3a, 0x3a, 0x3a, 0x3a,
- 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
- 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
- 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
- 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
- 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
- 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
- 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
- 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
- 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
- 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
- 0x3b, 0x3b, 0x3b, 0x3b, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
- 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
- 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
- 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
- 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
- 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x39, 0x39, 0x39, 0x39,
- 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
- 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
- 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
- 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
- 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
- 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
- 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
- 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
- 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
- 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
- 0x3e, 0x3e, 0x3e, 0x3e, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
- 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
- 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
- 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
- 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
- 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3c, 0x3c, 0x3c, 0x3c,
- 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
- 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
- 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
- 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
- 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
- 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
- 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
- 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
- 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
- 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
- 0x3d, 0x3d, 0x3d, 0x3d, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
- 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
- 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
- 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
- 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
- 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x33, 0x33, 0x33, 0x33,
- 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
- 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
- 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
- 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
- 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
- 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
- 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
- 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
- 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
- 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
- 0x30, 0x30, 0x30, 0x30, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
- 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
- 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
- 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
- 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
- 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x36, 0x36, 0x36, 0x36,
- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
- 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,
- 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,
- 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,
- 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,
- 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,
- 0x37, 0x37, 0x37, 0x37, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
- 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
- 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
- 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
- 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
- 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x35, 0x35, 0x35, 0x35,
- 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
- 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
- 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
- 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
- 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
- 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
- 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
- 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0b,
- 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
- 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
- 0x0b, 0x0b, 0x0b, 0x0b, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
- 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
- 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
- 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
- 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
- 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x0e, 0x0e, 0x0e, 0x0e,
- 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
- 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
- 0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
- 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
- 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
- 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
- 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
- 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0d, 0x0d, 0x0d, 0x0d,
- 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
- 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
- 0x0d, 0x0d, 0x0d, 0x0d, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
- 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
- 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
- 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
- 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
- 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
- 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
- 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07,
- 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
- 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
- 0x07, 0x07, 0x07, 0x07, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
- 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
- 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x1a, 0x1a, 0x1a, 0x1a,
- 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
- 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
- 0x1b, 0x1b, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x19, 0x19, 0x19, 0x19,
- 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
- 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
- 0x1e, 0x1e, 0x1e, 0x1e, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
- 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1c, 0x1c, 0x1c, 0x1c,
- 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
- 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
- 0x1d, 0x1d, 0x1d, 0x1d, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
- 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13,
- 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
- 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
- 0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
- 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x16, 0x16, 0x16, 0x16,
- 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
- 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
- 0x17, 0x17, 0x17, 0x17, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
- 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15,
- 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
- 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6b, 0x6b, 0x6b, 0x6b,
- 0x6b, 0x6b, 0x6b, 0x6b, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68,
- 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x6e, 0x6e, 0x6e, 0x6e,
- 0x6e, 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f,
- 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6d,
- 0x6d, 0x6d, 0x6d, 0x6d, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62,
- 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x60, 0x60, 0x60, 0x60,
- 0x60, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
- 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x67,
- 0x67, 0x67, 0x67, 0x67, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64,
- 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x7a, 0x7a, 0x7a, 0x7a,
- 0x7b, 0x7b, 0x7b, 0x7b, 0x78, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79, 0x79,
- 0x7e, 0x7e, 0x7e, 0x7e, 0x7f, 0x7f, 0x7f, 0x7f, 0x7c, 0x7c, 0x7c, 0x7c,
- 0x7d, 0x7d, 0x7d, 0x7d, 0x72, 0x72, 0x72, 0x72, 0x73, 0x73, 0x73, 0x73,
- 0x70, 0x70, 0x70, 0x70, 0x71, 0x71, 0x71, 0x71, 0x76, 0x76, 0x76, 0x76,
- 0x77, 0x77, 0x77, 0x77, 0x74, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x75,
- 0x4a, 0x4a, 0x4b, 0x4b, 0x48, 0x48, 0x49, 0x49, 0x4e, 0x4e, 0x4f, 0x4f,
- 0x4c, 0x4c, 0x4d, 0x4d, 0x42, 0x42, 0x43, 0x43, 0x40, 0x40, 0x41, 0x41,
- 0x46, 0x46, 0x47, 0x47, 0x44, 0x44, 0x45, 0x45, 0x5a, 0x5a, 0x5b, 0x5b,
- 0x58, 0x58, 0x59, 0x59, 0x5e, 0x5e, 0x5f, 0x5f, 0x5c, 0x5c, 0x5d, 0x5d,
- 0x52, 0x52, 0x53, 0x53, 0x50, 0x50, 0x51, 0x51, 0x56, 0x56, 0x57, 0x57,
- 0x54, 0x54, 0x55, 0x55, 0xd5, 0xd5, 0xd4, 0xd4, 0xd7, 0xd7, 0xd6, 0xd6,
- 0xd1, 0xd1, 0xd0, 0xd0, 0xd3, 0xd3, 0xd2, 0xd2, 0xdd, 0xdd, 0xdc, 0xdc,
- 0xdf, 0xdf, 0xde, 0xde, 0xd9, 0xd9, 0xd8, 0xd8, 0xdb, 0xdb, 0xda, 0xda,
- 0xc5, 0xc5, 0xc4, 0xc4, 0xc7, 0xc7, 0xc6, 0xc6, 0xc1, 0xc1, 0xc0, 0xc0,
- 0xc3, 0xc3, 0xc2, 0xc2, 0xcd, 0xcd, 0xcc, 0xcc, 0xcf, 0xcf, 0xce, 0xce,
- 0xc9, 0xc9, 0xc8, 0xc8, 0xcb, 0xcb, 0xca, 0xca, 0xf5, 0xf5, 0xf5, 0xf5,
- 0xf4, 0xf4, 0xf4, 0xf4, 0xf7, 0xf7, 0xf7, 0xf7, 0xf6, 0xf6, 0xf6, 0xf6,
- 0xf1, 0xf1, 0xf1, 0xf1, 0xf0, 0xf0, 0xf0, 0xf0, 0xf3, 0xf3, 0xf3, 0xf3,
- 0xf2, 0xf2, 0xf2, 0xf2, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc, 0xfc, 0xfc, 0xfc,
- 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xf9, 0xf9, 0xf9, 0xf9,
- 0xf8, 0xf8, 0xf8, 0xf8, 0xfb, 0xfb, 0xfb, 0xfb, 0xfa, 0xfa, 0xfa, 0xfa,
- 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe4, 0xe4, 0xe4, 0xe4,
- 0xe4, 0xe4, 0xe4, 0xe4, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
- 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe1, 0xe1, 0xe1, 0xe1,
- 0xe1, 0xe1, 0xe1, 0xe1, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0,
- 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe2, 0xe2, 0xe2, 0xe2,
- 0xe2, 0xe2, 0xe2, 0xe2, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed,
- 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xef, 0xef, 0xef, 0xef,
- 0xef, 0xef, 0xef, 0xef, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee,
- 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe8, 0xe8, 0xe8, 0xe8,
- 0xe8, 0xe8, 0xe8, 0xe8, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb,
- 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0x95, 0x95, 0x95, 0x95,
- 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
- 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
- 0x94, 0x94, 0x94, 0x94, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
- 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x96, 0x96, 0x96, 0x96,
- 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
- 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
- 0x91, 0x91, 0x91, 0x91, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
- 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x93, 0x93, 0x93, 0x93,
- 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
- 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
- 0x92, 0x92, 0x92, 0x92, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
- 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9c, 0x9c, 0x9c, 0x9c,
- 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
- 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
- 0x9f, 0x9f, 0x9f, 0x9f, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
- 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x99, 0x99, 0x99, 0x99,
- 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
- 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
- 0x98, 0x98, 0x98, 0x98, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
- 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9a, 0x9a, 0x9a, 0x9a,
- 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
- 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
- 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
- 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x84, 0x84, 0x84, 0x84,
- 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
- 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
- 0x84, 0x84, 0x84, 0x84, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
- 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
- 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
- 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
- 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
- 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x81, 0x81, 0x81, 0x81,
- 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
- 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
- 0x81, 0x81, 0x81, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
- 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
- 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
- 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
- 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
- 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x82, 0x82, 0x82, 0x82,
- 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
- 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
- 0x82, 0x82, 0x82, 0x82, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
- 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
- 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
- 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
- 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
- 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8f, 0x8f, 0x8f, 0x8f,
- 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
- 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
- 0x8f, 0x8f, 0x8f, 0x8f, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
- 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
- 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
- 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
- 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
- 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x88, 0x88, 0x88, 0x88,
- 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
- 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
- 0x88, 0x88, 0x88, 0x88, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
- 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
- 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
- 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
- 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
- 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0xb5, 0xb5, 0xb5, 0xb5,
- 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
- 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
- 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
- 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
- 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
- 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
- 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
- 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
- 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
- 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
- 0xb4, 0xb4, 0xb4, 0xb4, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
- 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
- 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
- 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
- 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
- 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb6, 0xb6, 0xb6, 0xb6,
- 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
- 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
- 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
- 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
- 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
- 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,
- 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,
- 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,
- 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,
- 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,
- 0xb1, 0xb1, 0xb1, 0xb1, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
- 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
- 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
- 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
- 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
- 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb3, 0xb3, 0xb3, 0xb3,
- 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
- 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
- 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
- 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
- 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
- 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
- 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
- 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
- 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
- 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
- 0xb2, 0xb2, 0xb2, 0xb2, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
- 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
- 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
- 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
- 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
- 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbc, 0xbc, 0xbc, 0xbc,
- 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
- 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
- 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
- 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
- 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
- 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
- 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
- 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
- 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
- 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
- 0xbf, 0xbf, 0xbf, 0xbf, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
- 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
- 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
- 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
- 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
- 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xb9, 0xb9, 0xb9, 0xb9,
- 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
- 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
- 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
- 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
- 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
- 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
- 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
- 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
- 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
- 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
- 0xb8, 0xb8, 0xb8, 0xb8, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
- 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
- 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
- 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
- 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
- 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xba, 0xba, 0xba, 0xba,
- 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
- 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
- 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
- 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
- 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
- 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
- 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
- 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
- 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
- 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
- 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
- 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
- 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
- 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
- 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
- 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa4, 0xa4, 0xa4, 0xa4,
- 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
- 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
- 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
- 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
- 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
- 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
- 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
- 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
- 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
- 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
- 0xa4, 0xa4, 0xa4, 0xa4, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
- 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
- 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
- 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
- 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
- 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
- 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
- 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
- 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
- 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
- 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
- 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
- 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
- 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
- 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
- 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
- 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
- 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
- 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
- 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
- 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
- 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa1, 0xa1, 0xa1, 0xa1,
- 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
- 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
- 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
- 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
- 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
- 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
- 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
- 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
- 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
- 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
- 0xa1, 0xa1, 0xa1, 0xa1, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
- 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
- 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
- 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
- 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
- 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
- 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
- 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
- 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
- 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
- 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
- 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
- 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
- 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
- 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
- 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
- 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
- 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
- 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
- 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
- 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
- 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa2, 0xa2, 0xa2, 0xa2,
- 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
- 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
- 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
- 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
- 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
- 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
- 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
- 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
- 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
- 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
- 0xa2, 0xa2, 0xa2, 0xa2, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
- 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
- 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
- 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
- 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
- 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
- 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
- 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
- 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
- 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
- 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
- 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
- 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
- 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
- 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
- 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
- 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
- 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
- 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
- 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
- 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
- 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xaf, 0xaf, 0xaf, 0xaf,
- 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
- 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
- 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
- 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
- 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
- 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
- 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
- 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
- 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
- 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
- 0xaf, 0xaf, 0xaf, 0xaf, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
- 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
- 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
- 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
- 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
- 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
- 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
- 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
- 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
- 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
- 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
- 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
- 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
- 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
- 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
- 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
- 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
- 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
- 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
- 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
- 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
- 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa8, 0xa8, 0xa8, 0xa8,
- 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
- 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
- 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
- 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
- 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
- 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
- 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
- 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
- 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
- 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
- 0xa8, 0xa8, 0xa8, 0xa8, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
- 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
- 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
- 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
- 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
- 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
- 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
- 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
- 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
- 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
- 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
- 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
- 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
- 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
- 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
- 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
- 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
- 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
- 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
- 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
- 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
- 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa
-};
-
-#endif /* FAST_ALAW_CONVERSION */
-
-#ifdef FAST_ULAW_CONVERSION
-
-int16_t _st_ulaw2linear16[256] = {
- -32124, -31100, -30076, -29052, -28028, -27004, -25980,
- -24956, -23932, -22908, -21884, -20860, -19836, -18812,
- -17788, -16764, -15996, -15484, -14972, -14460, -13948,
- -13436, -12924, -12412, -11900, -11388, -10876, -10364,
- -9852, -9340, -8828, -8316, -7932, -7676, -7420,
- -7164, -6908, -6652, -6396, -6140, -5884, -5628,
- -5372, -5116, -4860, -4604, -4348, -4092, -3900,
- -3772, -3644, -3516, -3388, -3260, -3132, -3004,
- -2876, -2748, -2620, -2492, -2364, -2236, -2108,
- -1980, -1884, -1820, -1756, -1692, -1628, -1564,
- -1500, -1436, -1372, -1308, -1244, -1180, -1116,
- -1052, -988, -924, -876, -844, -812, -780,
- -748, -716, -684, -652, -620, -588, -556,
- -524, -492, -460, -428, -396, -372, -356,
- -340, -324, -308, -292, -276, -260, -244,
- -228, -212, -196, -180, -164, -148, -132,
- -120, -112, -104, -96, -88, -80, -72,
- -64, -56, -48, -40, -32, -24, -16,
- -8, 0, 32124, 31100, 30076, 29052, 28028,
- 27004, 25980, 24956, 23932, 22908, 21884, 20860,
- 19836, 18812, 17788, 16764, 15996, 15484, 14972,
- 14460, 13948, 13436, 12924, 12412, 11900, 11388,
- 10876, 10364, 9852, 9340, 8828, 8316, 7932,
- 7676, 7420, 7164, 6908, 6652, 6396, 6140,
- 5884, 5628, 5372, 5116, 4860, 4604, 4348,
- 4092, 3900, 3772, 3644, 3516, 3388, 3260,
- 3132, 3004, 2876, 2748, 2620, 2492, 2364,
- 2236, 2108, 1980, 1884, 1820, 1756, 1692,
- 1628, 1564, 1500, 1436, 1372, 1308, 1244,
- 1180, 1116, 1052, 988, 924, 876, 844,
- 812, 780, 748, 716, 684, 652, 620,
- 588, 556, 524, 492, 460, 428, 396,
- 372, 356, 340, 324, 308, 292, 276,
- 260, 244, 228, 212, 196, 180, 164,
- 148, 132, 120, 112, 104, 96, 88,
- 80, 72, 64, 56, 48, 40, 32,
- 24, 16, 8, 0
-};
-
-uint8_t _st_14linear2ulaw[0x4000] = {
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
- 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
- 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
- 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
- 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
- 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
- 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
- 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
- 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
- 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
- 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
- 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
- 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
- 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
- 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
- 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
- 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
- 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
- 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
- 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
- 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
- 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03,
- 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
- 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
- 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
- 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
- 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
- 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
- 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
- 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
- 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
- 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
- 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
- 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
- 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
- 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
- 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
- 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
- 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
- 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
- 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
- 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
- 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
- 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
- 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
- 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
- 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
- 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
- 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
- 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
- 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
- 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
- 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
- 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
- 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
- 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
- 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
- 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
- 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
- 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
- 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
- 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
- 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
- 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
- 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06,
- 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
- 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
- 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
- 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
- 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
- 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
- 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
- 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
- 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
- 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
- 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
- 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
- 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
- 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
- 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
- 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
- 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
- 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
- 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
- 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
- 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
- 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
- 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
- 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
- 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
- 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
- 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
- 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
- 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
- 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
- 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
- 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
- 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
- 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
- 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
- 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
- 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
- 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
- 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
- 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
- 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
- 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
- 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
- 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
- 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
- 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
- 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
- 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
- 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
- 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
- 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
- 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
- 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
- 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
- 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
- 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
- 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
- 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
- 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
- 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
- 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
- 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
- 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
- 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x09, 0x09,
- 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
- 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
- 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
- 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
- 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
- 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
- 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
- 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
- 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
- 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
- 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
- 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
- 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
- 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
- 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
- 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
- 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
- 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
- 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
- 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
- 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
- 0x09, 0x09, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
- 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
- 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
- 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
- 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
- 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
- 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
- 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
- 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
- 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
- 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
- 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
- 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
- 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
- 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
- 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
- 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
- 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
- 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
- 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
- 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
- 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
- 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
- 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
- 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
- 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
- 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
- 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
- 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
- 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
- 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
- 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
- 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
- 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
- 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
- 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
- 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
- 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
- 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
- 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
- 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
- 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
- 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0c, 0x0c,
- 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
- 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
- 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
- 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
- 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
- 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
- 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
- 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
- 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
- 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
- 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
- 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
- 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
- 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
- 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
- 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
- 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
- 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
- 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
- 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
- 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
- 0x0c, 0x0c, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
- 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
- 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
- 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
- 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
- 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
- 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
- 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
- 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
- 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
- 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
- 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
- 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
- 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
- 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
- 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
- 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
- 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
- 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
- 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
- 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
- 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
- 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
- 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
- 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
- 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
- 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
- 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
- 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
- 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
- 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
- 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
- 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
- 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
- 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
- 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
- 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
- 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
- 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
- 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
- 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
- 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
- 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f,
- 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
- 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
- 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
- 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
- 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
- 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
- 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
- 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
- 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
- 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
- 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
- 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
- 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
- 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
- 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
- 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
- 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
- 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
- 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
- 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
- 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
- 0x0f, 0x0f, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
- 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
- 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
- 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
- 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
- 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
- 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
- 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
- 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
- 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
- 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x11, 0x11,
- 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
- 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
- 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
- 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
- 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
- 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
- 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
- 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
- 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
- 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
- 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
- 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
- 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
- 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
- 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
- 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
- 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
- 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
- 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
- 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
- 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
- 0x12, 0x12, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
- 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
- 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
- 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
- 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
- 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
- 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
- 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
- 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
- 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
- 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x14, 0x14,
- 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
- 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
- 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
- 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
- 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
- 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
- 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
- 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
- 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
- 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
- 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
- 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
- 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
- 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
- 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
- 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
- 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
- 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
- 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
- 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
- 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
- 0x15, 0x15, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
- 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
- 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
- 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
- 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
- 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
- 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
- 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
- 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
- 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
- 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x17, 0x17,
- 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
- 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
- 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
- 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
- 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
- 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
- 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
- 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
- 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
- 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
- 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
- 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
- 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
- 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
- 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
- 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
- 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
- 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
- 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
- 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
- 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x1a, 0x1a,
- 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
- 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
- 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
- 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
- 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
- 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
- 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
- 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
- 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
- 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
- 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
- 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
- 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
- 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
- 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
- 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
- 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
- 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
- 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
- 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
- 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
- 0x1b, 0x1b, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
- 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
- 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
- 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
- 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
- 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
- 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
- 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
- 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
- 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
- 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1d, 0x1d,
- 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
- 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
- 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
- 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
- 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
- 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
- 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
- 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
- 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
- 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
- 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
- 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
- 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
- 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
- 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
- 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
- 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
- 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
- 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
- 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
- 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
- 0x1e, 0x1e, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
- 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
- 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
- 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
- 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
- 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
- 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
- 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
- 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
- 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
- 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
- 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
- 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
- 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
- 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
- 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
- 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
- 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
- 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
- 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
- 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23,
- 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
- 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
- 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
- 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
- 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
- 0x23, 0x23, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
- 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
- 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
- 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
- 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
- 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
- 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
- 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
- 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
- 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
- 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x26, 0x26,
- 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
- 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
- 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
- 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
- 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
- 0x26, 0x26, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
- 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
- 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
- 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
- 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
- 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
- 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
- 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
- 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
- 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
- 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x29, 0x29,
- 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
- 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
- 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
- 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
- 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
- 0x29, 0x29, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
- 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
- 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
- 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
- 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
- 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
- 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
- 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
- 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
- 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
- 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2c, 0x2c,
- 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
- 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
- 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
- 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
- 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
- 0x2c, 0x2c, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
- 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
- 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
- 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
- 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
- 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
- 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
- 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
- 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
- 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
- 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f,
- 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
- 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
- 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
- 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
- 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
- 0x2f, 0x2f, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
- 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
- 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x31,
- 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
- 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
- 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
- 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
- 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
- 0x32, 0x32, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
- 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
- 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x34, 0x34,
- 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
- 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
- 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
- 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
- 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
- 0x35, 0x35, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x37,
- 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,
- 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,
- 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
- 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
- 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
- 0x38, 0x38, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
- 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
- 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x3a, 0x3a,
- 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
- 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
- 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
- 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
- 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
- 0x3b, 0x3b, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
- 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
- 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3d, 0x3d,
- 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
- 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
- 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
- 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
- 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
- 0x3e, 0x3e, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
- 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
- 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x40, 0x40,
- 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
- 0x40, 0x40, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
- 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
- 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x43, 0x43,
- 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43,
- 0x43, 0x43, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
- 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45,
- 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x46, 0x46,
- 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46,
- 0x46, 0x46, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47,
- 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48,
- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x49, 0x49,
- 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49,
- 0x49, 0x49, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
- 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b,
- 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4c, 0x4c,
- 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c,
- 0x4c, 0x4c, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d,
- 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e,
- 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4f, 0x4f,
- 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f,
- 0x4f, 0x4f, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x51, 0x51,
- 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52,
- 0x52, 0x52, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x54, 0x54,
- 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
- 0x55, 0x55, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x57, 0x57,
- 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58,
- 0x58, 0x58, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x5a, 0x5a,
- 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b,
- 0x5b, 0x5b, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d,
- 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e,
- 0x5e, 0x5e, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x60, 0x60,
- 0x60, 0x60, 0x61, 0x61, 0x61, 0x61, 0x62, 0x62, 0x62, 0x62, 0x63, 0x63,
- 0x63, 0x63, 0x64, 0x64, 0x64, 0x64, 0x65, 0x65, 0x65, 0x65, 0x66, 0x66,
- 0x66, 0x66, 0x67, 0x67, 0x67, 0x67, 0x68, 0x68, 0x68, 0x68, 0x69, 0x69,
- 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6a, 0x6b, 0x6b, 0x6b, 0x6b, 0x6c, 0x6c,
- 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6d, 0x6e, 0x6e, 0x6e, 0x6e, 0x6f, 0x6f,
- 0x6f, 0x6f, 0x70, 0x70, 0x71, 0x71, 0x72, 0x72, 0x73, 0x73, 0x74, 0x74,
- 0x75, 0x75, 0x76, 0x76, 0x77, 0x77, 0x78, 0x78, 0x79, 0x79, 0x7a, 0x7a,
- 0x7b, 0x7b, 0x7c, 0x7c, 0x7d, 0x7d, 0x7e, 0x7e, 0xff, 0xfe, 0xfe, 0xfd,
- 0xfd, 0xfc, 0xfc, 0xfb, 0xfb, 0xfa, 0xfa, 0xf9, 0xf9, 0xf8, 0xf8, 0xf7,
- 0xf7, 0xf6, 0xf6, 0xf5, 0xf5, 0xf4, 0xf4, 0xf3, 0xf3, 0xf2, 0xf2, 0xf1,
- 0xf1, 0xf0, 0xf0, 0xef, 0xef, 0xef, 0xef, 0xee, 0xee, 0xee, 0xee, 0xed,
- 0xed, 0xed, 0xed, 0xec, 0xec, 0xec, 0xec, 0xeb, 0xeb, 0xeb, 0xeb, 0xea,
- 0xea, 0xea, 0xea, 0xe9, 0xe9, 0xe9, 0xe9, 0xe8, 0xe8, 0xe8, 0xe8, 0xe7,
- 0xe7, 0xe7, 0xe7, 0xe6, 0xe6, 0xe6, 0xe6, 0xe5, 0xe5, 0xe5, 0xe5, 0xe4,
- 0xe4, 0xe4, 0xe4, 0xe3, 0xe3, 0xe3, 0xe3, 0xe2, 0xe2, 0xe2, 0xe2, 0xe1,
- 0xe1, 0xe1, 0xe1, 0xe0, 0xe0, 0xe0, 0xe0, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf,
- 0xdf, 0xdf, 0xdf, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdd,
- 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc,
- 0xdc, 0xdc, 0xdc, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xda,
- 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9,
- 0xd9, 0xd9, 0xd9, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd7,
- 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6,
- 0xd6, 0xd6, 0xd6, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd4,
- 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3,
- 0xd3, 0xd3, 0xd3, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd1,
- 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0,
- 0xd0, 0xd0, 0xd0, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf,
- 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xce, 0xce, 0xce, 0xce, 0xce,
- 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xcd,
- 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
- 0xcd, 0xcd, 0xcd, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
- 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb,
- 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xca,
- 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca,
- 0xca, 0xca, 0xca, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9,
- 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8,
- 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc7,
- 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7,
- 0xc7, 0xc7, 0xc7, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
- 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5,
- 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc4,
- 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4,
- 0xc4, 0xc4, 0xc4, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3,
- 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2,
- 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc1,
- 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1,
- 0xc1, 0xc1, 0xc1, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
- 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
- 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
- 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
- 0xbf, 0xbf, 0xbf, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
- 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
- 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbd,
- 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
- 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
- 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
- 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
- 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
- 0xbc, 0xbc, 0xbc, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
- 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
- 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xba,
- 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
- 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
- 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
- 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
- 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
- 0xb9, 0xb9, 0xb9, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
- 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
- 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb7,
- 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
- 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
- 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
- 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
- 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
- 0xb6, 0xb6, 0xb6, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
- 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
- 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb4,
- 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
- 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
- 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
- 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
- 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
- 0xb3, 0xb3, 0xb3, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
- 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
- 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb1,
- 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,
- 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,
- 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
- 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
- 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
- 0xb0, 0xb0, 0xb0, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
- 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
- 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
- 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
- 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
- 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xae, 0xae, 0xae, 0xae, 0xae,
- 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
- 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
- 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
- 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
- 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xad,
- 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
- 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
- 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
- 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
- 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
- 0xad, 0xad, 0xad, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
- 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
- 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
- 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
- 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
- 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xab, 0xab, 0xab, 0xab, 0xab,
- 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
- 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
- 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
- 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
- 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xaa,
- 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
- 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
- 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
- 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
- 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
- 0xaa, 0xaa, 0xaa, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
- 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
- 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
- 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
- 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
- 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
- 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
- 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
- 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
- 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
- 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa7,
- 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
- 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
- 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
- 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
- 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
- 0xa7, 0xa7, 0xa7, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
- 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
- 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
- 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
- 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
- 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
- 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
- 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
- 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
- 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
- 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa4,
- 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
- 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
- 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
- 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
- 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
- 0xa4, 0xa4, 0xa4, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
- 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
- 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
- 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
- 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
- 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
- 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
- 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
- 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
- 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
- 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa1,
- 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
- 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
- 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
- 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
- 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
- 0xa1, 0xa1, 0xa1, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
- 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
- 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
- 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
- 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
- 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
- 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
- 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
- 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
- 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
- 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
- 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
- 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
- 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
- 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
- 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
- 0x9f, 0x9f, 0x9f, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
- 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
- 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
- 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
- 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
- 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
- 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
- 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
- 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
- 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
- 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9d,
- 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
- 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
- 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
- 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
- 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
- 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
- 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
- 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
- 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
- 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
- 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
- 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
- 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
- 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
- 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
- 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
- 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
- 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
- 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
- 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
- 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
- 0x9c, 0x9c, 0x9c, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
- 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
- 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
- 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
- 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
- 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
- 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
- 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
- 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
- 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
- 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9a,
- 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
- 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
- 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
- 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
- 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
- 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
- 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
- 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
- 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
- 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
- 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x99, 0x99, 0x99, 0x99, 0x99,
- 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
- 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
- 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
- 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
- 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
- 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
- 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
- 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
- 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
- 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
- 0x99, 0x99, 0x99, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
- 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
- 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
- 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
- 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
- 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
- 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
- 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
- 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
- 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
- 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x97,
- 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
- 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
- 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
- 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
- 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
- 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
- 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
- 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
- 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
- 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
- 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x96, 0x96, 0x96, 0x96, 0x96,
- 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
- 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
- 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
- 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
- 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
- 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
- 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
- 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
- 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
- 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
- 0x96, 0x96, 0x96, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
- 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
- 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
- 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
- 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
- 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
- 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
- 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
- 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
- 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
- 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x94,
- 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
- 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
- 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
- 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
- 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
- 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
- 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
- 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
- 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
- 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
- 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x93, 0x93, 0x93, 0x93, 0x93,
- 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
- 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
- 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
- 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
- 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
- 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
- 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
- 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
- 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
- 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
- 0x93, 0x93, 0x93, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
- 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
- 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
- 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
- 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
- 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
- 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
- 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
- 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
- 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
- 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x91,
- 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
- 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
- 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
- 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
- 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
- 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
- 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
- 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
- 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
- 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
- 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x90, 0x90, 0x90, 0x90, 0x90,
- 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
- 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
- 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
- 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
- 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
- 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
- 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
- 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
- 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
- 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
- 0x90, 0x90, 0x90, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
- 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
- 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
- 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
- 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
- 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
- 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
- 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
- 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
- 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
- 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
- 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
- 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
- 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
- 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
- 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
- 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
- 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
- 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
- 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
- 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
- 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
- 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
- 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
- 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
- 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
- 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
- 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
- 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
- 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
- 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
- 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
- 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
- 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
- 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
- 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
- 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
- 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
- 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
- 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
- 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
- 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
- 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8d,
- 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
- 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
- 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
- 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
- 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
- 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
- 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
- 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
- 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
- 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
- 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
- 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
- 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
- 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
- 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
- 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
- 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
- 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
- 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
- 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
- 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
- 0x8d, 0x8d, 0x8d, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
- 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
- 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
- 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
- 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
- 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
- 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
- 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
- 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
- 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
- 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
- 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
- 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
- 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
- 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
- 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
- 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
- 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
- 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
- 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
- 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
- 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
- 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
- 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
- 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
- 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
- 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
- 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
- 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
- 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
- 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
- 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
- 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
- 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
- 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
- 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
- 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
- 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
- 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
- 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
- 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
- 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
- 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8a,
- 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
- 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
- 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
- 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
- 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
- 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
- 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
- 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
- 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
- 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
- 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
- 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
- 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
- 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
- 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
- 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
- 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
- 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
- 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
- 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
- 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
- 0x8a, 0x8a, 0x8a, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
- 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
- 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
- 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
- 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
- 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
- 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
- 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
- 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
- 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
- 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
- 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
- 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
- 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
- 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
- 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
- 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
- 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
- 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
- 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
- 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
- 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x88, 0x88, 0x88, 0x88, 0x88,
- 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
- 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
- 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
- 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
- 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
- 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
- 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
- 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
- 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
- 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
- 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
- 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
- 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
- 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
- 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
- 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
- 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
- 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
- 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
- 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
- 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x87,
- 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
- 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
- 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
- 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
- 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
- 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
- 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
- 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
- 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
- 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
- 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
- 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
- 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
- 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
- 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
- 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
- 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
- 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
- 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
- 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
- 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
- 0x87, 0x87, 0x87, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
- 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
- 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
- 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
- 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
- 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
- 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
- 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
- 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
- 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
- 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
- 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
- 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
- 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
- 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
- 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
- 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
- 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
- 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
- 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
- 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
- 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x85, 0x85, 0x85, 0x85, 0x85,
- 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
- 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
- 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
- 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
- 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
- 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
- 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
- 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
- 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
- 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
- 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
- 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
- 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
- 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
- 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
- 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
- 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
- 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
- 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
- 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
- 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x84,
- 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
- 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
- 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
- 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
- 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
- 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
- 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
- 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
- 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
- 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
- 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
- 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
- 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
- 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
- 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
- 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
- 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
- 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
- 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
- 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
- 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
- 0x84, 0x84, 0x84, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
- 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
- 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
- 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
- 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
- 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
- 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
- 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
- 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
- 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
- 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
- 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
- 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
- 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
- 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
- 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
- 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
- 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
- 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
- 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
- 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
- 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x82, 0x82, 0x82, 0x82, 0x82,
- 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
- 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
- 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
- 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
- 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
- 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
- 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
- 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
- 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
- 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
- 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
- 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
- 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
- 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
- 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
- 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
- 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
- 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
- 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
- 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
- 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x81,
- 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
- 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
- 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
- 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
- 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
- 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
- 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
- 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
- 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
- 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
- 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
- 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
- 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
- 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
- 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
- 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
- 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
- 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
- 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
- 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
- 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
- 0x81, 0x81, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
- 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
- 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
- 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
- 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
- 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
- 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
- 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
- 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
- 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
- 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
- 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
- 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
- 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
- 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
- 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
- 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
- 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
- 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
- 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
- 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
- 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
- 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
- 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
- 0x80, 0x80, 0x80, 0x80
-};
-
-#endif /* FAST_ULAW_CONVERSION */
-
-/* The following code was used to generate the lookup tables */
-#if 0
-int main()
-{
- int x, y, find2a = 0;
-
- y = 0;
- printf("int16_t _st_alaw2linear16[256] = {\n ");
- for (x = 0; x < 256; x++)
- {
- printf("%8d,", st_alaw2linear16(x));
- y++;
- if (y == 7)
- {
- y = 0;
- printf("\n ");
- }
- }
-
- printf("\n};\n\nuint8_t _st_13linear2alaw[0x2000] = {\n ");
- y = 0;
- for (x = 0; x < 0x2000; x++)
- {
- printf(" 0x%02x,", st_13linear2alaw((-0x1000)+x));
- y++;
- if (y == 12)
- {
- y = 0;
- printf("\n ");
- }
- }
-
- printf("\n};\n\nint16_t _st_ulaw2linear16[256] = {\n ");
- y = 0;
- for (x = 0; x < 256; x++)
- {
- printf("%8d,", st_ulaw2linear16(x));
- y++;
- if (y == 7)
- {
- y = 0;
- printf("\n ");
- }
- }
-
- printf("\n};\n\nuint8_t _st_14linear2ulaw[0x4000] = {\n ");
- y = 0;
- for (x = 0; x < 0x4000; x++)
- {
- printf(" 0x%02x,", st_14linear2ulaw((-0x2000)+x));
- y++;
- if (y == 12)
- {
- y = 0;
- printf("\n ");
- }
- }
- printf("\n};\n");
-
-}
-#endif
-
-/* The following is not used by SoX but kept for reference */
-#if 0
-/* copy from CCITT G.711 specifications */
-unsigned char _u2a[128] = { /* u- to A-law conversions */
- 1, 1, 2, 2, 3, 3, 4, 4,
- 5, 5, 6, 6, 7, 7, 8, 8,
- 9, 10, 11, 12, 13, 14, 15, 16,
- 17, 18, 19, 20, 21, 22, 23, 24,
- 25, 27, 29, 31, 33, 34, 35, 36,
- 37, 38, 39, 40, 41, 42, 43, 44,
- 46, 48, 49, 50, 51, 52, 53, 54,
- 55, 56, 57, 58, 59, 60, 61, 62,
- 64, 65, 66, 67, 68, 69, 70, 71,
- 72, 73, 74, 75, 76, 77, 78, 79,
-/* corrected:
- 81, 82, 83, 84, 85, 86, 87, 88,
- should be: */
- 80, 82, 83, 84, 85, 86, 87, 88,
- 89, 90, 91, 92, 93, 94, 95, 96,
- 97, 98, 99, 100, 101, 102, 103, 104,
- 105, 106, 107, 108, 109, 110, 111, 112,
- 113, 114, 115, 116, 117, 118, 119, 120,
- 121, 122, 123, 124, 125, 126, 127, 128};
-
-unsigned char _a2u[128] = { /* A- to u-law conversions */
- 1, 3, 5, 7, 9, 11, 13, 15,
- 16, 17, 18, 19, 20, 21, 22, 23,
- 24, 25, 26, 27, 28, 29, 30, 31,
- 32, 32, 33, 33, 34, 34, 35, 35,
- 36, 37, 38, 39, 40, 41, 42, 43,
- 44, 45, 46, 47, 48, 48, 49, 49,
- 50, 51, 52, 53, 54, 55, 56, 57,
- 58, 59, 60, 61, 62, 63, 64, 64,
- 65, 66, 67, 68, 69, 70, 71, 72,
-/* corrected:
- 73, 74, 75, 76, 77, 78, 79, 79,
- should be: */
- 73, 74, 75, 76, 77, 78, 79, 80,
-
- 80, 81, 82, 83, 84, 85, 86, 87,
- 88, 89, 90, 91, 92, 93, 94, 95,
- 96, 97, 98, 99, 100, 101, 102, 103,
- 104, 105, 106, 107, 108, 109, 110, 111,
- 112, 113, 114, 115, 116, 117, 118, 119,
- 120, 121, 122, 123, 124, 125, 126, 127};
-
-/* A-law to u-law conversion */
-unsigned char st_alaw2ulaw(
- unsigned char aval)
-{
- aval &= 0xff;
- return (unsigned char) ((aval & 0x80) ? (0xFF ^ _a2u[aval ^ 0xD5]) :
- (0x7F ^ _a2u[aval ^ 0x55]));
-}
-
-/* u-law to A-law conversion */
-unsigned char st_ulaw2alaw(
- unsigned char uval)
-{
- uval &= 0xff;
- return (unsigned char) ((uval & 0x80) ? (0xD5 ^ (_u2a[0xFF ^ uval] - 1)) :
- (unsigned char) (0x55 ^ (_u2a[0x7F ^ uval] - 1)));
-}
-#endif
+/*
+ * This source code is a product of Sun Microsystems, Inc. and is provided
+ * for unrestricted use. Users may copy or modify this source code without
+ * charge.
+ *
+ * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
+ * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun source code is provided with no support and without any obligation on
+ * the part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+/*
+ * g711.c
+ *
+ * u-law, A-law and linear PCM conversions.
+ */
+
+/*
+ * December 30, 1994:
+ * Functions linear2alaw, linear2ulaw have been updated to correctly
+ * convert unquantized 16 bit values.
+ * Tables for direct u- to A-law and A- to u-law conversions have been
+ * corrected.
+ * Borge Lindberg, Center for PersonKommunikation, Aalborg University.
+ * bli@cpk.auc.dk
+ *
+ */
+
+#include "g711.h"
+
+#define SIGN_BIT (0x80) /* Sign bit for a A-law byte. */
+#define QUANT_MASK (0xf) /* Quantization field mask. */
+#define NSEGS (8) /* Number of A-law segments. */
+#define SEG_SHIFT (4) /* Left shift for segment number. */
+#define SEG_MASK (0x70) /* Segment field mask. */
+
+#if !defined(FAST_ALAW_CONVERSION) || !defined(FAST_ULAW_CONVERSION)
+static int16_t seg_aend[8] = {0x1F, 0x3F, 0x7F, 0xFF,
+ 0x1FF, 0x3FF, 0x7FF, 0xFFF};
+static int16_t seg_uend[8] = {0x3F, 0x7F, 0xFF, 0x1FF,
+ 0x3FF, 0x7FF, 0xFFF, 0x1FFF};
+
+static int16_t search(
+ int16_t val,
+ int16_t *table,
+ int size)
+{
+ int i;
+
+ for (i = 0; i < size; i++) {
+ if (val <= *table++)
+ return (i);
+ }
+ return (size);
+}
+#endif /* !FAST_*_CONVERSION */
+
+#ifndef FAST_ALAW_CONVERSION
+/*
+ * linear2alaw() accepts an 13-bit signed integer and encodes it as A-law data
+ * stored in a unsigned char. This function should only be called with
+ * the data shifted such that it only contains information in the lower
+ * 13-bits.
+ *
+ * Linear Input Code Compressed Code
+ * ------------------------ ---------------
+ * 0000000wxyza 000wxyz
+ * 0000001wxyza 001wxyz
+ * 000001wxyzab 010wxyz
+ * 00001wxyzabc 011wxyz
+ * 0001wxyzabcd 100wxyz
+ * 001wxyzabcde 101wxyz
+ * 01wxyzabcdef 110wxyz
+ * 1wxyzabcdefg 111wxyz
+ *
+ * For further information see John C. Bellamy's Digital Telephony, 1982,
+ * John Wiley & Sons, pps 98-111 and 472-476.
+ */
+unsigned char st_13linear2alaw(
+ int16_t pcm_val) /* 2's complement (13-bit range) */
+{
+ int16_t mask;
+ short seg;
+ unsigned char aval;
+
+ /* Have calling software do it since its already doing a shift
+ * from 32-bits down to 16-bits.
+ */
+ /* pcm_val = pcm_val >> 3; */
+
+ /* A-law using even bit inversion */
+ if (pcm_val >= 0) {
+ mask = 0xD5; /* sign (7th) bit = 1 */
+ } else {
+ mask = 0x55; /* sign bit = 0 */
+ pcm_val = -pcm_val - 1;
+ }
+
+ /* Convert the scaled magnitude to segment number. */
+ seg = search(pcm_val, seg_aend, 8);
+
+ /* Combine the sign, segment, and quantization bits. */
+
+ if (seg >= 8) /* out of range, return maximum value. */
+ return (unsigned char) (0x7F ^ mask);
+ else {
+ aval = (unsigned char) seg << SEG_SHIFT;
+ if (seg < 2)
+ aval |= (pcm_val >> 1) & QUANT_MASK;
+ else
+ aval |= (pcm_val >> seg) & QUANT_MASK;
+ return (aval ^ mask);
+ }
+}
+
+/*
+ * alaw2linear() - Convert an A-law value to 16-bit signed linear PCM
+ *
+ */
+int16_t st_alaw2linear16(
+ unsigned char a_val)
+{
+ int16_t t;
+ int16_t seg;
+
+ a_val ^= 0x55;
+
+ t = (a_val & QUANT_MASK) << 4;
+ seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT;
+ switch (seg) {
+ case 0:
+ t += 8;
+ break;
+ case 1:
+ t += 0x108;
+ break;
+ default:
+ t += 0x108;
+ t <<= seg - 1;
+ }
+ return ((a_val & SIGN_BIT) ? t : -t);
+}
+#endif /* !FAST_ALAW_CONVERSION */
+
+#define BIAS (0x84) /* Bias for linear code. */
+#define CLIP 8159
+
+#ifndef FAST_ULAW_CONVERSION
+/*
+ * linear2ulaw() accepts a 14-bit signed integer and encodes it as u-law data
+ * stored in a unsigned char. This function should only be called with
+ * the data shifted such that it only contains information in the lower
+ * 14-bits.
+ *
+ * In order to simplify the encoding process, the original linear magnitude
+ * is biased by adding 33 which shifts the encoding range from (0 - 8158) to
+ * (33 - 8191). The result can be seen in the following encoding table:
+ *
+ * Biased Linear Input Code Compressed Code
+ * ------------------------ ---------------
+ * 00000001wxyza 000wxyz
+ * 0000001wxyzab 001wxyz
+ * 000001wxyzabc 010wxyz
+ * 00001wxyzabcd 011wxyz
+ * 0001wxyzabcde 100wxyz
+ * 001wxyzabcdef 101wxyz
+ * 01wxyzabcdefg 110wxyz
+ * 1wxyzabcdefgh 111wxyz
+ *
+ * Each biased linear code has a leading 1 which identifies the segment
+ * number. The value of the segment number is equal to 7 minus the number
+ * of leading 0's. The quantization interval is directly available as the
+ * four bits wxyz. * The trailing bits (a - h) are ignored.
+ *
+ * Ordinarily the complement of the resulting code word is used for
+ * transmission, and so the code word is complemented before it is returned.
+ *
+ * For further information see John C. Bellamy's Digital Telephony, 1982,
+ * John Wiley & Sons, pps 98-111 and 472-476.
+ */
+unsigned char st_14linear2ulaw(
+ int16_t pcm_val) /* 2's complement (14-bit range) */
+{
+ int16_t mask;
+ int16_t seg;
+ unsigned char uval;
+
+ /* Have calling software do it since its already doing a shift
+ * from 32-bits down to 16-bits.
+ */
+ /* pcm_val = pcm_val >> 2; */
+
+ /* u-law inverts all bits */
+ /* Get the sign and the magnitude of the value. */
+ if (pcm_val < 0) {
+ pcm_val = -pcm_val;
+ mask = 0x7F;
+ } else {
+ mask = 0xFF;
+ }
+ if ( pcm_val > CLIP ) pcm_val = CLIP; /* clip the magnitude */
+ pcm_val += (BIAS >> 2);
+
+ /* Convert the scaled magnitude to segment number. */
+ seg = search(pcm_val, seg_uend, 8);
+
+ /*
+ * Combine the sign, segment, quantization bits;
+ * and complement the code word.
+ */
+ if (seg >= 8) /* out of range, return maximum value. */
+ return (unsigned char) (0x7F ^ mask);
+ else {
+ uval = (unsigned char) (seg << 4) | ((pcm_val >> (seg + 1)) & 0xF);
+ return (uval ^ mask);
+ }
+
+}
+
+/*
+ * ulaw2linear() - Convert a u-law value to 16-bit linear PCM
+ *
+ * First, a biased linear code is derived from the code word. An unbiased
+ * output can then be obtained by subtracting 33 from the biased code.
+ *
+ * Note that this function expects to be passed the complement of the
+ * original code word. This is in keeping with ISDN conventions.
+ */
+int16_t st_ulaw2linear16(
+ unsigned char u_val)
+{
+ int16_t t;
+
+ /* Complement to obtain normal u-law value. */
+ u_val = ~u_val;
+
+ /*
+ * Extract and bias the quantization bits. Then
+ * shift up by the segment number and subtract out the bias.
+ */
+ t = ((u_val & QUANT_MASK) << 3) + BIAS;
+ t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT;
+
+ return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS));
+}
+#endif /* !FAST_ULAW_CONVERSION */
+
+#ifdef FAST_ALAW_CONVERSION
+
+int16_t _st_alaw2linear16[256] = {
+ -5504, -5248, -6016, -5760, -4480, -4224, -4992,
+ -4736, -7552, -7296, -8064, -7808, -6528, -6272,
+ -7040, -6784, -2752, -2624, -3008, -2880, -2240,
+ -2112, -2496, -2368, -3776, -3648, -4032, -3904,
+ -3264, -3136, -3520, -3392, -22016, -20992, -24064,
+ -23040, -17920, -16896, -19968, -18944, -30208, -29184,
+ -32256, -31232, -26112, -25088, -28160, -27136, -11008,
+ -10496, -12032, -11520, -8960, -8448, -9984, -9472,
+ -15104, -14592, -16128, -15616, -13056, -12544, -14080,
+ -13568, -344, -328, -376, -360, -280, -264,
+ -312, -296, -472, -456, -504, -488, -408,
+ -392, -440, -424, -88, -72, -120, -104,
+ -24, -8, -56, -40, -216, -200, -248,
+ -232, -152, -136, -184, -168, -1376, -1312,
+ -1504, -1440, -1120, -1056, -1248, -1184, -1888,
+ -1824, -2016, -1952, -1632, -1568, -1760, -1696,
+ -688, -656, -752, -720, -560, -528, -624,
+ -592, -944, -912, -1008, -976, -816, -784,
+ -880, -848, 5504, 5248, 6016, 5760, 4480,
+ 4224, 4992, 4736, 7552, 7296, 8064, 7808,
+ 6528, 6272, 7040, 6784, 2752, 2624, 3008,
+ 2880, 2240, 2112, 2496, 2368, 3776, 3648,
+ 4032, 3904, 3264, 3136, 3520, 3392, 22016,
+ 20992, 24064, 23040, 17920, 16896, 19968, 18944,
+ 30208, 29184, 32256, 31232, 26112, 25088, 28160,
+ 27136, 11008, 10496, 12032, 11520, 8960, 8448,
+ 9984, 9472, 15104, 14592, 16128, 15616, 13056,
+ 12544, 14080, 13568, 344, 328, 376, 360,
+ 280, 264, 312, 296, 472, 456, 504,
+ 488, 408, 392, 440, 424, 88, 72,
+ 120, 104, 24, 8, 56, 40, 216,
+ 200, 248, 232, 152, 136, 184, 168,
+ 1376, 1312, 1504, 1440, 1120, 1056, 1248,
+ 1184, 1888, 1824, 2016, 1952, 1632, 1568,
+ 1760, 1696, 688, 656, 752, 720, 560,
+ 528, 624, 592, 944, 912, 1008, 976,
+ 816, 784, 880, 848
+};
+
+uint8_t _st_13linear2alaw[0x2000] = {
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x3a, 0x3a, 0x3a, 0x3a,
+ 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
+ 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
+ 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
+ 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
+ 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
+ 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+ 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+ 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+ 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+ 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+ 0x3b, 0x3b, 0x3b, 0x3b, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x39, 0x39, 0x39, 0x39,
+ 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+ 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+ 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+ 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+ 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+ 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
+ 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
+ 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
+ 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
+ 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
+ 0x3e, 0x3e, 0x3e, 0x3e, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
+ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
+ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
+ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
+ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
+ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3c, 0x3c, 0x3c, 0x3c,
+ 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
+ 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
+ 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
+ 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
+ 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
+ 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
+ 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
+ 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
+ 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
+ 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
+ 0x3d, 0x3d, 0x3d, 0x3d, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
+ 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
+ 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
+ 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
+ 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
+ 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x33, 0x33, 0x33, 0x33,
+ 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
+ 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
+ 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
+ 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
+ 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,
+ 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,
+ 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,
+ 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,
+ 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,
+ 0x37, 0x37, 0x37, 0x37, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
+ 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
+ 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
+ 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
+ 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
+ 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x35, 0x35, 0x35, 0x35,
+ 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
+ 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
+ 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
+ 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
+ 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x1a, 0x1a, 0x1a, 0x1a,
+ 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+ 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+ 0x1b, 0x1b, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x19, 0x19, 0x19, 0x19,
+ 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+ 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1e, 0x1e, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+ 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+ 0x1d, 0x1d, 0x1d, 0x1d, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+ 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13,
+ 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x16, 0x16, 0x16, 0x16,
+ 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15,
+ 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+ 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6b, 0x6b, 0x6b, 0x6b,
+ 0x6b, 0x6b, 0x6b, 0x6b, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x6e, 0x6e, 0x6e, 0x6e,
+ 0x6e, 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f,
+ 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6d,
+ 0x6d, 0x6d, 0x6d, 0x6d, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x60, 0x60, 0x60, 0x60,
+ 0x60, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
+ 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x67,
+ 0x67, 0x67, 0x67, 0x67, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64,
+ 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x7a, 0x7a, 0x7a, 0x7a,
+ 0x7b, 0x7b, 0x7b, 0x7b, 0x78, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79, 0x79,
+ 0x7e, 0x7e, 0x7e, 0x7e, 0x7f, 0x7f, 0x7f, 0x7f, 0x7c, 0x7c, 0x7c, 0x7c,
+ 0x7d, 0x7d, 0x7d, 0x7d, 0x72, 0x72, 0x72, 0x72, 0x73, 0x73, 0x73, 0x73,
+ 0x70, 0x70, 0x70, 0x70, 0x71, 0x71, 0x71, 0x71, 0x76, 0x76, 0x76, 0x76,
+ 0x77, 0x77, 0x77, 0x77, 0x74, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x75,
+ 0x4a, 0x4a, 0x4b, 0x4b, 0x48, 0x48, 0x49, 0x49, 0x4e, 0x4e, 0x4f, 0x4f,
+ 0x4c, 0x4c, 0x4d, 0x4d, 0x42, 0x42, 0x43, 0x43, 0x40, 0x40, 0x41, 0x41,
+ 0x46, 0x46, 0x47, 0x47, 0x44, 0x44, 0x45, 0x45, 0x5a, 0x5a, 0x5b, 0x5b,
+ 0x58, 0x58, 0x59, 0x59, 0x5e, 0x5e, 0x5f, 0x5f, 0x5c, 0x5c, 0x5d, 0x5d,
+ 0x52, 0x52, 0x53, 0x53, 0x50, 0x50, 0x51, 0x51, 0x56, 0x56, 0x57, 0x57,
+ 0x54, 0x54, 0x55, 0x55, 0xd5, 0xd5, 0xd4, 0xd4, 0xd7, 0xd7, 0xd6, 0xd6,
+ 0xd1, 0xd1, 0xd0, 0xd0, 0xd3, 0xd3, 0xd2, 0xd2, 0xdd, 0xdd, 0xdc, 0xdc,
+ 0xdf, 0xdf, 0xde, 0xde, 0xd9, 0xd9, 0xd8, 0xd8, 0xdb, 0xdb, 0xda, 0xda,
+ 0xc5, 0xc5, 0xc4, 0xc4, 0xc7, 0xc7, 0xc6, 0xc6, 0xc1, 0xc1, 0xc0, 0xc0,
+ 0xc3, 0xc3, 0xc2, 0xc2, 0xcd, 0xcd, 0xcc, 0xcc, 0xcf, 0xcf, 0xce, 0xce,
+ 0xc9, 0xc9, 0xc8, 0xc8, 0xcb, 0xcb, 0xca, 0xca, 0xf5, 0xf5, 0xf5, 0xf5,
+ 0xf4, 0xf4, 0xf4, 0xf4, 0xf7, 0xf7, 0xf7, 0xf7, 0xf6, 0xf6, 0xf6, 0xf6,
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf0, 0xf0, 0xf0, 0xf0, 0xf3, 0xf3, 0xf3, 0xf3,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc, 0xfc, 0xfc, 0xfc,
+ 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xf9, 0xf9, 0xf9, 0xf9,
+ 0xf8, 0xf8, 0xf8, 0xf8, 0xfb, 0xfb, 0xfb, 0xfb, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe4, 0xe4, 0xe4, 0xe4,
+ 0xe4, 0xe4, 0xe4, 0xe4, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
+ 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe1, 0xe1, 0xe1, 0xe1,
+ 0xe1, 0xe1, 0xe1, 0xe1, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0,
+ 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe2, 0xe2, 0xe2, 0xe2,
+ 0xe2, 0xe2, 0xe2, 0xe2, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed,
+ 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xef, 0xef, 0xef, 0xef,
+ 0xef, 0xef, 0xef, 0xef, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee,
+ 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe8, 0xe8, 0xe8, 0xe8,
+ 0xe8, 0xe8, 0xe8, 0xe8, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb,
+ 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x92, 0x92, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+ 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9c, 0x9c, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+ 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+ 0x9f, 0x9f, 0x9f, 0x9f, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+ 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9a, 0x9a, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0xb5, 0xb5, 0xb5, 0xb5,
+ 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
+ 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
+ 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
+ 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
+ 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+ 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+ 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+ 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+ 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+ 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb6, 0xb6, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
+ 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,
+ 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,
+ 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,
+ 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,
+ 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,
+ 0xb1, 0xb1, 0xb1, 0xb1, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+ 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+ 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+ 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+ 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+ 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbc, 0xbc, 0xbc, 0xbc,
+ 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+ 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+ 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+ 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+ 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+ 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xbf, 0xbf, 0xbf, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+ 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+ 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+ 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+ 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+ 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xb9, 0xb9, 0xb9, 0xb9,
+ 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
+ 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
+ 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
+ 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
+ 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
+ 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
+ 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
+ 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
+ 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
+ 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
+ 0xb8, 0xb8, 0xb8, 0xb8, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa
+};
+
+#endif /* FAST_ALAW_CONVERSION */
+
+#ifdef FAST_ULAW_CONVERSION
+
+int16_t _st_ulaw2linear16[256] = {
+ -32124, -31100, -30076, -29052, -28028, -27004, -25980,
+ -24956, -23932, -22908, -21884, -20860, -19836, -18812,
+ -17788, -16764, -15996, -15484, -14972, -14460, -13948,
+ -13436, -12924, -12412, -11900, -11388, -10876, -10364,
+ -9852, -9340, -8828, -8316, -7932, -7676, -7420,
+ -7164, -6908, -6652, -6396, -6140, -5884, -5628,
+ -5372, -5116, -4860, -4604, -4348, -4092, -3900,
+ -3772, -3644, -3516, -3388, -3260, -3132, -3004,
+ -2876, -2748, -2620, -2492, -2364, -2236, -2108,
+ -1980, -1884, -1820, -1756, -1692, -1628, -1564,
+ -1500, -1436, -1372, -1308, -1244, -1180, -1116,
+ -1052, -988, -924, -876, -844, -812, -780,
+ -748, -716, -684, -652, -620, -588, -556,
+ -524, -492, -460, -428, -396, -372, -356,
+ -340, -324, -308, -292, -276, -260, -244,
+ -228, -212, -196, -180, -164, -148, -132,
+ -120, -112, -104, -96, -88, -80, -72,
+ -64, -56, -48, -40, -32, -24, -16,
+ -8, 0, 32124, 31100, 30076, 29052, 28028,
+ 27004, 25980, 24956, 23932, 22908, 21884, 20860,
+ 19836, 18812, 17788, 16764, 15996, 15484, 14972,
+ 14460, 13948, 13436, 12924, 12412, 11900, 11388,
+ 10876, 10364, 9852, 9340, 8828, 8316, 7932,
+ 7676, 7420, 7164, 6908, 6652, 6396, 6140,
+ 5884, 5628, 5372, 5116, 4860, 4604, 4348,
+ 4092, 3900, 3772, 3644, 3516, 3388, 3260,
+ 3132, 3004, 2876, 2748, 2620, 2492, 2364,
+ 2236, 2108, 1980, 1884, 1820, 1756, 1692,
+ 1628, 1564, 1500, 1436, 1372, 1308, 1244,
+ 1180, 1116, 1052, 988, 924, 876, 844,
+ 812, 780, 748, 716, 684, 652, 620,
+ 588, 556, 524, 492, 460, 428, 396,
+ 372, 356, 340, 324, 308, 292, 276,
+ 260, 244, 228, 212, 196, 180, 164,
+ 148, 132, 120, 112, 104, 96, 88,
+ 80, 72, 64, 56, 48, 40, 32,
+ 24, 16, 8, 0
+};
+
+uint8_t _st_14linear2ulaw[0x4000] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+ 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+ 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+ 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+ 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+ 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+ 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+ 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+ 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+ 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+ 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+ 0x12, 0x12, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+ 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+ 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+ 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+ 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+ 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+ 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+ 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+ 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+ 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+ 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+ 0x15, 0x15, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+ 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+ 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+ 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+ 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+ 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+ 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+ 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+ 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+ 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+ 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+ 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+ 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+ 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+ 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+ 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+ 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+ 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+ 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+ 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+ 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x1a, 0x1a,
+ 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+ 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+ 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+ 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+ 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+ 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+ 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+ 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+ 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+ 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+ 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+ 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+ 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+ 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+ 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+ 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+ 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+ 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+ 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+ 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+ 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+ 0x1b, 0x1b, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1d, 0x1d,
+ 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+ 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+ 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+ 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+ 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+ 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+ 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+ 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+ 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+ 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+ 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+ 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+ 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+ 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+ 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+ 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+ 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+ 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+ 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+ 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+ 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
+ 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
+ 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
+ 0x32, 0x32, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
+ 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
+ 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x34, 0x34,
+ 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
+ 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
+ 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
+ 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
+ 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
+ 0x35, 0x35, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x37,
+ 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,
+ 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,
+ 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+ 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+ 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x3a, 0x3a,
+ 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
+ 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
+ 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+ 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+ 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+ 0x3b, 0x3b, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
+ 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
+ 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3d, 0x3d,
+ 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
+ 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
+ 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
+ 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
+ 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
+ 0x3e, 0x3e, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
+ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
+ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x43, 0x43,
+ 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43,
+ 0x43, 0x43, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
+ 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45,
+ 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x46, 0x46,
+ 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46,
+ 0x46, 0x46, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47,
+ 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48,
+ 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x49, 0x49,
+ 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49,
+ 0x49, 0x49, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
+ 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b,
+ 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4c, 0x4c,
+ 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c,
+ 0x4c, 0x4c, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d,
+ 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e,
+ 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4f, 0x4f,
+ 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f,
+ 0x4f, 0x4f, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x51, 0x51,
+ 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52,
+ 0x52, 0x52, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x54, 0x54,
+ 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
+ 0x55, 0x55, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x57, 0x57,
+ 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58,
+ 0x58, 0x58, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x5a, 0x5a,
+ 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b,
+ 0x5b, 0x5b, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d,
+ 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e,
+ 0x5e, 0x5e, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x60, 0x60,
+ 0x60, 0x60, 0x61, 0x61, 0x61, 0x61, 0x62, 0x62, 0x62, 0x62, 0x63, 0x63,
+ 0x63, 0x63, 0x64, 0x64, 0x64, 0x64, 0x65, 0x65, 0x65, 0x65, 0x66, 0x66,
+ 0x66, 0x66, 0x67, 0x67, 0x67, 0x67, 0x68, 0x68, 0x68, 0x68, 0x69, 0x69,
+ 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6a, 0x6b, 0x6b, 0x6b, 0x6b, 0x6c, 0x6c,
+ 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6d, 0x6e, 0x6e, 0x6e, 0x6e, 0x6f, 0x6f,
+ 0x6f, 0x6f, 0x70, 0x70, 0x71, 0x71, 0x72, 0x72, 0x73, 0x73, 0x74, 0x74,
+ 0x75, 0x75, 0x76, 0x76, 0x77, 0x77, 0x78, 0x78, 0x79, 0x79, 0x7a, 0x7a,
+ 0x7b, 0x7b, 0x7c, 0x7c, 0x7d, 0x7d, 0x7e, 0x7e, 0xff, 0xfe, 0xfe, 0xfd,
+ 0xfd, 0xfc, 0xfc, 0xfb, 0xfb, 0xfa, 0xfa, 0xf9, 0xf9, 0xf8, 0xf8, 0xf7,
+ 0xf7, 0xf6, 0xf6, 0xf5, 0xf5, 0xf4, 0xf4, 0xf3, 0xf3, 0xf2, 0xf2, 0xf1,
+ 0xf1, 0xf0, 0xf0, 0xef, 0xef, 0xef, 0xef, 0xee, 0xee, 0xee, 0xee, 0xed,
+ 0xed, 0xed, 0xed, 0xec, 0xec, 0xec, 0xec, 0xeb, 0xeb, 0xeb, 0xeb, 0xea,
+ 0xea, 0xea, 0xea, 0xe9, 0xe9, 0xe9, 0xe9, 0xe8, 0xe8, 0xe8, 0xe8, 0xe7,
+ 0xe7, 0xe7, 0xe7, 0xe6, 0xe6, 0xe6, 0xe6, 0xe5, 0xe5, 0xe5, 0xe5, 0xe4,
+ 0xe4, 0xe4, 0xe4, 0xe3, 0xe3, 0xe3, 0xe3, 0xe2, 0xe2, 0xe2, 0xe2, 0xe1,
+ 0xe1, 0xe1, 0xe1, 0xe0, 0xe0, 0xe0, 0xe0, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf,
+ 0xdf, 0xdf, 0xdf, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc,
+ 0xdc, 0xdc, 0xdc, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xda,
+ 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9,
+ 0xd9, 0xd9, 0xd9, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd7,
+ 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6,
+ 0xd6, 0xd6, 0xd6, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd4,
+ 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3,
+ 0xd3, 0xd3, 0xd3, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd1,
+ 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0,
+ 0xd0, 0xd0, 0xd0, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf,
+ 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xce, 0xce, 0xce, 0xce, 0xce,
+ 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb,
+ 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xca,
+ 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca,
+ 0xca, 0xca, 0xca, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9,
+ 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8,
+ 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc7,
+ 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7,
+ 0xc7, 0xc7, 0xc7, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5,
+ 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc4,
+ 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4,
+ 0xc4, 0xc4, 0xc4, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3,
+ 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2,
+ 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc1,
+ 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1,
+ 0xc1, 0xc1, 0xc1, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xbf, 0xbf, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+ 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+ 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbd,
+ 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+ 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+ 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+ 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+ 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+ 0xbc, 0xbc, 0xbc, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
+ 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
+ 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
+ 0xb9, 0xb9, 0xb9, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
+ 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
+ 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb7,
+ 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+ 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+ 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb6, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
+ 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
+ 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb4,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb3, 0xb3, 0xb3, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb1,
+ 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,
+ 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,
+ 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb0, 0xb0, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+ 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+ 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+ 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+ 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+ 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+ 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+ 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+ 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+ 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+ 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+ 0x9f, 0x9f, 0x9f, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9d,
+ 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+ 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+ 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+ 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+ 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+ 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+ 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+ 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+ 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+ 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+ 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9c, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+ 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+ 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+ 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+ 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+ 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+ 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+ 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+ 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+ 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+ 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9a,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80
+};
+
+#endif /* FAST_ULAW_CONVERSION */
+
+/* The following code was used to generate the lookup tables */
+#if 0
+int main()
+{
+ int x, y, find2a = 0;
+
+ y = 0;
+ printf("int16_t _st_alaw2linear16[256] = {\n ");
+ for (x = 0; x < 256; x++)
+ {
+ printf("%8d,", st_alaw2linear16(x));
+ y++;
+ if (y == 7)
+ {
+ y = 0;
+ printf("\n ");
+ }
+ }
+
+ printf("\n};\n\nuint8_t _st_13linear2alaw[0x2000] = {\n ");
+ y = 0;
+ for (x = 0; x < 0x2000; x++)
+ {
+ printf(" 0x%02x,", st_13linear2alaw((-0x1000)+x));
+ y++;
+ if (y == 12)
+ {
+ y = 0;
+ printf("\n ");
+ }
+ }
+
+ printf("\n};\n\nint16_t _st_ulaw2linear16[256] = {\n ");
+ y = 0;
+ for (x = 0; x < 256; x++)
+ {
+ printf("%8d,", st_ulaw2linear16(x));
+ y++;
+ if (y == 7)
+ {
+ y = 0;
+ printf("\n ");
+ }
+ }
+
+ printf("\n};\n\nuint8_t _st_14linear2ulaw[0x4000] = {\n ");
+ y = 0;
+ for (x = 0; x < 0x4000; x++)
+ {
+ printf(" 0x%02x,", st_14linear2ulaw((-0x2000)+x));
+ y++;
+ if (y == 12)
+ {
+ y = 0;
+ printf("\n ");
+ }
+ }
+ printf("\n};\n");
+
+}
+#endif
+
+/* The following is not used by SoX but kept for reference */
+#if 0
+/* copy from CCITT G.711 specifications */
+unsigned char _u2a[128] = { /* u- to A-law conversions */
+ 1, 1, 2, 2, 3, 3, 4, 4,
+ 5, 5, 6, 6, 7, 7, 8, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 27, 29, 31, 33, 34, 35, 36,
+ 37, 38, 39, 40, 41, 42, 43, 44,
+ 46, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, 57, 58, 59, 60, 61, 62,
+ 64, 65, 66, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, 76, 77, 78, 79,
+/* corrected:
+ 81, 82, 83, 84, 85, 86, 87, 88,
+ should be: */
+ 80, 82, 83, 84, 85, 86, 87, 88,
+ 89, 90, 91, 92, 93, 94, 95, 96,
+ 97, 98, 99, 100, 101, 102, 103, 104,
+ 105, 106, 107, 108, 109, 110, 111, 112,
+ 113, 114, 115, 116, 117, 118, 119, 120,
+ 121, 122, 123, 124, 125, 126, 127, 128};
+
+unsigned char _a2u[128] = { /* A- to u-law conversions */
+ 1, 3, 5, 7, 9, 11, 13, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 32, 33, 33, 34, 34, 35, 35,
+ 36, 37, 38, 39, 40, 41, 42, 43,
+ 44, 45, 46, 47, 48, 48, 49, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57,
+ 58, 59, 60, 61, 62, 63, 64, 64,
+ 65, 66, 67, 68, 69, 70, 71, 72,
+/* corrected:
+ 73, 74, 75, 76, 77, 78, 79, 79,
+ should be: */
+ 73, 74, 75, 76, 77, 78, 79, 80,
+
+ 80, 81, 82, 83, 84, 85, 86, 87,
+ 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101, 102, 103,
+ 104, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127};
+
+/* A-law to u-law conversion */
+unsigned char st_alaw2ulaw(
+ unsigned char aval)
+{
+ aval &= 0xff;
+ return (unsigned char) ((aval & 0x80) ? (0xFF ^ _a2u[aval ^ 0xD5]) :
+ (0x7F ^ _a2u[aval ^ 0x55]));
+}
+
+/* u-law to A-law conversion */
+unsigned char st_ulaw2alaw(
+ unsigned char uval)
+{
+ uval &= 0xff;
+ return (unsigned char) ((uval & 0x80) ? (0xD5 ^ (_u2a[0xFF ^ uval] - 1)) :
+ (unsigned char) (0x55 ^ (_u2a[0x7F ^ uval] - 1)));
+}
+#endif
diff --git a/src/pulsecore/g711.h b/src/pulsecore/g711.h
index 97cedf81..37ebcf72 100644
--- a/src/pulsecore/g711.h
+++ b/src/pulsecore/g711.h
@@ -13,7 +13,7 @@
** implied warranty.
*/
-/** Copied from sox -- Lennart Poettring*/
+/** Copied from sox -- Lennart Poettering */
#include <inttypes.h>
@@ -33,7 +33,7 @@ extern int16_t _st_ulaw2linear16[256];
#define st_14linear2ulaw(sw) (_st_14linear2ulaw[(sw + 0x2000)])
#define st_ulaw2linear16(uc) (_st_ulaw2linear16[uc])
#else
-unsigned char st_14linear2ulaw(int16_t pcm_val);
+unsigned char st_14linear2ulaw(int16_t pcm_val);
int16_t st_ulaw2linear16(unsigned char);
#endif
diff --git a/src/pulsecore/hashmap.c b/src/pulsecore/hashmap.c
index 81a160a6..b7f4109b 100644
--- a/src/pulsecore/hashmap.c
+++ b/src/pulsecore/hashmap.c
@@ -1,18 +1,18 @@
-/* $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
@@ -24,13 +24,14 @@
#endif
#include <stdlib.h>
-#include <assert.h>
#include <string.h>
#include <pulse/xmalloc.h>
#include <pulsecore/idxset.h>
#include <pulsecore/log.h>
+#include <pulsecore/flist.h>
+#include <pulsecore/macro.h>
#include "hashmap.h"
@@ -47,28 +48,30 @@ struct pa_hashmap {
unsigned size;
struct hashmap_entry **data;
struct hashmap_entry *first_entry;
-
+
unsigned n_entries;
pa_hash_func_t hash_func;
pa_compare_func_t compare_func;
};
+PA_STATIC_FLIST_DECLARE(entries, 0, pa_xfree);
+
pa_hashmap *pa_hashmap_new(pa_hash_func_t hash_func, pa_compare_func_t compare_func) {
pa_hashmap *h;
-
+
h = pa_xnew(pa_hashmap, 1);
h->data = pa_xnew0(struct hashmap_entry*, h->size = BUCKETS);
h->first_entry = NULL;
h->n_entries = 0;
h->hash_func = hash_func ? hash_func : pa_idxset_trivial_hash_func;
h->compare_func = compare_func ? compare_func : pa_idxset_trivial_compare_func;
-
+
return h;
}
-static void remove(pa_hashmap *h, struct hashmap_entry *e) {
- assert(h);
- assert(e);
+static void remove_entry(pa_hashmap *h, struct hashmap_entry *e) {
+ pa_assert(h);
+ pa_assert(e);
if (e->next)
e->next->previous = e->previous;
@@ -82,31 +85,33 @@ static void remove(pa_hashmap *h, struct hashmap_entry *e) {
if (e->bucket_previous)
e->bucket_previous->bucket_next = e->bucket_next;
else {
- assert(e->hash < h->size);
+ pa_assert(e->hash < h->size);
h->data[e->hash] = e->bucket_next;
}
- pa_xfree(e);
+ if (pa_flist_push(PA_STATIC_FLIST_GET(entries), e) < 0)
+ pa_xfree(e);
+
h->n_entries--;
}
void pa_hashmap_free(pa_hashmap*h, void (*free_func)(void *p, void *userdata), void *userdata) {
- assert(h);
+ pa_assert(h);
while (h->first_entry) {
if (free_func)
free_func(h->first_entry->value, userdata);
- remove(h, h->first_entry);
+ remove_entry(h, h->first_entry);
}
-
+
pa_xfree(h->data);
pa_xfree(h);
}
static struct hashmap_entry *get(pa_hashmap *h, unsigned hash, const void *key) {
struct hashmap_entry *e;
- assert(h);
- assert(hash < h->size);
+ pa_assert(h);
+ pa_assert(hash < h->size);
for (e = h->data[hash]; e; e = e->bucket_next)
if (h->compare_func(e->key, key) == 0)
@@ -118,30 +123,32 @@ static struct hashmap_entry *get(pa_hashmap *h, unsigned hash, const void *key)
int pa_hashmap_put(pa_hashmap *h, const void *key, void *value) {
struct hashmap_entry *e;
unsigned hash;
- assert(h);
+ pa_assert(h);
hash = h->hash_func(key) % h->size;
if ((e = get(h, hash, key)))
return -1;
-
- e = pa_xnew(struct hashmap_entry, 1);
+
+ if (!(e = pa_flist_pop(PA_STATIC_FLIST_GET(entries))))
+ e = pa_xnew(struct hashmap_entry, 1);
+
e->hash = hash;
e->key = key;
e->value = value;
-
+
e->previous = NULL;
e->next = h->first_entry;
if (h->first_entry)
h->first_entry->previous = e;
h->first_entry = e;
-
+
e->bucket_previous = NULL;
e->bucket_next = h->data[hash];
if (h->data[hash])
h->data[hash]->bucket_previous = e;
h->data[hash] = e;
-
+
h->n_entries ++;
return 0;
}
@@ -150,7 +157,7 @@ void* pa_hashmap_get(pa_hashmap *h, const void *key) {
unsigned hash;
struct hashmap_entry *e;
- assert(h);
+ pa_assert(h);
hash = h->hash_func(key) % h->size;
@@ -164,8 +171,8 @@ void* pa_hashmap_remove(pa_hashmap *h, const void *key) {
struct hashmap_entry *e;
unsigned hash;
void *data;
-
- assert(h);
+
+ pa_assert(h);
hash = h->hash_func(key) % h->size;
@@ -173,7 +180,7 @@ void* pa_hashmap_remove(pa_hashmap *h, const void *key) {
return NULL;
data = e->value;
- remove(h, e);
+ remove_entry(h, e);
return data;
}
@@ -182,41 +189,53 @@ unsigned pa_hashmap_size(pa_hashmap *h) {
}
void *pa_hashmap_iterate(pa_hashmap *h, void **state, const void **key) {
- assert(h);
- assert(state);
+ struct hashmap_entry *e;
+
+ pa_assert(h);
+ pa_assert(state);
+
+ if (*state == (void*) -1)
+ goto at_end;
- if (!*state)
- *state = h->first_entry;
+ if ((!*state && !h->first_entry))
+ goto at_end;
+
+ e = *state ? *state : h->first_entry;
+
+ if (e->next)
+ *state = e->next;
else
- *state = ((struct hashmap_entry*) *state)->next;
+ *state = (void*) -1;
- if (!*state) {
- if (key)
- *key = NULL;
- return NULL;
- }
+ if (key)
+ *key = e->key;
+
+ return e->value;
+
+at_end:
+ *state = (void *) -1;
if (key)
- *key = ((struct hashmap_entry*) *state)->key;
-
- return ((struct hashmap_entry*) *state)->value;
+ *key = NULL;
+
+ return NULL;
}
void* pa_hashmap_steal_first(pa_hashmap *h) {
void *data;
-
- assert(h);
+
+ pa_assert(h);
if (!h->first_entry)
return NULL;
data = h->first_entry->value;
- remove(h, h->first_entry);
+ remove_entry(h, h->first_entry);
return data;
}
void *pa_hashmap_get_first(pa_hashmap *h) {
- assert(h);
+ pa_assert(h);
if (!h->first_entry)
return NULL;
diff --git a/src/pulsecore/hashmap.h b/src/pulsecore/hashmap.h
index b8a358ec..a4505c4c 100644
--- a/src/pulsecore/hashmap.h
+++ b/src/pulsecore/hashmap.h
@@ -1,21 +1,21 @@
#ifndef foohashmaphfoo
#define foohashmaphfoo
-/* $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
@@ -30,11 +30,13 @@
typedef struct pa_hashmap pa_hashmap;
+typedef void (*pa_free2_cb_t)(void *p, void *userdata);
+
/* Create a new hashmap. Use the specified functions for hashing and comparing objects in the map */
pa_hashmap *pa_hashmap_new(pa_hash_func_t hash_func, pa_compare_func_t compare_func);
/* Free the hash table. Calls the specified function for every value in the table. The function may be NULL */
-void pa_hashmap_free(pa_hashmap*, void (*free_func)(void *p, void *userdata), void *userdata);
+void pa_hashmap_free(pa_hashmap*, pa_free2_cb_t free_cb, void *userdata);
/* Returns non-zero when the entry already exists */
int pa_hashmap_put(pa_hashmap *h, const void *key, void *value);
@@ -47,9 +49,10 @@ unsigned pa_hashmap_size(pa_hashmap *h);
/* May be used to iterate through the hashmap. Initially the opaque
pointer *state has to be set to NULL. The hashmap may not be
- modified during iteration. The key of the entry is returned in
- *key, if key is non-NULL. After the last entry in the hashmap NULL
- is returned. */
+ modified during iteration -- except for deleting the current entry
+ via pa_hashmap_remove(). The key of the entry is returned in *key,
+ if key is non-NULL. After the last entry in the hashmap NULL is
+ returned. */
void *pa_hashmap_iterate(pa_hashmap *h, void **state, const void**key);
void *pa_hashmap_steal_first(pa_hashmap *h);
diff --git a/src/pulsecore/hook-list.c b/src/pulsecore/hook-list.c
index 91c2598b..0aac4759 100644
--- a/src/pulsecore/hook-list.c
+++ b/src/pulsecore/hook-list.c
@@ -1,80 +1,89 @@
-/* $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.
***/
-#include <pulsecore/hook-list.h>
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/macro.h>
+
+#include "hook-list.h"
void pa_hook_init(pa_hook *hook, void *data) {
- assert(hook);
+ pa_assert(hook);
PA_LLIST_HEAD_INIT(pa_hook_slot, hook->slots);
- hook->last = NULL;
- hook->n_dead = hook->firing = 0;
+ hook->n_dead = hook->n_firing = 0;
hook->data = data;
}
static void slot_free(pa_hook *hook, pa_hook_slot *slot) {
- assert(hook);
- assert(slot);
+ pa_assert(hook);
+ pa_assert(slot);
- if (hook->last == slot)
- hook->last = slot->prev;
-
PA_LLIST_REMOVE(pa_hook_slot, hook->slots, slot);
-
+
pa_xfree(slot);
}
void pa_hook_free(pa_hook *hook) {
- assert(hook);
- assert(!hook->firing);
+ pa_assert(hook);
+ pa_assert(hook->n_firing == 0);
while (hook->slots)
slot_free(hook, hook->slots);
-
+
pa_hook_init(hook, NULL);
}
-pa_hook_slot* pa_hook_connect(pa_hook *hook, pa_hook_cb_t cb, void *data) {
- pa_hook_slot *slot;
-
- assert(cb);
+pa_hook_slot* pa_hook_connect(pa_hook *hook, pa_hook_priority_t prio, pa_hook_cb_t cb, void *data) {
+ pa_hook_slot *slot, *where, *prev;
+
+ pa_assert(cb);
slot = pa_xnew(pa_hook_slot, 1);
slot->hook = hook;
- slot->dead = 0;
+ slot->dead = FALSE;
slot->callback = cb;
slot->data = data;
-
- PA_LLIST_INSERT_AFTER(pa_hook_slot, hook->slots, hook->last, slot);
- hook->last = slot;
-
+ slot->priority = prio;
+
+ prev = NULL;
+ for (where = hook->slots; where; where = where->next) {
+ if (prio < where->priority)
+ break;
+ prev = where;
+ }
+
+ PA_LLIST_INSERT_AFTER(pa_hook_slot, hook->slots, prev, slot);
+
return slot;
}
void pa_hook_slot_free(pa_hook_slot *slot) {
- assert(slot);
- assert(!slot->dead);
-
- if (slot->hook->firing > 0) {
- slot->dead = 1;
+ pa_assert(slot);
+ pa_assert(!slot->dead);
+
+ if (slot->hook->n_firing > 0) {
+ slot->dead = TRUE;
slot->hook->n_dead++;
} else
slot_free(slot->hook, slot);
@@ -83,30 +92,32 @@ void pa_hook_slot_free(pa_hook_slot *slot) {
pa_hook_result_t pa_hook_fire(pa_hook *hook, void *data) {
pa_hook_slot *slot, *next;
pa_hook_result_t result = PA_HOOK_OK;
-
- assert(hook);
- hook->firing ++;
+ pa_assert(hook);
+
+ hook->n_firing ++;
for (slot = hook->slots; slot; slot = slot->next) {
if (slot->dead)
continue;
-
+
if ((result = slot->callback(hook->data, data, slot->data)) != PA_HOOK_OK)
break;
}
-
- hook->firing --;
-
+
+ hook->n_firing --;
+ pa_assert(hook->n_firing >= 0);
+
for (slot = hook->slots; hook->n_dead > 0 && slot; slot = next) {
next = slot->next;
-
+
if (slot->dead) {
slot_free(hook, slot);
hook->n_dead--;
}
}
+ pa_assert(hook->n_dead == 0);
+
return result;
}
-
diff --git a/src/pulsecore/hook-list.h b/src/pulsecore/hook-list.h
index 67e5d1ae..cf85aca5 100644
--- a/src/pulsecore/hook-list.h
+++ b/src/pulsecore/hook-list.h
@@ -1,30 +1,31 @@
#ifndef foohooklistfoo
#define foohooklistfoo
-/* $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.
***/
-#include <pulsecore/llist.h>
#include <pulse/xmalloc.h>
-#include <pulsecore/gccmacro.h>
+#include <pulse/gccmacro.h>
+
+#include <pulsecore/llist.h>
typedef struct pa_hook_slot pa_hook_slot;
typedef struct pa_hook pa_hook;
@@ -35,14 +36,21 @@ typedef enum pa_hook_result {
PA_HOOK_CANCEL = -1
} pa_hook_result_t;
+typedef enum pa_hook_priority {
+ PA_HOOK_EARLY = -100,
+ PA_HOOK_NORMAL = 0,
+ PA_HOOK_LATE = 100
+} pa_hook_priority_t;
+
typedef pa_hook_result_t (*pa_hook_cb_t)(
void *hook_data,
void *call_data,
void *slot_data);
struct pa_hook_slot {
- int dead;
+ pa_bool_t dead;
pa_hook *hook;
+ pa_hook_priority_t priority;
pa_hook_cb_t callback;
void *data;
PA_LLIST_FIELDS(pa_hook_slot);
@@ -50,8 +58,7 @@ struct pa_hook_slot {
struct pa_hook {
PA_LLIST_HEAD(pa_hook_slot, slots);
- pa_hook_slot *last;
- int firing, n_dead;
+ int n_firing, n_dead;
void *data;
};
@@ -59,7 +66,7 @@ struct pa_hook {
void pa_hook_init(pa_hook *hook, void *data);
void pa_hook_free(pa_hook *hook);
-pa_hook_slot* pa_hook_connect(pa_hook *hook, pa_hook_cb_t, void *data);
+pa_hook_slot* pa_hook_connect(pa_hook *hook, pa_hook_priority_t prio, pa_hook_cb_t cb, void *data);
void pa_hook_slot_free(pa_hook_slot *slot);
pa_hook_result_t pa_hook_fire(pa_hook *hook, void *data);
diff --git a/src/pulsecore/idxset.c b/src/pulsecore/idxset.c
index ee0137a3..7c9520a4 100644
--- a/src/pulsecore/idxset.c
+++ b/src/pulsecore/idxset.c
@@ -1,18 +1,19 @@
-/* $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.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
@@ -24,36 +25,39 @@
#endif
#include <stdio.h>
-#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/flist.h>
#include "idxset.h"
-typedef struct idxset_entry {
+struct idxset_entry {
void *data;
uint32_t index;
unsigned hash_value;
struct idxset_entry *hash_prev, *hash_next;
struct idxset_entry* iterate_prev, *iterate_next;
-} idxset_entry;
+};
struct pa_idxset {
pa_hash_func_t hash_func;
pa_compare_func_t compare_func;
-
+
unsigned hash_table_size, n_entries;
- idxset_entry **hash_table, **array, *iterate_list_head, *iterate_list_tail;
+ struct idxset_entry **hash_table, **array, *iterate_list_head, *iterate_list_tail;
uint32_t index, start_index, array_size;
};
+PA_STATIC_FLIST_DECLARE(entries, 0, pa_xfree);
+
unsigned pa_idxset_string_hash_func(const void *p) {
unsigned hash = 0;
const char *c;
-
+
for (c = p; *c; c++)
hash = 31 * hash + *c;
@@ -79,7 +83,7 @@ pa_idxset* pa_idxset_new(pa_hash_func_t hash_func, pa_compare_func_t compare_fun
s->hash_func = hash_func ? hash_func : pa_idxset_trivial_hash_func;
s->compare_func = compare_func ? compare_func : pa_idxset_trivial_compare_func;
s->hash_table_size = 127;
- s->hash_table = pa_xnew0(idxset_entry*, s->hash_table_size);
+ s->hash_table = pa_xnew0(struct idxset_entry*, s->hash_table_size);
s->array = NULL;
s->array_size = 0;
s->index = 0;
@@ -92,15 +96,17 @@ pa_idxset* pa_idxset_new(pa_hash_func_t hash_func, pa_compare_func_t compare_fun
}
void pa_idxset_free(pa_idxset *s, void (*free_func) (void *p, void *userdata), void *userdata) {
- assert(s);
+ pa_assert(s);
while (s->iterate_list_head) {
- idxset_entry *e = s->iterate_list_head;
+ struct idxset_entry *e = s->iterate_list_head;
s->iterate_list_head = s->iterate_list_head->iterate_next;
-
+
if (free_func)
free_func(e->data, userdata);
- pa_xfree(e);
+
+ if (pa_flist_push(PA_STATIC_FLIST_GET(entries), e) < 0)
+ pa_xfree(e);
}
pa_xfree(s->hash_table);
@@ -108,10 +114,10 @@ void pa_idxset_free(pa_idxset *s, void (*free_func) (void *p, void *userdata), v
pa_xfree(s);
}
-static idxset_entry* hash_scan(pa_idxset *s, idxset_entry* e, const void *p) {
- assert(p);
+static struct idxset_entry* hash_scan(pa_idxset *s, struct idxset_entry* e, const void *p) {
+ pa_assert(p);
- assert(s->compare_func);
+ pa_assert(s->compare_func);
for (; e; e = e->hash_next)
if (s->compare_func(e->data, p) == 0)
return e;
@@ -121,8 +127,10 @@ static idxset_entry* hash_scan(pa_idxset *s, idxset_entry* e, const void *p) {
static void extend_array(pa_idxset *s, uint32_t idx) {
uint32_t i, j, l;
- idxset_entry** n;
- assert(idx >= s->start_index);
+ struct idxset_entry** n;
+
+ pa_assert(s);
+ pa_assert(idx >= s->start_index);
if (idx < s->start_index + s->array_size)
return;
@@ -132,47 +140,50 @@ static void extend_array(pa_idxset *s, uint32_t idx) {
break;
l = idx - s->start_index - i + 100;
- n = pa_xnew0(idxset_entry*, l);
-
+ n = pa_xnew0(struct idxset_entry*, l);
+
for (j = 0; j < s->array_size-i; j++)
n[j] = s->array[i+j];
pa_xfree(s->array);
-
+
s->array = n;
s->array_size = l;
s->start_index += i;
}
-static idxset_entry** array_index(pa_idxset*s, uint32_t idx) {
+static struct idxset_entry** array_index(pa_idxset*s, uint32_t idx) {
+ pa_assert(s);
+
if (idx >= s->start_index + s->array_size)
return NULL;
-
+
if (idx < s->start_index)
return NULL;
-
+
return s->array + idx - s->start_index;
}
int pa_idxset_put(pa_idxset*s, void *p, uint32_t *idx) {
unsigned h;
- idxset_entry *e, **a;
-
- assert(s);
- assert(p);
+ struct idxset_entry *e, **a;
+
+ pa_assert(s);
+ pa_assert(p);
- assert(s->hash_func);
+ pa_assert(s->hash_func);
h = s->hash_func(p) % s->hash_table_size;
- assert(s->hash_table);
+ pa_assert(s->hash_table);
if ((e = hash_scan(s, s->hash_table[h], p))) {
if (idx)
*idx = e->index;
-
+
return -1;
}
- e = pa_xmalloc(sizeof(idxset_entry));
+ if (!(e = pa_flist_pop(PA_STATIC_FLIST_GET(entries))))
+ e = pa_xnew(struct idxset_entry, 1);
e->data = p;
e->index = s->index++;
e->hash_value = h;
@@ -187,24 +198,24 @@ int pa_idxset_put(pa_idxset*s, void *p, uint32_t *idx) {
/* Insert into array */
extend_array(s, e->index);
a = array_index(s, e->index);
- assert(a && !*a);
+ pa_assert(a && !*a);
*a = e;
/* Insert into linked list */
e->iterate_next = NULL;
e->iterate_prev = s->iterate_list_tail;
if (s->iterate_list_tail) {
- assert(s->iterate_list_head);
+ pa_assert(s->iterate_list_head);
s->iterate_list_tail->iterate_next = e;
} else {
- assert(!s->iterate_list_head);
+ pa_assert(!s->iterate_list_head);
s->iterate_list_head = e;
}
s->iterate_list_tail = e;
-
+
s->n_entries++;
- assert(s->n_entries >= 1);
-
+ pa_assert(s->n_entries >= 1);
+
if (idx)
*idx = e->index;
@@ -212,9 +223,9 @@ int pa_idxset_put(pa_idxset*s, void *p, uint32_t *idx) {
}
void* pa_idxset_get_by_index(pa_idxset*s, uint32_t idx) {
- idxset_entry **a;
- assert(s);
-
+ struct idxset_entry **a;
+ pa_assert(s);
+
if (!(a = array_index(s, idx)))
return NULL;
@@ -226,13 +237,15 @@ void* pa_idxset_get_by_index(pa_idxset*s, uint32_t idx) {
void* pa_idxset_get_by_data(pa_idxset*s, const void *p, uint32_t *idx) {
unsigned h;
- idxset_entry *e;
- assert(s && p);
-
- assert(s->hash_func);
+ struct idxset_entry *e;
+
+ pa_assert(s);
+ pa_assert(p);
+
+ pa_assert(s->hash_func);
h = s->hash_func(p) % s->hash_table_size;
- assert(s->hash_table);
+ pa_assert(s->hash_table);
if (!(e = hash_scan(s, s->hash_table[h], p)))
return NULL;
@@ -242,21 +255,23 @@ void* pa_idxset_get_by_data(pa_idxset*s, const void *p, uint32_t *idx) {
return e->data;
}
-static void remove_entry(pa_idxset *s, idxset_entry *e) {
- idxset_entry **a;
- assert(s && e);
+static void remove_entry(pa_idxset *s, struct idxset_entry *e) {
+ struct idxset_entry **a;
+
+ pa_assert(s);
+ pa_assert(e);
/* Remove from array */
a = array_index(s, e->index);
- assert(a && *a && *a == e);
+ pa_assert(a && *a && *a == e);
*a = NULL;
-
+
/* Remove from linked list */
if (e->iterate_next)
e->iterate_next->iterate_prev = e->iterate_prev;
else
s->iterate_list_tail = e->iterate_prev;
-
+
if (e->iterate_prev)
e->iterate_prev->iterate_next = e->iterate_next;
else
@@ -271,17 +286,18 @@ static void remove_entry(pa_idxset *s, idxset_entry *e) {
else
s->hash_table[e->hash_value] = e->hash_next;
- pa_xfree(e);
+ if (pa_flist_push(PA_STATIC_FLIST_GET(entries), e) < 0)
+ pa_xfree(e);
- assert(s->n_entries >= 1);
+ pa_assert(s->n_entries >= 1);
s->n_entries--;
}
void* pa_idxset_remove_by_index(pa_idxset*s, uint32_t idx) {
- idxset_entry **a;
+ struct idxset_entry **a;
void *data;
-
- assert(s);
+
+ pa_assert(s);
if (!(a = array_index(s, idx)))
return NULL;
@@ -291,19 +307,21 @@ void* pa_idxset_remove_by_index(pa_idxset*s, uint32_t idx) {
data = (*a)->data;
remove_entry(s, *a);
-
- return data;
+
+ return data;
}
void* pa_idxset_remove_by_data(pa_idxset*s, const void *data, uint32_t *idx) {
- idxset_entry *e;
+ struct idxset_entry *e;
unsigned h;
void *r;
-
- assert(s->hash_func);
+
+ pa_assert(s);
+
+ pa_assert(s->hash_func);
h = s->hash_func(data) % s->hash_table_size;
- assert(s->hash_table);
+ pa_assert(s->hash_table);
if (!(e = hash_scan(s, s->hash_table[h], data)))
return NULL;
@@ -317,8 +335,10 @@ void* pa_idxset_remove_by_data(pa_idxset*s, const void *data, uint32_t *idx) {
}
void* pa_idxset_rrobin(pa_idxset *s, uint32_t *idx) {
- idxset_entry **a, *e = NULL;
- assert(s && idx);
+ struct idxset_entry **a, *e = NULL;
+
+ pa_assert(s);
+ pa_assert(idx);
if ((a = array_index(s, *idx)) && *a)
e = (*a)->iterate_next;
@@ -328,13 +348,13 @@ void* pa_idxset_rrobin(pa_idxset *s, uint32_t *idx) {
if (!e)
return NULL;
-
+
*idx = e->index;
return e->data;
}
void* pa_idxset_first(pa_idxset *s, uint32_t *idx) {
- assert(s);
+ pa_assert(s);
if (!s->iterate_list_head)
return NULL;
@@ -345,13 +365,14 @@ void* pa_idxset_first(pa_idxset *s, uint32_t *idx) {
}
void *pa_idxset_next(pa_idxset *s, uint32_t *idx) {
- idxset_entry **a, *e = NULL;
- assert(s);
- assert(idx);
+ struct idxset_entry **a, *e = NULL;
+
+ pa_assert(s);
+ pa_assert(idx);
if ((a = array_index(s, *idx)) && *a)
e = (*a)->iterate_next;
-
+
if (e) {
*idx = e->index;
return e->data;
@@ -362,13 +383,15 @@ void *pa_idxset_next(pa_idxset *s, uint32_t *idx) {
}
int pa_idxset_foreach(pa_idxset*s, int (*func)(void *p, uint32_t idx, int *del, void*userdata), void *userdata) {
- idxset_entry *e;
- assert(s && func);
+ struct idxset_entry *e;
+
+ pa_assert(s);
+ pa_assert(func);
e = s->iterate_list_head;
while (e) {
int del = 0, r;
- idxset_entry *n = e->iterate_next;
+ struct idxset_entry *n = e->iterate_next;
r = func(e->data, e->index, &del, userdata);
@@ -380,17 +403,19 @@ int pa_idxset_foreach(pa_idxset*s, int (*func)(void *p, uint32_t idx, int *del,
e = n;
}
-
+
return 0;
}
unsigned pa_idxset_size(pa_idxset*s) {
- assert(s);
+ pa_assert(s);
+
return s->n_entries;
}
int pa_idxset_isempty(pa_idxset *s) {
- assert(s);
+ pa_assert(s);
+
return s->n_entries == 0;
}
diff --git a/src/pulsecore/idxset.h b/src/pulsecore/idxset.h
index 1765e843..0a4e528e 100644
--- a/src/pulsecore/idxset.h
+++ b/src/pulsecore/idxset.h
@@ -1,21 +1,22 @@
#ifndef fooidxsethfoo
#define fooidxsethfoo
-/* $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.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
@@ -41,11 +42,6 @@ int pa_idxset_trivial_compare_func(const void *a, const void *b);
unsigned pa_idxset_string_hash_func(const void *p);
int pa_idxset_string_compare_func(const void *a, const void *b);
-#define PA_PTR_TO_UINT(p) ((unsigned int) (unsigned long) (p))
-#define PA_UINT_TO_PTR(u) ((void*) (unsigned long) (u))
-#define PA_PTR_TO_UINT32(p) ((uint32_t) PA_PTR_TO_UINT(p))
-#define PA_UINT32_TO_PTR(u) PA_UINT_TO_PTR(u)
-
typedef unsigned (*pa_hash_func_t)(const void *p);
typedef int (*pa_compare_func_t)(const void *a, const void *b);
diff --git a/src/pulsecore/inet_ntop.c b/src/pulsecore/inet_ntop.c
index 483c3e26..87551232 100644
--- a/src/pulsecore/inet_ntop.c
+++ b/src/pulsecore/inet_ntop.c
@@ -1,18 +1,18 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
+ Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
@@ -25,7 +25,6 @@
#include <stdio.h>
#include <errno.h>
-#include <assert.h>
#ifndef HAVE_INET_NTOP
@@ -45,7 +44,7 @@ const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt) {
switch (af) {
case AF_INET:
- snprintf(dst, cnt, "%d.%d.%d.%d",
+ pa_snprintf(dst, cnt, "%d.%d.%d.%d",
#ifdef WORDS_BIGENDIAN
(int)(in->s_addr >> 24) & 0xff,
(int)(in->s_addr >> 16) & 0xff,
@@ -59,7 +58,7 @@ const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt) {
#endif
break;
case AF_INET6:
- snprintf(dst, cnt, "%x:%x:%x:%x:%x:%x:%x:%x",
+ pa_snprintf(dst, cnt, "%x:%x:%x:%x:%x:%x:%x:%x",
in6->s6_addr[ 0] << 8 | in6->s6_addr[ 1],
in6->s6_addr[ 2] << 8 | in6->s6_addr[ 3],
in6->s6_addr[ 4] << 8 | in6->s6_addr[ 5],
diff --git a/src/pulsecore/inet_pton.c b/src/pulsecore/inet_pton.c
index 7b6bbc31..d191e550 100644
--- a/src/pulsecore/inet_pton.c
+++ b/src/pulsecore/inet_pton.c
@@ -1,18 +1,18 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
+ Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
@@ -25,7 +25,6 @@
#include <stdio.h>
#include <errno.h>
-#include <assert.h>
#ifndef HAVE_INET_PTON
diff --git a/src/pulsecore/iochannel.c b/src/pulsecore/iochannel.c
index af732c26..b40c9815 100644
--- a/src/pulsecore/iochannel.c
+++ b/src/pulsecore/iochannel.c
@@ -1,18 +1,19 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
+ Copyright 2004-2006 Lennart Poettering
+ Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
@@ -24,7 +25,6 @@
#endif
#include <stdlib.h>
-#include <assert.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
@@ -44,6 +44,7 @@
#include <pulsecore/core-util.h>
#include <pulsecore/socket-util.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
#include "iochannel.h"
@@ -54,23 +55,23 @@ struct pa_iochannel {
pa_iochannel_cb_t callback;
void*userdata;
-
- int readable;
- int writable;
- int hungup;
-
- int no_close;
+
+ pa_bool_t readable;
+ pa_bool_t writable;
+ pa_bool_t hungup;
+
+ pa_bool_t no_close;
pa_io_event* input_event, *output_event;
};
static void enable_mainloop_sources(pa_iochannel *io) {
- assert(io);
+ pa_assert(io);
if (io->input_event == io->output_event && io->input_event) {
pa_io_event_flags_t f = PA_IO_EVENT_NULL;
- assert(io->input_event);
-
+ pa_assert(io->input_event);
+
if (!io->readable)
f |= PA_IO_EVENT_INPUT;
if (!io->writable)
@@ -87,33 +88,33 @@ static void enable_mainloop_sources(pa_iochannel *io) {
static void callback(pa_mainloop_api* m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
pa_iochannel *io = userdata;
- int changed = 0;
-
- assert(m);
- assert(e);
- assert(fd >= 0);
- assert(userdata);
+ pa_bool_t changed = FALSE;
+
+ pa_assert(m);
+ pa_assert(e);
+ pa_assert(fd >= 0);
+ pa_assert(userdata);
if ((f & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) && !io->hungup) {
- io->hungup = 1;
- changed = 1;
+ io->hungup = TRUE;
+ changed = TRUE;
}
if ((f & PA_IO_EVENT_INPUT) && !io->readable) {
- io->readable = 1;
- changed = 1;
- assert(e == io->input_event);
+ io->readable = TRUE;
+ changed = TRUE;
+ pa_assert(e == io->input_event);
}
-
+
if ((f & PA_IO_EVENT_OUTPUT) && !io->writable) {
- io->writable = 1;
- changed = 1;
- assert(e == io->output_event);
+ io->writable = TRUE;
+ changed = TRUE;
+ pa_assert(e == io->output_event);
}
if (changed) {
enable_mainloop_sources(io);
-
+
if (io->callback)
io->callback(io, io->userdata);
}
@@ -121,9 +122,9 @@ static void callback(pa_mainloop_api* m, pa_io_event *e, int fd, pa_io_event_fla
pa_iochannel* pa_iochannel_new(pa_mainloop_api*m, int ifd, int ofd) {
pa_iochannel *io;
-
- assert(m);
- assert(ifd >= 0 || ofd >= 0);
+
+ pa_assert(m);
+ pa_assert(ifd >= 0 || ofd >= 0);
io = pa_xnew(pa_iochannel, 1);
io->ifd = ifd;
@@ -133,26 +134,26 @@ pa_iochannel* pa_iochannel_new(pa_mainloop_api*m, int ifd, int ofd) {
io->userdata = NULL;
io->callback = NULL;
- io->readable = 0;
- io->writable = 0;
- io->hungup = 0;
- io->no_close = 0;
+ io->readable = FALSE;
+ io->writable = FALSE;
+ io->hungup = FALSE;
+ io->no_close = FALSE;
io->input_event = io->output_event = NULL;
if (ifd == ofd) {
- assert(ifd >= 0);
- pa_make_nonblock_fd(io->ifd);
+ pa_assert(ifd >= 0);
+ pa_make_fd_nonblock(io->ifd);
io->input_event = io->output_event = m->io_new(m, ifd, PA_IO_EVENT_INPUT|PA_IO_EVENT_OUTPUT, callback, io);
} else {
if (ifd >= 0) {
- pa_make_nonblock_fd(io->ifd);
+ pa_make_fd_nonblock(io->ifd);
io->input_event = m->io_new(m, ifd, PA_IO_EVENT_INPUT, callback, io);
}
if (ofd >= 0) {
- pa_make_nonblock_fd(io->ofd);
+ pa_make_fd_nonblock(io->ofd);
io->output_event = m->io_new(m, ofd, PA_IO_EVENT_OUTPUT, callback, io);
}
}
@@ -161,54 +162,52 @@ pa_iochannel* pa_iochannel_new(pa_mainloop_api*m, int ifd, int ofd) {
}
void pa_iochannel_free(pa_iochannel*io) {
- assert(io);
+ pa_assert(io);
if (io->input_event)
io->mainloop->io_free(io->input_event);
-
+
if (io->output_event && (io->output_event != io->input_event))
io->mainloop->io_free(io->output_event);
if (!io->no_close) {
if (io->ifd >= 0)
-
- close(io->ifd);
+ pa_close(io->ifd);
if (io->ofd >= 0 && io->ofd != io->ifd)
- close(io->ofd);
+ pa_close(io->ofd);
}
-
+
pa_xfree(io);
}
-int pa_iochannel_is_readable(pa_iochannel*io) {
- assert(io);
-
+pa_bool_t pa_iochannel_is_readable(pa_iochannel*io) {
+ pa_assert(io);
+
return io->readable || io->hungup;
}
-int pa_iochannel_is_writable(pa_iochannel*io) {
- assert(io);
-
+pa_bool_t pa_iochannel_is_writable(pa_iochannel*io) {
+ pa_assert(io);
+
return io->writable && !io->hungup;
}
-int pa_iochannel_is_hungup(pa_iochannel*io) {
- assert(io);
-
+pa_bool_t pa_iochannel_is_hungup(pa_iochannel*io) {
+ pa_assert(io);
+
return io->hungup;
}
ssize_t pa_iochannel_write(pa_iochannel*io, const void*data, size_t l) {
ssize_t r;
-
- assert(io);
- assert(data);
- assert(l);
- assert(io->ofd >= 0);
-
- r = pa_write(io->ofd, data, l, &io->ofd_type);
- if (r >= 0) {
- io->writable = 0;
+
+ pa_assert(io);
+ pa_assert(data);
+ pa_assert(l);
+ pa_assert(io->ofd >= 0);
+
+ if ((r = pa_write(io->ofd, data, l, &io->ofd_type)) >= 0) {
+ io->writable = FALSE;
enable_mainloop_sources(io);
}
@@ -217,14 +216,13 @@ ssize_t pa_iochannel_write(pa_iochannel*io, const void*data, size_t l) {
ssize_t pa_iochannel_read(pa_iochannel*io, void*data, size_t l) {
ssize_t r;
-
- assert(io);
- assert(data);
- assert(io->ifd >= 0);
-
- r = pa_read(io->ifd, data, l, &io->ifd_type);
- if (r >= 0) {
- io->readable = 0;
+
+ pa_assert(io);
+ pa_assert(data);
+ pa_assert(io->ifd >= 0);
+
+ if ((r = pa_read(io->ifd, data, l, &io->ifd_type)) >= 0) {
+ io->readable = FALSE;
enable_mainloop_sources(io);
}
@@ -233,16 +231,16 @@ ssize_t pa_iochannel_read(pa_iochannel*io, void*data, size_t l) {
#ifdef HAVE_CREDS
-int pa_iochannel_creds_supported(pa_iochannel *io) {
+pa_bool_t pa_iochannel_creds_supported(pa_iochannel *io) {
struct sockaddr_un sa;
socklen_t l;
-
- assert(io);
- assert(io->ifd >= 0);
- assert(io->ofd == io->ifd);
+
+ pa_assert(io);
+ pa_assert(io->ifd >= 0);
+ pa_assert(io->ofd == io->ifd);
l = sizeof(sa);
-
+
if (getsockname(io->ifd, (struct sockaddr*) &sa, &l) < 0)
return 0;
@@ -252,9 +250,9 @@ int pa_iochannel_creds_supported(pa_iochannel *io) {
int pa_iochannel_creds_enable(pa_iochannel *io) {
int t = 1;
- assert(io);
- assert(io->ifd >= 0);
-
+ pa_assert(io);
+ pa_assert(io->ifd >= 0);
+
if (setsockopt(io->ifd, SOL_SOCKET, SO_PASSCRED, &t, sizeof(t)) < 0) {
pa_log_error("setsockopt(SOL_SOCKET, SO_PASSCRED): %s", pa_cstrerror(errno));
return -1;
@@ -267,26 +265,27 @@ ssize_t pa_iochannel_write_with_creds(pa_iochannel*io, const void*data, size_t l
ssize_t r;
struct msghdr mh;
struct iovec iov;
- uint8_t cmsg_data[CMSG_SPACE(sizeof(struct ucred))];
+ union {
+ struct cmsghdr hdr;
+ uint8_t data[CMSG_SPACE(sizeof(struct ucred))];
+ } cmsg;
struct ucred *u;
- struct cmsghdr *cmsg;
-
- assert(io);
- assert(data);
- assert(l);
- assert(io->ofd >= 0);
+
+ pa_assert(io);
+ pa_assert(data);
+ pa_assert(l);
+ pa_assert(io->ofd >= 0);
memset(&iov, 0, sizeof(iov));
iov.iov_base = (void*) data;
iov.iov_len = l;
- memset(cmsg_data, 0, sizeof(cmsg_data));
- cmsg = (struct cmsghdr*) cmsg_data;
- cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_CREDENTIALS;
+ memset(&cmsg, 0, sizeof(cmsg));
+ cmsg.hdr.cmsg_len = CMSG_LEN(sizeof(struct ucred));
+ cmsg.hdr.cmsg_level = SOL_SOCKET;
+ cmsg.hdr.cmsg_type = SCM_CREDENTIALS;
- u = (struct ucred*) CMSG_DATA(cmsg);
+ u = (struct ucred*) CMSG_DATA(&cmsg.hdr);
u->pid = getpid();
if (ucred) {
@@ -296,121 +295,143 @@ ssize_t pa_iochannel_write_with_creds(pa_iochannel*io, const void*data, size_t l
u->uid = getuid();
u->gid = getgid();
}
-
+
memset(&mh, 0, sizeof(mh));
mh.msg_name = NULL;
mh.msg_namelen = 0;
mh.msg_iov = &iov;
mh.msg_iovlen = 1;
- mh.msg_control = cmsg_data;
- mh.msg_controllen = sizeof(cmsg_data);
+ mh.msg_control = &cmsg;
+ mh.msg_controllen = sizeof(cmsg);
mh.msg_flags = 0;
if ((r = sendmsg(io->ofd, &mh, MSG_NOSIGNAL)) >= 0) {
- io->writable = 0;
+ io->writable = FALSE;
enable_mainloop_sources(io);
}
return r;
}
-ssize_t pa_iochannel_read_with_creds(pa_iochannel*io, void*data, size_t l, pa_creds *creds, int *creds_valid) {
+ssize_t pa_iochannel_read_with_creds(pa_iochannel*io, void*data, size_t l, pa_creds *creds, pa_bool_t *creds_valid) {
ssize_t r;
struct msghdr mh;
struct iovec iov;
- uint8_t cmsg_data[CMSG_SPACE(sizeof(struct ucred))];
-
- assert(io);
- assert(data);
- assert(l);
- assert(io->ifd >= 0);
- assert(creds);
- assert(creds_valid);
+ union {
+ struct cmsghdr hdr;
+ uint8_t data[CMSG_SPACE(sizeof(struct ucred))];
+ } cmsg;
+
+ pa_assert(io);
+ pa_assert(data);
+ pa_assert(l);
+ pa_assert(io->ifd >= 0);
+ pa_assert(creds);
+ pa_assert(creds_valid);
memset(&iov, 0, sizeof(iov));
iov.iov_base = data;
iov.iov_len = l;
- memset(cmsg_data, 0, sizeof(cmsg_data));
+ memset(&cmsg, 0, sizeof(cmsg));
memset(&mh, 0, sizeof(mh));
mh.msg_name = NULL;
mh.msg_namelen = 0;
mh.msg_iov = &iov;
mh.msg_iovlen = 1;
- mh.msg_control = cmsg_data;
- mh.msg_controllen = sizeof(cmsg_data);
+ mh.msg_control = &cmsg;
+ mh.msg_controllen = sizeof(cmsg);
mh.msg_flags = 0;
if ((r = recvmsg(io->ifd, &mh, 0)) >= 0) {
- struct cmsghdr *cmsg;
+ struct cmsghdr *cmh;
*creds_valid = 0;
-
- for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg)) {
-
- if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDENTIALS) {
+
+ for (cmh = CMSG_FIRSTHDR(&mh); cmh; cmh = CMSG_NXTHDR(&mh, cmh)) {
+
+ if (cmh->cmsg_level == SOL_SOCKET && cmh->cmsg_type == SCM_CREDENTIALS) {
struct ucred u;
- assert(cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred)));
- memcpy(&u, CMSG_DATA(cmsg), sizeof(struct ucred));
+ pa_assert(cmh->cmsg_len == CMSG_LEN(sizeof(struct ucred)));
+ memcpy(&u, CMSG_DATA(cmh), sizeof(struct ucred));
creds->gid = u.gid;
creds->uid = u.uid;
- *creds_valid = 1;
+ *creds_valid = TRUE;
break;
}
}
- io->readable = 0;
+ io->readable = FALSE;
enable_mainloop_sources(io);
}
-
+
return r;
}
#endif /* HAVE_CREDS */
void pa_iochannel_set_callback(pa_iochannel*io, pa_iochannel_cb_t _callback, void *userdata) {
- assert(io);
-
+ pa_assert(io);
+
io->callback = _callback;
io->userdata = userdata;
}
-void pa_iochannel_set_noclose(pa_iochannel*io, int b) {
- assert(io);
-
- io->no_close = b;
+void pa_iochannel_set_noclose(pa_iochannel*io, pa_bool_t b) {
+ pa_assert(io);
+
+ io->no_close = !!b;
}
void pa_iochannel_socket_peer_to_string(pa_iochannel*io, char*s, size_t l) {
- assert(io);
- assert(s);
- assert(l);
-
+ pa_assert(io);
+ pa_assert(s);
+ pa_assert(l);
+
pa_socket_peer_to_string(io->ifd, s, l);
}
int pa_iochannel_socket_set_rcvbuf(pa_iochannel *io, size_t l) {
- assert(io);
-
+ pa_assert(io);
+
return pa_socket_set_rcvbuf(io->ifd, l);
}
int pa_iochannel_socket_set_sndbuf(pa_iochannel *io, size_t l) {
- assert(io);
-
+ pa_assert(io);
+
return pa_socket_set_sndbuf(io->ofd, l);
}
pa_mainloop_api* pa_iochannel_get_mainloop_api(pa_iochannel *io) {
- assert(io);
-
+ pa_assert(io);
+
return io->mainloop;
}
int pa_iochannel_get_recv_fd(pa_iochannel *io) {
- assert(io);
+ pa_assert(io);
return io->ifd;
}
+
+int pa_iochannel_get_send_fd(pa_iochannel *io) {
+ pa_assert(io);
+
+ return io->ofd;
+}
+
+pa_bool_t pa_iochannel_socket_is_local(pa_iochannel *io) {
+ pa_assert(io);
+
+ if (pa_socket_is_local(io->ifd))
+ return TRUE;
+
+ if (io->ifd != io->ofd)
+ if (pa_socket_is_local(io->ofd))
+ return TRUE;
+
+ return FALSE;
+}
diff --git a/src/pulsecore/iochannel.h b/src/pulsecore/iochannel.h
index 0e6d6d3a..9050df90 100644
--- a/src/pulsecore/iochannel.h
+++ b/src/pulsecore/iochannel.h
@@ -1,31 +1,37 @@
#ifndef fooiochannelhfoo
#define fooiochannelhfoo
-/* $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.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
+#ifndef PACKAGE
+#error "Please include config.h before including this file!"
+#endif
+
#include <sys/types.h>
#include <pulse/mainloop-api.h>
#include <pulsecore/creds.h>
+#include <pulsecore/macro.h>
/* A wrapper around UNIX file descriptors for attaching them to the a
main event loop. Everytime new data may be read or be written to
@@ -51,20 +57,20 @@ ssize_t pa_iochannel_write(pa_iochannel*io, const void*data, size_t l);
ssize_t pa_iochannel_read(pa_iochannel*io, void*data, size_t l);
#ifdef HAVE_CREDS
-int pa_iochannel_creds_supported(pa_iochannel *io);
+pa_bool_t pa_iochannel_creds_supported(pa_iochannel *io);
int pa_iochannel_creds_enable(pa_iochannel *io);
ssize_t pa_iochannel_write_with_creds(pa_iochannel*io, const void*data, size_t l, const pa_creds *ucred);
-ssize_t pa_iochannel_read_with_creds(pa_iochannel*io, void*data, size_t l, pa_creds *ucred, int *creds_valid);
+ssize_t pa_iochannel_read_with_creds(pa_iochannel*io, void*data, size_t l, pa_creds *ucred, pa_bool_t *creds_valid);
#endif
-int pa_iochannel_is_readable(pa_iochannel*io);
-int pa_iochannel_is_writable(pa_iochannel*io);
-int pa_iochannel_is_hungup(pa_iochannel*io);
+pa_bool_t pa_iochannel_is_readable(pa_iochannel*io);
+pa_bool_t pa_iochannel_is_writable(pa_iochannel*io);
+pa_bool_t pa_iochannel_is_hungup(pa_iochannel*io);
/* Don't close the file descirptors when the io channel is freed. By
* default the file descriptors are closed. */
-void pa_iochannel_set_noclose(pa_iochannel*io, int b);
+void pa_iochannel_set_noclose(pa_iochannel*io, pa_bool_t b);
/* Set the callback function that is called whenever data becomes available for read or write */
typedef void (*pa_iochannel_cb_t)(pa_iochannel*io, void *userdata);
@@ -77,8 +83,11 @@ void pa_iochannel_socket_peer_to_string(pa_iochannel*io, char*s, size_t l);
int pa_iochannel_socket_set_rcvbuf(pa_iochannel*io, size_t l);
int pa_iochannel_socket_set_sndbuf(pa_iochannel*io, size_t l);
+pa_bool_t pa_iochannel_socket_is_local(pa_iochannel *io);
+
pa_mainloop_api* pa_iochannel_get_mainloop_api(pa_iochannel *io);
int pa_iochannel_get_recv_fd(pa_iochannel *io);
+int pa_iochannel_get_send_fd(pa_iochannel *io);
#endif
diff --git a/src/pulsecore/ioline.c b/src/pulsecore/ioline.c
index a3bca22f..90afaafd 100644
--- a/src/pulsecore/ioline.c
+++ b/src/pulsecore/ioline.c
@@ -1,18 +1,18 @@
-/* $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
@@ -25,14 +25,16 @@
#include <errno.h>
#include <stdio.h>
-#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <pulse/xmalloc.h>
+#include <pulsecore/winsock.h>
#include <pulsecore/core-error.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/refcnt.h>
#include "ioline.h"
@@ -40,11 +42,11 @@
#define READ_SIZE (1024)
struct pa_ioline {
+ PA_REFCNT_DECLARE;
+
pa_iochannel *io;
pa_defer_event *defer_event;
pa_mainloop_api *mainloop;
- int ref;
- int dead;
char *wbuf;
size_t wbuf_length, wbuf_index, wbuf_valid_length;
@@ -52,10 +54,11 @@ struct pa_ioline {
char *rbuf;
size_t rbuf_length, rbuf_index, rbuf_valid_length;
- void (*callback)(pa_ioline*io, const char *s, void *userdata);
+ pa_ioline_cb_t callback;
void *userdata;
- int defer_close;
+ pa_bool_t dead:1;
+ pa_bool_t defer_close:1;
};
static void io_callback(pa_iochannel*io, void *userdata);
@@ -63,11 +66,11 @@ static void defer_callback(pa_mainloop_api*m, pa_defer_event*e, void *userdata);
pa_ioline* pa_ioline_new(pa_iochannel *io) {
pa_ioline *l;
- assert(io);
-
+ pa_assert(io);
+
l = pa_xnew(pa_ioline, 1);
+ PA_REFCNT_INIT(l);
l->io = io;
- l->dead = 0;
l->wbuf = NULL;
l->wbuf_length = l->wbuf_index = l->wbuf_valid_length = 0;
@@ -77,22 +80,22 @@ pa_ioline* pa_ioline_new(pa_iochannel *io) {
l->callback = NULL;
l->userdata = NULL;
- l->ref = 1;
l->mainloop = pa_iochannel_get_mainloop_api(io);
l->defer_event = l->mainloop->defer_new(l->mainloop, defer_callback, l);
l->mainloop->defer_enable(l->defer_event, 0);
- l->defer_close = 0;
-
+ l->dead = FALSE;
+ l->defer_close = FALSE;
+
pa_iochannel_set_callback(io, io_callback, l);
-
+
return l;
}
static void ioline_free(pa_ioline *l) {
- assert(l);
+ pa_assert(l);
if (l->io)
pa_iochannel_free(l->io);
@@ -106,27 +109,27 @@ static void ioline_free(pa_ioline *l) {
}
void pa_ioline_unref(pa_ioline *l) {
- assert(l);
- assert(l->ref >= 1);
+ pa_assert(l);
+ pa_assert(PA_REFCNT_VALUE(l) >= 1);
- if ((--l->ref) <= 0)
+ if (PA_REFCNT_DEC(l) <= 0)
ioline_free(l);
}
pa_ioline* pa_ioline_ref(pa_ioline *l) {
- assert(l);
- assert(l->ref >= 1);
+ pa_assert(l);
+ pa_assert(PA_REFCNT_VALUE(l) >= 1);
- l->ref++;
+ PA_REFCNT_INC(l);
return l;
}
void pa_ioline_close(pa_ioline *l) {
- assert(l);
- assert(l->ref >= 1);
+ pa_assert(l);
+ pa_assert(PA_REFCNT_VALUE(l) >= 1);
+
+ l->dead = TRUE;
- l->dead = 1;
-
if (l->io) {
pa_iochannel_free(l->io);
l->io = NULL;
@@ -143,41 +146,43 @@ void pa_ioline_close(pa_ioline *l) {
void pa_ioline_puts(pa_ioline *l, const char *c) {
size_t len;
-
- assert(l);
- assert(l->ref >= 1);
- assert(c);
+
+ pa_assert(l);
+ pa_assert(PA_REFCNT_VALUE(l) >= 1);
+ pa_assert(c);
if (l->dead)
return;
-
+
len = strlen(c);
if (len > BUFFER_LIMIT - l->wbuf_valid_length)
len = BUFFER_LIMIT - l->wbuf_valid_length;
if (len) {
- assert(l->wbuf_length >= l->wbuf_valid_length);
-
+ pa_assert(l->wbuf_length >= l->wbuf_valid_length);
+
/* In case the allocated buffer is too small, enlarge it. */
if (l->wbuf_valid_length + len > l->wbuf_length) {
size_t n = l->wbuf_valid_length+len;
- char *new = pa_xmalloc(n);
+ char *new = pa_xnew(char, n);
+
if (l->wbuf) {
memcpy(new, l->wbuf+l->wbuf_index, l->wbuf_valid_length);
pa_xfree(l->wbuf);
}
+
l->wbuf = new;
l->wbuf_length = n;
l->wbuf_index = 0;
} else if (l->wbuf_index + l->wbuf_valid_length + len > l->wbuf_length) {
-
+
/* In case the allocated buffer fits, but the current index is too far from the start, move it to the front. */
memmove(l->wbuf, l->wbuf+l->wbuf_index, l->wbuf_valid_length);
l->wbuf_index = 0;
}
-
- assert(l->wbuf_index + l->wbuf_valid_length + len <= l->wbuf_length);
-
+
+ pa_assert(l->wbuf_index + l->wbuf_valid_length + len <= l->wbuf_length);
+
/* Append the new string */
memcpy(l->wbuf + l->wbuf_index + l->wbuf_valid_length, c, len);
l->wbuf_valid_length += len;
@@ -186,18 +191,21 @@ void pa_ioline_puts(pa_ioline *l, const char *c) {
}
}
-void pa_ioline_set_callback(pa_ioline*l, void (*callback)(pa_ioline*io, const char *s, void *userdata), void *userdata) {
- assert(l);
- assert(l->ref >= 1);
-
+void pa_ioline_set_callback(pa_ioline*l, pa_ioline_cb_t callback, void *userdata) {
+ pa_assert(l);
+ pa_assert(PA_REFCNT_VALUE(l) >= 1);
+
+ if (l->dead)
+ return;
+
l->callback = callback;
l->userdata = userdata;
}
-static void failure(pa_ioline *l, int process_leftover) {
- assert(l);
- assert(l->ref >= 1);
- assert(!l->dead);
+static void failure(pa_ioline *l, pa_bool_t process_leftover) {
+ pa_assert(l);
+ pa_assert(PA_REFCNT_VALUE(l) >= 1);
+ pa_assert(!l->dead);
if (process_leftover && l->rbuf_valid_length > 0) {
/* Pass the last missing bit to the client */
@@ -213,22 +221,24 @@ static void failure(pa_ioline *l, int process_leftover) {
l->callback(l, NULL, l->userdata);
l->callback = NULL;
}
-
+
pa_ioline_close(l);
}
static void scan_for_lines(pa_ioline *l, size_t skip) {
- assert(l && l->ref >= 1 && skip < l->rbuf_valid_length);
+ pa_assert(l);
+ pa_assert(PA_REFCNT_VALUE(l) >= 1);
+ pa_assert(skip < l->rbuf_valid_length);
while (!l->dead && l->rbuf_valid_length > skip) {
char *e, *p;
size_t m;
-
+
if (!(e = memchr(l->rbuf + l->rbuf_index + skip, '\n', l->rbuf_valid_length - skip)))
break;
*e = 0;
-
+
p = l->rbuf + l->rbuf_index;
m = strlen(p);
@@ -240,7 +250,7 @@ static void scan_for_lines(pa_ioline *l, size_t skip) {
l->rbuf_index = 0;
if (l->callback)
- l->callback(l, p, l->userdata);
+ l->callback(l, pa_strip_nl(p), l->userdata);
skip = 0;
}
@@ -253,95 +263,109 @@ static void scan_for_lines(pa_ioline *l, size_t skip) {
static int do_write(pa_ioline *l);
static int do_read(pa_ioline *l) {
- assert(l && l->ref >= 1);
+ pa_assert(l);
+ pa_assert(PA_REFCNT_VALUE(l) >= 1);
while (!l->dead && pa_iochannel_is_readable(l->io)) {
ssize_t r;
size_t len;
len = l->rbuf_length - l->rbuf_index - l->rbuf_valid_length;
-
+
/* Check if we have to enlarge the read buffer */
if (len < READ_SIZE) {
size_t n = l->rbuf_valid_length+READ_SIZE;
-
+
if (n >= BUFFER_LIMIT)
n = BUFFER_LIMIT;
-
+
if (l->rbuf_length >= n) {
/* The current buffer is large enough, let's just move the data to the front */
if (l->rbuf_valid_length)
memmove(l->rbuf, l->rbuf+l->rbuf_index, l->rbuf_valid_length);
} else {
/* Enlarge the buffer */
- char *new = pa_xmalloc(n);
+ char *new = pa_xnew(char, n);
if (l->rbuf_valid_length)
memcpy(new, l->rbuf+l->rbuf_index, l->rbuf_valid_length);
pa_xfree(l->rbuf);
l->rbuf = new;
l->rbuf_length = n;
}
-
+
l->rbuf_index = 0;
}
-
+
len = l->rbuf_length - l->rbuf_index - l->rbuf_valid_length;
-
- assert(len >= READ_SIZE);
-
+
+ pa_assert(len >= READ_SIZE);
+
/* Read some data */
if ((r = pa_iochannel_read(l->io, l->rbuf+l->rbuf_index+l->rbuf_valid_length, len)) <= 0) {
- if (r < 0) {
+
+ if (r < 0 && errno == EAGAIN)
+ return 0;
+
+ if (r < 0 && errno != ECONNRESET) {
pa_log("read(): %s", pa_cstrerror(errno));
- failure(l, 0);
+ failure(l, FALSE);
} else
- failure(l, 1);
-
+ failure(l, TRUE);
+
return -1;
}
-
+
l->rbuf_valid_length += r;
-
+
/* Look if a line has been terminated in the newly read data */
scan_for_lines(l, l->rbuf_valid_length - r);
}
-
+
return 0;
}
/* Try to flush the buffer */
static int do_write(pa_ioline *l) {
ssize_t r;
- assert(l && l->ref >= 1);
+
+ pa_assert(l);
+ pa_assert(PA_REFCNT_VALUE(l) >= 1);
while (!l->dead && pa_iochannel_is_writable(l->io) && l->wbuf_valid_length) {
-
- if ((r = pa_iochannel_write(l->io, l->wbuf+l->wbuf_index, l->wbuf_valid_length)) < 0) {
- pa_log("write(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");
- failure(l, 0);
+
+ if ((r = pa_iochannel_write(l->io, l->wbuf+l->wbuf_index, l->wbuf_valid_length)) <= 0) {
+
+ if (r < 0 && errno == EAGAIN)
+ return 0;
+
+ if (r < 0 && errno != EPIPE)
+ pa_log("write(): %s", pa_cstrerror(errno));
+
+ failure(l, FALSE);
+
return -1;
}
-
+
l->wbuf_index += r;
l->wbuf_valid_length -= r;
-
+
/* A shortcut for the next time */
if (l->wbuf_valid_length == 0)
l->wbuf_index = 0;
}
-
+
return 0;
}
/* Try to flush read/write data */
static void do_work(pa_ioline *l) {
- assert(l);
- assert(l->ref >= 1);
+ pa_assert(l);
+ pa_assert(PA_REFCNT_VALUE(l) >= 1);
pa_ioline_ref(l);
l->mainloop->defer_enable(l->defer_event, 0);
-
+
if (!l->dead)
do_read(l);
@@ -349,30 +373,37 @@ static void do_work(pa_ioline *l) {
do_write(l);
if (l->defer_close && !l->wbuf_valid_length)
- failure(l, 1);
+ failure(l, TRUE);
pa_ioline_unref(l);
}
static void io_callback(pa_iochannel*io, void *userdata) {
pa_ioline *l = userdata;
- assert(io && l && l->ref >= 1);
+
+ pa_assert(io);
+ pa_assert(l);
+ pa_assert(PA_REFCNT_VALUE(l) >= 1);
do_work(l);
}
static void defer_callback(pa_mainloop_api*m, pa_defer_event*e, void *userdata) {
pa_ioline *l = userdata;
- assert(l && l->ref >= 1 && l->mainloop == m && l->defer_event == e);
+
+ pa_assert(l);
+ pa_assert(PA_REFCNT_VALUE(l) >= 1);
+ pa_assert(l->mainloop == m);
+ pa_assert(l->defer_event == e);
do_work(l);
}
void pa_ioline_defer_close(pa_ioline *l) {
- assert(l);
- assert(l->ref >= 1);
-
- l->defer_close = 1;
+ pa_assert(l);
+ pa_assert(PA_REFCNT_VALUE(l) >= 1);
+
+ l->defer_close = TRUE;
if (!l->wbuf_valid_length)
l->mainloop->defer_enable(l->defer_event, 1);
@@ -381,9 +412,9 @@ void pa_ioline_defer_close(pa_ioline *l) {
void pa_ioline_printf(pa_ioline *l, const char *format, ...) {
char *t;
va_list ap;
-
- assert(l);
- assert(l->ref >= 1);
+
+ pa_assert(l);
+ pa_assert(PA_REFCNT_VALUE(l) >= 1);
va_start(ap, format);
t = pa_vsprintf_malloc(format, ap);
diff --git a/src/pulsecore/ioline.h b/src/pulsecore/ioline.h
index e736e2b3..b9a3d9f4 100644
--- a/src/pulsecore/ioline.h
+++ b/src/pulsecore/ioline.h
@@ -1,21 +1,21 @@
#ifndef fooiolinehfoo
#define fooiolinehfoo
-/* $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
@@ -31,6 +31,8 @@
typedef struct pa_ioline pa_ioline;
+typedef void (*pa_ioline_cb_t)(pa_ioline*io, const char *s, void *userdata);
+
pa_ioline* pa_ioline_new(pa_iochannel *io);
void pa_ioline_unref(pa_ioline *l);
pa_ioline* pa_ioline_ref(pa_ioline *l);
@@ -43,7 +45,7 @@ void pa_ioline_puts(pa_ioline *s, const char *c);
void pa_ioline_printf(pa_ioline *s, const char *format, ...) PA_GCC_PRINTF_ATTR(2,3);
/* Set the callback function that is called for every recieved line */
-void pa_ioline_set_callback(pa_ioline*io, void (*callback)(pa_ioline*io, const char *s, void *userdata), void *userdata);
+void pa_ioline_set_callback(pa_ioline*io, pa_ioline_cb_t callback, void *userdata);
/* Make sure to close the ioline object as soon as the send buffer is emptied */
void pa_ioline_defer_close(pa_ioline *io);
diff --git a/src/pulsecore/ipacl.c b/src/pulsecore/ipacl.c
index 36159fab..7b5f865b 100644
--- a/src/pulsecore/ipacl.c
+++ b/src/pulsecore/ipacl.c
@@ -1,18 +1,19 @@
-/* $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.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
@@ -43,13 +44,13 @@
#include <arpa/inet.h>
#endif
-#include "winsock.h"
-
#include <pulse/xmalloc.h>
#include <pulsecore/core-util.h>
#include <pulsecore/llist.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/winsock.h>
#ifndef HAVE_INET_PTON
#include "inet_pton.h"
@@ -59,7 +60,7 @@
struct acl_entry {
PA_LLIST_FIELDS(struct acl_entry);
- int family;
+ int family;
struct in_addr address_ipv4;
struct in6_addr address_ipv6;
int bits;
@@ -74,11 +75,11 @@ pa_ip_acl* pa_ip_acl_new(const char *s) {
char *a;
pa_ip_acl *acl;
- assert(s);
-
+ pa_assert(s);
+
acl = pa_xnew(pa_ip_acl, 1);
PA_LLIST_HEAD_INIT(struct acl_entry, acl->entries);
-
+
while ((a = pa_split(s, ";", &state))) {
char *slash;
struct acl_entry e, *n;
@@ -88,7 +89,7 @@ pa_ip_acl* pa_ip_acl_new(const char *s) {
*slash = 0;
slash++;
if (pa_atou(slash, &bits) < 0) {
- pa_log("failed to parse number of bits: %s", slash);
+ pa_log_warn("Failed to parse number of bits: %s", slash);
goto fail;
}
} else
@@ -97,23 +98,23 @@ pa_ip_acl* pa_ip_acl_new(const char *s) {
if (inet_pton(AF_INET, a, &e.address_ipv4) > 0) {
e.bits = bits == (uint32_t) -1 ? 32 : (int) bits;
-
+
if (e.bits > 32) {
- pa_log("number of bits out of range: %i", e.bits);
+ pa_log_warn("Number of bits out of range: %i", e.bits);
goto fail;
}
e.family = AF_INET;
if (e.bits < 32 && (uint32_t) (ntohl(e.address_ipv4.s_addr) << e.bits) != 0)
- pa_log_warn("WARNING: Host part of ACL entry '%s/%u' is not zero!", a, e.bits);
-
+ pa_log_warn("Host part of ACL entry '%s/%u' is not zero!", a, e.bits);
+
} else if (inet_pton(AF_INET6, a, &e.address_ipv6) > 0) {
e.bits = bits == (uint32_t) -1 ? 128 : (int) bits;
if (e.bits > 128) {
- pa_log("number of bits out of range: %i", e.bits);
+ pa_log_warn("Number of bits out of range: %i", e.bits);
goto fail;
}
e.family = AF_INET6;
@@ -123,7 +124,7 @@ pa_ip_acl* pa_ip_acl_new(const char *s) {
for (i = 0, bits = e.bits; i < 16; i++) {
- if (bits >= 8)
+ if (bits >= 8)
bits -= 8;
else {
if ((uint8_t) ((e.address_ipv6.s6_addr[i]) << bits) != 0) {
@@ -135,38 +136,38 @@ pa_ip_acl* pa_ip_acl_new(const char *s) {
}
if (t)
- pa_log_warn("WARNING: Host part of ACL entry '%s/%u' is not zero!", a, e.bits);
+ pa_log_warn("Host part of ACL entry '%s/%u' is not zero!", a, e.bits);
}
-
+
} else {
- pa_log("failed to parse address: %s", a);
+ pa_log_warn("Failed to parse address: %s", a);
goto fail;
}
n = pa_xmemdup(&e, sizeof(struct acl_entry));
PA_LLIST_PREPEND(struct acl_entry, acl->entries, n);
-
+
pa_xfree(a);
}
return acl;
-
+
fail:
pa_xfree(a);
pa_ip_acl_free(acl);
-
+
return NULL;
}
void pa_ip_acl_free(pa_ip_acl *acl) {
- assert(acl);
+ pa_assert(acl);
while (acl->entries) {
struct acl_entry *e = acl->entries;
PA_LLIST_REMOVE(struct acl_entry, acl->entries, e);
pa_xfree(e);
}
-
+
pa_xfree(acl);
}
@@ -174,9 +175,9 @@ int pa_ip_acl_check(pa_ip_acl *acl, int fd) {
struct sockaddr_storage sa;
struct acl_entry *e;
socklen_t salen;
-
- assert(acl);
- assert(fd >= 0);
+
+ pa_assert(acl);
+ pa_assert(fd >= 0);
salen = sizeof(sa);
if (getpeername(fd, (struct sockaddr*) &sa, &salen) < 0)
@@ -190,7 +191,7 @@ int pa_ip_acl_check(pa_ip_acl *acl, int fd) {
if (sa.ss_family == AF_INET6 && salen != sizeof(struct sockaddr_in6))
return -1;
-
+
for (e = acl->entries; e; e = e->next) {
if (e->family != sa.ss_family)
@@ -198,7 +199,7 @@ int pa_ip_acl_check(pa_ip_acl *acl, int fd) {
if (e->family == AF_INET) {
struct sockaddr_in *sai = (struct sockaddr_in*) &sa;
-
+
if (e->bits == 0 || /* this needs special handling because >> takes the right-hand side modulo 32 */
(ntohl(sai->sin_addr.s_addr ^ e->address_ipv4.s_addr) >> (32 - e->bits)) == 0)
return 1;
@@ -211,7 +212,7 @@ int pa_ip_acl_check(pa_ip_acl *acl, int fd) {
if (e->bits == 0)
return 1;
-
+
for (i = 0, bits = e->bits; i < 16; i++) {
if (bits >= 8) {
diff --git a/src/pulsecore/ipacl.h b/src/pulsecore/ipacl.h
index 7a4540ce..7b7ffa61 100644
--- a/src/pulsecore/ipacl.h
+++ b/src/pulsecore/ipacl.h
@@ -1,21 +1,22 @@
#ifndef fooparseaddrhfoo
#define fooparseaddrhfoo
-/* $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.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
diff --git a/src/pulsecore/llist.h b/src/pulsecore/llist.h
index 31279431..46b54eb3 100644
--- a/src/pulsecore/llist.h
+++ b/src/pulsecore/llist.h
@@ -1,98 +1,107 @@
#ifndef foollistfoo
#define foollistfoo
-/* $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.
***/
-#include <assert.h>
+#include <pulsecore/macro.h>
/* Some macros for maintaining doubly linked lists */
/* The head of the linked list. Use this in the structure that shall
* contain the head of the linked list */
-#define PA_LLIST_HEAD(t,name) t *name
+#define PA_LLIST_HEAD(t,name) \
+ t *name
/* The pointers in the linked list's items. Use this in the item structure */
-#define PA_LLIST_FIELDS(t) t *next, *prev
+#define PA_LLIST_FIELDS(t) \
+ t *next, *prev
/* Initialize the list's head */
-#define PA_LLIST_HEAD_INIT(t,item) do { (item) = (t*) NULL; } while(0)
+#define PA_LLIST_HEAD_INIT(t,item) \
+ do { \
+ (item) = (t*) NULL; } \
+ while(0)
/* Initialize a list item */
-#define PA_LLIST_INIT(t,item) do { \
- t *_item = (item); \
- assert(_item); \
- _item->prev = _item->next = NULL; \
- } while(0)
+#define PA_LLIST_INIT(t,item) \
+ do { \
+ t *_item = (item); \
+ pa_assert(_item); \
+ _item->prev = _item->next = NULL; \
+ } while(0)
/* Prepend an item to the list */
-#define PA_LLIST_PREPEND(t,head,item) do { \
- t **_head = &(head), *_item = (item); \
- assert(_item); \
- if ((_item->next = *_head)) \
- _item->next->prev = _item; \
- _item->prev = NULL; \
- *_head = _item; \
- } while (0)
+#define PA_LLIST_PREPEND(t,head,item) \
+ do { \
+ t **_head = &(head), *_item = (item); \
+ pa_assert(_item); \
+ if ((_item->next = *_head)) \
+ _item->next->prev = _item; \
+ _item->prev = NULL; \
+ *_head = _item; \
+ } while (0)
/* Remove an item from the list */
-#define PA_LLIST_REMOVE(t,head,item) do { \
- t **_head = &(head), *_item = (item); \
- assert(_item); \
- if (_item->next) \
- _item->next->prev = _item->prev; \
- if (_item->prev) \
- _item->prev->next = _item->next; \
- else {\
- assert(*_head == _item); \
- *_head = _item->next; \
- } \
- _item->next = _item->prev = NULL; \
- } while(0)
-
-#define PA_LLIST_FIND_HEAD(t,item,head) \
-do { \
- t **_head = (head), *_item = (item); \
- *_head = _item; \
- assert(_head); \
- while ((*_head)->prev) \
- *_head = (*_head)->prev; \
-} while (0)
-
-#define PA_LLIST_INSERT_AFTER(t,head,a,b) \
-do { \
- t **_head = &(head), *_a = (a), *_b = (b); \
- assert(_b); \
- if (!_a) { \
- if ((_b->next = *_head)) \
- _b->next->prev = _b; \
- _b->prev = NULL; \
- *_head = _b; \
- } else { \
- if ((_b->next = _a->next)) \
- _b->next->prev = _b; \
- _b->prev = _a; \
- _a->next = _b; \
- } \
-} while (0)
-
+#define PA_LLIST_REMOVE(t,head,item) \
+ do { \
+ t **_head = &(head), *_item = (item); \
+ pa_assert(_item); \
+ if (_item->next) \
+ _item->next->prev = _item->prev; \
+ if (_item->prev) \
+ _item->prev->next = _item->next; \
+ else { \
+ pa_assert(*_head == _item); \
+ *_head = _item->next; \
+ } \
+ _item->next = _item->prev = NULL; \
+ } while(0)
+
+/* Find the head of the list */
+#define PA_LLIST_FIND_HEAD(t,item,head) \
+ do { \
+ t **_head = (head), *_item = (item); \
+ *_head = _item; \
+ pa_assert(_head); \
+ while ((*_head)->prev) \
+ *_head = (*_head)->prev; \
+ } while (0)
+
+/* Insert an item after another one (a = where, b = what) */
+#define PA_LLIST_INSERT_AFTER(t,head,a,b) \
+ do { \
+ t **_head = &(head), *_a = (a), *_b = (b); \
+ pa_assert(_b); \
+ if (!_a) { \
+ if ((_b->next = *_head)) \
+ _b->next->prev = _b; \
+ _b->prev = NULL; \
+ *_head = _b; \
+ } else { \
+ if ((_b->next = _a->next)) \
+ _b->next->prev = _b; \
+ _b->prev = _a; \
+ _a->next = _b; \
+ } \
+ } while (0)
#endif
diff --git a/src/pulsecore/log.c b/src/pulsecore/log.c
index ce093221..5eda4f65 100644
--- a/src/pulsecore/log.c
+++ b/src/pulsecore/log.c
@@ -1,18 +1,19 @@
-/* $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
@@ -23,11 +24,11 @@
#include <config.h>
#endif
-#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
+#include <errno.h>
#ifdef HAVE_SYSLOG_H
#include <syslog.h>
@@ -37,6 +38,7 @@
#include <pulse/xmalloc.h>
#include <pulse/util.h>
+#include <pulsecore/macro.h>
#include <pulsecore/core-util.h>
#include "log.h"
@@ -59,25 +61,39 @@ static const int level_to_syslog[] = {
};
#endif
+static const char level_to_char[] = {
+ [PA_LOG_ERROR] = 'E',
+ [PA_LOG_WARN] = 'W',
+ [PA_LOG_NOTICE] = 'N',
+ [PA_LOG_INFO] = 'I',
+ [PA_LOG_DEBUG] = 'D'
+};
+
void pa_log_set_ident(const char *p) {
- if (log_ident)
- pa_xfree(log_ident);
- if (log_ident_local)
- pa_xfree(log_ident_local);
+ pa_xfree(log_ident);
+ pa_xfree(log_ident_local);
log_ident = pa_xstrdup(p);
- log_ident_local = pa_utf8_to_locale(log_ident);
- if (!log_ident_local)
+ if (!(log_ident_local = pa_utf8_to_locale(log_ident)))
log_ident_local = pa_xstrdup(log_ident);
}
+/* To make valgrind shut up. */
+static void ident_destructor(void) PA_GCC_DESTRUCTOR;
+static void ident_destructor(void) {
+ pa_xfree(log_ident);
+ pa_xfree(log_ident_local);
+}
+
void pa_log_set_maximal_level(pa_log_level_t l) {
- assert(l < PA_LOG_LEVEL_MAX);
+ pa_assert(l < PA_LOG_LEVEL_MAX);
+
maximal_level = l;
}
void pa_log_set_target(pa_log_target_t t, void (*func)(pa_log_level_t l, const char*s)) {
- assert(t == PA_LOG_USER || !func);
+ pa_assert(t == PA_LOG_USER || !func);
+
log_target = t;
user_log_func = func;
}
@@ -89,27 +105,34 @@ void pa_log_levelv_meta(
const char *func,
const char *format,
va_list ap) {
-
+
const char *e;
- char *text, *t, *n, *location;
-
- assert(level < PA_LOG_LEVEL_MAX);
- assert(format);
+ char *t, *n;
+ int saved_errno = errno;
+
+ /* We don't use dynamic memory allocation here to minimize the hit
+ * in RT threads */
+ char text[1024], location[128];
+
+ pa_assert(level < PA_LOG_LEVEL_MAX);
+ pa_assert(format);
if ((e = getenv(ENV_LOGLEVEL)))
maximal_level = atoi(e);
-
- if (level > maximal_level)
+
+ if (level > maximal_level) {
+ errno = saved_errno;
return;
+ }
- text = pa_vsprintf_malloc(format, ap);
+ pa_vsnprintf(text, sizeof(text), format, ap);
if (getenv(ENV_LOGMETA) && file && line > 0 && func)
- location = pa_sprintf_malloc("[%s:%i %s()] ", file, line, func);
+ pa_snprintf(location, sizeof(location), "[%s:%i %s()] ", file, line, func);
else if (file)
- location = pa_sprintf_malloc("%s: ", pa_path_get_filename(file));
+ pa_snprintf(location, sizeof(location), "%s: ", pa_path_get_filename(file));
else
- location = pa_xstrdup("");
+ location[0] = 0;
if (!pa_utf8_valid(text))
pa_log_level(level, __FILE__": invalid UTF-8 string following below:");
@@ -122,13 +145,13 @@ void pa_log_levelv_meta(
if (!*t)
continue;
-
+
switch (log_target) {
case PA_LOG_STDERR: {
const char *prefix = "", *suffix = "";
char *local_t;
-#ifndef OS_IS_WIN32
+#ifndef OS_IS_WIN32
/* Yes indeed. Useless, but fun! */
if (isatty(STDERR_FILENO)) {
if (level <= PA_LOG_ERROR) {
@@ -141,18 +164,20 @@ void pa_log_levelv_meta(
}
#endif
+ /* We shouldn't be using dynamic allocation here to
+ * minimize the hit in RT threads */
local_t = pa_utf8_to_locale(t);
if (!local_t)
- fprintf(stderr, "%s%s%s%s\n", location, prefix, t, suffix);
+ fprintf(stderr, "%c: %s%s%s%s\n", level_to_char[level], location, prefix, t, suffix);
else {
- fprintf(stderr, "%s%s%s%s\n", location, prefix, local_t, suffix);
+ fprintf(stderr, "%c: %s%s%s%s\n", level_to_char[level], location, prefix, local_t, suffix);
pa_xfree(local_t);
}
break;
}
-
-#ifdef HAVE_SYSLOG_H
+
+#ifdef HAVE_SYSLOG_H
case PA_LOG_SYSLOG: {
char *local_t;
@@ -167,28 +192,26 @@ void pa_log_levelv_meta(
}
closelog();
- break;
+ break;
}
#endif
-
+
case PA_LOG_USER: {
- char *x;
+ char x[1024];
- x = pa_sprintf_malloc("%s%s", location, t);
+ pa_snprintf(x, sizeof(x), "%s%s", location, t);
user_log_func(level, x);
- pa_xfree(x);
-
+
break;
}
-
+
case PA_LOG_NULL:
default:
break;
}
}
- pa_xfree(text);
- pa_xfree(location);
+ errno = saved_errno;
}
void pa_log_level_meta(
@@ -197,7 +220,7 @@ void pa_log_level_meta(
int line,
const char *func,
const char *format, ...) {
-
+
va_list ap;
va_start(ap, format);
pa_log_levelv_meta(level, file, line, func, format, ap);
@@ -210,6 +233,7 @@ void pa_log_levelv(pa_log_level_t level, const char *format, va_list ap) {
void pa_log_level(pa_log_level_t level, const char *format, ...) {
va_list ap;
+
va_start(ap, format);
pa_log_levelv_meta(level, NULL, 0, NULL, format, ap);
va_end(ap);
diff --git a/src/pulsecore/log.h b/src/pulsecore/log.h
index bf0e75f5..2047696e 100644
--- a/src/pulsecore/log.h
+++ b/src/pulsecore/log.h
@@ -1,21 +1,22 @@
#ifndef foologhfoo
#define foologhfoo
-/* $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
@@ -24,7 +25,7 @@
#include <stdarg.h>
#include <stdlib.h>
-#include <pulsecore/gccmacro.h>
+#include <pulse/gccmacro.h>
/* A simple logging subsystem */
diff --git a/src/pulsecore/ltdl-helper.c b/src/pulsecore/ltdl-helper.c
new file mode 100644
index 00000000..0d4c22f8
--- /dev/null
+++ b/src/pulsecore/ltdl-helper.c
@@ -0,0 +1,64 @@
+/***
+ 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 <stdlib.h>
+#include <ctype.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+
+#include "ltdl-helper.h"
+
+pa_void_func_t pa_load_sym(lt_dlhandle handle, const char *module, const char *symbol) {
+ char *sn, *c;
+ pa_void_func_t f;
+
+ pa_assert(handle);
+ pa_assert(symbol);
+
+ if ((f = ((pa_void_func_t) (size_t) lt_dlsym(handle, symbol))))
+ return f;
+
+ if (!module)
+ return NULL;
+
+ /* As the .la files might have been cleansed from the system, we should
+ * try with the ltdl prefix as well. */
+
+ sn = pa_sprintf_malloc("%s_LTX_%s", module, symbol);
+
+ for (c = sn; *c; c++)
+ if (!isalnum(*c))
+ *c = '_';
+
+ f = (pa_void_func_t) (size_t) lt_dlsym(handle, sn);
+ pa_xfree(sn);
+
+ return f;
+}
diff --git a/src/pulsecore/ltdl-helper.h b/src/pulsecore/ltdl-helper.h
new file mode 100644
index 00000000..ea73de54
--- /dev/null
+++ b/src/pulsecore/ltdl-helper.h
@@ -0,0 +1,32 @@
+#ifndef foopulsecoreltdlhelperhfoo
+#define foopulsecoreltdlhelperhfoo
+
+/***
+ 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.
+***/
+
+#include <ltdl.h>
+
+typedef void (*pa_void_func_t)(void);
+
+pa_void_func_t pa_load_sym(lt_dlhandle handle, const char*module, const char *symbol);
+
+#endif
+
diff --git a/src/pulsecore/macro.h b/src/pulsecore/macro.h
new file mode 100644
index 00000000..fd33b7bb
--- /dev/null
+++ b/src/pulsecore/macro.h
@@ -0,0 +1,224 @@
+#ifndef foopulsemacrohfoo
+#define foopulsemacrohfoo
+
+/***
+ 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.
+***/
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <assert.h>
+#include <limits.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <pulsecore/log.h>
+#include <pulse/gccmacro.h>
+
+#ifndef PACKAGE
+#error "Please include config.h before including this file!"
+#endif
+
+#ifndef PA_LIKELY
+#ifdef __GNUC__
+#define PA_LIKELY(x) (__builtin_expect(!!(x),1))
+#define PA_UNLIKELY(x) (__builtin_expect((x),0))
+#else
+#define PA_LIKELY(x) (x)
+#define PA_UNLIKELY(x) (x)
+#endif
+#endif
+
+#if defined(PAGE_SIZE)
+#define PA_PAGE_SIZE ((size_t) PAGE_SIZE)
+#elif defined(PAGESIZE)
+#define PA_PAGE_SIZE ((size_t) PAGESIZE)
+#elif defined(HAVE_SYSCONF)
+#define PA_PAGE_SIZE ((size_t) (sysconf(_SC_PAGE_SIZE)))
+#else
+/* Let's hope it's like x86. */
+#define PA_PAGE_SIZE ((size_t) 4096)
+#endif
+
+static inline size_t pa_align(size_t l) {
+ return (((l + sizeof(void*) - 1) / sizeof(void*)) * sizeof(void*));
+}
+#define PA_ALIGN(x) (pa_align(x))
+
+static inline void* pa_page_align_ptr(const void *p) {
+ return (void*) (((size_t) p) & ~(PA_PAGE_SIZE-1));
+}
+#define PA_PAGE_ALIGN_PTR(x) (pa_page_align_ptr(x))
+
+static inline size_t pa_page_align(size_t l) {
+ return l & ~(PA_PAGE_SIZE-1);
+}
+#define PA_PAGE_ALIGN(x) (pa_page_align(x))
+
+#define PA_ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0]))
+
+/* The users of PA_MIN and PA_MAX should be aware that these macros on
+ * non-GCC executed code with side effects twice. It is thus
+ * considered misuse to use code with side effects as arguments to MIN
+ * and MAX. */
+
+#ifdef __GNUC__
+#define PA_MAX(a,b) \
+ __extension__ ({ typeof(a) _a = (a); \
+ typeof(b) _b = (b); \
+ _a > _b ? _a : _b; \
+ })
+#else
+#define PA_MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
+
+#ifdef __GNUC__
+#define PA_MIN(a,b) \
+ __extension__ ({ typeof(a) _a = (a); \
+ typeof(b) _b = (b); \
+ _a < _b ? _a : _b; \
+ })
+#else
+#define PA_MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+#ifdef __GNUC__
+#define PA_CLAMP(x, low, high) \
+ __extension__ ({ typeof(x) _x = (x); \
+ typeof(low) _low = (low); \
+ typeof(high) _high = (high); \
+ ((_x > _high) ? _high : ((_x < _low) ? _low : _x)); \
+ })
+#else
+#define PA_CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
+#endif
+
+#ifdef __GNUC__
+#define PA_CLAMP_UNLIKELY(x, low, high) \
+ __extension__ ({ typeof(x) _x = (x); \
+ typeof(low) _low = (low); \
+ typeof(high) _high = (high); \
+ (PA_UNLIKELY(_x > _high) ? _high : (PA_UNLIKELY(_x < _low) ? _low : _x)); \
+ })
+#else
+#define PA_CLAMP_UNLIKELY(x, low, high) (PA_UNLIKELY((x) > (high)) ? (high) : (PA_UNLIKELY((x) < (low)) ? (low) : (x)))
+#endif
+
+/* We don't define a PA_CLAMP_LIKELY here, because it doesn't really
+ * make sense: we cannot know if it is more likely that the values is
+ * lower or greater than the boundaries.*/
+
+/* This type is not intended to be used in exported APIs! Use classic "int" there! */
+#ifdef HAVE_STD_BOOL
+typedef _Bool pa_bool_t;
+#else
+typedef int pa_bool_t;
+#endif
+
+#ifndef FALSE
+#define FALSE ((pa_bool_t) 0)
+#endif
+
+#ifndef TRUE
+#define TRUE (!FALSE)
+#endif
+
+#ifdef __GNUC__
+#define PA_PRETTY_FUNCTION __PRETTY_FUNCTION__
+#else
+#define PA_PRETTY_FUNCTION ""
+#endif
+
+#define pa_return_if_fail(expr) \
+ do { \
+ if (PA_UNLIKELY(!(expr))) { \
+ pa_log_debug("Assertion '%s' failed at %s:%u, function %s.\n", #expr , __FILE__, __LINE__, PA_PRETTY_FUNCTION); \
+ return; \
+ } \
+ } while(FALSE)
+
+#define pa_return_val_if_fail(expr, val) \
+ do { \
+ if (PA_UNLIKELY(!(expr))) { \
+ pa_log_debug("Assertion '%s' failed at %s:%u, function %s.\n", #expr , __FILE__, __LINE__, PA_PRETTY_FUNCTION); \
+ return (val); \
+ } \
+ } while(FALSE)
+
+#define pa_return_null_if_fail(expr) pa_return_val_if_fail(expr, NULL)
+
+/* An assert which guarantees side effects of x, i.e. is never
+ * optimized away */
+#define pa_assert_se(expr) \
+ do { \
+ if (PA_UNLIKELY(!(expr))) { \
+ pa_log_error("Assertion '%s' failed at %s:%u, function %s(). Aborting.", #expr , __FILE__, __LINE__, PA_PRETTY_FUNCTION); \
+ abort(); \
+ } \
+ } while (FALSE)
+
+/* An assert that may be optimized away by defining NDEBUG */
+#ifdef NDEBUG
+#define pa_assert(expr) do {} while (FALSE)
+#else
+#define pa_assert(expr) pa_assert_se(expr)
+#endif
+
+#define pa_assert_not_reached() \
+ do { \
+ pa_log_error("Code should not be reached at %s:%u, function %s(). Aborting.", __FILE__, __LINE__, PA_PRETTY_FUNCTION); \
+ abort(); \
+ } while (FALSE)
+
+#define PA_PTR_TO_UINT(p) ((unsigned int) (unsigned long) (p))
+#define PA_UINT_TO_PTR(u) ((void*) (unsigned long) (u))
+
+#define PA_PTR_TO_UINT32(p) ((uint32_t) PA_PTR_TO_UINT(p))
+#define PA_UINT32_TO_PTR(u) PA_UINT_TO_PTR((uint32_t) u)
+
+#define PA_PTR_TO_INT(p) ((int) PA_PTR_TO_UINT(p))
+#define PA_INT_TO_PTR(u) PA_UINT_TO_PTR((int) u)
+
+#define PA_PTR_TO_INT32(p) ((int32_t) PA_PTR_TO_UINT(p))
+#define PA_INT32_TO_PTR(u) PA_UINT_TO_PTR((int32_t) u)
+
+#ifdef OS_IS_WIN32
+#define PA_PATH_SEP "\\"
+#define PA_PATH_SEP_CHAR '\\'
+#else
+#define PA_PATH_SEP "/"
+#define PA_PATH_SEP_CHAR '/'
+#endif
+
+#ifdef __GNUC__
+
+#define PA_WARN_REFERENCE(sym, msg) \
+ __asm__(".section .gnu.warning." #sym); \
+ __asm__(".asciz \"" msg "\""); \
+ __asm__(".previous")
+
+#else
+
+#define PA_WARN_REFERENCE(sym, msg)
+
+#endif
+
+#endif
diff --git a/src/pulsecore/mcalign.c b/src/pulsecore/mcalign.c
index aa2eae46..a03d5ae7 100644
--- a/src/pulsecore/mcalign.c
+++ b/src/pulsecore/mcalign.c
@@ -1,18 +1,18 @@
-/* $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.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
@@ -25,10 +25,10 @@
#include <stdio.h>
#include <stdlib.h>
-#include <assert.h>
#include <string.h>
#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
#include "mcalign.h"
@@ -39,41 +39,41 @@ struct pa_mcalign {
pa_mcalign *pa_mcalign_new(size_t base) {
pa_mcalign *m;
- assert(base);
+ pa_assert(base);
m = pa_xnew(pa_mcalign, 1);
-
+
m->base = base;
pa_memchunk_reset(&m->leftover);
pa_memchunk_reset(&m->current);
-
+
return m;
}
void pa_mcalign_free(pa_mcalign *m) {
- assert(m);
+ pa_assert(m);
if (m->leftover.memblock)
pa_memblock_unref(m->leftover.memblock);
if (m->current.memblock)
pa_memblock_unref(m->current.memblock);
-
+
pa_xfree(m);
}
void pa_mcalign_push(pa_mcalign *m, const pa_memchunk *c) {
- assert(m);
- assert(c);
-
- assert(c->memblock);
- assert(c->length > 0);
-
- assert(!m->current.memblock);
-
+ pa_assert(m);
+ pa_assert(c);
+
+ pa_assert(c->memblock);
+ pa_assert(c->length > 0);
+
+ pa_assert(!m->current.memblock);
+
/* Append to the leftover memory block */
if (m->leftover.memblock) {
-
+
/* Try to merge */
if (m->leftover.memblock == c->memblock &&
m->leftover.index + m->leftover.length == c->index) {
@@ -85,16 +85,16 @@ void pa_mcalign_push(pa_mcalign *m, const pa_memchunk *c) {
if (m->leftover.length >= m->base) {
m->current = m->leftover;
pa_memchunk_reset(&m->leftover);
- }
+ }
} else {
size_t l;
void *lo_data, *m_data;
/* We have to copy */
- assert(m->leftover.length < m->base);
+ pa_assert(m->leftover.length < m->base);
l = m->base - m->leftover.length;
-
+
if (l > c->length)
l = c->length;
@@ -108,8 +108,8 @@ void pa_mcalign_push(pa_mcalign *m, const pa_memchunk *c) {
pa_memblock_release(c->memblock);
m->leftover.length += l;
- assert(m->leftover.length <= m->base);
- assert(m->leftover.length <= pa_memblock_get_length(m->leftover.memblock));
+ pa_assert(m->leftover.length <= m->base);
+ pa_assert(m->leftover.length <= pa_memblock_get_length(m->leftover.memblock));
if (c->length > l) {
/* Save the remainder of the memory block */
@@ -121,7 +121,7 @@ void pa_mcalign_push(pa_mcalign *m, const pa_memchunk *c) {
}
} else {
/* Nothing to merge or copy, just store it */
-
+
if (c->length >= m->base)
m->current = *c;
else
@@ -132,12 +132,13 @@ void pa_mcalign_push(pa_mcalign *m, const pa_memchunk *c) {
}
int pa_mcalign_pop(pa_mcalign *m, pa_memchunk *c) {
- assert(m);
- assert(c);
+ pa_assert(m);
+ pa_assert(c);
/* First test if there's a leftover memory block available */
if (m->leftover.memblock) {
- assert(m->leftover.length > 0 && m->leftover.length <= m->base);
+ pa_assert(m->leftover.length > 0);
+ pa_assert(m->leftover.length <= m->base);
/* The leftover memory block is not yet complete */
if (m->leftover.length < m->base)
@@ -152,20 +153,20 @@ int pa_mcalign_pop(pa_mcalign *m, pa_memchunk *c) {
m->leftover = m->current;
pa_memchunk_reset(&m->current);
}
-
+
return 0;
}
/* Now let's see if there is other data available */
if (m->current.memblock) {
size_t l;
- assert(m->current.length >= m->base);
+ pa_assert(m->current.length >= m->base);
/* The length of the returned memory block */
l = m->current.length;
l /= m->base;
l *= m->base;
- assert(l > 0);
+ pa_assert(l > 0);
/* Prepare the returned block */
*c = m->current;
@@ -173,7 +174,7 @@ int pa_mcalign_pop(pa_mcalign *m, pa_memchunk *c) {
c->length = l;
/* Drop that from the current memory block */
- assert(l <= m->current.length);
+ pa_assert(l <= m->current.length);
m->current.index += l;
m->current.length -= l;
@@ -182,29 +183,36 @@ int pa_mcalign_pop(pa_mcalign *m, pa_memchunk *c) {
pa_memblock_unref(m->current.memblock);
else {
/* Move the raimainder to leftover */
- assert(m->current.length < m->base && !m->leftover.memblock);
+ pa_assert(m->current.length < m->base && !m->leftover.memblock);
m->leftover = m->current;
}
pa_memchunk_reset(&m->current);
-
+
return 0;
}
/* There's simply nothing */
return -1;
-
}
size_t pa_mcalign_csize(pa_mcalign *m, size_t l) {
- assert(m);
- assert(l > 0);
+ pa_assert(m);
+ pa_assert(l > 0);
+
+ pa_assert(!m->current.memblock);
- assert(!m->current.memblock);
-
if (m->leftover.memblock)
l += m->leftover.length;
-
+
return (l/m->base)*m->base;
}
+
+void pa_mcalign_flush(pa_mcalign *m) {
+ pa_memchunk chunk;
+ pa_assert(m);
+
+ while (pa_mcalign_pop(m, &chunk) >= 0)
+ pa_memblock_unref(chunk.memblock);
+}
diff --git a/src/pulsecore/mcalign.h b/src/pulsecore/mcalign.h
index 94e99e21..e82eb007 100644
--- a/src/pulsecore/mcalign.h
+++ b/src/pulsecore/mcalign.h
@@ -1,21 +1,21 @@
#ifndef foomcalignhfoo
#define foomcalignhfoo
-/* $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.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
@@ -41,11 +41,11 @@
* for (;;) {
* pa_memchunk input;
*
- * ... fill input ...
+ * ... fill input ...
*
* pa_mcalign_push(m, &input);
* pa_memblock_unref(input.memblock);
- *
+ *
* for (;;) {
* pa_memchunk output;
*
@@ -77,4 +77,7 @@ int pa_mcalign_pop(pa_mcalign *m, pa_memchunk *c);
/* If we pass l bytes in now, how many bytes would we get out? */
size_t pa_mcalign_csize(pa_mcalign *m, size_t l);
+/* Flush what's still stored in the aligner */
+void pa_mcalign_flush(pa_mcalign *m);
+
#endif
diff --git a/src/pulsecore/memblock.c b/src/pulsecore/memblock.c
index f11a7174..c2ee1360 100644
--- a/src/pulsecore/memblock.c
+++ b/src/pulsecore/memblock.c
@@ -1,18 +1,19 @@
-/* $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.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
+ Lesser General Public License for more details
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
@@ -25,9 +26,10 @@
#include <stdio.h>
#include <stdlib.h>
-#include <assert.h>
#include <string.h>
#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
#include <pulse/xmalloc.h>
#include <pulse/def.h>
@@ -35,13 +37,19 @@
#include <pulsecore/shm.h>
#include <pulsecore/log.h>
#include <pulsecore/hashmap.h>
-#include <pulsecore/mutex.h>
+#include <pulsecore/semaphore.h>
+#include <pulsecore/macro.h>
#include <pulsecore/flist.h>
+#include <pulsecore/core-util.h>
#include "memblock.h"
-#define PA_MEMPOOL_SLOTS_MAX 128
-#define PA_MEMPOOL_SLOT_SIZE (16*1024)
+/* We can allocate 64*1024*1024 bytes at maximum. That's 64MB. Please
+ * note that the footprint is usually much smaller, since the data is
+ * stored in SHM and our OS does not commit the memory before we use
+ * it for the first time. */
+#define PA_MEMPOOL_SLOTS_MAX 1024
+#define PA_MEMPOOL_SLOT_SIZE (64*1024)
#define PA_MEMEXPORT_SLOTS_MAX 128
@@ -53,20 +61,22 @@ struct pa_memblock {
pa_mempool *pool;
pa_memblock_type_t type;
- int read_only; /* boolean */
-
+
+ pa_bool_t read_only:1;
+ pa_bool_t is_silence:1;
+
pa_atomic_ptr_t data;
size_t length;
- pa_atomic_int_t n_acquired;
- pa_atomic_int_t please_signal;
+ pa_atomic_t n_acquired;
+ pa_atomic_t please_signal;
union {
struct {
/* If type == PA_MEMBLOCK_USER this points to a function for freeing this memory block */
pa_free_cb_t free_cb;
} user;
-
+
struct {
uint32_t id;
pa_memimport_segment *segment;
@@ -82,7 +92,7 @@ struct pa_memimport_segment {
struct pa_memimport {
pa_mutex *mutex;
-
+
pa_mempool *pool;
pa_hashmap *segments;
pa_hashmap *blocks;
@@ -103,7 +113,7 @@ struct memexport_slot {
struct pa_memexport {
pa_mutex *mutex;
pa_mempool *pool;
-
+
struct memexport_slot slots[PA_MEMEXPORT_SLOTS_MAX];
PA_LLIST_HEAD(struct memexport_slot, free_slots);
@@ -119,46 +129,43 @@ struct pa_memexport {
PA_LLIST_FIELDS(pa_memexport);
};
-struct mempool_slot {
- PA_LLIST_FIELDS(struct mempool_slot);
- /* the actual data follows immediately hereafter */
-};
-
struct pa_mempool {
+ pa_semaphore *semaphore;
pa_mutex *mutex;
- pa_cond *cond;
-
+
pa_shm memory;
size_t block_size;
unsigned n_blocks;
- pa_atomic_int_t n_init;
+ pa_atomic_t n_init;
PA_LLIST_HEAD(pa_memimport, imports);
PA_LLIST_HEAD(pa_memexport, exports);
/* A list of free slots that may be reused */
pa_flist *free_slots;
-
+
pa_mempool_stat stat;
};
static void segment_detach(pa_memimport_segment *seg);
+PA_STATIC_FLIST_DECLARE(unused_memblocks, 0, pa_xfree);
+
/* No lock necessary */
static void stat_add(pa_memblock*b) {
- assert(b);
- assert(b->pool);
+ pa_assert(b);
+ pa_assert(b->pool);
pa_atomic_inc(&b->pool->stat.n_allocated);
- pa_atomic_add(&b->pool->stat.allocated_size, (int) b->length);
+ pa_atomic_add(&b->pool->stat.allocated_size, b->length);
pa_atomic_inc(&b->pool->stat.n_accumulated);
- pa_atomic_add(&b->pool->stat.accumulated_size, (int) b->length);
+ pa_atomic_add(&b->pool->stat.accumulated_size, b->length);
if (b->type == PA_MEMBLOCK_IMPORTED) {
pa_atomic_inc(&b->pool->stat.n_imported);
- pa_atomic_add(&b->pool->stat.imported_size, (int) b->length);
+ pa_atomic_add(&b->pool->stat.imported_size, b->length);
}
pa_atomic_inc(&b->pool->stat.n_allocated_by_type[b->type]);
@@ -167,21 +174,21 @@ static void stat_add(pa_memblock*b) {
/* No lock necessary */
static void stat_remove(pa_memblock *b) {
- assert(b);
- assert(b->pool);
+ pa_assert(b);
+ pa_assert(b->pool);
+
+ pa_assert(pa_atomic_load(&b->pool->stat.n_allocated) > 0);
+ pa_assert(pa_atomic_load(&b->pool->stat.allocated_size) >= (int) b->length);
- assert(pa_atomic_load(&b->pool->stat.n_allocated) > 0);
- assert(pa_atomic_load(&b->pool->stat.allocated_size) >= (int) b->length);
-
pa_atomic_dec(&b->pool->stat.n_allocated);
- pa_atomic_add(&b->pool->stat.allocated_size, - (int) b->length);
+ pa_atomic_sub(&b->pool->stat.allocated_size, b->length);
if (b->type == PA_MEMBLOCK_IMPORTED) {
- assert(pa_atomic_load(&b->pool->stat.n_imported) > 0);
- assert(pa_atomic_load(&b->pool->stat.imported_size) >= (int) b->length);
-
+ pa_assert(pa_atomic_load(&b->pool->stat.n_imported) > 0);
+ pa_assert(pa_atomic_load(&b->pool->stat.imported_size) >= (int) b->length);
+
pa_atomic_dec(&b->pool->stat.n_imported);
- pa_atomic_add(&b->pool->stat.imported_size, - (int) b->length);
+ pa_atomic_sub(&b->pool->stat.imported_size, b->length);
}
pa_atomic_dec(&b->pool->stat.n_allocated_by_type[b->type]);
@@ -192,10 +199,10 @@ static pa_memblock *memblock_new_appended(pa_mempool *p, size_t length);
/* No lock necessary */
pa_memblock *pa_memblock_new(pa_mempool *p, size_t length) {
pa_memblock *b;
-
- assert(p);
- assert(length > 0);
-
+
+ pa_assert(p);
+ pa_assert(length);
+
if (!(b = pa_memblock_new_pool(p, length)))
b = memblock_new_appended(p, length);
@@ -206,19 +213,24 @@ pa_memblock *pa_memblock_new(pa_mempool *p, size_t length) {
static pa_memblock *memblock_new_appended(pa_mempool *p, size_t length) {
pa_memblock *b;
- assert(p);
- assert(length > 0);
+ pa_assert(p);
+ pa_assert(length);
+
+ /* If -1 is passed as length we choose the size for the caller. */
- b = pa_xmalloc(sizeof(pa_memblock) + length);
+ if (length == (size_t) -1)
+ length = p->block_size - PA_ALIGN(sizeof(pa_memblock));
+
+ b = pa_xmalloc(PA_ALIGN(sizeof(pa_memblock)) + length);
PA_REFCNT_INIT(b);
b->pool = p;
b->type = PA_MEMBLOCK_APPENDED;
- b->read_only = 0;
- pa_atomic_ptr_store(&b->data, (uint8_t*)b + sizeof(pa_memblock));
+ b->read_only = b->is_silence = FALSE;
+ pa_atomic_ptr_store(&b->data, (uint8_t*) b + PA_ALIGN(sizeof(pa_memblock)));
b->length = length;
pa_atomic_store(&b->n_acquired, 0);
pa_atomic_store(&b->please_signal, 0);
-
+
stat_add(b);
return b;
}
@@ -226,40 +238,39 @@ static pa_memblock *memblock_new_appended(pa_mempool *p, size_t length) {
/* No lock necessary */
static struct mempool_slot* mempool_allocate_slot(pa_mempool *p) {
struct mempool_slot *slot;
- assert(p);
+ pa_assert(p);
if (!(slot = pa_flist_pop(p->free_slots))) {
int idx;
-
+
/* The free list was empty, we have to allocate a new entry */
if ((unsigned) (idx = pa_atomic_inc(&p->n_init)) >= p->n_blocks)
pa_atomic_dec(&p->n_init);
else
slot = (struct mempool_slot*) ((uint8_t*) p->memory.ptr + (p->block_size * idx));
-
+
if (!slot) {
- pa_log_debug("Pool full");
+ pa_log_info("Pool full");
pa_atomic_inc(&p->stat.n_pool_full);
+ return NULL;
}
}
return slot;
}
-/* No lock necessary */
-static void* mempool_slot_data(struct mempool_slot *slot) {
- assert(slot);
-
- return (uint8_t*) slot + sizeof(struct mempool_slot);
+/* No lock necessary, totally redundant anyway */
+static inline void* mempool_slot_data(struct mempool_slot *slot) {
+ return slot;
}
/* No lock necessary */
static unsigned mempool_slot_idx(pa_mempool *p, void *ptr) {
- assert(p);
+ pa_assert(p);
- assert((uint8_t*) ptr >= (uint8_t*) p->memory.ptr);
- assert((uint8_t*) ptr < (uint8_t*) p->memory.ptr + p->memory.size);
+ pa_assert((uint8_t*) ptr >= (uint8_t*) p->memory.ptr);
+ pa_assert((uint8_t*) ptr < (uint8_t*) p->memory.ptr + p->memory.size);
return ((uint8_t*) ptr - (uint8_t*) p->memory.ptr) / p->block_size;
}
@@ -279,36 +290,44 @@ pa_memblock *pa_memblock_new_pool(pa_mempool *p, size_t length) {
pa_memblock *b = NULL;
struct mempool_slot *slot;
- assert(p);
- assert(length > 0);
+ pa_assert(p);
+ pa_assert(length);
+
+ /* If -1 is passed as length we choose the size for the caller: we
+ * take the largest size that fits in one of our slots. */
+
+ if (length == (size_t) -1)
+ length = pa_mempool_block_size_max(p);
- if (p->block_size - sizeof(struct mempool_slot) >= sizeof(pa_memblock) + length) {
+ if (p->block_size >= PA_ALIGN(sizeof(pa_memblock)) + length) {
if (!(slot = mempool_allocate_slot(p)))
return NULL;
-
+
b = mempool_slot_data(slot);
b->type = PA_MEMBLOCK_POOL;
- pa_atomic_ptr_store(&b->data, (uint8_t*) b + sizeof(pa_memblock));
-
- } else if (p->block_size - sizeof(struct mempool_slot) >= length) {
+ pa_atomic_ptr_store(&b->data, (uint8_t*) b + PA_ALIGN(sizeof(pa_memblock)));
+
+ } else if (p->block_size >= length) {
if (!(slot = mempool_allocate_slot(p)))
return NULL;
-
- b = pa_xnew(pa_memblock, 1);
+
+ if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks))))
+ b = pa_xnew(pa_memblock, 1);
+
b->type = PA_MEMBLOCK_POOL_EXTERNAL;
pa_atomic_ptr_store(&b->data, mempool_slot_data(slot));
-
+
} else {
- pa_log_debug("Memory block too large for pool: %u > %u", length, p->block_size - sizeof(struct mempool_slot));
+ pa_log_debug("Memory block too large for pool: %lu > %lu", (unsigned long) length, (unsigned long) p->block_size);
pa_atomic_inc(&p->stat.n_too_large_for_pool);
return NULL;
}
PA_REFCNT_INIT(b);
b->pool = p;
- b->read_only = 0;
+ b->read_only = b->is_silence = FALSE;
b->length = length;
pa_atomic_store(&b->n_acquired, 0);
pa_atomic_store(&b->please_signal, 0);
@@ -318,18 +337,21 @@ pa_memblock *pa_memblock_new_pool(pa_mempool *p, size_t length) {
}
/* No lock necessary */
-pa_memblock *pa_memblock_new_fixed(pa_mempool *p, void *d, size_t length, int read_only) {
+pa_memblock *pa_memblock_new_fixed(pa_mempool *p, void *d, size_t length, pa_bool_t read_only) {
pa_memblock *b;
- assert(p);
- assert(d);
- assert(length > 0);
+ pa_assert(p);
+ pa_assert(d);
+ pa_assert(length != (size_t) -1);
+ pa_assert(length);
- b = pa_xnew(pa_memblock, 1);
+ if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks))))
+ b = pa_xnew(pa_memblock, 1);
PA_REFCNT_INIT(b);
b->pool = p;
b->type = PA_MEMBLOCK_FIXED;
b->read_only = read_only;
+ b->is_silence = FALSE;
pa_atomic_ptr_store(&b->data, d);
b->length = length;
pa_atomic_store(&b->n_acquired, 0);
@@ -340,24 +362,27 @@ pa_memblock *pa_memblock_new_fixed(pa_mempool *p, void *d, size_t length, int re
}
/* No lock necessary */
-pa_memblock *pa_memblock_new_user(pa_mempool *p, void *d, size_t length, void (*free_cb)(void *p), int read_only) {
+pa_memblock *pa_memblock_new_user(pa_mempool *p, void *d, size_t length, pa_free_cb_t free_cb, pa_bool_t read_only) {
pa_memblock *b;
- assert(p);
- assert(d);
- assert(length > 0);
- assert(free_cb);
-
- b = pa_xnew(pa_memblock, 1);
+ pa_assert(p);
+ pa_assert(d);
+ pa_assert(length);
+ pa_assert(length != (size_t) -1);
+ pa_assert(free_cb);
+
+ if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks))))
+ b = pa_xnew(pa_memblock, 1);
PA_REFCNT_INIT(b);
b->pool = p;
b->type = PA_MEMBLOCK_USER;
b->read_only = read_only;
+ b->is_silence = FALSE;
pa_atomic_ptr_store(&b->data, d);
b->length = length;
pa_atomic_store(&b->n_acquired, 0);
pa_atomic_store(&b->please_signal, 0);
-
+
b->per_type.user.free_cb = free_cb;
stat_add(b);
@@ -365,103 +390,129 @@ pa_memblock *pa_memblock_new_user(pa_mempool *p, void *d, size_t length, void (*
}
/* No lock necessary */
-int pa_memblock_is_read_only(pa_memblock *b) {
- assert(b);
- assert(PA_REFCNT_VALUE(b) > 0);
+pa_bool_t pa_memblock_is_read_only(pa_memblock *b) {
+ pa_assert(b);
+ pa_assert(PA_REFCNT_VALUE(b) > 0);
return b->read_only && PA_REFCNT_VALUE(b) == 1;
}
/* No lock necessary */
+pa_bool_t pa_memblock_is_silence(pa_memblock *b) {
+ pa_assert(b);
+ pa_assert(PA_REFCNT_VALUE(b) > 0);
+
+ return b->is_silence;
+}
+
+/* No lock necessary */
+void pa_memblock_set_is_silence(pa_memblock *b, pa_bool_t v) {
+ pa_assert(b);
+ pa_assert(PA_REFCNT_VALUE(b) > 0);
+
+ b->is_silence = v;
+}
+
+/* No lock necessary */
+pa_bool_t pa_memblock_ref_is_one(pa_memblock *b) {
+ int r;
+ pa_assert(b);
+
+ pa_assert_se((r = PA_REFCNT_VALUE(b)) > 0);
+
+ return r == 1;
+}
+
+/* No lock necessary */
void* pa_memblock_acquire(pa_memblock *b) {
- assert(b);
- assert(PA_REFCNT_VALUE(b) > 0);
+ pa_assert(b);
+ pa_assert(PA_REFCNT_VALUE(b) > 0);
pa_atomic_inc(&b->n_acquired);
-
+
return pa_atomic_ptr_load(&b->data);
}
/* No lock necessary, in corner cases locks by its own */
void pa_memblock_release(pa_memblock *b) {
int r;
- assert(b);
- assert(PA_REFCNT_VALUE(b) > 0);
-
+ pa_assert(b);
+ pa_assert(PA_REFCNT_VALUE(b) > 0);
+
r = pa_atomic_dec(&b->n_acquired);
- assert(r >= 1);
-
- if (r == 1 && pa_atomic_load(&b->please_signal)) {
- pa_mempool *p = b->pool;
- /* Signal a waiting thread that this memblock is no longer used */
- pa_mutex_lock(p->mutex);
- pa_cond_signal(p->cond, 1);
- pa_mutex_unlock(p->mutex);
- }
+ pa_assert(r >= 1);
+
+ /* Signal a waiting thread that this memblock is no longer used */
+ if (r == 1 && pa_atomic_load(&b->please_signal))
+ pa_semaphore_post(b->pool->semaphore);
}
size_t pa_memblock_get_length(pa_memblock *b) {
- assert(b);
- assert(PA_REFCNT_VALUE(b) > 0);
+ pa_assert(b);
+ pa_assert(PA_REFCNT_VALUE(b) > 0);
return b->length;
}
pa_mempool* pa_memblock_get_pool(pa_memblock *b) {
- assert(b);
- assert(PA_REFCNT_VALUE(b) > 0);
+ pa_assert(b);
+ pa_assert(PA_REFCNT_VALUE(b) > 0);
return b->pool;
}
/* No lock necessary */
pa_memblock* pa_memblock_ref(pa_memblock*b) {
- assert(b);
- assert(PA_REFCNT_VALUE(b) > 0);
+ pa_assert(b);
+ pa_assert(PA_REFCNT_VALUE(b) > 0);
PA_REFCNT_INC(b);
return b;
}
static void memblock_free(pa_memblock *b) {
- assert(b);
-
- assert(pa_atomic_load(&b->n_acquired) == 0);
+ pa_assert(b);
+
+ pa_assert(pa_atomic_load(&b->n_acquired) == 0);
stat_remove(b);
switch (b->type) {
case PA_MEMBLOCK_USER :
- assert(b->per_type.user.free_cb);
+ pa_assert(b->per_type.user.free_cb);
b->per_type.user.free_cb(pa_atomic_ptr_load(&b->data));
/* Fall through */
case PA_MEMBLOCK_FIXED:
case PA_MEMBLOCK_APPENDED :
- pa_xfree(b);
+ if (pa_flist_push(PA_STATIC_FLIST_GET(unused_memblocks), b) < 0)
+ pa_xfree(b);
+
break;
case PA_MEMBLOCK_IMPORTED : {
pa_memimport_segment *segment;
pa_memimport *import;
-
+
/* FIXME! This should be implemented lock-free */
-
+
segment = b->per_type.imported.segment;
- assert(segment);
+ pa_assert(segment);
import = segment->import;
- assert(import);
-
+ pa_assert(import);
+
pa_mutex_lock(import->mutex);
pa_hashmap_remove(import->blocks, PA_UINT32_TO_PTR(b->per_type.imported.id));
if (-- segment->n_blocks <= 0)
segment_detach(segment);
+
pa_mutex_unlock(import->mutex);
import->release_cb(import, b->per_type.imported.id, import->userdata);
- pa_xfree(b);
+ if (pa_flist_push(PA_STATIC_FLIST_GET(unused_memblocks), b) < 0)
+ pa_xfree(b);
break;
}
@@ -471,7 +522,7 @@ static void memblock_free(pa_memblock *b) {
int call_free;
slot = mempool_slot_by_ptr(b->pool, pa_atomic_ptr_load(&b->data));
- assert(slot);
+ pa_assert(slot);
call_free = b->type == PA_MEMBLOCK_POOL_EXTERNAL;
@@ -480,23 +531,24 @@ static void memblock_free(pa_memblock *b) {
* the free list fails */
while (pa_flist_push(b->pool->free_slots, slot) < 0)
;
-
+
if (call_free)
- pa_xfree(b);
+ if (pa_flist_push(PA_STATIC_FLIST_GET(unused_memblocks), b) < 0)
+ pa_xfree(b);
break;
}
case PA_MEMBLOCK_TYPE_MAX:
default:
- abort();
+ pa_assert_not_reached();
}
}
/* No lock necessary */
void pa_memblock_unref(pa_memblock*b) {
- assert(b);
- assert(PA_REFCNT_VALUE(b) > 0);
+ pa_assert(b);
+ pa_assert(PA_REFCNT_VALUE(b) > 0);
if (PA_REFCNT_DEC(b) > 0)
return;
@@ -506,7 +558,7 @@ void pa_memblock_unref(pa_memblock*b) {
/* Self locked */
static void memblock_wait(pa_memblock *b) {
- assert(b);
+ pa_assert(b);
if (pa_atomic_load(&b->n_acquired) > 0) {
/* We need to wait until all threads gave up access to the
@@ -515,10 +567,8 @@ static void memblock_wait(pa_memblock *b) {
pa_atomic_inc(&b->please_signal);
- pa_mutex_lock(b->pool->mutex);
while (pa_atomic_load(&b->n_acquired) > 0)
- pa_cond_wait(b->pool->cond, b->pool->mutex);
- pa_mutex_unlock(b->pool->mutex);
+ pa_semaphore_wait(b->pool->semaphore);
pa_atomic_dec(&b->please_signal);
}
@@ -526,23 +576,23 @@ static void memblock_wait(pa_memblock *b) {
/* No lock necessary. This function is not multiple caller safe! */
static void memblock_make_local(pa_memblock *b) {
- assert(b);
+ pa_assert(b);
pa_atomic_dec(&b->pool->stat.n_allocated_by_type[b->type]);
- if (b->length <= b->pool->block_size - sizeof(struct mempool_slot)) {
+ if (b->length <= b->pool->block_size) {
struct mempool_slot *slot;
if ((slot = mempool_allocate_slot(b->pool))) {
void *new_data;
/* We can move it into a local pool, perfect! */
-
+
new_data = mempool_slot_data(slot);
memcpy(new_data, pa_atomic_ptr_load(&b->data), b->length);
pa_atomic_ptr_store(&b->data, new_data);
b->type = PA_MEMBLOCK_POOL_EXTERNAL;
- b->read_only = 0;
+ b->read_only = FALSE;
goto finish;
}
@@ -553,83 +603,85 @@ static void memblock_make_local(pa_memblock *b) {
pa_atomic_ptr_store(&b->data, pa_xmemdup(pa_atomic_ptr_load(&b->data), b->length));
b->type = PA_MEMBLOCK_USER;
- b->read_only = 0;
+ b->read_only = FALSE;
finish:
pa_atomic_inc(&b->pool->stat.n_allocated_by_type[b->type]);
pa_atomic_inc(&b->pool->stat.n_accumulated_by_type[b->type]);
-
memblock_wait(b);
}
/* No lock necessary. This function is not multiple caller safe*/
void pa_memblock_unref_fixed(pa_memblock *b) {
- assert(b);
- assert(PA_REFCNT_VALUE(b) > 0);
- assert(b->type == PA_MEMBLOCK_FIXED);
+ pa_assert(b);
+ pa_assert(PA_REFCNT_VALUE(b) > 0);
+ pa_assert(b->type == PA_MEMBLOCK_FIXED);
- if (PA_REFCNT_DEC(b) > 0)
+ if (PA_REFCNT_VALUE(b) > 1)
memblock_make_local(b);
- else
- memblock_free(b);
+
+ pa_memblock_unref(b);
+}
+
+/* No lock necessary. */
+pa_memblock *pa_memblock_will_need(pa_memblock *b) {
+ void *p;
+
+ pa_assert(b);
+ pa_assert(PA_REFCNT_VALUE(b) > 0);
+
+ p = pa_memblock_acquire(b);
+ pa_will_need(p, b->length);
+ pa_memblock_release(b);
+
+ return b;
}
/* Self-locked. This function is not multiple-caller safe */
static void memblock_replace_import(pa_memblock *b) {
pa_memimport_segment *seg;
-
- assert(b);
- assert(b->type == PA_MEMBLOCK_IMPORTED);
- assert(pa_atomic_load(&b->pool->stat.n_imported) > 0);
- assert(pa_atomic_load(&b->pool->stat.imported_size) >= (int) b->length);
+ pa_assert(b);
+ pa_assert(b->type == PA_MEMBLOCK_IMPORTED);
+
+ pa_assert(pa_atomic_load(&b->pool->stat.n_imported) > 0);
+ pa_assert(pa_atomic_load(&b->pool->stat.imported_size) >= (int) b->length);
pa_atomic_dec(&b->pool->stat.n_imported);
- pa_atomic_add(&b->pool->stat.imported_size, (int) - b->length);
+ pa_atomic_sub(&b->pool->stat.imported_size, b->length);
seg = b->per_type.imported.segment;
- assert(seg);
- assert(seg->import);
+ pa_assert(seg);
+ pa_assert(seg->import);
pa_mutex_lock(seg->import->mutex);
-
+
pa_hashmap_remove(
seg->import->blocks,
PA_UINT32_TO_PTR(b->per_type.imported.id));
memblock_make_local(b);
- if (-- seg->n_blocks <= 0)
+ if (-- seg->n_blocks <= 0) {
+ pa_mutex_unlock(seg->import->mutex);
segment_detach(seg);
-
- pa_mutex_unlock(seg->import->mutex);
+ } else
+ pa_mutex_unlock(seg->import->mutex);
}
-pa_mempool* pa_mempool_new(int shared) {
- size_t ps;
+pa_mempool* pa_mempool_new(pa_bool_t shared) {
pa_mempool *p;
p = pa_xnew(pa_mempool, 1);
- p->mutex = pa_mutex_new(1);
- p->cond = pa_cond_new();
-
-#ifdef HAVE_SYSCONF
- ps = (size_t) sysconf(_SC_PAGESIZE);
-#elif defined(PAGE_SIZE)
- ps = (size_t) PAGE_SIZE;
-#else
- ps = 4096; /* Let's hope it's like x86. */
-#endif
+ p->mutex = pa_mutex_new(TRUE, TRUE);
+ p->semaphore = pa_semaphore_new(0);
- p->block_size = (PA_MEMPOOL_SLOT_SIZE/ps)*ps;
+ p->block_size = PA_PAGE_ALIGN(PA_MEMPOOL_SLOT_SIZE);
+ if (p->block_size < PA_PAGE_SIZE)
+ p->block_size = PA_PAGE_SIZE;
- if (p->block_size < ps)
- p->block_size = ps;
-
p->n_blocks = PA_MEMPOOL_SLOTS_MAX;
- assert(p->block_size > sizeof(struct mempool_slot));
-
if (pa_shm_create_rw(&p->memory, p->n_blocks * p->block_size, shared, 0700) < 0) {
pa_xfree(p);
return NULL;
@@ -637,7 +689,7 @@ pa_mempool* pa_mempool_new(int shared) {
memset(&p->stat, 0, sizeof(p->stat));
pa_atomic_store(&p->n_init, 0);
-
+
PA_LLIST_HEAD_INIT(pa_memimport, p->imports);
PA_LLIST_HEAD_INIT(pa_memexport, p->exports);
@@ -647,7 +699,7 @@ pa_mempool* pa_mempool_new(int shared) {
}
void pa_mempool_free(pa_mempool *p) {
- assert(p);
+ pa_assert(p);
pa_mutex_lock(p->mutex);
@@ -659,32 +711,41 @@ void pa_mempool_free(pa_mempool *p) {
pa_mutex_unlock(p->mutex);
- if (pa_atomic_load(&p->stat.n_allocated) > 0)
- pa_log_warn("WARNING! Memory pool destroyed but not all memory blocks freed!");
-
pa_flist_free(p->free_slots, NULL);
-
+
+ if (pa_atomic_load(&p->stat.n_allocated) > 0) {
+/* raise(SIGTRAP); */
+ pa_log_warn("Memory pool destroyed but not all memory blocks freed! %u remain.", pa_atomic_load(&p->stat.n_allocated));
+ }
+
pa_shm_free(&p->memory);
pa_mutex_free(p->mutex);
- pa_cond_free(p->cond);
-
+ pa_semaphore_free(p->semaphore);
+
pa_xfree(p);
}
/* No lock necessary */
const pa_mempool_stat* pa_mempool_get_stat(pa_mempool *p) {
- assert(p);
+ pa_assert(p);
return &p->stat;
}
/* No lock necessary */
+size_t pa_mempool_block_size_max(pa_mempool *p) {
+ pa_assert(p);
+
+ return p->block_size - PA_ALIGN(sizeof(pa_memblock));
+}
+
+/* No lock necessary */
void pa_mempool_vacuum(pa_mempool *p) {
struct mempool_slot *slot;
pa_flist *list;
-
- assert(p);
+
+ pa_assert(p);
list = pa_flist_new(p->n_blocks*2);
@@ -693,9 +754,7 @@ void pa_mempool_vacuum(pa_mempool *p) {
;
while ((slot = pa_flist_pop(list))) {
- pa_shm_punch(&p->memory,
- (uint8_t*) slot - (uint8_t*) p->memory.ptr + sizeof(struct mempool_slot),
- p->block_size - sizeof(struct mempool_slot));
+ pa_shm_punch(&p->memory, (uint8_t*) slot - (uint8_t*) p->memory.ptr, p->block_size);
while (pa_flist_push(p->free_slots, slot))
;
@@ -706,19 +765,19 @@ void pa_mempool_vacuum(pa_mempool *p) {
/* No lock necessary */
int pa_mempool_get_shm_id(pa_mempool *p, uint32_t *id) {
- assert(p);
+ pa_assert(p);
if (!p->memory.shared)
return -1;
*id = p->memory.id;
-
+
return 0;
}
/* No lock necessary */
-int pa_mempool_is_shared(pa_mempool *p) {
- assert(p);
+pa_bool_t pa_mempool_is_shared(pa_mempool *p) {
+ pa_assert(p);
return !!p->memory.shared;
}
@@ -727,11 +786,11 @@ int pa_mempool_is_shared(pa_mempool *p) {
pa_memimport* pa_memimport_new(pa_mempool *p, pa_memimport_release_cb_t cb, void *userdata) {
pa_memimport *i;
- assert(p);
- assert(cb);
-
+ pa_assert(p);
+ pa_assert(cb);
+
i = pa_xnew(pa_memimport, 1);
- i->mutex = pa_mutex_new(0);
+ i->mutex = pa_mutex_new(TRUE, TRUE);
i->pool = p;
i->segments = pa_hashmap_new(NULL, NULL);
i->blocks = pa_hashmap_new(NULL, NULL);
@@ -755,7 +814,7 @@ static pa_memimport_segment* segment_attach(pa_memimport *i, uint32_t shm_id) {
return NULL;
seg = pa_xnew(pa_memimport_segment, 1);
-
+
if (pa_shm_attach_ro(&seg->memory, shm_id) < 0) {
pa_xfree(seg);
return NULL;
@@ -763,14 +822,14 @@ static pa_memimport_segment* segment_attach(pa_memimport *i, uint32_t shm_id) {
seg->import = i;
seg->n_blocks = 0;
-
+
pa_hashmap_put(i->segments, PA_UINT32_TO_PTR(shm_id), seg);
return seg;
}
/* Should be called locked */
static void segment_detach(pa_memimport_segment *seg) {
- assert(seg);
+ pa_assert(seg);
pa_hashmap_remove(seg->import->segments, PA_UINT32_TO_PTR(seg->memory.id));
pa_shm_free(&seg->memory);
@@ -781,16 +840,16 @@ static void segment_detach(pa_memimport_segment *seg) {
void pa_memimport_free(pa_memimport *i) {
pa_memexport *e;
pa_memblock *b;
-
- assert(i);
+
+ pa_assert(i);
pa_mutex_lock(i->mutex);
while ((b = pa_hashmap_get_first(i->blocks)))
memblock_replace_import(b);
- assert(pa_hashmap_size(i->segments) == 0);
-
+ pa_assert(pa_hashmap_size(i->segments) == 0);
+
pa_mutex_unlock(i->mutex);
pa_mutex_lock(i->pool->mutex);
@@ -800,14 +859,14 @@ void pa_memimport_free(pa_memimport *i) {
memexport_revoke_blocks(e, i);
PA_LLIST_REMOVE(pa_memimport, i->pool->imports, i);
-
+
pa_mutex_unlock(i->pool->mutex);
-
+
pa_hashmap_free(i->blocks, NULL, NULL);
pa_hashmap_free(i->segments, NULL, NULL);
pa_mutex_free(i->mutex);
-
+
pa_xfree(i);
}
@@ -815,26 +874,29 @@ void pa_memimport_free(pa_memimport *i) {
pa_memblock* pa_memimport_get(pa_memimport *i, uint32_t block_id, uint32_t shm_id, size_t offset, size_t size) {
pa_memblock *b = NULL;
pa_memimport_segment *seg;
-
- assert(i);
+
+ pa_assert(i);
pa_mutex_lock(i->mutex);
-
+
if (pa_hashmap_size(i->blocks) >= PA_MEMIMPORT_SLOTS_MAX)
goto finish;
- if (!(seg = pa_hashmap_get(i->segments, PA_UINT32_TO_PTR(shm_id))))
+ if (!(seg = pa_hashmap_get(i->segments, PA_UINT32_TO_PTR(shm_id))))
if (!(seg = segment_attach(i, shm_id)))
goto finish;
if (offset+size > seg->memory.size)
goto finish;
- b = pa_xnew(pa_memblock, 1);
+ if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks))))
+ b = pa_xnew(pa_memblock, 1);
+
PA_REFCNT_INIT(b);
b->pool = i->pool;
b->type = PA_MEMBLOCK_IMPORTED;
- b->read_only = 1;
+ b->read_only = TRUE;
+ b->is_silence = FALSE;
pa_atomic_ptr_store(&b->data, (uint8_t*) seg->memory.ptr + offset);
b->length = size;
pa_atomic_store(&b->n_acquired, 0);
@@ -845,44 +907,48 @@ pa_memblock* pa_memimport_get(pa_memimport *i, uint32_t block_id, uint32_t shm_i
pa_hashmap_put(i->blocks, PA_UINT32_TO_PTR(block_id), b);
seg->n_blocks++;
-
+
finish:
pa_mutex_unlock(i->mutex);
if (b)
- stat_add(b);
-
+ stat_add(b);
+
return b;
}
int pa_memimport_process_revoke(pa_memimport *i, uint32_t id) {
pa_memblock *b;
- assert(i);
+ int ret = 0;
+ pa_assert(i);
pa_mutex_lock(i->mutex);
- if (!(b = pa_hashmap_get(i->blocks, PA_UINT32_TO_PTR(id))))
- return -1;
-
+ if (!(b = pa_hashmap_get(i->blocks, PA_UINT32_TO_PTR(id)))) {
+ ret = -1;
+ goto finish;
+ }
+
memblock_replace_import(b);
+finish:
pa_mutex_unlock(i->mutex);
- return 0;
+ return ret;
}
/* For sending blocks to other nodes */
pa_memexport* pa_memexport_new(pa_mempool *p, pa_memexport_revoke_cb_t cb, void *userdata) {
pa_memexport *e;
-
- assert(p);
- assert(cb);
+
+ pa_assert(p);
+ pa_assert(cb);
if (!p->memory.shared)
return NULL;
-
+
e = pa_xnew(pa_memexport, 1);
- e->mutex = pa_mutex_new(1);
+ e->mutex = pa_mutex_new(TRUE, TRUE);
e->pool = p;
PA_LLIST_HEAD_INIT(struct memexport_slot, e->free_slots);
PA_LLIST_HEAD_INIT(struct memexport_slot, e->used_slots);
@@ -893,12 +959,11 @@ pa_memexport* pa_memexport_new(pa_mempool *p, pa_memexport_revoke_cb_t cb, void
pa_mutex_lock(p->mutex);
PA_LLIST_PREPEND(pa_memexport, p->exports, e);
pa_mutex_unlock(p->mutex);
-
return e;
}
void pa_memexport_free(pa_memexport *e) {
- assert(e);
+ pa_assert(e);
pa_mutex_lock(e->mutex);
while (e->used_slots)
@@ -908,18 +973,19 @@ void pa_memexport_free(pa_memexport *e) {
pa_mutex_lock(e->pool->mutex);
PA_LLIST_REMOVE(pa_memexport, e->pool->exports, e);
pa_mutex_unlock(e->pool->mutex);
-
+
+ pa_mutex_free(e->mutex);
pa_xfree(e);
}
/* Self-locked */
int pa_memexport_process_release(pa_memexport *e, uint32_t id) {
pa_memblock *b;
-
- assert(e);
+
+ pa_assert(e);
pa_mutex_lock(e->mutex);
-
+
if (id >= e->n_init)
goto fail;
@@ -933,37 +999,37 @@ int pa_memexport_process_release(pa_memexport *e, uint32_t id) {
PA_LLIST_PREPEND(struct memexport_slot, e->free_slots, &e->slots[id]);
pa_mutex_unlock(e->mutex);
-
+
/* pa_log("Processing release for %u", id); */
- assert(pa_atomic_load(&e->pool->stat.n_exported) > 0);
- assert(pa_atomic_load(&e->pool->stat.exported_size) >= (int) b->length);
-
+ pa_assert(pa_atomic_load(&e->pool->stat.n_exported) > 0);
+ pa_assert(pa_atomic_load(&e->pool->stat.exported_size) >= (int) b->length);
+
pa_atomic_dec(&e->pool->stat.n_exported);
- pa_atomic_add(&e->pool->stat.exported_size, (int) -b->length);
-
+ pa_atomic_sub(&e->pool->stat.exported_size, b->length);
+
pa_memblock_unref(b);
return 0;
fail:
pa_mutex_unlock(e->mutex);
-
+
return -1;
}
/* Self-locked */
static void memexport_revoke_blocks(pa_memexport *e, pa_memimport *i) {
struct memexport_slot *slot, *next;
- assert(e);
- assert(i);
+ pa_assert(e);
+ pa_assert(i);
pa_mutex_lock(e->mutex);
for (slot = e->used_slots; slot; slot = next) {
uint32_t idx;
next = slot->next;
-
+
if (slot->block->type != PA_MEMBLOCK_IMPORTED ||
slot->block->per_type.imported.segment->import != i)
continue;
@@ -980,13 +1046,13 @@ static void memexport_revoke_blocks(pa_memexport *e, pa_memimport *i) {
static pa_memblock *memblock_shared_copy(pa_mempool *p, pa_memblock *b) {
pa_memblock *n;
- assert(p);
- assert(b);
-
+ pa_assert(p);
+ pa_assert(b);
+
if (b->type == PA_MEMBLOCK_IMPORTED ||
b->type == PA_MEMBLOCK_POOL ||
b->type == PA_MEMBLOCK_POOL_EXTERNAL) {
- assert(b->pool == p);
+ pa_assert(b->pool == p);
return pa_memblock_ref(b);
}
@@ -1002,25 +1068,24 @@ int pa_memexport_put(pa_memexport *e, pa_memblock *b, uint32_t *block_id, uint32
pa_shm *memory;
struct memexport_slot *slot;
void *data;
- size_t length;
-
- assert(e);
- assert(b);
- assert(block_id);
- assert(shm_id);
- assert(offset);
- assert(size);
- assert(b->pool == e->pool);
+
+ pa_assert(e);
+ pa_assert(b);
+ pa_assert(block_id);
+ pa_assert(shm_id);
+ pa_assert(offset);
+ pa_assert(size);
+ pa_assert(b->pool == e->pool);
if (!(b = memblock_shared_copy(e->pool, b)))
return -1;
pa_mutex_lock(e->mutex);
-
+
if (e->free_slots) {
slot = e->free_slots;
PA_LLIST_REMOVE(struct memexport_slot, e->free_slots, slot);
- } else if (e->n_init < PA_MEMEXPORT_SLOTS_MAX)
+ } else if (e->n_init < PA_MEMEXPORT_SLOTS_MAX)
slot = &e->slots[e->n_init++];
else {
pa_mutex_unlock(e->mutex);
@@ -1038,25 +1103,25 @@ int pa_memexport_put(pa_memexport *e, pa_memblock *b, uint32_t *block_id, uint32
data = pa_memblock_acquire(b);
if (b->type == PA_MEMBLOCK_IMPORTED) {
- assert(b->per_type.imported.segment);
+ pa_assert(b->per_type.imported.segment);
memory = &b->per_type.imported.segment->memory;
} else {
- assert(b->type == PA_MEMBLOCK_POOL || b->type == PA_MEMBLOCK_POOL_EXTERNAL);
- assert(b->pool);
+ pa_assert(b->type == PA_MEMBLOCK_POOL || b->type == PA_MEMBLOCK_POOL_EXTERNAL);
+ pa_assert(b->pool);
memory = &b->pool->memory;
}
-
- assert(data >= memory->ptr);
- assert((uint8_t*) data + length <= (uint8_t*) memory->ptr + memory->size);
-
+
+ pa_assert(data >= memory->ptr);
+ pa_assert((uint8_t*) data + b->length <= (uint8_t*) memory->ptr + memory->size);
+
*shm_id = memory->id;
*offset = (uint8_t*) data - (uint8_t*) memory->ptr;
- *size = length;
+ *size = b->length;
pa_memblock_release(b);
-
+
pa_atomic_inc(&e->pool->stat.n_exported);
- pa_atomic_add(&e->pool->stat.exported_size, (int) length);
+ pa_atomic_add(&e->pool->stat.exported_size, b->length);
return 0;
}
diff --git a/src/pulsecore/memblock.h b/src/pulsecore/memblock.h
index 9937818f..efe55b02 100644
--- a/src/pulsecore/memblock.h
+++ b/src/pulsecore/memblock.h
@@ -1,21 +1,22 @@
#ifndef foopulsememblockhfoo
#define foopulsememblockhfoo
-/* $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.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
@@ -28,6 +29,7 @@
#include <pulse/def.h>
#include <pulsecore/llist.h>
#include <pulsecore/refcnt.h>
+#include <pulsecore/atomic.h>
/* A pa_memblock is a reference counted memory block. PulseAudio
* passed references to pa_memblocks around instead of copying
@@ -38,7 +40,7 @@
typedef enum pa_memblock_type {
PA_MEMBLOCK_POOL, /* Memory is part of the memory pool */
PA_MEMBLOCK_POOL_EXTERNAL, /* Data memory is part of the memory pool but the pa_memblock structure itself not */
- PA_MEMBLOCK_APPENDED, /* the data is appended to the memory block */
+ PA_MEMBLOCK_APPENDED, /* the data is appended to the memory block */
PA_MEMBLOCK_USER, /* User supplied memory, to be freed with free_cb */
PA_MEMBLOCK_FIXED, /* data is a pointer to fixed memory that needs not to be freed */
PA_MEMBLOCK_IMPORTED, /* Memory is imported from another process via shm */
@@ -60,20 +62,20 @@ typedef void (*pa_memexport_revoke_cb_t)(pa_memexport *e, uint32_t block_id, voi
* n_accumulated is not yet. Take these values with a grain of salt,
* they are here for purely statistical reasons.*/
struct pa_mempool_stat {
- pa_atomic_int_t n_allocated;
- pa_atomic_int_t n_accumulated;
- pa_atomic_int_t n_imported;
- pa_atomic_int_t n_exported;
- pa_atomic_int_t allocated_size;
- pa_atomic_int_t accumulated_size;
- pa_atomic_int_t imported_size;
- pa_atomic_int_t exported_size;
-
- pa_atomic_int_t n_too_large_for_pool;
- pa_atomic_int_t n_pool_full;
-
- pa_atomic_int_t n_allocated_by_type[PA_MEMBLOCK_TYPE_MAX];
- pa_atomic_int_t n_accumulated_by_type[PA_MEMBLOCK_TYPE_MAX];
+ pa_atomic_t n_allocated;
+ pa_atomic_t n_accumulated;
+ pa_atomic_t n_imported;
+ pa_atomic_t n_exported;
+ pa_atomic_t allocated_size;
+ pa_atomic_t accumulated_size;
+ pa_atomic_t imported_size;
+ pa_atomic_t exported_size;
+
+ pa_atomic_t n_too_large_for_pool;
+ pa_atomic_t n_pool_full;
+
+ pa_atomic_t n_allocated_by_type[PA_MEMBLOCK_TYPE_MAX];
+ pa_atomic_t n_accumulated_by_type[PA_MEMBLOCK_TYPE_MAX];
};
/* Allocate a new memory block of type PA_MEMBLOCK_MEMPOOL or PA_MEMBLOCK_APPENDED, depending on the size */
@@ -83,13 +85,13 @@ pa_memblock *pa_memblock_new(pa_mempool *, size_t length);
pa_memblock *pa_memblock_new_pool(pa_mempool *, size_t length);
/* Allocate a new memory block of type PA_MEMBLOCK_USER */
-pa_memblock *pa_memblock_new_user(pa_mempool *, void *data, size_t length, void (*free_cb)(void *p), int read_only);
+pa_memblock *pa_memblock_new_user(pa_mempool *, void *data, size_t length, pa_free_cb_t free_cb, pa_bool_t read_only);
/* A special case of pa_memblock_new_user: take a memory buffer previously allocated with pa_xmalloc() */
#define pa_memblock_new_malloced(p,data,length) pa_memblock_new_user(p, data, length, pa_xfree, 0)
/* Allocate a new memory block of type PA_MEMBLOCK_FIXED */
-pa_memblock *pa_memblock_new_fixed(pa_mempool *, void *data, size_t length, int read_only);
+pa_memblock *pa_memblock_new_fixed(pa_mempool *, void *data, size_t length, pa_bool_t read_only);
void pa_memblock_unref(pa_memblock*b);
pa_memblock* pa_memblock_ref(pa_memblock*b);
@@ -102,19 +104,26 @@ function is not multiple caller safe, i.e. needs to be locked
manually if called from more than one thread at the same time. */
void pa_memblock_unref_fixed(pa_memblock*b);
-int pa_memblock_is_read_only(pa_memblock *b);
+pa_bool_t pa_memblock_is_read_only(pa_memblock *b);
+pa_bool_t pa_memblock_is_silence(pa_memblock *b);
+pa_bool_t pa_memblock_ref_is_one(pa_memblock *b);
+void pa_memblock_set_is_silence(pa_memblock *b, pa_bool_t v);
+
void* pa_memblock_acquire(pa_memblock *b);
void pa_memblock_release(pa_memblock *b);
size_t pa_memblock_get_length(pa_memblock *b);
pa_mempool * pa_memblock_get_pool(pa_memblock *b);
+pa_memblock *pa_memblock_will_need(pa_memblock *b);
+
/* The memory block manager */
-pa_mempool* pa_mempool_new(int shared);
+pa_mempool* pa_mempool_new(pa_bool_t shared);
void pa_mempool_free(pa_mempool *p);
const pa_mempool_stat* pa_mempool_get_stat(pa_mempool *p);
void pa_mempool_vacuum(pa_mempool *p);
int pa_mempool_get_shm_id(pa_mempool *p, uint32_t *id);
-int pa_mempool_is_shared(pa_mempool *p);
+pa_bool_t pa_mempool_is_shared(pa_mempool *p);
+size_t pa_mempool_block_size_max(pa_mempool *p);
/* For recieving blocks from other nodes */
pa_memimport* pa_memimport_new(pa_mempool *p, pa_memimport_release_cb_t cb, void *userdata);
diff --git a/src/pulsecore/memblockq.c b/src/pulsecore/memblockq.c
index dab44dc3..a9f28a07 100644
--- a/src/pulsecore/memblockq.c
+++ b/src/pulsecore/memblockq.c
@@ -1,18 +1,18 @@
-/* $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
@@ -26,7 +26,6 @@
#include <sys/time.h>
#include <time.h>
#include <stdio.h>
-#include <assert.h>
#include <stdlib.h>
#include <string.h>
@@ -34,23 +33,30 @@
#include <pulsecore/log.h>
#include <pulsecore/mcalign.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/flist.h>
#include "memblockq.h"
-struct memblock_list {
- struct memblock_list *next, *prev;
+struct list_item {
+ struct list_item *next, *prev;
int64_t index;
pa_memchunk chunk;
};
+PA_STATIC_FLIST_DECLARE(list_items, 0, pa_xfree);
+
struct pa_memblockq {
- struct memblock_list *blocks, *blocks_tail;
+ struct list_item *blocks, *blocks_tail;
+ struct list_item *current_read, *current_write;
unsigned n_blocks;
- size_t maxlength, tlength, base, prebuf, minreq;
+ size_t maxlength, tlength, base, prebuf, minreq, maxrewind;
int64_t read_index, write_index;
- enum { PREBUF, RUNNING } state;
- pa_memblock *silence;
+ pa_bool_t in_prebuf;
+ pa_memchunk silence;
pa_mcalign *mcalign;
+ int64_t missing;
+ size_t requested;
};
pa_memblockq* pa_memblockq_new(
@@ -60,93 +66,165 @@ pa_memblockq* pa_memblockq_new(
size_t base,
size_t prebuf,
size_t minreq,
- pa_memblock *silence) {
-
+ size_t maxrewind,
+ pa_memchunk *silence) {
+
pa_memblockq* bq;
-
- assert(base > 0);
- assert(maxlength >= base);
-
+
+ pa_assert(base > 0);
+
bq = pa_xnew(pa_memblockq, 1);
bq->blocks = bq->blocks_tail = NULL;
+ bq->current_read = bq->current_write = NULL;
bq->n_blocks = 0;
bq->base = base;
bq->read_index = bq->write_index = idx;
- pa_log_debug("memblockq requested: maxlength=%lu, tlength=%lu, base=%lu, prebuf=%lu, minreq=%lu",
- (unsigned long)maxlength, (unsigned long)tlength, (unsigned long)base, (unsigned long)prebuf, (unsigned long)minreq);
+ pa_log_debug("memblockq requested: maxlength=%lu, tlength=%lu, base=%lu, prebuf=%lu, minreq=%lu maxrewind=%lu",
+ (unsigned long) maxlength, (unsigned long) tlength, (unsigned long) base, (unsigned long) prebuf, (unsigned long) minreq, (unsigned long) maxrewind);
- bq->maxlength = ((maxlength+base-1)/base)*base;
- assert(bq->maxlength >= base);
+ bq->missing = bq->requested = bq->maxlength = bq->tlength = bq->prebuf = bq->minreq = bq->maxrewind = 0;
+ bq->in_prebuf = TRUE;
- bq->tlength = ((tlength+base-1)/base)*base;
- if (!bq->tlength || bq->tlength >= bq->maxlength)
- bq->tlength = bq->maxlength;
+ pa_memblockq_set_maxlength(bq, maxlength);
+ pa_memblockq_set_tlength(bq, tlength);
+ pa_memblockq_set_prebuf(bq, prebuf);
+ pa_memblockq_set_minreq(bq, minreq);
+ pa_memblockq_set_maxrewind(bq, maxrewind);
+
+ pa_log_debug("memblockq sanitized: maxlength=%lu, tlength=%lu, base=%lu, prebuf=%lu, minreq=%lu maxrewind=%lu",
+ (unsigned long) bq->maxlength, (unsigned long) bq->tlength, (unsigned long) bq->base, (unsigned long) bq->prebuf, (unsigned long) bq->minreq, (unsigned long) bq->maxrewind);
+
+ if (silence) {
+ bq->silence = *silence;
+ pa_memblock_ref(bq->silence.memblock);
+ } else
+ pa_memchunk_reset(&bq->silence);
+
+ bq->mcalign = pa_mcalign_new(bq->base);
- bq->prebuf = (prebuf == (size_t) -1) ? bq->tlength/2 : prebuf;
- bq->prebuf = ((bq->prebuf+base-1)/base)*base;
- if (bq->prebuf > bq->maxlength)
- bq->prebuf = bq->maxlength;
-
- bq->minreq = (minreq/base)*base;
-
- if (bq->minreq > bq->tlength - bq->prebuf)
- bq->minreq = bq->tlength - bq->prebuf;
-
- if (!bq->minreq)
- bq->minreq = 1;
-
- pa_log_debug("memblockq sanitized: maxlength=%lu, tlength=%lu, base=%lu, prebuf=%lu, minreq=%lu",
- (unsigned long)bq->maxlength, (unsigned long)bq->tlength, (unsigned long)bq->base, (unsigned long)bq->prebuf, (unsigned long)bq->minreq);
-
- bq->state = bq->prebuf ? PREBUF : RUNNING;
- bq->silence = silence ? pa_memblock_ref(silence) : NULL;
- bq->mcalign = NULL;
-
return bq;
}
void pa_memblockq_free(pa_memblockq* bq) {
- assert(bq);
+ pa_assert(bq);
pa_memblockq_flush(bq);
- if (bq->silence)
- pa_memblock_unref(bq->silence);
+ if (bq->silence.memblock)
+ pa_memblock_unref(bq->silence.memblock);
if (bq->mcalign)
pa_mcalign_free(bq->mcalign);
-
+
pa_xfree(bq);
}
-static void drop_block(pa_memblockq *bq, struct memblock_list *q) {
- assert(bq);
- assert(q);
+static void fix_current_read(pa_memblockq *bq) {
+ pa_assert(bq);
+
+ if (PA_UNLIKELY(!bq->blocks)) {
+ bq->current_read = NULL;
+ return;
+ }
+
+ if (PA_UNLIKELY(!bq->current_read))
+ bq->current_read = bq->blocks;
+
+ /* Scan left */
+ while (PA_UNLIKELY(bq->current_read->index > bq->read_index))
+
+ if (bq->current_read->prev)
+ bq->current_read = bq->current_read->prev;
+ else
+ break;
+
+ /* Scan right */
+ while (PA_LIKELY(bq->current_read != NULL) && PA_UNLIKELY(bq->current_read->index + (int64_t) bq->current_read->chunk.length <= bq->read_index))
+ bq->current_read = bq->current_read->next;
+
+ /* At this point current_read will either point at or left of the
+ next block to play. It may be NULL in case everything in
+ the queue was already played */
+}
+
+static void fix_current_write(pa_memblockq *bq) {
+ pa_assert(bq);
+
+ if (PA_UNLIKELY(!bq->blocks)) {
+ bq->current_write = NULL;
+ return;
+ }
+
+ if (PA_UNLIKELY(!bq->current_write))
+ bq->current_write = bq->blocks_tail;
+
+ /* Scan right */
+ while (PA_UNLIKELY(bq->current_write->index + (int64_t) bq->current_write->chunk.length <= bq->write_index))
+
+ if (bq->current_write->next)
+ bq->current_write = bq->current_write->next;
+ else
+ break;
+
+ /* Scan left */
+ while (PA_LIKELY(bq->current_write != NULL) && PA_UNLIKELY(bq->current_write->index > bq->write_index))
+ bq->current_write = bq->current_write->prev;
+
+ /* At this point current_write will either point at or right of
+ the next block to write data to. It may be NULL in case
+ everything in the queue is still to be played */
+}
+
+static void drop_block(pa_memblockq *bq, struct list_item *q) {
+ pa_assert(bq);
+ pa_assert(q);
+
+ pa_assert(bq->n_blocks >= 1);
- assert(bq->n_blocks >= 1);
-
if (q->prev)
q->prev->next = q->next;
- else
+ else {
+ pa_assert(bq->blocks == q);
bq->blocks = q->next;
-
+ }
+
if (q->next)
q->next->prev = q->prev;
- else
+ else {
+ pa_assert(bq->blocks_tail == q);
bq->blocks_tail = q->prev;
+ }
+
+ if (bq->current_write == q)
+ bq->current_write = q->prev;
+
+ if (bq->current_read == q)
+ bq->current_read = q->next;
pa_memblock_unref(q->chunk.memblock);
- pa_xfree(q);
+
+ if (pa_flist_push(PA_STATIC_FLIST_GET(list_items), q) < 0)
+ pa_xfree(q);
bq->n_blocks--;
}
-static int can_push(pa_memblockq *bq, size_t l) {
+static void drop_backlog(pa_memblockq *bq) {
+ int64_t boundary;
+ pa_assert(bq);
+
+ boundary = bq->read_index - bq->maxrewind;
+
+ while (bq->blocks && (bq->blocks->index + (int64_t) bq->blocks->chunk.length <= boundary))
+ drop_block(bq, bq->blocks);
+}
+
+static pa_bool_t can_push(pa_memblockq *bq, size_t l) {
int64_t end;
- assert(bq);
+ pa_assert(bq);
if (bq->read_index > bq->write_index) {
size_t d = bq->read_index - bq->write_index;
@@ -154,29 +232,29 @@ static int can_push(pa_memblockq *bq, size_t l) {
if (l > d)
l -= d;
else
- return 1;
+ return TRUE;
}
- end = bq->blocks_tail ? bq->blocks_tail->index + bq->blocks_tail->chunk.length : 0;
+ end = bq->blocks_tail ? bq->blocks_tail->index + (int64_t) bq->blocks_tail->chunk.length : bq->write_index;
/* Make sure that the list doesn't get too long */
- if (bq->write_index + (int64_t)l > end)
+ if (bq->write_index + (int64_t) l > end)
if (bq->write_index + l - bq->read_index > bq->maxlength)
- return 0;
+ return FALSE;
- return 1;
+ return TRUE;
}
int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {
-
- struct memblock_list *q, *n;
+ struct list_item *q, *n;
pa_memchunk chunk;
-
- assert(bq);
- assert(uchunk);
- assert(uchunk->memblock);
- assert(uchunk->length > 0);
- assert(uchunk->index + uchunk->length <= pa_memblock_get_length(uchunk->memblock));
+ int64_t old, delta;
+
+ pa_assert(bq);
+ pa_assert(uchunk);
+ pa_assert(uchunk->memblock);
+ pa_assert(uchunk->length > 0);
+ pa_assert(uchunk->index + uchunk->length <= pa_memblock_get_length(uchunk->memblock));
if (uchunk->length % bq->base)
return -1;
@@ -184,36 +262,35 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {
if (!can_push(bq, uchunk->length))
return -1;
+ old = bq->write_index;
chunk = *uchunk;
-
- if (bq->read_index > bq->write_index) {
- /* We currently have a buffer underflow, we need to drop some
- * incoming data */
+ fix_current_write(bq);
+ q = bq->current_write;
- size_t d = bq->read_index - bq->write_index;
+ /* First we advance the q pointer right of where we want to
+ * write to */
- if (chunk.length > d) {
- chunk.index += d;
- chunk.length -= d;
- bq->write_index = bq->read_index;
- } else {
- /* We drop the incoming data completely */
- bq->write_index += chunk.length;
- return 0;
- }
+ if (q) {
+ while (bq->write_index + (int64_t) chunk.length > q->index)
+ if (q->next)
+ q = q->next;
+ else
+ break;
}
-
+
+ if (!q)
+ q = bq->blocks_tail;
+
/* We go from back to front to look for the right place to add
* this new entry. Drop data we will overwrite on the way */
- q = bq->blocks_tail;
while (q) {
- if (bq->write_index >= q->index + (int64_t)q->chunk.length)
+ if (bq->write_index >= q->index + (int64_t) q->chunk.length)
/* We found the entry where we need to place the new entry immediately after */
break;
- else if (bq->write_index + (int64_t)chunk.length <= q->index) {
+ else if (bq->write_index + (int64_t) chunk.length <= q->index) {
/* This entry isn't touched at all, let's skip it */
q = q->prev;
} else if (bq->write_index <= q->index &&
@@ -221,7 +298,7 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {
/* This entry is fully replaced by the new entry, so let's drop it */
- struct memblock_list *p;
+ struct list_item *p;
p = q;
q = q->prev;
drop_block(bq, p);
@@ -232,17 +309,19 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {
if (bq->write_index + chunk.length < q->index + q->chunk.length) {
/* We need to save the end of this memchunk */
- struct memblock_list *p;
+ struct list_item *p;
size_t d;
/* Create a new list entry for the end of thie memchunk */
- p = pa_xnew(struct memblock_list, 1);
+ if (!(p = pa_flist_pop(PA_STATIC_FLIST_GET(list_items))))
+ p = pa_xnew(struct list_item, 1);
+
p->chunk = q->chunk;
pa_memblock_ref(p->chunk.memblock);
/* Calculate offset */
d = bq->write_index + chunk.length - q->index;
- assert(d > 0);
+ pa_assert(d > 0);
/* Drop it from the new entry */
p->index = q->index + d;
@@ -261,7 +340,7 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {
/* Truncate the chunk */
if (!(q->chunk.length = bq->write_index - q->index)) {
- struct memblock_list *p;
+ struct list_item *p;
p = q;
q = q->prev;
drop_block(bq, p);
@@ -272,41 +351,41 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {
} else {
size_t d;
- assert(bq->write_index + (int64_t)chunk.length > q->index &&
+ pa_assert(bq->write_index + (int64_t)chunk.length > q->index &&
bq->write_index + (int64_t)chunk.length < q->index + (int64_t)q->chunk.length &&
bq->write_index < q->index);
-
+
/* The job overwrites the current entry at the end, so let's drop the beginning of this entry */
d = bq->write_index + chunk.length - q->index;
q->index += d;
q->chunk.index += d;
q->chunk.length -= d;
-
+
q = q->prev;
}
-
}
if (q) {
- assert(bq->write_index >= q->index + (int64_t)q->chunk.length);
- assert(!q->next || (bq->write_index + (int64_t)chunk.length <= q->next->index));
-
+ pa_assert(bq->write_index >= q->index + (int64_t)q->chunk.length);
+ pa_assert(!q->next || (bq->write_index + (int64_t)chunk.length <= q->next->index));
+
/* Try to merge memory blocks */
-
+
if (q->chunk.memblock == chunk.memblock &&
q->chunk.index + (int64_t)q->chunk.length == chunk.index &&
bq->write_index == q->index + (int64_t)q->chunk.length) {
-
+
q->chunk.length += chunk.length;
bq->write_index += chunk.length;
- return 0;
+ goto finish;
}
} else
- assert(!bq->blocks || (bq->write_index + (int64_t)chunk.length <= bq->blocks->index));
+ pa_assert(!bq->blocks || (bq->write_index + (int64_t)chunk.length <= bq->blocks->index));
+ if (!(n = pa_flist_pop(PA_STATIC_FLIST_GET(list_items))))
+ n = pa_xnew(struct list_item, 1);
- n = pa_xnew(struct memblock_list, 1);
n->chunk = chunk;
pa_memblock_ref(n->chunk.memblock);
n->index = bq->write_index;
@@ -324,53 +403,95 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {
n->prev->next = n;
else
bq->blocks = n;
-
+
bq->n_blocks++;
+
+finish:
+
+ delta = bq->write_index - old;
+
+ if (delta >= (int64_t) bq->requested) {
+ delta -= bq->requested;
+ bq->requested = 0;
+ } else {
+ bq->requested -= delta;
+ delta = 0;
+ }
+
+ bq->missing -= delta;
+
return 0;
}
-int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk) {
- assert(bq);
- assert(chunk);
+pa_bool_t pa_memblockq_prebuf_active(pa_memblockq *bq) {
+ pa_assert(bq);
- if (bq->state == PREBUF) {
+ if (bq->in_prebuf)
+ return pa_memblockq_get_length(bq) < bq->prebuf;
+ else
+ return bq->prebuf > 0 && bq->read_index >= bq->write_index;
+}
+
+static pa_bool_t update_prebuf(pa_memblockq *bq) {
+ pa_assert(bq);
+
+ if (bq->in_prebuf) {
- /* We need to pre-buffer */
if (pa_memblockq_get_length(bq) < bq->prebuf)
- return -1;
+ return TRUE;
- bq->state = RUNNING;
+ bq->in_prebuf = FALSE;
+ return FALSE;
+ } else {
- } else if (bq->prebuf > 0 && bq->read_index >= bq->write_index) {
+ if (bq->prebuf > 0 && bq->read_index >= bq->write_index) {
+ bq->in_prebuf = TRUE;
+ return TRUE;
+ }
- /* Buffer underflow protection */
- bq->state = PREBUF;
- return -1;
+ return FALSE;
}
-
+}
+
+int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk) {
+ int64_t d;
+ pa_assert(bq);
+ pa_assert(chunk);
+
+ /* We need to pre-buffer */
+ if (update_prebuf(bq))
+ return -1;
+
+ fix_current_read(bq);
+
/* Do we need to spit out silence? */
- if (!bq->blocks || bq->blocks->index > bq->read_index) {
+ if (!bq->current_read || bq->current_read->index > bq->read_index) {
size_t length;
/* How much silence shall we return? */
- length = bq->blocks ? bq->blocks->index - bq->read_index : 0;
+ if (bq->current_read)
+ length = bq->current_read->index - bq->read_index;
+ else if (bq->write_index > bq->read_index)
+ length = (size_t) (bq->write_index - bq->read_index);
+ else
+ length = 0;
/* We need to return silence, since no data is yet available */
- if (bq->silence) {
- chunk->memblock = pa_memblock_ref(bq->silence);
+ if (bq->silence.memblock) {
+ *chunk = bq->silence;
+ pa_memblock_ref(chunk->memblock);
+
+ if (length > 0 && length < chunk->length)
+ chunk->length = length;
- if (!length || length > pa_memblock_get_length(chunk->memblock))
- length = pa_memblock_get_length(chunk->memblock);
-
- chunk->length = length;
} else {
/* If the memblockq is empty, return -1, otherwise return
* the time to sleep */
- if (!bq->blocks)
+ if (length <= 0)
return -1;
-
+
chunk->memblock = NULL;
chunk->length = length;
}
@@ -380,89 +501,48 @@ int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk) {
}
/* Ok, let's pass real data to the caller */
- assert(bq->blocks->index == bq->read_index);
-
- *chunk = bq->blocks->chunk;
+ *chunk = bq->current_read->chunk;
pa_memblock_ref(chunk->memblock);
+ pa_assert(bq->read_index >= bq->current_read->index);
+ d = bq->read_index - bq->current_read->index;
+ chunk->index += d;
+ chunk->length -= d;
+
return 0;
}
-void pa_memblockq_drop(pa_memblockq *bq, const pa_memchunk *chunk, size_t length) {
- assert(bq);
- assert(length % bq->base == 0);
-
- assert(!chunk || length <= chunk->length);
-
- if (chunk) {
-
- if (bq->blocks && bq->blocks->index == bq->read_index) {
- /* The first item in queue is valid */
-
- /* Does the chunk match with what the user supplied us? */
- if (memcmp(chunk, &bq->blocks->chunk, sizeof(pa_memchunk)) != 0)
- return;
-
- } else {
- size_t l;
-
- /* The first item in the queue is not yet relevant */
-
- assert(!bq->blocks || bq->blocks->index > bq->read_index);
- l = bq->blocks ? bq->blocks->index - bq->read_index : 0;
-
- if (bq->silence) {
-
- if (!l || l > pa_memblock_get_length(bq->silence))
- l = pa_memblock_get_length(bq->silence);
-
- }
+void pa_memblockq_drop(pa_memblockq *bq, size_t length) {
+ int64_t old, delta;
+ pa_assert(bq);
+ pa_assert(length % bq->base == 0);
- /* Do the entries still match? */
- if (chunk->index != 0 || chunk->length != l || chunk->memblock != bq->silence)
- return;
- }
- }
+ old = bq->read_index;
while (length > 0) {
- if (bq->blocks) {
- size_t d;
+ /* Do not drop any data when we are in prebuffering mode */
+ if (update_prebuf(bq))
+ break;
- assert(bq->blocks->index >= bq->read_index);
+ fix_current_read(bq);
- d = (size_t) (bq->blocks->index - bq->read_index);
-
- if (d >= length) {
- /* The first block is too far in the future */
-
- bq->read_index += length;
- break;
- } else {
-
- length -= d;
- bq->read_index += d;
- }
+ if (bq->current_read) {
+ int64_t p, d;
- assert(bq->blocks->index == bq->read_index);
+ /* We go through this piece by piece to make sure we don't
+ * drop more than allowed by prebuf */
- if (bq->blocks->chunk.length <= length) {
- /* We need to drop the full block */
+ p = bq->current_read->index + bq->current_read->chunk.length;
+ pa_assert(p >= bq->read_index);
+ d = p - bq->read_index;
- length -= bq->blocks->chunk.length;
- bq->read_index += bq->blocks->chunk.length;
+ if (d > (int64_t) length)
+ d = length;
- drop_block(bq, bq->blocks);
- } else {
- /* Only the start of this block needs to be dropped */
+ bq->read_index += d;
+ length -= d;
- bq->blocks->chunk.index += length;
- bq->blocks->chunk.length -= length;
- bq->blocks->index += length;
- bq->read_index += length;
- break;
- }
-
} else {
/* The list is empty, there's nothing we could drop */
@@ -470,170 +550,368 @@ void pa_memblockq_drop(pa_memblockq *bq, const pa_memchunk *chunk, size_t length
break;
}
}
-}
-int pa_memblockq_is_readable(pa_memblockq *bq) {
- assert(bq);
+ drop_backlog(bq);
- if (bq->prebuf > 0) {
- size_t l = pa_memblockq_get_length(bq);
-
- if (bq->state == PREBUF && l < bq->prebuf)
- return 0;
+ delta = bq->read_index - old;
+ bq->missing += delta;
+}
- if (l <= 0)
- return 0;
- }
+void pa_memblockq_rewind(pa_memblockq *bq, size_t length) {
+ pa_assert(bq);
+ pa_assert(length % bq->base == 0);
+
+ /* This is kind of the inverse of pa_memblockq_drop() */
- return 1;
+ bq->read_index -= length;
+ bq->missing -= length;
}
-int pa_memblockq_is_writable(pa_memblockq *bq, size_t length) {
- assert(bq);
+pa_bool_t pa_memblockq_is_readable(pa_memblockq *bq) {
+ pa_assert(bq);
- if (length % bq->base)
- return 0;
-
- return pa_memblockq_get_length(bq) + length <= bq->tlength;
+ if (pa_memblockq_prebuf_active(bq))
+ return FALSE;
+
+ if (pa_memblockq_get_length(bq) <= 0)
+ return FALSE;
+
+ return TRUE;
}
size_t pa_memblockq_get_length(pa_memblockq *bq) {
- assert(bq);
+ pa_assert(bq);
if (bq->write_index <= bq->read_index)
return 0;
-
+
return (size_t) (bq->write_index - bq->read_index);
}
size_t pa_memblockq_missing(pa_memblockq *bq) {
size_t l;
- assert(bq);
+ pa_assert(bq);
if ((l = pa_memblockq_get_length(bq)) >= bq->tlength)
return 0;
l = bq->tlength - l;
- return (l >= bq->minreq) ? l : 0;
-}
-
-size_t pa_memblockq_get_minreq(pa_memblockq *bq) {
- assert(bq);
- return bq->minreq;
+ return l >= bq->minreq ? l : 0;
}
void pa_memblockq_seek(pa_memblockq *bq, int64_t offset, pa_seek_mode_t seek) {
- assert(bq);
+ int64_t old, delta;
+ pa_assert(bq);
+
+ old = bq->write_index;
switch (seek) {
case PA_SEEK_RELATIVE:
bq->write_index += offset;
- return;
+ break;
case PA_SEEK_ABSOLUTE:
bq->write_index = offset;
- return;
+ break;
case PA_SEEK_RELATIVE_ON_READ:
bq->write_index = bq->read_index + offset;
- return;
+ break;
case PA_SEEK_RELATIVE_END:
- bq->write_index = (bq->blocks_tail ? bq->blocks_tail->index + (int64_t)bq->blocks_tail->chunk.length : bq->read_index) + offset;
- return;
+ bq->write_index = (bq->blocks_tail ? bq->blocks_tail->index + (int64_t) bq->blocks_tail->chunk.length : bq->read_index) + offset;
+ break;
+ default:
+ pa_assert_not_reached();
}
- assert(0);
+ drop_backlog(bq);
+
+ delta = bq->write_index - old;
+
+ if (delta >= (int64_t) bq->requested) {
+ delta -= bq->requested;
+ bq->requested = 0;
+ } else if (delta >= 0) {
+ bq->requested -= delta;
+ delta = 0;
+ }
+
+ bq->missing -= delta;
}
void pa_memblockq_flush(pa_memblockq *bq) {
- assert(bq);
-
- while (bq->blocks)
- drop_block(bq, bq->blocks);
+ int64_t old, delta;
+ pa_assert(bq);
- assert(bq->n_blocks == 0);
+ pa_memblockq_silence(bq);
+ old = bq->write_index;
bq->write_index = bq->read_index;
pa_memblockq_prebuf_force(bq);
+
+ delta = bq->write_index - old;
+
+ if (delta >= (int64_t) bq->requested) {
+ delta -= bq->requested;
+ bq->requested = 0;
+ } else if (delta >= 0) {
+ bq->requested -= delta;
+ delta = 0;
+ }
+
+ bq->missing -= delta;
}
size_t pa_memblockq_get_tlength(pa_memblockq *bq) {
- assert(bq);
-
+ pa_assert(bq);
+
return bq->tlength;
}
+size_t pa_memblockq_get_minreq(pa_memblockq *bq) {
+ pa_assert(bq);
+
+ return bq->minreq;
+}
+
int64_t pa_memblockq_get_read_index(pa_memblockq *bq) {
- assert(bq);
+ pa_assert(bq);
+
return bq->read_index;
}
int64_t pa_memblockq_get_write_index(pa_memblockq *bq) {
- assert(bq);
+ pa_assert(bq);
+
return bq->write_index;
}
int pa_memblockq_push_align(pa_memblockq* bq, const pa_memchunk *chunk) {
pa_memchunk rchunk;
- assert(bq);
- assert(chunk && bq->base);
-
+ pa_assert(bq);
+ pa_assert(chunk);
+
if (bq->base == 1)
return pa_memblockq_push(bq, chunk);
-
- if (!bq->mcalign)
- bq->mcalign = pa_mcalign_new(bq->base);
if (!can_push(bq, pa_mcalign_csize(bq->mcalign, chunk->length)))
return -1;
-
+
pa_mcalign_push(bq->mcalign, chunk);
-
+
while (pa_mcalign_pop(bq->mcalign, &rchunk) >= 0) {
int r;
r = pa_memblockq_push(bq, &rchunk);
pa_memblock_unref(rchunk.memblock);
- if (r < 0)
+ if (r < 0) {
+ pa_mcalign_flush(bq->mcalign);
return -1;
+ }
}
return 0;
}
-void pa_memblockq_shorten(pa_memblockq *bq, size_t length) {
- size_t l;
- assert(bq);
-
- l = pa_memblockq_get_length(bq);
-
- if (l > length)
- pa_memblockq_drop(bq, NULL, l - length);
-}
-
void pa_memblockq_prebuf_disable(pa_memblockq *bq) {
- assert(bq);
+ pa_assert(bq);
- if (bq->state == PREBUF)
- bq->state = RUNNING;
+ bq->in_prebuf = FALSE;
}
void pa_memblockq_prebuf_force(pa_memblockq *bq) {
- assert(bq);
+ pa_assert(bq);
- if (bq->state == RUNNING && bq->prebuf > 0)
- bq->state = PREBUF;
+ if (bq->prebuf > 0)
+ bq->in_prebuf = TRUE;
}
size_t pa_memblockq_get_maxlength(pa_memblockq *bq) {
- assert(bq);
+ pa_assert(bq);
return bq->maxlength;
}
size_t pa_memblockq_get_prebuf(pa_memblockq *bq) {
- assert(bq);
+ pa_assert(bq);
return bq->prebuf;
}
+
+size_t pa_memblockq_pop_missing(pa_memblockq *bq) {
+ size_t l;
+
+ pa_assert(bq);
+
+/* pa_log("pop: %lli", bq->missing); */
+
+ if (bq->missing <= 0)
+ return 0;
+
+ l = (size_t) bq->missing;
+ bq->missing = 0;
+ bq->requested += l;
+
+ return l;
+}
+
+void pa_memblockq_set_maxlength(pa_memblockq *bq, size_t maxlength) {
+ pa_assert(bq);
+
+ bq->maxlength = ((maxlength+bq->base-1)/bq->base)*bq->base;
+
+ if (bq->maxlength < bq->base)
+ bq->maxlength = bq->base;
+
+ if (bq->tlength > bq->maxlength)
+ pa_memblockq_set_tlength(bq, bq->maxlength);
+
+ if (bq->prebuf > bq->maxlength)
+ pa_memblockq_set_prebuf(bq, bq->maxlength);
+}
+
+void pa_memblockq_set_tlength(pa_memblockq *bq, size_t tlength) {
+ size_t old_tlength;
+ pa_assert(bq);
+
+ if (tlength <= 0)
+ tlength = bq->maxlength;
+
+ old_tlength = bq->tlength;
+ bq->tlength = ((tlength+bq->base-1)/bq->base)*bq->base;
+
+ if (bq->tlength > bq->maxlength)
+ bq->tlength = bq->maxlength;
+
+ if (bq->prebuf > bq->tlength)
+ pa_memblockq_set_prebuf(bq, bq->tlength);
+
+ if (bq->minreq > bq->tlength)
+ pa_memblockq_set_minreq(bq, bq->tlength);
+
+ bq->missing += (int64_t) bq->tlength - (int64_t) old_tlength;
+}
+
+void pa_memblockq_set_prebuf(pa_memblockq *bq, size_t prebuf) {
+ pa_assert(bq);
+
+ if (prebuf == (size_t) -1)
+ prebuf = bq->tlength;
+
+ bq->prebuf = ((prebuf+bq->base-1)/bq->base)*bq->base;
+
+ if (prebuf > 0 && bq->prebuf < bq->base)
+ bq->prebuf = bq->base;
+
+ if (bq->prebuf > bq->tlength)
+ bq->prebuf = bq->tlength;
+
+ if (bq->prebuf <= 0 || pa_memblockq_get_length(bq) >= bq->prebuf)
+ bq->in_prebuf = FALSE;
+
+ if (bq->minreq > bq->prebuf)
+ pa_memblockq_set_minreq(bq, bq->prebuf);
+}
+
+void pa_memblockq_set_minreq(pa_memblockq *bq, size_t minreq) {
+ pa_assert(bq);
+
+ bq->minreq = (minreq/bq->base)*bq->base;
+
+ if (bq->minreq > bq->tlength)
+ bq->minreq = bq->tlength;
+
+ if (bq->minreq > bq->prebuf)
+ bq->minreq = bq->prebuf;
+
+ if (bq->minreq < bq->base)
+ bq->minreq = bq->base;
+}
+
+void pa_memblockq_set_maxrewind(pa_memblockq *bq, size_t maxrewind) {
+ pa_assert(bq);
+
+ bq->maxrewind = (maxrewind/bq->base)*bq->base;
+}
+
+int pa_memblockq_splice(pa_memblockq *bq, pa_memblockq *source) {
+
+ pa_assert(bq);
+ pa_assert(source);
+
+ pa_memblockq_prebuf_disable(bq);
+
+ for (;;) {
+ pa_memchunk chunk;
+
+ if (pa_memblockq_peek(source, &chunk) < 0)
+ return 0;
+
+ pa_assert(chunk.length > 0);
+
+ if (chunk.memblock) {
+
+ if (pa_memblockq_push_align(bq, &chunk) < 0) {
+ pa_memblock_unref(chunk.memblock);
+ return -1;
+ }
+
+ pa_memblock_unref(chunk.memblock);
+ } else
+ pa_memblockq_seek(bq, chunk.length, PA_SEEK_RELATIVE);
+
+ pa_memblockq_drop(bq, chunk.length);
+ }
+}
+
+void pa_memblockq_willneed(pa_memblockq *bq) {
+ struct list_item *q;
+
+ pa_assert(bq);
+
+ fix_current_read(bq);
+
+ for (q = bq->current_read; q; q = q->next)
+ pa_memchunk_will_need(&q->chunk);
+}
+
+void pa_memblockq_set_silence(pa_memblockq *bq, pa_memchunk *silence) {
+ pa_assert(bq);
+
+ if (bq->silence.memblock)
+ pa_memblock_unref(bq->silence.memblock);
+
+ if (silence) {
+ bq->silence = *silence;
+ pa_memblock_ref(bq->silence.memblock);
+ } else
+ pa_memchunk_reset(&bq->silence);
+}
+
+pa_bool_t pa_memblockq_is_empty(pa_memblockq *bq) {
+ pa_assert(bq);
+
+ return !bq->blocks;
+}
+
+void pa_memblockq_silence(pa_memblockq *bq) {
+ pa_assert(bq);
+
+ while (bq->blocks)
+ drop_block(bq, bq->blocks);
+
+ pa_assert(bq->n_blocks == 0);
+}
+
+unsigned pa_memblockq_get_nblocks(pa_memblockq *bq) {
+ pa_assert(bq);
+
+ return bq->n_blocks;
+}
+
+size_t pa_memblockq_get_base(pa_memblockq *bq) {
+ pa_assert(bq);
+
+ return bq->base;
+}
diff --git a/src/pulsecore/memblockq.h b/src/pulsecore/memblockq.h
index 4d701a80..81f7cbb8 100644
--- a/src/pulsecore/memblockq.h
+++ b/src/pulsecore/memblockq.h
@@ -1,21 +1,21 @@
#ifndef foomemblockqhfoo
#define foomemblockqhfoo
-/* $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
@@ -39,37 +39,40 @@ typedef struct pa_memblockq pa_memblockq;
/* Parameters:
-
+
- idx: start value for both read and write index
- maxlength: maximum length of queue. If more data is pushed into
the queue, the operation will fail. Must not be 0.
-
+
- tlength: the target length of the queue. Pass 0 for the default.
-
+
- base: a base value for all metrics. Only multiples of this value
are popped from the queue or should be pushed into
it. Must not be 0.
-
+
- prebuf: If the queue runs empty wait until this many bytes are in
queue again before passing the first byte out. If set
to 0 pa_memblockq_pop() will return a silence memblock
if no data is in the queue and will never fail. Pass
(size_t) -1 for the default.
-
+
- minreq: pa_memblockq_missing() will only return values greater
than this value. Pass 0 for the default.
-
- - silence: return this memblock whzen reading unitialized data
+
+ - maxrewind: how many bytes of history to keep in the queue
+
+ - silence: return this memchunk when reading unitialized data
*/
pa_memblockq* pa_memblockq_new(
int64_t idx,
size_t maxlength,
size_t tlength,
size_t base,
- size_t prebuf,
+ size_t prebuf,
size_t minreq,
- pa_memblock *silence);
+ size_t maxrewind,
+ pa_memchunk *silence);
void pa_memblockq_free(pa_memblockq*bq);
@@ -81,19 +84,19 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *chunk);
* you know what you do. */
int pa_memblockq_push_align(pa_memblockq* bq, const pa_memchunk *chunk);
-/* Return a copy of the next memory chunk in the queue. It is not removed from the queue */
+/* Return a copy of the next memory chunk in the queue. It is not
+ * removed from the queue. There are two reasons this function might
+ * fail: 1. prebuffering is active, 2. queue is empty and no silence
+ * memblock was passed at initialization. If the queue is not empty,
+ * but we're currently at a hole in the queue and no silence memblock
+ * was passed we return the length of the hole in chunk->length. */
int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk);
-/* Drop the specified bytes from the queue, but only if the first
- * chunk in the queue matches the one passed here. If NULL is passed,
- * this check isn't done. */
-void pa_memblockq_drop(pa_memblockq *bq, const pa_memchunk *chunk, size_t length);
+/* Drop the specified bytes from the queue. */
+void pa_memblockq_drop(pa_memblockq *bq, size_t length);
/* Test if the pa_memblockq is currently readable, that is, more data than base */
-int pa_memblockq_is_readable(pa_memblockq *bq);
-
-/* Test if the pa_memblockq is currently writable for the specified amount of bytes */
-int pa_memblockq_is_writable(pa_memblockq *bq, size_t length);
+pa_bool_t pa_memblockq_is_readable(pa_memblockq *bq);
/* Return the length of the queue in bytes */
size_t pa_memblockq_get_length(pa_memblockq *bq);
@@ -101,6 +104,13 @@ size_t pa_memblockq_get_length(pa_memblockq *bq);
/* Return how many bytes are missing in queue to the specified fill amount */
size_t pa_memblockq_missing(pa_memblockq *bq);
+/* Return the number of bytes that are missing since the last call to
+ * this function, reset the internal counter to 0. */
+size_t pa_memblockq_pop_missing(pa_memblockq *bq);
+
+/* Directly moves the data from the source memblockq into bq */
+int pa_memblockq_splice(pa_memblockq *bq, pa_memblockq *source);
+
/* Returns the minimal request value */
size_t pa_memblockq_get_minreq(pa_memblockq *bq);
@@ -119,10 +129,8 @@ int64_t pa_memblockq_get_read_index(pa_memblockq *bq);
/* Return the current write index */
int64_t pa_memblockq_get_write_index(pa_memblockq *bq);
-/* Shorten the pa_memblockq to the specified length by dropping data
- * at the read end of the queue. The read index is increased until the
- * queue has the specified length */
-void pa_memblockq_shorten(pa_memblockq *bq, size_t length);
+/* Rewind the read index. If the history is shorter than the specified length we'll point to silence afterwards. */
+void pa_memblockq_rewind(pa_memblockq *bq, size_t length);
/* Ignore prebuf for now */
void pa_memblockq_prebuf_disable(pa_memblockq *bq);
@@ -136,4 +144,29 @@ size_t pa_memblockq_get_maxlength(pa_memblockq *bq);
/* Return the prebuffer length in bytes */
size_t pa_memblockq_get_prebuf(pa_memblockq *bq);
+/* Change metrics. Always call in order. */
+void pa_memblockq_set_maxlength(pa_memblockq *memblockq, size_t maxlength); /* might modify tlength, prebuf, minreq too */
+void pa_memblockq_set_tlength(pa_memblockq *memblockq, size_t tlength); /* might modify minreq, too */
+void pa_memblockq_set_prebuf(pa_memblockq *memblockq, size_t prebuf); /* might modify minreq, too */
+void pa_memblockq_set_minreq(pa_memblockq *memblockq, size_t minreq);
+void pa_memblockq_set_maxrewind(pa_memblockq *memblockq, size_t rewind); /* Set the maximum history size */
+void pa_memblockq_set_silence(pa_memblockq *memblockq, pa_memchunk *silence);
+
+/* Call pa_memchunk_willneed() for every chunk in the queue from the current read pointer to the end */
+void pa_memblockq_willneed(pa_memblockq *bq);
+
+/* Check whether the memblockq is completely empty, i.e. no data
+ * neither left nor right of the read pointer, and hence no buffered
+ * data for the future nor data in the backlog. */
+pa_bool_t pa_memblockq_is_empty(pa_memblockq *bq);
+
+void pa_memblockq_silence(pa_memblockq *bq);
+
+/* Check whether we currently are in prebuf state */
+pa_bool_t pa_memblockq_prebuf_active(pa_memblockq *bq);
+
+unsigned pa_memblockq_get_nblocks(pa_memblockq *bq);
+
+size_t pa_memblockq_get_base(pa_memblockq *bq);
+
#endif
diff --git a/src/pulsecore/memchunk.c b/src/pulsecore/memchunk.c
index 55c4bfa7..0bbf8590 100644
--- a/src/pulsecore/memchunk.c
+++ b/src/pulsecore/memchunk.c
@@ -1,18 +1,18 @@
-/* $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.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
@@ -25,43 +25,88 @@
#include <stdio.h>
#include <stdlib.h>
-#include <assert.h>
#include <string.h>
+#include <errno.h>
#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
#include "memchunk.h"
-void pa_memchunk_make_writable(pa_memchunk *c, size_t min) {
+pa_memchunk* pa_memchunk_make_writable(pa_memchunk *c, size_t min) {
pa_memblock *n;
size_t l;
void *tdata, *sdata;
-
- assert(c);
- assert(c->memblock);
- if (pa_memblock_is_read_only(c->memblock) &&
+ pa_assert(c);
+ pa_assert(c->memblock);
+
+ if (pa_memblock_ref_is_one(c->memblock) &&
+ !pa_memblock_is_read_only(c->memblock) &&
pa_memblock_get_length(c->memblock) >= c->index+min)
- return;
+ return c;
+
+ l = PA_MAX(c->length, min);
- l = c->length;
- if (l < min)
- l = min;
-
n = pa_memblock_new(pa_memblock_get_pool(c->memblock), l);
- tdata = pa_memblock_acquire(n);
+
sdata = pa_memblock_acquire(c->memblock);
+ tdata = pa_memblock_acquire(n);
+
memcpy(tdata, (uint8_t*) sdata + c->index, c->length);
- pa_memblock_release(n);
+
pa_memblock_release(c->memblock);
+ pa_memblock_release(n);
+
pa_memblock_unref(c->memblock);
+
c->memblock = n;
c->index = 0;
+
+ return c;
+}
+
+pa_memchunk* pa_memchunk_reset(pa_memchunk *c) {
+ pa_assert(c);
+
+ memset(c, 0, sizeof(*c));
+
+ return c;
+}
+
+pa_memchunk *pa_memchunk_will_need(const pa_memchunk *c) {
+ void *p;
+
+ pa_assert(c);
+ pa_assert(c->memblock);
+
+ /* A version of pa_memblock_will_need() that works on memchunks
+ * instead of memblocks */
+
+ p = (uint8_t*) pa_memblock_acquire(c->memblock) + c->index;
+ pa_will_need(p, c->length);
+ pa_memblock_release(c->memblock);
+
+ return (pa_memchunk*) c;
}
-void pa_memchunk_reset(pa_memchunk *c) {
- assert(c);
+pa_memchunk* pa_memchunk_memcpy(pa_memchunk *dst, pa_memchunk *src) {
+ void *p, *q;
+
+ pa_assert(dst);
+ pa_assert(src);
+ pa_assert(dst->length == src->length);
+
+ p = pa_memblock_acquire(dst->memblock);
+ q = pa_memblock_acquire(src->memblock);
+
+ memmove((uint8_t*) p + dst->index,
+ (uint8_t*) q + src->index,
+ dst->length);
+
+ pa_memblock_release(dst->memblock);
+ pa_memblock_release(src->memblock);
- c->memblock = NULL;
- c->length = c->index = 0;
+ return dst;
}
diff --git a/src/pulsecore/memchunk.h b/src/pulsecore/memchunk.h
index b8ce6249..9458f4ff 100644
--- a/src/pulsecore/memchunk.h
+++ b/src/pulsecore/memchunk.h
@@ -1,21 +1,21 @@
#ifndef foomemchunkhfoo
#define foomemchunkhfoo
-/* $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.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
@@ -35,11 +35,19 @@ typedef struct pa_memchunk {
/* Make a memchunk writable, i.e. make sure that the caller may have
* exclusive access to the memblock and it is not read_only. If needed
- * the memblock in the structure is replaced by a copy. */
-void pa_memchunk_make_writable(pa_memchunk *c, size_t min);
+ * the memblock in the structure is replaced by a copy. If min is not
+ * 0 it is made sure that the returned memblock is at least of the
+ * specified size, i.e. is enlarged if necessary. */
+pa_memchunk* pa_memchunk_make_writable(pa_memchunk *c, size_t min);
/* Invalidate a memchunk. This does not free the cotaining memblock,
* but sets all members to zero. */
-void pa_memchunk_reset(pa_memchunk *c);
+pa_memchunk* pa_memchunk_reset(pa_memchunk *c);
+
+/* Map a memory chunk back into memory if it was swapped out */
+pa_memchunk *pa_memchunk_will_need(const pa_memchunk *c);
+
+/* Copy the data in the src memchunk to the dst memchunk */
+pa_memchunk* pa_memchunk_memcpy(pa_memchunk *dst, pa_memchunk *src);
#endif
diff --git a/src/pulsecore/modargs.c b/src/pulsecore/modargs.c
index 13a48785..5f5902c9 100644
--- a/src/pulsecore/modargs.c
+++ b/src/pulsecore/modargs.c
@@ -1,18 +1,18 @@
-/* $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
@@ -24,7 +24,6 @@
#endif
#include <ctype.h>
-#include <assert.h>
#include <stdlib.h>
#include <string.h>
@@ -37,6 +36,7 @@
#include <pulsecore/sink.h>
#include <pulsecore/source.h>
#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
#include "modargs.h"
@@ -46,7 +46,16 @@ struct entry {
static int add_key_value(pa_hashmap *map, char *key, char *value, const char* const valid_keys[]) {
struct entry *e;
- assert(map && key && value);
+
+ pa_assert(map);
+ pa_assert(key);
+ pa_assert(value);
+
+ if (pa_hashmap_get(map, key)) {
+ pa_xfree(key);
+ pa_xfree(value);
+ return -1;
+ }
if (valid_keys) {
const char*const* v;
@@ -60,11 +69,12 @@ static int add_key_value(pa_hashmap *map, char *key, char *value, const char* co
return -1;
}
}
-
- e = pa_xmalloc(sizeof(struct entry));
+
+ e = pa_xnew(struct entry, 1);
e->key = key;
e->value = value;
pa_hashmap_put(map, key, e);
+
return 0;
}
@@ -72,13 +82,20 @@ pa_modargs *pa_modargs_new(const char *args, const char* const* valid_keys) {
pa_hashmap *map = NULL;
map = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
- assert(map);
if (args) {
- enum { WHITESPACE, KEY, VALUE_START, VALUE_SIMPLE, VALUE_DOUBLE_QUOTES, VALUE_TICKS } state;
+ enum {
+ WHITESPACE,
+ KEY,
+ VALUE_START,
+ VALUE_SIMPLE,
+ VALUE_DOUBLE_QUOTES,
+ VALUE_TICKS
+ } state;
+
const char *p, *key, *value;
size_t key_len = 0, value_len = 0;
-
+
key = value = NULL;
state = WHITESPACE;
for (p = args; *p; p++) {
@@ -95,6 +112,8 @@ pa_modargs *pa_modargs_new(const char *args, const char* const* valid_keys) {
case KEY:
if (*p == '=')
state = VALUE_START;
+ else if (isspace(*p))
+ goto fail;
else
key_len++;
break;
@@ -160,14 +179,14 @@ fail:
if (map)
pa_modargs_free((pa_modargs*) map);
-
+
return NULL;
}
-
static void free_func(void *p, PA_GCC_UNUSED void*userdata) {
struct entry *e = p;
- assert(e);
+ pa_assert(e);
+
pa_xfree(e->key);
pa_xfree(e->value);
pa_xfree(e);
@@ -190,7 +209,10 @@ const char *pa_modargs_get_value(pa_modargs *ma, const char *key, const char *de
int pa_modargs_get_value_u32(pa_modargs *ma, const char *key, uint32_t *value) {
const char *v;
- assert(ma && key && value);
+
+ pa_assert(ma);
+ pa_assert(key);
+ pa_assert(value);
if (!(v = pa_modargs_get_value(ma, key, NULL)))
return 0;
@@ -203,21 +225,27 @@ int pa_modargs_get_value_u32(pa_modargs *ma, const char *key, uint32_t *value) {
int pa_modargs_get_value_s32(pa_modargs *ma, const char *key, int32_t *value) {
const char *v;
- assert(ma && key && value);
+
+ pa_assert(ma);
+ pa_assert(key);
+ pa_assert(value);
if (!(v = pa_modargs_get_value(ma, key, NULL)))
return 0;
if (pa_atoi(v, value) < 0)
return -1;
-
+
return 0;
}
-int pa_modargs_get_value_boolean(pa_modargs *ma, const char *key, int *value) {
+int pa_modargs_get_value_boolean(pa_modargs *ma, const char *key, pa_bool_t *value) {
const char *v;
int r;
- assert(ma && key && value);
+
+ pa_assert(ma);
+ pa_assert(key);
+ pa_assert(value);
if (!(v = pa_modargs_get_value(ma, key, NULL)))
return 0;
@@ -236,10 +264,10 @@ int pa_modargs_get_sample_spec(pa_modargs *ma, pa_sample_spec *rss) {
const char *format;
uint32_t channels;
pa_sample_spec ss;
- assert(ma && rss);
-/* DEBUG_TRAP;*/
-
+ pa_assert(ma);
+ pa_assert(rss);
+
ss = *rss;
if ((pa_modargs_get_value_u32(ma, "rate", &ss.rate)) < 0)
return -1;
@@ -257,20 +285,20 @@ int pa_modargs_get_sample_spec(pa_modargs *ma, pa_sample_spec *rss) {
return -1;
*rss = ss;
-
+
return 0;
}
-int pa_modargs_get_channel_map(pa_modargs *ma, pa_channel_map *rmap) {
+int pa_modargs_get_channel_map(pa_modargs *ma, const char *name, pa_channel_map *rmap) {
pa_channel_map map;
const char *cm;
-
- assert(ma);
- assert(rmap);
+
+ pa_assert(ma);
+ pa_assert(rmap);
map = *rmap;
- if ((cm = pa_modargs_get_value(ma, "channel_map", NULL)))
+ if ((cm = pa_modargs_get_value(ma, name ? name : "channel_map", NULL)))
if (!pa_channel_map_parse(&map, cm))
return -1;
@@ -284,10 +312,10 @@ int pa_modargs_get_channel_map(pa_modargs *ma, pa_channel_map *rmap) {
int pa_modargs_get_sample_spec_and_channel_map(pa_modargs *ma, pa_sample_spec *rss, pa_channel_map *rmap, pa_channel_map_def_t def) {
pa_sample_spec ss;
pa_channel_map map;
-
- assert(ma);
- assert(rss);
- assert(rmap);
+
+ pa_assert(ma);
+ pa_assert(rss);
+ pa_assert(rmap);
ss = *rss;
@@ -297,7 +325,7 @@ int pa_modargs_get_sample_spec_and_channel_map(pa_modargs *ma, pa_sample_spec *r
if (!pa_channel_map_init_auto(&map, ss.channels, def))
map.channels = 0;
- if (pa_modargs_get_channel_map(ma, &map) < 0)
+ if (pa_modargs_get_channel_map(ma, NULL, &map) < 0)
return -1;
if (map.channels != ss.channels)
diff --git a/src/pulsecore/modargs.h b/src/pulsecore/modargs.h
index 730cf396..23766cfc 100644
--- a/src/pulsecore/modargs.h
+++ b/src/pulsecore/modargs.h
@@ -1,21 +1,21 @@
#ifndef foomodargshfoo
#define foomodargshfoo
-/* $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
@@ -26,6 +26,7 @@
#include <pulse/sample.h>
#include <pulse/channelmap.h>
#include <pulsecore/core.h>
+#include <pulsecore/macro.h>
typedef struct pa_modargs pa_modargs;
@@ -42,13 +43,13 @@ const char *pa_modargs_get_value(pa_modargs *ma, const char *key, const char *de
/* Return a module argument as unsigned 32bit value in *value */
int pa_modargs_get_value_u32(pa_modargs *ma, const char *key, uint32_t *value);
int pa_modargs_get_value_s32(pa_modargs *ma, const char *key, int32_t *value);
-int pa_modargs_get_value_boolean(pa_modargs *ma, const char *key, int *value);
+int pa_modargs_get_value_boolean(pa_modargs *ma, const char *key, pa_bool_t *value);
/* Return sample spec data from the three arguments "rate", "format" and "channels" */
int pa_modargs_get_sample_spec(pa_modargs *ma, pa_sample_spec *ss);
-/* Return channel map data from the argument "channel_map" */
-int pa_modargs_get_channel_map(pa_modargs *ma, pa_channel_map *map);
+/* Return channel map data from the argument "channel_map" if name is NULL, otherwise read from the specified argument */
+int pa_modargs_get_channel_map(pa_modargs *ma, const char *name, pa_channel_map *map);
/* Combination of pa_modargs_get_sample_spec() and
pa_modargs_get_channel_map(). Not always suitable, since this routine
diff --git a/src/pulsecore/modinfo.c b/src/pulsecore/modinfo.c
index 00720113..ac4ca88a 100644
--- a/src/pulsecore/modinfo.c
+++ b/src/pulsecore/modinfo.c
@@ -1,18 +1,18 @@
-/* $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
@@ -24,12 +24,13 @@
#endif
#include <ltdl.h>
-#include <assert.h>
#include <pulse/xmalloc.h>
#include <pulsecore/core-util.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/ltdl-helper.h>
#include "modinfo.h"
@@ -37,54 +38,55 @@
#define PA_SYMBOL_DESCRIPTION "pa__get_description"
#define PA_SYMBOL_USAGE "pa__get_usage"
#define PA_SYMBOL_VERSION "pa__get_version"
+#define PA_SYMBOL_LOAD_ONCE "pa__load_once"
-/* lt_dlsym() violates ISO C, so confide the breakage into this function to
- * avoid warnings. */
-typedef void (*fnptr)(void);
-static inline fnptr lt_dlsym_fn(lt_dlhandle handle, const char *symbol) {
- return (fnptr) (long) lt_dlsym(handle, symbol);
-}
-
-pa_modinfo *pa_modinfo_get_by_handle(lt_dlhandle dl) {
+pa_modinfo *pa_modinfo_get_by_handle(lt_dlhandle dl, const char *module_name) {
pa_modinfo *i;
const char* (*func)(void);
- assert(dl);
+ pa_bool_t (*func2) (void);
+
+ pa_assert(dl);
i = pa_xnew0(pa_modinfo, 1);
- if ((func = (const char* (*)(void)) lt_dlsym_fn(dl, PA_SYMBOL_AUTHOR)))
+ if ((func = (const char* (*)(void)) pa_load_sym(dl, module_name, PA_SYMBOL_AUTHOR)))
i->author = pa_xstrdup(func());
- if ((func = (const char* (*)(void)) lt_dlsym_fn(dl, PA_SYMBOL_DESCRIPTION)))
+ if ((func = (const char* (*)(void)) pa_load_sym(dl, module_name, PA_SYMBOL_DESCRIPTION)))
i->description = pa_xstrdup(func());
- if ((func = (const char* (*)(void)) lt_dlsym_fn(dl, PA_SYMBOL_USAGE)))
+ if ((func = (const char* (*)(void)) pa_load_sym(dl, module_name, PA_SYMBOL_USAGE)))
i->usage = pa_xstrdup(func());
- if ((func = (const char* (*)(void)) lt_dlsym_fn(dl, PA_SYMBOL_VERSION)))
+ if ((func = (const char* (*)(void)) pa_load_sym(dl, module_name, PA_SYMBOL_VERSION)))
i->version = pa_xstrdup(func());
+ if ((func2 = (pa_bool_t (*)(void)) pa_load_sym(dl, module_name, PA_SYMBOL_LOAD_ONCE)))
+ i->load_once = func2();
+
return i;
}
pa_modinfo *pa_modinfo_get_by_name(const char *name) {
lt_dlhandle dl;
pa_modinfo *i;
- assert(name);
+
+ pa_assert(name);
if (!(dl = lt_dlopenext(name))) {
pa_log("Failed to open module \"%s\": %s", name, lt_dlerror());
return NULL;
}
- i = pa_modinfo_get_by_handle(dl);
+ i = pa_modinfo_get_by_handle(dl, name);
lt_dlclose(dl);
return i;
}
void pa_modinfo_free(pa_modinfo *i) {
- assert(i);
+ pa_assert(i);
+
pa_xfree(i->author);
pa_xfree(i->description);
pa_xfree(i->usage);
diff --git a/src/pulsecore/modinfo.h b/src/pulsecore/modinfo.h
index 90404504..605637c4 100644
--- a/src/pulsecore/modinfo.h
+++ b/src/pulsecore/modinfo.h
@@ -1,21 +1,21 @@
#ifndef foomodinfohfoo
#define foomodinfohfoo
-/* $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
@@ -23,16 +23,18 @@
***/
/* Some functions for reading module meta data from PulseAudio modules */
+#include <pulsecore/macro.h>
typedef struct pa_modinfo {
char *author;
char *description;
char *usage;
char *version;
+ pa_bool_t load_once;
} pa_modinfo;
/* Read meta data from an libtool handle */
-pa_modinfo *pa_modinfo_get_by_handle(lt_dlhandle dl);
+pa_modinfo *pa_modinfo_get_by_handle(lt_dlhandle dl, const char *module_name);
/* Read meta data from a module file */
pa_modinfo *pa_modinfo_get_by_name(const char *name);
diff --git a/src/pulsecore/module.c b/src/pulsecore/module.c
index ea3d726e..f1eeb762 100644
--- a/src/pulsecore/module.c
+++ b/src/pulsecore/module.c
@@ -1,18 +1,19 @@
-/* $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
@@ -26,7 +27,6 @@
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
-#include <assert.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
@@ -37,103 +37,80 @@
#include <pulsecore/core-subscribe.h>
#include <pulsecore/log.h>
#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/ltdl-helper.h>
#include "module.h"
#define PA_SYMBOL_INIT "pa__init"
#define PA_SYMBOL_DONE "pa__done"
+#define PA_SYMBOL_LOAD_ONCE "pa__load_once"
#define UNLOAD_POLL_TIME 2
-/* lt_dlsym() violates ISO C, so confide the breakage into this function to
- * avoid warnings. */
-typedef void (*fnptr)(void);
-static inline fnptr lt_dlsym_fn(lt_dlhandle handle, const char *symbol) {
- return (fnptr) (long) lt_dlsym(handle, symbol);
-}
-
static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
- pa_core *c = userdata;
+ pa_core *c = PA_CORE(userdata);
struct timeval ntv;
- assert(c && c->mainloop == m && c->module_auto_unload_event == e);
+
+ pa_core_assert_ref(c);
+ pa_assert(c->mainloop == m);
+ pa_assert(c->module_auto_unload_event == e);
pa_module_unload_unused(c);
pa_gettimeofday(&ntv);
- ntv.tv_sec += UNLOAD_POLL_TIME;
+ pa_timeval_add(&ntv, UNLOAD_POLL_TIME*1000000);
m->time_restart(e, &ntv);
}
-static inline fnptr load_sym(lt_dlhandle handle, const char *module, const char *symbol) {
- char *buffer, *ch;
- size_t buflen;
- fnptr res;
-
- res = lt_dlsym_fn(handle, symbol);
- if (res)
- return res;
-
- /* As the .la files might have been cleansed from the system, we should
- * try with the ltdl prefix as well. */
-
- buflen = strlen(symbol) + strlen(module) + strlen("_LTX_") + 1;
- buffer = pa_xmalloc(buflen);
- assert(buffer);
-
- strcpy(buffer, module);
-
- for (ch = buffer;*ch != '\0';ch++) {
- if (!isalnum(*ch))
- *ch = '_';
- }
-
- strcat(buffer, "_LTX_");
- strcat(buffer, symbol);
-
- res = lt_dlsym_fn(handle, buffer);
-
- pa_xfree(buffer);
-
- return res;
-}
-
pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {
pa_module *m = NULL;
- int r;
-
- assert(c && name);
+ pa_bool_t (*load_once)(void);
+
+ pa_assert(c);
+ pa_assert(name);
if (c->disallow_module_loading)
goto fail;
-
- m = pa_xmalloc(sizeof(pa_module));
+ m = pa_xnew(pa_module, 1);
m->name = pa_xstrdup(name);
m->argument = pa_xstrdup(argument);
-
+
if (!(m->dl = lt_dlopenext(name))) {
pa_log("Failed to open module \"%s\": %s", name, lt_dlerror());
goto fail;
}
- if (!(m->init = (int (*)(pa_core *_c, pa_module*_m)) load_sym(m->dl, name, PA_SYMBOL_INIT))) {
- pa_log("Failed to load module \"%s\": symbol \""PA_SYMBOL_INIT"\" not found.", name);
- goto fail;
+ if ((load_once = (pa_bool_t (*)(void)) pa_load_sym(m->dl, name, PA_SYMBOL_LOAD_ONCE))) {
+
+ if (load_once() && c->modules) {
+ pa_module *i;
+ uint32_t idx;
+ /* OK, the module only wants to be loaded once, let's make sure it is */
+
+ for (i = pa_idxset_first(c->modules, &idx); i; i = pa_idxset_next(c->modules, &idx)) {
+ if (strcmp(name, i->name) == 0) {
+ pa_log("Module \"%s\" should be loaded once at most. Refusing to load.", name);
+ goto fail;
+ }
+ }
+ }
}
- if (!(m->done = (void (*)(pa_core *_c, pa_module*_m)) load_sym(m->dl, name, PA_SYMBOL_DONE))) {
- pa_log("Failed to load module \"%s\": symbol \""PA_SYMBOL_DONE"\" not found.", name);
+ if (!(m->init = (int (*)(pa_module*_m)) pa_load_sym(m->dl, name, PA_SYMBOL_INIT))) {
+ pa_log("Failed to load module \"%s\": symbol \""PA_SYMBOL_INIT"\" not found.", name);
goto fail;
}
-
+
+ m->done = (void (*)(pa_module*_m)) pa_load_sym(m->dl, name, PA_SYMBOL_DONE);
m->userdata = NULL;
m->core = c;
m->n_used = -1;
- m->auto_unload = 0;
- m->unload_requested = 0;
+ m->auto_unload = FALSE;
+ m->unload_requested = FALSE;
- assert(m->init);
- if (m->init(c, m) < 0) {
+ if (m->init(m) < 0) {
pa_log_error("Failed to load module \"%s\" (argument: \"%s\"): initialization failed.", name, argument ? argument : "");
goto fail;
}
@@ -141,30 +118,28 @@ pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {
if (!c->modules)
c->modules = pa_idxset_new(NULL, NULL);
- if (!c->module_auto_unload_event) {
+ if (m->auto_unload && !c->module_auto_unload_event) {
struct timeval ntv;
pa_gettimeofday(&ntv);
- ntv.tv_sec += UNLOAD_POLL_TIME;
+ pa_timeval_add(&ntv, UNLOAD_POLL_TIME*1000000);
c->module_auto_unload_event = c->mainloop->time_new(c->mainloop, &ntv, timeout_callback, c);
}
- assert(c->module_auto_unload_event);
-
- assert(c->modules);
- r = pa_idxset_put(c->modules, m, &m->index);
- assert(r >= 0 && m->index != PA_IDXSET_INVALID);
- pa_log_info("Loaded \"%s\" (index: #%u; argument: \"%s\").", m->name, m->index, m->argument ? m->argument : "");
+ pa_assert_se(pa_idxset_put(c->modules, m, &m->index) >= 0);
+ pa_assert(m->index != PA_IDXSET_INVALID);
+
+ pa_log_info("Loaded \"%s\" (index: #%u; argument: \"%s\").", m->name, m->index, m->argument ? m->argument : "");
pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_NEW, m->index);
-
+
return m;
-
+
fail:
if (m) {
pa_xfree(m->argument);
pa_xfree(m->name);
-
+
if (m->dl)
lt_dlclose(m->dl);
@@ -175,30 +150,33 @@ fail:
}
static void pa_module_free(pa_module *m) {
- assert(m && m->done && m->core);
+ pa_assert(m);
+ pa_assert(m->core);
if (m->core->disallow_module_loading)
return;
- pa_log_info("Unloading \"%s\" (index: #%u).", m->name, m->index);
+ pa_log_info("Unloading \"%s\" (index: #%u).", m->name, m->index);
- m->done(m->core, m);
+ if (m->done)
+ m->done(m);
lt_dlclose(m->dl);
-
- pa_log_info("Unloaded \"%s\" (index: #%u).", m->name, m->index);
+
+ pa_log_info("Unloaded \"%s\" (index: #%u).", m->name, m->index);
pa_subscription_post(m->core, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_REMOVE, m->index);
-
+
pa_xfree(m->name);
pa_xfree(m->argument);
pa_xfree(m);
}
void pa_module_unload(pa_core *c, pa_module *m) {
- assert(c && m);
+ pa_assert(c);
+ pa_assert(m);
- assert(c->modules);
+ pa_assert(c->modules);
if (!(m = pa_idxset_remove_by_data(c->modules, m, NULL)))
return;
@@ -207,9 +185,9 @@ void pa_module_unload(pa_core *c, pa_module *m) {
void pa_module_unload_by_index(pa_core *c, uint32_t idx) {
pa_module *m;
- assert(c && idx != PA_IDXSET_INVALID);
+ pa_assert(c);
+ pa_assert(idx != PA_IDXSET_INVALID);
- assert(c->modules);
if (!(m = pa_idxset_remove_by_index(c->modules, idx)))
return;
@@ -218,13 +196,14 @@ void pa_module_unload_by_index(pa_core *c, uint32_t idx) {
static void free_callback(void *p, PA_GCC_UNUSED void *userdata) {
pa_module *m = p;
- assert(m);
+ pa_assert(m);
pa_module_free(m);
}
void pa_module_unload_all(pa_core *c) {
pa_module *m;
- assert(c);
+
+ pa_assert(c);
if (!c->modules)
return;
@@ -249,8 +228,11 @@ void pa_module_unload_all(pa_core *c) {
static int unused_callback(void *p, PA_GCC_UNUSED uint32_t idx, int *del, void *userdata) {
pa_module *m = p;
time_t *now = userdata;
- assert(p && del && now);
-
+
+ pa_assert(m);
+ pa_assert(del);
+ pa_assert(now);
+
if (m->n_used == 0 && m->auto_unload && m->last_used_time+m->core->module_idle_time <= *now) {
pa_module_free(m);
*del = 1;
@@ -261,18 +243,18 @@ static int unused_callback(void *p, PA_GCC_UNUSED uint32_t idx, int *del, void *
void pa_module_unload_unused(pa_core *c) {
time_t now;
- assert(c);
+ pa_assert(c);
if (!c->modules)
return;
-
+
time(&now);
pa_idxset_foreach(c->modules, unused_callback, &now);
}
static int unload_callback(void *p, PA_GCC_UNUSED uint32_t idx, int *del, PA_GCC_UNUSED void *userdata) {
pa_module *m = p;
- assert(m);
+ pa_assert(m);
if (m->unload_requested) {
pa_module_free(m);
@@ -283,20 +265,21 @@ static int unload_callback(void *p, PA_GCC_UNUSED uint32_t idx, int *del, PA_GCC
}
static void defer_cb(pa_mainloop_api*api, pa_defer_event *e, void *userdata) {
- pa_core *core = userdata;
+ pa_core *core = PA_CORE(userdata);
+
+ pa_core_assert_ref(core);
api->defer_enable(e, 0);
if (!core->modules)
return;
pa_idxset_foreach(core->modules, unload_callback, NULL);
-
}
void pa_module_unload_request(pa_module *m) {
- assert(m);
+ pa_assert(m);
- m->unload_requested = 1;
+ m->unload_requested = TRUE;
if (!m->core->module_defer_unload_event)
m->core->module_defer_unload_event = m->core->mainloop->defer_new(m->core->mainloop, defer_cb, m->core);
@@ -305,19 +288,19 @@ void pa_module_unload_request(pa_module *m) {
}
void pa_module_set_used(pa_module*m, int used) {
- assert(m);
+ pa_assert(m);
if (m->n_used != used)
pa_subscription_post(m->core, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_CHANGE, m->index);
-
- if (m->n_used != used && used == 0)
+
+ if (used == 0 && m->n_used > 0)
time(&m->last_used_time);
m->n_used = used;
}
pa_modinfo *pa_module_get_info(pa_module *m) {
- assert(m);
+ pa_assert(m);
- return pa_modinfo_get_by_handle(m->dl);
+ return pa_modinfo_get_by_handle(m->dl, m->name);
}
diff --git a/src/pulsecore/module.h b/src/pulsecore/module.h
index 8c320be8..ec582f25 100644
--- a/src/pulsecore/module.h
+++ b/src/pulsecore/module.h
@@ -1,21 +1,21 @@
#ifndef foomodulehfoo
#define foomodulehfoo
-/* $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
@@ -36,17 +36,17 @@ struct pa_module {
uint32_t index;
lt_dlhandle dl;
-
- int (*init)(pa_core *c, pa_module*m);
- void (*done)(pa_core *c, pa_module*m);
+
+ int (*init)(pa_module*m);
+ void (*done)(pa_module*m);
void *userdata;
int n_used;
- int auto_unload;
+ pa_bool_t auto_unload;
time_t last_used_time;
- int unload_requested;
+ pa_bool_t unload_requested;
};
pa_module* pa_module_load(pa_core *c, const char *name, const char*argument);
@@ -60,10 +60,25 @@ void pa_module_unload_request(pa_module *m);
void pa_module_set_used(pa_module*m, int used);
-#define PA_MODULE_AUTHOR(s) const char * pa__get_author(void) { return s; }
-#define PA_MODULE_DESCRIPTION(s) const char * pa__get_description(void) { return s; }
-#define PA_MODULE_USAGE(s) const char * pa__get_usage(void) { return s; }
-#define PA_MODULE_VERSION(s) const char * pa__get_version(void) { return s; }
+#define PA_MODULE_AUTHOR(s) \
+ const char *pa__get_author(void) { return s; } \
+ struct __stupid_useless_struct_to_allow_trailing_semicolon
+
+#define PA_MODULE_DESCRIPTION(s) \
+ const char *pa__get_description(void) { return s; } \
+ struct __stupid_useless_struct_to_allow_trailing_semicolon
+
+#define PA_MODULE_USAGE(s) \
+ const char *pa__get_usage(void) { return s; } \
+ struct __stupid_useless_struct_to_allow_trailing_semicolon
+
+#define PA_MODULE_VERSION(s) \
+ const char * pa__get_version(void) { return s; } \
+ struct __stupid_useless_struct_to_allow_trailing_semicolon
+
+#define PA_MODULE_LOAD_ONCE(b) \
+ pa_bool_t pa__load_once(void) { return b; } \
+ struct __stupid_useless_struct_to_allow_trailing_semicolon
pa_modinfo *pa_module_get_info(pa_module *m);
diff --git a/src/pulsecore/msgobject.c b/src/pulsecore/msgobject.c
new file mode 100644
index 00000000..81417ea4
--- /dev/null
+++ b/src/pulsecore/msgobject.c
@@ -0,0 +1,47 @@
+/***
+ 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 "msgobject.h"
+
+PA_DEFINE_CHECK_TYPE(pa_msgobject, pa_object);
+
+pa_msgobject *pa_msgobject_new_internal(size_t size, const char *type_name, int (*check_type)(const char *type_name)) {
+ pa_msgobject *o;
+
+ pa_assert(size > sizeof(pa_msgobject));
+ pa_assert(type_name);
+
+ if (!check_type)
+ check_type = pa_msgobject_check_type;
+
+ pa_assert(check_type(type_name));
+ pa_assert(check_type("pa_object"));
+ pa_assert(check_type("pa_msgobject"));
+
+ o = PA_MSGOBJECT(pa_object_new_internal(size, type_name, check_type));
+ o->process_msg = NULL;
+ return o;
+}
diff --git a/src/pulsecore/msgobject.h b/src/pulsecore/msgobject.h
new file mode 100644
index 00000000..1a43fa35
--- /dev/null
+++ b/src/pulsecore/msgobject.h
@@ -0,0 +1,52 @@
+#ifndef foopulsemsgobjecthfoo
+#define foopulsemsgobjecthfoo
+
+/***
+ 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.
+***/
+
+#include <sys/types.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/refcnt.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/object.h>
+#include <pulsecore/memchunk.h>
+
+typedef struct pa_msgobject pa_msgobject;
+
+struct pa_msgobject {
+ pa_object parent;
+ int (*process_msg)(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
+};
+
+pa_msgobject *pa_msgobject_new_internal(size_t size, const char *type_name, int (*check_type)(const char *type_name));
+
+int pa_msgobject_check_type(const char *type);
+
+#define pa_msgobject_new(type) ((type*) pa_msgobject_new_internal(sizeof(type), #type, type##_check_type))
+#define pa_msgobject_free ((void (*) (pa_msgobject* o)) pa_object_free)
+
+#define PA_MSGOBJECT(o) pa_msgobject_cast(o)
+
+PA_DECLARE_CLASS(pa_msgobject);
+
+#endif
diff --git a/src/pulsecore/mutex-posix.c b/src/pulsecore/mutex-posix.c
index 094d637d..35465b7b 100644
--- a/src/pulsecore/mutex-posix.c
+++ b/src/pulsecore/mutex-posix.c
@@ -1,18 +1,18 @@
-/* $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
@@ -23,20 +23,16 @@
#include <config.h>
#endif
-#include <assert.h>
#include <pthread.h>
-
-#include <atomic_ops.h>
+#include <errno.h>
#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-error.h>
#include "mutex.h"
-#define ASSERT_SUCCESS(x) do { \
- int _r = (x); \
- assert(_r == 0); \
-} while(0)
-
struct pa_mutex {
pthread_mutex_t mutex;
};
@@ -45,68 +41,100 @@ struct pa_cond {
pthread_cond_t cond;
};
-pa_mutex* pa_mutex_new(int recursive) {
+pa_mutex* pa_mutex_new(pa_bool_t recursive, pa_bool_t inherit_priority) {
pa_mutex *m;
pthread_mutexattr_t attr;
+ int r;
- pthread_mutexattr_init(&attr);
+ pa_assert_se(pthread_mutexattr_init(&attr) == 0);
if (recursive)
- ASSERT_SUCCESS(pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE));
+ pa_assert_se(pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) == 0);
+
+#ifdef HAVE_PTHREAD_PRIO_INHERIT
+ if (inherit_priority)
+ pa_assert_se(pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT) == 0);
+#endif
m = pa_xnew(pa_mutex, 1);
- ASSERT_SUCCESS(pthread_mutex_init(&m->mutex, &attr));
+#ifndef HAVE_PTHREAD_PRIO_INHERIT
+ pa_assert_se(pthread_mutex_init(&m->mutex, &attr) == 0);
+
+#else
+ if ((r = pthread_mutex_init(&m->mutex, &attr))) {
+
+ /* If this failed, then this was probably due to non-available
+ * priority inheritance. In which case we fall back to normal
+ * mutexes. */
+ pa_assert(r == ENOTSUP && inherit_priority);
+
+ pa_assert_se(pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_NONE) == 0);
+ pa_assert_se(pthread_mutex_init(&m->mutex, &attr) == 0);
+ }
+#endif
+
return m;
}
void pa_mutex_free(pa_mutex *m) {
- assert(m);
+ pa_assert(m);
- ASSERT_SUCCESS(pthread_mutex_destroy(&m->mutex));
+ pa_assert_se(pthread_mutex_destroy(&m->mutex) == 0);
pa_xfree(m);
}
void pa_mutex_lock(pa_mutex *m) {
- assert(m);
+ pa_assert(m);
- ASSERT_SUCCESS(pthread_mutex_lock(&m->mutex));
+ pa_assert_se(pthread_mutex_lock(&m->mutex) == 0);
+}
+
+pa_bool_t pa_mutex_try_lock(pa_mutex *m) {
+ int r;
+ pa_assert(m);
+
+ if ((r = pthread_mutex_trylock(&m->mutex)) != 0) {
+ pa_assert(r == EBUSY);
+ return FALSE;
+ }
+
+ return TRUE;
}
void pa_mutex_unlock(pa_mutex *m) {
- assert(m);
+ pa_assert(m);
- ASSERT_SUCCESS(pthread_mutex_unlock(&m->mutex));
+ pa_assert_se(pthread_mutex_unlock(&m->mutex) == 0);
}
pa_cond *pa_cond_new(void) {
pa_cond *c;
c = pa_xnew(pa_cond, 1);
-
- ASSERT_SUCCESS(pthread_cond_init(&c->cond, NULL));
+ pa_assert_se(pthread_cond_init(&c->cond, NULL) == 0);
return c;
}
void pa_cond_free(pa_cond *c) {
- assert(c);
+ pa_assert(c);
- ASSERT_SUCCESS(pthread_cond_destroy(&c->cond));
+ pa_assert_se(pthread_cond_destroy(&c->cond) == 0);
pa_xfree(c);
}
void pa_cond_signal(pa_cond *c, int broadcast) {
- assert(c);
+ pa_assert(c);
if (broadcast)
- ASSERT_SUCCESS(pthread_cond_broadcast(&c->cond));
+ pa_assert_se(pthread_cond_broadcast(&c->cond) == 0);
else
- ASSERT_SUCCESS(pthread_cond_signal(&c->cond));
+ pa_assert_se(pthread_cond_signal(&c->cond) == 0);
}
int pa_cond_wait(pa_cond *c, pa_mutex *m) {
- assert(c);
- assert(m);
+ pa_assert(c);
+ pa_assert(m);
return pthread_cond_wait(&c->cond, &m->mutex);
}
diff --git a/src/pulsecore/mutex-win32.c b/src/pulsecore/mutex-win32.c
index 3710d914..5e884e7f 100644
--- a/src/pulsecore/mutex-win32.c
+++ b/src/pulsecore/mutex-win32.c
@@ -1,18 +1,18 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
+ 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
@@ -38,7 +38,7 @@ struct pa_cond {
pa_hashmap *wait_events;
};
-pa_mutex* pa_mutex_new(int recursive) {
+pa_mutex* pa_mutex_new(pa_bool_t recursive, pa_bool_t inherit_priority) {
pa_mutex *m;
m = pa_xnew(pa_mutex, 1);
diff --git a/src/pulsecore/mutex.h b/src/pulsecore/mutex.h
index b3b9c5c6..36e1d635 100644
--- a/src/pulsecore/mutex.h
+++ b/src/pulsecore/mutex.h
@@ -1,32 +1,41 @@
#ifndef foopulsemutexhfoo
#define foopulsemutexhfoo
-/* $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.
***/
+#include <pulsecore/macro.h>
+
typedef struct pa_mutex pa_mutex;
-pa_mutex* pa_mutex_new(int recursive);
+/* Please think twice before enabling priority inheritance. This is no
+ * magic wand! Use it only when the potentially priorized threads are
+ * good candidates for it. Don't use this blindly! Also, note that
+ * only very few operating systems actually implement this, hence this
+ * is merely a hint. */
+pa_mutex* pa_mutex_new(pa_bool_t recursive, pa_bool_t inherit_priority);
+
void pa_mutex_free(pa_mutex *m);
void pa_mutex_lock(pa_mutex *m);
+pa_bool_t pa_mutex_try_lock(pa_mutex *m);
void pa_mutex_unlock(pa_mutex *m);
typedef struct pa_cond pa_cond;
diff --git a/src/pulsecore/namereg.c b/src/pulsecore/namereg.c
index fcd271bf..cc18adab 100644
--- a/src/pulsecore/namereg.c
+++ b/src/pulsecore/namereg.c
@@ -1,18 +1,18 @@
-/* $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
@@ -25,7 +25,6 @@
#include <stdlib.h>
#include <string.h>
-#include <assert.h>
#include <string.h>
#include <stdio.h>
@@ -36,6 +35,7 @@
#include <pulsecore/sink.h>
#include <pulsecore/core-subscribe.h>
#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
#include "namereg.h"
@@ -54,20 +54,20 @@ static int is_valid_char(char c) {
c == '_';
}
-static int is_valid_name(const char *name) {
+pa_bool_t pa_namereg_is_valid_name(const char *name) {
const char *c;
if (*name == 0)
- return 0;
-
+ return FALSE;
+
for (c = name; *c && (c-name < PA_NAME_MAX); c++)
if (!is_valid_char(*c))
- return 0;
+ return FALSE;
if (*c)
- return 0;
-
- return 1;
+ return FALSE;
+
+ return TRUE;
}
static char* cleanup_name(const char *name) {
@@ -78,7 +78,7 @@ static char* cleanup_name(const char *name) {
return NULL;
n = pa_xnew(char, strlen(name)+1);
-
+
for (a = name, b = n; *a && (a-name < PA_NAME_MAX); a++, b++)
*b = is_valid_char(*a) ? *a : '_';
@@ -88,30 +88,29 @@ static char* cleanup_name(const char *name) {
}
void pa_namereg_free(pa_core *c) {
- assert(c);
-
+ pa_assert(c);
+
if (!c->namereg)
return;
-
- assert(pa_hashmap_size(c->namereg) == 0);
+
+ pa_assert(pa_hashmap_size(c->namereg) == 0);
pa_hashmap_free(c->namereg, NULL, NULL);
}
const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t type, void *data, int fail) {
struct namereg_entry *e;
char *n = NULL;
- int r;
-
- assert(c);
- assert(name);
- assert(data);
+
+ pa_assert(c);
+ pa_assert(name);
+ pa_assert(data);
if (!*name)
return NULL;
-
+
if ((type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE) &&
- !is_valid_name(name) ) {
-
+ !pa_namereg_is_valid_name(name) ) {
+
if (fail)
return NULL;
@@ -136,11 +135,11 @@ const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t
pa_xfree(n);
return NULL;
}
-
+
k = pa_xnew(char, l+4);
-
+
for (i = 2; i <= 99; i++) {
- snprintf(k, l+4, "%s.%u", name, i);
+ pa_snprintf(k, l+4, "%s.%u", name, i);
if (!(e = pa_hashmap_get(c->namereg, k)))
break;
@@ -151,59 +150,57 @@ const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t
pa_xfree(k);
return NULL;
}
-
+
pa_xfree(n);
n = k;
}
-
+
e = pa_xnew(struct namereg_entry, 1);
e->type = type;
e->name = n ? n : pa_xstrdup(name);
e->data = data;
- r = pa_hashmap_put(c->namereg, e->name, e);
- assert (r >= 0);
+ pa_assert_se(pa_hashmap_put(c->namereg, e->name, e) >= 0);
return e->name;
}
void pa_namereg_unregister(pa_core *c, const char *name) {
struct namereg_entry *e;
-
- assert(c);
- assert(name);
- e = pa_hashmap_remove(c->namereg, name);
- assert(e);
+ pa_assert(c);
+ pa_assert(name);
+
+ pa_assert_se(e = pa_hashmap_remove(c->namereg, name));
pa_xfree(e->name);
pa_xfree(e);
}
-void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, int autoload) {
+void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, pa_bool_t autoload) {
struct namereg_entry *e;
uint32_t idx;
- assert(c);
-
+ pa_assert(c);
+
if (!name) {
-
+
if (type == PA_NAMEREG_SOURCE)
name = pa_namereg_get_default_source_name(c);
else if (type == PA_NAMEREG_SINK)
name = pa_namereg_get_default_sink_name(c);
-
+
} else if (strcmp(name, "@DEFAULT_SINK@") == 0) {
if (type == PA_NAMEREG_SINK)
name = pa_namereg_get_default_sink_name(c);
-
+
} else if (strcmp(name, "@DEFAULT_SOURCE@") == 0) {
if (type == PA_NAMEREG_SOURCE)
name = pa_namereg_get_default_source_name(c);
-
+
} else if (strcmp(name, "@DEFAULT_MONITOR@") == 0) {
if (type == PA_NAMEREG_SOURCE) {
pa_sink *k;
-
+
if ((k = pa_namereg_get(c, NULL, PA_NAMEREG_SINK, autoload)))
return k->monitor_source;
}
@@ -212,7 +209,7 @@ void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, int a
if (!name)
return NULL;
-
+
if (c->namereg && (e = pa_hashmap_get(c->namereg, name)))
if (e->type == type)
return e->data;
@@ -221,12 +218,12 @@ void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, int a
if (autoload) {
pa_autoload_request(c, name, type);
-
+
if (c->namereg && (e = pa_hashmap_get(c->namereg, name)))
if (e->type == type)
return e->data;
}
-
+
return NULL;
}
@@ -242,9 +239,9 @@ void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, int a
int pa_namereg_set_default(pa_core*c, const char *name, pa_namereg_type_t type) {
char **s;
-
- assert(c);
- assert(type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE);
+
+ pa_assert(c);
+ pa_assert(type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE);
s = type == PA_NAMEREG_SINK ? &c->default_sink_name : &c->default_source_name;
@@ -254,9 +251,9 @@ int pa_namereg_set_default(pa_core*c, const char *name, pa_namereg_type_t type)
if (name && *s && !strcmp(name, *s))
return 0;
- if (!is_valid_name(name))
+ if (!pa_namereg_is_valid_name(name))
return -1;
-
+
pa_xfree(*s);
*s = pa_xstrdup(name);
pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SERVER|PA_SUBSCRIPTION_EVENT_CHANGE, PA_INVALID_INDEX);
@@ -266,12 +263,12 @@ int pa_namereg_set_default(pa_core*c, const char *name, pa_namereg_type_t type)
const char *pa_namereg_get_default_sink_name(pa_core *c) {
pa_sink *s;
-
- assert(c);
+
+ pa_assert(c);
if (c->default_sink_name)
return c->default_sink_name;
-
+
if ((s = pa_idxset_first(c->sinks, NULL)))
pa_namereg_set_default(c, s->name, PA_NAMEREG_SINK);
@@ -281,8 +278,8 @@ const char *pa_namereg_get_default_sink_name(pa_core *c) {
const char *pa_namereg_get_default_source_name(pa_core *c) {
pa_source *s;
uint32_t idx;
-
- assert(c);
+
+ pa_assert(c);
if (c->default_source_name)
return c->default_source_name;
diff --git a/src/pulsecore/namereg.h b/src/pulsecore/namereg.h
index 53fb6618..af0153ec 100644
--- a/src/pulsecore/namereg.h
+++ b/src/pulsecore/namereg.h
@@ -1,21 +1,21 @@
#ifndef foonamereghfoo
#define foonamereghfoo
-/* $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
@@ -23,6 +23,7 @@
***/
#include <pulsecore/core.h>
+#include <pulsecore/macro.h>
#define PA_NAME_MAX 128
@@ -36,10 +37,12 @@ void pa_namereg_free(pa_core *c);
const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t type, void *data, int fail);
void pa_namereg_unregister(pa_core *c, const char *name);
-void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, int autoload);
+void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, pa_bool_t autoload);
int pa_namereg_set_default(pa_core*c, const char *name, pa_namereg_type_t type);
const char *pa_namereg_get_default_sink_name(pa_core *c);
const char *pa_namereg_get_default_source_name(pa_core *c);
+pa_bool_t pa_namereg_is_valid_name(const char *name);
+
#endif
diff --git a/src/pulsecore/native-common.h b/src/pulsecore/native-common.h
index 785289eb..809d6c75 100644
--- a/src/pulsecore/native-common.h
+++ b/src/pulsecore/native-common.h
@@ -1,21 +1,22 @@
#ifndef foonativecommonhfoo
#define foonativecommonhfoo
-/* $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.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
@@ -33,10 +34,10 @@ enum {
PA_COMMAND_TIMEOUT, /* pseudo command */
PA_COMMAND_REPLY,
- /* Commands from client to server */
- PA_COMMAND_CREATE_PLAYBACK_STREAM,
+ /* CLIENT->SERVER */
+ PA_COMMAND_CREATE_PLAYBACK_STREAM, /* Payload changed in v9, v12 (0.9.0, 0.9.8) */
PA_COMMAND_DELETE_PLAYBACK_STREAM,
- PA_COMMAND_CREATE_RECORD_STREAM,
+ PA_COMMAND_CREATE_RECORD_STREAM, /* Payload changed in v9, v12 (0.9.0, 0.9.8) */
PA_COMMAND_DELETE_RECORD_STREAM,
PA_COMMAND_EXIT,
PA_COMMAND_AUTH,
@@ -51,7 +52,7 @@ enum {
PA_COMMAND_FINISH_UPLOAD_STREAM,
PA_COMMAND_PLAY_SAMPLE,
PA_COMMAND_REMOVE_SAMPLE,
-
+
PA_COMMAND_GET_SERVER_INFO,
PA_COMMAND_GET_SINK_INFO,
PA_COMMAND_GET_SINK_INFO_LIST,
@@ -61,46 +62,49 @@ enum {
PA_COMMAND_GET_MODULE_INFO_LIST,
PA_COMMAND_GET_CLIENT_INFO,
PA_COMMAND_GET_CLIENT_INFO_LIST,
- PA_COMMAND_GET_SINK_INPUT_INFO,
- PA_COMMAND_GET_SINK_INPUT_INFO_LIST,
+ PA_COMMAND_GET_SINK_INPUT_INFO, /* Payload changed in v11 (0.9.7) */
+ PA_COMMAND_GET_SINK_INPUT_INFO_LIST, /* Payload changed in v11 (0.9.7) */
PA_COMMAND_GET_SOURCE_OUTPUT_INFO,
PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST,
PA_COMMAND_GET_SAMPLE_INFO,
PA_COMMAND_GET_SAMPLE_INFO_LIST,
PA_COMMAND_SUBSCRIBE,
-
+
PA_COMMAND_SET_SINK_VOLUME,
PA_COMMAND_SET_SINK_INPUT_VOLUME,
PA_COMMAND_SET_SOURCE_VOLUME,
PA_COMMAND_SET_SINK_MUTE,
PA_COMMAND_SET_SOURCE_MUTE,
-
+
PA_COMMAND_CORK_PLAYBACK_STREAM,
PA_COMMAND_FLUSH_PLAYBACK_STREAM,
PA_COMMAND_TRIGGER_PLAYBACK_STREAM,
-
+
PA_COMMAND_SET_DEFAULT_SINK,
PA_COMMAND_SET_DEFAULT_SOURCE,
-
+
PA_COMMAND_SET_PLAYBACK_STREAM_NAME,
PA_COMMAND_SET_RECORD_STREAM_NAME,
-
+
PA_COMMAND_KILL_CLIENT,
PA_COMMAND_KILL_SINK_INPUT,
PA_COMMAND_KILL_SOURCE_OUTPUT,
+
PA_COMMAND_LOAD_MODULE,
PA_COMMAND_UNLOAD_MODULE,
+
PA_COMMAND_ADD_AUTOLOAD,
PA_COMMAND_REMOVE_AUTOLOAD,
PA_COMMAND_GET_AUTOLOAD_INFO,
PA_COMMAND_GET_AUTOLOAD_INFO_LIST,
+
PA_COMMAND_GET_RECORD_LATENCY,
PA_COMMAND_CORK_RECORD_STREAM,
PA_COMMAND_FLUSH_RECORD_STREAM,
PA_COMMAND_PREBUF_PLAYBACK_STREAM,
- /* Commands from server to client */
+ /* SERVER->CLIENT */
PA_COMMAND_REQUEST,
PA_COMMAND_OVERFLOW,
PA_COMMAND_UNDERFLOW,
@@ -109,9 +113,41 @@ enum {
PA_COMMAND_SUBSCRIBE_EVENT,
/* A few more client->server commands */
+
+ /* Supported since protocol v10 (0.9.5) */
PA_COMMAND_MOVE_SINK_INPUT,
PA_COMMAND_MOVE_SOURCE_OUTPUT,
+ /* Supported since protocol v11 (0.9.7) */
+ PA_COMMAND_SET_SINK_INPUT_MUTE,
+
+ PA_COMMAND_SUSPEND_SINK,
+ PA_COMMAND_SUSPEND_SOURCE,
+
+ /* Supported since protocol v12 (0.9.8) */
+ PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR,
+ PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR,
+
+ PA_COMMAND_UPDATE_PLAYBACK_STREAM_SAMPLE_RATE,
+ PA_COMMAND_UPDATE_RECORD_STREAM_SAMPLE_RATE,
+
+ /* SERVER->CLIENT */
+ PA_COMMAND_PLAYBACK_STREAM_SUSPENDED,
+ PA_COMMAND_RECORD_STREAM_SUSPENDED,
+ PA_COMMAND_PLAYBACK_STREAM_MOVED,
+ PA_COMMAND_RECORD_STREAM_MOVED,
+
+ /* Supported since protocol v13 (0.9.10) */
+ PA_COMMAND_UPDATE_RECORD_STREAM_PROPLIST,
+ PA_COMMAND_UPDATE_PLAYBACK_STREAM_PROPLIST,
+ PA_COMMAND_UPDATE_CLIENT_PROPLIST,
+ PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST,
+ PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST,
+ PA_COMMAND_REMOVE_CLIENT_PROPLIST,
+
+ /* SERVER->CLIENT */
+ PA_COMMAND_STARTED,
+
PA_COMMAND_MAX
};
diff --git a/src/pulsecore/object.c b/src/pulsecore/object.c
new file mode 100644
index 00000000..9a2f28f3
--- /dev/null
+++ b/src/pulsecore/object.c
@@ -0,0 +1,70 @@
+/***
+ 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 "object.h"
+
+pa_object *pa_object_new_internal(size_t size, const char *type_name, int (*check_type)(const char *type_name)) {
+ pa_object *o;
+
+ pa_assert(size > sizeof(pa_object));
+ pa_assert(type_name);
+
+ if (!check_type)
+ check_type = pa_object_check_type;
+
+ pa_assert(check_type(type_name));
+ pa_assert(check_type("pa_object"));
+
+ o = pa_xmalloc(size);
+ PA_REFCNT_INIT(o);
+ o->type_name = type_name;
+ o->free = pa_object_free;
+ o->check_type = check_type;
+
+ return o;
+}
+
+pa_object *pa_object_ref(pa_object *o) {
+ pa_object_assert_ref(o);
+
+ PA_REFCNT_INC(o);
+ return o;
+}
+
+void pa_object_unref(pa_object *o) {
+ pa_object_assert_ref(o);
+
+ if (PA_REFCNT_DEC(o) <= 0) {
+ pa_assert(o->free);
+ o->free(o);
+ }
+}
+
+int pa_object_check_type(const char *type_name) {
+ pa_assert(type_name);
+
+ return strcmp(type_name, "pa_object") == 0;
+}
diff --git a/src/pulsecore/object.h b/src/pulsecore/object.h
new file mode 100644
index 00000000..7dcfa2eb
--- /dev/null
+++ b/src/pulsecore/object.h
@@ -0,0 +1,104 @@
+#ifndef foopulseobjecthfoo
+#define foopulseobjecthfoo
+
+/***
+ 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.
+***/
+
+#include <string.h>
+#include <sys/types.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/refcnt.h>
+#include <pulsecore/macro.h>
+
+typedef struct pa_object pa_object;
+
+struct pa_object {
+ PA_REFCNT_DECLARE;
+ const char *type_name;
+ void (*free)(pa_object *o);
+ int (*check_type)(const char *type_name);
+};
+
+pa_object *pa_object_new_internal(size_t size, const char *type_name, int (*check_type)(const char *type_name));
+#define pa_object_new(type) ((type*) pa_object_new_internal(sizeof(type), #type, type##_check_type)
+
+#define pa_object_free ((void (*) (pa_object* o)) pa_xfree)
+
+int pa_object_check_type(const char *type);
+
+static inline int pa_object_isinstance(void *o) {
+ pa_object *obj = (pa_object*) o;
+ return obj ? obj->check_type("pa_object") : 0;
+}
+
+pa_object *pa_object_ref(pa_object *o);
+void pa_object_unref(pa_object *o);
+
+static inline int pa_object_refcnt(pa_object *o) {
+ return o ? PA_REFCNT_VALUE(o) : 0;
+}
+
+static inline pa_object* pa_object_cast(void *o) {
+ pa_object *obj = (pa_object*) o;
+ pa_assert(!obj || obj->check_type("pa_object"));
+ return obj;
+}
+
+#define pa_object_assert_ref(o) pa_assert(pa_object_refcnt(o) > 0)
+
+#define PA_OBJECT(o) pa_object_cast(o)
+
+#define PA_DECLARE_CLASS(c) \
+ static inline int c##_isinstance(void *o) { \
+ pa_object *obj = (pa_object*) o; \
+ return obj ? obj->check_type(#c) : 1; \
+ } \
+ static inline c* c##_cast(void *o) { \
+ pa_assert(c##_isinstance(o)); \
+ return (c*) o; \
+ } \
+ static inline c* c##_ref(c *o) { \
+ return (c*) pa_object_ref(PA_OBJECT(o)); \
+ } \
+ static inline void c##_unref(c* o) { \
+ pa_object_unref(PA_OBJECT(o)); \
+ } \
+ static inline int c##_refcnt(c* o) { \
+ return pa_object_refcnt(PA_OBJECT(o)); \
+ } \
+ static inline void c##_assert_ref(c *o) { \
+ pa_object_assert_ref(PA_OBJECT(o)); \
+ } \
+ struct __stupid_useless_struct_to_allow_trailing_semicolon
+
+#define PA_DEFINE_CHECK_TYPE(c, parent) \
+ int c##_check_type(const char *type) { \
+ pa_assert(type); \
+ if (strcmp(type, #c) == 0) \
+ return 1; \
+ return parent##_check_type(type); \
+ } \
+ struct __stupid_useless_struct_to_allow_trailing_semicolon
+
+
+#endif
diff --git a/src/pulsecore/once-posix.c b/src/pulsecore/once-posix.c
deleted file mode 100644
index 865997df..00000000
--- a/src/pulsecore/once-posix.c
+++ /dev/null
@@ -1,69 +0,0 @@
-/* $Id$ */
-
-/***
- This file is part of PulseAudio.
-
- PulseAudio is free software; you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published
- by the Free Software Foundation; either version 2 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 <pthread.h>
-#include <assert.h>
-
-#include <pulsecore/mutex.h>
-
-#include "once.h"
-
-#define ASSERT_SUCCESS(x) do { \
- int _r = (x); \
- assert(_r == 0); \
-} while(0)
-
-static pa_mutex *global_mutex;
-static pthread_once_t global_mutex_once = PTHREAD_ONCE_INIT;
-
-static void global_mutex_once_func(void) {
- global_mutex = pa_mutex_new(0);
-}
-
-void pa_once(pa_once_t *control, pa_once_func_t func) {
- assert(control);
- assert(func);
-
- /* Create the global mutex */
- ASSERT_SUCCESS(pthread_once(&global_mutex_once, global_mutex_once_func));
-
- /* Create the local mutex */
- pa_mutex_lock(global_mutex);
- if (!control->mutex)
- control->mutex = pa_mutex_new(1);
- pa_mutex_unlock(global_mutex);
-
- /* Execute function */
- pa_mutex_lock(control->mutex);
- if (!control->once_value) {
- control->once_value = 1;
- func();
- }
- pa_mutex_unlock(control->mutex);
-
- /* Caveat: We have to make sure that the once func has completed
- * before returning, even if the once func is not actually
- * executed by us. Hence the awkward locking. */
-}
diff --git a/src/pulsecore/once-win32.c b/src/pulsecore/once-win32.c
deleted file mode 100644
index 8b9282f4..00000000
--- a/src/pulsecore/once-win32.c
+++ /dev/null
@@ -1,67 +0,0 @@
-/* $Id$ */
-
-/***
- This file is part of PulseAudio.
-
- PulseAudio is free software; you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published
- by the Free Software Foundation; either version 2 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 <assert.h>
-#include <stdio.h>
-
-#include <windows.h>
-
-#include <pulsecore/mutex.h>
-
-#include "once.h"
-
-void pa_once(pa_once_t *control, pa_once_func_t func) {
- HANDLE mutex;
- char name[64];
-
- assert(control);
- assert(func);
-
- /* Create the global mutex */
- sprintf(name, "pulse%d", (int)GetCurrentProcessId());
-
- mutex = CreateMutex(NULL, FALSE, name);
- assert(mutex);
-
- /* Create the local mutex */
- WaitForSingleObject(mutex, INFINITE);
- if (!control->mutex)
- control->mutex = pa_mutex_new(1);
- ReleaseMutex(mutex);
-
- CloseHandle(mutex);
-
- /* Execute function */
- pa_mutex_lock(control->mutex);
- if (!control->once_value) {
- control->once_value = 1;
- func();
- }
- pa_mutex_unlock(control->mutex);
-
- /* Caveat: We have to make sure that the once func has completed
- * before returning, even if the once func is not actually
- * executed by us. Hence the awkward locking. */
-}
diff --git a/src/pulsecore/once.c b/src/pulsecore/once.c
new file mode 100644
index 00000000..989741dc
--- /dev/null
+++ b/src/pulsecore/once.c
@@ -0,0 +1,94 @@
+/***
+ 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/macro.h>
+#include <pulsecore/mutex.h>
+
+#include "once.h"
+
+int pa_once_begin(pa_once *control) {
+ pa_mutex *m;
+
+ pa_assert(control);
+
+ if (pa_atomic_load(&control->done))
+ return 0;
+
+ pa_atomic_inc(&control->ref);
+
+ /* Caveat: We have to make sure that the once func has completed
+ * before returning, even if the once func is not actually
+ * executed by us. Hence the awkward locking. */
+
+ for (;;) {
+
+ if ((m = pa_atomic_ptr_load(&control->mutex))) {
+
+ /* The mutex is stored in locked state, hence let's just
+ * wait until it is unlocked */
+ pa_mutex_lock(m);
+
+ pa_once_end(control);
+ return 0;
+ }
+
+ pa_assert_se(m = pa_mutex_new(FALSE, FALSE));
+ pa_mutex_lock(m);
+
+ if (pa_atomic_ptr_cmpxchg(&control->mutex, NULL, m))
+ return 1;
+
+ pa_mutex_unlock(m);
+ pa_mutex_free(m);
+ }
+}
+
+void pa_once_end(pa_once *control) {
+ pa_mutex *m;
+
+ pa_assert(control);
+
+ pa_atomic_store(&control->done, 1);
+
+ pa_assert_se(m = pa_atomic_ptr_load(&control->mutex));
+ pa_mutex_unlock(m);
+
+ if (pa_atomic_dec(&control->ref) <= 1) {
+ pa_assert_se(pa_atomic_ptr_cmpxchg(&control->mutex, m, NULL));
+ pa_mutex_free(m);
+ }
+}
+
+/* Not reentrant -- how could it be? */
+void pa_run_once(pa_once *control, pa_once_func_t func) {
+ pa_assert(control);
+ pa_assert(func);
+
+ if (pa_once_begin(control)) {
+ func();
+ pa_once_end(control);
+ }
+}
+
diff --git a/src/pulsecore/once.h b/src/pulsecore/once.h
index 0aabb3f2..576d40fa 100644
--- a/src/pulsecore/once.h
+++ b/src/pulsecore/once.h
@@ -1,21 +1,21 @@
#ifndef foopulseoncehfoo
#define foopulseoncehfoo
-/* $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
@@ -23,16 +23,52 @@
***/
#include <pulsecore/mutex.h>
+#include <pulsecore/atomic.h>
typedef struct pa_once {
- unsigned int once_value;
- pa_mutex *mutex;
-} pa_once_t;
+ pa_atomic_ptr_t mutex;
+ pa_atomic_t ref, done;
+} pa_once;
-#define PA_ONCE_INIT { .once_value = 0, .mutex = NULL }
+#define PA_ONCE_INIT \
+ { \
+ .mutex = PA_ATOMIC_PTR_INIT(NULL), \
+ .ref = PA_ATOMIC_INIT(0), \
+ .done = PA_ATOMIC_INIT(0) \
+ }
-typedef void (*pa_once_func_t) (void);
+/* Not to be called directly, use the macros defined below instead */
+int pa_once_begin(pa_once *o);
+void pa_once_end(pa_once *o);
+
+#define PA_ONCE_BEGIN \
+ do { \
+ static pa_once _once = PA_ONCE_INIT; \
+ if (pa_once_begin(&_once)) {{
+
+#define PA_ONCE_END \
+ } \
+ pa_once_end(&_once); \
+ } \
+ } while(0)
-void pa_once(pa_once_t *o, pa_once_func_t f);
+/*
+
+ Usage of these macros is like this:
+
+ void foo() {
+
+ PA_ONCE_BEGIN {
+
+ ... stuff to be called just once ...
+
+ } PA_ONCE_END;
+ }
+
+*/
+
+/* Same API but calls a function */
+typedef void (*pa_once_func_t) (void);
+void pa_run_once(pa_once *o, pa_once_func_t f);
#endif
diff --git a/src/pulsecore/packet.c b/src/pulsecore/packet.c
index 8b010f01..cee468bd 100644
--- a/src/pulsecore/packet.c
+++ b/src/pulsecore/packet.c
@@ -1,18 +1,18 @@
-/* $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.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
@@ -23,55 +23,55 @@
#include <config.h>
#endif
-#include <assert.h>
#include <stdlib.h>
#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
#include "packet.h"
pa_packet* pa_packet_new(size_t length) {
pa_packet *p;
- assert(length);
+ pa_assert(length > 0);
- p = pa_xmalloc(sizeof(pa_packet)+length);
- p->ref = 1;
+ p = pa_xmalloc(PA_ALIGN(sizeof(pa_packet)) + length);
+ PA_REFCNT_INIT(p);
p->length = length;
- p->data = (uint8_t*) (p+1);
+ p->data = (uint8_t*) p + PA_ALIGN(sizeof(pa_packet));
p->type = PA_PACKET_APPENDED;
-
+
return p;
}
pa_packet* pa_packet_new_dynamic(void* data, size_t length) {
pa_packet *p;
- assert(data);
- assert(length);
+ pa_assert(data);
+ pa_assert(length > 0);
p = pa_xnew(pa_packet, 1);
- p->ref = 1;
+ PA_REFCNT_INIT(p);
p->length = length;
p->data = data;
p->type = PA_PACKET_DYNAMIC;
-
+
return p;
}
pa_packet* pa_packet_ref(pa_packet *p) {
- assert(p);
- assert(p->ref >= 1);
-
- p->ref++;
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) >= 1);
+
+ PA_REFCNT_INC(p);
return p;
}
void pa_packet_unref(pa_packet *p) {
- assert(p);
- assert(p->ref >= 1);
-
- if (--p->ref == 0) {
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) >= 1);
+
+ if (PA_REFCNT_DEC(p) <= 0) {
if (p->type == PA_PACKET_DYNAMIC)
pa_xfree(p->data);
pa_xfree(p);
diff --git a/src/pulsecore/packet.h b/src/pulsecore/packet.h
index 7842857a..5989b1fa 100644
--- a/src/pulsecore/packet.h
+++ b/src/pulsecore/packet.h
@@ -1,21 +1,21 @@
#ifndef foopackethfoo
#define foopackethfoo
-/* $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.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
@@ -25,9 +25,11 @@
#include <sys/types.h>
#include <inttypes.h>
+#include <pulsecore/refcnt.h>
+
typedef struct pa_packet {
+ PA_REFCNT_DECLARE;
enum { PA_PACKET_APPENDED, PA_PACKET_DYNAMIC } type;
- unsigned ref;
size_t length;
uint8_t *data;
} pa_packet;
diff --git a/src/pulsecore/parseaddr.c b/src/pulsecore/parseaddr.c
index da1647af..f2b6b2cf 100644
--- a/src/pulsecore/parseaddr.c
+++ b/src/pulsecore/parseaddr.c
@@ -1,18 +1,18 @@
-/* $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.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
@@ -24,13 +24,13 @@
#endif
#include <string.h>
-#include <assert.h>
#include <stdlib.h>
#include <pulse/xmalloc.h>
-
#include <pulse/util.h>
+
#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
#include "parseaddr.h"
@@ -43,7 +43,9 @@
* Return a newly allocated string of the hostname and fill in *ret_port if specified */
static char *parse_host(const char *s, uint16_t *ret_port) {
- assert(s && ret_port);
+ pa_assert(s);
+ pa_assert(ret_port);
+
if (*s == '[') {
char *e;
if (!(e = strchr(s+1, ']')))
@@ -53,11 +55,11 @@ static char *parse_host(const char *s, uint16_t *ret_port) {
*ret_port = atoi(e+2);
else if (e[1] != 0)
return NULL;
-
+
return pa_xstrndup(s+1, e-s-1);
} else {
char *e;
-
+
if (!(e = strrchr(s, ':')))
return pa_xstrdup(s);
@@ -68,37 +70,43 @@ static char *parse_host(const char *s, uint16_t *ret_port) {
int pa_parse_address(const char *name, pa_parsed_address *ret_p) {
const char *p;
- assert(name && ret_p);
+
+ pa_assert(name);
+ pa_assert(ret_p);
+
memset(ret_p, 0, sizeof(pa_parsed_address));
ret_p->type = PA_PARSED_ADDRESS_TCP_AUTO;
if (*name == '{') {
char hn[256], *pfx;
/* The URL starts with a host specification for detecting local connections */
-
+
if (!pa_get_host_name(hn, sizeof(hn)))
return -1;
-
+
pfx = pa_sprintf_malloc("{%s}", hn);
if (!pa_startswith(name, pfx)) {
pa_xfree(pfx);
/* Not local */
return -1;
}
-
+
p = name + strlen(pfx);
pa_xfree(pfx);
} else
p = name;
-
+
if (*p == '/')
ret_p->type = PA_PARSED_ADDRESS_UNIX;
else if (pa_startswith(p, "unix:")) {
ret_p->type = PA_PARSED_ADDRESS_UNIX;
p += sizeof("unix:")-1;
- } else if (pa_startswith(p, "tcp:") || pa_startswith(p, "tcp4:")) {
+ } else if (pa_startswith(p, "tcp:")) {
ret_p->type = PA_PARSED_ADDRESS_TCP4;
p += sizeof("tcp:")-1;
+ } else if (pa_startswith(p, "tcp4:")) {
+ ret_p->type = PA_PARSED_ADDRESS_TCP4;
+ p += sizeof("tcp4:")-1;
} else if (pa_startswith(p, "tcp6:")) {
ret_p->type = PA_PARSED_ADDRESS_TCP6;
p += sizeof("tcp6:")-1;
@@ -109,7 +117,6 @@ int pa_parse_address(const char *name, pa_parsed_address *ret_p) {
else
if (!(ret_p->path_or_host = parse_host(p, &ret_p->port)))
return -1;
-
-
+
return 0;
}
diff --git a/src/pulsecore/parseaddr.h b/src/pulsecore/parseaddr.h
index 0393f665..5fbcb9a7 100644
--- a/src/pulsecore/parseaddr.h
+++ b/src/pulsecore/parseaddr.h
@@ -1,21 +1,21 @@
#ifndef fooparseaddrhfoo
#define fooparseaddrhfoo
-/* $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.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
diff --git a/src/pulsecore/pdispatch.c b/src/pulsecore/pdispatch.c
index 6ecf710a..e6a6ae4d 100644
--- a/src/pulsecore/pdispatch.c
+++ b/src/pulsecore/pdispatch.c
@@ -1,18 +1,19 @@
-/* $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.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
@@ -25,7 +26,6 @@
#include <stdio.h>
#include <stdlib.h>
-#include <assert.h>
#include <pulse/timeval.h>
#include <pulse/xmalloc.h>
@@ -34,6 +34,9 @@
#include <pulsecore/llist.h>
#include <pulsecore/log.h>
#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/refcnt.h>
+#include <pulsecore/flist.h>
#include "pdispatch.h"
@@ -94,6 +97,8 @@ static const char *command_names[PA_COMMAND_MAX] = {
#endif
+PA_STATIC_FLIST_DECLARE(reply_infos, 0, pa_xfree);
+
struct reply_info {
pa_pdispatch *pdispatch;
PA_LLIST_FIELDS(struct reply_info);
@@ -105,7 +110,7 @@ struct reply_info {
};
struct pa_pdispatch {
- int ref;
+ PA_REFCNT_DECLARE;
pa_mainloop_api *mainloop;
const pa_pdispatch_cb_t *callback_table;
unsigned n_commands;
@@ -116,24 +121,27 @@ struct pa_pdispatch {
};
static void reply_info_free(struct reply_info *r) {
- assert(r && r->pdispatch && r->pdispatch->mainloop);
+ pa_assert(r);
+ pa_assert(r->pdispatch);
+ pa_assert(r->pdispatch->mainloop);
if (r->time_event)
r->pdispatch->mainloop->time_free(r->time_event);
-
+
PA_LLIST_REMOVE(struct reply_info, r->pdispatch->replies, r);
-
- pa_xfree(r);
+
+ if (pa_flist_push(PA_STATIC_FLIST_GET(reply_infos), r) < 0)
+ pa_xfree(r);
}
pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *mainloop, const pa_pdispatch_cb_t*table, unsigned entries) {
pa_pdispatch *pd;
- assert(mainloop);
+ pa_assert(mainloop);
+
+ pa_assert((entries && table) || (!entries && !table));
- assert((entries && table) || (!entries && !table));
-
- pd = pa_xmalloc(sizeof(pa_pdispatch));
- pd->ref = 1;
+ pd = pa_xnew(pa_pdispatch, 1);
+ PA_REFCNT_INIT(pd);
pd->mainloop = mainloop;
pd->callback_table = table;
pd->n_commands = entries;
@@ -141,12 +149,12 @@ pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *mainloop, const pa_pdispatch_cb_
pd->drain_callback = NULL;
pd->drain_userdata = NULL;
pd->creds = NULL;
-
+
return pd;
}
static void pdispatch_free(pa_pdispatch *pd) {
- assert(pd);
+ pa_assert(pd);
while (pd->replies) {
if (pd->replies->free_cb)
@@ -154,7 +162,7 @@ static void pdispatch_free(pa_pdispatch *pd) {
reply_info_free(pd->replies);
}
-
+
pa_xfree(pd);
}
@@ -162,16 +170,16 @@ static void run_action(pa_pdispatch *pd, struct reply_info *r, uint32_t command,
pa_pdispatch_cb_t callback;
void *userdata;
uint32_t tag;
- assert(r);
+ pa_assert(r);
pa_pdispatch_ref(pd);
-
+
callback = r->callback;
userdata = r->userdata;
tag = r->tag;
-
+
reply_info_free(r);
-
+
callback(pd, command, tag, ts, userdata);
if (pd->drain_callback && !pa_pdispatch_is_pending(pd))
@@ -184,27 +192,31 @@ int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*packet, const pa_creds *creds,
uint32_t tag, command;
pa_tagstruct *ts = NULL;
int ret = -1;
- assert(pd && packet && packet->data);
+
+ pa_assert(pd);
+ pa_assert(PA_REFCNT_VALUE(pd) >= 1);
+ pa_assert(packet);
+ pa_assert(PA_REFCNT_VALUE(packet) >= 1);
+ pa_assert(packet->data);
pa_pdispatch_ref(pd);
-
+
if (packet->length <= 8)
goto finish;
ts = pa_tagstruct_new(packet->data, packet->length);
- assert(ts);
-
+
if (pa_tagstruct_getu32(ts, &command) < 0 ||
pa_tagstruct_getu32(ts, &tag) < 0)
goto finish;
-
+
#ifdef DEBUG_OPCODES
{
char t[256];
char const *p;
if (!(p = command_names[command]))
- snprintf((char*) (p = t), sizeof(t), "%u", command);
-
+ pa_snprintf((char*) (p = t), sizeof(t), "%u", command);
+
pa_log("Recieved opcode <%s>", p);
}
#endif
@@ -231,10 +243,10 @@ int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*packet, const pa_creds *creds,
}
ret = 0;
-
+
finish:
pd->creds = NULL;
-
+
if (ts)
pa_tagstruct_free(ts);
@@ -245,7 +257,12 @@ finish:
static void timeout_callback(pa_mainloop_api*m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
struct reply_info*r = userdata;
- assert(r && r->time_event == e && r->pdispatch && r->pdispatch->mainloop == m && r->callback);
+
+ pa_assert(r);
+ pa_assert(r->time_event == e);
+ pa_assert(r->pdispatch);
+ pa_assert(r->pdispatch->mainloop == m);
+ pa_assert(r->callback);
run_action(r->pdispatch, r, PA_COMMAND_TIMEOUT, NULL);
}
@@ -253,33 +270,39 @@ static void timeout_callback(pa_mainloop_api*m, pa_time_event*e, PA_GCC_UNUSED c
void pa_pdispatch_register_reply(pa_pdispatch *pd, uint32_t tag, int timeout, pa_pdispatch_cb_t cb, void *userdata, pa_free_cb_t free_cb) {
struct reply_info *r;
struct timeval tv;
- assert(pd && pd->ref >= 1 && cb);
- r = pa_xmalloc(sizeof(struct reply_info));
+ pa_assert(pd);
+ pa_assert(PA_REFCNT_VALUE(pd) >= 1);
+ pa_assert(cb);
+
+ if (!(r = pa_flist_pop(PA_STATIC_FLIST_GET(reply_infos))))
+ r = pa_xnew(struct reply_info, 1);
+
r->pdispatch = pd;
r->callback = cb;
r->userdata = userdata;
r->free_cb = free_cb;
r->tag = tag;
-
+
pa_gettimeofday(&tv);
tv.tv_sec += timeout;
- r->time_event = pd->mainloop->time_new(pd->mainloop, &tv, timeout_callback, r);
- assert(r->time_event);
+ pa_assert_se(r->time_event = pd->mainloop->time_new(pd->mainloop, &tv, timeout_callback, r));
PA_LLIST_PREPEND(struct reply_info, pd->replies, r);
}
int pa_pdispatch_is_pending(pa_pdispatch *pd) {
- assert(pd);
+ pa_assert(pd);
+ pa_assert(PA_REFCNT_VALUE(pd) >= 1);
return !!pd->replies;
}
void pa_pdispatch_set_drain_callback(pa_pdispatch *pd, void (*cb)(pa_pdispatch *pd, void *userdata), void *userdata) {
- assert(pd);
- assert(!cb || pa_pdispatch_is_pending(pd));
+ pa_assert(pd);
+ pa_assert(PA_REFCNT_VALUE(pd) >= 1);
+ pa_assert(!cb || pa_pdispatch_is_pending(pd));
pd->drain_callback = cb;
pd->drain_userdata = userdata;
@@ -287,32 +310,37 @@ void pa_pdispatch_set_drain_callback(pa_pdispatch *pd, void (*cb)(pa_pdispatch *
void pa_pdispatch_unregister_reply(pa_pdispatch *pd, void *userdata) {
struct reply_info *r, *n;
- assert(pd);
+
+ pa_assert(pd);
+ pa_assert(PA_REFCNT_VALUE(pd) >= 1);
for (r = pd->replies; r; r = n) {
n = r->next;
- if (r->userdata == userdata)
+ if (r->userdata == userdata)
reply_info_free(r);
}
}
void pa_pdispatch_unref(pa_pdispatch *pd) {
- assert(pd && pd->ref >= 1);
+ pa_assert(pd);
+ pa_assert(PA_REFCNT_VALUE(pd) >= 1);
- if (!(--(pd->ref)))
+ if (PA_REFCNT_DEC(pd) <= 0)
pdispatch_free(pd);
}
pa_pdispatch* pa_pdispatch_ref(pa_pdispatch *pd) {
- assert(pd && pd->ref >= 1);
- pd->ref++;
+ pa_assert(pd);
+ pa_assert(PA_REFCNT_VALUE(pd) >= 1);
+
+ PA_REFCNT_INC(pd);
return pd;
}
const pa_creds * pa_pdispatch_creds(pa_pdispatch *pd) {
- assert(pd);
- assert(pd->ref >= 1);
-
+ pa_assert(pd);
+ pa_assert(PA_REFCNT_VALUE(pd) >= 1);
+
return pd->creds;
}
diff --git a/src/pulsecore/pdispatch.h b/src/pulsecore/pdispatch.h
index 479eb6b4..5c31d80e 100644
--- a/src/pulsecore/pdispatch.h
+++ b/src/pulsecore/pdispatch.h
@@ -1,21 +1,22 @@
#ifndef foopdispatchhfoo
#define foopdispatchhfoo
-/* $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.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
diff --git a/src/pulsecore/pid.c b/src/pulsecore/pid.c
index 6e0c085e..addb17cc 100644
--- a/src/pulsecore/pid.c
+++ b/src/pulsecore/pid.c
@@ -1,18 +1,19 @@
-/* $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
@@ -30,7 +31,6 @@
#include <sys/stat.h>
#include <string.h>
#include <stdio.h>
-#include <assert.h>
#include <stdlib.h>
#include <limits.h>
#include <signal.h>
@@ -40,10 +40,12 @@
#endif
#include <pulse/xmalloc.h>
+#include <pulse/util.h>
#include <pulsecore/core-error.h>
#include <pulsecore/core-util.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
#include "pid.h"
@@ -54,23 +56,23 @@ static pid_t read_pid(const char *fn, int fd) {
char t[20], *e;
uint32_t pid;
- assert(fn && fd >= 0);
+ pa_assert(fn);
+ pa_assert(fd >= 0);
if ((r = pa_loop_read(fd, t, sizeof(t)-1, NULL)) < 0) {
- pa_log_warn("WARNING: failed to read PID file '%s': %s",
- fn, pa_cstrerror(errno));
+ pa_log_warn("Failed to read PID file '%s': %s", fn, pa_cstrerror(errno));
return (pid_t) -1;
}
if (r == 0)
return (pid_t) 0;
-
+
t[r] = 0;
if ((e = strchr(t, '\n')))
*e = 0;
if (pa_atou(t, &pid) < 0) {
- pa_log("WARNING: failed to parse PID file '%s'", fn);
+ pa_log_warn("Failed to parse PID file '%s'", fn);
return (pid_t) -1;
}
@@ -79,24 +81,32 @@ static pid_t read_pid(const char *fn, int fd) {
static int open_pid_file(const char *fn, int mode) {
int fd = -1;
-
+
+ pa_assert(fn);
+
for (;;) {
struct stat st;
-
- if ((fd = open(fn, mode, S_IRUSR|S_IWUSR)) < 0) {
+
+ if ((fd = open(fn, mode
+#ifdef O_NOCTTY
+ |O_NOCTTY
+#endif
+#ifdef O_NOFOLLOW
+ |O_NOFOLLOW
+#endif
+ , S_IRUSR|S_IWUSR
+ )) < 0) {
if (mode != O_RDONLY || errno != ENOENT)
- pa_log_warn("WARNING: failed to open PID file '%s': %s",
- fn, pa_cstrerror(errno));
+ pa_log_warn("Failed to open PID file '%s': %s", fn, pa_cstrerror(errno));
goto fail;
}
/* Try to lock the file. If that fails, go without */
if (pa_lock_fd(fd, 1) < 0)
goto fail;
-
+
if (fstat(fd, &st) < 0) {
- pa_log_warn("WARNING: failed to fstat() PID file '%s': %s",
- fn, pa_cstrerror(errno));
+ pa_log_warn("Failed to fstat() PID file '%s': %s", fn, pa_cstrerror(errno));
goto fail;
}
@@ -107,9 +117,9 @@ static int open_pid_file(const char *fn, int mode) {
if (pa_lock_fd(fd, 0) < 0)
goto fail;
- if (close(fd) < 0) {
- pa_log_warn("WARNING: failed to close file '%s': %s",
- fn, pa_cstrerror(errno));
+ if (pa_close(fd) < 0) {
+ pa_log_warn("Failed to close file '%s': %s", fn, pa_cstrerror(errno));
+ fd = -1;
goto fail;
}
@@ -122,84 +132,143 @@ fail:
if (fd >= 0) {
pa_lock_fd(fd, 0);
- close(fd);
+ pa_close(fd);
}
return -1;
}
+static int proc_name_ours(pid_t pid, const char *procname) {
+#ifdef __linux__
+ char bn[PATH_MAX];
+ FILE *f;
+
+ pa_snprintf(bn, sizeof(bn), "/proc/%lu/stat", (unsigned long) pid);
+
+ if (!(f = fopen(bn, "r"))) {
+ pa_log_info("Failed to open %s: %s", bn, pa_cstrerror(errno));
+ return -1;
+ } else {
+ char *expected;
+ pa_bool_t good;
+ char stored[64];
+
+ if (!(fgets(stored, sizeof(stored), f))) {
+ pa_log_info("Failed to read from %s: %s", bn, feof(f) ? "EOF" : pa_cstrerror(errno));
+ fclose(f);
+ return -1;
+ }
+
+ fclose(f);
+
+ expected = pa_sprintf_malloc("%lu (%s)", (unsigned long) pid, procname);
+ good = pa_startswith(stored, expected);
+ pa_xfree(expected);
+
+#if !defined(__OPTIMIZE__)
+ if (!good) {
+ /* libtool likes to rename our binary names ... */
+ expected = pa_sprintf_malloc("%lu (lt-%s)", (unsigned long) pid, procname);
+ good = pa_startswith(stored, expected);
+ pa_xfree(expected);
+ }
+#endif
+
+ return !!good;
+ }
+#endif
+
+ return 1;
+}
+
/* Create a new PID file for the current process. */
-int pa_pid_file_create(void) {
+int pa_pid_file_create(const char *procname) {
int fd = -1;
int ret = -1;
- char fn[PATH_MAX];
char t[20];
pid_t pid;
size_t l;
+ char *fn;
#ifdef OS_IS_WIN32
HANDLE process;
#endif
- pa_runtime_path("pid", fn, sizeof(fn));
+ if (!(fn = pa_runtime_path("pid")))
+ goto fail;
if ((fd = open_pid_file(fn, O_CREAT|O_RDWR)) < 0)
goto fail;
if ((pid = read_pid(fn, fd)) == (pid_t) -1)
- pa_log("corrupt PID file, overwriting.");
+ pa_log_warn("Corrupt PID file, overwriting.");
else if (pid > 0) {
+
#ifdef OS_IS_WIN32
if ((process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid)) != NULL) {
CloseHandle(process);
#else
if (kill(pid, 0) >= 0 || errno != ESRCH) {
#endif
- pa_log("daemon already running.");
- goto fail;
+ int ours = 1;
+
+ if (procname)
+ if ((ours = proc_name_ours(pid, procname)) < 0)
+ goto fail;
+
+ if (ours) {
+ pa_log("Daemon already running.");
+ ret = 1;
+ goto fail;
+ }
}
- pa_log("stale PID file, overwriting.");
+ pa_log_warn("Stale PID file, overwriting.");
}
/* Overwrite the current PID file */
if (lseek(fd, 0, SEEK_SET) == (off_t) -1 || ftruncate(fd, 0) < 0) {
- pa_log("failed to truncate PID file '%s': %s",
- fn, pa_cstrerror(errno));
+ pa_log("Failed to truncate PID file '%s': %s", fn, pa_cstrerror(errno));
goto fail;
}
-
- snprintf(t, sizeof(t), "%lu\n", (unsigned long) getpid());
+
+ pa_snprintf(t, sizeof(t), "%lu\n", (unsigned long) getpid());
l = strlen(t);
-
+
if (pa_loop_write(fd, t, l, NULL) != (ssize_t) l) {
- pa_log("failed to write PID file.");
+ pa_log("Failed to write PID file.");
goto fail;
}
ret = 0;
-
+
fail:
if (fd >= 0) {
pa_lock_fd(fd, 0);
- close(fd);
+
+ if (pa_close(fd) < 0) {
+ pa_log("Failed to close PID file '%s': %s", fn, pa_cstrerror(errno));
+ ret = -1;
+ }
}
-
+
+ pa_xfree(fn);
+
return ret;
}
/* Remove the PID file, if it is ours */
int pa_pid_file_remove(void) {
int fd = -1;
- char fn[PATH_MAX];
+ char *fn;
int ret = -1;
pid_t pid;
- pa_runtime_path("pid", fn, sizeof(fn));
+ if (!(fn = pa_runtime_path("pid")))
+ goto fail;
if ((fd = open_pid_file(fn, O_RDWR)) < 0) {
- pa_log_warn("WARNING: failed to open PID file '%s': %s",
- fn, pa_cstrerror(errno));
+ pa_log_warn("Failed to open PID file '%s': %s", fn, pa_cstrerror(errno));
goto fail;
}
@@ -207,37 +276,41 @@ int pa_pid_file_remove(void) {
goto fail;
if (pid != getpid()) {
- pa_log("WARNING: PID file '%s' not mine!", fn);
+ pa_log("PID file '%s' not mine!", fn);
goto fail;
}
if (ftruncate(fd, 0) < 0) {
- pa_log_warn("WARNING: failed to truncate PID file '%s': %s",
- fn, pa_cstrerror(errno));
+ pa_log_warn("Failed to truncate PID file '%s': %s", fn, pa_cstrerror(errno));
goto fail;
}
#ifdef OS_IS_WIN32
pa_lock_fd(fd, 0);
- close(fd);
+ pa_close(fd);
fd = -1;
#endif
if (unlink(fn) < 0) {
- pa_log_warn("WARNING: failed to remove PID file '%s': %s",
- fn, pa_cstrerror(errno));
+ pa_log_warn("Failed to remove PID file '%s': %s", fn, pa_cstrerror(errno));
goto fail;
}
ret = 0;
-
+
fail:
if (fd >= 0) {
pa_lock_fd(fd, 0);
- close(fd);
+
+ if (pa_close(fd) < 0) {
+ pa_log_warn("Failed to close PID file '%s': %s", fn, pa_cstrerror(errno));
+ ret = -1;
+ }
}
+ pa_xfree(fn);
+
return ret;
}
@@ -245,8 +318,8 @@ fail:
* exists and the PID therein too. Returns 0 on succcess, -1
* otherwise. If pid is non-NULL and a running daemon was found,
* return its PID therein */
-int pa_pid_file_check_running(pid_t *pid) {
- return pa_pid_file_kill(0, pid);
+int pa_pid_file_check_running(pid_t *pid, const char *procname) {
+ return pa_pid_file_kill(0, pid, procname);
}
#ifndef OS_IS_WIN32
@@ -254,39 +327,59 @@ int pa_pid_file_check_running(pid_t *pid) {
/* Kill a current running daemon. Return non-zero on success, -1
* otherwise. If successful *pid contains the PID of the daemon
* process. */
-int pa_pid_file_kill(int sig, pid_t *pid) {
+int pa_pid_file_kill(int sig, pid_t *pid, const char *procname) {
int fd = -1;
- char fn[PATH_MAX];
+ char *fn;
int ret = -1;
pid_t _pid;
+#ifdef __linux__
+ char *e = NULL;
+#endif
if (!pid)
pid = &_pid;
-
- pa_runtime_path("pid", fn, sizeof(fn));
-
+
+ if (!(fn = pa_runtime_path("pid")))
+ goto fail;
+
if ((fd = open_pid_file(fn, O_RDONLY)) < 0)
goto fail;
-
+
if ((*pid = read_pid(fn, fd)) == (pid_t) -1)
goto fail;
+ if (procname) {
+ int ours;
+
+ if ((ours = proc_name_ours(*pid, procname)) < 0)
+ goto fail;
+
+ if (!ours)
+ goto fail;
+ }
+
ret = kill(*pid, sig);
-
+
fail:
-
+
if (fd >= 0) {
pa_lock_fd(fd, 0);
- close(fd);
+ pa_close(fd);
}
+#ifdef __linux__
+ pa_xfree(e);
+#endif
+
+ pa_xfree(fn);
+
return ret;
-
+
}
#else /* OS_IS_WIN32 */
-int pa_pid_file_kill(int sig, pid_t *pid) {
+int pa_pid_file_kill(int sig, pid_t *pid, const char *exe_name) {
return -1;
}
diff --git a/src/pulsecore/pid.h b/src/pulsecore/pid.h
index bd476b29..3c8a9de3 100644
--- a/src/pulsecore/pid.h
+++ b/src/pulsecore/pid.h
@@ -1,30 +1,30 @@
#ifndef foopidhfoo
#define foopidhfoo
-/* $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.
***/
-int pa_pid_file_create(void);
+int pa_pid_file_create(const char *procname);
int pa_pid_file_remove(void);
-int pa_pid_file_check_running(pid_t *pid);
-int pa_pid_file_kill(int sig, pid_t *pid);
+int pa_pid_file_check_running(pid_t *pid, const char *procname);
+int pa_pid_file_kill(int sig, pid_t *pid, const char *procname);
#endif
diff --git a/src/pulsecore/pipe.c b/src/pulsecore/pipe.c
index 41ffb693..93d78a22 100644
--- a/src/pulsecore/pipe.c
+++ b/src/pulsecore/pipe.c
@@ -1,18 +1,18 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
+ Copyright 2006-2007 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
@@ -142,19 +142,19 @@ int pipe(int filedes[2]) {
if ((addr.sin_port != peer.sin_port) || (addr.sin_addr.s_addr != peer.sin_addr.s_addr))
goto error;
- close(listener);
+ pa_close(listener);
return 0;
error:
- if (listener >= 0)
- close(listener);
- if (filedes[0] >= 0)
- close(filedes[0]);
- if (filedes[1] >= 0)
- close(filedes[0]);
-
- return -1;
+ if (listener >= 0)
+ pa_close(listener);
+ if (filedes[0] >= 0)
+ pa_close(filedes[0]);
+ if (filedes[1] >= 0)
+ pa_close(filedes[0]);
+
+ return -1;
}
#endif /* HAVE_PIPE */
diff --git a/src/pulsecore/pipe.h b/src/pulsecore/pipe.h
index 21049e17..9a7e62c8 100644
--- a/src/pulsecore/pipe.h
+++ b/src/pulsecore/pipe.h
@@ -1,21 +1,21 @@
#ifndef foopipehfoo
#define foopipehfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
+ 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
diff --git a/src/pulsecore/play-memblockq.c b/src/pulsecore/play-memblockq.c
index f459142a..8b3e79b9 100644
--- a/src/pulsecore/play-memblockq.c
+++ b/src/pulsecore/play-memblockq.c
@@ -1,18 +1,18 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
+ Copyright 2006-2008 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
@@ -24,103 +24,255 @@
#endif
#include <stdlib.h>
-#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <pulse/xmalloc.h>
+#include <pulse/gccmacro.h>
#include <pulsecore/sink-input.h>
-#include <pulsecore/gccmacro.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/sample-util.h>
#include "play-memblockq.h"
-static void sink_input_kill(pa_sink_input *i) {
- pa_memblockq *q;
- assert(i);
- assert(i->userdata);
+typedef struct memblockq_stream {
+ pa_msgobject parent;
+ pa_core *core;
+ pa_sink_input *sink_input;
+ pa_memblockq *memblockq;
+} memblockq_stream;
- q = i->userdata;
+enum {
+ MEMBLOCKQ_STREAM_MESSAGE_UNLINK,
+};
- pa_sink_input_disconnect(i);
- pa_sink_input_unref(i);
+PA_DECLARE_CLASS(memblockq_stream);
+#define MEMBLOCKQ_STREAM(o) (memblockq_stream_cast(o))
+static PA_DEFINE_CHECK_TYPE(memblockq_stream, pa_msgobject);
+
+static void memblockq_stream_unlink(memblockq_stream *u) {
+ pa_assert(u);
+
+ if (!u->sink_input)
+ return;
- pa_memblockq_free(q);
+ pa_sink_input_unlink(u->sink_input);
+ pa_sink_input_unref(u->sink_input);
+ u->sink_input = NULL;
+
+ memblockq_stream_unref(u);
}
-static int sink_input_peek(pa_sink_input *i, pa_memchunk *chunk) {
- pa_memblockq *q;
- assert(i);
- assert(chunk);
- assert(i->userdata);
+static void memblockq_stream_free(pa_object *o) {
+ memblockq_stream *u = MEMBLOCKQ_STREAM(o);
+ pa_assert(u);
- q = i->userdata;
+ if (u->memblockq)
+ pa_memblockq_free(u->memblockq);
- return pa_memblockq_peek(q, chunk);
+ pa_xfree(u);
}
-static void si_kill(PA_GCC_UNUSED pa_mainloop_api *m, void *i) {
- sink_input_kill(i);
+static int memblockq_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
+ memblockq_stream *u = MEMBLOCKQ_STREAM(o);
+ memblockq_stream_assert_ref(u);
+
+ switch (code) {
+ case MEMBLOCKQ_STREAM_MESSAGE_UNLINK:
+ memblockq_stream_unlink(u);
+ break;
+ }
+
+ return 0;
}
-static void sink_input_drop(pa_sink_input *i, const pa_memchunk*chunk, size_t length) {
- pa_memblockq *q;
-
- assert(i);
- assert(length > 0);
- assert( i->userdata);
-
- q = i->userdata;
+static void sink_input_kill_cb(pa_sink_input *i) {
+ memblockq_stream *u;
- pa_memblockq_drop(q, chunk, length);
+ pa_sink_input_assert_ref(i);
+ u = MEMBLOCKQ_STREAM(i->userdata);
+ memblockq_stream_assert_ref(u);
- if (pa_memblockq_get_length(q) <= 0)
- pa_mainloop_api_once(i->sink->core->mainloop, si_kill, i);
+ memblockq_stream_unlink(u);
}
-int pa_play_memblockq(
+/* Called from IO thread context */
+static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) {
+ memblockq_stream *u;
+
+ pa_sink_input_assert_ref(i);
+ u = MEMBLOCKQ_STREAM(i->userdata);
+ memblockq_stream_assert_ref(u);
+
+ /* If we are added for the first time, ask for a rewinding so that
+ * we are heard right-away. */
+ if (PA_SINK_INPUT_IS_LINKED(state) &&
+ i->thread_info.state == PA_SINK_INPUT_INIT)
+ pa_sink_input_request_rewind(i, 0, FALSE, TRUE);
+}
+
+static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
+ memblockq_stream *u;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert(chunk);
+ u = MEMBLOCKQ_STREAM(i->userdata);
+ memblockq_stream_assert_ref(u);
+
+ if (!u->memblockq)
+ return -1;
+
+ if (pa_memblockq_peek(u->memblockq, chunk) < 0) {
+
+ if (pa_sink_input_safe_to_remove(i)) {
+
+ pa_memblockq_free(u->memblockq);
+ u->memblockq = NULL;
+
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u), MEMBLOCKQ_STREAM_MESSAGE_UNLINK, NULL, 0, NULL, NULL);
+ }
+
+ return -1;
+ }
+
+ chunk->length = PA_MIN(chunk->length, nbytes);
+ pa_memblockq_drop(u->memblockq, chunk->length);
+
+ return 0;
+}
+
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
+ memblockq_stream *u;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert(nbytes > 0);
+ u = MEMBLOCKQ_STREAM(i->userdata);
+ memblockq_stream_assert_ref(u);
+
+ if (!u->memblockq)
+ return;
+
+ pa_memblockq_rewind(u->memblockq, nbytes);
+}
+
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
+ memblockq_stream *u;
+
+ pa_sink_input_assert_ref(i);
+ u = MEMBLOCKQ_STREAM(i->userdata);
+ memblockq_stream_assert_ref(u);
+
+ if (!u->memblockq)
+ return;
+
+ pa_memblockq_set_maxrewind(u->memblockq, nbytes);
+}
+
+pa_sink_input* pa_memblockq_sink_input_new(
pa_sink *sink,
- const char *name,
const pa_sample_spec *ss,
const pa_channel_map *map,
pa_memblockq *q,
- pa_cvolume *volume) {
-
- pa_sink_input *si;
+ pa_cvolume *volume,
+ pa_proplist *p) {
+
+ memblockq_stream *u = NULL;
pa_sink_input_new_data data;
- assert(sink);
- assert(ss);
- assert(q);
+ pa_assert(sink);
+ pa_assert(ss);
- if (pa_memblockq_get_length(q) <= 0) {
- pa_memblockq_free(q);
- return 0;
- }
+ /* We allow creating this stream with no q set, so that it can be
+ * filled in later */
- if (volume && pa_cvolume_is_muted(volume)) {
- pa_memblockq_free(q);
- return 0;
- }
+ u = pa_msgobject_new(memblockq_stream);
+ u->parent.parent.free = memblockq_stream_free;
+ u->parent.process_msg = memblockq_stream_process_msg;
+ u->core = sink->core;
+ u->sink_input = NULL;
+ u->memblockq = NULL;
pa_sink_input_new_data_init(&data);
data.sink = sink;
- data.name = name;
data.driver = __FILE__;
- pa_sink_input_new_data_set_channel_map(&data, map);
pa_sink_input_new_data_set_sample_spec(&data, ss);
+ pa_sink_input_new_data_set_channel_map(&data, map);
pa_sink_input_new_data_set_volume(&data, volume);
-
- if (!(si = pa_sink_input_new(sink->core, &data, 0)))
+ pa_proplist_update(data.proplist, PA_UPDATE_REPLACE, p);
+
+ u->sink_input = pa_sink_input_new(sink->core, &data, 0);
+ pa_sink_input_new_data_done(&data);
+
+ if (!u->sink_input)
+ goto fail;
+
+ u->sink_input->pop = sink_input_pop_cb;
+ u->sink_input->process_rewind = sink_input_process_rewind_cb;
+ u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
+ u->sink_input->kill = sink_input_kill_cb;
+ u->sink_input->state_change = sink_input_state_change_cb;
+ u->sink_input->userdata = u;
+
+ if (q)
+ pa_memblockq_sink_input_set_queue(u->sink_input, q);
+
+ /* The reference to u is dangling here, because we want
+ * to keep this stream around until it is fully played. */
+
+ /* This sink input is not "put" yet, i.e. pa_sink_input_put() has
+ * not been called! */
+
+ return pa_sink_input_ref(u->sink_input);
+
+fail:
+ if (u)
+ memblockq_stream_unref(u);
+
+ return NULL;
+}
+
+int pa_play_memblockq(
+ pa_sink *sink,
+ const pa_sample_spec *ss,
+ const pa_channel_map *map,
+ pa_memblockq *q,
+ pa_cvolume *volume,
+ pa_proplist *p,
+ uint32_t *sink_input_index) {
+
+ pa_sink_input *i;
+
+ pa_assert(sink);
+ pa_assert(ss);
+ pa_assert(q);
+
+ if (!(i = pa_memblockq_sink_input_new(sink, ss, map, q, volume, p)))
return -1;
- si->peek = sink_input_peek;
- si->drop = sink_input_drop;
- si->kill = sink_input_kill;
-
- si->userdata = q;
+ pa_sink_input_put(i);
+
+ if (sink_input_index)
+ *sink_input_index = i->index;
+
+ pa_sink_input_unref(i);
- pa_sink_notify(si->sink);
-
return 0;
}
+
+void pa_memblockq_sink_input_set_queue(pa_sink_input *i, pa_memblockq *q) {
+ memblockq_stream *u;
+
+ pa_sink_input_assert_ref(i);
+ u = MEMBLOCKQ_STREAM(i->userdata);
+ memblockq_stream_assert_ref(u);
+
+ if (u->memblockq)
+ pa_memblockq_free(u->memblockq);
+
+ if ((u->memblockq = q)) {
+ pa_memblockq_set_prebuf(q, 0);
+ pa_memblockq_set_silence(q, NULL);
+ pa_memblockq_willneed(q);
+ }
+}
diff --git a/src/pulsecore/play-memblockq.h b/src/pulsecore/play-memblockq.h
index 9b96efe3..1a42867b 100644
--- a/src/pulsecore/play-memblockq.h
+++ b/src/pulsecore/play-memblockq.h
@@ -1,21 +1,21 @@
#ifndef fooplaymemblockqhfoo
#define fooplaymemblockqhfoo
-/* $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
@@ -25,12 +25,23 @@
#include <pulsecore/sink.h>
#include <pulsecore/memblockq.h>
+pa_sink_input* pa_memblockq_sink_input_new(
+ pa_sink *sink,
+ const pa_sample_spec *ss,
+ const pa_channel_map *map,
+ pa_memblockq *q,
+ pa_cvolume *volume,
+ pa_proplist *p);
+
+void pa_memblockq_sink_input_set_queue(pa_sink_input *i, pa_memblockq *q);
+
int pa_play_memblockq(
pa_sink *sink,
- const char *name,
const pa_sample_spec *ss,
const pa_channel_map *map,
pa_memblockq *q,
- pa_cvolume *cvolume);
+ pa_cvolume *cvolume,
+ pa_proplist *p,
+ uint32_t *sink_input_index);
#endif
diff --git a/src/pulsecore/play-memchunk.c b/src/pulsecore/play-memchunk.c
index b711c98c..0dd48251 100644
--- a/src/pulsecore/play-memchunk.c
+++ b/src/pulsecore/play-memchunk.c
@@ -1,18 +1,18 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
+ Copyright 2004-2008 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
@@ -24,103 +24,41 @@
#endif
#include <stdlib.h>
-#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <pulse/xmalloc.h>
+#include <pulse/gccmacro.h>
#include <pulsecore/sink-input.h>
-#include <pulsecore/gccmacro.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/play-memblockq.h>
#include "play-memchunk.h"
-static void sink_input_kill(pa_sink_input *i) {
- pa_memchunk *c;
- assert(i && i->userdata);
- c = i->userdata;
-
- pa_sink_input_disconnect(i);
- pa_sink_input_unref(i);
-
- pa_memblock_unref(c->memblock);
- pa_xfree(c);
-}
-
-static int sink_input_peek(pa_sink_input *i, pa_memchunk *chunk) {
- pa_memchunk *c;
- assert(i && chunk && i->userdata);
- c = i->userdata;
-
- if (c->length <= 0)
- return -1;
-
- assert(c->memblock);
- *chunk = *c;
- pa_memblock_ref(c->memblock);
-
- return 0;
-}
-
-static void si_kill(PA_GCC_UNUSED pa_mainloop_api *m, void *i) {
- sink_input_kill(i);
-}
-
-static void sink_input_drop(pa_sink_input *i, const pa_memchunk*chunk, size_t length) {
- pa_memchunk *c;
- assert(i && length && i->userdata);
- c = i->userdata;
-
- assert(!memcmp(chunk, c, sizeof(chunk)));
- assert(length <= c->length);
-
- c->length -= length;
- c->index += length;
-
- if (c->length <= 0)
- pa_mainloop_api_once(i->sink->core->mainloop, si_kill, i);
-}
-
int pa_play_memchunk(
pa_sink *sink,
- const char *name,
const pa_sample_spec *ss,
const pa_channel_map *map,
const pa_memchunk *chunk,
- pa_cvolume *volume) {
-
- pa_sink_input *si;
- pa_memchunk *nchunk;
- pa_sink_input_new_data data;
+ pa_cvolume *volume,
+ pa_proplist *p,
+ uint32_t *sink_input_index) {
- assert(sink);
- assert(ss);
- assert(chunk);
+ pa_memblockq *q;
+ int r;
- if (volume && pa_cvolume_is_muted(volume))
- return 0;
+ pa_assert(sink);
+ pa_assert(ss);
+ pa_assert(chunk);
- pa_sink_input_new_data_init(&data);
- data.sink = sink;
- data.name = name;
- data.driver = __FILE__;
- pa_sink_input_new_data_set_sample_spec(&data, ss);
- pa_sink_input_new_data_set_channel_map(&data, map);
- pa_sink_input_new_data_set_volume(&data, volume);
-
- if (!(si = pa_sink_input_new(sink->core, &data, 0)))
- return -1;
+ q = pa_memblockq_new(0, chunk->length, 0, pa_frame_size(ss), 1, 1, 0, NULL);
+ pa_assert_se(pa_memblockq_push(q, chunk) >= 0);
- si->peek = sink_input_peek;
- si->drop = sink_input_drop;
- si->kill = sink_input_kill;
-
- si->userdata = nchunk = pa_xnew(pa_memchunk, 1);
- *nchunk = *chunk;
-
- pa_memblock_ref(chunk->memblock);
+ if ((r = pa_play_memblockq(sink, ss, map, q, volume, p, sink_input_index)) < 0) {
+ pa_memblockq_free(q);
+ return r;
+ }
- pa_sink_notify(si->sink);
-
return 0;
}
diff --git a/src/pulsecore/play-memchunk.h b/src/pulsecore/play-memchunk.h
index 3d5b8cc6..c312ae82 100644
--- a/src/pulsecore/play-memchunk.h
+++ b/src/pulsecore/play-memchunk.h
@@ -1,21 +1,21 @@
#ifndef fooplaychunkhfoo
#define fooplaychunkhfoo
-/* $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
@@ -27,10 +27,11 @@
int pa_play_memchunk(
pa_sink *sink,
- const char *name,
const pa_sample_spec *ss,
const pa_channel_map *map,
const pa_memchunk *chunk,
- pa_cvolume *cvolume);
+ pa_cvolume *cvolume,
+ pa_proplist *p,
+ uint32_t *sink_input_index);
#endif
diff --git a/src/pulsecore/poll.c b/src/pulsecore/poll.c
index 289e0cdf..88ac21e4 100644
--- a/src/pulsecore/poll.c
+++ b/src/pulsecore/poll.c
@@ -1,31 +1,30 @@
-/* $Id$ */
-
-/***
- Copyright (C) 1994, 1996, 1997 Free Software Foundation, Inc.
- Copyright (C) 2005, Cendio AB.
- This file is part of PulseAudio.
- Based on work for the GNU C Library.
-***/
/***
This file is part of PulseAudio.
-
+
+ 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.
***/
+/***
+ Based on work for the GNU C Library.
+ Copyright (C) 1994, 1996, 1997 Free Software Foundation, Inc.
+***/
+
/* Poll the file descriptors described by the NFDS structures starting at
FDS. If TIMEOUT is nonzero and not -1, allow TIMEOUT milliseconds for
an event to occur; if TIMEOUT is -1, block until an event occurs.
@@ -44,7 +43,7 @@
#include "winsock.h"
-#ifndef HAVE_SYS_POLL_H
+#ifndef HAVE_POLL_H
#include <pulsecore/core-util.h>
diff --git a/src/pulsecore/poll.h b/src/pulsecore/poll.h
index c3d486e1..86c37a08 100644
--- a/src/pulsecore/poll.h
+++ b/src/pulsecore/poll.h
@@ -1,32 +1,29 @@
-/* $Id$ */
-
-/***
- Compatibility definitions for System V `poll' interface.
- Copyright (C) 1994,96,97,98,99,2000,2001,2004 Free Software Foundation, Inc.
- Copyright (C) 2005, Cendio AB.
- This file is part of PulseAudio.
- Based on work for the GNU C Library.
-***/
-
/***
This file is part of PulseAudio.
-
+
+ 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.
***/
+/***
+ Based on work for the GNU C Library.
+ Copyright (C) 1994,96,97,98,99,2000,2001,2004 Free Software Foundation, Inc.
+***/
+
/* Event types that can be polled for. These bits may be set in `events'
to indicate the interesting event types; they will appear in `revents'
to indicate the status of the file descriptor. */
diff --git a/src/pulsecore/proplist-util.c b/src/pulsecore/proplist-util.c
new file mode 100644
index 00000000..6005775e
--- /dev/null
+++ b/src/pulsecore/proplist-util.c
@@ -0,0 +1,118 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2008 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <locale.h>
+
+#include <pulse/proplist.h>
+#include <pulse/utf8.h>
+#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+
+#include <pulsecore/core-util.h>
+
+#include "proplist-util.h"
+
+void pa_init_proplist(pa_proplist *p) {
+ int a, b;
+#ifndef HAVE_DECL_ENVIRON
+ extern char **environ;
+#endif
+ char **e;
+
+ pa_assert(p);
+
+ for (e = environ; *e; e++) {
+
+ if (pa_startswith(*e, "PULSE_PROP_")) {
+ size_t kl = strcspn(*e+11, "=");
+ char *k;
+
+ if ((*e)[11+kl] != '=')
+ continue;
+
+ if (!pa_utf8_valid(*e+11+kl+1))
+ continue;
+
+ k = pa_xstrndup(*e+11, kl);
+
+ if (pa_proplist_contains(p, k)) {
+ pa_xfree(k);
+ continue;
+ }
+
+ pa_proplist_sets(p, k, *e+11+kl+1);
+ pa_xfree(k);
+ }
+ }
+
+ if (!pa_proplist_contains(p, PA_PROP_APPLICATION_PROCESS_ID)) {
+ char t[32];
+ pa_snprintf(t, sizeof(t), "%lu", (unsigned long) getpid());
+ pa_proplist_sets(p, PA_PROP_APPLICATION_PROCESS_ID, t);
+ }
+
+ if (!pa_proplist_contains(p, PA_PROP_APPLICATION_PROCESS_USER)) {
+ char t[64];
+ if (pa_get_user_name(t, sizeof(t))) {
+ char *c = pa_utf8_filter(t);
+ pa_proplist_sets(p, PA_PROP_APPLICATION_PROCESS_USER, c);
+ pa_xfree(c);
+ }
+ }
+
+ if (!pa_proplist_contains(p, PA_PROP_APPLICATION_PROCESS_HOST)) {
+ char t[64];
+ if (pa_get_host_name(t, sizeof(t))) {
+ char *c = pa_utf8_filter(t);
+ pa_proplist_sets(p, PA_PROP_APPLICATION_PROCESS_HOST, c);
+ pa_xfree(c);
+ }
+ }
+
+ a = pa_proplist_contains(p, PA_PROP_APPLICATION_PROCESS_BINARY);
+ b = pa_proplist_contains(p, PA_PROP_APPLICATION_NAME);
+
+ if (!a || !b) {
+ char t[PATH_MAX];
+ if (pa_get_binary_name(t, sizeof(t))) {
+ char *c = pa_utf8_filter(t);
+
+ if (!a)
+ pa_proplist_sets(p, PA_PROP_APPLICATION_PROCESS_BINARY, c);
+ if (!b)
+ pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, c);
+
+ pa_xfree(c);
+ }
+ }
+
+ if (!pa_proplist_contains(p, PA_PROP_APPLICATION_LANGUAGE)) {
+ const char *l;
+
+ if ((l = setlocale(LC_MESSAGES, NULL)))
+ pa_proplist_sets(p, PA_PROP_APPLICATION_LANGUAGE, l);
+ }
+}
diff --git a/src/pulsecore/proplist-util.h b/src/pulsecore/proplist-util.h
new file mode 100644
index 00000000..c6bdc103
--- /dev/null
+++ b/src/pulsecore/proplist-util.h
@@ -0,0 +1,29 @@
+#ifndef fooproplistutilutilhfoo
+#define fooproplistutilutilhfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2008 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <pulse/proplist.h>
+
+void pa_init_proplist(pa_proplist *p);
+
+#endif
diff --git a/src/pulsecore/props.c b/src/pulsecore/props.c
index 8879b7aa..23d432ec 100644
--- a/src/pulsecore/props.c
+++ b/src/pulsecore/props.c
@@ -1,29 +1,31 @@
-/* $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.
***/
-#include <assert.h>
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
#include <pulse/xmalloc.h>
-
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
#include "props.h"
@@ -35,9 +37,11 @@ typedef struct pa_property {
/* Allocate a new property object */
static pa_property* property_new(const char *name, void *data) {
pa_property* p;
- assert(name && data);
-
- p = pa_xmalloc(sizeof(pa_property));
+
+ pa_assert(name);
+ pa_assert(data);
+
+ p = pa_xnew(pa_property, 1);
p->name = pa_xstrdup(name);
p->data = data;
@@ -46,7 +50,7 @@ static pa_property* property_new(const char *name, void *data) {
/* Free a property object */
static void property_free(pa_property *p) {
- assert(p);
+ pa_assert(p);
pa_xfree(p->name);
pa_xfree(p);
@@ -54,7 +58,10 @@ static void property_free(pa_property *p) {
void* pa_property_get(pa_core *c, const char *name) {
pa_property *p;
- assert(c && name && c->properties);
+
+ pa_assert(c);
+ pa_assert(name);
+ pa_assert(c->properties);
if (!(p = pa_hashmap_get(c->properties, name)))
return NULL;
@@ -64,7 +71,11 @@ void* pa_property_get(pa_core *c, const char *name) {
int pa_property_set(pa_core *c, const char *name, void *data) {
pa_property *p;
- assert(c && name && data && c->properties);
+
+ pa_assert(c);
+ pa_assert(name);
+ pa_assert(data);
+ pa_assert(c->properties);
if (pa_hashmap_get(c->properties, name))
return -1;
@@ -76,45 +87,51 @@ int pa_property_set(pa_core *c, const char *name, void *data) {
int pa_property_remove(pa_core *c, const char *name) {
pa_property *p;
- assert(c && name && c->properties);
+
+ pa_assert(c);
+ pa_assert(name);
+ pa_assert(c->properties);
if (!(p = pa_hashmap_remove(c->properties, name)))
return -1;
-
+
property_free(p);
return 0;
}
void pa_property_init(pa_core *c) {
- assert(c);
+ pa_assert(c);
c->properties = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
}
void pa_property_cleanup(pa_core *c) {
- assert(c);
+ pa_assert(c);
if (!c->properties)
return;
- assert(!pa_hashmap_size(c->properties));
+ pa_assert(!pa_hashmap_size(c->properties));
pa_hashmap_free(c->properties, NULL, NULL);
c->properties = NULL;
-
+
}
void pa_property_dump(pa_core *c, pa_strbuf *s) {
void *state = NULL;
pa_property *p;
- assert(c && s);
+
+ pa_assert(c);
+ pa_assert(s);
while ((p = pa_hashmap_iterate(c->properties, &state, NULL)))
pa_strbuf_printf(s, "[%s] -> [%p]\n", p->name, p->data);
}
int pa_property_replace(pa_core *c, const char *name, void *data) {
- assert(c && name);
+ pa_assert(c);
+ pa_assert(name);
pa_property_remove(c, name);
return pa_property_set(c, name, data);
diff --git a/src/pulsecore/props.h b/src/pulsecore/props.h
index 39b7ca68..95db229b 100644
--- a/src/pulsecore/props.h
+++ b/src/pulsecore/props.h
@@ -1,21 +1,21 @@
#ifndef foopropshfoo
#define foopropshfoo
-/* $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
diff --git a/src/pulsecore/protocol-cli.c b/src/pulsecore/protocol-cli.c
index 81ce5e8f..30cb475d 100644
--- a/src/pulsecore/protocol-cli.c
+++ b/src/pulsecore/protocol-cli.c
@@ -1,18 +1,18 @@
-/* $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
@@ -23,13 +23,13 @@
#include <config.h>
#endif
-#include <assert.h>
#include <stdlib.h>
#include <pulse/xmalloc.h>
#include <pulsecore/cli.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
#include "protocol-cli.h"
@@ -45,7 +45,8 @@ struct pa_protocol_cli {
static void cli_eof_cb(pa_cli*c, void*userdata) {
pa_protocol_cli *p = userdata;
- assert(p);
+ pa_assert(p);
+
pa_idxset_remove_by_data(p->connections, c, NULL);
pa_cli_free(c);
}
@@ -53,16 +54,18 @@ static void cli_eof_cb(pa_cli*c, void*userdata) {
static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) {
pa_protocol_cli *p = userdata;
pa_cli *c;
- assert(s && io && p);
+
+ pa_assert(s);
+ pa_assert(io);
+ pa_assert(p);
if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {
pa_log("Warning! Too many connections (%u), dropping incoming connection.", MAX_CONNECTIONS);
pa_iochannel_free(io);
return;
}
-
+
c = pa_cli_new(p->core, io, p->module);
- assert(c);
pa_cli_set_eof_callback(c, cli_eof_cb, p);
pa_idxset_put(p->connections, c, NULL);
@@ -70,26 +73,29 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
pa_protocol_cli* pa_protocol_cli_new(pa_core *core, pa_socket_server *server, pa_module *m, PA_GCC_UNUSED pa_modargs *ma) {
pa_protocol_cli* p;
- assert(core && server);
- p = pa_xmalloc(sizeof(pa_protocol_cli));
+ pa_core_assert_ref(core);
+ pa_assert(server);
+
+ p = pa_xnew(pa_protocol_cli, 1);
p->module = m;
p->core = core;
- p->server = server;
+ p->server = pa_socket_server_ref(server);
p->connections = pa_idxset_new(NULL, NULL);
pa_socket_server_set_callback(p->server, on_connection, p);
-
+
return p;
}
static void free_connection(void *p, PA_GCC_UNUSED void *userdata) {
- assert(p);
+ pa_assert(p);
+
pa_cli_free(p);
}
void pa_protocol_cli_free(pa_protocol_cli *p) {
- assert(p);
+ pa_assert(p);
pa_idxset_free(p->connections, free_connection, NULL);
pa_socket_server_unref(p->server);
diff --git a/src/pulsecore/protocol-cli.h b/src/pulsecore/protocol-cli.h
index 84101e14..8922ac62 100644
--- a/src/pulsecore/protocol-cli.h
+++ b/src/pulsecore/protocol-cli.h
@@ -1,21 +1,21 @@
#ifndef fooprotocolclihfoo
#define fooprotocolclihfoo
-/* $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
diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c
index 65b93eb4..db1b4305 100644
--- a/src/pulsecore/protocol-esound.c
+++ b/src/pulsecore/protocol-esound.c
@@ -1,18 +1,19 @@
-/* $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
@@ -26,7 +27,6 @@
#include <errno.h>
#include <string.h>
#include <stdio.h>
-#include <assert.h>
#include <stdlib.h>
#include <limits.h>
@@ -50,6 +50,8 @@
#include <pulsecore/core-util.h>
#include <pulsecore/core-error.h>
#include <pulsecore/ipacl.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/thread-mq.h>
#include "endianmacros.h"
@@ -66,21 +68,25 @@
#define PLAYBACK_BUFFER_SECONDS (.25)
#define PLAYBACK_BUFFER_FRAGMENTS (10)
#define RECORD_BUFFER_SECONDS (5)
-#define RECORD_BUFFER_FRAGMENTS (100)
-#define MAX_CACHE_SAMPLE_SIZE (1024000)
+#define MAX_CACHE_SAMPLE_SIZE (2048000)
+
+#define DEFAULT_SINK_LATENCY (150*PA_USEC_PER_MSEC)
+#define DEFAULT_SOURCE_LATENCY (150*PA_USEC_PER_MSEC)
#define SCACHE_PREFIX "esound."
/* This is heavily based on esound's code */
-struct connection {
+typedef struct connection {
+ pa_msgobject parent;
+
uint32_t index;
- int dead;
+ pa_bool_t dead;
pa_protocol_esound *protocol;
pa_iochannel *io;
pa_client *client;
- int authorized, swap_byte_order;
+ pa_bool_t authorized, swap_byte_order;
void *write_data;
size_t write_data_alloc, write_data_index, write_data_length;
void *read_data;
@@ -93,10 +99,12 @@ struct connection {
pa_defer_event *defer_event;
char *original_name;
-
+
struct {
pa_memblock *current_memblock;
- size_t memblock_index, fragment_size;
+ size_t memblock_index;
+ pa_atomic_t missing;
+ pa_bool_t underrun;
} playback;
struct {
@@ -106,46 +114,63 @@ struct connection {
} scache;
pa_time_event *auth_timeout_event;
-};
+} connection;
+
+PA_DECLARE_CLASS(connection);
+#define CONNECTION(o) (connection_cast(o))
+static PA_DEFINE_CHECK_TYPE(connection, pa_msgobject);
struct pa_protocol_esound {
- int public;
pa_module *module;
pa_core *core;
+ pa_bool_t public;
pa_socket_server *server;
pa_idxset *connections;
+
char *sink_name, *source_name;
unsigned n_player;
uint8_t esd_key[ESD_KEY_LEN];
pa_ip_acl *auth_ip_acl;
};
+enum {
+ SINK_INPUT_MESSAGE_POST_DATA = PA_SINK_INPUT_MESSAGE_MAX, /* data from main loop to sink input */
+ SINK_INPUT_MESSAGE_DISABLE_PREBUF
+};
+
+enum {
+ CONNECTION_MESSAGE_REQUEST_DATA,
+ CONNECTION_MESSAGE_POST_DATA,
+ CONNECTION_MESSAGE_UNLINK_CONNECTION
+};
+
typedef struct proto_handler {
size_t data_length;
- int (*proc)(struct connection *c, esd_proto_t request, const void *data, size_t length);
+ int (*proc)(connection *c, esd_proto_t request, const void *data, size_t length);
const char *description;
} esd_proto_handler_info_t;
-static void sink_input_drop_cb(pa_sink_input *i, const pa_memchunk *chunk, size_t length);
-static int sink_input_peek_cb(pa_sink_input *i, pa_memchunk *chunk);
+static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk);
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes);
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes);
static void sink_input_kill_cb(pa_sink_input *i);
-static pa_usec_t sink_input_get_latency_cb(pa_sink_input *i);
+static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
static pa_usec_t source_output_get_latency_cb(pa_source_output *o);
static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk);
static void source_output_kill_cb(pa_source_output *o);
-static int esd_proto_connect(struct connection *c, esd_proto_t request, const void *data, size_t length);
-static int esd_proto_stream_play(struct connection *c, esd_proto_t request, const void *data, size_t length);
-static int esd_proto_stream_record(struct connection *c, esd_proto_t request, const void *data, size_t length);
-static int esd_proto_get_latency(struct connection *c, esd_proto_t request, const void *data, size_t length);
-static int esd_proto_server_info(struct connection *c, esd_proto_t request, const void *data, size_t length);
-static int esd_proto_all_info(struct connection *c, esd_proto_t request, const void *data, size_t length);
-static int esd_proto_stream_pan(struct connection *c, esd_proto_t request, const void *data, size_t length);
-static int esd_proto_sample_cache(struct connection *c, esd_proto_t request, const void *data, size_t length);
-static int esd_proto_sample_free_or_play(struct connection *c, esd_proto_t request, const void *data, size_t length);
-static int esd_proto_sample_get_id(struct connection *c, esd_proto_t request, const void *data, size_t length);
-static int esd_proto_standby_or_resume(struct connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_connect(connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_stream_play(connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_stream_record(connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_get_latency(connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_server_info(connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_all_info(connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_stream_pan(connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_sample_cache(connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_sample_free_or_play(connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_sample_get_id(connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_standby_or_resume(connection *c, esd_proto_t request, const void *data, size_t length);
/* the big map of protocol handler info */
static struct proto_handler proto_map[ESD_PROTO_MAX] = {
@@ -177,30 +202,61 @@ static struct proto_handler proto_map[ESD_PROTO_MAX] = {
{ 3 * sizeof(int), esd_proto_stream_pan, "stream pan"},
{ 3 * sizeof(int), NULL, "sample pan" },
-
+
{ sizeof(int), NULL, "standby mode" },
{ 0, esd_proto_get_latency, "get latency" }
};
-static void connection_free(struct connection *c) {
- assert(c);
- pa_idxset_remove_by_data(c->protocol->connections, c, NULL);
+static void connection_unlink(connection *c) {
+ pa_assert(c);
- if (c->state == ESD_STREAMING_DATA)
- c->protocol->n_player--;
-
- pa_client_free(c->client);
+ if (!c->protocol)
+ return;
if (c->sink_input) {
- pa_sink_input_disconnect(c->sink_input);
+ pa_sink_input_unlink(c->sink_input);
pa_sink_input_unref(c->sink_input);
+ c->sink_input = NULL;
}
-
+
if (c->source_output) {
- pa_source_output_disconnect(c->source_output);
+ pa_source_output_unlink(c->source_output);
pa_source_output_unref(c->source_output);
+ c->source_output = NULL;
+ }
+
+ if (c->client) {
+ pa_client_free(c->client);
+ c->client = NULL;
}
-
+
+ if (c->state == ESD_STREAMING_DATA)
+ c->protocol->n_player--;
+
+ if (c->io) {
+ pa_iochannel_free(c->io);
+ c->io = NULL;
+ }
+
+ if (c->defer_event) {
+ c->protocol->core->mainloop->defer_free(c->defer_event);
+ c->defer_event = NULL;
+ }
+
+ if (c->auth_timeout_event) {
+ c->protocol->core->mainloop->time_free(c->auth_timeout_event);
+ c->auth_timeout_event = NULL;
+ }
+
+ pa_assert_se(pa_idxset_remove_by_data(c->protocol->connections, c, NULL) == c);
+ c->protocol = NULL;
+ connection_unref(c);
+}
+
+static void connection_free(pa_object *obj) {
+ connection *c = CONNECTION(obj);
+ pa_assert(c);
+
if (c->input_memblockq)
pa_memblockq_free(c->input_memblockq);
if (c->output_memblockq)
@@ -208,58 +264,48 @@ static void connection_free(struct connection *c) {
if (c->playback.current_memblock)
pa_memblock_unref(c->playback.current_memblock);
-
+
pa_xfree(c->read_data);
pa_xfree(c->write_data);
- if (c->io)
- pa_iochannel_free(c->io);
-
- if (c->defer_event)
- c->protocol->core->mainloop->defer_free(c->defer_event);
-
if (c->scache.memchunk.memblock)
pa_memblock_unref(c->scache.memchunk.memblock);
pa_xfree(c->scache.name);
- if (c->auth_timeout_event)
- c->protocol->core->mainloop->time_free(c->auth_timeout_event);
-
pa_xfree(c->original_name);
pa_xfree(c);
}
-static void connection_write_prepare(struct connection *c, size_t length) {
+static void connection_write_prepare(connection *c, size_t length) {
size_t t;
- assert(c);
+ pa_assert(c);
t = c->write_data_length+length;
if (c->write_data_alloc < t)
c->write_data = pa_xrealloc(c->write_data, c->write_data_alloc = t);
- assert(c->write_data);
+ pa_assert(c->write_data);
}
-static void connection_write(struct connection *c, const void *data, size_t length) {
+static void connection_write(connection *c, const void *data, size_t length) {
size_t i;
- assert(c);
+ pa_assert(c);
- assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable);
c->protocol->core->mainloop->defer_enable(c->defer_event, 1);
connection_write_prepare(c, length);
- assert(c->write_data);
+ pa_assert(c->write_data);
i = c->write_data_length;
c->write_data_length += length;
-
- memcpy((char*)c->write_data + i, data, length);
+
+ memcpy((uint8_t*) c->write_data + i, data, length);
}
-static void format_esd2native(int format, int swap_bytes, pa_sample_spec *ss) {
- assert(ss);
+static void format_esd2native(int format, pa_bool_t swap_bytes, pa_sample_spec *ss) {
+ pa_assert(ss);
ss->channels = ((format & ESD_MASK_CHAN) == ESD_STEREO) ? 2 : 1;
if ((format & ESD_MASK_BITS) == ESD_BITS16)
@@ -270,27 +316,29 @@ static void format_esd2native(int format, int swap_bytes, pa_sample_spec *ss) {
static int format_native2esd(pa_sample_spec *ss) {
int format = 0;
-
+
format = (ss->format == PA_SAMPLE_U8) ? ESD_BITS8 : ESD_BITS16;
format |= (ss->channels >= 2) ? ESD_STEREO : ESD_MONO;
return format;
}
-#define CHECK_VALIDITY(expression, string) do { \
+#define CHECK_VALIDITY(expression, ...) do { \
if (!(expression)) { \
- pa_log_warn(__FILE__ ": " string); \
+ pa_log_warn(__FILE__ ": " __VA_ARGS__); \
return -1; \
} \
} while(0);
/*** esound commands ***/
-static int esd_proto_connect(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
+static int esd_proto_connect(connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
uint32_t ekey;
int ok;
- assert(length == (ESD_KEY_LEN + sizeof(uint32_t)));
+ connection_assert_ref(c);
+ pa_assert(data);
+ pa_assert(length == (ESD_KEY_LEN + sizeof(uint32_t)));
if (!c->authorized) {
if (memcmp(data, c->protocol->esd_key, ESD_KEY_LEN) != 0) {
@@ -298,7 +346,7 @@ static int esd_proto_connect(struct connection *c, PA_GCC_UNUSED esd_proto_t req
return -1;
}
- c->authorized = 1;
+ c->authorized = TRUE;
if (c->auth_timeout_event) {
c->protocol->core->mainloop->time_free(c->auth_timeout_event);
c->auth_timeout_event = NULL;
@@ -309,11 +357,11 @@ static int esd_proto_connect(struct connection *c, PA_GCC_UNUSED esd_proto_t req
memcpy(&ekey, data, sizeof(uint32_t));
if (ekey == ESD_ENDIAN_KEY)
- c->swap_byte_order = 0;
+ c->swap_byte_order = FALSE;
else if (ekey == ESD_SWAP_ENDIAN_KEY)
- c->swap_byte_order = 1;
+ c->swap_byte_order = TRUE;
else {
- pa_log("client sent invalid endian key");
+ pa_log_warn("Client sent invalid endian key");
return -1;
}
@@ -322,7 +370,7 @@ static int esd_proto_connect(struct connection *c, PA_GCC_UNUSED esd_proto_t req
return 0;
}
-static int esd_proto_stream_play(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
+static int esd_proto_stream_play(connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
char name[ESD_NAME_MAX], *utf8_name;
int32_t format, rate;
pa_sample_spec ss;
@@ -330,15 +378,17 @@ static int esd_proto_stream_play(struct connection *c, PA_GCC_UNUSED esd_proto_t
pa_sink *sink = NULL;
pa_sink_input_new_data sdata;
- assert(c && length == (sizeof(int32_t)*2+ESD_NAME_MAX));
-
+ connection_assert_ref(c);
+ pa_assert(data);
+ pa_assert(length == (sizeof(int32_t)*2+ESD_NAME_MAX));
+
memcpy(&format, data, sizeof(int32_t));
- format = MAYBE_INT32_SWAP(c->swap_byte_order, format);
- data = (const char*)data + sizeof(int32_t);
+ format = PA_MAYBE_INT32_SWAP(c->swap_byte_order, format);
+ data = (const char*) data + sizeof(int32_t);
memcpy(&rate, data, sizeof(int32_t));
- rate = MAYBE_INT32_SWAP(c->swap_byte_order, rate);
- data = (const char*)data + sizeof(int32_t);
+ rate = PA_MAYBE_INT32_SWAP(c->swap_byte_order, rate);
+ data = (const char*) data + sizeof(int32_t);
ss.rate = rate;
format_esd2native(format, c->swap_byte_order, &ss);
@@ -347,57 +397,65 @@ static int esd_proto_stream_play(struct connection *c, PA_GCC_UNUSED esd_proto_t
if (c->protocol->sink_name) {
sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1);
- CHECK_VALIDITY(sink, "No such sink");
+ CHECK_VALIDITY(sink, "No such sink: %s", c->protocol->sink_name);
}
- strncpy(name, data, sizeof(name));
- name[sizeof(name)-1] = 0;
+ pa_strlcpy(name, data, sizeof(name));
utf8_name = pa_utf8_filter(name);
pa_client_set_name(c->client, utf8_name);
pa_xfree(utf8_name);
-
+
c->original_name = pa_xstrdup(name);
- assert(!c->sink_input && !c->input_memblockq);
+ pa_assert(!c->sink_input && !c->input_memblockq);
pa_sink_input_new_data_init(&sdata);
- sdata.sink = sink;
sdata.driver = __FILE__;
- sdata.name = c->client->name;
- pa_sink_input_new_data_set_sample_spec(&sdata, &ss);
sdata.module = c->protocol->module;
sdata.client = c->client;
-
+ sdata.sink = sink;
+ pa_proplist_update(sdata.proplist, PA_UPDATE_MERGE, c->client->proplist);
+ pa_sink_input_new_data_set_sample_spec(&sdata, &ss);
+
c->sink_input = pa_sink_input_new(c->protocol->core, &sdata, 0);
+ pa_sink_input_new_data_done(&sdata);
+
CHECK_VALIDITY(c->sink_input, "Failed to create sink input.");
- l = (size_t) (pa_bytes_per_second(&ss)*PLAYBACK_BUFFER_SECONDS);
+ l = (size_t) (pa_bytes_per_second(&ss)*PLAYBACK_BUFFER_SECONDS);
c->input_memblockq = pa_memblockq_new(
0,
l,
- 0,
+ l,
pa_frame_size(&ss),
(size_t) -1,
l/PLAYBACK_BUFFER_FRAGMENTS,
+ 0,
NULL);
- pa_iochannel_socket_set_rcvbuf(c->io, l/PLAYBACK_BUFFER_FRAGMENTS*2);
- c->playback.fragment_size = l/10;
+ pa_iochannel_socket_set_rcvbuf(c->io, l);
- c->sink_input->peek = sink_input_peek_cb;
- c->sink_input->drop = sink_input_drop_cb;
+ c->sink_input->parent.process_msg = sink_input_process_msg;
+ c->sink_input->pop = sink_input_pop_cb;
+ c->sink_input->process_rewind = sink_input_process_rewind_cb;
+ c->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
c->sink_input->kill = sink_input_kill_cb;
- c->sink_input->get_latency = sink_input_get_latency_cb;
c->sink_input->userdata = c;
+ pa_sink_input_set_requested_latency(c->sink_input, DEFAULT_SINK_LATENCY);
+
c->state = ESD_STREAMING_DATA;
c->protocol->n_player++;
-
+
+ pa_atomic_store(&c->playback.missing, pa_memblockq_missing(c->input_memblockq));
+
+ pa_sink_input_put(c->sink_input);
+
return 0;
}
-static int esd_proto_stream_record(struct connection *c, esd_proto_t request, const void *data, size_t length) {
+static int esd_proto_stream_record(connection *c, esd_proto_t request, const void *data, size_t length) {
char name[ESD_NAME_MAX], *utf8_name;
int32_t format, rate;
pa_source *source = NULL;
@@ -405,15 +463,17 @@ static int esd_proto_stream_record(struct connection *c, esd_proto_t request, co
size_t l;
pa_source_output_new_data sdata;
- assert(c && length == (sizeof(int32_t)*2+ESD_NAME_MAX));
-
+ connection_assert_ref(c);
+ pa_assert(data);
+ pa_assert(length == (sizeof(int32_t)*2+ESD_NAME_MAX));
+
memcpy(&format, data, sizeof(int32_t));
- format = MAYBE_INT32_SWAP(c->swap_byte_order, format);
- data = (const char*)data + sizeof(int32_t);
+ format = PA_MAYBE_INT32_SWAP(c->swap_byte_order, format);
+ data = (const char*) data + sizeof(int32_t);
memcpy(&rate, data, sizeof(int32_t));
- rate = MAYBE_INT32_SWAP(c->swap_byte_order, rate);
- data = (const char*)data + sizeof(int32_t);
+ rate = PA_MAYBE_INT32_SWAP(c->swap_byte_order, rate);
+ data = (const char*) data + sizeof(int32_t);
ss.rate = rate;
format_esd2native(format, c->swap_byte_order, &ss);
@@ -433,7 +493,7 @@ static int esd_proto_stream_record(struct connection *c, esd_proto_t request, co
return -1;
}
} else {
- assert(request == ESD_PROTO_STREAM_REC);
+ pa_assert(request == ESD_PROTO_STREAM_REC);
if (c->protocol->source_name) {
if (!(source = pa_namereg_get(c->protocol->core, c->protocol->source_name, PA_NAMEREG_SOURCE, 1))) {
@@ -442,57 +502,65 @@ static int esd_proto_stream_record(struct connection *c, esd_proto_t request, co
}
}
}
-
- strncpy(name, data, sizeof(name));
- name[sizeof(name)-1] = 0;
+
+ pa_strlcpy(name, data, sizeof(name));
utf8_name = pa_utf8_filter(name);
pa_client_set_name(c->client, utf8_name);
pa_xfree(utf8_name);
-
+
c->original_name = pa_xstrdup(name);
- assert(!c->output_memblockq && !c->source_output);
+ pa_assert(!c->output_memblockq && !c->source_output);
pa_source_output_new_data_init(&sdata);
- sdata.source = source;
sdata.driver = __FILE__;
- sdata.name = c->client->name;
- pa_source_output_new_data_set_sample_spec(&sdata, &ss);
sdata.module = c->protocol->module;
sdata.client = c->client;
-
- c->source_output = pa_source_output_new(c->protocol->core, &sdata, 9);
- CHECK_VALIDITY(c->sink_input, "Failed to create source_output.");
+ sdata.source = source;
+ pa_proplist_update(sdata.proplist, PA_UPDATE_MERGE, c->client->proplist);
+ pa_source_output_new_data_set_sample_spec(&sdata, &ss);
+
+ c->source_output = pa_source_output_new(c->protocol->core, &sdata, 0);
+ pa_source_output_new_data_done(&sdata);
+
+ CHECK_VALIDITY(c->source_output, "Failed to create source output.");
- l = (size_t) (pa_bytes_per_second(&ss)*RECORD_BUFFER_SECONDS);
+ l = (size_t) (pa_bytes_per_second(&ss)*RECORD_BUFFER_SECONDS);
c->output_memblockq = pa_memblockq_new(
0,
l,
- 0,
+ l,
pa_frame_size(&ss),
1,
0,
+ 0,
NULL);
- pa_iochannel_socket_set_sndbuf(c->io, l/RECORD_BUFFER_FRAGMENTS*2);
-
+ pa_iochannel_socket_set_sndbuf(c->io, l);
+
c->source_output->push = source_output_push_cb;
c->source_output->kill = source_output_kill_cb;
c->source_output->get_latency = source_output_get_latency_cb;
c->source_output->userdata = c;
+ pa_source_output_set_requested_latency(c->source_output, DEFAULT_SOURCE_LATENCY);
+
c->state = ESD_STREAMING_DATA;
c->protocol->n_player++;
-
+
+ pa_source_output_put(c->source_output);
+
return 0;
}
-static int esd_proto_get_latency(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
+static int esd_proto_get_latency(connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
pa_sink *sink;
int32_t latency;
- assert(c && !data && length == 0);
+ connection_ref(c);
+ pa_assert(!data);
+ pa_assert(length == 0);
if (!(sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1)))
latency = 0;
@@ -500,18 +568,20 @@ static int esd_proto_get_latency(struct connection *c, PA_GCC_UNUSED esd_proto_t
double usec = pa_sink_get_latency(sink);
latency = (int) ((usec*44100)/1000000);
}
-
- latency = MAYBE_INT32_SWAP(c->swap_byte_order, latency);
+
+ latency = PA_MAYBE_INT32_SWAP(c->swap_byte_order, latency);
connection_write(c, &latency, sizeof(int32_t));
return 0;
}
-static int esd_proto_server_info(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
+static int esd_proto_server_info(connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
int32_t rate = 44100, format = ESD_STEREO|ESD_BITS16;
int32_t response;
pa_sink *sink;
- assert(c && data && length == sizeof(int32_t));
+ connection_ref(c);
+ pa_assert(data);
+ pa_assert(length == sizeof(int32_t));
if ((sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1))) {
rate = sink->sample_spec.rate;
@@ -522,23 +592,25 @@ static int esd_proto_server_info(struct connection *c, PA_GCC_UNUSED esd_proto_t
response = 0;
connection_write(c, &response, sizeof(int32_t));
- rate = MAYBE_INT32_SWAP(c->swap_byte_order, rate);
+ rate = PA_MAYBE_INT32_SWAP(c->swap_byte_order, rate);
connection_write(c, &rate, sizeof(int32_t));
- format = MAYBE_INT32_SWAP(c->swap_byte_order, format);
+ format = PA_MAYBE_INT32_SWAP(c->swap_byte_order, format);
connection_write(c, &format, sizeof(int32_t));
return 0;
}
-static int esd_proto_all_info(struct connection *c, esd_proto_t request, const void *data, size_t length) {
+static int esd_proto_all_info(connection *c, esd_proto_t request, const void *data, size_t length) {
size_t t, k, s;
- struct connection *conn;
+ connection *conn;
uint32_t idx = PA_IDXSET_INVALID;
unsigned nsamples;
char terminator[sizeof(int32_t)*6+ESD_NAME_MAX];
- assert(c && data && length == sizeof(int32_t));
-
+ connection_ref(c);
+ pa_assert(data);
+ pa_assert(length == sizeof(int32_t));
+
if (esd_proto_server_info(c, request, data, length) < 0)
return -1;
@@ -558,8 +630,8 @@ static int esd_proto_all_info(struct connection *c, esd_proto_t request, const v
if (conn->state != ESD_STREAMING_DATA)
continue;
- assert(t >= k*2+s);
-
+ pa_assert(t >= k*2+s);
+
if (conn->sink_input) {
pa_cvolume volume = *pa_sink_input_get_volume(conn->sink_input);
rate = conn->sink_input->sample_spec.rate;
@@ -567,113 +639,115 @@ static int esd_proto_all_info(struct connection *c, esd_proto_t request, const v
rvolume = (volume.values[1]*ESD_VOLUME_BASE)/PA_VOLUME_NORM;
format = format_native2esd(&conn->sink_input->sample_spec);
}
-
+
/* id */
- id = MAYBE_INT32_SWAP(c->swap_byte_order, (int32_t) (conn->index+1));
+ id = PA_MAYBE_INT32_SWAP(c->swap_byte_order, (int32_t) (conn->index+1));
connection_write(c, &id, sizeof(int32_t));
/* name */
memset(name, 0, ESD_NAME_MAX); /* don't leak old data */
if (conn->original_name)
strncpy(name, conn->original_name, ESD_NAME_MAX);
- else if (conn->client && conn->client->name)
- strncpy(name, conn->client->name, ESD_NAME_MAX);
+ else if (conn->client && pa_proplist_gets(conn->client->proplist, PA_PROP_APPLICATION_NAME))
+ strncpy(name, pa_proplist_gets(conn->client->proplist, PA_PROP_APPLICATION_NAME), ESD_NAME_MAX);
connection_write(c, name, ESD_NAME_MAX);
/* rate */
- rate = MAYBE_INT32_SWAP(c->swap_byte_order, rate);
+ rate = PA_MAYBE_INT32_SWAP(c->swap_byte_order, rate);
connection_write(c, &rate, sizeof(int32_t));
/* left */
- lvolume = MAYBE_INT32_SWAP(c->swap_byte_order, lvolume);
+ lvolume = PA_MAYBE_INT32_SWAP(c->swap_byte_order, lvolume);
connection_write(c, &lvolume, sizeof(int32_t));
/*right*/
- rvolume = MAYBE_INT32_SWAP(c->swap_byte_order, rvolume);
+ rvolume = PA_MAYBE_INT32_SWAP(c->swap_byte_order, rvolume);
connection_write(c, &rvolume, sizeof(int32_t));
/*format*/
- format = MAYBE_INT32_SWAP(c->swap_byte_order, format);
+ format = PA_MAYBE_INT32_SWAP(c->swap_byte_order, format);
connection_write(c, &format, sizeof(int32_t));
t -= k;
}
- assert(t == s*(nsamples+1)+k);
+ pa_assert(t == s*(nsamples+1)+k);
t -= k;
connection_write(c, terminator, k);
if (nsamples) {
pa_scache_entry *ce;
-
+
idx = PA_IDXSET_INVALID;
for (ce = pa_idxset_first(c->protocol->core->scache, &idx); ce; ce = pa_idxset_next(c->protocol->core->scache, &idx)) {
int32_t id, rate, lvolume, rvolume, format, len;
char name[ESD_NAME_MAX];
- assert(t >= s*2);
+ pa_assert(t >= s*2);
/* id */
- id = MAYBE_INT32_SWAP(c->swap_byte_order, (int) (ce->index+1));
+ id = PA_MAYBE_INT32_SWAP(c->swap_byte_order, (int) (ce->index+1));
connection_write(c, &id, sizeof(int32_t));
-
+
/* name */
memset(name, 0, ESD_NAME_MAX); /* don't leak old data */
if (strncmp(ce->name, SCACHE_PREFIX, sizeof(SCACHE_PREFIX)-1) == 0)
strncpy(name, ce->name+sizeof(SCACHE_PREFIX)-1, ESD_NAME_MAX);
else
- snprintf(name, ESD_NAME_MAX, "native.%s", ce->name);
+ pa_snprintf(name, ESD_NAME_MAX, "native.%s", ce->name);
connection_write(c, name, ESD_NAME_MAX);
-
+
/* rate */
- rate = MAYBE_UINT32_SWAP(c->swap_byte_order, ce->sample_spec.rate);
+ rate = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, ce->sample_spec.rate);
connection_write(c, &rate, sizeof(int32_t));
-
+
/* left */
- lvolume = MAYBE_UINT32_SWAP(c->swap_byte_order, (ce->volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM);
+ lvolume = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, (ce->volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM);
connection_write(c, &lvolume, sizeof(int32_t));
-
+
/*right*/
- rvolume = MAYBE_UINT32_SWAP(c->swap_byte_order, (ce->volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM);
+ rvolume = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, (ce->volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM);
connection_write(c, &rvolume, sizeof(int32_t));
-
+
/*format*/
- format = MAYBE_INT32_SWAP(c->swap_byte_order, format_native2esd(&ce->sample_spec));
+ format = PA_MAYBE_INT32_SWAP(c->swap_byte_order, format_native2esd(&ce->sample_spec));
connection_write(c, &format, sizeof(int32_t));
/*length*/
- len = MAYBE_INT32_SWAP(c->swap_byte_order, (int) ce->memchunk.length);
+ len = PA_MAYBE_INT32_SWAP(c->swap_byte_order, (int) ce->memchunk.length);
connection_write(c, &len, sizeof(int32_t));
t -= s;
}
}
- assert(t == s);
+ pa_assert(t == s);
connection_write(c, terminator, s);
return 0;
}
-static int esd_proto_stream_pan(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
+static int esd_proto_stream_pan(connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
int32_t ok;
uint32_t idx, lvolume, rvolume;
- struct connection *conn;
+ connection *conn;
+
+ connection_assert_ref(c);
+ pa_assert(data);
+ pa_assert(length == sizeof(int32_t)*3);
- assert(c && data && length == sizeof(int32_t)*3);
-
memcpy(&idx, data, sizeof(uint32_t));
- idx = MAYBE_UINT32_SWAP(c->swap_byte_order, idx) - 1;
+ idx = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, idx) - 1;
data = (const char*)data + sizeof(uint32_t);
memcpy(&lvolume, data, sizeof(uint32_t));
- lvolume = MAYBE_UINT32_SWAP(c->swap_byte_order, lvolume);
+ lvolume = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, lvolume);
data = (const char*)data + sizeof(uint32_t);
memcpy(&rvolume, data, sizeof(uint32_t));
- rvolume = MAYBE_UINT32_SWAP(c->swap_byte_order, rvolume);
+ rvolume = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, rvolume);
data = (const char*)data + sizeof(uint32_t);
if ((conn = pa_idxset_get_by_index(c->protocol->connections, idx)) && conn->sink_input) {
@@ -687,71 +761,73 @@ static int esd_proto_stream_pan(struct connection *c, PA_GCC_UNUSED esd_proto_t
ok = 0;
connection_write(c, &ok, sizeof(int32_t));
-
+
return 0;
}
-static int esd_proto_sample_cache(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
+static int esd_proto_sample_cache(connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
pa_sample_spec ss;
int32_t format, rate, sc_length;
uint32_t idx;
char name[ESD_NAME_MAX+sizeof(SCACHE_PREFIX)-1];
- assert(c && data && length == (ESD_NAME_MAX+3*sizeof(int32_t)));
+ connection_assert_ref(c);
+ pa_assert(data);
+ pa_assert(length == (ESD_NAME_MAX+3*sizeof(int32_t)));
memcpy(&format, data, sizeof(int32_t));
- format = MAYBE_INT32_SWAP(c->swap_byte_order, format);
+ format = PA_MAYBE_INT32_SWAP(c->swap_byte_order, format);
data = (const char*)data + sizeof(int32_t);
memcpy(&rate, data, sizeof(int32_t));
- rate = MAYBE_INT32_SWAP(c->swap_byte_order, rate);
+ rate = PA_MAYBE_INT32_SWAP(c->swap_byte_order, rate);
data = (const char*)data + sizeof(int32_t);
-
+
ss.rate = rate;
format_esd2native(format, c->swap_byte_order, &ss);
CHECK_VALIDITY(pa_sample_spec_valid(&ss), "Invalid sample specification.");
memcpy(&sc_length, data, sizeof(int32_t));
- sc_length = MAYBE_INT32_SWAP(c->swap_byte_order, sc_length);
+ sc_length = PA_MAYBE_INT32_SWAP(c->swap_byte_order, sc_length);
data = (const char*)data + sizeof(int32_t);
- CHECK_VALIDITY(sc_length <= MAX_CACHE_SAMPLE_SIZE, "Sample too large.");
+ CHECK_VALIDITY(sc_length <= MAX_CACHE_SAMPLE_SIZE, "Sample too large (%d bytes).", (int)sc_length);
strcpy(name, SCACHE_PREFIX);
- strncpy(name+sizeof(SCACHE_PREFIX)-1, data, ESD_NAME_MAX);
- name[sizeof(name)-1] = 0;
+ pa_strlcpy(name+sizeof(SCACHE_PREFIX)-1, data, ESD_NAME_MAX);
CHECK_VALIDITY(pa_utf8_valid(name), "Invalid UTF8 in sample name.");
-
- assert(!c->scache.memchunk.memblock);
+
+ pa_assert(!c->scache.memchunk.memblock);
c->scache.memchunk.memblock = pa_memblock_new(c->protocol->core->mempool, sc_length);
c->scache.memchunk.index = 0;
c->scache.memchunk.length = sc_length;
c->scache.sample_spec = ss;
- assert(!c->scache.name);
+ pa_assert(!c->scache.name);
c->scache.name = pa_xstrdup(name);
-
+
c->state = ESD_CACHING_SAMPLE;
- pa_scache_add_item(c->protocol->core, c->scache.name, NULL, NULL, NULL, &idx);
+ pa_scache_add_item(c->protocol->core, c->scache.name, NULL, NULL, NULL, c->client->proplist, &idx);
idx += 1;
connection_write(c, &idx, sizeof(uint32_t));
-
+
return 0;
}
-static int esd_proto_sample_get_id(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
+static int esd_proto_sample_get_id(connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
int32_t ok;
uint32_t idx;
char name[ESD_NAME_MAX+sizeof(SCACHE_PREFIX)-1];
- assert(c && data && length == ESD_NAME_MAX);
+ connection_assert_ref(c);
+ pa_assert(data);
+ pa_assert(length == ESD_NAME_MAX);
strcpy(name, SCACHE_PREFIX);
- strncpy(name+sizeof(SCACHE_PREFIX)-1, data, ESD_NAME_MAX);
- name[sizeof(name)-1] = 0;
+ pa_strlcpy(name+sizeof(SCACHE_PREFIX)-1, data, ESD_NAME_MAX);
CHECK_VALIDITY(pa_utf8_valid(name), "Invalid UTF8 in sample name.");
@@ -764,41 +840,45 @@ static int esd_proto_sample_get_id(struct connection *c, PA_GCC_UNUSED esd_proto
return 0;
}
-static int esd_proto_sample_free_or_play(struct connection *c, esd_proto_t request, const void *data, size_t length) {
+static int esd_proto_sample_free_or_play(connection *c, esd_proto_t request, const void *data, size_t length) {
int32_t ok;
const char *name;
uint32_t idx;
- assert(c && data && length == sizeof(int32_t));
+ connection_assert_ref(c);
+ pa_assert(data);
+ pa_assert(length == sizeof(int32_t));
memcpy(&idx, data, sizeof(uint32_t));
- idx = MAYBE_UINT32_SWAP(c->swap_byte_order, idx) - 1;
+ idx = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, idx) - 1;
ok = 0;
-
+
if ((name = pa_scache_get_name_by_id(c->protocol->core, idx))) {
if (request == ESD_PROTO_SAMPLE_PLAY) {
pa_sink *sink;
-
+
if ((sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1)))
- if (pa_scache_play_item(c->protocol->core, name, sink, PA_VOLUME_NORM) >= 0)
+ if (pa_scache_play_item(c->protocol->core, name, sink, PA_VOLUME_NORM, c->client->proplist, NULL) >= 0)
ok = idx + 1;
} else {
- assert(request == ESD_PROTO_SAMPLE_FREE);
+ pa_assert(request == ESD_PROTO_SAMPLE_FREE);
if (pa_scache_remove_item(c->protocol->core, name) >= 0)
ok = idx + 1;
}
}
-
+
connection_write(c, &ok, sizeof(int32_t));
return 0;
}
-static int esd_proto_standby_or_resume(struct connection *c, PA_GCC_UNUSED esd_proto_t request, PA_GCC_UNUSED const void *data, PA_GCC_UNUSED size_t length) {
+static int esd_proto_standby_or_resume(connection *c, PA_GCC_UNUSED esd_proto_t request, PA_GCC_UNUSED const void *data, PA_GCC_UNUSED size_t length) {
int32_t ok;
+ connection_assert_ref(c);
+
connection_write_prepare(c, sizeof(int32_t) * 2);
ok = 1;
@@ -811,20 +891,21 @@ static int esd_proto_standby_or_resume(struct connection *c, PA_GCC_UNUSED esd_p
/*** client callbacks ***/
static void client_kill_cb(pa_client *c) {
- assert(c && c->userdata);
- connection_free(c->userdata);
+ pa_assert(c);
+
+ connection_unlink(CONNECTION(c->userdata));
}
/*** pa_iochannel callbacks ***/
-static int do_read(struct connection *c) {
- assert(c && c->io);
+static int do_read(connection *c) {
+ connection_assert_ref(c);
+
+/* pa_log("READ"); */
-/* pa_log("READ"); */
-
if (c->state == ESD_NEXT_REQUEST) {
ssize_t r;
- assert(c->read_data_length < sizeof(c->request));
+ pa_assert(c->read_data_length < sizeof(c->request));
if ((r = pa_iochannel_read(c->io, ((uint8_t*) &c->request) + c->read_data_length, sizeof(c->request) - c->read_data_length)) <= 0) {
pa_log_debug("read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");
@@ -833,8 +914,8 @@ static int do_read(struct connection *c) {
if ((c->read_data_length+= r) >= sizeof(c->request)) {
struct proto_handler *handler;
-
- c->request = MAYBE_INT32_SWAP(c->swap_byte_order, c->request);
+
+ c->request = PA_MAYBE_INT32_SWAP(c->swap_byte_order, c->request);
if (c->request < ESD_PROTO_CONNECT || c->request > ESD_PROTO_MAX) {
pa_log("recieved invalid request.");
@@ -849,18 +930,18 @@ static int do_read(struct connection *c) {
pa_log("recieved unimplemented request #%u.", c->request);
return -1;
}
-
+
if (handler->data_length == 0) {
c->read_data_length = 0;
if (handler->proc(c, c->request, NULL, 0) < 0)
return -1;
-
+
} else {
if (c->read_data_alloc < handler->data_length)
c->read_data = pa_xrealloc(c->read_data, c->read_data_alloc = handler->data_length);
- assert(c->read_data);
-
+ pa_assert(c->read_data);
+
c->state = ESD_NEEDS_REQDATA;
c->read_data_length = 0;
}
@@ -870,22 +951,25 @@ static int do_read(struct connection *c) {
ssize_t r;
struct proto_handler *handler = proto_map+c->request;
- assert(handler->proc);
-
- assert(c->read_data && c->read_data_length < handler->data_length);
+ pa_assert(handler->proc);
+
+ pa_assert(c->read_data && c->read_data_length < handler->data_length);
if ((r = pa_iochannel_read(c->io, (uint8_t*) c->read_data + c->read_data_length, handler->data_length - c->read_data_length)) <= 0) {
+ if (r < 0 && (errno == EINTR || errno == EAGAIN))
+ return 0;
+
pa_log_debug("read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");
return -1;
}
if ((c->read_data_length += r) >= handler->data_length) {
size_t l = c->read_data_length;
- assert(handler->proc);
+ pa_assert(handler->proc);
c->state = ESD_NEXT_REQUEST;
c->read_data_length = 0;
-
+
if (handler->proc(c, c->request, c->read_data, l) < 0)
return -1;
}
@@ -893,28 +977,30 @@ static int do_read(struct connection *c) {
ssize_t r;
void *p;
- assert(c->scache.memchunk.memblock);
- assert(c->scache.name);
- assert(c->scache.memchunk.index < c->scache.memchunk.length);
+ pa_assert(c->scache.memchunk.memblock);
+ pa_assert(c->scache.name);
+ pa_assert(c->scache.memchunk.index < c->scache.memchunk.length);
p = pa_memblock_acquire(c->scache.memchunk.memblock);
-
- if ((r = pa_iochannel_read(c->io, (uint8_t*) p+c->scache.memchunk.index, c->scache.memchunk.length-c->scache.memchunk.index)) <= 0) {
- pa_memblock_release(c->scache.memchunk.memblock);
+ r = pa_iochannel_read(c->io, (uint8_t*) p+c->scache.memchunk.index, c->scache.memchunk.length-c->scache.memchunk.index);
+ pa_memblock_release(c->scache.memchunk.memblock);
+
+ if (r <= 0) {
+ if (r < 0 && (errno == EINTR || errno == EAGAIN))
+ return 0;
+
pa_log_debug("read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");
return -1;
}
- pa_memblock_release(c->scache.memchunk.memblock);
-
c->scache.memchunk.index += r;
- assert(c->scache.memchunk.index <= c->scache.memchunk.length);
-
+ pa_assert(c->scache.memchunk.index <= c->scache.memchunk.length);
+
if (c->scache.memchunk.index == c->scache.memchunk.length) {
uint32_t idx;
-
+
c->scache.memchunk.index = 0;
- pa_scache_add_item(c->protocol->core, c->scache.name, &c->scache.sample_spec, NULL, &c->scache.memchunk, &idx);
+ pa_scache_add_item(c->protocol->core, c->scache.name, &c->scache.sample_spec, NULL, &c->scache.memchunk, c->client->proplist, &idx);
pa_memblock_unref(c->scache.memchunk.memblock);
c->scache.memchunk.memblock = NULL;
@@ -928,127 +1014,133 @@ static int do_read(struct connection *c) {
idx += 1;
connection_write(c, &idx, sizeof(uint32_t));
}
-
+
} else if (c->state == ESD_STREAMING_DATA && c->sink_input) {
pa_memchunk chunk;
ssize_t r;
size_t l;
void *p;
+ size_t space;
- assert(c->input_memblockq);
+ pa_assert(c->input_memblockq);
/* pa_log("STREAMING_DATA"); */
- if (!(l = pa_memblockq_missing(c->input_memblockq)))
+ if (!(l = pa_atomic_load(&c->playback.missing)))
return 0;
- if (l > c->playback.fragment_size)
- l = c->playback.fragment_size;
+ if (c->playback.current_memblock) {
+
+ space = pa_memblock_get_length(c->playback.current_memblock) - c->playback.memblock_index;
- if (c->playback.current_memblock)
- if (pa_memblock_get_length(c->playback.current_memblock) - c->playback.memblock_index < l) {
+ if (space <= 0) {
pa_memblock_unref(c->playback.current_memblock);
c->playback.current_memblock = NULL;
- c->playback.memblock_index = 0;
}
-
+ }
+
if (!c->playback.current_memblock) {
- c->playback.current_memblock = pa_memblock_new(c->protocol->core->mempool, c->playback.fragment_size*2);
- assert(c->playback.current_memblock);
- assert(pa_memblock_get_length(c->playback.current_memblock) >= l);
+ pa_assert_se(c->playback.current_memblock = pa_memblock_new(c->protocol->core->mempool, (size_t) -1));
c->playback.memblock_index = 0;
+
+ space = pa_memblock_get_length(c->playback.current_memblock);
}
+ if (l > space)
+ l = space;
+
p = pa_memblock_acquire(c->playback.current_memblock);
-
- if ((r = pa_iochannel_read(c->io, (uint8_t*) p+c->playback.memblock_index, l)) <= 0) {
- pa_memblock_release(c->playback.current_memblock);
+ r = pa_iochannel_read(c->io, (uint8_t*) p+c->playback.memblock_index, l);
+ pa_memblock_release(c->playback.current_memblock);
+
+ if (r <= 0) {
+
+ if (r < 0 && (errno == EINTR || errno == EAGAIN))
+ return 0;
+
pa_log_debug("read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");
return -1;
}
- pa_memblock_release(c->playback.current_memblock);
-
chunk.memblock = c->playback.current_memblock;
chunk.index = c->playback.memblock_index;
chunk.length = r;
- assert(chunk.memblock);
c->playback.memblock_index += r;
-
- assert(c->input_memblockq);
- pa_memblockq_push_align(c->input_memblockq, &chunk);
- assert(c->sink_input);
- pa_sink_notify(c->sink_input->sink);
+
+ pa_asyncmsgq_post(c->sink_input->sink->asyncmsgq, PA_MSGOBJECT(c->sink_input), SINK_INPUT_MESSAGE_POST_DATA, NULL, 0, &chunk, NULL);
+ pa_atomic_sub(&c->playback.missing, r);
}
-
+
return 0;
}
-static int do_write(struct connection *c) {
- assert(c && c->io);
+static int do_write(connection *c) {
+ connection_assert_ref(c);
/* pa_log("WRITE"); */
-
+
if (c->write_data_length) {
ssize_t r;
-
- assert(c->write_data_index < c->write_data_length);
+
+ pa_assert(c->write_data_index < c->write_data_length);
if ((r = pa_iochannel_write(c->io, (uint8_t*) c->write_data+c->write_data_index, c->write_data_length-c->write_data_index)) < 0) {
+
+ if (r < 0 && (errno == EINTR || errno == EAGAIN))
+ return 0;
+
pa_log("write(): %s", pa_cstrerror(errno));
return -1;
}
-
+
if ((c->write_data_index +=r) >= c->write_data_length)
c->write_data_length = c->write_data_index = 0;
-
+
} else if (c->state == ESD_STREAMING_DATA && c->source_output) {
pa_memchunk chunk;
ssize_t r;
void *p;
- assert(c->output_memblockq);
if (pa_memblockq_peek(c->output_memblockq, &chunk) < 0)
return 0;
-
- assert(chunk.memblock);
- assert(chunk.length);
- p = pa_memblock_acquire(chunk.memblock);
-
- if ((r = pa_iochannel_write(c->io, (uint8_t*) p+chunk.index, chunk.length)) < 0) {
- pa_memblock_release(chunk.memblock);
- pa_memblock_unref(chunk.memblock);
- pa_log("write(): %s", pa_cstrerror(errno));
- return -1;
- }
+ pa_assert(chunk.memblock);
+ pa_assert(chunk.length);
+ p = pa_memblock_acquire(chunk.memblock);
+ r = pa_iochannel_write(c->io, (uint8_t*) p+chunk.index, chunk.length);
pa_memblock_release(chunk.memblock);
- pa_memblockq_drop(c->output_memblockq, &chunk, r);
pa_memblock_unref(chunk.memblock);
- pa_source_notify(c->source_output->source);
+ if (r < 0) {
+
+ if (r < 0 && (errno == EINTR || errno == EAGAIN))
+ return 0;
+
+ pa_log("write(): %s", pa_cstrerror(errno));
+ return -1;
+ }
+
+ pa_memblockq_drop(c->output_memblockq, r);
}
-
+
return 0;
}
-static void do_work(struct connection *c) {
- assert(c);
+static void do_work(connection *c) {
+ connection_assert_ref(c);
- assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable);
c->protocol->core->mainloop->defer_enable(c->defer_event, 0);
if (c->dead)
return;
- if (pa_iochannel_is_readable(c->io)) {
+ if (pa_iochannel_is_readable(c->io))
if (do_read(c) < 0)
goto fail;
- }
- if (c->state == ESD_STREAMING_DATA && c->source_output && pa_iochannel_is_hungup(c->io))
+ if (c->state == ESD_STREAMING_DATA && !c->sink_input && pa_iochannel_is_hungup(c->io))
/* In case we are in capture mode we will never call read()
* on the socket, hence we need to detect the hangup manually
* here, instead of simply waiting for read() to return 0. */
@@ -1057,152 +1149,254 @@ static void do_work(struct connection *c) {
if (pa_iochannel_is_writable(c->io))
if (do_write(c) < 0)
goto fail;
-
+
return;
fail:
if (c->state == ESD_STREAMING_DATA && c->sink_input) {
- c->dead = 1;
+ c->dead = TRUE;
pa_iochannel_free(c->io);
c->io = NULL;
- pa_memblockq_prebuf_disable(c->input_memblockq);
- pa_sink_notify(c->sink_input->sink);
+ pa_asyncmsgq_post(c->sink_input->sink->asyncmsgq, PA_MSGOBJECT(c->sink_input), SINK_INPUT_MESSAGE_DISABLE_PREBUF, NULL, 0, NULL, NULL);
} else
- connection_free(c);
+ connection_unlink(c);
}
static void io_callback(pa_iochannel*io, void *userdata) {
- struct connection *c = userdata;
- assert(io && c && c->io == io);
+ connection *c = CONNECTION(userdata);
+
+ connection_assert_ref(c);
+ pa_assert(io);
do_work(c);
}
-/*** defer callback ***/
-
static void defer_callback(pa_mainloop_api*a, pa_defer_event *e, void *userdata) {
- struct connection *c = userdata;
- assert(a && c && c->defer_event == e);
+ connection *c = CONNECTION(userdata);
+
+ connection_assert_ref(c);
+ pa_assert(e);
-/* pa_log("DEFER"); */
-
do_work(c);
}
+static int connection_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
+ connection *c = CONNECTION(o);
+ connection_assert_ref(c);
+
+ switch (code) {
+ case CONNECTION_MESSAGE_REQUEST_DATA:
+ do_work(c);
+ break;
+
+ case CONNECTION_MESSAGE_POST_DATA:
+/* pa_log("got data %u", chunk->length); */
+ pa_memblockq_push_align(c->output_memblockq, chunk);
+ do_work(c);
+ break;
+
+ case CONNECTION_MESSAGE_UNLINK_CONNECTION:
+ connection_unlink(c);
+ break;
+ }
+
+ return 0;
+}
+
/*** sink_input callbacks ***/
-static int sink_input_peek_cb(pa_sink_input *i, pa_memchunk *chunk) {
- struct connection*c;
- assert(i && i->userdata && chunk);
- c = i->userdata;
-
+/* Called from thread context */
+static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
+ pa_sink_input *i = PA_SINK_INPUT(o);
+ connection*c;
+
+ pa_sink_input_assert_ref(i);
+ c = CONNECTION(i->userdata);
+ connection_assert_ref(c);
+
+ switch (code) {
+
+ case SINK_INPUT_MESSAGE_POST_DATA: {
+ pa_assert(chunk);
+
+ /* New data from the main loop */
+ pa_memblockq_push_align(c->input_memblockq, chunk);
+
+ if (pa_memblockq_is_readable(c->input_memblockq) && c->playback.underrun) {
+ pa_log_debug("Requesting rewind due to end of underrun.");
+ pa_sink_input_request_rewind(c->sink_input, 0, FALSE, TRUE);
+ }
+
+/* pa_log("got data, %u", pa_memblockq_get_length(c->input_memblockq)); */
+
+ return 0;
+ }
+
+ case SINK_INPUT_MESSAGE_DISABLE_PREBUF:
+ pa_memblockq_prebuf_disable(c->input_memblockq);
+ return 0;
+
+ case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
+ pa_usec_t *r = userdata;
+
+ *r = pa_bytes_to_usec(pa_memblockq_get_length(c->input_memblockq), &c->sink_input->sample_spec);
+
+ /* Fall through, the default handler will add in the extra
+ * latency added by the resampler */
+ }
+
+ default:
+ return pa_sink_input_process_msg(o, code, userdata, offset, chunk);
+ }
+}
+
+/* Called from thread context */
+static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
+ connection*c;
+
+ pa_sink_input_assert_ref(i);
+ c = CONNECTION(i->userdata);
+ connection_assert_ref(c);
+ pa_assert(chunk);
+
if (pa_memblockq_peek(c->input_memblockq, chunk) < 0) {
- if (c->dead)
- connection_free(c);
-
+ c->playback.underrun = TRUE;
+
+ if (c->dead && pa_sink_input_safe_to_remove(i))
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_UNLINK_CONNECTION, NULL, 0, NULL, NULL);
+
return -1;
- }
+ } else {
+ size_t m;
- return 0;
+ chunk->length = PA_MIN(length, chunk->length);
+
+ c->playback.underrun = FALSE;
+
+ pa_memblockq_drop(c->input_memblockq, chunk->length);
+ m = pa_memblockq_pop_missing(c->input_memblockq);
+
+ if (m > 0)
+ if (pa_atomic_add(&c->playback.missing, m) <= 0)
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL);
+
+ return 0;
+ }
}
-static void sink_input_drop_cb(pa_sink_input *i, const pa_memchunk *chunk, size_t length) {
- struct connection*c = i->userdata;
- assert(i && c && length);
+/* Called from thread context */
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
+ connection *c;
+
+ pa_sink_input_assert_ref(i);
+ c = CONNECTION(i->userdata);
+ connection_assert_ref(c);
+
+ /* If we are in an underrun, then we don't rewind */
+ if (i->thread_info.underrun_for > 0)
+ return;
-/* pa_log("DROP"); */
-
- pa_memblockq_drop(c->input_memblockq, chunk, length);
+ pa_memblockq_rewind(c->input_memblockq, nbytes);
+}
- /* do something */
- assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable);
+/* Called from thread context */
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
+ connection *c;
- if (!c->dead)
- c->protocol->core->mainloop->defer_enable(c->defer_event, 1);
+ pa_sink_input_assert_ref(i);
+ c = CONNECTION(i->userdata);
+ connection_assert_ref(c);
-/* assert(pa_memblockq_get_length(c->input_memblockq) > 2048); */
+ pa_memblockq_set_maxrewind(c->input_memblockq, nbytes);
}
static void sink_input_kill_cb(pa_sink_input *i) {
- assert(i && i->userdata);
- connection_free((struct connection *) i->userdata);
-}
+ pa_sink_input_assert_ref(i);
-static pa_usec_t sink_input_get_latency_cb(pa_sink_input *i) {
- struct connection*c = i->userdata;
- assert(i && c);
- return pa_bytes_to_usec(pa_memblockq_get_length(c->input_memblockq), &c->sink_input->sample_spec);
+ connection_unlink(CONNECTION(i->userdata));
}
/*** source_output callbacks ***/
+/* Called from thread context */
static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
- struct connection *c = o->userdata;
- assert(o && c && chunk);
+ connection *c;
- pa_memblockq_push(c->output_memblockq, chunk);
+ pa_source_output_assert_ref(o);
+ c = CONNECTION(o->userdata);
+ pa_assert(c);
+ pa_assert(chunk);
- /* do something */
- assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable);
-
- if (!c->dead)
- c->protocol->core->mainloop->defer_enable(c->defer_event, 1);
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_POST_DATA, NULL, 0, chunk, NULL);
}
static void source_output_kill_cb(pa_source_output *o) {
- assert(o && o->userdata);
- connection_free((struct connection *) o->userdata);
+ pa_source_output_assert_ref(o);
+
+ connection_unlink(CONNECTION(o->userdata));
}
static pa_usec_t source_output_get_latency_cb(pa_source_output *o) {
- struct connection*c = o->userdata;
- assert(o && c);
+ connection*c;
+
+ pa_source_output_assert_ref(o);
+ c = CONNECTION(o->userdata);
+ pa_assert(c);
+
return pa_bytes_to_usec(pa_memblockq_get_length(c->output_memblockq), &c->source_output->sample_spec);
}
/*** socket server callback ***/
static void auth_timeout(pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata) {
- struct connection *c = userdata;
- assert(m && tv && c && c->auth_timeout_event == e);
+ connection *c = CONNECTION(userdata);
+
+ pa_assert(m);
+ pa_assert(tv);
+ connection_assert_ref(c);
+ pa_assert(c->auth_timeout_event == e);
if (!c->authorized)
- connection_free(c);
+ connection_unlink(c);
}
static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) {
- struct connection *c;
+ connection *c;
pa_protocol_esound *p = userdata;
char cname[256], pname[128];
- assert(s && io && p);
+
+ pa_assert(s);
+ pa_assert(io);
+ pa_assert(p);
if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {
pa_log("Warning! Too many connections (%u), dropping incoming connection.", MAX_CONNECTIONS);
pa_iochannel_free(io);
return;
}
-
- c = pa_xnew(struct connection, 1);
+
+ c = pa_msgobject_new(connection);
+ c->parent.parent.free = connection_free;
+ c->parent.process_msg = connection_process_msg;
c->protocol = p;
c->io = io;
pa_iochannel_set_callback(c->io, io_callback, c);
pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname));
- snprintf(cname, sizeof(cname), "EsounD client (%s)", pname);
- assert(p->core);
+ pa_snprintf(cname, sizeof(cname), "EsounD client (%s)", pname);
c->client = pa_client_new(p->core, __FILE__, cname);
- assert(c->client);
- c->client->owner = p->module;
+ pa_proplist_sets(c->client->proplist, "esound-protocol.peer", pname);
+ c->client->module = p->module;
c->client->kill = client_kill_cb;
c->client->userdata = c;
-
+
c->authorized = !!p->public;
- c->swap_byte_order = 0;
- c->dead = 0;
+ c->swap_byte_order = FALSE;
+ c->dead = FALSE;
c->read_data_length = 0;
c->read_data = pa_xmalloc(c->read_data_alloc = proto_map[ESD_PROTO_CONNECT].data_length);
@@ -1221,17 +1415,17 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
c->playback.current_memblock = NULL;
c->playback.memblock_index = 0;
- c->playback.fragment_size = 0;
+ c->playback.underrun = TRUE;
+ pa_atomic_store(&c->playback.missing, 0);
- c->scache.memchunk.length = c->scache.memchunk.index = 0;
- c->scache.memchunk.memblock = NULL;
+ pa_memchunk_reset(&c->scache.memchunk);
c->scache.name = NULL;
c->original_name = NULL;
if (!c->authorized && p->auth_ip_acl && pa_ip_acl_check(p->auth_ip_acl, pa_iochannel_get_recv_fd(io)) > 0) {
pa_log_info("Client authenticated by IP ACL.");
- c->authorized = 1;
+ c->authorized = TRUE;
}
if (!c->authorized) {
@@ -1241,9 +1435,8 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
c->auth_timeout_event = p->core->mainloop->time_new(p->core->mainloop, &tv, auth_timeout, c);
} else
c->auth_timeout_event = NULL;
-
+
c->defer_event = p->core->mainloop->defer_new(p->core->mainloop, defer_callback, c);
- assert(c->defer_event);
p->core->mainloop->defer_enable(c->defer_event, 0);
pa_idxset_put(p->connections, c, &c->index);
@@ -1252,22 +1445,22 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
/*** entry points ***/
pa_protocol_esound* pa_protocol_esound_new(pa_core*core, pa_socket_server *server, pa_module *m, pa_modargs *ma) {
- pa_protocol_esound *p;
- int public = 0;
+ pa_protocol_esound *p = NULL;
+ pa_bool_t public = FALSE;
const char *acl;
-
- assert(core);
- assert(server);
- assert(m);
- assert(ma);
- p = pa_xnew(pa_protocol_esound, 1);
+ pa_assert(core);
+ pa_assert(server);
+ pa_assert(m);
+ pa_assert(ma);
if (pa_modargs_get_value_boolean(ma, "auth-anonymous", &public) < 0) {
pa_log("auth-anonymous= expects a boolean argument.");
goto fail;
}
+ p = pa_xnew(pa_protocol_esound, 1);
+
if (pa_authkey_load_auto(pa_modargs_get_value(ma, "cookie", DEFAULT_COOKIE_FILE), p->esd_key, sizeof(p->esd_key)) < 0)
goto fail;
@@ -1279,14 +1472,13 @@ pa_protocol_esound* pa_protocol_esound_new(pa_core*core, pa_socket_server *serve
}
} else
p->auth_ip_acl = NULL;
-
+
+ p->core = core;
p->module = m;
p->public = public;
- p->server = server;
+ p->server = pa_socket_server_ref(server);
pa_socket_server_set_callback(p->server, on_connection, p);
- p->core = core;
p->connections = pa_idxset_new(NULL, NULL);
- assert(p->connections);
p->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
p->source_name = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL));
@@ -1300,17 +1492,21 @@ fail:
}
void pa_protocol_esound_free(pa_protocol_esound *p) {
- struct connection *c;
- assert(p);
+ connection *c;
+ pa_assert(p);
while ((c = pa_idxset_first(p->connections, NULL)))
- connection_free(c);
-
+ connection_unlink(c);
pa_idxset_free(p->connections, NULL, NULL);
- pa_socket_server_unref(p->server);
+
+ if (p->server)
+ pa_socket_server_unref(p->server);
if (p->auth_ip_acl)
pa_ip_acl_free(p->auth_ip_acl);
+ pa_xfree(p->sink_name);
+ pa_xfree(p->source_name);
+
pa_xfree(p);
}
diff --git a/src/pulsecore/protocol-esound.h b/src/pulsecore/protocol-esound.h
index 79b5acf0..0c9447d3 100644
--- a/src/pulsecore/protocol-esound.h
+++ b/src/pulsecore/protocol-esound.h
@@ -1,21 +1,22 @@
#ifndef fooprotocolesoundhfoo
#define fooprotocolesoundhfoo
-/* $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
diff --git a/src/pulsecore/protocol-http.c b/src/pulsecore/protocol-http.c
index 3b1207f6..03990435 100644
--- a/src/pulsecore/protocol-http.c
+++ b/src/pulsecore/protocol-http.c
@@ -1,18 +1,18 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
+ Copyright 2005-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
@@ -23,7 +23,6 @@
#include <config.h>
#endif
-#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
@@ -32,6 +31,7 @@
#include <pulse/xmalloc.h>
#include <pulsecore/ioline.h>
+#include <pulsecore/macro.h>
#include <pulsecore/log.h>
#include <pulsecore/namereg.h>
#include <pulsecore/cli-text.h>
@@ -63,11 +63,12 @@ struct pa_protocol_http {
static void http_response(struct connection *c, int code, const char *msg, const char *mime) {
char s[256];
- assert(c);
- assert(msg);
- assert(mime);
- snprintf(s, sizeof(s),
+ pa_assert(c);
+ pa_assert(msg);
+ pa_assert(mime);
+
+ pa_snprintf(s, sizeof(s),
"HTTP/1.0 %i %s\n"
"Connection: close\n"
"Content-Type: %s\n"
@@ -81,14 +82,14 @@ static void http_response(struct connection *c, int code, const char *msg, const
static void http_message(struct connection *c, int code, const char *msg, const char *text) {
char s[256];
- assert(c);
+ pa_assert(c);
http_response(c, code, msg, "text/html");
if (!text)
text = msg;
- snprintf(s, sizeof(s),
+ pa_snprintf(s, sizeof(s),
"<?xml version=\"1.0\"?>\n"
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
"<html xmlns=\"http://www.w3.org/1999/xhtml\"><head><title>%s</title></head>\n"
@@ -101,21 +102,22 @@ static void http_message(struct connection *c, int code, const char *msg, const
static void connection_free(struct connection *c, int del) {
- assert(c);
+ pa_assert(c);
if (c->url)
pa_xfree(c->url);
if (del)
pa_idxset_remove_by_data(c->protocol->connections, c, NULL);
+
pa_ioline_unref(c->line);
pa_xfree(c);
}
static void line_callback(pa_ioline *line, const char *s, void *userdata) {
struct connection *c = userdata;
- assert(line);
- assert(c);
+ pa_assert(line);
+ pa_assert(c);
if (!s) {
/* EOF */
@@ -137,16 +139,16 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
}
case MIME_HEADER: {
-
+
/* Ignore MIME headers */
if (strcspn(s, " \r\n") != 0)
break;
-
+
/* We're done */
c->state = DATA;
pa_log_info("request for %s", c->url);
-
+
if (!strcmp(c->url, URL_ROOT)) {
char txt[256];
http_response(c, 200, "OK", "text/html");
@@ -164,22 +166,22 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
#define PRINTF_FIELD(a,b) pa_ioline_printf(c->line, "<tr><td><b>%s</b></td><td>%s</td></tr>\n",(a),(b))
PRINTF_FIELD("User Name:", pa_get_user_name(txt, sizeof(txt)));
- PRINTF_FIELD("Fully Qualified Domain Name:", pa_get_fqdn(txt, sizeof(txt)));
+ PRINTF_FIELD("Host name:", pa_get_host_name(txt, sizeof(txt)));
PRINTF_FIELD("Default Sample Specification:", pa_sample_spec_snprint(txt, sizeof(txt), &c->protocol->core->default_sample_spec));
PRINTF_FIELD("Default Sink:", pa_namereg_get_default_sink_name(c->protocol->core));
PRINTF_FIELD("Default Source:", pa_namereg_get_default_source_name(c->protocol->core));
-
+
pa_ioline_puts(c->line, "</table>");
pa_ioline_puts(c->line, "<p><a href=\"/status\">Click here</a> for an extensive server status report.</p>");
-
+
pa_ioline_puts(c->line, "</body></html>\n");
-
- pa_ioline_defer_close(c->line);
+
+ pa_ioline_defer_close(c->line);
} else if (!strcmp(c->url, URL_CSS)) {
http_response(c, 200, "OK", "text/css");
- pa_ioline_puts(c->line,
+ pa_ioline_puts(c->line,
"body { color: black; background-color: white; margin: 0.5cm; }\n"
"a:link, a:visited { color: #900000; }\n"
"p { margin-left: 0.5cm; margin-right: 0.5cm; }\n"
@@ -207,13 +209,13 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
break;
}
-
+
default:
;
}
return;
-
+
fail:
internal_server_error(c);
}
@@ -221,7 +223,10 @@ fail:
static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) {
pa_protocol_http *p = userdata;
struct connection *c;
- assert(s && io && p);
+
+ pa_assert(s);
+ pa_assert(io);
+ pa_assert(p);
if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {
pa_log_warn("Warning! Too many connections (%u), dropping incoming connection.", MAX_CONNECTIONS);
@@ -229,7 +234,7 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
return;
}
- c = pa_xmalloc(sizeof(struct connection));
+ c = pa_xnew(struct connection, 1);
c->protocol = p;
c->line = pa_ioline_new(io);
c->state = REQUEST_LINE;
@@ -241,12 +246,14 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
pa_protocol_http* pa_protocol_http_new(pa_core *core, pa_socket_server *server, pa_module *m, PA_GCC_UNUSED pa_modargs *ma) {
pa_protocol_http* p;
- assert(core && server);
- p = pa_xmalloc(sizeof(pa_protocol_http));
+ pa_core_assert_ref(core);
+ pa_assert(server);
+
+ p = pa_xnew(pa_protocol_http, 1);
p->module = m;
p->core = core;
- p->server = server;
+ p->server = pa_socket_server_ref(server);
p->connections = pa_idxset_new(NULL, NULL);
pa_socket_server_set_callback(p->server, on_connection, p);
@@ -255,12 +262,12 @@ pa_protocol_http* pa_protocol_http_new(pa_core *core, pa_socket_server *server,
}
static void free_connection(void *p, PA_GCC_UNUSED void *userdata) {
- assert(p);
+ pa_assert(p);
connection_free(p, 0);
}
void pa_protocol_http_free(pa_protocol_http *p) {
- assert(p);
+ pa_assert(p);
pa_idxset_free(p->connections, free_connection, NULL);
pa_socket_server_unref(p->server);
diff --git a/src/pulsecore/protocol-http.h b/src/pulsecore/protocol-http.h
index 5d5dba31..e3372335 100644
--- a/src/pulsecore/protocol-http.h
+++ b/src/pulsecore/protocol-http.h
@@ -1,21 +1,21 @@
#ifndef fooprotocolhttphfoo
#define fooprotocolhttphfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
+ Copyright 2005-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
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index fba611d7..923a5665 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -1,18 +1,19 @@
-/* $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
@@ -25,7 +26,6 @@
#include <string.h>
#include <stdio.h>
-#include <assert.h>
#include <stdlib.h>
#include <unistd.h>
@@ -58,6 +58,7 @@
#include <pulsecore/creds.h>
#include <pulsecore/core-util.h>
#include <pulsecore/ipacl.h>
+#include <pulsecore/thread-mq.h>
#include "protocol-native.h"
@@ -68,56 +69,69 @@
#define MAX_CONNECTIONS 64
#define MAX_MEMBLOCKQ_LENGTH (4*1024*1024) /* 4MB */
+#define DEFAULT_TLENGTH_MSEC 2000 /* 2s */
+#define DEFAULT_PROCESS_MSEC 20 /* 20ms */
+#define DEFAULT_FRAGSIZE_MSEC DEFAULT_TLENGTH_MSEC
-struct connection;
+typedef struct connection connection;
struct pa_protocol_native;
-struct record_stream {
- struct connection *connection;
+typedef struct record_stream {
+ pa_msgobject parent;
+
+ connection *connection;
uint32_t index;
+
pa_source_output *source_output;
pa_memblockq *memblockq;
size_t fragment_size;
-};
+ pa_usec_t source_latency;
+} record_stream;
+
+typedef struct output_stream {
+ pa_msgobject parent;
+} output_stream;
-struct playback_stream {
- int type;
- struct connection *connection;
+typedef struct playback_stream {
+ output_stream parent;
+
+ connection *connection;
uint32_t index;
+
pa_sink_input *sink_input;
pa_memblockq *memblockq;
- size_t requested_bytes;
- int drain_request;
+ pa_bool_t drain_request;
uint32_t drain_tag;
uint32_t syncid;
- int underrun;
- /* Sync group members */
- PA_LLIST_FIELDS(struct playback_stream);
-};
+ pa_atomic_t missing;
+ size_t minreq;
+ pa_usec_t sink_latency;
+
+ /* Only updated after SINK_INPUT_MESSAGE_UPDATE_LATENCY */
+ int64_t read_index, write_index;
+ size_t render_memblockq_length;
+} playback_stream;
-struct upload_stream {
- int type;
- struct connection *connection;
+typedef struct upload_stream {
+ output_stream parent;
+
+ connection *connection;
uint32_t index;
+
pa_memchunk memchunk;
size_t length;
char *name;
pa_sample_spec sample_spec;
pa_channel_map channel_map;
-};
-
-struct output_stream {
- int type;
-};
-
-enum {
- UPLOAD_STREAM,
- PLAYBACK_STREAM
-};
+ pa_proplist *proplist;
+} upload_stream;
struct connection {
- int authorized;
+ pa_msgobject parent;
+
+ pa_bool_t authorized:1;
+ pa_bool_t is_local:1;
uint32_t version;
pa_protocol_native *protocol;
pa_client *client;
@@ -129,31 +143,86 @@ struct connection {
pa_time_event *auth_timeout_event;
};
+PA_DECLARE_CLASS(record_stream);
+#define RECORD_STREAM(o) (record_stream_cast(o))
+static PA_DEFINE_CHECK_TYPE(record_stream, pa_msgobject);
+
+PA_DECLARE_CLASS(output_stream);
+#define OUTPUT_STREAM(o) (output_stream_cast(o))
+static PA_DEFINE_CHECK_TYPE(output_stream, pa_msgobject);
+
+PA_DECLARE_CLASS(playback_stream);
+#define PLAYBACK_STREAM(o) (playback_stream_cast(o))
+static PA_DEFINE_CHECK_TYPE(playback_stream, output_stream);
+
+PA_DECLARE_CLASS(upload_stream);
+#define UPLOAD_STREAM(o) (upload_stream_cast(o))
+static PA_DEFINE_CHECK_TYPE(upload_stream, output_stream);
+
+PA_DECLARE_CLASS(connection);
+#define CONNECTION(o) (connection_cast(o))
+static PA_DEFINE_CHECK_TYPE(connection, pa_msgobject);
+
struct pa_protocol_native {
pa_module *module;
- int public;
pa_core *core;
+ pa_bool_t public;
pa_socket_server *server;
pa_idxset *connections;
uint8_t auth_cookie[PA_NATIVE_COOKIE_LENGTH];
- int auth_cookie_in_property;
+ pa_bool_t auth_cookie_in_property;
#ifdef HAVE_CREDS
char *auth_group;
#endif
pa_ip_acl *auth_ip_acl;
};
-static int sink_input_peek_cb(pa_sink_input *i, pa_memchunk *chunk);
-static void sink_input_drop_cb(pa_sink_input *i, const pa_memchunk *chunk, size_t length);
+enum {
+ SINK_INPUT_MESSAGE_POST_DATA = PA_SINK_INPUT_MESSAGE_MAX, /* data from main loop to sink input */
+ SINK_INPUT_MESSAGE_DRAIN, /* disabled prebuf, get playback started. */
+ SINK_INPUT_MESSAGE_FLUSH,
+ SINK_INPUT_MESSAGE_TRIGGER,
+ SINK_INPUT_MESSAGE_SEEK,
+ SINK_INPUT_MESSAGE_PREBUF_FORCE,
+ SINK_INPUT_MESSAGE_UPDATE_LATENCY
+};
+
+enum {
+ PLAYBACK_STREAM_MESSAGE_REQUEST_DATA, /* data requested from sink input from the main loop */
+ PLAYBACK_STREAM_MESSAGE_UNDERFLOW,
+ PLAYBACK_STREAM_MESSAGE_OVERFLOW,
+ PLAYBACK_STREAM_MESSAGE_DRAIN_ACK,
+ PLAYBACK_STREAM_MESSAGE_STARTED
+};
+
+enum {
+ RECORD_STREAM_MESSAGE_POST_DATA /* data from source output to main loop */
+};
+
+enum {
+ CONNECTION_MESSAGE_RELEASE,
+ CONNECTION_MESSAGE_REVOKE
+};
+
+static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk);
static void sink_input_kill_cb(pa_sink_input *i);
-static pa_usec_t sink_input_get_latency_cb(pa_sink_input *i);
+static void sink_input_suspend_cb(pa_sink_input *i, pa_bool_t suspend);
+static void sink_input_moved_cb(pa_sink_input *i);
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes);
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes);
+
+static void send_memblock(connection *c);
static void request_bytes(struct playback_stream*s);
static void source_output_kill_cb(pa_source_output *o);
static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk);
+static void source_output_suspend_cb(pa_source_output *o, pa_bool_t suspend);
+static void source_output_moved_cb(pa_source_output *o);
static pa_usec_t source_output_get_latency_cb(pa_source_output *o);
+static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
+
static void command_exit(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static void command_drain_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
@@ -176,8 +245,7 @@ static void command_subscribe(pa_pdispatch *pd, uint32_t command, uint32_t tag,
static void command_set_volume(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static void command_set_mute(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static void command_cork_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
-static void command_flush_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
-static void command_trigger_or_prebuf_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_trigger_or_flush_or_prebuf_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static void command_set_default_sink_or_source(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static void command_set_stream_name(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static void command_kill(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
@@ -190,6 +258,11 @@ static void command_get_autoload_info_list(pa_pdispatch *pd, uint32_t command, u
static void command_cork_record_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static void command_flush_record_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_update_stream_sample_rate(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_update_proplist(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_remove_proplist(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
[PA_COMMAND_ERROR] = NULL,
@@ -234,21 +307,25 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
[PA_COMMAND_SET_SINK_VOLUME] = command_set_volume,
[PA_COMMAND_SET_SINK_INPUT_VOLUME] = command_set_volume,
[PA_COMMAND_SET_SOURCE_VOLUME] = command_set_volume,
-
+
[PA_COMMAND_SET_SINK_MUTE] = command_set_mute,
+ [PA_COMMAND_SET_SINK_INPUT_MUTE] = command_set_mute,
[PA_COMMAND_SET_SOURCE_MUTE] = command_set_mute,
+ [PA_COMMAND_SUSPEND_SINK] = command_suspend,
+ [PA_COMMAND_SUSPEND_SOURCE] = command_suspend,
+
[PA_COMMAND_CORK_PLAYBACK_STREAM] = command_cork_playback_stream,
- [PA_COMMAND_FLUSH_PLAYBACK_STREAM] = command_flush_playback_stream,
- [PA_COMMAND_TRIGGER_PLAYBACK_STREAM] = command_trigger_or_prebuf_playback_stream,
- [PA_COMMAND_PREBUF_PLAYBACK_STREAM] = command_trigger_or_prebuf_playback_stream,
-
+ [PA_COMMAND_FLUSH_PLAYBACK_STREAM] = command_trigger_or_flush_or_prebuf_playback_stream,
+ [PA_COMMAND_TRIGGER_PLAYBACK_STREAM] = command_trigger_or_flush_or_prebuf_playback_stream,
+ [PA_COMMAND_PREBUF_PLAYBACK_STREAM] = command_trigger_or_flush_or_prebuf_playback_stream,
+
[PA_COMMAND_CORK_RECORD_STREAM] = command_cork_record_stream,
[PA_COMMAND_FLUSH_RECORD_STREAM] = command_flush_record_stream,
-
+
[PA_COMMAND_SET_DEFAULT_SINK] = command_set_default_sink_or_source,
[PA_COMMAND_SET_DEFAULT_SOURCE] = command_set_default_sink_or_source,
- [PA_COMMAND_SET_PLAYBACK_STREAM_NAME] = command_set_stream_name,
+ [PA_COMMAND_SET_PLAYBACK_STREAM_NAME] = command_set_stream_name,
[PA_COMMAND_SET_RECORD_STREAM_NAME] = command_set_stream_name,
[PA_COMMAND_KILL_CLIENT] = command_kill,
[PA_COMMAND_KILL_SINK_INPUT] = command_kill,
@@ -261,140 +338,535 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
[PA_COMMAND_REMOVE_AUTOLOAD] = command_remove_autoload,
[PA_COMMAND_MOVE_SINK_INPUT] = command_move_stream,
- [PA_COMMAND_MOVE_SOURCE_OUTPUT] = command_move_stream
+ [PA_COMMAND_MOVE_SOURCE_OUTPUT] = command_move_stream,
+
+ [PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR] = command_set_stream_buffer_attr,
+ [PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR] = command_set_stream_buffer_attr,
+
+ [PA_COMMAND_UPDATE_PLAYBACK_STREAM_SAMPLE_RATE] = command_update_stream_sample_rate,
+ [PA_COMMAND_UPDATE_RECORD_STREAM_SAMPLE_RATE] = command_update_stream_sample_rate,
+
+ [PA_COMMAND_UPDATE_RECORD_STREAM_PROPLIST] = command_update_proplist,
+ [PA_COMMAND_UPDATE_PLAYBACK_STREAM_PROPLIST] = command_update_proplist,
+ [PA_COMMAND_UPDATE_CLIENT_PROPLIST] = command_update_proplist,
+
+ [PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST] = command_remove_proplist,
+ [PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST] = command_remove_proplist,
+ [PA_COMMAND_REMOVE_CLIENT_PROPLIST] = command_remove_proplist,
};
/* structure management */
-static struct upload_stream* upload_stream_new(
- struct connection *c,
- const pa_sample_spec *ss,
- const pa_channel_map *map,
- const char *name, size_t length) {
-
- struct upload_stream *s;
- assert(c && ss && name && length);
-
- s = pa_xnew(struct upload_stream, 1);
- s->type = UPLOAD_STREAM;
+static void upload_stream_unlink(upload_stream *s) {
+ pa_assert(s);
+
+ if (!s->connection)
+ return;
+
+ pa_assert_se(pa_idxset_remove_by_data(s->connection->output_streams, s, NULL) == s);
+ s->connection = NULL;
+ upload_stream_unref(s);
+}
+
+static void upload_stream_free(pa_object *o) {
+ upload_stream *s = UPLOAD_STREAM(o);
+ pa_assert(s);
+
+ upload_stream_unlink(s);
+
+ pa_xfree(s->name);
+
+ if (s->proplist)
+ pa_proplist_free(s->proplist);
+
+ if (s->memchunk.memblock)
+ pa_memblock_unref(s->memchunk.memblock);
+
+ pa_xfree(s);
+}
+
+static upload_stream* upload_stream_new(
+ connection *c,
+ const pa_sample_spec *ss,
+ const pa_channel_map *map,
+ const char *name,
+ size_t length,
+ pa_proplist *p) {
+
+ upload_stream *s;
+
+ pa_assert(c);
+ pa_assert(ss);
+ pa_assert(name);
+ pa_assert(length > 0);
+ pa_assert(p);
+
+ s = pa_msgobject_new(upload_stream);
+ s->parent.parent.parent.free = upload_stream_free;
s->connection = c;
s->sample_spec = *ss;
s->channel_map = *map;
s->name = pa_xstrdup(name);
-
- s->memchunk.memblock = NULL;
- s->memchunk.index = 0;
- s->memchunk.length = 0;
-
+ pa_memchunk_reset(&s->memchunk);
s->length = length;
-
+ s->proplist = pa_proplist_copy(p);
+ pa_proplist_update(s->proplist, PA_UPDATE_MERGE, c->client->proplist);
+
pa_idxset_put(c->output_streams, s, &s->index);
+
return s;
}
-static void upload_stream_free(struct upload_stream *o) {
- assert(o && o->connection);
+static void record_stream_unlink(record_stream *s) {
+ pa_assert(s);
+
+ if (!s->connection)
+ return;
+
+ if (s->source_output) {
+ pa_source_output_unlink(s->source_output);
+ pa_source_output_unref(s->source_output);
+ s->source_output = NULL;
+ }
+
+ pa_assert_se(pa_idxset_remove_by_data(s->connection->record_streams, s, NULL) == s);
+ s->connection = NULL;
+ record_stream_unref(s);
+}
+
+static void record_stream_free(pa_object *o) {
+ record_stream *s = RECORD_STREAM(o);
+ pa_assert(s);
+
+ record_stream_unlink(s);
+
+ pa_memblockq_free(s->memblockq);
+ pa_xfree(s);
+}
+
+static int record_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
+ record_stream *s = RECORD_STREAM(o);
+ record_stream_assert_ref(s);
+
+ if (!s->connection)
+ return -1;
+
+ switch (code) {
+
+ case RECORD_STREAM_MESSAGE_POST_DATA:
+
+ if (pa_memblockq_push_align(s->memblockq, chunk) < 0) {
+/* pa_log_warn("Failed to push data into output queue."); */
+ return -1;
+ }
+
+ if (!pa_pstream_is_pending(s->connection->pstream))
+ send_memblock(s->connection);
+
+ break;
+ }
+
+ return 0;
+}
+
+static void fix_record_buffer_attr_pre(record_stream *s, pa_bool_t adjust_latency, uint32_t *maxlength, uint32_t *fragsize) {
+ pa_assert(s);
+ pa_assert(maxlength);
+ pa_assert(fragsize);
+
+ if (*maxlength <= 0 || *maxlength > MAX_MEMBLOCKQ_LENGTH)
+ *maxlength = MAX_MEMBLOCKQ_LENGTH;
+
+ if (*fragsize <= 0)
+ *fragsize = pa_usec_to_bytes(DEFAULT_FRAGSIZE_MSEC*PA_USEC_PER_MSEC, &s->source_output->sample_spec);
+
+ if (adjust_latency) {
+ pa_usec_t fragsize_usec;
+
+ /* So, the user asked us to adjust the latency according to
+ * what the source can provide. Half the latency will be
+ * spent on the hw buffer, half of it in the async buffer
+ * queue we maintain for each client. */
+
+ fragsize_usec = pa_bytes_to_usec(*fragsize, &s->source_output->sample_spec);
+
+ s->source_latency = pa_source_output_set_requested_latency(s->source_output, fragsize_usec/2);
+
+ if (fragsize_usec >= s->source_latency*2)
+ fragsize_usec -= s->source_latency;
+ else
+ fragsize_usec = s->source_latency;
+
+ *fragsize = pa_usec_to_bytes(fragsize_usec, &s->source_output->sample_spec);
+ } else
+ s->source_latency = 0;
+}
+
+static void fix_record_buffer_attr_post(record_stream *s, uint32_t *maxlength, uint32_t *fragsize) {
+ size_t base;
+
+ pa_assert(s);
+ pa_assert(maxlength);
+ pa_assert(fragsize);
+
+ *maxlength = pa_memblockq_get_maxlength(s->memblockq);
+
+ base = pa_frame_size(&s->source_output->sample_spec);
+
+ s->fragment_size = (*fragsize/base)*base;
+ if (s->fragment_size <= 0)
+ s->fragment_size = base;
- pa_idxset_remove_by_data(o->connection->output_streams, o, NULL);
+ if (s->fragment_size > *maxlength)
+ s->fragment_size = *maxlength;
- pa_xfree(o->name);
-
- if (o->memchunk.memblock)
- pa_memblock_unref(o->memchunk.memblock);
-
- pa_xfree(o);
+ *fragsize = s->fragment_size;
}
-static struct record_stream* record_stream_new(
- struct connection *c,
- pa_source *source,
- const pa_sample_spec *ss,
- const pa_channel_map *map,
- const char *name,
- size_t maxlength,
- size_t fragment_size) {
-
- struct record_stream *s;
+static record_stream* record_stream_new(
+ connection *c,
+ pa_source *source,
+ pa_sample_spec *ss,
+ pa_channel_map *map,
+ pa_bool_t peak_detect,
+ uint32_t *maxlength,
+ uint32_t *fragsize,
+ pa_source_output_flags_t flags,
+ pa_proplist *p,
+ pa_bool_t adjust_latency,
+ pa_sink_input *direct_on_input) {
+
+ record_stream *s;
pa_source_output *source_output;
size_t base;
pa_source_output_new_data data;
-
- assert(c && ss && name && maxlength);
+
+ pa_assert(c);
+ pa_assert(ss);
+ pa_assert(maxlength);
+ pa_assert(p);
pa_source_output_new_data_init(&data);
- data.source = source;
+
+ pa_proplist_update(data.proplist, PA_UPDATE_REPLACE, p);
+ pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist);
data.driver = __FILE__;
- data.name = name;
- pa_source_output_new_data_set_sample_spec(&data, ss);
- pa_source_output_new_data_set_channel_map(&data, map);
data.module = c->protocol->module;
data.client = c->client;
-
- if (!(source_output = pa_source_output_new(c->protocol->core, &data, 0)))
+ data.source = source;
+ data.direct_on_input = direct_on_input;
+ pa_source_output_new_data_set_sample_spec(&data, ss);
+ pa_source_output_new_data_set_channel_map(&data, map);
+ if (peak_detect)
+ data.resample_method = PA_RESAMPLER_PEAKS;
+
+ source_output = pa_source_output_new(c->protocol->core, &data, flags);
+
+ pa_source_output_new_data_done(&data);
+
+ if (!source_output)
return NULL;
- s = pa_xnew(struct record_stream, 1);
+ s = pa_msgobject_new(record_stream);
+ s->parent.parent.free = record_stream_free;
+ s->parent.process_msg = record_stream_process_msg;
s->connection = c;
s->source_output = source_output;
+
s->source_output->push = source_output_push_cb;
s->source_output->kill = source_output_kill_cb;
s->source_output->get_latency = source_output_get_latency_cb;
+ s->source_output->moved = source_output_moved_cb;
+ s->source_output->suspend = source_output_suspend_cb;
s->source_output->userdata = s;
+ fix_record_buffer_attr_pre(s, adjust_latency, maxlength, fragsize);
+
s->memblockq = pa_memblockq_new(
0,
- maxlength,
+ *maxlength,
0,
- base = pa_frame_size(ss),
+ base = pa_frame_size(&source_output->sample_spec),
1,
0,
+ 0,
NULL);
- assert(s->memblockq);
- s->fragment_size = (fragment_size/base)*base;
- if (!s->fragment_size)
- s->fragment_size = base;
+ fix_record_buffer_attr_post(s, maxlength, fragsize);
+
+ *ss = s->source_output->sample_spec;
+ *map = s->source_output->channel_map;
pa_idxset_put(c->record_streams, s, &s->index);
+
+ pa_log_info("Final latency %0.2f ms = %0.2f ms + %0.2f ms",
+ ((double) pa_bytes_to_usec(s->fragment_size, &source_output->sample_spec) + (double) s->source_latency) / PA_USEC_PER_MSEC,
+ (double) pa_bytes_to_usec(s->fragment_size, &source_output->sample_spec) / PA_USEC_PER_MSEC,
+ (double) s->source_latency / PA_USEC_PER_MSEC);
+
+ pa_source_output_put(s->source_output);
return s;
}
-static void record_stream_free(struct record_stream* r) {
- assert(r && r->connection);
+static void playback_stream_unlink(playback_stream *s) {
+ pa_assert(s);
+
+ if (!s->connection)
+ return;
- pa_idxset_remove_by_data(r->connection->record_streams, r, NULL);
- pa_source_output_disconnect(r->source_output);
- pa_source_output_unref(r->source_output);
- pa_memblockq_free(r->memblockq);
- pa_xfree(r);
+ if (s->sink_input) {
+ pa_sink_input_unlink(s->sink_input);
+ pa_sink_input_unref(s->sink_input);
+ s->sink_input = NULL;
+ }
+
+ if (s->drain_request)
+ pa_pstream_send_error(s->connection->pstream, s->drain_tag, PA_ERR_NOENTITY);
+
+ pa_assert_se(pa_idxset_remove_by_data(s->connection->output_streams, s, NULL) == s);
+ s->connection = NULL;
+ playback_stream_unref(s);
}
-static struct playback_stream* playback_stream_new(
- struct connection *c,
+static void playback_stream_free(pa_object* o) {
+ playback_stream *s = PLAYBACK_STREAM(o);
+ pa_assert(s);
+
+ playback_stream_unlink(s);
+
+ pa_memblockq_free(s->memblockq);
+ pa_xfree(s);
+}
+
+static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
+ playback_stream *s = PLAYBACK_STREAM(o);
+ playback_stream_assert_ref(s);
+
+ if (!s->connection)
+ return -1;
+
+ switch (code) {
+ case PLAYBACK_STREAM_MESSAGE_REQUEST_DATA: {
+ pa_tagstruct *t;
+ uint32_t l = 0;
+
+ for (;;) {
+ if ((l = pa_atomic_load(&s->missing)) <= 0)
+ break;
+
+ if (pa_atomic_cmpxchg(&s->missing, l, 0))
+ break;
+ }
+
+ if (l <= 0)
+ break;
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_REQUEST);
+ pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+ pa_tagstruct_putu32(t, s->index);
+ pa_tagstruct_putu32(t, l);
+ pa_pstream_send_tagstruct(s->connection->pstream, t);
+
+/* pa_log("Requesting %lu bytes", (unsigned long) l); */
+ break;
+ }
+
+ case PLAYBACK_STREAM_MESSAGE_UNDERFLOW: {
+ pa_tagstruct *t;
+
+ /* Report that we're empty */
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_UNDERFLOW);
+ pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+ pa_tagstruct_putu32(t, s->index);
+ pa_pstream_send_tagstruct(s->connection->pstream, t);
+ break;
+ }
+
+ case PLAYBACK_STREAM_MESSAGE_OVERFLOW: {
+ pa_tagstruct *t;
+
+ /* Notify the user we're overflowed*/
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_OVERFLOW);
+ pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+ pa_tagstruct_putu32(t, s->index);
+ pa_pstream_send_tagstruct(s->connection->pstream, t);
+ break;
+ }
+
+ case PLAYBACK_STREAM_MESSAGE_STARTED:
+
+ if (s->connection->version >= 13) {
+ pa_tagstruct *t;
+
+ /* Notify the user we're overflowed*/
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_STARTED);
+ pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+ pa_tagstruct_putu32(t, s->index);
+ pa_pstream_send_tagstruct(s->connection->pstream, t);
+ }
+
+ break;
+
+ case PLAYBACK_STREAM_MESSAGE_DRAIN_ACK:
+ pa_pstream_send_simple_ack(s->connection->pstream, PA_PTR_TO_UINT(userdata));
+ break;
+ }
+
+ return 0;
+}
+
+static void fix_playback_buffer_attr_pre(playback_stream *s, pa_bool_t adjust_latency, uint32_t *maxlength, uint32_t *tlength, uint32_t* prebuf, uint32_t* minreq) {
+ size_t frame_size;
+ pa_usec_t tlength_usec, minreq_usec, sink_usec;
+
+ pa_assert(s);
+ pa_assert(maxlength);
+ pa_assert(tlength);
+ pa_assert(prebuf);
+ pa_assert(minreq);
+
+ if (*maxlength <= 0 || *maxlength > MAX_MEMBLOCKQ_LENGTH)
+ *maxlength = MAX_MEMBLOCKQ_LENGTH;
+ if (*tlength <= 0)
+ *tlength = pa_usec_to_bytes(DEFAULT_TLENGTH_MSEC*PA_USEC_PER_MSEC, &s->sink_input->sample_spec);
+ if (*minreq <= 0)
+ *minreq = pa_usec_to_bytes(DEFAULT_PROCESS_MSEC*PA_USEC_PER_MSEC, &s->sink_input->sample_spec);
+
+ frame_size = pa_frame_size(&s->sink_input->sample_spec);
+ if (*minreq <= 0)
+ *minreq = frame_size;
+ if (*tlength < *minreq+frame_size)
+ *tlength = *minreq+frame_size;
+
+ tlength_usec = pa_bytes_to_usec(*tlength, &s->sink_input->sample_spec);
+ minreq_usec = pa_bytes_to_usec(*minreq, &s->sink_input->sample_spec);
+
+ pa_log_info("Requested tlength=%0.2f ms, minreq=%0.2f ms",
+ (double) tlength_usec / PA_USEC_PER_MSEC,
+ (double) minreq_usec / PA_USEC_PER_MSEC);
+
+ if (adjust_latency) {
+
+ /* So, the user asked us to adjust the latency of the stream
+ * buffer according to the what the sink can provide. The
+ * tlength passed in shall be the overall latency. Roughly
+ * half the latency will be spent on the hw buffer, the other
+ * half of it in the async buffer queue we maintain for each
+ * client. In between we'll have a safety space of size
+ * 2*minreq. Why the 2*minreq? When the hw buffer is completey
+ * empty and needs to be filled, then our buffer must have
+ * enough data to fulfill this request immediatly and thus
+ * have at least the same tlength as the size of the hw
+ * buffer. It additionally needs space for 2 times minreq
+ * because if the buffer ran empty and a partial fillup
+ * happens immediately on the next iteration we need to be
+ * able to fulfill it and give the application also minreq
+ * time to fill it up again for the next request Makes 2 times
+ * minreq in plus.. */
+
+ if (tlength_usec > minreq_usec*2)
+ sink_usec = (tlength_usec - minreq_usec*2)/2;
+ else
+ sink_usec = 0;
+
+ } else {
+
+ /* Ok, the user didn't ask us to adjust the latency, but we
+ * still need to make sure that the parameters from the user
+ * do make sense. */
+
+ if (tlength_usec > minreq_usec*2)
+ sink_usec = (tlength_usec - minreq_usec*2);
+ else
+ sink_usec = 0;
+ }
+
+ s->sink_latency = pa_sink_input_set_requested_latency(s->sink_input, sink_usec);
+
+ if (adjust_latency) {
+ /* Ok, we didn't necessarily get what we were asking for, so
+ * let's subtract from what we asked for for the remaining
+ * buffer space */
+
+ if (tlength_usec >= s->sink_latency)
+ tlength_usec -= s->sink_latency;
+ }
+
+ if (tlength_usec < s->sink_latency + 2*minreq_usec)
+ tlength_usec = s->sink_latency + 2*minreq_usec;
+
+ *tlength = pa_usec_to_bytes(tlength_usec, &s->sink_input->sample_spec);
+ *minreq = pa_usec_to_bytes(minreq_usec, &s->sink_input->sample_spec);
+
+ if (*minreq <= 0) {
+ *minreq += frame_size;
+ *tlength += frame_size*2;
+ }
+
+ if (*tlength <= *minreq)
+ *tlength = *minreq*2 + frame_size;
+
+ if (*prebuf <= 0 || *prebuf > *tlength)
+ *prebuf = *tlength;
+}
+
+static void fix_playback_buffer_attr_post(playback_stream *s, uint32_t *maxlength, uint32_t *tlength, uint32_t* prebuf, uint32_t* minreq) {
+ pa_assert(s);
+ pa_assert(maxlength);
+ pa_assert(tlength);
+ pa_assert(prebuf);
+ pa_assert(minreq);
+
+ *maxlength = (uint32_t) pa_memblockq_get_maxlength(s->memblockq);
+ *tlength = (uint32_t) pa_memblockq_get_tlength(s->memblockq);
+ *prebuf = (uint32_t) pa_memblockq_get_prebuf(s->memblockq);
+ *minreq = (uint32_t) pa_memblockq_get_minreq(s->memblockq);
+
+ s->minreq = *minreq;
+}
+
+static playback_stream* playback_stream_new(
+ connection *c,
pa_sink *sink,
- const pa_sample_spec *ss,
- const pa_channel_map *map,
- const char *name,
- size_t maxlength,
- size_t tlength,
- size_t prebuf,
- size_t minreq,
+ pa_sample_spec *ss,
+ pa_channel_map *map,
+ uint32_t *maxlength,
+ uint32_t *tlength,
+ uint32_t *prebuf,
+ uint32_t *minreq,
pa_cvolume *volume,
- uint32_t syncid) {
-
- struct playback_stream *s, *ssync;
+ pa_bool_t muted,
+ uint32_t syncid,
+ uint32_t *missing,
+ pa_sink_input_flags_t flags,
+ pa_proplist *p,
+ pa_bool_t adjust_latency) {
+
+ playback_stream *s, *ssync;
pa_sink_input *sink_input;
- pa_memblock *silence;
+ pa_memchunk silence;
uint32_t idx;
int64_t start_index;
pa_sink_input_new_data data;
-
- assert(c && ss && name && maxlength);
+
+ pa_assert(c);
+ pa_assert(ss);
+ pa_assert(maxlength);
+ pa_assert(tlength);
+ pa_assert(prebuf);
+ pa_assert(minreq);
+ pa_assert(volume);
+ pa_assert(missing);
+ pa_assert(p);
/* Find syncid group */
for (ssync = pa_idxset_first(c->output_streams, &idx); ssync; ssync = pa_idxset_next(c->output_streams, &idx)) {
-
- if (ssync->type != PLAYBACK_STREAM)
+
+ if (!playback_stream_isinstance(ssync))
continue;
if (ssync->syncid == syncid)
@@ -402,158 +874,190 @@ static struct playback_stream* playback_stream_new(
}
/* Synced streams must connect to the same sink */
- if (ssync)
- sink = ssync->sink_input->sink;
+ if (ssync) {
+
+ if (!sink)
+ sink = ssync->sink_input->sink;
+ else if (sink != ssync->sink_input->sink)
+ return NULL;
+ }
pa_sink_input_new_data_init(&data);
- data.sink = sink;
+
+ pa_proplist_update(data.proplist, PA_UPDATE_REPLACE, p);
+ pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist);
data.driver = __FILE__;
- data.name = name;
+ data.module = c->protocol->module;
+ data.client = c->client;
+ data.sink = sink;
pa_sink_input_new_data_set_sample_spec(&data, ss);
pa_sink_input_new_data_set_channel_map(&data, map);
pa_sink_input_new_data_set_volume(&data, volume);
- data.module = c->protocol->module;
- data.client = c->client;
+ pa_sink_input_new_data_set_muted(&data, muted);
+ data.sync_base = ssync ? ssync->sink_input : NULL;
- if (!(sink_input = pa_sink_input_new(c->protocol->core, &data, 0)))
+ sink_input = pa_sink_input_new(c->protocol->core, &data, flags);
+
+ pa_sink_input_new_data_done(&data);
+
+ if (!sink_input)
return NULL;
-
- s = pa_xnew(struct playback_stream, 1);
- s->type = PLAYBACK_STREAM;
+
+ s = pa_msgobject_new(playback_stream);
+ s->parent.parent.parent.free = playback_stream_free;
+ s->parent.parent.process_msg = playback_stream_process_msg;
s->connection = c;
s->syncid = syncid;
s->sink_input = sink_input;
- s->underrun = 1;
-
- s->sink_input->peek = sink_input_peek_cb;
- s->sink_input->drop = sink_input_drop_cb;
+
+ s->sink_input->parent.process_msg = sink_input_process_msg;
+ s->sink_input->pop = sink_input_pop_cb;
+ s->sink_input->process_rewind = sink_input_process_rewind_cb;
+ s->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
s->sink_input->kill = sink_input_kill_cb;
- s->sink_input->get_latency = sink_input_get_latency_cb;
+ s->sink_input->moved = sink_input_moved_cb;
+ s->sink_input->suspend = sink_input_suspend_cb;
s->sink_input->userdata = s;
- if (ssync) {
- /* Sync id found, now find head of list */
- PA_LLIST_FIND_HEAD(struct playback_stream, ssync, &ssync);
+ start_index = ssync ? pa_memblockq_get_read_index(ssync->memblockq) : 0;
- /* Prepend ourselves */
- PA_LLIST_PREPEND(struct playback_stream, ssync, s);
+ fix_playback_buffer_attr_pre(s, adjust_latency, maxlength, tlength, prebuf, minreq);
+ pa_sink_input_get_silence(sink_input, &silence);
- /* Set our start index to the current read index of the other grozp member(s) */
- assert(ssync->next);
- start_index = pa_memblockq_get_read_index(ssync->next->memblockq);
- } else {
- /* This ia a new sync group */
- PA_LLIST_INIT(struct playback_stream, s);
- start_index = 0;
- }
-
- silence = pa_silence_memblock_new(c->protocol->core->mempool, ss, 0);
-
s->memblockq = pa_memblockq_new(
start_index,
- maxlength,
- tlength,
- pa_frame_size(ss),
- prebuf,
- minreq,
- silence);
-
- pa_memblock_unref(silence);
-
- s->requested_bytes = 0;
- s->drain_request = 0;
-
+ *maxlength,
+ *tlength,
+ pa_frame_size(&sink_input->sample_spec),
+ *prebuf,
+ *minreq,
+ 0,
+ &silence);
+
+ pa_memblock_unref(silence.memblock);
+ fix_playback_buffer_attr_post(s, maxlength, tlength, prebuf, minreq);
+
+ *missing = (uint32_t) pa_memblockq_pop_missing(s->memblockq);
+
+ *ss = s->sink_input->sample_spec;
+ *map = s->sink_input->channel_map;
+
+ pa_atomic_store(&s->missing, 0);
+ s->drain_request = FALSE;
+
pa_idxset_put(c->output_streams, s, &s->index);
+ pa_log_info("Final latency %0.2f ms = %0.2f ms + 2*%0.2f ms + %0.2f ms",
+ ((double) pa_bytes_to_usec(*tlength, &sink_input->sample_spec) + (double) s->sink_latency) / PA_USEC_PER_MSEC,
+ (double) pa_bytes_to_usec(*tlength-*minreq*2, &sink_input->sample_spec) / PA_USEC_PER_MSEC,
+ (double) pa_bytes_to_usec(*minreq, &sink_input->sample_spec) / PA_USEC_PER_MSEC,
+ (double) s->sink_latency / PA_USEC_PER_MSEC);
+
+ pa_sink_input_put(s->sink_input);
return s;
}
-static void playback_stream_free(struct playback_stream* p) {
- struct playback_stream *head;
- assert(p && p->connection);
+static int connection_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
+ connection *c = CONNECTION(o);
+ connection_assert_ref(c);
- if (p->drain_request)
- pa_pstream_send_error(p->connection->pstream, p->drain_tag, PA_ERR_NOENTITY);
+ if (!c->protocol)
+ return -1;
- PA_LLIST_FIND_HEAD(struct playback_stream, p, &head);
- PA_LLIST_REMOVE(struct playback_stream, head, p);
+ switch (code) {
- pa_idxset_remove_by_data(p->connection->output_streams, p, NULL);
- pa_sink_input_disconnect(p->sink_input);
- pa_sink_input_unref(p->sink_input);
- pa_memblockq_free(p->memblockq);
- pa_xfree(p);
+ case CONNECTION_MESSAGE_REVOKE:
+ pa_pstream_send_revoke(c->pstream, PA_PTR_TO_UINT(userdata));
+ break;
+
+ case CONNECTION_MESSAGE_RELEASE:
+ pa_pstream_send_release(c->pstream, PA_PTR_TO_UINT(userdata));
+ break;
+ }
+
+ return 0;
}
-static void connection_free(struct connection *c) {
- struct record_stream *r;
- struct output_stream *o;
- assert(c && c->protocol);
+static void connection_unlink(connection *c) {
+ record_stream *r;
+ output_stream *o;
+
+ pa_assert(c);
+
+ if (!c->protocol)
+ return;
- pa_idxset_remove_by_data(c->protocol->connections, c, NULL);
while ((r = pa_idxset_first(c->record_streams, NULL)))
- record_stream_free(r);
- pa_idxset_free(c->record_streams, NULL, NULL);
+ record_stream_unlink(r);
while ((o = pa_idxset_first(c->output_streams, NULL)))
- if (o->type == PLAYBACK_STREAM)
- playback_stream_free((struct playback_stream*) o);
+ if (playback_stream_isinstance(o))
+ playback_stream_unlink(PLAYBACK_STREAM(o));
else
- upload_stream_free((struct upload_stream*) o);
+ upload_stream_unlink(UPLOAD_STREAM(o));
+
+ if (c->subscription)
+ pa_subscription_free(c->subscription);
+
+ if (c->pstream)
+ pa_pstream_unlink(c->pstream);
+
+ if (c->auth_timeout_event) {
+ c->protocol->core->mainloop->time_free(c->auth_timeout_event);
+ c->auth_timeout_event = NULL;
+ }
+
+ pa_assert_se(pa_idxset_remove_by_data(c->protocol->connections, c, NULL) == c);
+ c->protocol = NULL;
+ connection_unref(c);
+}
+
+static void connection_free(pa_object *o) {
+ connection *c = CONNECTION(o);
+
+ pa_assert(c);
+
+ connection_unlink(c);
+
+ pa_idxset_free(c->record_streams, NULL, NULL);
pa_idxset_free(c->output_streams, NULL, NULL);
pa_pdispatch_unref(c->pdispatch);
- pa_pstream_close(c->pstream);
pa_pstream_unref(c->pstream);
pa_client_free(c->client);
- if (c->subscription)
- pa_subscription_free(c->subscription);
-
- if (c->auth_timeout_event)
- c->protocol->core->mainloop->time_free(c->auth_timeout_event);
-
pa_xfree(c);
}
-static void request_bytes(struct playback_stream *s) {
- pa_tagstruct *t;
- size_t l;
- assert(s);
+/* Called from thread context */
+static void request_bytes(playback_stream *s) {
+ size_t m, previous_missing;
- if (!(l = pa_memblockq_missing(s->memblockq)))
- return;
-
- if (l <= s->requested_bytes)
- return;
+ playback_stream_assert_ref(s);
- l -= s->requested_bytes;
+ m = pa_memblockq_pop_missing(s->memblockq);
- if (l < pa_memblockq_get_minreq(s->memblockq))
+ if (m <= 0)
return;
-
- s->requested_bytes += l;
- t = pa_tagstruct_new(NULL, 0);
- assert(t);
- pa_tagstruct_putu32(t, PA_COMMAND_REQUEST);
- pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
- pa_tagstruct_putu32(t, s->index);
- pa_tagstruct_putu32(t, l);
- pa_pstream_send_tagstruct(s->connection->pstream, t);
+/* pa_log("request_bytes(%lu)", (unsigned long) m); */
+
+ previous_missing = pa_atomic_add(&s->missing, m);
-/* pa_log("Requesting %u bytes", l); */
+ if (pa_memblockq_prebuf_active(s->memblockq) ||
+ (previous_missing < s->minreq && previous_missing+m >= s->minreq))
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL);
}
-static void send_memblock(struct connection *c) {
+static void send_memblock(connection *c) {
uint32_t start;
- struct record_stream *r;
+ record_stream *r;
start = PA_IDXSET_INVALID;
for (;;) {
pa_memchunk chunk;
-
- if (!(r = pa_idxset_rrobin(c->record_streams, &c->rrobin_index)))
+
+ if (!(r = RECORD_STREAM(pa_idxset_rrobin(c->record_streams, &c->rrobin_index))))
return;
if (start == PA_IDXSET_INVALID)
@@ -563,22 +1067,23 @@ static void send_memblock(struct connection *c) {
if (pa_memblockq_peek(r->memblockq, &chunk) >= 0) {
pa_memchunk schunk = chunk;
-
+
if (schunk.length > r->fragment_size)
schunk.length = r->fragment_size;
pa_pstream_send_memblock(c->pstream, r->index, 0, PA_SEEK_RELATIVE, &schunk);
- pa_memblockq_drop(r->memblockq, &chunk, schunk.length);
+
+ pa_memblockq_drop(r->memblockq, schunk.length);
pa_memblock_unref(schunk.memblock);
-
+
return;
}
}
}
-static void send_playback_stream_killed(struct playback_stream *p) {
+static void send_playback_stream_killed(playback_stream *p) {
pa_tagstruct *t;
- assert(p);
+ playback_stream_assert_ref(p);
t = pa_tagstruct_new(NULL, 0);
pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_STREAM_KILLED);
@@ -587,9 +1092,9 @@ static void send_playback_stream_killed(struct playback_stream *p) {
pa_pstream_send_tagstruct(p->connection->pstream, t);
}
-static void send_record_stream_killed(struct record_stream *r) {
+static void send_record_stream_killed(record_stream *r) {
pa_tagstruct *t;
- assert(r);
+ record_stream_assert_ref(r);
t = pa_tagstruct_new(NULL, 0);
pa_tagstruct_putu32(t, PA_COMMAND_RECORD_STREAM_KILLED);
@@ -598,107 +1103,418 @@ static void send_record_stream_killed(struct record_stream *r) {
pa_pstream_send_tagstruct(r->connection->pstream, t);
}
-/*** sinkinput callbacks ***/
+/*** sink input callbacks ***/
+
+static void handle_seek(playback_stream *s, int64_t indexw) {
+ playback_stream_assert_ref(s);
+
+/* pa_log("handle_seek: %llu -- %i", (unsigned long long) s->sink_input->thread_info.underrun_for, pa_memblockq_is_readable(s->memblockq)); */
+
+ if (s->sink_input->thread_info.underrun_for > 0) {
+
+/* pa_log("%lu vs. %lu", (unsigned long) pa_memblockq_get_length(s->memblockq), (unsigned long) pa_memblockq_get_prebuf(s->memblockq)); */
-static int sink_input_peek_cb(pa_sink_input *i, pa_memchunk *chunk) {
- struct playback_stream *s;
- assert(i && i->userdata && chunk);
- s = i->userdata;
+ if (pa_memblockq_is_readable(s->memblockq)) {
- if (pa_memblockq_get_length(s->memblockq) <= 0 && !s->underrun) {
- pa_tagstruct *t;
+ /* We just ended an underrun, let's ask the sink
+ * for a complete rewind rewrite */
- /* Report that we're empty */
+ pa_log_debug("Requesting rewind due to end of underrun.");
+ pa_sink_input_request_rewind(s->sink_input,
+ s->sink_input->thread_info.underrun_for == (size_t) -1 ? 0 : s->sink_input->thread_info.underrun_for,
+ FALSE, TRUE);
+ }
+
+ } else {
+ int64_t indexr;
+
+ indexr = pa_memblockq_get_read_index(s->memblockq);
- t = pa_tagstruct_new(NULL, 0);
- pa_tagstruct_putu32(t, PA_COMMAND_UNDERFLOW);
- pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
- pa_tagstruct_putu32(t, s->index);
- pa_pstream_send_tagstruct(s->connection->pstream, t);
+ if (indexw < indexr) {
+ /* OK, the sink already asked for this data, so
+ * let's have it usk us again */
- s->underrun = 1;
+ pa_log_debug("Requesting rewind due to rewrite.");
+ pa_sink_input_request_rewind(s->sink_input, indexr - indexw, TRUE, FALSE);
+ }
}
-
+
+ request_bytes(s);
+}
+
+/* Called from thread context */
+static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
+ pa_sink_input *i = PA_SINK_INPUT(o);
+ playback_stream *s;
+
+ pa_sink_input_assert_ref(i);
+ s = PLAYBACK_STREAM(i->userdata);
+ playback_stream_assert_ref(s);
+
+ switch (code) {
+
+ case SINK_INPUT_MESSAGE_SEEK: {
+ int64_t windex;
+
+ windex = pa_memblockq_get_write_index(s->memblockq);
+ pa_memblockq_seek(s->memblockq, offset, PA_PTR_TO_UINT(userdata));
+
+ handle_seek(s, windex);
+ return 0;
+ }
+
+ case SINK_INPUT_MESSAGE_POST_DATA: {
+ int64_t windex;
+
+ pa_assert(chunk);
+
+ windex = pa_memblockq_get_write_index(s->memblockq);
+
+/* pa_log("sink input post: %lu %lli", (unsigned long) chunk->length, (long long) windex); */
+
+ if (pa_memblockq_push_align(s->memblockq, chunk) < 0) {
+ pa_log_warn("Failed to push data into queue");
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_OVERFLOW, NULL, 0, NULL, NULL);
+ pa_memblockq_seek(s->memblockq, chunk->length, PA_SEEK_RELATIVE);
+ }
+
+ handle_seek(s, windex);
+
+/* pa_log("sink input post2: %lu", (unsigned long) pa_memblockq_get_length(s->memblockq)); */
+
+ return 0;
+ }
+
+ case SINK_INPUT_MESSAGE_DRAIN:
+ case SINK_INPUT_MESSAGE_FLUSH:
+ case SINK_INPUT_MESSAGE_PREBUF_FORCE:
+ case SINK_INPUT_MESSAGE_TRIGGER: {
+
+ int64_t windex;
+ pa_sink_input *isync;
+ void (*func)(pa_memblockq *bq);
+
+ switch (code) {
+ case SINK_INPUT_MESSAGE_FLUSH:
+ func = pa_memblockq_flush;
+ break;
+
+ case SINK_INPUT_MESSAGE_PREBUF_FORCE:
+ func = pa_memblockq_prebuf_force;
+ break;
+
+ case SINK_INPUT_MESSAGE_DRAIN:
+ case SINK_INPUT_MESSAGE_TRIGGER:
+ func = pa_memblockq_prebuf_disable;
+ break;
+
+ default:
+ pa_assert_not_reached();
+ }
+
+ windex = pa_memblockq_get_write_index(s->memblockq);
+ func(s->memblockq);
+ handle_seek(s, windex);
+
+ /* Do the same for all other members in the sync group */
+ for (isync = i->sync_prev; isync; isync = isync->sync_prev) {
+ playback_stream *ssync = PLAYBACK_STREAM(isync->userdata);
+ windex = pa_memblockq_get_write_index(ssync->memblockq);
+ func(ssync->memblockq);
+ handle_seek(ssync, windex);
+ }
+
+ for (isync = i->sync_next; isync; isync = isync->sync_next) {
+ playback_stream *ssync = PLAYBACK_STREAM(isync->userdata);
+ windex = pa_memblockq_get_write_index(ssync->memblockq);
+ func(ssync->memblockq);
+ handle_seek(ssync, windex);
+ }
+
+ if (code == SINK_INPUT_MESSAGE_DRAIN) {
+ if (!pa_memblockq_is_readable(s->memblockq))
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_DRAIN_ACK, userdata, 0, NULL, NULL);
+ else {
+ s->drain_tag = PA_PTR_TO_UINT(userdata);
+ s->drain_request = TRUE;
+ }
+ }
+
+ return 0;
+ }
+
+ case SINK_INPUT_MESSAGE_UPDATE_LATENCY:
+
+ s->read_index = pa_memblockq_get_read_index(s->memblockq);
+ s->write_index = pa_memblockq_get_write_index(s->memblockq);
+ s->render_memblockq_length = pa_memblockq_get_length(s->sink_input->thread_info.render_memblockq);
+ return 0;
+
+ case PA_SINK_INPUT_MESSAGE_SET_STATE: {
+ int64_t windex;
+
+ windex = pa_memblockq_get_write_index(s->memblockq);
+
+ pa_memblockq_prebuf_force(s->memblockq);
+
+ handle_seek(s, windex);
+
+ /* Fall through to the default handler */
+ break;
+ }
+
+ case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
+ pa_usec_t *r = userdata;
+
+ *r = pa_bytes_to_usec(pa_memblockq_get_length(s->memblockq), &i->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, userdata, offset, chunk);
+}
+
+/* Called from thread context */
+static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
+ playback_stream *s;
+
+ pa_sink_input_assert_ref(i);
+ s = PLAYBACK_STREAM(i->userdata);
+ playback_stream_assert_ref(s);
+ pa_assert(chunk);
+
if (pa_memblockq_peek(s->memblockq, chunk) < 0) {
-/* pa_log("peek: failure"); */
+
+/* pa_log("UNDERRUN: %lu", (unsigned long) pa_memblockq_get_length(s->memblockq)); */
+
+ if (s->drain_request && pa_sink_input_safe_to_remove(i)) {
+ s->drain_request = FALSE;
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_DRAIN_ACK, PA_UINT_TO_PTR(s->drain_tag), 0, NULL, NULL);
+ } else if (i->thread_info.playing_for > 0)
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_UNDERFLOW, NULL, 0, NULL, NULL);
+
+/* pa_log("adding %llu bytes", (unsigned long long) nbytes); */
+
+ request_bytes(s);
+
return -1;
}
-/* pa_log("peek: %u", chunk->length); */
-
+/* pa_log("NOTUNDERRUN %lu", (unsigned long) chunk->length); */
+
+ chunk->length = PA_MIN(nbytes, chunk->length);
+
+ if (i->thread_info.underrun_for > 0)
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_STARTED, NULL, 0, NULL, NULL);
+
+ pa_memblockq_drop(s->memblockq, chunk->length);
+ request_bytes(s);
+
return 0;
}
-static void sink_input_drop_cb(pa_sink_input *i, const pa_memchunk *chunk, size_t length) {
- struct playback_stream *s;
- assert(i && i->userdata && length);
- s = i->userdata;
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
+ playback_stream *s;
- pa_memblockq_drop(s->memblockq, chunk, length);
+ pa_sink_input_assert_ref(i);
+ s = PLAYBACK_STREAM(i->userdata);
+ playback_stream_assert_ref(s);
- request_bytes(s);
+ /* If we are in an underrun, then we don't rewind */
+ if (i->thread_info.underrun_for > 0)
+ return;
- if (s->drain_request && !pa_memblockq_is_readable(s->memblockq)) {
- pa_pstream_send_simple_ack(s->connection->pstream, s->drain_tag);
- s->drain_request = 0;
- }
+ pa_memblockq_rewind(s->memblockq, nbytes);
+}
+
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
+ playback_stream *s;
-/* pa_log("after_drop: %u %u", pa_memblockq_get_length(s->memblockq), pa_memblockq_is_readable(s->memblockq)); */
+ pa_sink_input_assert_ref(i);
+ s = PLAYBACK_STREAM(i->userdata);
+ playback_stream_assert_ref(s);
+
+ pa_memblockq_set_maxrewind(s->memblockq, nbytes);
}
+/* Called from main context */
static void sink_input_kill_cb(pa_sink_input *i) {
- assert(i && i->userdata);
- send_playback_stream_killed((struct playback_stream *) i->userdata);
- playback_stream_free((struct playback_stream *) i->userdata);
+ playback_stream *s;
+
+ pa_sink_input_assert_ref(i);
+ s = PLAYBACK_STREAM(i->userdata);
+ playback_stream_assert_ref(s);
+
+ send_playback_stream_killed(s);
+ playback_stream_unlink(s);
}
-static pa_usec_t sink_input_get_latency_cb(pa_sink_input *i) {
- struct playback_stream *s;
- assert(i && i->userdata);
- s = i->userdata;
+/* Called from main context */
+static void sink_input_suspend_cb(pa_sink_input *i, pa_bool_t suspend) {
+ playback_stream *s;
+ pa_tagstruct *t;
+
+ pa_sink_input_assert_ref(i);
+ s = PLAYBACK_STREAM(i->userdata);
+ playback_stream_assert_ref(s);
- /*pa_log("get_latency: %u", pa_memblockq_get_length(s->memblockq));*/
-
- return pa_bytes_to_usec(pa_memblockq_get_length(s->memblockq), &s->sink_input->sample_spec);
+ if (s->connection->version < 12)
+ return;
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_STREAM_SUSPENDED);
+ pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+ pa_tagstruct_putu32(t, s->index);
+ pa_tagstruct_put_boolean(t, suspend);
+ pa_pstream_send_tagstruct(s->connection->pstream, t);
+}
+
+/* Called from main context */
+static void sink_input_moved_cb(pa_sink_input *i) {
+ playback_stream *s;
+ pa_tagstruct *t;
+ uint32_t maxlength, tlength, prebuf, minreq;
+
+ pa_sink_input_assert_ref(i);
+ s = PLAYBACK_STREAM(i->userdata);
+ playback_stream_assert_ref(s);
+
+ maxlength = (uint32_t) pa_memblockq_get_maxlength(s->memblockq);
+ tlength = (uint32_t) pa_memblockq_get_tlength(s->memblockq);
+ prebuf = (uint32_t) pa_memblockq_get_prebuf(s->memblockq);
+ minreq = (uint32_t) pa_memblockq_get_minreq(s->memblockq);
+
+ fix_playback_buffer_attr_pre(s, TRUE, &maxlength, &tlength, &prebuf, &minreq);
+ pa_memblockq_set_maxlength(s->memblockq, maxlength);
+ pa_memblockq_set_tlength(s->memblockq, tlength);
+ pa_memblockq_set_prebuf(s->memblockq, prebuf);
+ pa_memblockq_set_minreq(s->memblockq, minreq);
+ fix_playback_buffer_attr_post(s, &maxlength, &tlength, &prebuf, &minreq);
+
+ if (s->connection->version < 12)
+ return;
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_STREAM_MOVED);
+ pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+ pa_tagstruct_putu32(t, s->index);
+ pa_tagstruct_putu32(t, i->sink->index);
+ pa_tagstruct_puts(t, i->sink->name);
+ pa_tagstruct_put_boolean(t, pa_sink_get_state(i->sink) == PA_SINK_SUSPENDED);
+
+ if (s->connection->version >= 13) {
+ pa_tagstruct_putu32(t, maxlength);
+ pa_tagstruct_putu32(t, tlength);
+ pa_tagstruct_putu32(t, prebuf);
+ pa_tagstruct_putu32(t, minreq);
+ pa_tagstruct_put_usec(t, s->sink_latency);
+ }
+
+ pa_pstream_send_tagstruct(s->connection->pstream, t);
}
/*** source_output callbacks ***/
+/* Called from thread context */
static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
- struct record_stream *s;
- assert(o && o->userdata && chunk);
- s = o->userdata;
-
- if (pa_memblockq_push_align(s->memblockq, chunk) < 0) {
- pa_log_warn("Failed to push data into output queue.");
- return;
- }
-
- if (!pa_pstream_is_pending(s->connection->pstream))
- send_memblock(s->connection);
+ record_stream *s;
+
+ pa_source_output_assert_ref(o);
+ s = RECORD_STREAM(o->userdata);
+ record_stream_assert_ref(s);
+ pa_assert(chunk);
+
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), RECORD_STREAM_MESSAGE_POST_DATA, NULL, 0, chunk, NULL);
}
static void source_output_kill_cb(pa_source_output *o) {
- assert(o && o->userdata);
- send_record_stream_killed((struct record_stream *) o->userdata);
- record_stream_free((struct record_stream *) o->userdata);
+ record_stream *s;
+
+ pa_source_output_assert_ref(o);
+ s = RECORD_STREAM(o->userdata);
+ record_stream_assert_ref(s);
+
+ send_record_stream_killed(s);
+ record_stream_unlink(s);
}
static pa_usec_t source_output_get_latency_cb(pa_source_output *o) {
- struct record_stream *s;
- assert(o && o->userdata);
- s = o->userdata;
+ record_stream *s;
+
+ pa_source_output_assert_ref(o);
+ s = RECORD_STREAM(o->userdata);
+ record_stream_assert_ref(s);
/*pa_log("get_latency: %u", pa_memblockq_get_length(s->memblockq));*/
-
+
return pa_bytes_to_usec(pa_memblockq_get_length(s->memblockq), &o->sample_spec);
}
+/* Called from main context */
+static void source_output_suspend_cb(pa_source_output *o, pa_bool_t suspend) {
+ record_stream *s;
+ pa_tagstruct *t;
+
+ pa_source_output_assert_ref(o);
+ s = RECORD_STREAM(o->userdata);
+ record_stream_assert_ref(s);
+
+ if (s->connection->version < 12)
+ return;
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_RECORD_STREAM_SUSPENDED);
+ pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+ pa_tagstruct_putu32(t, s->index);
+ pa_tagstruct_put_boolean(t, suspend);
+ pa_pstream_send_tagstruct(s->connection->pstream, t);
+}
+
+/* Called from main context */
+static void source_output_moved_cb(pa_source_output *o) {
+ record_stream *s;
+ pa_tagstruct *t;
+ uint32_t maxlength, fragsize;
+
+ pa_source_output_assert_ref(o);
+ s = RECORD_STREAM(o->userdata);
+ record_stream_assert_ref(s);
+
+ fragsize = (uint32_t) s->fragment_size;
+ maxlength = (uint32_t) pa_memblockq_get_length(s->memblockq);
+
+ fix_record_buffer_attr_pre(s, TRUE, &maxlength, &fragsize);
+ pa_memblockq_set_maxlength(s->memblockq, maxlength);
+ fix_record_buffer_attr_post(s, &maxlength, &fragsize);
+
+ if (s->connection->version < 12)
+ return;
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_RECORD_STREAM_MOVED);
+ pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+ pa_tagstruct_putu32(t, s->index);
+ pa_tagstruct_putu32(t, o->source->index);
+ pa_tagstruct_puts(t, o->source->name);
+ pa_tagstruct_put_boolean(t, pa_source_get_state(o->source) == PA_SOURCE_SUSPENDED);
+
+ if (s->connection->version >= 13) {
+ pa_tagstruct_putu32(t, maxlength);
+ pa_tagstruct_putu32(t, fragsize);
+ pa_tagstruct_put_usec(t, s->source_latency);
+ }
+
+ pa_pstream_send_tagstruct(s->connection->pstream, t);
+}
+
/*** pdispatch callbacks ***/
-static void protocol_error(struct connection *c) {
+static void protocol_error(connection *c) {
pa_log("protocol error, kicking client");
- connection_free(c);
+ connection_unlink(c);
}
#define CHECK_VALIDITY(pstream, expression, tag, error) do { \
@@ -710,7 +1526,7 @@ if (!(expression)) { \
static pa_tagstruct *reply_new(uint32_t tag) {
pa_tagstruct *reply;
-
+
reply = pa_tagstruct_new(NULL, 0);
pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
pa_tagstruct_putu32(reply, tag);
@@ -718,86 +1534,175 @@ static pa_tagstruct *reply_new(uint32_t tag) {
}
static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- struct connection *c = userdata;
- struct playback_stream *s;
- uint32_t maxlength, tlength, prebuf, minreq, sink_index, syncid;
- const char *name, *sink_name;
+ connection *c = CONNECTION(userdata);
+ playback_stream *s;
+ uint32_t maxlength, tlength, prebuf, minreq, sink_index, syncid, missing;
+ const char *name = NULL, *sink_name;
pa_sample_spec ss;
pa_channel_map map;
pa_tagstruct *reply;
pa_sink *sink = NULL;
pa_cvolume volume;
- int corked;
-
- assert(c && t && c->protocol && c->protocol->core);
-
- if (pa_tagstruct_get(
- t,
- PA_TAG_STRING, &name,
- PA_TAG_SAMPLE_SPEC, &ss,
- PA_TAG_CHANNEL_MAP, &map,
- PA_TAG_U32, &sink_index,
- PA_TAG_STRING, &sink_name,
- PA_TAG_U32, &maxlength,
- PA_TAG_BOOLEAN, &corked,
- PA_TAG_U32, &tlength,
- PA_TAG_U32, &prebuf,
- PA_TAG_U32, &minreq,
- PA_TAG_U32, &syncid,
- PA_TAG_CVOLUME, &volume,
- PA_TAG_INVALID) < 0 ||
- !pa_tagstruct_eof(t) ||
- !name) {
+ pa_bool_t
+ corked = FALSE,
+ no_remap = FALSE,
+ no_remix = FALSE,
+ fix_format = FALSE,
+ fix_rate = FALSE,
+ fix_channels = FALSE,
+ no_move = FALSE,
+ variable_rate = FALSE,
+ muted = FALSE,
+ adjust_latency = FALSE;
+
+ pa_sink_input_flags_t flags = 0;
+ pa_proplist *p;
+
+ connection_assert_ref(c);
+ pa_assert(t);
+
+ if ((c->version < 13 && (pa_tagstruct_gets(t, &name) < 0 || !name)) ||
+ pa_tagstruct_get(
+ t,
+ PA_TAG_SAMPLE_SPEC, &ss,
+ PA_TAG_CHANNEL_MAP, &map,
+ PA_TAG_U32, &sink_index,
+ PA_TAG_STRING, &sink_name,
+ PA_TAG_U32, &maxlength,
+ PA_TAG_BOOLEAN, &corked,
+ PA_TAG_U32, &tlength,
+ PA_TAG_U32, &prebuf,
+ PA_TAG_U32, &minreq,
+ PA_TAG_U32, &syncid,
+ PA_TAG_CVOLUME, &volume,
+ PA_TAG_INVALID) < 0) {
+
protocol_error(c);
return;
}
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
- CHECK_VALIDITY(c->pstream, name && pa_utf8_valid(name), tag, PA_ERR_INVALID);
- CHECK_VALIDITY(c->pstream, sink_index != PA_INVALID_INDEX || !sink_name || (*sink_name && pa_utf8_valid(name)), tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, sink_index != PA_INVALID_INDEX || !sink_name || (*sink_name && pa_utf8_valid(sink_name)), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, pa_channel_map_valid(&map), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, pa_cvolume_valid(&volume), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, map.channels == ss.channels && volume.channels == ss.channels, tag, PA_ERR_INVALID);
- CHECK_VALIDITY(c->pstream, maxlength > 0 && maxlength <= MAX_MEMBLOCKQ_LENGTH, tag, PA_ERR_INVALID);
+
+ p = pa_proplist_new();
+
+ if (name)
+ pa_proplist_sets(p, PA_PROP_MEDIA_NAME, name);
+
+ if (c->version >= 12) {
+ /* Since 0.9.8 the user can ask for a couple of additional flags */
+
+ if (pa_tagstruct_get_boolean(t, &no_remap) < 0 ||
+ pa_tagstruct_get_boolean(t, &no_remix) < 0 ||
+ pa_tagstruct_get_boolean(t, &fix_format) < 0 ||
+ pa_tagstruct_get_boolean(t, &fix_rate) < 0 ||
+ pa_tagstruct_get_boolean(t, &fix_channels) < 0 ||
+ pa_tagstruct_get_boolean(t, &no_move) < 0 ||
+ pa_tagstruct_get_boolean(t, &variable_rate) < 0) {
+
+ protocol_error(c);
+ pa_proplist_free(p);
+ return;
+ }
+ }
+
+ if (c->version >= 13) {
+
+ if (pa_tagstruct_get_boolean(t, &muted) < 0 ||
+ pa_tagstruct_get_boolean(t, &adjust_latency) < 0 ||
+ pa_tagstruct_get_proplist(t, p) < 0) {
+ protocol_error(c);
+ pa_proplist_free(p);
+ return;
+ }
+ }
+
+ if (!pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ pa_proplist_free(p);
+ return;
+ }
if (sink_index != PA_INVALID_INDEX) {
- sink = pa_idxset_get_by_index(c->protocol->core->sinks, sink_index);
- CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
+
+ if (!(sink = pa_idxset_get_by_index(c->protocol->core->sinks, sink_index))) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
+ pa_proplist_free(p);
+ return;
+ }
+
} else if (sink_name) {
- sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK, 1);
- CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
+
+ if (!(sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK, 1))) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
+ pa_proplist_free(p);
+ return;
+ }
}
- s = playback_stream_new(c, sink, &ss, &map, name, maxlength, tlength, prebuf, minreq, &volume, syncid);
+ flags =
+ (corked ? PA_SINK_INPUT_START_CORKED : 0) |
+ (no_remap ? PA_SINK_INPUT_NO_REMAP : 0) |
+ (no_remix ? PA_SINK_INPUT_NO_REMIX : 0) |
+ (fix_format ? PA_SINK_INPUT_FIX_FORMAT : 0) |
+ (fix_rate ? PA_SINK_INPUT_FIX_RATE : 0) |
+ (fix_channels ? PA_SINK_INPUT_FIX_CHANNELS : 0) |
+ (no_move ? PA_SINK_INPUT_DONT_MOVE : 0) |
+ (variable_rate ? PA_SINK_INPUT_VARIABLE_RATE : 0);
+
+ s = playback_stream_new(c, sink, &ss, &map, &maxlength, &tlength, &prebuf, &minreq, &volume, muted, syncid, &missing, flags, p, adjust_latency);
+ pa_proplist_free(p);
+
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID);
- pa_sink_input_cork(s->sink_input, corked);
-
reply = reply_new(tag);
pa_tagstruct_putu32(reply, s->index);
- assert(s->sink_input);
+ pa_assert(s->sink_input);
pa_tagstruct_putu32(reply, s->sink_input->index);
- pa_tagstruct_putu32(reply, s->requested_bytes = pa_memblockq_missing(s->memblockq));
+ pa_tagstruct_putu32(reply, missing);
+
+/* pa_log("initial request is %u", missing); */
if (c->version >= 9) {
- /* Since 0.9 we support sending the buffer metrics back to the client */
+ /* Since 0.9.0 we support sending the buffer metrics back to the client */
+
+ pa_tagstruct_putu32(reply, (uint32_t) maxlength);
+ pa_tagstruct_putu32(reply, (uint32_t) tlength);
+ pa_tagstruct_putu32(reply, (uint32_t) prebuf);
+ pa_tagstruct_putu32(reply, (uint32_t) minreq);
+ }
- pa_tagstruct_putu32(reply, (uint32_t) pa_memblockq_get_maxlength(s->memblockq));
- pa_tagstruct_putu32(reply, (uint32_t) pa_memblockq_get_tlength(s->memblockq));
- pa_tagstruct_putu32(reply, (uint32_t) pa_memblockq_get_prebuf(s->memblockq));
- pa_tagstruct_putu32(reply, (uint32_t) pa_memblockq_get_minreq(s->memblockq));
+ if (c->version >= 12) {
+ /* Since 0.9.8 we support sending the chosen sample
+ * spec/channel map/device/suspend status back to the
+ * client */
+
+ pa_tagstruct_put_sample_spec(reply, &ss);
+ pa_tagstruct_put_channel_map(reply, &map);
+
+ pa_tagstruct_putu32(reply, s->sink_input->sink->index);
+ pa_tagstruct_puts(reply, s->sink_input->sink->name);
+
+ pa_tagstruct_put_boolean(reply, pa_sink_get_state(s->sink_input->sink) == PA_SINK_SUSPENDED);
}
-
+
+ if (c->version >= 13)
+ pa_tagstruct_put_usec(reply, s->sink_latency);
+
pa_pstream_send_tagstruct(c->pstream, reply);
- request_bytes(s);
}
static void command_delete_stream(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
uint32_t channel;
- assert(c && t);
-
+
+ connection_assert_ref(c);
+ pa_assert(t);
+
if (pa_tagstruct_getu32(t, &channel) < 0 ||
!pa_tagstruct_eof(t)) {
protocol_error(c);
@@ -806,119 +1711,234 @@ static void command_delete_stream(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t comma
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
- if (command == PA_COMMAND_DELETE_PLAYBACK_STREAM) {
- struct playback_stream *s;
- if (!(s = pa_idxset_get_by_index(c->output_streams, channel)) || (s->type != PLAYBACK_STREAM)) {
- pa_pstream_send_error(c->pstream, tag, PA_ERR_EXIST);
- return;
+ switch (command) {
+
+ case PA_COMMAND_DELETE_PLAYBACK_STREAM: {
+ playback_stream *s;
+ if (!(s = pa_idxset_get_by_index(c->output_streams, channel)) || !playback_stream_isinstance(s)) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERR_EXIST);
+ return;
+ }
+
+ playback_stream_unlink(s);
+ break;
}
- playback_stream_free(s);
- } else if (command == PA_COMMAND_DELETE_RECORD_STREAM) {
- struct record_stream *s;
- if (!(s = pa_idxset_get_by_index(c->record_streams, channel))) {
- pa_pstream_send_error(c->pstream, tag, PA_ERR_EXIST);
- return;
+ case PA_COMMAND_DELETE_RECORD_STREAM: {
+ record_stream *s;
+ if (!(s = pa_idxset_get_by_index(c->record_streams, channel))) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERR_EXIST);
+ return;
+ }
+
+ record_stream_unlink(s);
+ break;
}
- record_stream_free(s);
- } else {
- struct upload_stream *s;
- assert(command == PA_COMMAND_DELETE_UPLOAD_STREAM);
- if (!(s = pa_idxset_get_by_index(c->output_streams, channel)) || (s->type != UPLOAD_STREAM)) {
- pa_pstream_send_error(c->pstream, tag, PA_ERR_EXIST);
- return;
+ case PA_COMMAND_DELETE_UPLOAD_STREAM: {
+ upload_stream *s;
+
+ if (!(s = pa_idxset_get_by_index(c->output_streams, channel)) || !upload_stream_isinstance(s)) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERR_EXIST);
+ return;
+ }
+
+ upload_stream_unlink(s);
+ break;
}
- upload_stream_free(s);
+ default:
+ pa_assert_not_reached();
}
-
+
pa_pstream_send_simple_ack(c->pstream, tag);
}
static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- struct connection *c = userdata;
- struct record_stream *s;
+ connection *c = CONNECTION(userdata);
+ record_stream *s;
uint32_t maxlength, fragment_size;
uint32_t source_index;
- const char *name, *source_name;
+ const char *name = NULL, *source_name;
pa_sample_spec ss;
pa_channel_map map;
pa_tagstruct *reply;
pa_source *source = NULL;
- int corked;
- assert(c && t && c->protocol && c->protocol->core);
-
- if (pa_tagstruct_gets(t, &name) < 0 ||
+ pa_bool_t
+ corked = FALSE,
+ no_remap = FALSE,
+ no_remix = FALSE,
+ fix_format = FALSE,
+ fix_rate = FALSE,
+ fix_channels = FALSE,
+ no_move = FALSE,
+ variable_rate = FALSE,
+ adjust_latency = FALSE,
+ peak_detect = FALSE;
+ pa_source_output_flags_t flags = 0;
+ pa_proplist *p;
+ uint32_t direct_on_input_idx = PA_INVALID_INDEX;
+ pa_sink_input *direct_on_input = NULL;
+
+ connection_assert_ref(c);
+ pa_assert(t);
+
+ if ((c->version < 13 && (pa_tagstruct_gets(t, &name) < 0 || !name)) ||
pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
pa_tagstruct_get_channel_map(t, &map) < 0 ||
pa_tagstruct_getu32(t, &source_index) < 0 ||
pa_tagstruct_gets(t, &source_name) < 0 ||
pa_tagstruct_getu32(t, &maxlength) < 0 ||
pa_tagstruct_get_boolean(t, &corked) < 0 ||
- pa_tagstruct_getu32(t, &fragment_size) < 0 ||
- !pa_tagstruct_eof(t)) {
+ pa_tagstruct_getu32(t, &fragment_size) < 0) {
protocol_error(c);
return;
}
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
- CHECK_VALIDITY(c->pstream, name && pa_utf8_valid(name), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, pa_channel_map_valid(&map), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, source_index != PA_INVALID_INDEX || !source_name || (*source_name && pa_utf8_valid(source_name)), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, map.channels == ss.channels, tag, PA_ERR_INVALID);
- CHECK_VALIDITY(c->pstream, maxlength <= MAX_MEMBLOCKQ_LENGTH, tag, PA_ERR_INVALID);
+
+ p = pa_proplist_new();
+
+ if (name)
+ pa_proplist_sets(p, PA_PROP_MEDIA_NAME, name);
+
+ if (c->version >= 12) {
+ /* Since 0.9.8 the user can ask for a couple of additional flags */
+
+ if (pa_tagstruct_get_boolean(t, &no_remap) < 0 ||
+ pa_tagstruct_get_boolean(t, &no_remix) < 0 ||
+ pa_tagstruct_get_boolean(t, &fix_format) < 0 ||
+ pa_tagstruct_get_boolean(t, &fix_rate) < 0 ||
+ pa_tagstruct_get_boolean(t, &fix_channels) < 0 ||
+ pa_tagstruct_get_boolean(t, &no_move) < 0 ||
+ pa_tagstruct_get_boolean(t, &variable_rate) < 0) {
+
+ protocol_error(c);
+ pa_proplist_free(p);
+ return;
+ }
+ }
+
+ if (c->version >= 13) {
+
+ if (pa_tagstruct_get_boolean(t, &peak_detect) < 0 ||
+ pa_tagstruct_get_boolean(t, &adjust_latency) < 0 ||
+ pa_tagstruct_get_proplist(t, p) < 0 ||
+ pa_tagstruct_getu32(t, &direct_on_input_idx) < 0) {
+ protocol_error(c);
+ pa_proplist_free(p);
+ return;
+ }
+ }
+
+ if (!pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ pa_proplist_free(p);
+ return;
+ }
if (source_index != PA_INVALID_INDEX) {
- source = pa_idxset_get_by_index(c->protocol->core->sources, source_index);
- CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY);
+
+ if (!(source = pa_idxset_get_by_index(c->protocol->core->sources, source_index))) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
+ pa_proplist_free(p);
+ return;
+ }
+
} else if (source_name) {
- source = pa_namereg_get(c->protocol->core, source_name, PA_NAMEREG_SOURCE, 1);
- CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY);
- }
-
- s = record_stream_new(c, source, &ss, &map, name, maxlength, fragment_size);
+
+ if (!(source = pa_namereg_get(c->protocol->core, source_name, PA_NAMEREG_SOURCE, 1))) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
+ pa_proplist_free(p);
+ return;
+ }
+ }
+
+ if (direct_on_input_idx != PA_INVALID_INDEX) {
+
+ if (!(direct_on_input = pa_idxset_get_by_index(c->protocol->core->sink_inputs, direct_on_input_idx))) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
+ pa_proplist_free(p);
+ return;
+ }
+ }
+
+ flags =
+ (corked ? PA_SOURCE_OUTPUT_START_CORKED : 0) |
+ (no_remap ? PA_SOURCE_OUTPUT_NO_REMAP : 0) |
+ (no_remix ? PA_SOURCE_OUTPUT_NO_REMIX : 0) |
+ (fix_format ? PA_SOURCE_OUTPUT_FIX_FORMAT : 0) |
+ (fix_rate ? PA_SOURCE_OUTPUT_FIX_RATE : 0) |
+ (fix_channels ? PA_SOURCE_OUTPUT_FIX_CHANNELS : 0) |
+ (no_move ? PA_SOURCE_OUTPUT_DONT_MOVE : 0) |
+ (variable_rate ? PA_SOURCE_OUTPUT_VARIABLE_RATE : 0);
+
+ s = record_stream_new(c, source, &ss, &map, peak_detect, &maxlength, &fragment_size, flags, p, adjust_latency, direct_on_input);
+ pa_proplist_free(p);
+
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID);
-
- pa_source_output_cork(s->source_output, corked);
-
+
reply = reply_new(tag);
pa_tagstruct_putu32(reply, s->index);
- assert(s->source_output);
+ pa_assert(s->source_output);
pa_tagstruct_putu32(reply, s->source_output->index);
if (c->version >= 9) {
/* Since 0.9 we support sending the buffer metrics back to the client */
- pa_tagstruct_putu32(reply, (uint32_t) pa_memblockq_get_maxlength(s->memblockq));
- pa_tagstruct_putu32(reply, (uint32_t) s->fragment_size);
+ pa_tagstruct_putu32(reply, (uint32_t) maxlength);
+ pa_tagstruct_putu32(reply, (uint32_t) fragment_size);
}
-
+
+ if (c->version >= 12) {
+ /* Since 0.9.8 we support sending the chosen sample
+ * spec/channel map/device/suspend status back to the
+ * client */
+
+ pa_tagstruct_put_sample_spec(reply, &ss);
+ pa_tagstruct_put_channel_map(reply, &map);
+
+ pa_tagstruct_putu32(reply, s->source_output->source->index);
+ pa_tagstruct_puts(reply, s->source_output->source->name);
+
+ pa_tagstruct_put_boolean(reply, pa_source_get_state(s->source_output->source) == PA_SOURCE_SUSPENDED);
+ }
+
+ if (c->version >= 13)
+ pa_tagstruct_put_usec(reply, s->source_latency);
+
pa_pstream_send_tagstruct(c->pstream, reply);
}
static void command_exit(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- struct connection *c = userdata;
- assert(c && t);
-
+ connection *c = CONNECTION(userdata);
+
+ connection_assert_ref(c);
+ pa_assert(t);
+
if (!pa_tagstruct_eof(t)) {
protocol_error(c);
return;
}
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
-
- assert(c->protocol && c->protocol->core && c->protocol->core->mainloop);
+
c->protocol->core->mainloop->quit(c->protocol->core->mainloop, 0);
pa_pstream_send_simple_ack(c->pstream, tag); /* nonsense */
}
static void command_auth(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
const void*cookie;
pa_tagstruct *reply;
- assert(c && t);
+ pa_bool_t shm_on_remote, do_shm;
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (pa_tagstruct_getu32(t, &c->version) < 0 ||
pa_tagstruct_get_arbitrary(t, &cookie, PA_NATIVE_COOKIE_LENGTH) < 0 ||
@@ -933,76 +1953,103 @@ static void command_auth(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t
return;
}
+ /* Starting with protocol version 13 the MSB of the version tag
+ reflects if shm is available for this connection or
+ not. */
+ if (c->version >= 13) {
+ shm_on_remote = !!(c->version & 0x80000000U);
+ c->version &= 0x7FFFFFFFU;
+ }
+
+ pa_log_debug("Protocol version: remote %u, local %u", c->version, PA_PROTOCOL_VERSION);
+
+ pa_proplist_setf(c->client->proplist, "native-protocol.version", "%u", c->version);
+
if (!c->authorized) {
- int success = 0;
-
+ pa_bool_t success = FALSE;
+
#ifdef HAVE_CREDS
const pa_creds *creds;
if ((creds = pa_pdispatch_creds(pd))) {
if (creds->uid == getuid())
- success = 1;
+ success = TRUE;
else if (c->protocol->auth_group) {
int r;
gid_t gid;
if ((gid = pa_get_gid_of_group(c->protocol->auth_group)) == (gid_t) -1)
- pa_log_warn("failed to get GID of group '%s'", c->protocol->auth_group);
+ pa_log_warn("Failed to get GID of group '%s'", c->protocol->auth_group);
else if (gid == creds->gid)
- success = 1;
-
+ success = TRUE;
+
if (!success) {
if ((r = pa_uid_in_group(creds->uid, c->protocol->auth_group)) < 0)
- pa_log_warn("failed to check group membership.");
+ pa_log_warn("Failed to check group membership.");
else if (r > 0)
- success = 1;
+ success = TRUE;
}
}
-
+
pa_log_info("Got credentials: uid=%lu gid=%lu success=%i",
(unsigned long) creds->uid,
(unsigned long) creds->gid,
- success);
-
- if (c->version >= 10 &&
- pa_mempool_is_shared(c->protocol->core->mempool) &&
- creds->uid == getuid()) {
-
- pa_pstream_use_shm(c->pstream, 1);
- pa_log_info("Enabled SHM for new connection");
- }
-
+ (int) success);
}
#endif
if (!success && memcmp(c->protocol->auth_cookie, cookie, PA_NATIVE_COOKIE_LENGTH) == 0)
- success = 1;
+ success = TRUE;
if (!success) {
pa_log_warn("Denied access to client with invalid authorization data.");
pa_pstream_send_error(c->pstream, tag, PA_ERR_ACCESS);
return;
}
-
- c->authorized = 1;
+
+ c->authorized = TRUE;
if (c->auth_timeout_event) {
c->protocol->core->mainloop->time_free(c->auth_timeout_event);
c->auth_timeout_event = NULL;
}
}
+ /* Enable shared memory support if possible */
+ do_shm =
+ pa_mempool_is_shared(c->protocol->core->mempool) &&
+ c->is_local;
+
+ pa_log_debug("SHM possible: %s", pa_yes_no(do_shm));
+
+ if (do_shm)
+ if (c->version < 10 || (c->version >= 13 && !shm_on_remote))
+ do_shm = FALSE;
+
+ if (do_shm) {
+ /* Only enable SHM if both sides are owned by the same
+ * user. This is a security measure because otherwise data
+ * private to the user might leak. */
+
+ const pa_creds *creds;
+ if (!(creds = pa_pdispatch_creds(pd)) || getuid() != creds->uid)
+ do_shm = FALSE;
+ }
+
+ pa_log_debug("Negotiated SHM: %s", pa_yes_no(do_shm));
+ pa_pstream_enable_shm(c->pstream, do_shm);
+
reply = reply_new(tag);
- pa_tagstruct_putu32(reply, PA_PROTOCOL_VERSION);
+ pa_tagstruct_putu32(reply, PA_PROTOCOL_VERSION | (do_shm ? 0x80000000 : 0));
#ifdef HAVE_CREDS
{
/* SHM support is only enabled after both sides made sure they are the same user. */
-
+
pa_creds ucred;
ucred.uid = getuid();
ucred.gid = getgid();
-
+
pa_pstream_send_tagstruct_with_creds(c->pstream, reply, &ucred);
}
#else
@@ -1011,27 +2058,52 @@ static void command_auth(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t
}
static void command_set_client_name(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- struct connection *c = userdata;
- const char *name;
- assert(c && t);
+ connection *c = CONNECTION(userdata);
+ const char *name = NULL;
+ pa_proplist *p;
+ pa_tagstruct *reply;
- if (pa_tagstruct_gets(t, &name) < 0 ||
+ connection_assert_ref(c);
+ pa_assert(t);
+
+ p = pa_proplist_new();
+
+ if ((c->version < 13 && pa_tagstruct_gets(t, &name) < 0) ||
+ (c->version >= 13 && pa_tagstruct_get_proplist(t, p) < 0) ||
!pa_tagstruct_eof(t)) {
+
protocol_error(c);
+ pa_proplist_free(p);
return;
}
- CHECK_VALIDITY(c->pstream, name && pa_utf8_valid(name), tag, PA_ERR_INVALID);
-
- pa_client_set_name(c->client, name);
- pa_pstream_send_simple_ack(c->pstream, tag);
+ if (name)
+ if (pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, name) < 0) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
+ pa_proplist_free(p);
+ return;
+ }
+
+ pa_proplist_update(c->client->proplist, PA_UPDATE_REPLACE, p);
+ pa_proplist_free(p);
+
+ pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->client->index);
+
+ reply = reply_new(tag);
+
+ if (c->version >= 13)
+ pa_tagstruct_putu32(reply, c->client->index);
+
+ pa_pstream_send_tagstruct(c->pstream, reply);
}
static void command_lookup(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
const char *name;
uint32_t idx = PA_IDXSET_INVALID;
- assert(c && t);
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (pa_tagstruct_gets(t, &name) < 0 ||
!pa_tagstruct_eof(t)) {
@@ -1048,7 +2120,7 @@ static void command_lookup(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uin
idx = sink->index;
} else {
pa_source *source;
- assert(command == PA_COMMAND_LOOKUP_SOURCE);
+ pa_assert(command == PA_COMMAND_LOOKUP_SOURCE);
if ((source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1)))
idx = source->index;
}
@@ -1064,10 +2136,12 @@ static void command_lookup(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uin
}
static void command_drain_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
uint32_t idx;
- struct playback_stream *s;
- assert(c && t);
+ playback_stream *s;
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (pa_tagstruct_getu32(t, &idx) < 0 ||
!pa_tagstruct_eof(t)) {
@@ -1078,29 +2152,18 @@ static void command_drain_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
s = pa_idxset_get_by_index(c->output_streams, idx);
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
- CHECK_VALIDITY(c->pstream, s->type == PLAYBACK_STREAM, tag, PA_ERR_NOENTITY);
-
- s->drain_request = 0;
-
- pa_memblockq_prebuf_disable(s->memblockq);
-
- if (!pa_memblockq_is_readable(s->memblockq)) {
-/* pa_log("immediate drain: %u", pa_memblockq_get_length(s->memblockq)); */
- pa_pstream_send_simple_ack(c->pstream, tag);
- } else {
-/* pa_log("slow drain triggered"); */
- s->drain_request = 1;
- s->drain_tag = tag;
+ CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
- pa_sink_notify(s->sink_input->sink);
- }
-}
+ pa_asyncmsgq_post(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_DRAIN, PA_UINT_TO_PTR(tag), 0, NULL, NULL);
+}
static void command_stat(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
pa_tagstruct *reply;
const pa_mempool_stat *stat;
- assert(c && t);
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (!pa_tagstruct_eof(t)) {
protocol_error(c);
@@ -1110,25 +2173,27 @@ static void command_stat(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
stat = pa_mempool_get_stat(c->protocol->core->mempool);
-
+
reply = reply_new(tag);
- pa_tagstruct_putu32(reply, (uint32_t) AO_load_acquire_read((AO_t*) &stat->n_allocated));
- pa_tagstruct_putu32(reply, (uint32_t) AO_load_acquire_read((AO_t*) &stat->allocated_size));
- pa_tagstruct_putu32(reply, (uint32_t) AO_load_acquire_read((AO_t*) &stat->n_accumulated));
- pa_tagstruct_putu32(reply, (uint32_t) AO_load_acquire_read((AO_t*) &stat->accumulated_size));
+ pa_tagstruct_putu32(reply, (uint32_t) pa_atomic_load(&stat->n_allocated));
+ pa_tagstruct_putu32(reply, (uint32_t) pa_atomic_load(&stat->allocated_size));
+ pa_tagstruct_putu32(reply, (uint32_t) pa_atomic_load(&stat->n_accumulated));
+ pa_tagstruct_putu32(reply, (uint32_t) pa_atomic_load(&stat->accumulated_size));
pa_tagstruct_putu32(reply, pa_scache_total_size(c->protocol->core));
pa_pstream_send_tagstruct(c->pstream, reply);
}
static void command_get_playback_latency(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
pa_tagstruct *reply;
- struct playback_stream *s;
+ playback_stream *s;
struct timeval tv, now;
uint32_t idx;
pa_usec_t latency;
- assert(c && t);
-
+
+ connection_assert_ref(c);
+ pa_assert(t);
+
if (pa_tagstruct_getu32(t, &idx) < 0 ||
pa_tagstruct_get_timeval(t, &tv) < 0 ||
!pa_tagstruct_eof(t)) {
@@ -1139,31 +2204,40 @@ static void command_get_playback_latency(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
s = pa_idxset_get_by_index(c->output_streams, idx);
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
- CHECK_VALIDITY(c->pstream, s->type == PLAYBACK_STREAM, tag, PA_ERR_NOENTITY);
+ CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
+ CHECK_VALIDITY(c->pstream, pa_asyncmsgq_send(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_UPDATE_LATENCY, s, 0, NULL) == 0, tag, PA_ERR_NOENTITY)
reply = reply_new(tag);
latency = pa_sink_get_latency(s->sink_input->sink);
- if (s->sink_input->resampled_chunk.memblock)
- latency += pa_bytes_to_usec(s->sink_input->resampled_chunk.length, &s->sink_input->sample_spec);
+ latency += pa_bytes_to_usec(s->render_memblockq_length, &s->sink_input->sample_spec);
+
pa_tagstruct_put_usec(reply, latency);
-
+
pa_tagstruct_put_usec(reply, 0);
- pa_tagstruct_put_boolean(reply, s->sink_input->state == PA_SINK_INPUT_RUNNING);
+ pa_tagstruct_put_boolean(reply, s->sink_input->thread_info.playing_for > 0);
pa_tagstruct_put_timeval(reply, &tv);
pa_tagstruct_put_timeval(reply, pa_gettimeofday(&now));
- pa_tagstruct_puts64(reply, pa_memblockq_get_write_index(s->memblockq));
- pa_tagstruct_puts64(reply, pa_memblockq_get_read_index(s->memblockq));
+ pa_tagstruct_puts64(reply, s->write_index);
+ pa_tagstruct_puts64(reply, s->read_index);
+
+ if (c->version >= 13) {
+ pa_tagstruct_putu64(reply, s->sink_input->thread_info.underrun_for);
+ pa_tagstruct_putu64(reply, s->sink_input->thread_info.playing_for);
+ }
+
pa_pstream_send_tagstruct(c->pstream, reply);
}
static void command_get_record_latency(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
pa_tagstruct *reply;
- struct record_stream *s;
+ record_stream *s;
struct timeval tv, now;
uint32_t idx;
- assert(c && t);
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (pa_tagstruct_getu32(t, &idx) < 0 ||
pa_tagstruct_get_timeval(t, &tv) < 0 ||
@@ -1179,7 +2253,7 @@ static void command_get_record_latency(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UN
reply = reply_new(tag);
pa_tagstruct_put_usec(reply, s->source_output->source->monitor_of ? pa_sink_get_latency(s->source_output->source->monitor_of) : 0);
pa_tagstruct_put_usec(reply, pa_source_get_latency(s->source_output->source));
- pa_tagstruct_put_boolean(reply, 0);
+ pa_tagstruct_put_boolean(reply, pa_source_get_state(s->source_output->source) == PA_SOURCE_RUNNING);
pa_tagstruct_put_timeval(reply, &tv);
pa_tagstruct_put_timeval(reply, pa_gettimeofday(&now));
pa_tagstruct_puts64(reply, pa_memblockq_get_write_index(s->memblockq));
@@ -1188,20 +2262,22 @@ static void command_get_record_latency(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UN
}
static void command_create_upload_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- struct connection *c = userdata;
- struct upload_stream *s;
+ connection *c = CONNECTION(userdata);
+ upload_stream *s;
uint32_t length;
- const char *name;
+ const char *name = NULL;
pa_sample_spec ss;
pa_channel_map map;
pa_tagstruct *reply;
- assert(c && t && c->protocol && c->protocol->core);
-
- if (pa_tagstruct_gets(t, &name) < 0 ||
+ pa_proplist *p;
+
+ connection_assert_ref(c);
+ pa_assert(t);
+
+ if (pa_tagstruct_gets(t, &name) < 0 ||
pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
pa_tagstruct_get_channel_map(t, &map) < 0 ||
- pa_tagstruct_getu32(t, &length) < 0 ||
- !pa_tagstruct_eof(t)) {
+ pa_tagstruct_getu32(t, &length) < 0) {
protocol_error(c);
return;
}
@@ -1212,11 +2288,28 @@ static void command_create_upload_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
CHECK_VALIDITY(c->pstream, map.channels == ss.channels, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, (length % pa_frame_size(&ss)) == 0 && length > 0, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, length <= PA_SCACHE_ENTRY_SIZE_MAX, tag, PA_ERR_TOOLARGE);
+
+ p = pa_proplist_new();
+
+ if (c->version >= 13 && pa_tagstruct_get_proplist(t, p) < 0) {
+ protocol_error(c);
+ pa_proplist_free(p);
+ return;
+ }
+
+ if (c->version < 13)
+ pa_proplist_sets(p, PA_PROP_MEDIA_NAME, name);
+ else if (!name)
+ if (!(name = pa_proplist_gets(p, PA_PROP_EVENT_ID)))
+ name = pa_proplist_gets(p, PA_PROP_MEDIA_NAME);
+
CHECK_VALIDITY(c->pstream, name && *name && pa_utf8_valid(name), tag, PA_ERR_INVALID);
-
- s = upload_stream_new(c, &ss, &map, name, length);
+
+ s = upload_stream_new(c, &ss, &map, name, length, p);
+ pa_proplist_free(p);
+
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID);
-
+
reply = reply_new(tag);
pa_tagstruct_putu32(reply, s->index);
pa_tagstruct_putu32(reply, length);
@@ -1224,12 +2317,14 @@ static void command_create_upload_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
}
static void command_finish_upload_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
uint32_t channel;
- struct upload_stream *s;
+ upload_stream *s;
uint32_t idx;
- assert(c && t);
-
+
+ connection_assert_ref(c);
+ pa_assert(t);
+
if (pa_tagstruct_getu32(t, &channel) < 0 ||
!pa_tagstruct_eof(t)) {
protocol_error(c);
@@ -1240,34 +2335,39 @@ static void command_finish_upload_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
s = pa_idxset_get_by_index(c->output_streams, channel);
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
- CHECK_VALIDITY(c->pstream, s->type == UPLOAD_STREAM, tag, PA_ERR_NOENTITY);
+ CHECK_VALIDITY(c->pstream, upload_stream_isinstance(s), tag, PA_ERR_NOENTITY);
- if (pa_scache_add_item(c->protocol->core, s->name, &s->sample_spec, &s->channel_map, &s->memchunk, &idx) < 0)
+ if (pa_scache_add_item(c->protocol->core, s->name, &s->sample_spec, &s->channel_map, &s->memchunk, s->proplist, &idx) < 0)
pa_pstream_send_error(c->pstream, tag, PA_ERR_INTERNAL);
else
pa_pstream_send_simple_ack(c->pstream, tag);
-
- upload_stream_free(s);
+
+ upload_stream_unlink(s);
}
static void command_play_sample(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
uint32_t sink_index;
pa_volume_t volume;
pa_sink *sink;
const char *name, *sink_name;
- assert(c && t);
+ uint32_t idx;
+ pa_proplist *p;
+ pa_tagstruct *reply;
+
+ connection_assert_ref(c);
+ pa_assert(t);
+
+ CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
if (pa_tagstruct_getu32(t, &sink_index) < 0 ||
pa_tagstruct_gets(t, &sink_name) < 0 ||
pa_tagstruct_getu32(t, &volume) < 0 ||
- pa_tagstruct_gets(t, &name) < 0 ||
- !pa_tagstruct_eof(t)) {
+ pa_tagstruct_gets(t, &name) < 0) {
protocol_error(c);
return;
}
-
- CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+
CHECK_VALIDITY(c->pstream, sink_index != PA_INVALID_INDEX || !sink_name || (*sink_name && pa_utf8_valid(name)), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, name && *name && pa_utf8_valid(name), tag, PA_ERR_INVALID);
@@ -1278,18 +2378,37 @@ static void command_play_sample(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED ui
CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
- if (pa_scache_play_item(c->protocol->core, name, sink, volume) < 0) {
+ p = pa_proplist_new();
+
+ if ((c->version >= 13 && pa_tagstruct_get_proplist(t, p) < 0) ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ pa_proplist_free(p);
+ return;
+ }
+
+ if (pa_scache_play_item(c->protocol->core, name, sink, volume, p, &idx) < 0) {
pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
+ pa_proplist_free(p);
return;
}
- pa_pstream_send_simple_ack(c->pstream, tag);
+ pa_proplist_free(p);
+
+ reply = reply_new(tag);
+
+ if (c->version >= 13)
+ pa_tagstruct_putu32(reply, idx);
+
+ pa_pstream_send_tagstruct(c->pstream, reply);
}
static void command_remove_sample(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
const char *name;
- assert(c && t);
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (pa_tagstruct_gets(t, &name) < 0 ||
!pa_tagstruct_eof(t)) {
@@ -1308,62 +2427,105 @@ static void command_remove_sample(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED
pa_pstream_send_simple_ack(c->pstream, tag);
}
-static void sink_fill_tagstruct(pa_tagstruct *t, pa_sink *sink) {
- assert(t && sink);
+static void fixup_sample_spec(connection *c, pa_sample_spec *fixed, const pa_sample_spec *original) {
+ pa_assert(c);
+ pa_assert(fixed);
+ pa_assert(original);
+
+ *fixed = *original;
+
+ if (c->version < 12) {
+ /* Before protocol version 12 we didn't support S32 samples,
+ * so we need to lie about this to the client */
+
+ if (fixed->format == PA_SAMPLE_S32LE)
+ fixed->format = PA_SAMPLE_FLOAT32LE;
+ if (fixed->format == PA_SAMPLE_S32BE)
+ fixed->format = PA_SAMPLE_FLOAT32BE;
+ }
+}
+
+static void sink_fill_tagstruct(connection *c, pa_tagstruct *t, pa_sink *sink) {
+ pa_sample_spec fixed_ss;
+
+ pa_assert(t);
+ pa_sink_assert_ref(sink);
+
+ fixup_sample_spec(c, &fixed_ss, &sink->sample_spec);
+
pa_tagstruct_put(
t,
PA_TAG_U32, sink->index,
PA_TAG_STRING, sink->name,
- PA_TAG_STRING, sink->description,
- PA_TAG_SAMPLE_SPEC, &sink->sample_spec,
+ PA_TAG_STRING, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)),
+ PA_TAG_SAMPLE_SPEC, &fixed_ss,
PA_TAG_CHANNEL_MAP, &sink->channel_map,
- PA_TAG_U32, sink->owner ? sink->owner->index : PA_INVALID_INDEX,
- PA_TAG_CVOLUME, pa_sink_get_volume(sink, PA_MIXER_HARDWARE),
- PA_TAG_BOOLEAN, pa_sink_get_mute(sink, PA_MIXER_HARDWARE),
+ PA_TAG_U32, sink->module ? sink->module->index : PA_INVALID_INDEX,
+ PA_TAG_CVOLUME, pa_sink_get_volume(sink),
+ PA_TAG_BOOLEAN, pa_sink_get_mute(sink),
PA_TAG_U32, sink->monitor_source ? sink->monitor_source->index : PA_INVALID_INDEX,
PA_TAG_STRING, sink->monitor_source ? sink->monitor_source->name : NULL,
PA_TAG_USEC, pa_sink_get_latency(sink),
PA_TAG_STRING, sink->driver,
- PA_TAG_U32,
- (sink->get_hw_volume ? PA_SINK_HW_VOLUME_CTRL : 0) |
- (sink->get_latency ? PA_SINK_LATENCY : 0) |
- (sink->is_hardware ? PA_SINK_HARDWARE : 0),
+ PA_TAG_U32, sink->flags,
PA_TAG_INVALID);
+
+ if (c->version >= 13) {
+ pa_tagstruct_put_proplist(t, sink->proplist);
+ pa_tagstruct_put_usec(t, pa_sink_get_requested_latency(sink));
+ }
}
-static void source_fill_tagstruct(pa_tagstruct *t, pa_source *source) {
- assert(t && source);
+static void source_fill_tagstruct(connection *c, pa_tagstruct *t, pa_source *source) {
+ pa_sample_spec fixed_ss;
+
+ pa_assert(t);
+ pa_source_assert_ref(source);
+
+ fixup_sample_spec(c, &fixed_ss, &source->sample_spec);
+
pa_tagstruct_put(
t,
PA_TAG_U32, source->index,
PA_TAG_STRING, source->name,
- PA_TAG_STRING, source->description,
- PA_TAG_SAMPLE_SPEC, &source->sample_spec,
+ PA_TAG_STRING, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)),
+ PA_TAG_SAMPLE_SPEC, &fixed_ss,
PA_TAG_CHANNEL_MAP, &source->channel_map,
- PA_TAG_U32, source->owner ? source->owner->index : PA_INVALID_INDEX,
- PA_TAG_CVOLUME, pa_source_get_volume(source, PA_MIXER_HARDWARE),
- PA_TAG_BOOLEAN, pa_source_get_mute(source, PA_MIXER_HARDWARE),
+ PA_TAG_U32, source->module ? source->module->index : PA_INVALID_INDEX,
+ PA_TAG_CVOLUME, pa_source_get_volume(source),
+ PA_TAG_BOOLEAN, pa_source_get_mute(source),
PA_TAG_U32, source->monitor_of ? source->monitor_of->index : PA_INVALID_INDEX,
PA_TAG_STRING, source->monitor_of ? source->monitor_of->name : NULL,
PA_TAG_USEC, pa_source_get_latency(source),
PA_TAG_STRING, source->driver,
- PA_TAG_U32,
- (source->get_hw_volume ? PA_SOURCE_HW_VOLUME_CTRL : 0) |
- (source->get_latency ? PA_SOURCE_LATENCY : 0) |
- (source->is_hardware ? PA_SOURCE_HARDWARE : 0),
+ PA_TAG_U32, source->flags,
PA_TAG_INVALID);
+
+ if (c->version >= 13) {
+ pa_tagstruct_put_proplist(t, source->proplist);
+ pa_tagstruct_put_usec(t, pa_source_get_requested_latency(source));
+ }
}
-static void client_fill_tagstruct(pa_tagstruct *t, pa_client *client) {
- assert(t && client);
+
+static void client_fill_tagstruct(connection *c, pa_tagstruct *t, pa_client *client) {
+ pa_assert(t);
+ pa_assert(client);
+
pa_tagstruct_putu32(t, client->index);
- pa_tagstruct_puts(t, client->name);
- pa_tagstruct_putu32(t, client->owner ? client->owner->index : PA_INVALID_INDEX);
+ pa_tagstruct_puts(t, pa_strnull(pa_proplist_gets(client->proplist, PA_PROP_APPLICATION_NAME)));
+ pa_tagstruct_putu32(t, client->module ? client->module->index : PA_INVALID_INDEX);
pa_tagstruct_puts(t, client->driver);
+
+ if (c->version >= 13)
+ pa_tagstruct_put_proplist(t, client->proplist);
+
}
static void module_fill_tagstruct(pa_tagstruct *t, pa_module *module) {
- assert(t && module);
+ pa_assert(t);
+ pa_assert(module);
+
pa_tagstruct_putu32(t, module->index);
pa_tagstruct_puts(t, module->name);
pa_tagstruct_puts(t, module->argument);
@@ -1371,52 +2533,85 @@ static void module_fill_tagstruct(pa_tagstruct *t, pa_module *module) {
pa_tagstruct_put_boolean(t, module->auto_unload);
}
-static void sink_input_fill_tagstruct(pa_tagstruct *t, pa_sink_input *s) {
- assert(t && s);
+static void sink_input_fill_tagstruct(connection *c, pa_tagstruct *t, pa_sink_input *s) {
+ pa_sample_spec fixed_ss;
+ pa_usec_t sink_latency;
+
+ pa_assert(t);
+ pa_sink_input_assert_ref(s);
+
+ fixup_sample_spec(c, &fixed_ss, &s->sample_spec);
+
pa_tagstruct_putu32(t, s->index);
- pa_tagstruct_puts(t, s->name);
+ pa_tagstruct_puts(t, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_MEDIA_NAME)));
pa_tagstruct_putu32(t, s->module ? s->module->index : PA_INVALID_INDEX);
pa_tagstruct_putu32(t, s->client ? s->client->index : PA_INVALID_INDEX);
pa_tagstruct_putu32(t, s->sink->index);
- pa_tagstruct_put_sample_spec(t, &s->sample_spec);
+ pa_tagstruct_put_sample_spec(t, &fixed_ss);
pa_tagstruct_put_channel_map(t, &s->channel_map);
pa_tagstruct_put_cvolume(t, &s->volume);
- pa_tagstruct_put_usec(t, pa_sink_input_get_latency(s));
- pa_tagstruct_put_usec(t, pa_sink_get_latency(s->sink));
+ pa_tagstruct_put_usec(t, pa_sink_input_get_latency(s, &sink_latency));
+ pa_tagstruct_put_usec(t, sink_latency);
pa_tagstruct_puts(t, pa_resample_method_to_string(pa_sink_input_get_resample_method(s)));
pa_tagstruct_puts(t, s->driver);
+ if (c->version >= 11)
+ pa_tagstruct_put_boolean(t, pa_sink_input_get_mute(s));
+ if (c->version >= 13)
+ pa_tagstruct_put_proplist(t, s->proplist);
}
-static void source_output_fill_tagstruct(pa_tagstruct *t, pa_source_output *s) {
- assert(t && s);
+static void source_output_fill_tagstruct(connection *c, pa_tagstruct *t, pa_source_output *s) {
+ pa_sample_spec fixed_ss;
+ pa_usec_t source_latency;
+
+ pa_assert(t);
+ pa_source_output_assert_ref(s);
+
+ fixup_sample_spec(c, &fixed_ss, &s->sample_spec);
+
pa_tagstruct_putu32(t, s->index);
- pa_tagstruct_puts(t, s->name);
+ pa_tagstruct_puts(t, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_MEDIA_NAME)));
pa_tagstruct_putu32(t, s->module ? s->module->index : PA_INVALID_INDEX);
pa_tagstruct_putu32(t, s->client ? s->client->index : PA_INVALID_INDEX);
pa_tagstruct_putu32(t, s->source->index);
- pa_tagstruct_put_sample_spec(t, &s->sample_spec);
+ pa_tagstruct_put_sample_spec(t, &fixed_ss);
pa_tagstruct_put_channel_map(t, &s->channel_map);
- pa_tagstruct_put_usec(t, pa_source_output_get_latency(s));
- pa_tagstruct_put_usec(t, pa_source_get_latency(s->source));
+ pa_tagstruct_put_usec(t, pa_source_output_get_latency(s, &source_latency));
+ pa_tagstruct_put_usec(t, source_latency);
pa_tagstruct_puts(t, pa_resample_method_to_string(pa_source_output_get_resample_method(s)));
pa_tagstruct_puts(t, s->driver);
+
+ if (c->version >= 13)
+ pa_tagstruct_put_proplist(t, s->proplist);
}
-static void scache_fill_tagstruct(pa_tagstruct *t, pa_scache_entry *e) {
- assert(t && e);
+static void scache_fill_tagstruct(connection *c, pa_tagstruct *t, pa_scache_entry *e) {
+ pa_sample_spec fixed_ss;
+
+ pa_assert(t);
+ pa_assert(e);
+
+ if (e->memchunk.memblock)
+ fixup_sample_spec(c, &fixed_ss, &e->sample_spec);
+ else
+ memset(&fixed_ss, 0, sizeof(fixed_ss));
+
pa_tagstruct_putu32(t, e->index);
pa_tagstruct_puts(t, e->name);
pa_tagstruct_put_cvolume(t, &e->volume);
- pa_tagstruct_put_usec(t, pa_bytes_to_usec(e->memchunk.length, &e->sample_spec));
- pa_tagstruct_put_sample_spec(t, &e->sample_spec);
+ pa_tagstruct_put_usec(t, e->memchunk.memblock ? pa_bytes_to_usec(e->memchunk.length, &e->sample_spec) : 0);
+ pa_tagstruct_put_sample_spec(t, &fixed_ss);
pa_tagstruct_put_channel_map(t, &e->channel_map);
pa_tagstruct_putu32(t, e->memchunk.length);
pa_tagstruct_put_boolean(t, e->lazy);
pa_tagstruct_puts(t, e->filename);
+
+ if (c->version >= 13)
+ pa_tagstruct_put_proplist(t, e->proplist);
}
static void command_get_info(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
uint32_t idx;
pa_sink *sink = NULL;
pa_source *source = NULL;
@@ -1427,7 +2622,9 @@ static void command_get_info(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, u
pa_scache_entry *sce = NULL;
const char *name;
pa_tagstruct *reply;
- assert(c && t);
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (pa_tagstruct_getu32(t, &idx) < 0 ||
(command != PA_COMMAND_GET_CLIENT_INFO &&
@@ -1455,20 +2652,20 @@ static void command_get_info(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, u
source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1);
} else if (command == PA_COMMAND_GET_CLIENT_INFO)
client = pa_idxset_get_by_index(c->protocol->core->clients, idx);
- else if (command == PA_COMMAND_GET_MODULE_INFO)
+ else if (command == PA_COMMAND_GET_MODULE_INFO)
module = pa_idxset_get_by_index(c->protocol->core->modules, idx);
else if (command == PA_COMMAND_GET_SINK_INPUT_INFO)
si = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx);
else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO)
so = pa_idxset_get_by_index(c->protocol->core->source_outputs, idx);
else {
- assert(command == PA_COMMAND_GET_SAMPLE_INFO);
+ pa_assert(command == PA_COMMAND_GET_SAMPLE_INFO);
if (idx != PA_INVALID_INDEX)
sce = pa_idxset_get_by_index(c->protocol->core->scache, idx);
else
sce = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SAMPLE, 0);
}
-
+
if (!sink && !source && !client && !module && !si && !so && !sce) {
pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
return;
@@ -1476,29 +2673,31 @@ static void command_get_info(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, u
reply = reply_new(tag);
if (sink)
- sink_fill_tagstruct(reply, sink);
+ sink_fill_tagstruct(c, reply, sink);
else if (source)
- source_fill_tagstruct(reply, source);
+ source_fill_tagstruct(c, reply, source);
else if (client)
- client_fill_tagstruct(reply, client);
+ client_fill_tagstruct(c, reply, client);
else if (module)
module_fill_tagstruct(reply, module);
else if (si)
- sink_input_fill_tagstruct(reply, si);
+ sink_input_fill_tagstruct(c, reply, si);
else if (so)
- source_output_fill_tagstruct(reply, so);
+ source_output_fill_tagstruct(c, reply, so);
else
- scache_fill_tagstruct(reply, sce);
+ scache_fill_tagstruct(c, reply, sce);
pa_pstream_send_tagstruct(c->pstream, reply);
}
static void command_get_info_list(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
pa_idxset *i;
uint32_t idx;
void *p;
pa_tagstruct *reply;
- assert(c && t);
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (!pa_tagstruct_eof(t)) {
protocol_error(c);
@@ -1522,54 +2721,59 @@ static void command_get_info_list(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t comma
else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST)
i = c->protocol->core->source_outputs;
else {
- assert(command == PA_COMMAND_GET_SAMPLE_INFO_LIST);
+ pa_assert(command == PA_COMMAND_GET_SAMPLE_INFO_LIST);
i = c->protocol->core->scache;
}
if (i) {
for (p = pa_idxset_first(i, &idx); p; p = pa_idxset_next(i, &idx)) {
if (command == PA_COMMAND_GET_SINK_INFO_LIST)
- sink_fill_tagstruct(reply, p);
+ sink_fill_tagstruct(c, reply, p);
else if (command == PA_COMMAND_GET_SOURCE_INFO_LIST)
- source_fill_tagstruct(reply, p);
+ source_fill_tagstruct(c, reply, p);
else if (command == PA_COMMAND_GET_CLIENT_INFO_LIST)
- client_fill_tagstruct(reply, p);
+ client_fill_tagstruct(c, reply, p);
else if (command == PA_COMMAND_GET_MODULE_INFO_LIST)
module_fill_tagstruct(reply, p);
else if (command == PA_COMMAND_GET_SINK_INPUT_INFO_LIST)
- sink_input_fill_tagstruct(reply, p);
- else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST)
- source_output_fill_tagstruct(reply, p);
+ sink_input_fill_tagstruct(c, reply, p);
+ else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST)
+ source_output_fill_tagstruct(c, reply, p);
else {
- assert(command == PA_COMMAND_GET_SAMPLE_INFO_LIST);
- scache_fill_tagstruct(reply, p);
+ pa_assert(command == PA_COMMAND_GET_SAMPLE_INFO_LIST);
+ scache_fill_tagstruct(c, reply, p);
}
}
}
-
+
pa_pstream_send_tagstruct(c->pstream, reply);
}
static void command_get_server_info(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
pa_tagstruct *reply;
char txt[256];
const char *n;
- assert(c && t);
+ pa_sample_spec fixed_ss;
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (!pa_tagstruct_eof(t)) {
protocol_error(c);
return;
}
-
+
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
reply = reply_new(tag);
pa_tagstruct_puts(reply, PACKAGE_NAME);
pa_tagstruct_puts(reply, PACKAGE_VERSION);
pa_tagstruct_puts(reply, pa_get_user_name(txt, sizeof(txt)));
- pa_tagstruct_puts(reply, pa_get_fqdn(txt, sizeof(txt)));
- pa_tagstruct_put_sample_spec(reply, &c->protocol->core->default_sample_spec);
+ pa_tagstruct_puts(reply, pa_get_host_name(txt, sizeof(txt)));
+
+ fixup_sample_spec(c, &fixed_ss, &c->protocol->core->default_sample_spec);
+ pa_tagstruct_put_sample_spec(reply, &fixed_ss);
n = pa_namereg_get_default_sink_name(c->protocol->core);
pa_tagstruct_puts(reply, n);
@@ -1577,14 +2781,15 @@ static void command_get_server_info(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSE
pa_tagstruct_puts(reply, n);
pa_tagstruct_putu32(reply, c->protocol->core->cookie);
-
+
pa_pstream_send_tagstruct(c->pstream, reply);
}
static void subscription_cb(pa_core *core, pa_subscription_event_type_t e, uint32_t idx, void *userdata) {
pa_tagstruct *t;
- struct connection *c = userdata;
- assert(c && core);
+ connection *c = CONNECTION(userdata);
+
+ connection_assert_ref(c);
t = pa_tagstruct_new(NULL, 0);
pa_tagstruct_putu32(t, PA_COMMAND_SUBSCRIBE_EVENT);
@@ -1595,9 +2800,11 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t e, uint3
}
static void command_subscribe(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
pa_subscription_mask_t m;
- assert(c && t);
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (pa_tagstruct_getu32(t, &m) < 0 ||
!pa_tagstruct_eof(t)) {
@@ -1607,13 +2814,13 @@ static void command_subscribe(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
CHECK_VALIDITY(c->pstream, (m & ~PA_SUBSCRIPTION_MASK_ALL) == 0, tag, PA_ERR_INVALID);
-
+
if (c->subscription)
pa_subscription_free(c->subscription);
if (m != 0) {
c->subscription = pa_subscription_new(c->protocol->core, m, subscription_cb, c);
- assert(c->subscription);
+ pa_assert(c->subscription);
} else
c->subscription = NULL;
@@ -1626,15 +2833,17 @@ static void command_set_volume(
uint32_t tag,
pa_tagstruct *t,
void *userdata) {
-
- struct connection *c = userdata;
+
+ connection *c = CONNECTION(userdata);
uint32_t idx;
pa_cvolume volume;
pa_sink *sink = NULL;
pa_source *source = NULL;
pa_sink_input *si = NULL;
const char *name = NULL;
- assert(c && t);
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (pa_tagstruct_getu32(t, &idx) < 0 ||
(command == PA_COMMAND_SET_SINK_VOLUME && pa_tagstruct_gets(t, &name) < 0) ||
@@ -1644,32 +2853,41 @@ static void command_set_volume(
protocol_error(c);
return;
}
-
+
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX || !name || (*name && pa_utf8_valid(name)), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, pa_cvolume_valid(&volume), tag, PA_ERR_INVALID);
- if (command == PA_COMMAND_SET_SINK_VOLUME) {
- if (idx != PA_INVALID_INDEX)
- sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx);
- else
- sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1);
- } else if (command == PA_COMMAND_SET_SOURCE_VOLUME) {
- if (idx != (uint32_t) -1)
- source = pa_idxset_get_by_index(c->protocol->core->sources, idx);
- else
- source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1);
- } else {
- assert(command == PA_COMMAND_SET_SINK_INPUT_VOLUME);
- si = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx);
+ switch (command) {
+
+ case PA_COMMAND_SET_SINK_VOLUME:
+ if (idx != PA_INVALID_INDEX)
+ sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx);
+ else
+ sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1);
+ break;
+
+ case PA_COMMAND_SET_SOURCE_VOLUME:
+ if (idx != PA_INVALID_INDEX)
+ source = pa_idxset_get_by_index(c->protocol->core->sources, idx);
+ else
+ source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1);
+ break;
+
+ case PA_COMMAND_SET_SINK_INPUT_VOLUME:
+ si = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx);
+ break;
+
+ default:
+ pa_assert_not_reached();
}
CHECK_VALIDITY(c->pstream, si || sink || source, tag, PA_ERR_NOENTITY);
if (sink)
- pa_sink_set_volume(sink, PA_MIXER_HARDWARE, &volume);
+ pa_sink_set_volume(sink, &volume);
else if (source)
- pa_source_set_volume(source, PA_MIXER_HARDWARE, &volume);
+ pa_source_set_volume(source, &volume);
else if (si)
pa_sink_input_set_volume(si, &volume);
@@ -1682,55 +2900,77 @@ static void command_set_mute(
uint32_t tag,
pa_tagstruct *t,
void *userdata) {
-
- struct connection *c = userdata;
+
+ connection *c = CONNECTION(userdata);
uint32_t idx;
- int mute;
+ pa_bool_t mute;
pa_sink *sink = NULL;
pa_source *source = NULL;
+ pa_sink_input *si = NULL;
const char *name = NULL;
- assert(c && t);
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (pa_tagstruct_getu32(t, &idx) < 0 ||
- pa_tagstruct_gets(t, &name) < 0 ||
+ (command == PA_COMMAND_SET_SINK_MUTE && pa_tagstruct_gets(t, &name) < 0) ||
+ (command == PA_COMMAND_SET_SOURCE_MUTE && pa_tagstruct_gets(t, &name) < 0) ||
pa_tagstruct_get_boolean(t, &mute) ||
!pa_tagstruct_eof(t)) {
protocol_error(c);
return;
}
-
+
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX || !name || (*name && pa_utf8_valid(name)), tag, PA_ERR_INVALID);
- if (command == PA_COMMAND_SET_SINK_MUTE) {
- if (idx != PA_INVALID_INDEX)
- sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx);
- else
- sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1);
- } else {
- assert(command == PA_COMMAND_SET_SOURCE_MUTE);
- if (idx != (uint32_t) -1)
- source = pa_idxset_get_by_index(c->protocol->core->sources, idx);
- else
- source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1);
+ switch (command) {
+
+ case PA_COMMAND_SET_SINK_MUTE:
+
+ if (idx != PA_INVALID_INDEX)
+ sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx);
+ else
+ sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1);
+
+ break;
+
+ case PA_COMMAND_SET_SOURCE_MUTE:
+ if (idx != PA_INVALID_INDEX)
+ source = pa_idxset_get_by_index(c->protocol->core->sources, idx);
+ else
+ source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1);
+
+ break;
+
+ case PA_COMMAND_SET_SINK_INPUT_MUTE:
+ si = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx);
+ break;
+
+ default:
+ pa_assert_not_reached();
}
- CHECK_VALIDITY(c->pstream, sink || source, tag, PA_ERR_NOENTITY);
+ CHECK_VALIDITY(c->pstream, si || sink || source, tag, PA_ERR_NOENTITY);
if (sink)
- pa_sink_set_mute(sink, PA_MIXER_HARDWARE, mute);
+ pa_sink_set_mute(sink, mute);
else if (source)
- pa_source_set_mute(source, PA_MIXER_HARDWARE, mute);
+ pa_source_set_mute(source, mute);
+ else if (si)
+ pa_sink_input_set_mute(si, mute);
pa_pstream_send_simple_ack(c->pstream, tag);
}
static void command_cork_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
uint32_t idx;
- int b;
- struct playback_stream *s, *ssync;
- assert(c && t);
+ pa_bool_t b;
+ playback_stream *s;
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (pa_tagstruct_getu32(t, &idx) < 0 ||
pa_tagstruct_get_boolean(t, &b) < 0 ||
@@ -1743,73 +2983,19 @@ static void command_cork_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX, tag, PA_ERR_INVALID);
s = pa_idxset_get_by_index(c->output_streams, idx);
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
- CHECK_VALIDITY(c->pstream, s->type == PLAYBACK_STREAM, tag, PA_ERR_NOENTITY);
+ CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
pa_sink_input_cork(s->sink_input, b);
- pa_memblockq_prebuf_force(s->memblockq);
-
- /* Do the same for all other members in the sync group */
- for (ssync = s->prev; ssync; ssync = ssync->prev) {
- pa_sink_input_cork(ssync->sink_input, b);
- pa_memblockq_prebuf_force(ssync->memblockq);
- }
-
- for (ssync = s->next; ssync; ssync = ssync->next) {
- pa_sink_input_cork(ssync->sink_input, b);
- pa_memblockq_prebuf_force(ssync->memblockq);
- }
-
- pa_pstream_send_simple_ack(c->pstream, tag);
-}
-
-static void command_flush_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- struct connection *c = userdata;
- uint32_t idx;
- struct playback_stream *s, *ssync;
- assert(c && t);
-
- if (pa_tagstruct_getu32(t, &idx) < 0 ||
- !pa_tagstruct_eof(t)) {
- protocol_error(c);
- return;
- }
-
- CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
- CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX, tag, PA_ERR_INVALID);
- s = pa_idxset_get_by_index(c->output_streams, idx);
- CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
- CHECK_VALIDITY(c->pstream, s->type == PLAYBACK_STREAM, tag, PA_ERR_NOENTITY);
-
- pa_memblockq_flush(s->memblockq);
- s->underrun = 0;
-
- /* Do the same for all other members in the sync group */
- for (ssync = s->prev; ssync; ssync = ssync->prev) {
- pa_memblockq_flush(ssync->memblockq);
- ssync->underrun = 0;
- }
-
- for (ssync = s->next; ssync; ssync = ssync->next) {
- pa_memblockq_flush(ssync->memblockq);
- ssync->underrun = 0;
- }
-
pa_pstream_send_simple_ack(c->pstream, tag);
- pa_sink_notify(s->sink_input->sink);
- request_bytes(s);
-
- for (ssync = s->prev; ssync; ssync = ssync->prev)
- request_bytes(ssync);
-
- for (ssync = s->next; ssync; ssync = ssync->next)
- request_bytes(ssync);
}
-static void command_trigger_or_prebuf_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- struct connection *c = userdata;
+static void command_trigger_or_flush_or_prebuf_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ connection *c = CONNECTION(userdata);
uint32_t idx;
- struct playback_stream *s;
- assert(c && t);
+ playback_stream *s;
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (pa_tagstruct_getu32(t, &idx) < 0 ||
!pa_tagstruct_eof(t)) {
@@ -1821,32 +3007,36 @@ static void command_trigger_or_prebuf_playback_stream(PA_GCC_UNUSED pa_pdispatch
CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX, tag, PA_ERR_INVALID);
s = pa_idxset_get_by_index(c->output_streams, idx);
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
- CHECK_VALIDITY(c->pstream, s->type == PLAYBACK_STREAM, tag, PA_ERR_NOENTITY);
+ CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
switch (command) {
+ case PA_COMMAND_FLUSH_PLAYBACK_STREAM:
+ pa_asyncmsgq_send(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_FLUSH, NULL, 0, NULL);
+ break;
+
case PA_COMMAND_PREBUF_PLAYBACK_STREAM:
- pa_memblockq_prebuf_force(s->memblockq);
+ pa_asyncmsgq_send(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_PREBUF_FORCE, NULL, 0, NULL);
break;
-
+
case PA_COMMAND_TRIGGER_PLAYBACK_STREAM:
- pa_memblockq_prebuf_disable(s->memblockq);
+ pa_asyncmsgq_send(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_TRIGGER, NULL, 0, NULL);
break;
-
+
default:
- abort();
+ pa_assert_not_reached();
}
- pa_sink_notify(s->sink_input->sink);
pa_pstream_send_simple_ack(c->pstream, tag);
- request_bytes(s);
}
static void command_cork_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
uint32_t idx;
- struct record_stream *s;
- int b;
- assert(c && t);
+ record_stream *s;
+ pa_bool_t b;
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (pa_tagstruct_getu32(t, &idx) < 0 ||
pa_tagstruct_get_boolean(t, &b) < 0 ||
@@ -1865,10 +3055,12 @@ static void command_cork_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UN
}
static void command_flush_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
uint32_t idx;
- struct record_stream *s;
- assert(c && t);
+ record_stream *s;
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (pa_tagstruct_getu32(t, &idx) < 0 ||
!pa_tagstruct_eof(t)) {
@@ -1884,10 +3076,301 @@ static void command_flush_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_U
pa_pstream_send_simple_ack(c->pstream, tag);
}
+static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ connection *c = CONNECTION(userdata);
+ uint32_t idx;
+ uint32_t maxlength, tlength, prebuf, minreq, fragsize;
+ pa_tagstruct *reply;
+
+ connection_assert_ref(c);
+ pa_assert(t);
+
+ if (pa_tagstruct_getu32(t, &idx) < 0) {
+ protocol_error(c);
+ return;
+ }
+
+ CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+
+ if (command == PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR) {
+ playback_stream *s;
+ pa_bool_t adjust_latency = FALSE;
+
+ s = pa_idxset_get_by_index(c->output_streams, idx);
+ CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+ CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
+
+ if (pa_tagstruct_get(
+ t,
+ PA_TAG_U32, &maxlength,
+ PA_TAG_U32, &tlength,
+ PA_TAG_U32, &prebuf,
+ PA_TAG_U32, &minreq,
+ PA_TAG_INVALID) < 0 ||
+ (c->version >= 13 && pa_tagstruct_get_boolean(t, &adjust_latency) < 0) ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ fix_playback_buffer_attr_pre(s, adjust_latency, &maxlength, &tlength, &prebuf, &minreq);
+ pa_memblockq_set_maxlength(s->memblockq, maxlength);
+ pa_memblockq_set_tlength(s->memblockq, tlength);
+ pa_memblockq_set_prebuf(s->memblockq, prebuf);
+ pa_memblockq_set_minreq(s->memblockq, minreq);
+ fix_playback_buffer_attr_post(s, &maxlength, &tlength, &prebuf, &minreq);
+
+ reply = reply_new(tag);
+ pa_tagstruct_putu32(reply, maxlength);
+ pa_tagstruct_putu32(reply, tlength);
+ pa_tagstruct_putu32(reply, prebuf);
+ pa_tagstruct_putu32(reply, minreq);
+
+ if (c->version >= 13)
+ pa_tagstruct_put_usec(reply, s->sink_latency);
+
+ } else {
+ record_stream *s;
+ pa_bool_t adjust_latency = FALSE;
+ pa_assert(command == PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR);
+
+ s = pa_idxset_get_by_index(c->record_streams, idx);
+ CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+
+ if (pa_tagstruct_get(
+ t,
+ PA_TAG_U32, &maxlength,
+ PA_TAG_U32, &fragsize,
+ PA_TAG_INVALID) < 0 ||
+ (c->version >= 13 && pa_tagstruct_get_boolean(t, &adjust_latency) < 0) ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ fix_record_buffer_attr_pre(s, adjust_latency, &maxlength, &fragsize);
+ pa_memblockq_set_maxlength(s->memblockq, maxlength);
+ fix_record_buffer_attr_post(s, &maxlength, &fragsize);
+
+ reply = reply_new(tag);
+ pa_tagstruct_putu32(reply, maxlength);
+ pa_tagstruct_putu32(reply, fragsize);
+
+ if (c->version >= 13)
+ pa_tagstruct_put_usec(reply, s->source_latency);
+ }
+
+ pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
+static void command_update_stream_sample_rate(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ connection *c = CONNECTION(userdata);
+ uint32_t idx;
+ uint32_t rate;
+
+ connection_assert_ref(c);
+ pa_assert(t);
+
+ if (pa_tagstruct_getu32(t, &idx) < 0 ||
+ pa_tagstruct_getu32(t, &rate) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+ CHECK_VALIDITY(c->pstream, rate > 0 && rate <= PA_RATE_MAX, tag, PA_ERR_INVALID);
+
+ if (command == PA_COMMAND_UPDATE_PLAYBACK_STREAM_SAMPLE_RATE) {
+ playback_stream *s;
+
+ s = pa_idxset_get_by_index(c->output_streams, idx);
+ CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+ CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
+
+ pa_sink_input_set_rate(s->sink_input, rate);
+
+ } else {
+ record_stream *s;
+ pa_assert(command == PA_COMMAND_UPDATE_RECORD_STREAM_SAMPLE_RATE);
+
+ s = pa_idxset_get_by_index(c->record_streams, idx);
+ CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+
+ pa_source_output_set_rate(s->source_output, rate);
+ }
+
+ pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_update_proplist(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ connection *c = CONNECTION(userdata);
+ uint32_t idx;
+ uint32_t mode;
+ pa_proplist *p;
+
+ connection_assert_ref(c);
+ pa_assert(t);
+
+ CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+
+ p = pa_proplist_new();
+
+ if (command == PA_COMMAND_UPDATE_CLIENT_PROPLIST) {
+
+ if (pa_tagstruct_getu32(t, &mode) < 0 ||
+ pa_tagstruct_get_proplist(t, p) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ pa_proplist_free(p);
+ return;
+ }
+
+ } else {
+
+ if (pa_tagstruct_getu32(t, &idx) < 0 ||
+ pa_tagstruct_getu32(t, &mode) < 0 ||
+ pa_tagstruct_get_proplist(t, p) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ pa_proplist_free(p);
+ return;
+ }
+ }
+
+ CHECK_VALIDITY(c->pstream, mode == PA_UPDATE_SET || mode == PA_UPDATE_MERGE || mode == PA_UPDATE_REPLACE, tag, PA_ERR_INVALID);
+
+ if (command == PA_COMMAND_UPDATE_PLAYBACK_STREAM_PROPLIST) {
+ playback_stream *s;
+
+ s = pa_idxset_get_by_index(c->output_streams, idx);
+ CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+ CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
+
+ pa_proplist_update(s->sink_input->proplist, mode, p);
+ pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, s->sink_input->index);
+
+ } else if (command == PA_COMMAND_UPDATE_RECORD_STREAM_PROPLIST) {
+ record_stream *s;
+
+ s = pa_idxset_get_by_index(c->record_streams, idx);
+ CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+
+ pa_proplist_update(s->source_output->proplist, mode, p);
+ pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, s->source_output->index);
+ } else {
+ pa_assert(command == PA_COMMAND_UPDATE_CLIENT_PROPLIST);
+
+ pa_proplist_update(c->client->proplist, mode, p);
+ pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->client->index);
+ }
+
+ pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_remove_proplist(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ connection *c = CONNECTION(userdata);
+ uint32_t idx;
+ unsigned changed = 0;
+ pa_proplist *p;
+ pa_strlist *l = NULL;
+
+ connection_assert_ref(c);
+ pa_assert(t);
+
+ CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+
+ if (command != PA_COMMAND_REMOVE_CLIENT_PROPLIST) {
+
+ if (pa_tagstruct_getu32(t, &idx) < 0) {
+ protocol_error(c);
+ return;
+ }
+ }
+
+ if (command == PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST) {
+ playback_stream *s;
+
+ s = pa_idxset_get_by_index(c->output_streams, idx);
+ CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+ CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
+
+ p = s->sink_input->proplist;
+
+ } else if (command == PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST) {
+ record_stream *s;
+
+ s = pa_idxset_get_by_index(c->record_streams, idx);
+ CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+
+ p = s->source_output->proplist;
+ } else {
+ pa_assert(command == PA_COMMAND_REMOVE_CLIENT_PROPLIST);
+
+ p = c->client->proplist;
+ }
+
+ for (;;) {
+ const char *k;
+
+ if (pa_tagstruct_gets(t, &k) < 0) {
+ protocol_error(c);
+ pa_strlist_free(l);
+ return;
+ }
+
+ if (!k)
+ break;
+
+ l = pa_strlist_prepend(l, k);
+ }
+
+ if (!pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ pa_strlist_free(l);
+ return;
+ }
+
+ for (;;) {
+ char *z;
+
+ l = pa_strlist_pop(l, &z);
+
+ if (!z)
+ break;
+
+ changed += pa_proplist_unset(p, z) >= 0;
+ pa_xfree(z);
+ }
+
+ pa_pstream_send_simple_ack(c->pstream, tag);
+
+ if (changed) {
+ if (command == PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST) {
+ playback_stream *s;
+
+ s = pa_idxset_get_by_index(c->output_streams, idx);
+ pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, s->sink_input->index);
+
+ } else if (command == PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST) {
+ record_stream *s;
+
+ s = pa_idxset_get_by_index(c->record_streams, idx);
+ pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, s->source_output->index);
+
+ } else {
+ pa_assert(command == PA_COMMAND_REMOVE_CLIENT_PROPLIST);
+ pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->client->index);
+ }
+ }
+}
+
static void command_set_default_sink_or_source(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
const char *s;
- assert(c && t);
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (pa_tagstruct_gets(t, &s) < 0 ||
!pa_tagstruct_eof(t)) {
@@ -1903,10 +3386,12 @@ static void command_set_default_sink_or_source(PA_GCC_UNUSED pa_pdispatch *pd, u
}
static void command_set_stream_name(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
uint32_t idx;
const char *name;
- assert(c && t);
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (pa_tagstruct_getu32(t, &idx) < 0 ||
pa_tagstruct_gets(t, &name) < 0 ||
@@ -1914,22 +3399,23 @@ static void command_set_stream_name(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t com
protocol_error(c);
return;
}
-
+
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
CHECK_VALIDITY(c->pstream, name && pa_utf8_valid(name), tag, PA_ERR_INVALID);
if (command == PA_COMMAND_SET_PLAYBACK_STREAM_NAME) {
- struct playback_stream *s;
-
+ playback_stream *s;
+
s = pa_idxset_get_by_index(c->output_streams, idx);
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
- CHECK_VALIDITY(c->pstream, s->type == PLAYBACK_STREAM, tag, PA_ERR_NOENTITY);
+ CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
pa_sink_input_set_name(s->sink_input, name);
-
+
} else {
- struct record_stream *s;
-
+ record_stream *s;
+ pa_assert(command == PA_COMMAND_SET_RECORD_STREAM_NAME);
+
s = pa_idxset_get_by_index(c->record_streams, idx);
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
@@ -1940,9 +3426,11 @@ static void command_set_stream_name(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t com
}
static void command_kill(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
uint32_t idx;
- assert(c && t);
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (pa_tagstruct_getu32(t, &idx) < 0 ||
!pa_tagstruct_eof(t)) {
@@ -1954,38 +3442,45 @@ static void command_kill(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint3
if (command == PA_COMMAND_KILL_CLIENT) {
pa_client *client;
-
+
client = pa_idxset_get_by_index(c->protocol->core->clients, idx);
CHECK_VALIDITY(c->pstream, client, tag, PA_ERR_NOENTITY);
+
+ connection_ref(c);
pa_client_kill(client);
-
+
} else if (command == PA_COMMAND_KILL_SINK_INPUT) {
pa_sink_input *s;
-
+
s = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx);
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+ connection_ref(c);
pa_sink_input_kill(s);
} else {
pa_source_output *s;
- assert(command == PA_COMMAND_KILL_SOURCE_OUTPUT);
-
+ pa_assert(command == PA_COMMAND_KILL_SOURCE_OUTPUT);
+
s = pa_idxset_get_by_index(c->protocol->core->source_outputs, idx);
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+ connection_ref(c);
pa_source_output_kill(s);
}
pa_pstream_send_simple_ack(c->pstream, tag);
+ connection_unref(c);
}
static void command_load_module(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
pa_module *m;
const char *name, *argument;
pa_tagstruct *reply;
- assert(c && t);
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (pa_tagstruct_gets(t, &name) < 0 ||
pa_tagstruct_gets(t, &argument) < 0 ||
@@ -1997,7 +3492,7 @@ static void command_load_module(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED ui
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
CHECK_VALIDITY(c->pstream, name && *name && pa_utf8_valid(name) && !strchr(name, '/'), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, !argument || pa_utf8_valid(argument), tag, PA_ERR_INVALID);
-
+
if (!(m = pa_module_load(c->protocol->core, name, argument))) {
pa_pstream_send_error(c->pstream, tag, PA_ERR_MODINITFAILED);
return;
@@ -2009,17 +3504,19 @@ static void command_load_module(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED ui
}
static void command_unload_module(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
uint32_t idx;
pa_module *m;
- assert(c && t);
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (pa_tagstruct_getu32(t, &idx) < 0 ||
!pa_tagstruct_eof(t)) {
protocol_error(c);
return;
}
-
+
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
m = pa_idxset_get_by_index(c->protocol->core->modules, idx);
CHECK_VALIDITY(c->pstream, m, tag, PA_ERR_NOENTITY);
@@ -2029,22 +3526,24 @@ static void command_unload_module(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED
}
static void command_add_autoload(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
const char *name, *module, *argument;
uint32_t type;
uint32_t idx;
pa_tagstruct *reply;
- assert(c && t);
- if (pa_tagstruct_gets(t, &name) < 0 ||
- pa_tagstruct_getu32(t, &type) < 0 ||
- pa_tagstruct_gets(t, &module) < 0 ||
+ connection_assert_ref(c);
+ pa_assert(t);
+
+ if (pa_tagstruct_gets(t, &name) < 0 ||
+ pa_tagstruct_getu32(t, &type) < 0 ||
+ pa_tagstruct_gets(t, &module) < 0 ||
pa_tagstruct_gets(t, &argument) < 0 ||
!pa_tagstruct_eof(t)) {
protocol_error(c);
return;
}
-
+
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
CHECK_VALIDITY(c->pstream, name && *name && pa_utf8_valid(name), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, type == 0 || type == 1, tag, PA_ERR_INVALID);
@@ -2062,11 +3561,13 @@ static void command_add_autoload(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED u
}
static void command_remove_autoload(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
const char *name = NULL;
uint32_t type, idx = PA_IDXSET_INVALID;
int r;
- assert(c && t);
+
+ connection_assert_ref(c);
+ pa_assert(t);
if ((pa_tagstruct_getu32(t, &idx) < 0 &&
(pa_tagstruct_gets(t, &name) < 0 ||
@@ -2075,12 +3576,12 @@ static void command_remove_autoload(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSE
protocol_error(c);
return;
}
-
+
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
CHECK_VALIDITY(c->pstream, name || idx != PA_IDXSET_INVALID, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, !name || (*name && pa_utf8_valid(name) && (type == 0 || type == 1)), tag, PA_ERR_INVALID);
- if (name)
+ if (name)
r = pa_autoload_remove_by_name(c->protocol->core, name, type == 0 ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE);
else
r = pa_autoload_remove_by_index(c->protocol->core, idx);
@@ -2091,7 +3592,7 @@ static void command_remove_autoload(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSE
}
static void autoload_fill_tagstruct(pa_tagstruct *t, const pa_autoload_entry *e) {
- assert(t && e);
+ pa_assert(t && e);
pa_tagstruct_putu32(t, e->index);
pa_tagstruct_puts(t, e->name);
@@ -2101,12 +3602,14 @@ static void autoload_fill_tagstruct(pa_tagstruct *t, const pa_autoload_entry *e)
}
static void command_get_autoload_info(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
const pa_autoload_entry *a = NULL;
uint32_t type, idx;
const char *name;
pa_tagstruct *reply;
- assert(c && t);
+
+ connection_assert_ref(c);
+ pa_assert(t);
if ((pa_tagstruct_getu32(t, &idx) < 0 &&
(pa_tagstruct_gets(t, &name) < 0 ||
@@ -2133,15 +3636,17 @@ static void command_get_autoload_info(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNU
}
static void command_get_autoload_info_list(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
pa_tagstruct *reply;
- assert(c && t);
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (!pa_tagstruct_eof(t)) {
protocol_error(c);
return;
}
-
+
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
reply = reply_new(tag);
@@ -2153,17 +3658,17 @@ static void command_get_autoload_info_list(PA_GCC_UNUSED pa_pdispatch *pd, PA_GC
while ((a = pa_hashmap_iterate(c->protocol->core->autoload_hashmap, &state, NULL)))
autoload_fill_tagstruct(reply, a);
}
-
+
pa_pstream_send_tagstruct(c->pstream, reply);
}
static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
uint32_t idx = PA_INVALID_INDEX, idx_device = PA_INVALID_INDEX;
const char *name = NULL;
-
- assert(c);
- assert(t);
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (pa_tagstruct_getu32(t, &idx) < 0 ||
pa_tagstruct_getu32(t, &idx_device) < 0 ||
@@ -2172,7 +3677,7 @@ static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag
protocol_error(c);
return;
}
-
+
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, idx_device != PA_INVALID_INDEX || !name || (*name && pa_utf8_valid(name)), tag, PA_ERR_INVALID);
@@ -2182,7 +3687,7 @@ static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag
pa_sink *sink = NULL;
si = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx);
-
+
if (idx_device != PA_INVALID_INDEX)
sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx_device);
else
@@ -2190,7 +3695,7 @@ static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag
CHECK_VALIDITY(c->pstream, si && sink, tag, PA_ERR_NOENTITY);
- if (pa_sink_input_move_to(si, sink, 0) < 0) {
+ if (pa_sink_input_move_to(si, sink) < 0) {
pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
return;
}
@@ -2198,8 +3703,10 @@ static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag
pa_source_output *so = NULL;
pa_source *source;
+ pa_assert(command == PA_COMMAND_MOVE_SOURCE_OUTPUT);
+
so = pa_idxset_get_by_index(c->protocol->core->source_outputs, idx);
-
+
if (idx_device != PA_INVALID_INDEX)
source = pa_idxset_get_by_index(c->protocol->core->sources, idx_device);
else
@@ -2212,70 +3719,124 @@ static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag
return;
}
}
-
+
+ pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ connection *c = CONNECTION(userdata);
+ uint32_t idx = PA_INVALID_INDEX;
+ const char *name = NULL;
+ pa_bool_t b;
+
+ connection_assert_ref(c);
+ pa_assert(t);
+
+ if (pa_tagstruct_getu32(t, &idx) < 0 ||
+ pa_tagstruct_gets(t, &name) < 0 ||
+ pa_tagstruct_get_boolean(t, &b) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+ CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX || !name || !*name || pa_utf8_valid(name), tag, PA_ERR_INVALID);
+
+ if (command == PA_COMMAND_SUSPEND_SINK) {
+
+ if (idx == PA_INVALID_INDEX && name && !*name) {
+
+ if (pa_sink_suspend_all(c->protocol->core, b) < 0) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
+ return;
+ }
+ } else {
+ pa_sink *sink = NULL;
+
+ if (idx != PA_INVALID_INDEX)
+ sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx);
+ else
+ sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1);
+
+ CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
+
+ if (pa_sink_suspend(sink, b) < 0) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
+ return;
+ }
+ }
+ } else {
+
+ pa_assert(command == PA_COMMAND_SUSPEND_SOURCE);
+
+ if (idx == PA_INVALID_INDEX && name && !*name) {
+
+ if (pa_source_suspend_all(c->protocol->core, b) < 0) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
+ return;
+ }
+
+ } else {
+ pa_source *source;
+
+ if (idx != PA_INVALID_INDEX)
+ source = pa_idxset_get_by_index(c->protocol->core->sources, idx);
+ else
+ source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1);
+
+ CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY);
+
+ if (pa_source_suspend(source, b) < 0) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
+ return;
+ }
+ }
+ }
+
pa_pstream_send_simple_ack(c->pstream, tag);
-
}
/*** pstream callbacks ***/
static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, const pa_creds *creds, void *userdata) {
- struct connection *c = userdata;
- assert(p && packet && packet->data && c);
+ connection *c = CONNECTION(userdata);
+
+ pa_assert(p);
+ pa_assert(packet);
+ connection_assert_ref(c);
if (pa_pdispatch_run(c->pdispatch, packet, creds, c) < 0) {
pa_log("invalid packet.");
- connection_free(c);
+ connection_unlink(c);
}
}
static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk, void *userdata) {
- struct connection *c = userdata;
- struct output_stream *stream;
- assert(p && chunk && userdata);
-
- if (!(stream = pa_idxset_get_by_index(c->output_streams, channel))) {
+ connection *c = CONNECTION(userdata);
+ output_stream *stream;
+
+ pa_assert(p);
+ pa_assert(chunk);
+ connection_assert_ref(c);
+
+ if (!(stream = OUTPUT_STREAM(pa_idxset_get_by_index(c->output_streams, channel)))) {
pa_log("client sent block for invalid stream.");
/* Ignoring */
return;
}
- if (stream->type == PLAYBACK_STREAM) {
- struct playback_stream *ps = (struct playback_stream*) stream;
- if (chunk->length >= ps->requested_bytes)
- ps->requested_bytes = 0;
- else
- ps->requested_bytes -= chunk->length;
-
- pa_memblockq_seek(ps->memblockq, offset, seek);
-
- if (pa_memblockq_push_align(ps->memblockq, chunk) < 0) {
- pa_tagstruct *t;
-
- pa_log_warn("failed to push data into queue");
-
- /* Pushing this block into the queue failed, so we simulate
- * it by skipping ahead */
-
- pa_memblockq_seek(ps->memblockq, chunk->length, PA_SEEK_RELATIVE);
+ if (playback_stream_isinstance(stream)) {
+ playback_stream *ps = PLAYBACK_STREAM(stream);
- /* Notify the user */
- t = pa_tagstruct_new(NULL, 0);
- pa_tagstruct_putu32(t, PA_COMMAND_OVERFLOW);
- pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
- pa_tagstruct_putu32(t, ps->index);
- pa_pstream_send_tagstruct(p, t);
- }
+ if (seek != PA_SEEK_RELATIVE || offset != 0)
+ pa_asyncmsgq_post(ps->sink_input->sink->asyncmsgq, PA_MSGOBJECT(ps->sink_input), SINK_INPUT_MESSAGE_SEEK, PA_UINT_TO_PTR(seek), offset, NULL, NULL);
- ps->underrun = 0;
-
- pa_sink_notify(ps->sink_input->sink);
+ pa_asyncmsgq_post(ps->sink_input->sink->asyncmsgq, PA_MSGOBJECT(ps->sink_input), SINK_INPUT_MESSAGE_POST_DATA, NULL, 0, chunk, NULL);
} else {
- struct upload_stream *u = (struct upload_stream*) stream;
+ upload_stream *u = UPLOAD_STREAM(stream);
size_t l;
-
- assert(u->type == UPLOAD_STREAM);
if (!u->memchunk.memblock) {
if (u->length == chunk->length) {
@@ -2287,25 +3848,25 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o
u->memchunk.index = u->memchunk.length = 0;
}
}
-
- assert(u->memchunk.memblock);
-
- l = u->length;
+
+ pa_assert(u->memchunk.memblock);
+
+ l = u->length;
if (l > chunk->length)
l = chunk->length;
-
+
if (l > 0) {
void *src, *dst;
dst = pa_memblock_acquire(u->memchunk.memblock);
src = pa_memblock_acquire(chunk->memblock);
-
+
memcpy((uint8_t*) dst + u->memchunk.index + u->memchunk.length,
(uint8_t*) src+chunk->index, l);
pa_memblock_release(u->memchunk.memblock);
pa_memblock_release(chunk->memblock);
-
+
u->memchunk.length += l;
u->length -= l;
}
@@ -2313,43 +3874,71 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o
}
static void pstream_die_callback(pa_pstream *p, void *userdata) {
- struct connection *c = userdata;
- assert(p && c);
- connection_free(c);
+ connection *c = CONNECTION(userdata);
-/* pa_log("connection died.");*/
-}
+ pa_assert(p);
+ connection_assert_ref(c);
+ connection_unlink(c);
+ pa_log_info("connection died.");
+}
static void pstream_drain_callback(pa_pstream *p, void *userdata) {
- struct connection *c = userdata;
- assert(p && c);
+ connection *c = CONNECTION(userdata);
+
+ pa_assert(p);
+ connection_assert_ref(c);
send_memblock(c);
}
+static void pstream_revoke_callback(pa_pstream *p, uint32_t block_id, void *userdata) {
+ pa_thread_mq *q;
+
+ if (!(q = pa_thread_mq_get()))
+ pa_pstream_send_revoke(p, block_id);
+ else
+ pa_asyncmsgq_post(q->outq, PA_MSGOBJECT(userdata), CONNECTION_MESSAGE_REVOKE, PA_UINT_TO_PTR(block_id), 0, NULL, NULL);
+}
+
+static void pstream_release_callback(pa_pstream *p, uint32_t block_id, void *userdata) {
+ pa_thread_mq *q;
+
+ if (!(q = pa_thread_mq_get()))
+ pa_pstream_send_release(p, block_id);
+ else
+ pa_asyncmsgq_post(q->outq, PA_MSGOBJECT(userdata), CONNECTION_MESSAGE_RELEASE, PA_UINT_TO_PTR(block_id), 0, NULL, NULL);
+}
+
/*** client callbacks ***/
static void client_kill_cb(pa_client *c) {
- assert(c && c->userdata);
- connection_free(c->userdata);
+ pa_assert(c);
+
+ connection_unlink(CONNECTION(c->userdata));
}
/*** socket server callbacks ***/
static void auth_timeout(pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata) {
- struct connection *c = userdata;
- assert(m && tv && c && c->auth_timeout_event == e);
+ connection *c = CONNECTION(userdata);
+
+ pa_assert(m);
+ pa_assert(tv);
+ connection_assert_ref(c);
+ pa_assert(c->auth_timeout_event == e);
if (!c->authorized)
- connection_free(c);
+ connection_unlink(c);
}
static void on_connection(PA_GCC_UNUSED pa_socket_server*s, pa_iochannel *io, void *userdata) {
pa_protocol_native *p = userdata;
- struct connection *c;
+ connection *c;
char cname[256], pname[128];
- assert(io && p);
+
+ pa_assert(io);
+ pa_assert(p);
if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {
pa_log_warn("Warning! Too many connections (%u), dropping incoming connection.", MAX_CONNECTIONS);
@@ -2357,15 +3946,17 @@ static void on_connection(PA_GCC_UNUSED pa_socket_server*s, pa_iochannel *io, vo
return;
}
- c = pa_xmalloc(sizeof(struct connection));
+ c = pa_msgobject_new(connection);
+ c->parent.parent.free = connection_free;
+ c->parent.process_msg = connection_process_msg;
- c->authorized = !!p->public;
+ c->authorized = p->public;
if (!c->authorized && p->auth_ip_acl && pa_ip_acl_check(p->auth_ip_acl, pa_iochannel_get_recv_fd(io)) > 0) {
pa_log_info("Client authenticated by IP ACL.");
- c->authorized = 1;
+ c->authorized = TRUE;
}
-
+
if (!c->authorized) {
struct timeval tv;
pa_gettimeofday(&tv);
@@ -2374,59 +3965,56 @@ static void on_connection(PA_GCC_UNUSED pa_socket_server*s, pa_iochannel *io, vo
} else
c->auth_timeout_event = NULL;
+ c->is_local = pa_iochannel_socket_is_local(io);
c->version = 8;
c->protocol = p;
pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname));
- snprintf(cname, sizeof(cname), "Native client (%s)", pname);
- assert(p->core);
+ pa_snprintf(cname, sizeof(cname), "Native client (%s)", pname);
c->client = pa_client_new(p->core, __FILE__, cname);
- assert(c->client);
+ pa_proplist_sets(c->client->proplist, "native-protocol.peer", pname);
c->client->kill = client_kill_cb;
c->client->userdata = c;
- c->client->owner = p->module;
-
+ c->client->module = p->module;
+
c->pstream = pa_pstream_new(p->core->mainloop, io, p->core->mempool);
- assert(c->pstream);
pa_pstream_set_recieve_packet_callback(c->pstream, pstream_packet_callback, c);
pa_pstream_set_recieve_memblock_callback(c->pstream, pstream_memblock_callback, c);
pa_pstream_set_die_callback(c->pstream, pstream_die_callback, c);
pa_pstream_set_drain_callback(c->pstream, pstream_drain_callback, c);
+ pa_pstream_set_revoke_callback(c->pstream, pstream_revoke_callback, c);
+ pa_pstream_set_release_callback(c->pstream, pstream_release_callback, c);
c->pdispatch = pa_pdispatch_new(p->core->mainloop, command_table, PA_COMMAND_MAX);
- assert(c->pdispatch);
c->record_streams = pa_idxset_new(NULL, NULL);
c->output_streams = pa_idxset_new(NULL, NULL);
- assert(c->record_streams && c->output_streams);
c->rrobin_index = PA_IDXSET_INVALID;
c->subscription = NULL;
pa_idxset_put(p->connections, c, NULL);
-
#ifdef HAVE_CREDS
if (pa_iochannel_creds_supported(io))
pa_iochannel_creds_enable(io);
-
#endif
}
/*** module entry points ***/
static int load_key(pa_protocol_native*p, const char*fn) {
- assert(p);
+ pa_assert(p);
+
+ p->auth_cookie_in_property = FALSE;
- p->auth_cookie_in_property = 0;
-
if (!fn && pa_authkey_prop_get(p->core, PA_NATIVE_COOKIE_PROPERTY_NAME, p->auth_cookie, sizeof(p->auth_cookie)) >= 0) {
pa_log_info("using already loaded auth cookie.");
pa_authkey_prop_ref(p->core, PA_NATIVE_COOKIE_PROPERTY_NAME);
- p->auth_cookie_in_property = 1;
+ p->auth_cookie_in_property = TRUE;
return 0;
}
-
+
if (!fn)
fn = PA_NATIVE_COOKIE_FILE;
@@ -2436,39 +4024,39 @@ static int load_key(pa_protocol_native*p, const char*fn) {
pa_log_info("loading cookie from disk.");
if (pa_authkey_prop_put(p->core, PA_NATIVE_COOKIE_PROPERTY_NAME, p->auth_cookie, sizeof(p->auth_cookie)) >= 0)
- p->auth_cookie_in_property = 1;
-
+ p->auth_cookie_in_property = TRUE;
+
return 0;
}
static pa_protocol_native* protocol_new_internal(pa_core *c, pa_module *m, pa_modargs *ma) {
pa_protocol_native *p;
- int public = 0;
+ pa_bool_t public = FALSE;
const char *acl;
-
- assert(c);
- assert(ma);
+
+ pa_assert(c);
+ pa_assert(ma);
if (pa_modargs_get_value_boolean(ma, "auth-anonymous", &public) < 0) {
pa_log("auth-anonymous= expects a boolean argument.");
return NULL;
}
-
+
p = pa_xnew(pa_protocol_native, 1);
p->core = c;
p->module = m;
p->public = public;
p->server = NULL;
p->auth_ip_acl = NULL;
-
+
#ifdef HAVE_CREDS
{
- int a = 1;
+ pa_bool_t a = TRUE;
if (pa_modargs_get_value_boolean(ma, "auth-group-enabled", &a) < 0) {
pa_log("auth-group-enabled= expects a boolean argument.");
return NULL;
}
- p->auth_group = a ? pa_xstrdup(pa_modargs_get_value(ma, "auth-group", c->is_system_instance ? PA_ACCESS_GROUP : NULL)) : NULL;
+ p->auth_group = a ? pa_xstrdup(pa_modargs_get_value(ma, "auth-group", pa_in_system_mode() ? PA_ACCESS_GROUP : NULL)) : NULL;
if (p->auth_group)
pa_log_info("Allowing access to group '%s'.", p->auth_group);
@@ -2488,7 +4076,6 @@ static pa_protocol_native* protocol_new_internal(pa_core *c, pa_module *m, pa_mo
goto fail;
p->connections = pa_idxset_new(NULL, NULL);
- assert(p->connections);
return p;
@@ -2508,8 +4095,8 @@ pa_protocol_native* pa_protocol_native_new(pa_core *core, pa_socket_server *serv
if (!(p = protocol_new_internal(core, m, ma)))
return NULL;
-
- p->server = server;
+
+ p->server = pa_socket_server_ref(server);
pa_socket_server_set_callback(p->server, on_connection, p);
if (pa_socket_server_get_address(p->server, t, sizeof(t))) {
@@ -2518,21 +4105,21 @@ pa_protocol_native* pa_protocol_native_new(pa_core *core, pa_socket_server *serv
l = pa_strlist_prepend(l, t);
pa_property_replace(core, PA_NATIVE_SERVER_PROPERTY_NAME, l);
}
-
+
return p;
}
void pa_protocol_native_free(pa_protocol_native *p) {
- struct connection *c;
- assert(p);
+ connection *c;
+ pa_assert(p);
while ((c = pa_idxset_first(p->connections, NULL)))
- connection_free(c);
+ connection_unlink(c);
pa_idxset_free(p->connections, NULL, NULL);
if (p->server) {
char t[256];
-
+
if (pa_socket_server_get_address(p->server, t, sizeof(t))) {
pa_strlist *l;
l = pa_property_get(p->core, PA_NATIVE_SERVER_PROPERTY_NAME);
@@ -2543,7 +4130,7 @@ void pa_protocol_native_free(pa_protocol_native *p) {
else
pa_property_remove(p->core, PA_NATIVE_SERVER_PROPERTY_NAME);
}
-
+
pa_socket_server_unref(p->server);
}
@@ -2552,20 +4139,25 @@ void pa_protocol_native_free(pa_protocol_native *p) {
if (p->auth_ip_acl)
pa_ip_acl_free(p->auth_ip_acl);
-
+
#ifdef HAVE_CREDS
pa_xfree(p->auth_group);
#endif
pa_xfree(p);
}
-pa_protocol_native* pa_protocol_native_new_iochannel(pa_core*core, pa_iochannel *io, pa_module *m, pa_modargs *ma) {
+pa_protocol_native* pa_protocol_native_new_iochannel(
+ pa_core*core,
+ pa_iochannel *io,
+ pa_module *m,
+ pa_modargs *ma) {
+
pa_protocol_native *p;
if (!(p = protocol_new_internal(core, m, ma)))
return NULL;
on_connection(NULL, io, p);
-
+
return p;
}
diff --git a/src/pulsecore/protocol-native.h b/src/pulsecore/protocol-native.h
index 5b091014..a52fa8cf 100644
--- a/src/pulsecore/protocol-native.h
+++ b/src/pulsecore/protocol-native.h
@@ -1,21 +1,22 @@
#ifndef fooprotocolnativehfoo
#define fooprotocolnativehfoo
-/* $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
diff --git a/src/pulsecore/protocol-simple.c b/src/pulsecore/protocol-simple.c
index bf203e42..020a281d 100644
--- a/src/pulsecore/protocol-simple.c
+++ b/src/pulsecore/protocol-simple.c
@@ -1,18 +1,18 @@
-/* $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
@@ -23,7 +23,6 @@
#include <config.h>
#endif
-#include <assert.h>
#include <stdlib.h>
#include <limits.h>
#include <stdio.h>
@@ -31,6 +30,7 @@
#include <string.h>
#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
#include <pulsecore/sink-input.h>
#include <pulsecore/source-output.h>
@@ -39,281 +39,438 @@
#include <pulsecore/namereg.h>
#include <pulsecore/log.h>
#include <pulsecore/core-error.h>
+#include <pulsecore/atomic.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/core-util.h>
#include "protocol-simple.h"
/* Don't allow more than this many concurrent connections */
#define MAX_CONNECTIONS 10
-struct connection {
+typedef struct connection {
+ pa_msgobject parent;
pa_protocol_simple *protocol;
pa_iochannel *io;
pa_sink_input *sink_input;
pa_source_output *source_output;
pa_client *client;
pa_memblockq *input_memblockq, *output_memblockq;
- pa_defer_event *defer_event;
- int dead;
-
+ pa_bool_t dead;
+
struct {
pa_memblock *current_memblock;
- size_t memblock_index, fragment_size;
+ size_t memblock_index;
+ pa_atomic_t missing;
+ pa_bool_t underrun;
} playback;
-};
+} connection;
+
+PA_DECLARE_CLASS(connection);
+#define CONNECTION(o) (connection_cast(o))
+static PA_DEFINE_CHECK_TYPE(connection, pa_msgobject);
struct pa_protocol_simple {
pa_module *module;
pa_core *core;
pa_socket_server*server;
pa_idxset *connections;
+
enum {
RECORD = 1,
PLAYBACK = 2,
DUPLEX = 3
} mode;
+
pa_sample_spec sample_spec;
char *source_name, *sink_name;
};
+enum {
+ SINK_INPUT_MESSAGE_POST_DATA = PA_SINK_INPUT_MESSAGE_MAX, /* data from main loop to sink input */
+ SINK_INPUT_MESSAGE_DISABLE_PREBUF /* disabled prebuf, get playback started. */
+};
+
+enum {
+ CONNECTION_MESSAGE_REQUEST_DATA, /* data requested from sink input from the main loop */
+ CONNECTION_MESSAGE_POST_DATA, /* data from source output to main loop */
+ CONNECTION_MESSAGE_UNLINK_CONNECTION /* Please drop a aconnection now */
+};
+
+
#define PLAYBACK_BUFFER_SECONDS (.5)
#define PLAYBACK_BUFFER_FRAGMENTS (10)
#define RECORD_BUFFER_SECONDS (5)
-#define RECORD_BUFFER_FRAGMENTS (100)
+#define DEFAULT_SINK_LATENCY (300*PA_USEC_PER_MSEC)
+#define DEFAULT_SOURCE_LATENCY (300*PA_USEC_PER_MSEC)
-static void connection_free(struct connection *c) {
- assert(c);
+static void connection_unlink(connection *c) {
+ pa_assert(c);
- pa_idxset_remove_by_data(c->protocol->connections, c, NULL);
+ if (!c->protocol)
+ return;
- if (c->playback.current_memblock)
- pa_memblock_unref(c->playback.current_memblock);
if (c->sink_input) {
- pa_sink_input_disconnect(c->sink_input);
+ pa_sink_input_unlink(c->sink_input);
pa_sink_input_unref(c->sink_input);
+ c->sink_input = NULL;
}
+
if (c->source_output) {
- pa_source_output_disconnect(c->source_output);
+ pa_source_output_unlink(c->source_output);
pa_source_output_unref(c->source_output);
+ c->source_output = NULL;
}
- if (c->client)
+
+ if (c->client) {
pa_client_free(c->client);
- if (c->io)
+ c->client = NULL;
+ }
+
+ if (c->io) {
pa_iochannel_free(c->io);
+ c->io = NULL;
+ }
+
+ pa_assert_se(pa_idxset_remove_by_data(c->protocol->connections, c, NULL) == c);
+ c->protocol = NULL;
+ connection_unref(c);
+}
+
+static void connection_free(pa_object *o) {
+ connection *c = CONNECTION(o);
+ pa_assert(c);
+
+ if (c->playback.current_memblock)
+ pa_memblock_unref(c->playback.current_memblock);
+
if (c->input_memblockq)
pa_memblockq_free(c->input_memblockq);
if (c->output_memblockq)
pa_memblockq_free(c->output_memblockq);
- if (c->defer_event)
- c->protocol->core->mainloop->defer_free(c->defer_event);
+
pa_xfree(c);
}
-static int do_read(struct connection *c) {
+static int do_read(connection *c) {
pa_memchunk chunk;
ssize_t r;
size_t l;
void *p;
+ size_t space;
+
+ connection_assert_ref(c);
- if (!c->sink_input || !(l = pa_memblockq_missing(c->input_memblockq)))
+ if (!c->sink_input || (l = pa_atomic_load(&c->playback.missing)) <= 0)
return 0;
- if (l > c->playback.fragment_size)
- l = c->playback.fragment_size;
+ if (c->playback.current_memblock) {
- if (c->playback.current_memblock)
- if (pa_memblock_get_length(c->playback.current_memblock) - c->playback.memblock_index < l) {
+ space = pa_memblock_get_length(c->playback.current_memblock) - c->playback.memblock_index;
+
+ if (space <= 0) {
pa_memblock_unref(c->playback.current_memblock);
c->playback.current_memblock = NULL;
- c->playback.memblock_index = 0;
}
+ }
if (!c->playback.current_memblock) {
- c->playback.current_memblock = pa_memblock_new(c->protocol->core->mempool, c->playback.fragment_size*2);
- assert(c->playback.current_memblock);
- assert(pa_memblock_get_length(c->playback.current_memblock) >= l);
+ pa_assert_se(c->playback.current_memblock = pa_memblock_new(c->protocol->core->mempool, 0));
c->playback.memblock_index = 0;
+
+ space = pa_memblock_get_length(c->playback.current_memblock);
}
+ if (l > space)
+ l = space;
+
p = pa_memblock_acquire(c->playback.current_memblock);
-
- if ((r = pa_iochannel_read(c->io, (uint8_t*) p + c->playback.memblock_index, l)) <= 0) {
- pa_memblock_release(c->playback.current_memblock);
+ r = pa_iochannel_read(c->io, (uint8_t*) p + c->playback.memblock_index, l);
+ pa_memblock_release(c->playback.current_memblock);
+
+ if (r <= 0) {
+
+ if (r < 0 && (errno == EINTR || errno == EAGAIN))
+ return 0;
+
pa_log_debug("read(): %s", r == 0 ? "EOF" : pa_cstrerror(errno));
return -1;
}
- pa_memblock_release(c->playback.current_memblock);
chunk.memblock = c->playback.current_memblock;
chunk.index = c->playback.memblock_index;
chunk.length = r;
- assert(chunk.memblock);
c->playback.memblock_index += r;
-
- assert(c->input_memblockq);
- pa_memblockq_push_align(c->input_memblockq, &chunk);
- assert(c->sink_input);
- pa_sink_notify(c->sink_input->sink);
-
+
+ pa_asyncmsgq_post(c->sink_input->sink->asyncmsgq, PA_MSGOBJECT(c->sink_input), SINK_INPUT_MESSAGE_POST_DATA, NULL, 0, &chunk, NULL);
+ pa_atomic_sub(&c->playback.missing, r);
+
return 0;
}
-static int do_write(struct connection *c) {
+static int do_write(connection *c) {
pa_memchunk chunk;
ssize_t r;
void *p;
-
+
+ connection_assert_ref(c);
+
if (!c->source_output)
- return 0;
+ return 0;
- assert(c->output_memblockq);
- if (pa_memblockq_peek(c->output_memblockq, &chunk) < 0)
+ if (pa_memblockq_peek(c->output_memblockq, &chunk) < 0) {
+/* pa_log("peek failed"); */
return 0;
-
- assert(chunk.memblock && chunk.length);
+ }
+
+ pa_assert(chunk.memblock);
+ pa_assert(chunk.length);
p = pa_memblock_acquire(chunk.memblock);
-
- if ((r = pa_iochannel_write(c->io, (uint8_t*) p+chunk.index, chunk.length)) < 0) {
- pa_memblock_release(chunk.memblock);
- pa_memblock_unref(chunk.memblock);
+ r = pa_iochannel_write(c->io, (uint8_t*) p+chunk.index, chunk.length);
+ pa_memblock_release(chunk.memblock);
+
+ pa_memblock_unref(chunk.memblock);
+
+ if (r < 0) {
+
+ if (errno == EINTR || errno == EAGAIN)
+ return 0;
+
pa_log("write(): %s", pa_cstrerror(errno));
return -1;
}
- pa_memblock_release(chunk.memblock);
-
- pa_memblockq_drop(c->output_memblockq, &chunk, r);
- pa_memblock_unref(chunk.memblock);
+ pa_memblockq_drop(c->output_memblockq, r);
- pa_source_notify(c->source_output->source);
-
return 0;
}
-static void do_work(struct connection *c) {
- assert(c);
-
- assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable);
- c->protocol->core->mainloop->defer_enable(c->defer_event, 0);
+static void do_work(connection *c) {
+ connection_assert_ref(c);
if (c->dead)
return;
-
- if (pa_iochannel_is_readable(c->io)) {
+
+ if (pa_iochannel_is_readable(c->io))
if (do_read(c) < 0)
goto fail;
- } else if (pa_iochannel_is_hungup(c->io))
+
+ if (!c->sink_input && pa_iochannel_is_hungup(c->io))
goto fail;
- if (pa_iochannel_is_writable(c->io)) {
+ if (pa_iochannel_is_writable(c->io))
if (do_write(c) < 0)
goto fail;
- }
return;
fail:
if (c->sink_input) {
- c->dead = 1;
-
+
+ /* If there is a sink input, we first drain what we already have read before shutting down the connection */
+ c->dead = TRUE;
+
pa_iochannel_free(c->io);
c->io = NULL;
- pa_memblockq_prebuf_disable(c->input_memblockq);
- pa_sink_notify(c->sink_input->sink);
+ pa_asyncmsgq_post(c->sink_input->sink->asyncmsgq, PA_MSGOBJECT(c->sink_input), SINK_INPUT_MESSAGE_DISABLE_PREBUF, NULL, 0, NULL, NULL);
} else
- connection_free(c);
+ connection_unlink(c);
+}
+
+static int connection_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
+ connection *c = CONNECTION(o);
+ connection_assert_ref(c);
+
+ switch (code) {
+ case CONNECTION_MESSAGE_REQUEST_DATA:
+ do_work(c);
+ break;
+
+ case CONNECTION_MESSAGE_POST_DATA:
+/* pa_log("got data %u", chunk->length); */
+ pa_memblockq_push_align(c->output_memblockq, chunk);
+ do_work(c);
+ break;
+
+ case CONNECTION_MESSAGE_UNLINK_CONNECTION:
+ connection_unlink(c);
+ break;
+ }
+
+ return 0;
}
/*** sink_input callbacks ***/
-static int sink_input_peek_cb(pa_sink_input *i, pa_memchunk *chunk) {
- struct connection*c;
- assert(i && i->userdata && chunk);
- c = i->userdata;
-
+/* Called from thread context */
+static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
+ pa_sink_input *i = PA_SINK_INPUT(o);
+ connection*c;
+
+ pa_sink_input_assert_ref(i);
+ c = CONNECTION(i->userdata);
+ connection_assert_ref(c);
+
+ switch (code) {
+
+ case SINK_INPUT_MESSAGE_POST_DATA: {
+ pa_assert(chunk);
+
+ /* New data from the main loop */
+ pa_memblockq_push_align(c->input_memblockq, chunk);
+
+ if (pa_memblockq_is_readable(c->input_memblockq) && c->playback.underrun) {
+ pa_log_debug("Requesting rewind due to end of underrun.");
+ pa_sink_input_request_rewind(c->sink_input, 0, FALSE, TRUE);
+ }
+
+/* pa_log("got data, %u", pa_memblockq_get_length(c->input_memblockq)); */
+
+ return 0;
+ }
+
+ case SINK_INPUT_MESSAGE_DISABLE_PREBUF:
+ pa_memblockq_prebuf_disable(c->input_memblockq);
+ return 0;
+
+ case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
+ pa_usec_t *r = userdata;
+
+ *r = pa_bytes_to_usec(pa_memblockq_get_length(c->input_memblockq), &c->sink_input->sample_spec);
+
+ /* Fall through, the default handler will add in the extra
+ * latency added by the resampler */
+ }
+
+ default:
+ return pa_sink_input_process_msg(o, code, userdata, offset, chunk);
+ }
+}
+
+/* Called from thread context */
+static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
+ connection *c;
+
+ pa_sink_input_assert_ref(i);
+ c = CONNECTION(i->userdata);
+ connection_assert_ref(c);
+ pa_assert(chunk);
+
if (pa_memblockq_peek(c->input_memblockq, chunk) < 0) {
-
- if (c->dead)
- connection_free(c);
-
+
+ c->playback.underrun = TRUE;
+
+ if (c->dead && pa_sink_input_safe_to_remove(i))
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_UNLINK_CONNECTION, NULL, 0, NULL, NULL);
+
return -1;
- }
+ } else {
+ size_t m;
- return 0;
+ chunk->length = PA_MIN(length, chunk->length);
+
+ c->playback.underrun = FALSE;
+
+ pa_memblockq_drop(c->input_memblockq, chunk->length);
+ m = pa_memblockq_pop_missing(c->input_memblockq);
+
+ if (m > 0)
+ if (pa_atomic_add(&c->playback.missing, m) <= 0)
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL);
+
+ return 0;
+ }
}
-static void sink_input_drop_cb(pa_sink_input *i, const pa_memchunk *chunk, size_t length) {
- struct connection*c = i->userdata;
- assert(i && c && length);
+/* Called from thread context */
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
+ connection *c;
+
+ pa_sink_input_assert_ref(i);
+ c = CONNECTION(i->userdata);
+ connection_assert_ref(c);
- pa_memblockq_drop(c->input_memblockq, chunk, length);
+ /* If we are in an underrun, then we don't rewind */
+ if (i->thread_info.underrun_for > 0)
+ return;
- /* do something */
- assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable);
- c->protocol->core->mainloop->defer_enable(c->defer_event, 1);
+ pa_memblockq_rewind(c->input_memblockq, nbytes);
}
-static void sink_input_kill_cb(pa_sink_input *i) {
- assert(i && i->userdata);
- connection_free((struct connection *) i->userdata);
+/* Called from thread context */
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
+ connection *c;
+
+ pa_sink_input_assert_ref(i);
+ c = CONNECTION(i->userdata);
+ connection_assert_ref(c);
+
+ pa_memblockq_set_maxrewind(c->input_memblockq, nbytes);
}
+/* Called from main context */
+static void sink_input_kill_cb(pa_sink_input *i) {
+ pa_sink_input_assert_ref(i);
-static pa_usec_t sink_input_get_latency_cb(pa_sink_input *i) {
- struct connection*c = i->userdata;
- assert(i && c);
- return pa_bytes_to_usec(pa_memblockq_get_length(c->input_memblockq), &c->sink_input->sample_spec);
+ connection_unlink(CONNECTION(i->userdata));
}
/*** source_output callbacks ***/
+/* Called from thread context */
static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
- struct connection *c = o->userdata;
- assert(o && c && chunk);
+ connection *c;
- pa_memblockq_push(c->output_memblockq, chunk);
+ pa_source_output_assert_ref(o);
+ c = CONNECTION(o->userdata);
+ pa_assert(c);
+ pa_assert(chunk);
- /* do something */
- assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable);
- c->protocol->core->mainloop->defer_enable(c->defer_event, 1);
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_POST_DATA, NULL, 0, chunk, NULL);
}
+/* Called from main context */
static void source_output_kill_cb(pa_source_output *o) {
- assert(o && o->userdata);
- connection_free((struct connection *) o->userdata);
+ pa_source_output_assert_ref(o);
+
+ connection_unlink(CONNECTION(o->userdata));
}
+/* Called from main context */
static pa_usec_t source_output_get_latency_cb(pa_source_output *o) {
- struct connection*c = o->userdata;
- assert(o && c);
+ connection*c;
+
+ pa_source_output_assert_ref(o);
+ c = CONNECTION(o->userdata);
+ pa_assert(c);
+
return pa_bytes_to_usec(pa_memblockq_get_length(c->output_memblockq), &c->source_output->sample_spec);
}
/*** client callbacks ***/
-static void client_kill_cb(pa_client *c) {
- assert(c && c->userdata);
- connection_free((struct connection *) c->userdata);
+static void client_kill_cb(pa_client *client) {
+ connection*c;
+
+ pa_assert(client);
+ c = CONNECTION(client->userdata);
+ pa_assert(c);
+
+ connection_unlink(c);
}
/*** pa_iochannel callbacks ***/
static void io_callback(pa_iochannel*io, void *userdata) {
- struct connection *c = userdata;
- assert(io && c && c->io == io);
-
- do_work(c);
-}
-
-/*** fixed callback ***/
+ connection *c = CONNECTION(userdata);
-static void defer_callback(pa_mainloop_api*a, pa_defer_event *e, void *userdata) {
- struct connection *c = userdata;
- assert(a && c && c->defer_event == e);
+ connection_assert_ref(c);
+ pa_assert(io);
do_work(c);
}
@@ -322,9 +479,12 @@ static void defer_callback(pa_mainloop_api*a, pa_defer_event *e, void *userdata)
static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) {
pa_protocol_simple *p = userdata;
- struct connection *c = NULL;
- char cname[256];
- assert(s && io && p);
+ connection *c = NULL;
+ char cname[256], pname[128];
+
+ pa_assert(s);
+ pa_assert(io);
+ pa_assert(p);
if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {
pa_log("Warning! Too many connections (%u), dropping incoming connection.", MAX_CONNECTIONS);
@@ -332,75 +492,102 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
return;
}
- c = pa_xmalloc(sizeof(struct connection));
+ c = pa_msgobject_new(connection);
+ c->parent.parent.free = connection_free;
+ c->parent.process_msg = connection_process_msg;
c->io = io;
c->sink_input = NULL;
c->source_output = NULL;
- c->defer_event = NULL;
c->input_memblockq = c->output_memblockq = NULL;
c->protocol = p;
c->playback.current_memblock = NULL;
c->playback.memblock_index = 0;
- c->playback.fragment_size = 0;
- c->dead = 0;
-
- pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname));
- c->client = pa_client_new(p->core, __FILE__, cname);
- assert(c->client);
- c->client->owner = p->module;
+ c->dead = FALSE;
+ c->playback.underrun = TRUE;
+ pa_atomic_store(&c->playback.missing, 0);
+
+ pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname));
+ pa_snprintf(cname, sizeof(cname), "Simple client (%s)", pname);
+ pa_assert_se(c->client = pa_client_new(p->core, __FILE__, cname));
+ pa_proplist_sets(c->client->proplist, "simple-protocol.peer", pname);
+ c->client->module = p->module;
c->client->kill = client_kill_cb;
c->client->userdata = c;
if (p->mode & PLAYBACK) {
pa_sink_input_new_data data;
size_t l;
+ pa_sink *sink;
+
+ if (!(sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, TRUE))) {
+ pa_log("Failed to get sink.");
+ goto fail;
+ }
pa_sink_input_new_data_init(&data);
data.driver = __FILE__;
- data.name = c->client->name;
- pa_sink_input_new_data_set_sample_spec(&data, &p->sample_spec);
data.module = p->module;
data.client = c->client;
+ data.sink = sink;
+ pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist);
+ pa_sink_input_new_data_set_sample_spec(&data, &p->sample_spec);
+
+ c->sink_input = pa_sink_input_new(p->core, &data, 0);
+ pa_sink_input_new_data_done(&data);
- if (!(c->sink_input = pa_sink_input_new(p->core, &data, 0))) {
+ if (!c->sink_input) {
pa_log("Failed to create sink input.");
goto fail;
}
-
- c->sink_input->peek = sink_input_peek_cb;
- c->sink_input->drop = sink_input_drop_cb;
+
+ c->sink_input->parent.process_msg = sink_input_process_msg;
+ c->sink_input->pop = sink_input_pop_cb;
+ c->sink_input->process_rewind = sink_input_process_rewind_cb;
+ c->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
c->sink_input->kill = sink_input_kill_cb;
- c->sink_input->get_latency = sink_input_get_latency_cb;
c->sink_input->userdata = c;
+ pa_sink_input_set_requested_latency(c->sink_input, DEFAULT_SINK_LATENCY);
+
l = (size_t) (pa_bytes_per_second(&p->sample_spec)*PLAYBACK_BUFFER_SECONDS);
c->input_memblockq = pa_memblockq_new(
0,
l,
- 0,
+ l,
pa_frame_size(&p->sample_spec),
(size_t) -1,
l/PLAYBACK_BUFFER_FRAGMENTS,
+ 0,
NULL);
- assert(c->input_memblockq);
- pa_iochannel_socket_set_rcvbuf(io, l/PLAYBACK_BUFFER_FRAGMENTS*5);
- c->playback.fragment_size = l/10;
+ pa_iochannel_socket_set_rcvbuf(io, l);
+
+ pa_atomic_store(&c->playback.missing, pa_memblockq_missing(c->input_memblockq));
- pa_sink_notify(c->sink_input->sink);
+ pa_sink_input_put(c->sink_input);
}
if (p->mode & RECORD) {
pa_source_output_new_data data;
size_t l;
+ pa_source *source;
+
+ if (!(source = pa_namereg_get(c->protocol->core, c->protocol->source_name, PA_NAMEREG_SOURCE, TRUE))) {
+ pa_log("Failed to get source.");
+ goto fail;
+ }
pa_source_output_new_data_init(&data);
data.driver = __FILE__;
- data.name = c->client->name;
- pa_source_output_new_data_set_sample_spec(&data, &p->sample_spec);
data.module = p->module;
data.client = c->client;
+ data.source = source;
+ pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist);
+ pa_source_output_new_data_set_sample_spec(&data, &p->sample_spec);
- if (!(c->source_output = pa_source_output_new(p->core, &data, 0))) {
+ c->source_output = pa_source_output_new(p->core, &data, 0);
+ pa_source_output_new_data_done(&data);
+
+ if (!c->source_output) {
pa_log("Failed to create source output.");
goto fail;
}
@@ -409,6 +596,8 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
c->source_output->get_latency = source_output_get_latency_cb;
c->source_output->userdata = c;
+ pa_source_output_set_requested_latency(c->source_output, DEFAULT_SOURCE_LATENCY);
+
l = (size_t) (pa_bytes_per_second(&p->sample_spec)*RECORD_BUFFER_SECONDS);
c->output_memblockq = pa_memblockq_new(
0,
@@ -417,34 +606,36 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
pa_frame_size(&p->sample_spec),
1,
0,
+ 0,
NULL);
- pa_iochannel_socket_set_sndbuf(io, l/RECORD_BUFFER_FRAGMENTS*2);
- pa_source_notify(c->source_output->source);
+ pa_iochannel_socket_set_sndbuf(io, l);
+
+ pa_source_output_put(c->source_output);
}
pa_iochannel_set_callback(c->io, io_callback, c);
pa_idxset_put(p->connections, c, NULL);
- c->defer_event = p->core->mainloop->defer_new(p->core->mainloop, defer_callback, c);
- assert(c->defer_event);
- p->core->mainloop->defer_enable(c->defer_event, 0);
-
return;
-
+
fail:
if (c)
- connection_free(c);
+ connection_unlink(c);
}
pa_protocol_simple* pa_protocol_simple_new(pa_core *core, pa_socket_server *server, pa_module *m, pa_modargs *ma) {
pa_protocol_simple* p = NULL;
- int enable;
- assert(core && server && ma);
+ pa_bool_t enable;
+
+ pa_assert(core);
+ pa_assert(server);
+ pa_assert(m);
+ pa_assert(ma);
- p = pa_xmalloc0(sizeof(pa_protocol_simple));
+ p = pa_xnew0(pa_protocol_simple, 1);
p->module = m;
p->core = core;
- p->server = server;
+ p->server = pa_socket_server_ref(server);
p->connections = pa_idxset_new(NULL, NULL);
p->sample_spec = core->default_sample_spec;
@@ -455,15 +646,15 @@ pa_protocol_simple* pa_protocol_simple_new(pa_core *core, pa_socket_server *serv
p->source_name = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL));
p->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
-
- enable = 0;
+
+ enable = FALSE;
if (pa_modargs_get_value_boolean(ma, "record", &enable) < 0) {
pa_log("record= expects a numeric argument.");
goto fail;
}
p->mode = enable ? RECORD : 0;
- enable = 1;
+ enable = TRUE;
if (pa_modargs_get_value_boolean(ma, "playback", &enable) < 0) {
pa_log("playback= expects a numeric argument.");
goto fail;
@@ -474,31 +665,32 @@ pa_protocol_simple* pa_protocol_simple_new(pa_core *core, pa_socket_server *serv
pa_log("neither playback nor recording enabled for protocol.");
goto fail;
}
-
+
pa_socket_server_set_callback(p->server, on_connection, p);
-
+
return p;
fail:
if (p)
pa_protocol_simple_free(p);
+
return NULL;
}
void pa_protocol_simple_free(pa_protocol_simple *p) {
- struct connection *c;
- assert(p);
+ connection *c;
+ pa_assert(p);
if (p->connections) {
while((c = pa_idxset_first(p->connections, NULL)))
- connection_free(c);
-
+ connection_unlink(c);
+
pa_idxset_free(p->connections, NULL, NULL);
}
if (p->server)
pa_socket_server_unref(p->server);
+
pa_xfree(p);
}
-
diff --git a/src/pulsecore/protocol-simple.h b/src/pulsecore/protocol-simple.h
index 8dfaee34..e1b3143b 100644
--- a/src/pulsecore/protocol-simple.h
+++ b/src/pulsecore/protocol-simple.h
@@ -1,21 +1,21 @@
#ifndef fooprotocolsimplehfoo
#define fooprotocolsimplehfoo
-/* $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
diff --git a/src/pulsecore/pstream-util.c b/src/pulsecore/pstream-util.c
index d7c1b31b..f84f486a 100644
--- a/src/pulsecore/pstream-util.c
+++ b/src/pulsecore/pstream-util.c
@@ -1,18 +1,18 @@
-/* $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.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
@@ -23,9 +23,8 @@
#include <config.h>
#endif
-#include <assert.h>
-
#include <pulsecore/native-common.h>
+#include <pulsecore/macro.h>
#include "pstream-util.h"
@@ -33,20 +32,20 @@ void pa_pstream_send_tagstruct_with_creds(pa_pstream *p, pa_tagstruct *t, const
size_t length;
uint8_t *data;
pa_packet *packet;
- assert(p);
- assert(t);
- data = pa_tagstruct_free_data(t, &length);
- assert(data && length);
- packet = pa_packet_new_dynamic(data, length);
- assert(packet);
+ pa_assert(p);
+ pa_assert(t);
+
+ pa_assert_se(data = pa_tagstruct_free_data(t, &length));
+ pa_assert_se(packet = pa_packet_new_dynamic(data, length));
pa_pstream_send_packet(p, packet, creds);
pa_packet_unref(packet);
}
void pa_pstream_send_error(pa_pstream *p, uint32_t tag, uint32_t error) {
- pa_tagstruct *t = pa_tagstruct_new(NULL, 0);
- assert(t);
+ pa_tagstruct *t;
+
+ pa_assert_se(t = pa_tagstruct_new(NULL, 0));
pa_tagstruct_putu32(t, PA_COMMAND_ERROR);
pa_tagstruct_putu32(t, tag);
pa_tagstruct_putu32(t, error);
@@ -54,8 +53,9 @@ void pa_pstream_send_error(pa_pstream *p, uint32_t tag, uint32_t error) {
}
void pa_pstream_send_simple_ack(pa_pstream *p, uint32_t tag) {
- pa_tagstruct *t = pa_tagstruct_new(NULL, 0);
- assert(t);
+ pa_tagstruct *t;
+
+ pa_assert_se(t = pa_tagstruct_new(NULL, 0));
pa_tagstruct_putu32(t, PA_COMMAND_REPLY);
pa_tagstruct_putu32(t, tag);
pa_pstream_send_tagstruct(p, t);
diff --git a/src/pulsecore/pstream-util.h b/src/pulsecore/pstream-util.h
index c6d76a7c..ae0d79c3 100644
--- a/src/pulsecore/pstream-util.h
+++ b/src/pulsecore/pstream-util.h
@@ -1,21 +1,21 @@
#ifndef foopstreamutilhfoo
#define foopstreamutilhfoo
-/* $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.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
diff --git a/src/pulsecore/pstream.c b/src/pulsecore/pstream.c
index 33963796..e26ca473 100644
--- a/src/pulsecore/pstream.c
+++ b/src/pulsecore/pstream.c
@@ -1,18 +1,19 @@
-/* $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.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
@@ -25,7 +26,6 @@
#include <stdio.h>
#include <stdlib.h>
-#include <assert.h>
#include <unistd.h>
#ifdef HAVE_SYS_SOCKET_H
@@ -38,17 +38,17 @@
#include <netinet/in.h>
#endif
-#include "winsock.h"
#include <pulse/xmalloc.h>
+#include <pulsecore/winsock.h>
#include <pulsecore/queue.h>
#include <pulsecore/log.h>
#include <pulsecore/core-scache.h>
#include <pulsecore/creds.h>
-#include <pulsecore/mutex.h>
#include <pulsecore/refcnt.h>
-#include <pulsecore/anotify.h>
+#include <pulsecore/flist.h>
+#include <pulsecore/macro.h>
#include "pstream.h"
@@ -82,7 +82,8 @@ typedef uint32_t pa_pstream_descriptor[PA_PSTREAM_DESCRIPTOR_MAX];
#define PA_PSTREAM_DESCRIPTOR_SIZE (PA_PSTREAM_DESCRIPTOR_MAX*sizeof(uint32_t))
#define FRAME_SIZE_MAX_ALLOW PA_SCACHE_ENTRY_SIZE_MAX /* allow uploading a single sample in one frame at max */
-#define FRAME_SIZE_MAX_USE (1024*64)
+
+PA_STATIC_FLIST_DECLARE(items, 0, pa_xfree);
struct item_info {
enum {
@@ -92,11 +93,10 @@ struct item_info {
PA_PSTREAM_ITEM_SHMREVOKE
} type;
-
/* packet info */
pa_packet *packet;
#ifdef HAVE_CREDS
- int with_creds;
+ pa_bool_t with_creds;
pa_creds creds;
#endif
@@ -112,15 +112,14 @@ struct item_info {
struct pa_pstream {
PA_REFCNT_DECLARE;
-
+
pa_mainloop_api *mainloop;
+ pa_defer_event *defer_event;
pa_iochannel *io;
pa_queue *send_queue;
- pa_mutex *mutex; /* only for access to the queue */
- pa_anotify *anotify;
- int dead;
+ pa_bool_t dead;
struct {
pa_pstream_descriptor descriptor;
@@ -140,7 +139,7 @@ struct pa_pstream {
size_t index;
} read;
- int use_shm;
+ pa_bool_t use_shm;
pa_memimport *import;
pa_memexport *export;
@@ -156,11 +155,17 @@ struct pa_pstream {
pa_pstream_notify_cb_t die_callback;
void *die_callback_userdata;
+ pa_pstream_block_id_cb_t revoke_callback;
+ void *revoke_callback_userdata;
+
+ pa_pstream_block_id_cb_t release_callback;
+ void *release_callback_userdata;
+
pa_mempool *mempool;
#ifdef HAVE_CREDS
pa_creds read_creds, write_creds;
- int read_creds_valid, send_creds_now;
+ pa_bool_t read_creds_valid, send_creds_now;
#endif
};
@@ -168,11 +173,13 @@ static int do_write(pa_pstream *p);
static int do_read(pa_pstream *p);
static void do_something(pa_pstream *p) {
- assert(p);
- assert(PA_REFCNT_VALUE(p) > 0);
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
pa_pstream_ref(p);
+ p->mainloop->defer_enable(p->defer_event, 0);
+
if (!p->dead && pa_iochannel_is_readable(p->io)) {
if (do_read(p) < 0)
goto fail;
@@ -189,27 +196,31 @@ static void do_something(pa_pstream *p) {
fail:
- p->dead = 1;
-
if (p->die_callback)
p->die_callback(p, p->die_callback_userdata);
-
+
+ pa_pstream_unlink(p);
pa_pstream_unref(p);
}
static void io_callback(pa_iochannel*io, void *userdata) {
pa_pstream *p = userdata;
-
- assert(p);
- assert(p->io == io);
-
+
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
+ pa_assert(p->io == io);
+
do_something(p);
}
-static void anotify_callback(uint8_t event, void *userdata) {
+static void defer_callback(pa_mainloop_api *m, pa_defer_event *e, void*userdata) {
pa_pstream *p = userdata;
- assert(p);
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
+ pa_assert(p->defer_event == e);
+ pa_assert(p->mainloop == m);
+
do_something(p);
}
@@ -217,24 +228,22 @@ static void memimport_release_cb(pa_memimport *i, uint32_t block_id, void *userd
pa_pstream *pa_pstream_new(pa_mainloop_api *m, pa_iochannel *io, pa_mempool *pool) {
pa_pstream *p;
-
- assert(m);
- assert(io);
- assert(pool);
+
+ pa_assert(m);
+ pa_assert(io);
+ pa_assert(pool);
p = pa_xnew(pa_pstream, 1);
PA_REFCNT_INIT(p);
p->io = io;
pa_iochannel_set_callback(io, io_callback, p);
- p->dead = 0;
-
- p->mutex = pa_mutex_new(1);
- p->anotify = pa_anotify_new(m, anotify_callback, p);
+ p->dead = FALSE;
p->mainloop = m;
-
+ p->defer_event = m->defer_new(m, defer_callback, p);
+ m->defer_enable(p->defer_event, 0);
+
p->send_queue = pa_queue_new();
- assert(p->send_queue);
p->write.current = NULL;
p->write.index = 0;
@@ -251,64 +260,63 @@ pa_pstream *pa_pstream_new(pa_mainloop_api *m, pa_iochannel *io, pa_mempool *poo
p->drain_callback_userdata = NULL;
p->die_callback = NULL;
p->die_callback_userdata = NULL;
+ p->revoke_callback = NULL;
+ p->revoke_callback_userdata = NULL;
+ p->release_callback = NULL;
+ p->release_callback_userdata = NULL;
p->mempool = pool;
- p->use_shm = 0;
+ p->use_shm = FALSE;
p->export = NULL;
/* We do importing unconditionally */
p->import = pa_memimport_new(p->mempool, memimport_release_cb, p);
- pa_iochannel_socket_set_rcvbuf(io, 1024*8);
- pa_iochannel_socket_set_sndbuf(io, 1024*8);
+ pa_iochannel_socket_set_rcvbuf(io, pa_mempool_block_size_max(p->mempool));
+ pa_iochannel_socket_set_sndbuf(io, pa_mempool_block_size_max(p->mempool));
#ifdef HAVE_CREDS
- p->send_creds_now = 0;
- p->read_creds_valid = 0;
+ p->send_creds_now = FALSE;
+ p->read_creds_valid = FALSE;
#endif
return p;
}
-static void item_free(void *item, PA_GCC_UNUSED void *p) {
+static void item_free(void *item, PA_GCC_UNUSED void *q) {
struct item_info *i = item;
- assert(i);
+ pa_assert(i);
if (i->type == PA_PSTREAM_ITEM_MEMBLOCK) {
- assert(i->chunk.memblock);
+ pa_assert(i->chunk.memblock);
pa_memblock_unref(i->chunk.memblock);
} else if (i->type == PA_PSTREAM_ITEM_PACKET) {
- assert(i->packet);
+ pa_assert(i->packet);
pa_packet_unref(i->packet);
}
- pa_xfree(i);
+ if (pa_flist_push(PA_STATIC_FLIST_GET(items), i) < 0)
+ pa_xfree(i);
}
static void pstream_free(pa_pstream *p) {
- assert(p);
+ pa_assert(p);
+
+ pa_pstream_unlink(p);
- pa_pstream_close(p);
-
pa_queue_free(p->send_queue, item_free, NULL);
if (p->write.current)
item_free(p->write.current, NULL);
- if (p->read.memblock)
- pa_memblock_unref(p->read.memblock);
-
- if (p->read.packet)
- pa_packet_unref(p->read.packet);
-
if (p->write.memchunk.memblock)
pa_memblock_unref(p->write.memchunk.memblock);
- if (p->mutex)
- pa_mutex_free(p->mutex);
+ if (p->read.memblock)
+ pa_memblock_unref(p->read.memblock);
- if (p->anotify)
- pa_anotify_free(p->anotify);
+ if (p->read.packet)
+ pa_packet_unref(p->read.packet);
pa_xfree(p);
}
@@ -316,123 +324,156 @@ static void pstream_free(pa_pstream *p) {
void pa_pstream_send_packet(pa_pstream*p, pa_packet *packet, const pa_creds *creds) {
struct item_info *i;
- assert(p);
- assert(PA_REFCNT_VALUE(p) > 0);
- assert(packet);
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
+ pa_assert(packet);
+
+ if (p->dead)
+ return;
+
+ if (!(i = pa_flist_pop(PA_STATIC_FLIST_GET(items))))
+ i = pa_xnew(struct item_info, 1);
- i = pa_xnew(struct item_info, 1);
i->type = PA_PSTREAM_ITEM_PACKET;
i->packet = pa_packet_ref(packet);
-
+
#ifdef HAVE_CREDS
if ((i->with_creds = !!creds))
i->creds = *creds;
#endif
- pa_mutex_lock(p->mutex);
pa_queue_push(p->send_queue, i);
- pa_mutex_unlock(p->mutex);
-
- pa_anotify_signal(p->anotify, 0);
+
+ p->mainloop->defer_enable(p->defer_event, 1);
}
void pa_pstream_send_memblock(pa_pstream*p, uint32_t channel, int64_t offset, pa_seek_mode_t seek_mode, const pa_memchunk *chunk) {
size_t length, idx;
-
- assert(p);
- assert(PA_REFCNT_VALUE(p) > 0);
- assert(channel != (uint32_t) -1);
- assert(chunk);
+ size_t bsm;
+
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
+ pa_assert(channel != (uint32_t) -1);
+ pa_assert(chunk);
+
+ if (p->dead)
+ return;
idx = 0;
+ length = chunk->length;
+
+ bsm = pa_mempool_block_size_max(p->mempool);
while (length > 0) {
struct item_info *i;
size_t n;
-
- i = pa_xnew(struct item_info, 1);
+
+ if (!(i = pa_flist_pop(PA_STATIC_FLIST_GET(items))))
+ i = pa_xnew(struct item_info, 1);
i->type = PA_PSTREAM_ITEM_MEMBLOCK;
- n = length < FRAME_SIZE_MAX_USE ? length : FRAME_SIZE_MAX_USE;
+ n = PA_MIN(length, bsm);
i->chunk.index = chunk->index + idx;
i->chunk.length = n;
i->chunk.memblock = pa_memblock_ref(chunk->memblock);
-
+
i->channel = channel;
i->offset = offset;
i->seek_mode = seek_mode;
#ifdef HAVE_CREDS
- i->with_creds = 0;
+ i->with_creds = FALSE;
#endif
-
- pa_mutex_lock(p->mutex);
+
pa_queue_push(p->send_queue, i);
- pa_mutex_unlock(p->mutex);
idx += n;
length -= n;
}
- pa_anotify_signal(p->anotify, 0);
+ p->mainloop->defer_enable(p->defer_event, 1);
}
-static void memimport_release_cb(pa_memimport *i, uint32_t block_id, void *userdata) {
+void pa_pstream_send_release(pa_pstream *p, uint32_t block_id) {
struct item_info *item;
- pa_pstream *p = userdata;
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
- assert(p);
- assert(PA_REFCNT_VALUE(p) > 0);
+ if (p->dead)
+ return;
/* pa_log("Releasing block %u", block_id); */
- item = pa_xnew(struct item_info, 1);
+ if (!(item = pa_flist_pop(PA_STATIC_FLIST_GET(items))))
+ item = pa_xnew(struct item_info, 1);
item->type = PA_PSTREAM_ITEM_SHMRELEASE;
item->block_id = block_id;
#ifdef HAVE_CREDS
- item->with_creds = 0;
+ item->with_creds = FALSE;
#endif
- pa_mutex_lock(p->mutex);
pa_queue_push(p->send_queue, item);
- pa_mutex_unlock(p->mutex);
-
- pa_anotify_signal(p->anotify, 0);
+ p->mainloop->defer_enable(p->defer_event, 1);
}
-static void memexport_revoke_cb(pa_memexport *e, uint32_t block_id, void *userdata) {
- struct item_info *item;
+/* might be called from thread context */
+static void memimport_release_cb(pa_memimport *i, uint32_t block_id, void *userdata) {
pa_pstream *p = userdata;
- assert(p);
- assert(PA_REFCNT_VALUE(p) > 0);
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+ if (p->dead)
+ return;
+
+ if (p->release_callback)
+ p->release_callback(p, block_id, p->release_callback_userdata);
+ else
+ pa_pstream_send_release(p, block_id);
+}
+
+void pa_pstream_send_revoke(pa_pstream *p, uint32_t block_id) {
+ struct item_info *item;
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
+ if (p->dead)
+ return;
/* pa_log("Revoking block %u", block_id); */
-
- item = pa_xnew(struct item_info, 1);
+
+ if (!(item = pa_flist_pop(PA_STATIC_FLIST_GET(items))))
+ item = pa_xnew(struct item_info, 1);
item->type = PA_PSTREAM_ITEM_SHMREVOKE;
item->block_id = block_id;
#ifdef HAVE_CREDS
- item->with_creds = 0;
+ item->with_creds = FALSE;
#endif
- pa_mutex_lock(p->mutex);
pa_queue_push(p->send_queue, item);
- pa_mutex_unlock(p->mutex);
+ p->mainloop->defer_enable(p->defer_event, 1);
+}
+
+/* might be called from thread context */
+static void memexport_revoke_cb(pa_memexport *e, uint32_t block_id, void *userdata) {
+ pa_pstream *p = userdata;
+
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
- pa_anotify_signal(p->anotify, 0);
+ if (p->revoke_callback)
+ p->revoke_callback(p, block_id, p->revoke_callback_userdata);
+ else
+ pa_pstream_send_revoke(p, block_id);
}
static void prepare_next_write_item(pa_pstream *p) {
- assert(p);
- assert(PA_REFCNT_VALUE(p) > 0);
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
- pa_mutex_lock(p->mutex);
p->write.current = pa_queue_pop(p->send_queue);
- pa_mutex_unlock(p->mutex);
if (!p->write.current)
return;
-
+
p->write.index = 0;
p->write.data = NULL;
pa_memchunk_reset(&p->write.memchunk);
@@ -442,10 +483,10 @@ static void prepare_next_write_item(pa_pstream *p) {
p->write.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI] = 0;
p->write.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_LO] = 0;
p->write.descriptor[PA_PSTREAM_DESCRIPTOR_FLAGS] = 0;
-
+
if (p->write.current->type == PA_PSTREAM_ITEM_PACKET) {
-
- assert(p->write.current->packet);
+
+ pa_assert(p->write.current->packet);
p->write.data = p->write.current->packet->data;
p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH] = htonl(p->write.current->packet->length);
@@ -458,14 +499,14 @@ static void prepare_next_write_item(pa_pstream *p) {
p->write.descriptor[PA_PSTREAM_DESCRIPTOR_FLAGS] = htonl(PA_FLAG_SHMREVOKE);
p->write.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI] = htonl(p->write.current->block_id);
-
+
} else {
uint32_t flags;
- int send_payload = 1;
-
- assert(p->write.current->type == PA_PSTREAM_ITEM_MEMBLOCK);
- assert(p->write.current->chunk.memblock);
-
+ pa_bool_t send_payload = TRUE;
+
+ pa_assert(p->write.current->type == PA_PSTREAM_ITEM_MEMBLOCK);
+ pa_assert(p->write.current->chunk.memblock);
+
p->write.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL] = htonl(p->write.current->channel);
p->write.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI] = htonl((uint32_t) (((uint64_t) p->write.current->offset) >> 32));
p->write.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_LO] = htonl((uint32_t) ((uint64_t) p->write.current->offset));
@@ -476,7 +517,7 @@ static void prepare_next_write_item(pa_pstream *p) {
uint32_t block_id, shm_id;
size_t offset, length;
- assert(p->export);
+ pa_assert(p->export);
if (pa_memexport_put(p->export,
p->write.current->chunk.memblock,
@@ -484,15 +525,15 @@ static void prepare_next_write_item(pa_pstream *p) {
&shm_id,
&offset,
&length) >= 0) {
-
+
flags |= PA_FLAG_SHMDATA;
- send_payload = 0;
-
+ send_payload = FALSE;
+
p->write.shm_info[PA_PSTREAM_SHM_BLOCKID] = htonl(block_id);
p->write.shm_info[PA_PSTREAM_SHM_SHMID] = htonl(shm_id);
p->write.shm_info[PA_PSTREAM_SHM_INDEX] = htonl((uint32_t) (offset + p->write.current->chunk.index));
p->write.shm_info[PA_PSTREAM_SHM_LENGTH] = htonl((uint32_t) p->write.current->chunk.length);
-
+
p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH] = htonl(sizeof(p->write.shm_info));
p->write.data = p->write.shm_info;
}
@@ -506,7 +547,7 @@ static void prepare_next_write_item(pa_pstream *p) {
pa_memblock_ref(p->write.memchunk.memblock);
p->write.data = NULL;
}
-
+
p->write.descriptor[PA_PSTREAM_DESCRIPTOR_FLAGS] = htonl(flags);
}
@@ -521,9 +562,9 @@ static int do_write(pa_pstream *p) {
size_t l;
ssize_t r;
pa_memblock *release_memblock = NULL;
-
- assert(p);
- assert(PA_REFCNT_VALUE(p) > 0);
+
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
if (!p->write.current)
prepare_next_write_item(p);
@@ -535,7 +576,7 @@ static int do_write(pa_pstream *p) {
d = (uint8_t*) p->write.descriptor + p->write.index;
l = PA_PSTREAM_DESCRIPTOR_SIZE - p->write.index;
} else {
- assert(p->write.data || p->write.memchunk.memblock);
+ pa_assert(p->write.data || p->write.memchunk.memblock);
if (p->write.data)
d = p->write.data;
@@ -543,20 +584,20 @@ static int do_write(pa_pstream *p) {
d = (uint8_t*) pa_memblock_acquire(p->write.memchunk.memblock) + p->write.memchunk.index;
release_memblock = p->write.memchunk.memblock;
}
-
+
d = (uint8_t*) d + p->write.index - PA_PSTREAM_DESCRIPTOR_SIZE;
l = ntohl(p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) - (p->write.index - PA_PSTREAM_DESCRIPTOR_SIZE);
}
- assert(l > 0);
-
+ pa_assert(l > 0);
+
#ifdef HAVE_CREDS
if (p->send_creds_now) {
if ((r = pa_iochannel_write_with_creds(p->io, d, l, &p->write_creds)) < 0)
goto fail;
- p->send_creds_now = 0;
+ p->send_creds_now = FALSE;
} else
#endif
@@ -569,10 +610,15 @@ static int do_write(pa_pstream *p) {
p->write.index += r;
if (p->write.index >= PA_PSTREAM_DESCRIPTOR_SIZE + ntohl(p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH])) {
- assert(p->write.current);
- item_free(p->write.current, (void *) 1);
+ pa_assert(p->write.current);
+ item_free(p->write.current, NULL);
p->write.current = NULL;
+ if (p->write.memchunk.memblock)
+ pa_memblock_unref(p->write.memchunk.memblock);
+
+ pa_memchunk_reset(&p->write.memchunk);
+
if (p->drain_callback && !pa_pstream_is_pending(p))
p->drain_callback(p, p->drain_callback_userdata);
}
@@ -583,24 +629,23 @@ fail:
if (release_memblock)
pa_memblock_release(release_memblock);
-
+
return -1;
}
static int do_read(pa_pstream *p) {
void *d;
- size_t l;
+ size_t l;
ssize_t r;
pa_memblock *release_memblock = NULL;
-
- assert(p);
- assert(PA_REFCNT_VALUE(p) > 0);
-
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
+
if (p->read.index < PA_PSTREAM_DESCRIPTOR_SIZE) {
d = (uint8_t*) p->read.descriptor + p->read.index;
l = PA_PSTREAM_DESCRIPTOR_SIZE - p->read.index;
} else {
- assert(p->read.data || p->read.memblock);
+ pa_assert(p->read.data || p->read.memblock);
if (p->read.data)
d = p->read.data;
@@ -608,15 +653,15 @@ static int do_read(pa_pstream *p) {
d = pa_memblock_acquire(p->read.memblock);
release_memblock = p->read.memblock;
}
-
+
d = (uint8_t*) d + p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE;
l = ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) - (p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE);
}
#ifdef HAVE_CREDS
{
- int b = 0;
-
+ pa_bool_t b = 0;
+
if ((r = pa_iochannel_read_with_creds(p->io, d, l, &p->read_creds, &b)) <= 0)
goto fail;
@@ -629,7 +674,7 @@ static int do_read(pa_pstream *p) {
if (release_memblock)
pa_memblock_release(release_memblock);
-
+
p->read.index += r;
if (p->read.index == PA_PSTREAM_DESCRIPTOR_SIZE) {
@@ -638,97 +683,97 @@ static int do_read(pa_pstream *p) {
flags = ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_FLAGS]);
- if (!p->import && (flags & PA_FLAG_SHMMASK) != 0) {
+ if (!p->use_shm && (flags & PA_FLAG_SHMMASK) != 0) {
pa_log_warn("Recieved SHM frame on a socket where SHM is disabled.");
return -1;
}
-
+
if (flags == PA_FLAG_SHMRELEASE) {
/* This is a SHM memblock release frame with no payload */
/* pa_log("Got release frame for %u", ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI])); */
-
- assert(p->export);
+
+ pa_assert(p->export);
pa_memexport_process_release(p->export, ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI]));
goto frame_done;
-
+
} else if (flags == PA_FLAG_SHMREVOKE) {
/* This is a SHM memblock revoke frame with no payload */
/* pa_log("Got revoke frame for %u", ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI])); */
- assert(p->import);
+ pa_assert(p->import);
pa_memimport_process_revoke(p->import, ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI]));
goto frame_done;
}
length = ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]);
-
- if (length > FRAME_SIZE_MAX_ALLOW) {
- pa_log_warn("Recieved invalid frame size : %lu", (unsigned long) length);
+
+ if (length > FRAME_SIZE_MAX_ALLOW || length <= 0) {
+ pa_log_warn("Recieved invalid frame size: %lu", (unsigned long) length);
return -1;
}
-
- assert(!p->read.packet && !p->read.memblock);
+
+ pa_assert(!p->read.packet && !p->read.memblock);
channel = ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL]);
-
+
if (channel == (uint32_t) -1) {
if (flags != 0) {
pa_log_warn("Received packet frame with invalid flags value.");
return -1;
}
-
+
/* Frame is a packet frame */
p->read.packet = pa_packet_new(length);
p->read.data = p->read.packet->data;
-
+
} else {
if ((flags & PA_FLAG_SEEKMASK) > PA_SEEK_RELATIVE_END) {
pa_log_warn("Received memblock frame with invalid seek mode.");
return -1;
}
-
+
if ((flags & PA_FLAG_SHMMASK) == PA_FLAG_SHMDATA) {
if (length != sizeof(p->read.shm_info)) {
pa_log_warn("Recieved SHM memblock frame with Invalid frame length.");
return -1;
}
-
+
/* Frame is a memblock frame referencing an SHM memblock */
p->read.data = p->read.shm_info;
} else if ((flags & PA_FLAG_SHMMASK) == 0) {
/* Frame is a memblock frame */
-
+
p->read.memblock = pa_memblock_new(p->mempool, length);
p->read.data = NULL;
} else {
-
+
pa_log_warn("Recieved memblock frame with invalid flags value.");
return -1;
}
}
-
+
} else if (p->read.index > PA_PSTREAM_DESCRIPTOR_SIZE) {
/* Frame payload available */
-
+
if (p->read.memblock && p->recieve_memblock_callback) {
/* Is this memblock data? Than pass it to the user */
l = (p->read.index - r) < PA_PSTREAM_DESCRIPTOR_SIZE ? p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE : (size_t) r;
-
+
if (l > 0) {
pa_memchunk chunk;
-
+
chunk.memblock = p->read.memblock;
chunk.index = p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE - l;
chunk.length = l;
@@ -739,7 +784,7 @@ static int do_read(pa_pstream *p) {
offset = (int64_t) (
(((uint64_t) ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI])) << 32) |
(((uint64_t) ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_LO]))));
-
+
p->recieve_memblock_callback(
p,
ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL]),
@@ -758,14 +803,14 @@ static int do_read(pa_pstream *p) {
/* Frame complete */
if (p->read.index >= ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) + PA_PSTREAM_DESCRIPTOR_SIZE) {
-
+
if (p->read.memblock) {
/* This was a memblock frame. We can unref the memblock now */
pa_memblock_unref(p->read.memblock);
} else if (p->read.packet) {
-
+
if (p->recieve_packet_callback)
#ifdef HAVE_CREDS
p->recieve_packet_callback(p, p->read.packet, p->read_creds_valid ? &p->read_creds : NULL, p->recieve_packet_callback_userdata);
@@ -776,10 +821,10 @@ static int do_read(pa_pstream *p) {
pa_packet_unref(p->read.packet);
} else {
pa_memblock *b;
-
- assert((ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_FLAGS]) & PA_FLAG_SHMMASK) == PA_FLAG_SHMDATA);
- assert(p->import);
+ pa_assert((ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_FLAGS]) & PA_FLAG_SHMMASK) == PA_FLAG_SHMDATA);
+
+ pa_assert(p->import);
if (!(b = pa_memimport_get(p->import,
ntohl(p->read.shm_info[PA_PSTREAM_SHM_BLOCKID]),
@@ -794,7 +839,7 @@ static int do_read(pa_pstream *p) {
if (p->recieve_memblock_callback) {
int64_t offset;
pa_memchunk chunk;
-
+
chunk.memblock = b;
chunk.index = 0;
chunk.length = pa_memblock_get_length(b);
@@ -802,7 +847,7 @@ static int do_read(pa_pstream *p) {
offset = (int64_t) (
(((uint64_t) ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI])) << 32) |
(((uint64_t) ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_LO]))));
-
+
p->recieve_memblock_callback(
p,
ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL]),
@@ -828,7 +873,7 @@ frame_done:
p->read.data = NULL;
#ifdef HAVE_CREDS
- p->read_creds_valid = 0;
+ p->read_creds_valid = FALSE;
#endif
return 0;
@@ -841,75 +886,90 @@ fail:
}
void pa_pstream_set_die_callback(pa_pstream *p, pa_pstream_notify_cb_t cb, void *userdata) {
- assert(p);
- assert(PA_REFCNT_VALUE(p) > 0);
-
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
+
p->die_callback = cb;
p->die_callback_userdata = userdata;
}
void pa_pstream_set_drain_callback(pa_pstream *p, pa_pstream_notify_cb_t cb, void *userdata) {
- assert(p);
- assert(PA_REFCNT_VALUE(p) > 0);
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
p->drain_callback = cb;
p->drain_callback_userdata = userdata;
}
void pa_pstream_set_recieve_packet_callback(pa_pstream *p, pa_pstream_packet_cb_t cb, void *userdata) {
- assert(p);
- assert(PA_REFCNT_VALUE(p) > 0);
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
p->recieve_packet_callback = cb;
p->recieve_packet_callback_userdata = userdata;
}
void pa_pstream_set_recieve_memblock_callback(pa_pstream *p, pa_pstream_memblock_cb_t cb, void *userdata) {
- assert(p);
- assert(PA_REFCNT_VALUE(p) > 0);
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
p->recieve_memblock_callback = cb;
p->recieve_memblock_callback_userdata = userdata;
}
-int pa_pstream_is_pending(pa_pstream *p) {
- int b;
+void pa_pstream_set_release_callback(pa_pstream *p, pa_pstream_block_id_cb_t cb, void *userdata) {
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
- assert(p);
- assert(PA_REFCNT_VALUE(p) > 0);
+ p->release_callback = cb;
+ p->release_callback_userdata = userdata;
+}
+
+void pa_pstream_set_revoke_callback(pa_pstream *p, pa_pstream_block_id_cb_t cb, void *userdata) {
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+ p->release_callback = cb;
+ p->release_callback_userdata = userdata;
+}
- pa_mutex_lock(p->mutex);
+pa_bool_t pa_pstream_is_pending(pa_pstream *p) {
+ pa_bool_t b;
+
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
if (p->dead)
- b = 0;
+ b = FALSE;
else
b = p->write.current || !pa_queue_is_empty(p->send_queue);
- pa_mutex_unlock(p->mutex);
-
return b;
}
void pa_pstream_unref(pa_pstream*p) {
- assert(p);
- assert(PA_REFCNT_VALUE(p) > 0);
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
if (PA_REFCNT_DEC(p) <= 0)
pstream_free(p);
}
pa_pstream* pa_pstream_ref(pa_pstream*p) {
- assert(p);
- assert(PA_REFCNT_VALUE(p) > 0);
-
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
+
PA_REFCNT_INC(p);
return p;
}
-void pa_pstream_close(pa_pstream *p) {
- assert(p);
+void pa_pstream_unlink(pa_pstream *p) {
+ pa_assert(p);
+
+ if (p->dead)
+ return;
- p->dead = 1;
+ p->dead = TRUE;
if (p->import) {
pa_memimport_free(p->import);
@@ -926,20 +986,25 @@ void pa_pstream_close(pa_pstream *p) {
p->io = NULL;
}
+ if (p->defer_event) {
+ p->mainloop->defer_free(p->defer_event);
+ p->defer_event = NULL;
+ }
+
p->die_callback = NULL;
p->drain_callback = NULL;
p->recieve_packet_callback = NULL;
p->recieve_memblock_callback = NULL;
}
-void pa_pstream_use_shm(pa_pstream *p, int enable) {
- assert(p);
- assert(PA_REFCNT_VALUE(p) > 0);
+void pa_pstream_enable_shm(pa_pstream *p, pa_bool_t enable) {
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
p->use_shm = enable;
if (enable) {
-
+
if (!p->export)
p->export = pa_memexport_new(p->mempool, memexport_revoke_cb, p);
@@ -951,3 +1016,10 @@ void pa_pstream_use_shm(pa_pstream *p, int enable) {
}
}
}
+
+pa_bool_t pa_pstream_get_shm(pa_pstream *p) {
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+ return p->use_shm;
+}
diff --git a/src/pulsecore/pstream.h b/src/pulsecore/pstream.h
index 26bb7699..a528b25c 100644
--- a/src/pulsecore/pstream.h
+++ b/src/pulsecore/pstream.h
@@ -1,21 +1,22 @@
#ifndef foopstreamhfoo
#define foopstreamhfoo
-/* $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.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
@@ -32,30 +33,37 @@
#include <pulsecore/iochannel.h>
#include <pulsecore/memchunk.h>
#include <pulsecore/creds.h>
+#include <pulsecore/macro.h>
typedef struct pa_pstream pa_pstream;
typedef void (*pa_pstream_packet_cb_t)(pa_pstream *p, pa_packet *packet, const pa_creds *creds, void *userdata);
typedef void (*pa_pstream_memblock_cb_t)(pa_pstream *p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk, void *userdata);
typedef void (*pa_pstream_notify_cb_t)(pa_pstream *p, void *userdata);
+typedef void (*pa_pstream_block_id_cb_t)(pa_pstream *p, uint32_t block_id, void *userdata);
pa_pstream* pa_pstream_new(pa_mainloop_api *m, pa_iochannel *io, pa_mempool *p);
-void pa_pstream_unref(pa_pstream*p);
+
pa_pstream* pa_pstream_ref(pa_pstream*p);
+void pa_pstream_unref(pa_pstream*p);
+
+void pa_pstream_unlink(pa_pstream *p);
void pa_pstream_send_packet(pa_pstream*p, pa_packet *packet, const pa_creds *creds);
void pa_pstream_send_memblock(pa_pstream*p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk);
+void pa_pstream_send_release(pa_pstream *p, uint32_t block_id);
+void pa_pstream_send_revoke(pa_pstream *p, uint32_t block_id);
void pa_pstream_set_recieve_packet_callback(pa_pstream *p, pa_pstream_packet_cb_t cb, void *userdata);
void pa_pstream_set_recieve_memblock_callback(pa_pstream *p, pa_pstream_memblock_cb_t cb, void *userdata);
void pa_pstream_set_drain_callback(pa_pstream *p, pa_pstream_notify_cb_t cb, void *userdata);
-
void pa_pstream_set_die_callback(pa_pstream *p, pa_pstream_notify_cb_t cb, void *userdata);
+void pa_pstream_set_release_callback(pa_pstream *p, pa_pstream_block_id_cb_t cb, void *userdata);
+void pa_pstream_set_revoke_callback(pa_pstream *p, pa_pstream_block_id_cb_t cb, void *userdata);
-int pa_pstream_is_pending(pa_pstream *p);
-
-void pa_pstream_use_shm(pa_pstream *p, int enable);
+pa_bool_t pa_pstream_is_pending(pa_pstream *p);
-void pa_pstream_close(pa_pstream *p);
+void pa_pstream_enable_shm(pa_pstream *p, pa_bool_t enable);
+pa_bool_t pa_pstream_get_shm(pa_pstream *p);
#endif
diff --git a/src/pulsecore/queue.c b/src/pulsecore/queue.c
index 93b119eb..08fd1426 100644
--- a/src/pulsecore/queue.c
+++ b/src/pulsecore/queue.c
@@ -1,18 +1,18 @@
-/* $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.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
@@ -23,13 +23,16 @@
#include <config.h>
#endif
-#include <assert.h>
#include <stdlib.h>
#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/flist.h>
#include "queue.h"
+PA_STATIC_FLIST_DECLARE(entries, 0, pa_xfree);
+
struct queue_entry {
struct queue_entry *next;
void *data;
@@ -42,25 +45,24 @@ struct pa_queue {
pa_queue* pa_queue_new(void) {
pa_queue *q = pa_xnew(pa_queue, 1);
+
q->front = q->back = NULL;
q->length = 0;
+
return q;
}
void pa_queue_free(pa_queue* q, void (*destroy)(void *p, void *userdata), void *userdata) {
- struct queue_entry *e;
- assert(q);
-
- e = q->front;
- while (e) {
- struct queue_entry *n = e->next;
+ void *data;
+ pa_assert(q);
+ while ((data = pa_queue_pop(q)))
if (destroy)
- destroy(e->data, userdata);
+ destroy(data, userdata);
- pa_xfree(e);
- e = n;
- }
+ pa_assert(!q->front);
+ pa_assert(!q->back);
+ pa_assert(q->length == 0);
pa_xfree(q);
}
@@ -68,14 +70,20 @@ void pa_queue_free(pa_queue* q, void (*destroy)(void *p, void *userdata), void *
void pa_queue_push(pa_queue *q, void *p) {
struct queue_entry *e;
- e = pa_xnew(struct queue_entry, 1);
+ pa_assert(q);
+ pa_assert(p);
+
+ if (!(e = pa_flist_pop(PA_STATIC_FLIST_GET(entries))))
+ e = pa_xnew(struct queue_entry, 1);
+
e->data = p;
e->next = NULL;
- if (q->back)
+ if (q->back) {
+ pa_assert(q->front);
q->back->next = e;
- else {
- assert(!q->front);
+ } else {
+ pa_assert(!q->front);
q->front = e;
}
@@ -86,24 +94,30 @@ void pa_queue_push(pa_queue *q, void *p) {
void* pa_queue_pop(pa_queue *q) {
void *p;
struct queue_entry *e;
- assert(q);
+ pa_assert(q);
if (!(e = q->front))
return NULL;
q->front = e->next;
- if (q->back == e)
+
+ if (q->back == e) {
+ pa_assert(!e->next);
q->back = NULL;
+ }
p = e->data;
- pa_xfree(e);
+
+ if (pa_flist_push(PA_STATIC_FLIST_GET(entries), e) < 0)
+ pa_xfree(e);
q->length--;
-
+
return p;
}
int pa_queue_is_empty(pa_queue *q) {
- assert(q);
+ pa_assert(q);
+
return q->length == 0;
}
diff --git a/src/pulsecore/queue.h b/src/pulsecore/queue.h
index 3fb9dea4..b09216cf 100644
--- a/src/pulsecore/queue.h
+++ b/src/pulsecore/queue.h
@@ -1,21 +1,21 @@
#ifndef fooqueuehfoo
#define fooqueuehfoo
-/* $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.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
diff --git a/src/pulsecore/random.c b/src/pulsecore/random.c
index 7908e87d..5deac37b 100644
--- a/src/pulsecore/random.c
+++ b/src/pulsecore/random.c
@@ -1,18 +1,19 @@
-/* $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.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
@@ -28,21 +29,22 @@
#include <errno.h>
#include <string.h>
#include <stdlib.h>
-#include <assert.h>
#include <time.h>
#include <pulsecore/core-util.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
#include "random.h"
static int has_whined = 0;
-static const char *devices[] = { "/dev/urandom", "/dev/random", NULL };
+static const char * const devices[] = { "/dev/urandom", "/dev/random", NULL };
static int random_proper(void *ret_data, size_t length) {
#ifdef OS_IS_WIN32
- assert(ret_data && length);
+ pa_assert(ret_data);
+ pa_assert(length > 0);
return -1;
@@ -50,21 +52,26 @@ static int random_proper(void *ret_data, size_t length) {
int fd, ret = -1;
ssize_t r = 0;
- const char **device;
+ const char *const * device;
- assert(ret_data && length);
+ pa_assert(ret_data);
+ pa_assert(length > 0);
device = devices;
while (*device) {
ret = 0;
- if ((fd = open(*device, O_RDONLY)) >= 0) {
+ if ((fd = open(*device, O_RDONLY
+#ifdef O_NOCTTY
+ | O_NOCTTY
+#endif
+ )) >= 0) {
if ((r = pa_loop_read(fd, ret_data, length, NULL)) < 0 || (size_t) r != length)
ret = -1;
- close(fd);
+ pa_close(fd);
} else
ret = -1;
@@ -81,7 +88,7 @@ void pa_random_seed(void) {
if (random_proper(&seed, sizeof(unsigned int)) < 0) {
if (!has_whined)
- pa_log_warn("failed to get proper entropy. Falling back to seeding with current time.");
+ pa_log_warn("Failed to get proper entropy. Falling back to seeding with current time.");
has_whined = 1;
seed = (unsigned int) time(NULL);
@@ -94,13 +101,14 @@ void pa_random(void *ret_data, size_t length) {
uint8_t *p;
size_t l;
- assert(ret_data && length);
+ pa_assert(ret_data);
+ pa_assert(length > 0);
if (random_proper(ret_data, length) >= 0)
return;
if (!has_whined)
- pa_log_warn("failed to get proper entropy. Falling back to unsecure pseudo RNG.");
+ pa_log_warn("Failed to get proper entropy. Falling back to unsecure pseudo RNG.");
has_whined = 1;
for (p = ret_data, l = length; l > 0; p++, l--)
diff --git a/src/pulsecore/random.h b/src/pulsecore/random.h
index b2bb3934..36d7f9df 100644
--- a/src/pulsecore/random.h
+++ b/src/pulsecore/random.h
@@ -1,21 +1,22 @@
#ifndef foorandomhfoo
#define foorandomhfoo
-/* $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.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
@@ -26,5 +27,5 @@
void pa_random_seed(void);
void pa_random(void *ret_data, size_t length);
-
+
#endif
diff --git a/src/pulsecore/refcnt.h b/src/pulsecore/refcnt.h
index 6eb5ee3f..291f4504 100644
--- a/src/pulsecore/refcnt.h
+++ b/src/pulsecore/refcnt.h
@@ -1,21 +1,21 @@
#ifndef foopulserefcnthfoo
#define foopulserefcnthfoo
-/* $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
@@ -25,18 +25,21 @@
#include <pulsecore/atomic.h>
#define PA_REFCNT_DECLARE \
- pa_atomic_int_t _ref
+ pa_atomic_t _ref
#define PA_REFCNT_INIT(p) \
- pa_atomic_store(&p->_ref, 1)
+ pa_atomic_store(&(p)->_ref, 1)
+
+#define PA_REFCNT_INIT_ZERO(p) \
+ pa_atomic_store(&(p)->_ref, 0)
#define PA_REFCNT_INC(p) \
- pa_atomic_inc(&p->_ref)
+ pa_atomic_inc(&(p)->_ref)
#define PA_REFCNT_DEC(p) \
- (pa_atomic_dec(&p->_ref)-1)
+ (pa_atomic_dec(&(p)->_ref)-1)
#define PA_REFCNT_VALUE(p) \
- pa_atomic_load(&p->_ref)
+ pa_atomic_load(&(p)->_ref)
#endif
diff --git a/src/pulsecore/resampler.c b/src/pulsecore/resampler.c
index c28c2fb3..00dc794c 100644
--- a/src/pulsecore/resampler.c
+++ b/src/pulsecore/resampler.c
@@ -1,18 +1,18 @@
-/* $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
@@ -23,52 +23,148 @@
#include <config.h>
#endif
-#include <assert.h>
#include <string.h>
+#if HAVE_LIBSAMPLERATE
#include <samplerate.h>
+#endif
+
#include <liboil/liboilfuncs.h>
#include <liboil/liboil.h>
#include <pulse/xmalloc.h>
-
#include <pulsecore/sconv.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/strbuf.h>
+
+#include "speexwrap.h"
+
+#include "ffmpeg/avcodec.h"
#include "resampler.h"
+/* Number of samples of extra space we allow the resamplers to return */
+#define EXTRA_FRAMES 128
+
struct pa_resampler {
- pa_resample_method_t resample_method;
+ pa_resample_method_t method;
+ pa_resample_flags_t flags;
+
pa_sample_spec i_ss, o_ss;
pa_channel_map i_cm, o_cm;
- size_t i_fz, o_fz;
+ size_t i_fz, o_fz, w_sz;
pa_mempool *mempool;
- void (*impl_free)(pa_resampler *r);
- void (*impl_update_input_rate)(pa_resampler *r, uint32_t rate);
- void (*impl_run)(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out);
- void *impl_data;
-};
-
-struct impl_libsamplerate {
pa_memchunk buf1, buf2, buf3, buf4;
unsigned buf1_samples, buf2_samples, buf3_samples, buf4_samples;
-
- pa_convert_to_float32ne_func_t to_float32ne_func;
- pa_convert_from_float32ne_func_t from_float32ne_func;
- SRC_STATE *src_state;
- int map_table[PA_CHANNELS_MAX][PA_CHANNELS_MAX];
- int map_required;
-};
+ pa_sample_format_t work_format;
+
+ pa_convert_func_t to_work_format_func;
+ pa_convert_func_t from_work_format_func;
+
+ float map_table[PA_CHANNELS_MAX][PA_CHANNELS_MAX];
+ pa_bool_t map_required;
+
+ void (*impl_free)(pa_resampler *r);
+ void (*impl_update_rates)(pa_resampler *r);
+ void (*impl_resample)(pa_resampler *r, const pa_memchunk *in, unsigned in_samples, pa_memchunk *out, unsigned *out_samples);
+ void (*impl_reset)(pa_resampler *r);
+
+ struct { /* data specific to the trivial resampler */
+ unsigned o_counter;
+ unsigned i_counter;
+ } trivial;
+
+ struct { /* data specific to the peak finder pseudo resampler */
+ unsigned o_counter;
+ unsigned i_counter;
+
+ float max_f[PA_CHANNELS_MAX];
+ int16_t max_i[PA_CHANNELS_MAX];
+
+ } peaks;
+
+#ifdef HAVE_LIBSAMPLERATE
+ struct { /* data specific to libsamplerate */
+ SRC_STATE *state;
+ } src;
+#endif
-struct impl_trivial {
- unsigned o_counter;
- unsigned i_counter;
+ struct { /* data specific to speex */
+ SpeexResamplerState* state;
+ } speex;
+
+ struct { /* data specific to ffmpeg */
+ struct AVResampleContext *state;
+ pa_memchunk buf[PA_CHANNELS_MAX];
+ } ffmpeg;
};
-static int libsamplerate_init(pa_resampler*r);
+static int copy_init(pa_resampler *r);
static int trivial_init(pa_resampler*r);
+static int speex_init(pa_resampler*r);
+static int ffmpeg_init(pa_resampler*r);
+static int peaks_init(pa_resampler*r);
+#ifdef HAVE_LIBSAMPLERATE
+static int libsamplerate_init(pa_resampler*r);
+#endif
+
+static void calc_map_table(pa_resampler *r);
+
+static int (* const init_table[])(pa_resampler*r) = {
+#ifdef HAVE_LIBSAMPLERATE
+ [PA_RESAMPLER_SRC_SINC_BEST_QUALITY] = libsamplerate_init,
+ [PA_RESAMPLER_SRC_SINC_MEDIUM_QUALITY] = libsamplerate_init,
+ [PA_RESAMPLER_SRC_SINC_FASTEST] = libsamplerate_init,
+ [PA_RESAMPLER_SRC_ZERO_ORDER_HOLD] = libsamplerate_init,
+ [PA_RESAMPLER_SRC_LINEAR] = libsamplerate_init,
+#else
+ [PA_RESAMPLER_SRC_SINC_BEST_QUALITY] = NULL,
+ [PA_RESAMPLER_SRC_SINC_MEDIUM_QUALITY] = NULL,
+ [PA_RESAMPLER_SRC_SINC_FASTEST] = NULL,
+ [PA_RESAMPLER_SRC_ZERO_ORDER_HOLD] = NULL,
+ [PA_RESAMPLER_SRC_LINEAR] = NULL,
+#endif
+ [PA_RESAMPLER_TRIVIAL] = trivial_init,
+ [PA_RESAMPLER_SPEEX_FLOAT_BASE+0] = speex_init,
+ [PA_RESAMPLER_SPEEX_FLOAT_BASE+1] = speex_init,
+ [PA_RESAMPLER_SPEEX_FLOAT_BASE+2] = speex_init,
+ [PA_RESAMPLER_SPEEX_FLOAT_BASE+3] = speex_init,
+ [PA_RESAMPLER_SPEEX_FLOAT_BASE+4] = speex_init,
+ [PA_RESAMPLER_SPEEX_FLOAT_BASE+5] = speex_init,
+ [PA_RESAMPLER_SPEEX_FLOAT_BASE+6] = speex_init,
+ [PA_RESAMPLER_SPEEX_FLOAT_BASE+7] = speex_init,
+ [PA_RESAMPLER_SPEEX_FLOAT_BASE+8] = speex_init,
+ [PA_RESAMPLER_SPEEX_FLOAT_BASE+9] = speex_init,
+ [PA_RESAMPLER_SPEEX_FLOAT_BASE+10] = speex_init,
+ [PA_RESAMPLER_SPEEX_FIXED_BASE+0] = speex_init,
+ [PA_RESAMPLER_SPEEX_FIXED_BASE+1] = speex_init,
+ [PA_RESAMPLER_SPEEX_FIXED_BASE+2] = speex_init,
+ [PA_RESAMPLER_SPEEX_FIXED_BASE+3] = speex_init,
+ [PA_RESAMPLER_SPEEX_FIXED_BASE+4] = speex_init,
+ [PA_RESAMPLER_SPEEX_FIXED_BASE+5] = speex_init,
+ [PA_RESAMPLER_SPEEX_FIXED_BASE+6] = speex_init,
+ [PA_RESAMPLER_SPEEX_FIXED_BASE+7] = speex_init,
+ [PA_RESAMPLER_SPEEX_FIXED_BASE+8] = speex_init,
+ [PA_RESAMPLER_SPEEX_FIXED_BASE+9] = speex_init,
+ [PA_RESAMPLER_SPEEX_FIXED_BASE+10] = speex_init,
+ [PA_RESAMPLER_FFMPEG] = ffmpeg_init,
+ [PA_RESAMPLER_AUTO] = NULL,
+ [PA_RESAMPLER_COPY] = copy_init,
+ [PA_RESAMPLER_PEAKS] = peaks_init,
+};
+
+static inline size_t sample_size(pa_sample_format_t f) {
+ pa_sample_spec ss = {
+ .format = f,
+ .rate = 0,
+ .channels = 1
+ };
+
+ return pa_sample_size(&ss);
+}
pa_resampler* pa_resampler_new(
pa_mempool *pool,
@@ -76,25 +172,53 @@ pa_resampler* pa_resampler_new(
const pa_channel_map *am,
const pa_sample_spec *b,
const pa_channel_map *bm,
- pa_resample_method_t resample_method) {
-
+ pa_resample_method_t method,
+ pa_resample_flags_t flags) {
+
pa_resampler *r = NULL;
- assert(pool);
- assert(a);
- assert(b);
- assert(pa_sample_spec_valid(a));
- assert(pa_sample_spec_valid(b));
- assert(resample_method != PA_RESAMPLER_INVALID);
+ pa_assert(pool);
+ pa_assert(a);
+ pa_assert(b);
+ pa_assert(pa_sample_spec_valid(a));
+ pa_assert(pa_sample_spec_valid(b));
+ pa_assert(method >= 0);
+ pa_assert(method < PA_RESAMPLER_MAX);
+
+ /* Fix method */
+
+ if (!(flags & PA_RESAMPLER_VARIABLE_RATE) && a->rate == b->rate) {
+ pa_log_info("Forcing resampler 'copy', because of fixed, identical sample rates.");
+ method = PA_RESAMPLER_COPY;
+ }
+
+ if (!pa_resample_method_supported(method)) {
+ pa_log_warn("Support for resampler '%s' not compiled in, reverting to 'auto'.", pa_resample_method_to_string(method));
+ method = PA_RESAMPLER_AUTO;
+ }
+
+ if (method == PA_RESAMPLER_FFMPEG && (flags & PA_RESAMPLER_VARIABLE_RATE)) {
+ pa_log_info("Resampler 'ffmpeg' cannot do variable rate, reverting to resampler 'auto'.");
+ method = PA_RESAMPLER_AUTO;
+ }
+
+ if (method == PA_RESAMPLER_COPY && ((flags & PA_RESAMPLER_VARIABLE_RATE) || a->rate != b->rate)) {
+ pa_log_info("Resampler 'copy' cannot change sampling rate, reverting to resampler 'auto'.");
+ method = PA_RESAMPLER_AUTO;
+ }
+
+ if (method == PA_RESAMPLER_AUTO)
+ method = PA_RESAMPLER_SPEEX_FLOAT_BASE + 3;
r = pa_xnew(pa_resampler, 1);
- r->impl_data = NULL;
r->mempool = pool;
- r->resample_method = resample_method;
+ r->method = method;
+ r->flags = flags;
r->impl_free = NULL;
- r->impl_update_input_rate = NULL;
- r->impl_run = NULL;
+ r->impl_update_rates = NULL;
+ r->impl_resample = NULL;
+ r->impl_reset = NULL;
/* Fill sample specs */
r->i_ss = *a;
@@ -102,82 +226,177 @@ pa_resampler* pa_resampler_new(
if (am)
r->i_cm = *am;
- else
- pa_channel_map_init_auto(&r->i_cm, r->i_ss.channels, PA_CHANNEL_MAP_DEFAULT);
+ else if (!pa_channel_map_init_auto(&r->i_cm, r->i_ss.channels, PA_CHANNEL_MAP_DEFAULT))
+ goto fail;
if (bm)
r->o_cm = *bm;
- else
- pa_channel_map_init_auto(&r->o_cm, r->o_ss.channels, PA_CHANNEL_MAP_DEFAULT);
-
+ else if (!pa_channel_map_init_auto(&r->o_cm, r->o_ss.channels, PA_CHANNEL_MAP_DEFAULT))
+ goto fail;
+
r->i_fz = pa_frame_size(a);
r->o_fz = pa_frame_size(b);
- /* Choose implementation */
- if (a->channels != b->channels ||
- a->format != b->format ||
- !pa_channel_map_equal(&r->i_cm, &r->o_cm) ||
- resample_method != PA_RESAMPLER_TRIVIAL) {
+ pa_memchunk_reset(&r->buf1);
+ pa_memchunk_reset(&r->buf2);
+ pa_memchunk_reset(&r->buf3);
+ pa_memchunk_reset(&r->buf4);
- /* Use the libsamplerate based resampler for the complicated cases */
- if (resample_method == PA_RESAMPLER_TRIVIAL)
- r->resample_method = PA_RESAMPLER_SRC_ZERO_ORDER_HOLD;
+ r->buf1_samples = r->buf2_samples = r->buf3_samples = r->buf4_samples = 0;
- if (libsamplerate_init(r) < 0)
+ calc_map_table(r);
+
+ pa_log_info("Using resampler '%s'", pa_resample_method_to_string(method));
+
+ if ((method >= PA_RESAMPLER_SPEEX_FIXED_BASE && method <= PA_RESAMPLER_SPEEX_FIXED_MAX) ||
+ (method == PA_RESAMPLER_FFMPEG))
+ r->work_format = PA_SAMPLE_S16NE;
+ else if (method == PA_RESAMPLER_TRIVIAL || method == PA_RESAMPLER_COPY || method == PA_RESAMPLER_PEAKS) {
+
+ if (r->map_required || a->format != b->format || method == PA_RESAMPLER_PEAKS) {
+
+ if (a->format == PA_SAMPLE_S32NE || a->format == PA_SAMPLE_S32RE ||
+ a->format == PA_SAMPLE_FLOAT32NE || a->format == PA_SAMPLE_FLOAT32RE ||
+ b->format == PA_SAMPLE_S32NE || b->format == PA_SAMPLE_S32RE ||
+ b->format == PA_SAMPLE_FLOAT32NE || b->format == PA_SAMPLE_FLOAT32RE)
+ r->work_format = PA_SAMPLE_FLOAT32NE;
+ else
+ r->work_format = PA_SAMPLE_S16NE;
+
+ } else
+ r->work_format = a->format;
+
+ } else
+ r->work_format = PA_SAMPLE_FLOAT32NE;
+
+ pa_log_info("Using %s as working format.", pa_sample_format_to_string(r->work_format));
+
+ r->w_sz = sample_size(r->work_format);
+
+ if (r->i_ss.format == r->work_format)
+ r->to_work_format_func = NULL;
+ else if (r->work_format == PA_SAMPLE_FLOAT32NE) {
+ if (!(r->to_work_format_func = pa_get_convert_to_float32ne_function(r->i_ss.format)))
goto fail;
-
} else {
- /* Use our own simple non-fp resampler for the trivial cases and when the user selects it */
- if (trivial_init(r) < 0)
+ pa_assert(r->work_format == PA_SAMPLE_S16NE);
+ if (!(r->to_work_format_func = pa_get_convert_to_s16ne_function(r->i_ss.format)))
goto fail;
}
-
+
+ if (r->o_ss.format == r->work_format)
+ r->from_work_format_func = NULL;
+ else if (r->work_format == PA_SAMPLE_FLOAT32NE) {
+ if (!(r->from_work_format_func = pa_get_convert_from_float32ne_function(r->o_ss.format)))
+ goto fail;
+ } else {
+ pa_assert(r->work_format == PA_SAMPLE_S16NE);
+ if (!(r->from_work_format_func = pa_get_convert_from_s16ne_function(r->o_ss.format)))
+ goto fail;
+ }
+
+ /* initialize implementation */
+ if (init_table[method](r) < 0)
+ goto fail;
+
return r;
-
+
fail:
if (r)
pa_xfree(r);
-
+
return NULL;
}
void pa_resampler_free(pa_resampler *r) {
- assert(r);
+ pa_assert(r);
if (r->impl_free)
r->impl_free(r);
-
+
+ if (r->buf1.memblock)
+ pa_memblock_unref(r->buf1.memblock);
+ if (r->buf2.memblock)
+ pa_memblock_unref(r->buf2.memblock);
+ if (r->buf3.memblock)
+ pa_memblock_unref(r->buf3.memblock);
+ if (r->buf4.memblock)
+ pa_memblock_unref(r->buf4.memblock);
+
pa_xfree(r);
}
void pa_resampler_set_input_rate(pa_resampler *r, uint32_t rate) {
- assert(r);
- assert(rate > 0);
+ pa_assert(r);
+ pa_assert(rate > 0);
if (r->i_ss.rate == rate)
return;
-
+
r->i_ss.rate = rate;
-
- if (r->impl_update_input_rate)
- r->impl_update_input_rate(r, rate);
+
+ r->impl_update_rates(r);
}
-void pa_resampler_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out) {
- assert(r && in && out && r->impl_run);
+void pa_resampler_set_output_rate(pa_resampler *r, uint32_t rate) {
+ pa_assert(r);
+ pa_assert(rate > 0);
- r->impl_run(r, in, out);
+ if (r->o_ss.rate == rate)
+ return;
+
+ r->o_ss.rate = rate;
+
+ r->impl_update_rates(r);
}
size_t pa_resampler_request(pa_resampler *r, size_t out_length) {
- assert(r);
-
+ pa_assert(r);
+
return (((out_length / r->o_fz)*r->i_ss.rate)/r->o_ss.rate) * r->i_fz;
}
+size_t pa_resampler_result(pa_resampler *r, size_t in_length) {
+ pa_assert(r);
+
+ return (((in_length / r->i_fz)*r->o_ss.rate)/r->i_ss.rate) * r->o_fz;
+}
+
+size_t pa_resampler_max_block_size(pa_resampler *r) {
+ size_t block_size_max;
+ pa_sample_spec ss;
+ size_t fs;
+
+ pa_assert(r);
+
+ block_size_max = pa_mempool_block_size_max(r->mempool);
+
+ /* We deduce the "largest" sample spec we're using during the
+ * conversion */
+ ss.channels = PA_MAX(r->i_ss.channels, r->o_ss.channels);
+
+ /* We silently assume that the format enum is ordered by size */
+ ss.format = PA_MAX(r->i_ss.format, r->o_ss.format);
+ ss.format = PA_MAX(ss.format, r->work_format);
+
+ ss.rate = PA_MAX(r->i_ss.rate, r->o_ss.rate);
+
+ fs = pa_frame_size(&ss);
+
+ return (((block_size_max/fs - EXTRA_FRAMES)*r->i_ss.rate)/ss.rate)*r->i_fz;
+}
+
+void pa_resampler_reset(pa_resampler *r) {
+ pa_assert(r);
+
+ if (r->impl_reset)
+ r->impl_reset(r);
+}
+
pa_resample_method_t pa_resampler_get_method(pa_resampler *r) {
- assert(r);
- return r->resample_method;
+ pa_assert(r);
+
+ return r->method;
}
static const char * const resample_methods[] = {
@@ -186,7 +405,33 @@ static const char * const resample_methods[] = {
"src-sinc-fastest",
"src-zero-order-hold",
"src-linear",
- "trivial"
+ "trivial",
+ "speex-float-0",
+ "speex-float-1",
+ "speex-float-2",
+ "speex-float-3",
+ "speex-float-4",
+ "speex-float-5",
+ "speex-float-6",
+ "speex-float-7",
+ "speex-float-8",
+ "speex-float-9",
+ "speex-float-10",
+ "speex-fixed-0",
+ "speex-fixed-1",
+ "speex-fixed-2",
+ "speex-fixed-3",
+ "speex-fixed-4",
+ "speex-fixed-5",
+ "speex-fixed-6",
+ "speex-fixed-7",
+ "speex-fixed-8",
+ "speex-fixed-9",
+ "speex-fixed-10",
+ "ffmpeg",
+ "auto",
+ "copy",
+ "peaks"
};
const char *pa_resample_method_to_string(pa_resample_method_t m) {
@@ -197,288 +442,718 @@ const char *pa_resample_method_to_string(pa_resample_method_t m) {
return resample_methods[m];
}
+int pa_resample_method_supported(pa_resample_method_t m) {
+
+ if (m < 0 || m >= PA_RESAMPLER_MAX)
+ return 0;
+
+#ifndef HAVE_LIBSAMPLERATE
+ if (m <= PA_RESAMPLER_SRC_LINEAR)
+ return 0;
+#endif
+
+ return 1;
+}
+
pa_resample_method_t pa_parse_resample_method(const char *string) {
pa_resample_method_t m;
-
- assert(string);
+
+ pa_assert(string);
for (m = 0; m < PA_RESAMPLER_MAX; m++)
if (!strcmp(string, resample_methods[m]))
return m;
+ if (!strcmp(string, "speex-fixed"))
+ return PA_RESAMPLER_SPEEX_FIXED_BASE + 3;
+
+ if (!strcmp(string, "speex-float"))
+ return PA_RESAMPLER_SPEEX_FLOAT_BASE + 3;
+
return PA_RESAMPLER_INVALID;
}
+static pa_bool_t on_left(pa_channel_position_t p) {
-/*** libsamplerate based implementation ***/
+ return
+ p == PA_CHANNEL_POSITION_FRONT_LEFT ||
+ p == PA_CHANNEL_POSITION_REAR_LEFT ||
+ p == PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER ||
+ p == PA_CHANNEL_POSITION_SIDE_LEFT ||
+ p == PA_CHANNEL_POSITION_TOP_FRONT_LEFT ||
+ p == PA_CHANNEL_POSITION_TOP_REAR_LEFT;
+}
-static void libsamplerate_free(pa_resampler *r) {
- struct impl_libsamplerate *u;
-
- assert(r);
- assert(r->impl_data);
-
- u = r->impl_data;
-
- if (u->src_state)
- src_delete(u->src_state);
-
- if (u->buf1.memblock)
- pa_memblock_unref(u->buf1.memblock);
- if (u->buf2.memblock)
- pa_memblock_unref(u->buf2.memblock);
- if (u->buf3.memblock)
- pa_memblock_unref(u->buf3.memblock);
- if (u->buf4.memblock)
- pa_memblock_unref(u->buf4.memblock);
- pa_xfree(u);
+static pa_bool_t on_right(pa_channel_position_t p) {
+
+ return
+ p == PA_CHANNEL_POSITION_FRONT_RIGHT ||
+ p == PA_CHANNEL_POSITION_REAR_RIGHT ||
+ p == PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER ||
+ p == PA_CHANNEL_POSITION_SIDE_RIGHT ||
+ p == PA_CHANNEL_POSITION_TOP_FRONT_RIGHT ||
+ p == PA_CHANNEL_POSITION_TOP_REAR_RIGHT;
+}
+
+static pa_bool_t on_center(pa_channel_position_t p) {
+
+ return
+ p == PA_CHANNEL_POSITION_FRONT_CENTER ||
+ p == PA_CHANNEL_POSITION_REAR_CENTER ||
+ p == PA_CHANNEL_POSITION_TOP_CENTER ||
+ p == PA_CHANNEL_POSITION_TOP_FRONT_CENTER ||
+ p == PA_CHANNEL_POSITION_TOP_REAR_CENTER;
+}
+
+static pa_bool_t on_lfe(pa_channel_position_t p) {
+ return
+ p == PA_CHANNEL_POSITION_LFE;
}
static void calc_map_table(pa_resampler *r) {
- struct impl_libsamplerate *u;
- unsigned oc;
- assert(r);
- assert(r->impl_data);
+ unsigned oc, ic;
+ pa_bool_t ic_connected[PA_CHANNELS_MAX];
+ pa_bool_t remix;
+ pa_strbuf *s;
+ char *t;
- u = r->impl_data;
+ pa_assert(r);
- if (!(u->map_required = (!pa_channel_map_equal(&r->i_cm, &r->o_cm) || r->i_ss.channels != r->o_ss.channels)))
+ if (!(r->map_required = (r->i_ss.channels != r->o_ss.channels || (!(r->flags & PA_RESAMPLER_NO_REMAP) && !pa_channel_map_equal(&r->i_cm, &r->o_cm)))))
return;
+ memset(r->map_table, 0, sizeof(r->map_table));
+ memset(ic_connected, 0, sizeof(ic_connected));
+ remix = (r->flags & (PA_RESAMPLER_NO_REMAP|PA_RESAMPLER_NO_REMIX)) == 0;
+
for (oc = 0; oc < r->o_ss.channels; oc++) {
- unsigned ic, i = 0;
+ pa_bool_t oc_connected = FALSE;
+ pa_channel_position_t b = r->o_cm.map[oc];
for (ic = 0; ic < r->i_ss.channels; ic++) {
- pa_channel_position_t a, b;
-
- a = r->i_cm.map[ic];
- b = r->o_cm.map[oc];
-
- if (a == b ||
- (a == PA_CHANNEL_POSITION_MONO && b == PA_CHANNEL_POSITION_LEFT) ||
- (a == PA_CHANNEL_POSITION_MONO && b == PA_CHANNEL_POSITION_RIGHT) ||
- (a == PA_CHANNEL_POSITION_LEFT && b == PA_CHANNEL_POSITION_MONO) ||
- (a == PA_CHANNEL_POSITION_RIGHT && b == PA_CHANNEL_POSITION_MONO))
-
- u->map_table[oc][i++] = ic;
+ pa_channel_position_t a = r->i_cm.map[ic];
+
+ if (r->flags & PA_RESAMPLER_NO_REMAP) {
+ /* We shall not do any remapping. Hence, just check by index */
+
+ if (ic == oc)
+ r->map_table[oc][ic] = 1.0;
+
+ continue;
+ }
+
+ if (r->flags & PA_RESAMPLER_NO_REMIX) {
+ /* We shall not do any remixing. Hence, just check by name */
+
+ if (a == b)
+ r->map_table[oc][ic] = 1.0;
+
+ continue;
+ }
+
+ pa_assert(remix);
+
+ /* OK, we shall do the full monty: upmixing and
+ * downmixing. Our algorithm is relatively simple, does
+ * not do spacialization, delay elements or apply lowpass
+ * filters for LFE. Patches are always welcome,
+ * though. Oh, and it doesn't do any matrix
+ * decoding. (Which probably wouldn't make any sense
+ * anyway.)
+ *
+ * This code is not idempotent: downmixing an upmixed
+ * stereo stream is not identical to the original. The
+ * volume will not match, and the two channels will be a
+ * linear combination of both.
+ *
+ * This is losely based on random suggestions found on the
+ * Internet, such as this:
+ * http://www.halfgaar.net/surround-sound-in-linux and the
+ * alsa upmix plugin.
+ *
+ * The algorithm works basically like this:
+ *
+ * 1) Connect all channels with matching names.
+ *
+ * 2) Mono Handling:
+ * S:Mono: Copy into all D:channels
+ * D:Mono: Copy in all S:channels
+ *
+ * 3) Mix D:Left, D:Right:
+ * D:Left: If not connected, avg all S:Left
+ * D:Right: If not connected, avg all S:Right
+ *
+ * 4) Mix D:Center
+ * If not connected, avg all S:Center
+ * If still not connected, avg all S:Left, S:Right
+ *
+ * 5) Mix D:LFE
+ * If not connected, avg all S:*
+ *
+ * 6) Make sure S:Left/S:Right is used: S:Left/S:Right: If
+ * not connected, mix into all D:left and all D:right
+ * channels. Gain is 0.1, the current left and right
+ * should be multiplied by 0.9.
+ *
+ * 7) Make sure S:Center, S:LFE is used:
+ *
+ * S:Center, S:LFE: If not connected, mix into all
+ * D:left, all D:right, all D:center channels, gain is
+ * 0.375. The current (as result of 1..6) factors
+ * should be multiplied by 0.75. (Alt. suggestion: 0.25
+ * vs. 0.5)
+ *
+ * S: and D: shall relate to the source resp. destination channels.
+ *
+ * Rationale: 1, 2 are probably obvious. For 3: this
+ * copies front to rear if needed. For 4: we try to find
+ * some suitable C source for C, if we don't find any, we
+ * avg L and R. For 5: LFE is mixed from all channels. For
+ * 6: the rear channels should not be dropped entirely,
+ * however have only minimal impact. For 7: movies usually
+ * encode speech on the center channel. Thus we have to
+ * make sure this channel is distributed to L and R if not
+ * available in the output. Also, LFE is used to achieve a
+ * greater dynamic range, and thus we should try to do our
+ * best to pass it to L+R.
+ */
+
+ if (a == b || a == PA_CHANNEL_POSITION_MONO || b == PA_CHANNEL_POSITION_MONO) {
+ r->map_table[oc][ic] = 1.0;
+
+ oc_connected = TRUE;
+ ic_connected[ic] = TRUE;
+ }
}
- /* Add an end marker */
- if (i < PA_CHANNELS_MAX)
- u->map_table[oc][i] = -1;
+ if (!oc_connected && remix) {
+ /* OK, we shall remix */
+
+ if (on_left(b)) {
+ unsigned n = 0;
+
+ /* We are not connected and on the left side, let's
+ * average all left side input channels. */
+
+ for (ic = 0; ic < r->i_ss.channels; ic++)
+ if (on_left(r->i_cm.map[ic]))
+ n++;
+
+ if (n > 0)
+ for (ic = 0; ic < r->i_ss.channels; ic++)
+ if (on_left(r->i_cm.map[ic])) {
+ r->map_table[oc][ic] = 1.0 / n;
+ ic_connected[ic] = TRUE;
+ }
+
+ /* We ignore the case where there is no left input
+ * channel. Something is really wrong in this case
+ * anyway. */
+
+ } else if (on_right(b)) {
+ unsigned n = 0;
+
+ /* We are not connected and on the right side, let's
+ * average all right side input channels. */
+
+ for (ic = 0; ic < r->i_ss.channels; ic++)
+ if (on_right(r->i_cm.map[ic]))
+ n++;
+
+ if (n > 0)
+ for (ic = 0; ic < r->i_ss.channels; ic++)
+ if (on_right(r->i_cm.map[ic])) {
+ r->map_table[oc][ic] = 1.0 / n;
+ ic_connected[ic] = TRUE;
+ }
+
+ /* We ignore the case where there is no right input
+ * channel. Something is really wrong in this case
+ * anyway. */
+
+ } else if (on_center(b)) {
+ unsigned n = 0;
+
+ /* We are not connected and at the center. Let's
+ * average all center input channels. */
+
+ for (ic = 0; ic < r->i_ss.channels; ic++)
+ if (on_center(r->i_cm.map[ic]))
+ n++;
+
+ if (n > 0) {
+ for (ic = 0; ic < r->i_ss.channels; ic++)
+ if (on_center(r->i_cm.map[ic])) {
+ r->map_table[oc][ic] = 1.0 / n;
+ ic_connected[ic] = TRUE;
+ }
+ } else {
+
+ /* Hmm, no center channel around, let's synthesize
+ * it by mixing L and R.*/
+
+ n = 0;
+
+ for (ic = 0; ic < r->i_ss.channels; ic++)
+ if (on_left(r->i_cm.map[ic]) || on_right(r->i_cm.map[ic]))
+ n++;
+
+ if (n > 0)
+ for (ic = 0; ic < r->i_ss.channels; ic++)
+ if (on_left(r->i_cm.map[ic]) || on_right(r->i_cm.map[ic])) {
+ r->map_table[oc][ic] = 1.0 / n;
+ ic_connected[ic] = TRUE;
+ }
+
+ /* We ignore the case where there is not even a
+ * left or right input channel. Something is
+ * really wrong in this case anyway. */
+ }
+
+ } else if (on_lfe(b)) {
+
+ /* We are not connected and an LFE. Let's average all
+ * channels for LFE. */
+
+ for (ic = 0; ic < r->i_ss.channels; ic++) {
+ r->map_table[oc][ic] = 1.0 / r->i_ss.channels;
+
+ /* Please note that a channel connected to LFE
+ * doesn't really count as connected. */
+ }
+ }
+ }
}
+
+ if (remix) {
+ unsigned
+ ic_unconnected_left = 0,
+ ic_unconnected_right = 0,
+ ic_unconnected_center = 0,
+ ic_unconnected_lfe = 0;
+
+ for (ic = 0; ic < r->i_ss.channels; ic++) {
+ pa_channel_position_t a = r->i_cm.map[ic];
+
+ if (ic_connected[ic])
+ continue;
+
+ if (on_left(a))
+ ic_unconnected_left++;
+ else if (on_right(a))
+ ic_unconnected_right++;
+ else if (on_center(a))
+ ic_unconnected_center++;
+ else if (on_lfe(a))
+ ic_unconnected_lfe++;
+ }
+
+ if (ic_unconnected_left > 0) {
+
+ /* OK, so there are unconnected input channels on the
+ * left. Let's multiply all already connected channels on
+ * the left side by .9 and add in our averaged unconnected
+ * channels multplied by .1 */
+
+ for (oc = 0; oc < r->o_ss.channels; oc++) {
+
+ if (!on_left(r->o_cm.map[oc]))
+ continue;
+
+ for (ic = 0; ic < r->i_ss.channels; ic++) {
+
+ if (ic_connected[ic]) {
+ r->map_table[oc][ic] *= .9;
+ continue;
+ }
+
+ if (on_left(r->i_cm.map[ic]))
+ r->map_table[oc][ic] = .1 / ic_unconnected_left;
+ }
+ }
+ }
+
+ if (ic_unconnected_right > 0) {
+
+ /* OK, so there are unconnected input channels on the
+ * right. Let's multiply all already connected channels on
+ * the right side by .9 and add in our averaged unconnected
+ * channels multplied by .1 */
+
+ for (oc = 0; oc < r->o_ss.channels; oc++) {
+
+ if (!on_right(r->o_cm.map[oc]))
+ continue;
+
+ for (ic = 0; ic < r->i_ss.channels; ic++) {
+
+ if (ic_connected[ic]) {
+ r->map_table[oc][ic] *= .9;
+ continue;
+ }
+
+ if (on_right(r->i_cm.map[ic]))
+ r->map_table[oc][ic] = .1 / ic_unconnected_right;
+ }
+ }
+ }
+
+ if (ic_unconnected_center > 0) {
+ pa_bool_t mixed_in = FALSE;
+
+ /* OK, so there are unconnected input channels on the
+ * center. Let's multiply all already connected channels on
+ * the center side by .9 and add in our averaged unconnected
+ * channels multplied by .1 */
+
+ for (oc = 0; oc < r->o_ss.channels; oc++) {
+
+ if (!on_center(r->o_cm.map[oc]))
+ continue;
+
+ for (ic = 0; ic < r->i_ss.channels; ic++) {
+
+ if (ic_connected[ic]) {
+ r->map_table[oc][ic] *= .9;
+ continue;
+ }
+
+ if (on_center(r->i_cm.map[ic])) {
+ r->map_table[oc][ic] = .1 / ic_unconnected_center;
+ mixed_in = TRUE;
+ }
+ }
+ }
+
+ if (!mixed_in) {
+
+ /* Hmm, as it appears there was no center channel we
+ could mix our center channel in. In this case, mix
+ it into left and right. Using .375 and 0.75 as
+ factors. */
+
+ for (oc = 0; oc < r->o_ss.channels; oc++) {
+
+ if (!on_left(r->o_cm.map[oc]) && !on_right(r->o_cm.map[oc]))
+ continue;
+
+ for (ic = 0; ic < r->i_ss.channels; ic++) {
+
+ if (ic_connected[ic]) {
+ r->map_table[oc][ic] *= .75;
+ continue;
+ }
+
+ if (on_center(r->i_cm.map[ic]))
+ r->map_table[oc][ic] = .375 / ic_unconnected_center;
+ }
+ }
+ }
+ }
+
+ if (ic_unconnected_lfe > 0) {
+
+ /* OK, so there is an unconnected LFE channel. Let's mix
+ * it into all channels, with factor 0.375 */
+
+ for (ic = 0; ic < r->i_ss.channels; ic++) {
+
+ if (!on_lfe(r->i_cm.map[ic]))
+ continue;
+
+ for (oc = 0; oc < r->o_ss.channels; oc++)
+ r->map_table[oc][ic] = 0.375 / ic_unconnected_lfe;
+ }
+ }
+ }
+
+
+ s = pa_strbuf_new();
+
+ pa_strbuf_printf(s, " ");
+ for (ic = 0; ic < r->i_ss.channels; ic++)
+ pa_strbuf_printf(s, " I%02u ", ic);
+ pa_strbuf_puts(s, "\n +");
+
+ for (ic = 0; ic < r->i_ss.channels; ic++)
+ pa_strbuf_printf(s, "------");
+ pa_strbuf_puts(s, "\n");
+
+ for (oc = 0; oc < r->o_ss.channels; oc++) {
+ pa_strbuf_printf(s, "O%02u |", oc);
+
+ for (ic = 0; ic < r->i_ss.channels; ic++)
+ pa_strbuf_printf(s, " %1.3f", r->map_table[oc][ic]);
+
+ pa_strbuf_puts(s, "\n");
+ }
+
+ pa_log_debug("Channel matrix:\n%s", t = pa_strbuf_tostring_free(s));
+ pa_xfree(t);
}
-static pa_memchunk* convert_to_float(pa_resampler *r, pa_memchunk *input) {
- struct impl_libsamplerate *u;
+static pa_memchunk* convert_to_work_format(pa_resampler *r, pa_memchunk *input) {
unsigned n_samples;
void *src, *dst;
- assert(r);
- assert(input);
- assert(input->memblock);
-
- assert(r->impl_data);
- u = r->impl_data;
-
- /* Convert the incoming sample into floats and place them in buf1 */
+ pa_assert(r);
+ pa_assert(input);
+ pa_assert(input->memblock);
- if (!u->to_float32ne_func || !input->length)
+ /* Convert the incoming sample into the work sample format and place them in buf1 */
+
+ if (!r->to_work_format_func || !input->length)
return input;
-
+
n_samples = (input->length / r->i_fz) * r->i_ss.channels;
- if (!u->buf1.memblock || u->buf1_samples < n_samples) {
- if (u->buf1.memblock)
- pa_memblock_unref(u->buf1.memblock);
+ r->buf1.index = 0;
+ r->buf1.length = r->w_sz * n_samples;
+
+ if (!r->buf1.memblock || r->buf1_samples < n_samples) {
+ if (r->buf1.memblock)
+ pa_memblock_unref(r->buf1.memblock);
- u->buf1_samples = n_samples;
- u->buf1.memblock = pa_memblock_new(r->mempool, u->buf1.length = sizeof(float) * n_samples);
- u->buf1.index = 0;
+ r->buf1_samples = n_samples;
+ r->buf1.memblock = pa_memblock_new(r->mempool, r->buf1.length);
}
src = (uint8_t*) pa_memblock_acquire(input->memblock) + input->index;
- dst = (uint8_t*) pa_memblock_acquire(u->buf1.memblock);
- u->to_float32ne_func(n_samples, src, dst);
+ dst = (uint8_t*) pa_memblock_acquire(r->buf1.memblock);
+
+ r->to_work_format_func(n_samples, src, dst);
+
pa_memblock_release(input->memblock);
- pa_memblock_release(u->buf1.memblock);
+ pa_memblock_release(r->buf1.memblock);
+
+ return &r->buf1;
+}
+
+static void vectoradd_s16_with_fraction(
+ int16_t *d, int dstr,
+ const int16_t *s1, int sstr1,
+ const int16_t *s2, int sstr2,
+ int n,
+ float s3, float s4) {
+
+ int32_t i3, i4;
- u->buf1.length = sizeof(float) * n_samples;
+ i3 = (int32_t) (s3 * 0x10000);
+ i4 = (int32_t) (s4 * 0x10000);
- return &u->buf1;
+ for (; n > 0; n--) {
+ int32_t a, b;
+
+ a = *s1;
+ b = *s2;
+
+ a = (a * i3) / 0x10000;
+ b = (b * i4) / 0x10000;
+
+ *d = (int16_t) (a + b);
+
+ s1 = (const int16_t*) ((const uint8_t*) s1 + sstr1);
+ s2 = (const int16_t*) ((const uint8_t*) s2 + sstr2);
+ d = (int16_t*) ((uint8_t*) d + dstr);
+
+ }
}
static pa_memchunk *remap_channels(pa_resampler *r, pa_memchunk *input) {
- struct impl_libsamplerate *u;
- unsigned n_samples, n_frames;
+ unsigned in_n_samples, out_n_samples, n_frames;
int i_skip, o_skip;
unsigned oc;
- float *src, *dst;
-
- assert(r);
- assert(input);
- assert(input->memblock);
-
- assert(r->impl_data);
- u = r->impl_data;
+ void *src, *dst;
+
+ pa_assert(r);
+ pa_assert(input);
+ pa_assert(input->memblock);
/* Remap channels and place the result int buf2 */
-
- if (!u->map_required || !input->length)
+
+ if (!r->map_required || !input->length)
return input;
- n_samples = input->length / sizeof(float);
- n_frames = n_samples / r->o_ss.channels;
+ in_n_samples = input->length / r->w_sz;
+ n_frames = in_n_samples / r->i_ss.channels;
+ out_n_samples = n_frames * r->o_ss.channels;
- if (!u->buf2.memblock || u->buf2_samples < n_samples) {
- if (u->buf2.memblock)
- pa_memblock_unref(u->buf2.memblock);
+ r->buf2.index = 0;
+ r->buf2.length = r->w_sz * out_n_samples;
- u->buf2_samples = n_samples;
- u->buf2.memblock = pa_memblock_new(r->mempool, u->buf2.length = sizeof(float) * n_samples);
- u->buf2.index = 0;
+ if (!r->buf2.memblock || r->buf2_samples < out_n_samples) {
+ if (r->buf2.memblock)
+ pa_memblock_unref(r->buf2.memblock);
+
+ r->buf2_samples = out_n_samples;
+ r->buf2.memblock = pa_memblock_new(r->mempool, r->buf2.length);
}
- src = (float*) ((uint8_t*) pa_memblock_acquire(input->memblock) + input->index);
- dst = (float*) pa_memblock_acquire(u->buf2.memblock);
-
- memset(dst, 0, n_samples * sizeof(float));
+ src = ((uint8_t*) pa_memblock_acquire(input->memblock) + input->index);
+ dst = pa_memblock_acquire(r->buf2.memblock);
- o_skip = sizeof(float) * r->o_ss.channels;
- i_skip = sizeof(float) * r->i_ss.channels;
-
- for (oc = 0; oc < r->o_ss.channels; oc++) {
- unsigned i;
- static const float one = 1.0;
-
- for (i = 0; i < PA_CHANNELS_MAX && u->map_table[oc][i] >= 0; i++)
- oil_vectoradd_f32(
- dst + oc, o_skip,
- dst + oc, o_skip,
- src + u->map_table[oc][i], i_skip,
- n_frames,
- &one, &one);
+ memset(dst, 0, r->buf2.length);
+
+ o_skip = r->w_sz * r->o_ss.channels;
+ i_skip = r->w_sz * r->i_ss.channels;
+
+ switch (r->work_format) {
+ case PA_SAMPLE_FLOAT32NE:
+
+ for (oc = 0; oc < r->o_ss.channels; oc++) {
+ unsigned ic;
+ static const float one = 1.0;
+
+ for (ic = 0; ic < r->i_ss.channels; ic++) {
+
+ if (r->map_table[oc][ic] <= 0.0)
+ continue;
+
+ oil_vectoradd_f32(
+ (float*) dst + oc, o_skip,
+ (float*) dst + oc, o_skip,
+ (float*) src + ic, i_skip,
+ n_frames,
+ &one, &r->map_table[oc][ic]);
+ }
+ }
+
+ break;
+
+ case PA_SAMPLE_S16NE:
+
+ for (oc = 0; oc < r->o_ss.channels; oc++) {
+ unsigned ic;
+
+ for (ic = 0; ic < r->i_ss.channels; ic++) {
+
+ if (r->map_table[oc][ic] <= 0.0)
+ continue;
+
+ if (r->map_table[oc][ic] >= 1.0) {
+ static const int16_t one = 1;
+
+ oil_vectoradd_s16(
+ (int16_t*) dst + oc, o_skip,
+ (int16_t*) dst + oc, o_skip,
+ (int16_t*) src + ic, i_skip,
+ n_frames,
+ &one, &one);
+
+ } else
+
+ vectoradd_s16_with_fraction(
+ (int16_t*) dst + oc, o_skip,
+ (int16_t*) dst + oc, o_skip,
+ (int16_t*) src + ic, i_skip,
+ n_frames,
+ 1.0, r->map_table[oc][ic]);
+ }
+ }
+
+ break;
+
+ default:
+ pa_assert_not_reached();
}
pa_memblock_release(input->memblock);
- pa_memblock_release(u->buf2.memblock);
+ pa_memblock_release(r->buf2.memblock);
- u->buf2.length = n_frames * sizeof(float) * r->o_ss.channels;
+ r->buf2.length = out_n_samples * r->w_sz;
- return &u->buf2;
+ return &r->buf2;
}
static pa_memchunk *resample(pa_resampler *r, pa_memchunk *input) {
- struct impl_libsamplerate *u;
- SRC_DATA data;
unsigned in_n_frames, in_n_samples;
unsigned out_n_frames, out_n_samples;
- int ret;
- assert(r);
- assert(input);
- assert(r->impl_data);
- u = r->impl_data;
+ pa_assert(r);
+ pa_assert(input);
/* Resample the data and place the result in buf3 */
-
- if (!u->src_state || !input->length)
+
+ if (!r->impl_resample || !input->length)
return input;
- in_n_samples = input->length / sizeof(float);
- in_n_frames = in_n_samples * r->o_ss.channels;
+ in_n_samples = input->length / r->w_sz;
+ in_n_frames = in_n_samples / r->o_ss.channels;
- out_n_frames = (in_n_frames*r->o_ss.rate/r->i_ss.rate)+1024;
+ out_n_frames = ((in_n_frames*r->o_ss.rate)/r->i_ss.rate)+EXTRA_FRAMES;
out_n_samples = out_n_frames * r->o_ss.channels;
- if (!u->buf3.memblock || u->buf3_samples < out_n_samples) {
- if (u->buf3.memblock)
- pa_memblock_unref(u->buf3.memblock);
+ r->buf3.index = 0;
+ r->buf3.length = r->w_sz * out_n_samples;
- u->buf3_samples = out_n_samples;
- u->buf3.memblock = pa_memblock_new(r->mempool, u->buf3.length = sizeof(float) * out_n_samples);
- u->buf3.index = 0;
- }
-
- data.data_in = (float*) ((uint8_t*) pa_memblock_acquire(input->memblock) + input->index);
- data.input_frames = in_n_frames;
+ if (!r->buf3.memblock || r->buf3_samples < out_n_samples) {
+ if (r->buf3.memblock)
+ pa_memblock_unref(r->buf3.memblock);
- data.data_out = (float*) pa_memblock_acquire(u->buf3.memblock);
- data.output_frames = out_n_frames;
-
- data.src_ratio = (double) r->o_ss.rate / r->i_ss.rate;
- data.end_of_input = 0;
-
- ret = src_process(u->src_state, &data);
- assert(ret == 0);
- assert((unsigned) data.input_frames_used == in_n_frames);
+ r->buf3_samples = out_n_samples;
+ r->buf3.memblock = pa_memblock_new(r->mempool, r->buf3.length);
+ }
- pa_memblock_release(input->memblock);
- pa_memblock_release(u->buf3.memblock);
-
- u->buf3.length = data.output_frames_gen * sizeof(float) * r->o_ss.channels;
+ r->impl_resample(r, input, in_n_frames, &r->buf3, &out_n_frames);
+ r->buf3.length = out_n_frames * r->w_sz * r->o_ss.channels;
- return &u->buf3;
+ return &r->buf3;
}
-static pa_memchunk *convert_from_float(pa_resampler *r, pa_memchunk *input) {
- struct impl_libsamplerate *u;
+static pa_memchunk *convert_from_work_format(pa_resampler *r, pa_memchunk *input) {
unsigned n_samples, n_frames;
void *src, *dst;
-
- assert(r);
- assert(input);
- assert(r->impl_data);
- u = r->impl_data;
-
+
+ pa_assert(r);
+ pa_assert(input);
+
/* Convert the data into the correct sample type and place the result in buf4 */
- if (!u->from_float32ne_func || !input->length)
+ if (!r->from_work_format_func || !input->length)
return input;
- n_frames = input->length / sizeof(float) / r->o_ss.channels;
- n_samples = n_frames * r->o_ss.channels;
+ n_samples = input->length / r->w_sz;
+ n_frames = n_samples / r->o_ss.channels;
+
+ r->buf4.index = 0;
+ r->buf4.length = r->o_fz * n_frames;
- if (u->buf4_samples < n_samples) {
- if (u->buf4.memblock)
- pa_memblock_unref(u->buf4.memblock);
+ if (!r->buf4.memblock || r->buf4_samples < n_samples) {
+ if (r->buf4.memblock)
+ pa_memblock_unref(r->buf4.memblock);
- u->buf4_samples = n_samples;
- u->buf4.memblock = pa_memblock_new(r->mempool, u->buf4.length = r->o_fz * n_frames);
- u->buf4.index = 0;
+ r->buf4_samples = n_samples;
+ r->buf4.memblock = pa_memblock_new(r->mempool, r->buf4.length);
}
- src = (uint8_t*) pa_memblock_acquire(input->memblock) + input->length;
- dst = pa_memblock_acquire(u->buf4.memblock);
- u->from_float32ne_func(n_samples, src, dst);
+ src = (uint8_t*) pa_memblock_acquire(input->memblock) + input->index;
+ dst = pa_memblock_acquire(r->buf4.memblock);
+ r->from_work_format_func(n_samples, src, dst);
pa_memblock_release(input->memblock);
- pa_memblock_release(u->buf4.memblock);
+ pa_memblock_release(r->buf4.memblock);
- u->buf4.length = r->o_fz * n_frames;
-
- return &u->buf4;
+ r->buf4.length = r->o_fz * n_frames;
+
+ return &r->buf4;
}
-static void libsamplerate_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out) {
- struct impl_libsamplerate *u;
+void pa_resampler_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out) {
pa_memchunk *buf;
- assert(r);
- assert(in);
- assert(out);
- assert(in->length);
- assert(in->memblock);
- assert(in->length % r->i_fz == 0);
- assert(r->impl_data);
-
- u = r->impl_data;
-
- buf = convert_to_float(r, (pa_memchunk*) in);
+ pa_assert(r);
+ pa_assert(in);
+ pa_assert(out);
+ pa_assert(in->length);
+ pa_assert(in->memblock);
+ pa_assert(in->length % r->i_fz == 0);
+
+ buf = (pa_memchunk*) in;
+ buf = convert_to_work_format(r, buf);
buf = remap_channels(r, buf);
buf = resample(r, buf);
if (buf->length) {
- buf = convert_from_float(r, buf);
+ buf = convert_from_work_format(r, buf);
*out = *buf;
if (buf == in)
@@ -487,175 +1162,486 @@ static void libsamplerate_run(pa_resampler *r, const pa_memchunk *in, pa_memchun
pa_memchunk_reset(buf);
} else
pa_memchunk_reset(out);
+}
+
+/*** libsamplerate based implementation ***/
+
+#ifdef HAVE_LIBSAMPLERATE
+static void libsamplerate_resample(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) {
+ SRC_DATA data;
+
+ pa_assert(r);
+ pa_assert(input);
+ pa_assert(output);
+ pa_assert(out_n_frames);
- pa_memblock_release(in->memblock);
-
+ memset(&data, 0, sizeof(data));
+
+ data.data_in = (float*) ((uint8_t*) pa_memblock_acquire(input->memblock) + input->index);
+ data.input_frames = in_n_frames;
+
+ data.data_out = (float*) ((uint8_t*) pa_memblock_acquire(output->memblock) + output->index);
+ data.output_frames = *out_n_frames;
+
+ data.src_ratio = (double) r->o_ss.rate / r->i_ss.rate;
+ data.end_of_input = 0;
+
+ pa_assert_se(src_process(r->src.state, &data) == 0);
+ pa_assert((unsigned) data.input_frames_used == in_n_frames);
+
+ pa_memblock_release(input->memblock);
+ pa_memblock_release(output->memblock);
+
+ *out_n_frames = data.output_frames_gen;
}
-static void libsamplerate_update_input_rate(pa_resampler *r, uint32_t rate) {
- struct impl_libsamplerate *u;
-
- assert(r);
- assert(rate > 0);
- assert(r->impl_data);
- u = r->impl_data;
+static void libsamplerate_update_rates(pa_resampler *r) {
+ pa_assert(r);
- if (!u->src_state) {
- int err;
- u->src_state = src_new(r->resample_method, r->o_ss.channels, &err);
- assert(u->src_state);
- } else {
- int ret = src_set_ratio(u->src_state, (double) r->o_ss.rate / rate);
- assert(ret == 0);
- }
+ pa_assert_se(src_set_ratio(r->src.state, (double) r->o_ss.rate / r->i_ss.rate) == 0);
}
-static int libsamplerate_init(pa_resampler *r) {
- struct impl_libsamplerate *u = NULL;
- int err;
+static void libsamplerate_reset(pa_resampler *r) {
+ pa_assert(r);
- r->impl_data = u = pa_xnew(struct impl_libsamplerate, 1);
+ pa_assert_se(src_reset(r->src.state) == 0);
+}
- pa_memchunk_reset(&u->buf1);
- pa_memchunk_reset(&u->buf2);
- pa_memchunk_reset(&u->buf3);
- pa_memchunk_reset(&u->buf4);
- u->buf1_samples = u->buf2_samples = u->buf3_samples = u->buf4_samples = 0;
+static void libsamplerate_free(pa_resampler *r) {
+ pa_assert(r);
- if (r->i_ss.format == PA_SAMPLE_FLOAT32NE)
- u->to_float32ne_func = NULL;
- else if (!(u->to_float32ne_func = pa_get_convert_to_float32ne_function(r->i_ss.format)))
- goto fail;
+ if (r->src.state)
+ src_delete(r->src.state);
+}
- if (r->o_ss.format == PA_SAMPLE_FLOAT32NE)
- u->from_float32ne_func = NULL;
- else if (!(u->from_float32ne_func = pa_get_convert_from_float32ne_function(r->o_ss.format)))
- goto fail;
+static int libsamplerate_init(pa_resampler *r) {
+ int err;
- if (r->o_ss.rate == r->i_ss.rate)
- u->src_state = NULL;
- else if (!(u->src_state = src_new(r->resample_method, r->o_ss.channels, &err)))
- goto fail;
+ pa_assert(r);
+
+ if (!(r->src.state = src_new(r->method, r->o_ss.channels, &err)))
+ return -1;
r->impl_free = libsamplerate_free;
- r->impl_update_input_rate = libsamplerate_update_input_rate;
- r->impl_run = libsamplerate_run;
+ r->impl_update_rates = libsamplerate_update_rates;
+ r->impl_resample = libsamplerate_resample;
+ r->impl_reset = libsamplerate_reset;
- calc_map_table(r);
-
return 0;
+}
+#endif
-fail:
- pa_xfree(u);
- return -1;
+/*** speex based implementation ***/
+
+static void speex_resample_float(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) {
+ float *in, *out;
+ uint32_t inf = in_n_frames, outf = *out_n_frames;
+
+ pa_assert(r);
+ pa_assert(input);
+ pa_assert(output);
+ pa_assert(out_n_frames);
+
+ in = (float*) ((uint8_t*) pa_memblock_acquire(input->memblock) + input->index);
+ out = (float*) ((uint8_t*) pa_memblock_acquire(output->memblock) + output->index);
+
+ pa_assert_se(paspfl_resampler_process_interleaved_float(r->speex.state, in, &inf, out, &outf) == 0);
+
+ pa_memblock_release(input->memblock);
+ pa_memblock_release(output->memblock);
+
+ pa_assert(inf == in_n_frames);
+ *out_n_frames = outf;
+}
+
+static void speex_resample_int(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) {
+ int16_t *in, *out;
+ uint32_t inf = in_n_frames, outf = *out_n_frames;
+
+ pa_assert(r);
+ pa_assert(input);
+ pa_assert(output);
+ pa_assert(out_n_frames);
+
+ in = (int16_t*) ((uint8_t*) pa_memblock_acquire(input->memblock) + input->index);
+ out = (int16_t*) ((uint8_t*) pa_memblock_acquire(output->memblock) + output->index);
+
+ pa_assert_se(paspfx_resampler_process_interleaved_int(r->speex.state, in, &inf, out, &outf) == 0);
+
+ pa_memblock_release(input->memblock);
+ pa_memblock_release(output->memblock);
+
+ pa_assert(inf == in_n_frames);
+ *out_n_frames = outf;
+}
+
+static void speex_update_rates(pa_resampler *r) {
+ pa_assert(r);
+
+ if (r->method >= PA_RESAMPLER_SPEEX_FIXED_BASE && r->method <= PA_RESAMPLER_SPEEX_FIXED_MAX)
+ pa_assert_se(paspfx_resampler_set_rate(r->speex.state, r->i_ss.rate, r->o_ss.rate) == 0);
+ else {
+ pa_assert(r->method >= PA_RESAMPLER_SPEEX_FLOAT_BASE && r->method <= PA_RESAMPLER_SPEEX_FLOAT_MAX);
+ pa_assert_se(paspfl_resampler_set_rate(r->speex.state, r->i_ss.rate, r->o_ss.rate) == 0);
+ }
+}
+
+static void speex_reset(pa_resampler *r) {
+ pa_assert(r);
+
+ if (r->method >= PA_RESAMPLER_SPEEX_FIXED_BASE && r->method <= PA_RESAMPLER_SPEEX_FIXED_MAX)
+ pa_assert_se(paspfx_resampler_reset_mem(r->speex.state) == 0);
+ else {
+ pa_assert(r->method >= PA_RESAMPLER_SPEEX_FLOAT_BASE && r->method <= PA_RESAMPLER_SPEEX_FLOAT_MAX);
+ pa_assert_se(paspfl_resampler_reset_mem(r->speex.state) == 0);
+ }
+}
+
+static void speex_free(pa_resampler *r) {
+ pa_assert(r);
+
+ if (!r->speex.state)
+ return;
+
+ if (r->method >= PA_RESAMPLER_SPEEX_FIXED_BASE && r->method <= PA_RESAMPLER_SPEEX_FIXED_MAX)
+ paspfx_resampler_destroy(r->speex.state);
+ else {
+ pa_assert(r->method >= PA_RESAMPLER_SPEEX_FLOAT_BASE && r->method <= PA_RESAMPLER_SPEEX_FLOAT_MAX);
+ paspfl_resampler_destroy(r->speex.state);
+ }
+}
+
+static int speex_init(pa_resampler *r) {
+ int q, err;
+
+ pa_assert(r);
+
+ r->impl_free = speex_free;
+ r->impl_update_rates = speex_update_rates;
+ r->impl_reset = speex_reset;
+
+ if (r->method >= PA_RESAMPLER_SPEEX_FIXED_BASE && r->method <= PA_RESAMPLER_SPEEX_FIXED_MAX) {
+ q = r->method - PA_RESAMPLER_SPEEX_FIXED_BASE;
+
+ pa_log_info("Choosing speex quality setting %i.", q);
+
+ if (!(r->speex.state = paspfx_resampler_init(r->o_ss.channels, r->i_ss.rate, r->o_ss.rate, q, &err)))
+ return -1;
+
+ r->impl_resample = speex_resample_int;
+ } else {
+ pa_assert(r->method >= PA_RESAMPLER_SPEEX_FLOAT_BASE && r->method <= PA_RESAMPLER_SPEEX_FLOAT_MAX);
+ q = r->method - PA_RESAMPLER_SPEEX_FLOAT_BASE;
+
+ pa_log_info("Choosing speex quality setting %i.", q);
+
+ if (!(r->speex.state = paspfl_resampler_init(r->o_ss.channels, r->i_ss.rate, r->o_ss.rate, q, &err)))
+ return -1;
+
+ r->impl_resample = speex_resample_float;
+ }
+
+ return 0;
}
/* Trivial implementation */
-static void trivial_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out) {
+static void trivial_resample(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) {
size_t fz;
- unsigned n_frames;
- struct impl_trivial *u;
+ unsigned o_index;
+ void *src, *dst;
- assert(r);
- assert(in);
- assert(out);
- assert(r->impl_data);
-
- u = r->impl_data;
+ pa_assert(r);
+ pa_assert(input);
+ pa_assert(output);
+ pa_assert(out_n_frames);
- fz = r->i_fz;
- assert(fz == r->o_fz);
+ fz = r->w_sz * r->o_ss.channels;
- n_frames = in->length/fz;
+ src = (uint8_t*) pa_memblock_acquire(input->memblock) + input->index;
+ dst = (uint8_t*) pa_memblock_acquire(output->memblock) + output->index;
- if (r->i_ss.rate == r->o_ss.rate) {
+ for (o_index = 0;; o_index++, r->trivial.o_counter++) {
+ unsigned j;
- /* In case there's no diefference in sample types, do nothing */
- *out = *in;
- pa_memblock_ref(out->memblock);
+ j = ((r->trivial.o_counter * r->i_ss.rate) / r->o_ss.rate);
+ j = j > r->trivial.i_counter ? j - r->trivial.i_counter : 0;
- u->o_counter += n_frames;
- } else {
- /* Do real resampling */
- size_t l;
- unsigned o_index;
- void *src, *dst;
-
- /* The length of the new memory block rounded up */
- l = ((((n_frames+1) * r->o_ss.rate) / r->i_ss.rate) + 1) * fz;
-
- out->index = 0;
- out->memblock = pa_memblock_new(r->mempool, l);
-
- src = (uint8_t*) pa_memblock_acquire(in->memblock) + in->index;
- dst = pa_memblock_acquire(out->memblock);
-
- for (o_index = 0;; o_index++, u->o_counter++) {
- unsigned j;
-
- j = (u->o_counter * r->i_ss.rate / r->o_ss.rate);
- j = j > u->i_counter ? j - u->i_counter : 0;
-
- if (j >= n_frames)
- break;
-
- assert(o_index*fz < pa_memblock_get_length(out->memblock));
-
- memcpy((uint8_t*) dst + fz*o_index,
- (uint8_t*) src + fz*j, fz);
-
+ if (j >= in_n_frames)
+ break;
+
+ pa_assert(o_index * fz < pa_memblock_get_length(output->memblock));
+
+ oil_memcpy((uint8_t*) dst + fz * o_index,
+ (uint8_t*) src + fz * j, fz);
+ }
+
+ pa_memblock_release(input->memblock);
+ pa_memblock_release(output->memblock);
+
+ *out_n_frames = o_index;
+
+ r->trivial.i_counter += in_n_frames;
+
+ /* Normalize counters */
+ while (r->trivial.i_counter >= r->i_ss.rate) {
+ pa_assert(r->trivial.o_counter >= r->o_ss.rate);
+
+ r->trivial.i_counter -= r->i_ss.rate;
+ r->trivial.o_counter -= r->o_ss.rate;
+ }
+}
+
+static void trivial_update_rates_or_reset(pa_resampler *r) {
+ pa_assert(r);
+
+ r->trivial.i_counter = 0;
+ r->trivial.o_counter = 0;
+}
+
+static int trivial_init(pa_resampler*r) {
+ pa_assert(r);
+
+ r->trivial.o_counter = r->trivial.i_counter = 0;
+
+ r->impl_resample = trivial_resample;
+ r->impl_update_rates = trivial_update_rates_or_reset;
+ r->impl_reset = trivial_update_rates_or_reset;
+
+ return 0;
+}
+
+/* Peak finder implementation */
+
+static void peaks_resample(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) {
+ size_t fz;
+ unsigned o_index;
+ void *src, *dst;
+ unsigned start = 0;
+
+ pa_assert(r);
+ pa_assert(input);
+ pa_assert(output);
+ pa_assert(out_n_frames);
+
+ fz = r->w_sz * r->o_ss.channels;
+
+ src = (uint8_t*) pa_memblock_acquire(input->memblock) + input->index;
+ dst = (uint8_t*) pa_memblock_acquire(output->memblock) + output->index;
+
+ for (o_index = 0;; o_index++, r->peaks.o_counter++) {
+ unsigned j;
+
+ j = ((r->peaks.o_counter * r->i_ss.rate) / r->o_ss.rate);
+ j = j > r->peaks.i_counter ? j - r->peaks.i_counter : 0;
+
+ if (j >= in_n_frames)
+ break;
+
+ pa_assert(o_index * fz < pa_memblock_get_length(output->memblock));
+
+ if (r->work_format == PA_SAMPLE_S16NE) {
+ unsigned i, c;
+ int16_t *s = (int16_t*) ((uint8_t*) src + fz * j);
+ int16_t *d = (int16_t*) ((uint8_t*) dst + fz * o_index);
+
+ for (i = start; i <= j; i++)
+ for (c = 0; c < r->o_ss.channels; c++, s++) {
+ int16_t n;
+
+ n = *s < 0 ? -*s : *s;
+
+ if (n > r->peaks.max_i[c])
+ r->peaks.max_i[c] = n;
+ }
+
+ for (c = 0; c < r->o_ss.channels; c++, d++) {
+ *d = r->peaks.max_i[c];
+ r->peaks.max_i[c] = 0;
+ }
+ } else {
+ unsigned i, c;
+ float *s = (float*) ((uint8_t*) src + fz * j);
+ float *d = (float*) ((uint8_t*) dst + fz * o_index);
+
+ pa_assert(r->work_format == PA_SAMPLE_FLOAT32NE);
+
+ for (i = start; i <= j; i++)
+ for (c = 0; c < r->o_ss.channels; c++, s++) {
+ float n = fabsf(*s);
+
+ if (n > r->peaks.max_f[c])
+ r->peaks.max_f[c] = n;
+ }
+
+ for (c = 0; c < r->o_ss.channels; c++, d++) {
+ *d = r->peaks.max_f[c];
+ r->peaks.max_f[c] = 0;
+ }
}
- pa_memblock_release(in->memblock);
- pa_memblock_release(out->memblock);
-
- out->length = o_index*fz;
+ start = j+1;
}
- u->i_counter += n_frames;
-
+ pa_memblock_release(input->memblock);
+ pa_memblock_release(output->memblock);
+
+ *out_n_frames = o_index;
+
+ r->peaks.i_counter += in_n_frames;
+
/* Normalize counters */
- while (u->i_counter >= r->i_ss.rate) {
- u->i_counter -= r->i_ss.rate;
- assert(u->o_counter >= r->o_ss.rate);
- u->o_counter -= r->o_ss.rate;
+ while (r->peaks.i_counter >= r->i_ss.rate) {
+ pa_assert(r->peaks.o_counter >= r->o_ss.rate);
+
+ r->peaks.i_counter -= r->i_ss.rate;
+ r->peaks.o_counter -= r->o_ss.rate;
}
}
-static void trivial_free(pa_resampler *r) {
- assert(r);
-
- pa_xfree(r->impl_data);
+static void peaks_update_rates_or_reset(pa_resampler *r) {
+ pa_assert(r);
+
+ r->peaks.i_counter = 0;
+ r->peaks.o_counter = 0;
+}
+
+static int peaks_init(pa_resampler*r) {
+ pa_assert(r);
+
+ r->peaks.o_counter = r->peaks.i_counter = 0;
+ memset(r->peaks.max_i, 0, sizeof(r->peaks.max_i));
+ memset(r->peaks.max_f, 0, sizeof(r->peaks.max_f));
+
+ r->impl_resample = peaks_resample;
+ r->impl_update_rates = peaks_update_rates_or_reset;
+ r->impl_reset = peaks_update_rates_or_reset;
+
+ return 0;
}
-static void trivial_update_input_rate(pa_resampler *r, uint32_t rate) {
- struct impl_trivial *u;
+/*** ffmpeg based implementation ***/
+
+static void ffmpeg_resample(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) {
+ unsigned used_frames = 0, c;
+
+ pa_assert(r);
+ pa_assert(input);
+ pa_assert(output);
+ pa_assert(out_n_frames);
+
+ for (c = 0; c < r->o_ss.channels; c++) {
+ unsigned u;
+ pa_memblock *b, *w;
+ int16_t *p, *t, *k, *q, *s;
+ int consumed_frames;
+ unsigned in, l;
+
+ /* Allocate a new block */
+ b = pa_memblock_new(r->mempool, r->ffmpeg.buf[c].length + in_n_frames * sizeof(int16_t));
+ p = pa_memblock_acquire(b);
+
+ /* Copy the remaining data into it */
+ l = r->ffmpeg.buf[c].length;
+ if (r->ffmpeg.buf[c].memblock) {
+ t = (int16_t*) ((uint8_t*) pa_memblock_acquire(r->ffmpeg.buf[c].memblock) + r->ffmpeg.buf[c].index);
+ memcpy(p, t, l);
+ pa_memblock_release(r->ffmpeg.buf[c].memblock);
+ pa_memblock_unref(r->ffmpeg.buf[c].memblock);
+ pa_memchunk_reset(&r->ffmpeg.buf[c]);
+ }
- assert(r);
- assert(rate > 0);
- assert(r->impl_data);
+ /* Now append the new data, splitting up channels */
+ t = ((int16_t*) ((uint8_t*) pa_memblock_acquire(input->memblock) + input->index)) + c;
+ k = (int16_t*) ((uint8_t*) p + l);
+ for (u = 0; u < in_n_frames; u++) {
+ *k = *t;
+ t += r->o_ss.channels;
+ k ++;
+ }
+ pa_memblock_release(input->memblock);
+
+ /* Calculate the resulting number of frames */
+ in = in_n_frames + l / sizeof(int16_t);
+
+ /* Allocate buffer for the result */
+ w = pa_memblock_new(r->mempool, *out_n_frames * sizeof(int16_t));
+ q = pa_memblock_acquire(w);
+
+ /* Now, resample */
+ used_frames = av_resample(r->ffmpeg.state,
+ q, p,
+ &consumed_frames,
+ in, *out_n_frames,
+ c >= (unsigned) r->o_ss.channels-1);
+
+ pa_memblock_release(b);
+
+ /* Now store the remaining samples away */
+ pa_assert(consumed_frames <= (int) in);
+ if (consumed_frames < (int) in) {
+ r->ffmpeg.buf[c].memblock = b;
+ r->ffmpeg.buf[c].index = consumed_frames * sizeof(int16_t);
+ r->ffmpeg.buf[c].length = (in - consumed_frames) * sizeof(int16_t);
+ } else
+ pa_memblock_unref(b);
+
+ /* And place the results in the output buffer */
+ s = (short*) ((uint8_t*) pa_memblock_acquire(output->memblock) + output->index) + c;
+ for (u = 0; u < used_frames; u++) {
+ *s = *q;
+ q++;
+ s += r->o_ss.channels;
+ }
+ pa_memblock_release(output->memblock);
+ pa_memblock_release(w);
+ pa_memblock_unref(w);
+ }
- u = r->impl_data;
- u->i_counter = 0;
- u->o_counter = 0;
+ *out_n_frames = used_frames;
}
-static int trivial_init(pa_resampler*r) {
- struct impl_trivial *u;
-
- assert(r);
- assert(r->i_ss.format == r->o_ss.format);
- assert(r->i_ss.channels == r->o_ss.channels);
-
- r->impl_data = u = pa_xnew(struct impl_trivial, 1);
- u->o_counter = u->i_counter = 0;
-
- r->impl_run = trivial_run;
- r->impl_free = trivial_free;
- r->impl_update_input_rate = trivial_update_input_rate;
-
+static void ffmpeg_free(pa_resampler *r) {
+ unsigned c;
+
+ pa_assert(r);
+
+ if (r->ffmpeg.state)
+ av_resample_close(r->ffmpeg.state);
+
+ for (c = 0; c < PA_ELEMENTSOF(r->ffmpeg.buf); c++)
+ if (r->ffmpeg.buf[c].memblock)
+ pa_memblock_unref(r->ffmpeg.buf[c].memblock);
+}
+
+static int ffmpeg_init(pa_resampler *r) {
+ unsigned c;
+
+ pa_assert(r);
+
+ /* We could probably implement different quality levels by
+ * adjusting the filter parameters here. However, ffmpeg
+ * internally only uses these hardcoded values, so let's use them
+ * here for now as well until ffmpeg makes this configurable. */
+
+ if (!(r->ffmpeg.state = av_resample_init(r->o_ss.rate, r->i_ss.rate, 16, 10, 0, 0.8)))
+ return -1;
+
+ r->impl_free = ffmpeg_free;
+ r->impl_resample = ffmpeg_resample;
+
+ for (c = 0; c < PA_ELEMENTSOF(r->ffmpeg.buf); c++)
+ pa_memchunk_reset(&r->ffmpeg.buf[c]);
+
return 0;
}
+/*** copy (noop) implementation ***/
+
+static int copy_init(pa_resampler *r) {
+ pa_assert(r);
+ pa_assert(r->o_ss.rate == r->i_ss.rate);
+
+ return 0;
+}
diff --git a/src/pulsecore/resampler.h b/src/pulsecore/resampler.h
index 327e24a2..5e302a9b 100644
--- a/src/pulsecore/resampler.h
+++ b/src/pulsecore/resampler.h
@@ -1,29 +1,27 @@
#ifndef fooresamplerhfoo
#define fooresamplerhfoo
-/* $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.
***/
-#include <samplerate.h>
-
#include <pulse/sample.h>
#include <pulse/channelmap.h>
#include <pulsecore/memblock.h>
@@ -33,34 +31,61 @@ typedef struct pa_resampler pa_resampler;
typedef enum pa_resample_method {
PA_RESAMPLER_INVALID = -1,
- PA_RESAMPLER_SRC_SINC_BEST_QUALITY = SRC_SINC_BEST_QUALITY,
- PA_RESAMPLER_SRC_SINC_MEDIUM_QUALITY = SRC_SINC_MEDIUM_QUALITY,
- PA_RESAMPLER_SRC_SINC_FASTEST = SRC_SINC_FASTEST,
- PA_RESAMPLER_SRC_ZERO_ORDER_HOLD = SRC_ZERO_ORDER_HOLD,
- PA_RESAMPLER_SRC_LINEAR = SRC_LINEAR,
+ PA_RESAMPLER_SRC_SINC_BEST_QUALITY = 0, /* = SRC_SINC_BEST_QUALITY */
+ PA_RESAMPLER_SRC_SINC_MEDIUM_QUALITY = 1, /* = SRC_SINC_MEDIUM_QUALITY */
+ PA_RESAMPLER_SRC_SINC_FASTEST = 2, /* = SRC_SINC_FASTEST */
+ PA_RESAMPLER_SRC_ZERO_ORDER_HOLD = 3, /* = SRC_ZERO_ORDER_HOLD */
+ PA_RESAMPLER_SRC_LINEAR = 4, /* = SRC_LINEAR */
PA_RESAMPLER_TRIVIAL,
+ PA_RESAMPLER_SPEEX_FLOAT_BASE,
+ PA_RESAMPLER_SPEEX_FLOAT_MAX = PA_RESAMPLER_SPEEX_FLOAT_BASE + 10,
+ PA_RESAMPLER_SPEEX_FIXED_BASE,
+ PA_RESAMPLER_SPEEX_FIXED_MAX = PA_RESAMPLER_SPEEX_FIXED_BASE + 10,
+ PA_RESAMPLER_FFMPEG,
+ PA_RESAMPLER_AUTO, /* automatic select based on sample format */
+ PA_RESAMPLER_COPY,
+ PA_RESAMPLER_PEAKS,
PA_RESAMPLER_MAX
} pa_resample_method_t;
+typedef enum pa_resample_flags {
+ PA_RESAMPLER_VARIABLE_RATE = 1,
+ PA_RESAMPLER_NO_REMAP = 2, /* implies NO_REMIX */
+ PA_RESAMPLER_NO_REMIX = 4
+} pa_resample_flags_t;
+
pa_resampler* pa_resampler_new(
pa_mempool *pool,
const pa_sample_spec *a,
const pa_channel_map *am,
const pa_sample_spec *b,
const pa_channel_map *bm,
- pa_resample_method_t resample_method);
+ pa_resample_method_t resample_method,
+ pa_resample_flags_t flags);
void pa_resampler_free(pa_resampler *r);
/* Returns the size of an input memory block which is required to return the specified amount of output data */
size_t pa_resampler_request(pa_resampler *r, size_t out_length);
+/* Inverse of pa_resampler_request() */
+size_t pa_resampler_result(pa_resampler *r, size_t in_length);
+
+/* Returns the maximum size of input blocks we can process without needing bounce buffers larger than the mempool tile size. */
+size_t pa_resampler_max_block_size(pa_resampler *r);
+
/* Pass the specified memory chunk to the resampler and return the newly resampled data */
void pa_resampler_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out);
/* Change the input rate of the resampler object */
void pa_resampler_set_input_rate(pa_resampler *r, uint32_t rate);
+/* Change the output rate of the resampler object */
+void pa_resampler_set_output_rate(pa_resampler *r, uint32_t rate);
+
+/* Reinitialize state of the resampler, possibly due to seeking or other discontinuities */
+void pa_resampler_reset(pa_resampler *r);
+
/* Return the resampling method of the resampler object */
pa_resample_method_t pa_resampler_get_method(pa_resampler *r);
@@ -70,4 +95,8 @@ pa_resample_method_t pa_parse_resample_method(const char *string);
/* return a human readable string for the specified resampling method. Inverse of pa_parse_resample_method() */
const char *pa_resample_method_to_string(pa_resample_method_t m);
+/* Return 1 when the specified resampling method is supported */
+int pa_resample_method_supported(pa_resample_method_t m);
+
+
#endif
diff --git a/src/pulsecore/rtclock.c b/src/pulsecore/rtclock.c
new file mode 100644
index 00000000..f33de830
--- /dev/null
+++ b/src/pulsecore/rtclock.c
@@ -0,0 +1,117 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+ Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stddef.h>
+#include <time.h>
+#include <sys/time.h>
+
+#include <pulse/timeval.h>
+#include <pulsecore/macro.h>
+
+#include "rtclock.h"
+
+pa_usec_t pa_rtclock_age(const struct timeval *tv) {
+ struct timeval now;
+ pa_assert(tv);
+
+ return pa_timeval_diff(pa_rtclock_get(&now), tv);
+}
+
+struct timeval *pa_rtclock_get(struct timeval *tv) {
+#ifdef HAVE_CLOCK_GETTIME
+ struct timespec ts;
+
+#ifdef CLOCK_MONOTONIC
+ /* No locking or atomic ops for no_monotonic here */
+ static pa_bool_t no_monotonic = FALSE;
+
+ if (!no_monotonic)
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0)
+ no_monotonic = TRUE;
+
+ if (no_monotonic)
+#endif
+ pa_assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
+
+ pa_assert(tv);
+
+ tv->tv_sec = ts.tv_sec;
+ tv->tv_usec = ts.tv_nsec / 1000;
+
+ return tv;
+
+#else /* HAVE_CLOCK_GETTIME */
+
+ return pa_gettimeofday(tv);
+
+#endif
+}
+
+pa_bool_t pa_rtclock_hrtimer(void) {
+#ifdef HAVE_CLOCK_GETTIME
+ struct timespec ts;
+
+#ifdef CLOCK_MONOTONIC
+ if (clock_getres(CLOCK_MONOTONIC, &ts) >= 0)
+ return ts.tv_sec == 0 && ts.tv_nsec <= PA_HRTIMER_THRESHOLD_USEC*1000;
+#endif
+
+ pa_assert_se(clock_getres(CLOCK_REALTIME, &ts) == 0);
+ return ts.tv_sec == 0 && ts.tv_nsec <= PA_HRTIMER_THRESHOLD_USEC*1000;
+
+#else /* HAVE_CLOCK_GETTIME */
+
+ return FALSE;
+
+#endif
+}
+
+pa_usec_t pa_rtclock_usec(void) {
+ struct timeval tv;
+
+ return pa_timeval_load(pa_rtclock_get(&tv));
+}
+
+struct timeval* pa_rtclock_from_wallclock(struct timeval *tv) {
+
+#ifdef HAVE_CLOCK_GETTIME
+ struct timeval wc_now, rt_now;
+
+ pa_gettimeofday(&wc_now);
+ pa_rtclock_get(&rt_now);
+
+ pa_assert(tv);
+
+ if (pa_timeval_cmp(&wc_now, tv) < 0)
+ pa_timeval_add(&rt_now, pa_timeval_diff(tv, &wc_now));
+ else
+ pa_timeval_sub(&rt_now, pa_timeval_diff(&wc_now, tv));
+
+ *tv = rt_now;
+#endif
+
+ return tv;
+}
diff --git a/src/pulsecore/rtclock.h b/src/pulsecore/rtclock.h
new file mode 100644
index 00000000..705de48f
--- /dev/null
+++ b/src/pulsecore/rtclock.h
@@ -0,0 +1,43 @@
+#ifndef foopulsertclockhfoo
+#define foopulsertclockhfoo
+
+/***
+ 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.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <pulsecore/macro.h>
+
+struct timeval;
+
+/* Something like pulse/timeval.h but based on CLOCK_MONOTONIC */
+
+struct timeval *pa_rtclock_get(struct timeval *ts);
+
+pa_usec_t pa_rtclock_usec(void);
+
+pa_usec_t pa_rtclock_age(const struct timeval *tv);
+pa_bool_t pa_rtclock_hrtimer(void);
+
+/* timer with a resolution better than this are considered high-resolution */
+#define PA_HRTIMER_THRESHOLD_USEC 10
+
+struct timeval* pa_rtclock_from_wallclock(struct timeval *tv);
+
+#endif
diff --git a/src/pulsecore/rtpoll.c b/src/pulsecore/rtpoll.c
new file mode 100644
index 00000000..a67a5516
--- /dev/null
+++ b/src/pulsecore/rtpoll.c
@@ -0,0 +1,766 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+ Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <signal.h>
+#include <string.h>
+#include <errno.h>
+
+#ifdef __linux__
+#include <sys/utsname.h>
+#endif
+
+#ifdef HAVE_POLL_H
+#include <poll.h>
+#else
+#include <pulsecore/poll.h>
+#endif
+
+#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/rtclock.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/llist.h>
+#include <pulsecore/rtsig.h>
+#include <pulsecore/flist.h>
+#include <pulsecore/core-util.h>
+
+#include <pulsecore/winsock.h>
+
+#include "rtpoll.h"
+
+struct pa_rtpoll {
+ struct pollfd *pollfd, *pollfd2;
+ unsigned n_pollfd_alloc, n_pollfd_used;
+
+ pa_bool_t timer_enabled;
+ struct timeval next_elapse;
+
+ pa_bool_t scan_for_dead;
+ pa_bool_t running, installed, rebuild_needed, quit;
+
+#ifdef HAVE_PPOLL
+ int rtsig;
+ sigset_t sigset_unblocked;
+ timer_t timer;
+ pa_bool_t timer_armed;
+#ifdef __linux__
+ pa_bool_t dont_use_ppoll;
+#endif
+#endif
+
+ PA_LLIST_HEAD(pa_rtpoll_item, items);
+};
+
+struct pa_rtpoll_item {
+ pa_rtpoll *rtpoll;
+ pa_bool_t dead;
+
+ pa_rtpoll_priority_t priority;
+
+ struct pollfd *pollfd;
+ unsigned n_pollfd;
+
+ int (*work_cb)(pa_rtpoll_item *i);
+ int (*before_cb)(pa_rtpoll_item *i);
+ void (*after_cb)(pa_rtpoll_item *i);
+ void *userdata;
+
+ PA_LLIST_FIELDS(pa_rtpoll_item);
+};
+
+PA_STATIC_FLIST_DECLARE(items, 0, pa_xfree);
+
+static void signal_handler_noop(int s) { /* write(2, "signal\n", 7); */ }
+
+pa_rtpoll *pa_rtpoll_new(void) {
+ pa_rtpoll *p;
+
+ p = pa_xnew(pa_rtpoll, 1);
+
+#ifdef HAVE_PPOLL
+
+#ifdef __linux__
+ /* ppoll is broken on Linux < 2.6.16 */
+ p->dont_use_ppoll = FALSE;
+
+ {
+ struct utsname u;
+ unsigned major, minor, micro;
+
+ pa_assert_se(uname(&u) == 0);
+
+ if (sscanf(u.release, "%u.%u.%u", &major, &minor, &micro) != 3 ||
+ (major < 2) ||
+ (major == 2 && minor < 6) ||
+ (major == 2 && minor == 6 && micro < 16))
+
+ p->dont_use_ppoll = TRUE;
+ }
+
+#endif
+
+ p->rtsig = -1;
+ sigemptyset(&p->sigset_unblocked);
+ p->timer = (timer_t) -1;
+ p->timer_armed = FALSE;
+
+#endif
+
+ p->n_pollfd_alloc = 32;
+ p->pollfd = pa_xnew(struct pollfd, p->n_pollfd_alloc);
+ p->pollfd2 = pa_xnew(struct pollfd, p->n_pollfd_alloc);
+ p->n_pollfd_used = 0;
+
+ memset(&p->next_elapse, 0, sizeof(p->next_elapse));
+ p->timer_enabled = FALSE;
+
+ p->running = FALSE;
+ p->installed = FALSE;
+ p->scan_for_dead = FALSE;
+ p->rebuild_needed = FALSE;
+ p->quit = FALSE;
+
+ PA_LLIST_HEAD_INIT(pa_rtpoll_item, p->items);
+
+ return p;
+}
+
+void pa_rtpoll_install(pa_rtpoll *p) {
+ pa_assert(p);
+ pa_assert(!p->installed);
+
+ p->installed = 1;
+
+#ifdef HAVE_PPOLL
+# ifdef __linux__
+ if (p->dont_use_ppoll)
+ return;
+# endif
+
+ if ((p->rtsig = pa_rtsig_get_for_thread()) < 0) {
+ pa_log_warn("Failed to reserve POSIX realtime signal.");
+ return;
+ }
+
+ pa_log_debug("Acquired POSIX realtime signal %s", pa_sig2str(p->rtsig));
+
+ {
+ sigset_t ss;
+ struct sigaction sa;
+
+ pa_assert_se(sigemptyset(&ss) == 0);
+ pa_assert_se(sigaddset(&ss, p->rtsig) == 0);
+ pa_assert_se(pthread_sigmask(SIG_BLOCK, &ss, &p->sigset_unblocked) == 0);
+ pa_assert_se(sigdelset(&p->sigset_unblocked, p->rtsig) == 0);
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = signal_handler_noop;
+ pa_assert_se(sigemptyset(&sa.sa_mask) == 0);
+
+ pa_assert_se(sigaction(p->rtsig, &sa, NULL) == 0);
+
+ /* We never reset the signal handler. Why should we? */
+ }
+
+#endif
+}
+
+static void rtpoll_rebuild(pa_rtpoll *p) {
+
+ struct pollfd *e, *t;
+ pa_rtpoll_item *i;
+ int ra = 0;
+
+ pa_assert(p);
+
+ p->rebuild_needed = FALSE;
+
+ if (p->n_pollfd_used > p->n_pollfd_alloc) {
+ /* Hmm, we have to allocate some more space */
+ p->n_pollfd_alloc = p->n_pollfd_used * 2;
+ p->pollfd2 = pa_xrealloc(p->pollfd2, p->n_pollfd_alloc * sizeof(struct pollfd));
+ ra = 1;
+ }
+
+ e = p->pollfd2;
+
+ for (i = p->items; i; i = i->next) {
+
+ if (i->n_pollfd > 0) {
+ size_t l = i->n_pollfd * sizeof(struct pollfd);
+
+ if (i->pollfd)
+ memcpy(e, i->pollfd, l);
+ else
+ memset(e, 0, l);
+
+ i->pollfd = e;
+ } else
+ i->pollfd = NULL;
+
+ e += i->n_pollfd;
+ }
+
+ pa_assert((unsigned) (e - p->pollfd2) == p->n_pollfd_used);
+ t = p->pollfd;
+ p->pollfd = p->pollfd2;
+ p->pollfd2 = t;
+
+ if (ra)
+ p->pollfd2 = pa_xrealloc(p->pollfd2, p->n_pollfd_alloc * sizeof(struct pollfd));
+
+}
+
+static void rtpoll_item_destroy(pa_rtpoll_item *i) {
+ pa_rtpoll *p;
+
+ pa_assert(i);
+
+ p = i->rtpoll;
+
+ PA_LLIST_REMOVE(pa_rtpoll_item, p->items, i);
+
+ p->n_pollfd_used -= i->n_pollfd;
+
+ if (pa_flist_push(PA_STATIC_FLIST_GET(items), i) < 0)
+ pa_xfree(i);
+
+ p->rebuild_needed = TRUE;
+}
+
+void pa_rtpoll_free(pa_rtpoll *p) {
+ pa_assert(p);
+
+ while (p->items)
+ rtpoll_item_destroy(p->items);
+
+ pa_xfree(p->pollfd);
+ pa_xfree(p->pollfd2);
+
+#ifdef HAVE_PPOLL
+ if (p->timer != (timer_t) -1)
+ timer_delete(p->timer);
+#endif
+
+ pa_xfree(p);
+}
+
+static void reset_revents(pa_rtpoll_item *i) {
+ struct pollfd *f;
+ unsigned n;
+
+ pa_assert(i);
+
+ if (!(f = pa_rtpoll_item_get_pollfd(i, &n)))
+ return;
+
+ for (; n > 0; n--)
+ f[n-1].revents = 0;
+}
+
+static void reset_all_revents(pa_rtpoll *p) {
+ pa_rtpoll_item *i;
+
+ pa_assert(p);
+
+ for (i = p->items; i; i = i->next) {
+
+ if (i->dead)
+ continue;
+
+ reset_revents(i);
+ }
+}
+
+int pa_rtpoll_run(pa_rtpoll *p, pa_bool_t wait) {
+ pa_rtpoll_item *i;
+ int r = 0;
+ struct timeval timeout;
+
+ pa_assert(p);
+ pa_assert(!p->running);
+ pa_assert(p->installed);
+
+ p->running = TRUE;
+
+ /* First, let's do some work */
+ for (i = p->items; i && i->priority < PA_RTPOLL_NEVER; i = i->next) {
+ int k;
+
+ if (i->dead)
+ continue;
+
+ if (!i->work_cb)
+ continue;
+
+ if (p->quit)
+ goto finish;
+
+ if ((k = i->work_cb(i)) != 0) {
+ if (k < 0)
+ r = k;
+
+ goto finish;
+ }
+ }
+
+ /* Now let's prepare for entering the sleep */
+ for (i = p->items; i && i->priority < PA_RTPOLL_NEVER; i = i->next) {
+ int k = 0;
+
+ if (i->dead)
+ continue;
+
+ if (!i->before_cb)
+ continue;
+
+ if (p->quit || (k = i->before_cb(i)) != 0) {
+
+ /* Hmm, this one doesn't let us enter the poll, so rewind everything */
+
+ for (i = i->prev; i; i = i->prev) {
+
+ if (i->dead)
+ continue;
+
+ if (!i->after_cb)
+ continue;
+
+ i->after_cb(i);
+ }
+
+ if (k < 0)
+ r = k;
+
+ goto finish;
+ }
+ }
+
+ if (p->rebuild_needed)
+ rtpoll_rebuild(p);
+
+ memset(&timeout, 0, sizeof(timeout));
+
+ /* Calculate timeout */
+ if (wait && !p->quit && p->timer_enabled) {
+ struct timeval now;
+ pa_rtclock_get(&now);
+
+ if (pa_timeval_cmp(&p->next_elapse, &now) > 0)
+ pa_timeval_add(&timeout, pa_timeval_diff(&p->next_elapse, &now));
+ }
+
+ /* OK, now let's sleep */
+#ifdef HAVE_PPOLL
+
+#ifdef __linux__
+ if (!p->dont_use_ppoll)
+#endif
+ {
+ struct timespec ts;
+ ts.tv_sec = timeout.tv_sec;
+ ts.tv_nsec = timeout.tv_usec * 1000;
+ r = ppoll(p->pollfd, p->n_pollfd_used, (!wait || p->quit || p->timer_enabled) ? &ts : NULL, p->rtsig < 0 ? NULL : &p->sigset_unblocked);
+ }
+#ifdef __linux__
+ else
+#endif
+
+#endif
+ r = poll(p->pollfd, p->n_pollfd_used, (!wait || p->quit || p->timer_enabled) ? (timeout.tv_sec*1000) + (timeout.tv_usec / 1000) : -1);
+
+ if (r < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ r = 0;
+ else
+ pa_log_error("poll(): %s", pa_cstrerror(errno));
+
+ reset_all_revents(p);
+ }
+
+ /* Let's tell everyone that we left the sleep */
+ for (i = p->items; i && i->priority < PA_RTPOLL_NEVER; i = i->next) {
+
+ if (i->dead)
+ continue;
+
+ if (!i->after_cb)
+ continue;
+
+ i->after_cb(i);
+ }
+
+finish:
+
+ p->running = FALSE;
+
+ if (p->scan_for_dead) {
+ pa_rtpoll_item *n;
+
+ p->scan_for_dead = FALSE;
+
+ for (i = p->items; i; i = n) {
+ n = i->next;
+
+ if (i->dead)
+ rtpoll_item_destroy(i);
+ }
+ }
+
+ return r < 0 ? r : !p->quit;
+}
+
+static void update_timer(pa_rtpoll *p) {
+ pa_assert(p);
+
+#ifdef HAVE_PPOLL
+
+#ifdef __linux__
+ if (!p->dont_use_ppoll) {
+#endif
+
+ if (p->timer == (timer_t) -1) {
+ struct sigevent se;
+
+ memset(&se, 0, sizeof(se));
+ se.sigev_notify = SIGEV_SIGNAL;
+ se.sigev_signo = p->rtsig;
+
+ if (timer_create(CLOCK_MONOTONIC, &se, &p->timer) < 0)
+ if (timer_create(CLOCK_REALTIME, &se, &p->timer) < 0) {
+ pa_log_warn("Failed to allocate POSIX timer: %s", pa_cstrerror(errno));
+ p->timer = (timer_t) -1;
+ }
+ }
+
+ if (p->timer != (timer_t) -1) {
+ struct itimerspec its;
+ struct timespec ts = { .tv_sec = 0, .tv_nsec = 0 };
+ sigset_t ss;
+
+ if (p->timer_armed) {
+ /* First disarm timer */
+ memset(&its, 0, sizeof(its));
+ pa_assert_se(timer_settime(p->timer, TIMER_ABSTIME, &its, NULL) == 0);
+
+ /* Remove a signal that might be waiting in the signal q */
+ pa_assert_se(sigemptyset(&ss) == 0);
+ pa_assert_se(sigaddset(&ss, p->rtsig) == 0);
+ sigtimedwait(&ss, NULL, &ts);
+ }
+
+ /* And install the new timer */
+ if (p->timer_enabled) {
+ memset(&its, 0, sizeof(its));
+
+ its.it_value.tv_sec = p->next_elapse.tv_sec;
+ its.it_value.tv_nsec = p->next_elapse.tv_usec*1000;
+
+ /* Make sure that 0,0 is not understood as
+ * "disarming" */
+ if (its.it_value.tv_sec == 0 && its.it_value.tv_nsec == 0)
+ its.it_value.tv_nsec = 1;
+ pa_assert_se(timer_settime(p->timer, TIMER_ABSTIME, &its, NULL) == 0);
+ }
+
+ p->timer_armed = p->timer_enabled;
+ }
+
+#ifdef __linux__
+ }
+#endif
+
+#endif
+}
+
+void pa_rtpoll_set_timer_absolute(pa_rtpoll *p, pa_usec_t usec) {
+ pa_assert(p);
+
+ pa_timeval_store(&p->next_elapse, usec);
+ p->timer_enabled = TRUE;
+
+ update_timer(p);
+}
+
+void pa_rtpoll_set_timer_relative(pa_rtpoll *p, pa_usec_t usec) {
+ pa_assert(p);
+
+ /* Scheduling a timeout for more than an hour is very very suspicious */
+ pa_assert(usec <= PA_USEC_PER_SEC*60ULL*60ULL);
+
+ pa_rtclock_get(&p->next_elapse);
+ pa_timeval_add(&p->next_elapse, usec);
+ p->timer_enabled = TRUE;
+
+ update_timer(p);
+}
+
+void pa_rtpoll_set_timer_disabled(pa_rtpoll *p) {
+ pa_assert(p);
+
+ memset(&p->next_elapse, 0, sizeof(p->next_elapse));
+ p->timer_enabled = FALSE;
+
+ update_timer(p);
+}
+
+pa_rtpoll_item *pa_rtpoll_item_new(pa_rtpoll *p, pa_rtpoll_priority_t prio, unsigned n_fds) {
+ pa_rtpoll_item *i, *j, *l = NULL;
+
+ pa_assert(p);
+
+ if (!(i = pa_flist_pop(PA_STATIC_FLIST_GET(items))))
+ i = pa_xnew(pa_rtpoll_item, 1);
+
+ i->rtpoll = p;
+ i->dead = FALSE;
+ i->n_pollfd = n_fds;
+ i->pollfd = NULL;
+ i->priority = prio;
+
+ i->userdata = NULL;
+ i->before_cb = NULL;
+ i->after_cb = NULL;
+ i->work_cb = NULL;
+
+ for (j = p->items; j; j = j->next) {
+ if (prio <= j->priority)
+ break;
+
+ l = j;
+ }
+
+ PA_LLIST_INSERT_AFTER(pa_rtpoll_item, p->items, j ? j->prev : l, i);
+
+ if (n_fds > 0) {
+ p->rebuild_needed = 1;
+ p->n_pollfd_used += n_fds;
+ }
+
+ return i;
+}
+
+void pa_rtpoll_item_free(pa_rtpoll_item *i) {
+ pa_assert(i);
+
+ if (i->rtpoll->running) {
+ i->dead = TRUE;
+ i->rtpoll->scan_for_dead = TRUE;
+ return;
+ }
+
+ rtpoll_item_destroy(i);
+}
+
+struct pollfd *pa_rtpoll_item_get_pollfd(pa_rtpoll_item *i, unsigned *n_fds) {
+ pa_assert(i);
+
+ if (i->n_pollfd > 0)
+ if (i->rtpoll->rebuild_needed)
+ rtpoll_rebuild(i->rtpoll);
+
+ if (n_fds)
+ *n_fds = i->n_pollfd;
+
+ return i->pollfd;
+}
+
+void pa_rtpoll_item_set_before_callback(pa_rtpoll_item *i, int (*before_cb)(pa_rtpoll_item *i)) {
+ pa_assert(i);
+ pa_assert(i->priority < PA_RTPOLL_NEVER);
+
+ i->before_cb = before_cb;
+}
+
+void pa_rtpoll_item_set_after_callback(pa_rtpoll_item *i, void (*after_cb)(pa_rtpoll_item *i)) {
+ pa_assert(i);
+ pa_assert(i->priority < PA_RTPOLL_NEVER);
+
+ i->after_cb = after_cb;
+}
+
+void pa_rtpoll_item_set_work_callback(pa_rtpoll_item *i, int (*work_cb)(pa_rtpoll_item *i)) {
+ pa_assert(i);
+ pa_assert(i->priority < PA_RTPOLL_NEVER);
+
+ i->work_cb = work_cb;
+}
+
+void pa_rtpoll_item_set_userdata(pa_rtpoll_item *i, void *userdata) {
+ pa_assert(i);
+
+ i->userdata = userdata;
+}
+
+void* pa_rtpoll_item_get_userdata(pa_rtpoll_item *i) {
+ pa_assert(i);
+
+ return i->userdata;
+}
+
+static int fdsem_before(pa_rtpoll_item *i) {
+
+ if (pa_fdsem_before_poll(i->userdata) < 0)
+ return 1; /* 1 means immediate restart of the loop */
+
+ return 0;
+}
+
+static void fdsem_after(pa_rtpoll_item *i) {
+ pa_assert(i);
+
+ pa_assert((i->pollfd[0].revents & ~POLLIN) == 0);
+ pa_fdsem_after_poll(i->userdata);
+}
+
+pa_rtpoll_item *pa_rtpoll_item_new_fdsem(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_fdsem *f) {
+ pa_rtpoll_item *i;
+ struct pollfd *pollfd;
+
+ pa_assert(p);
+ pa_assert(f);
+
+ i = pa_rtpoll_item_new(p, prio, 1);
+
+ pollfd = pa_rtpoll_item_get_pollfd(i, NULL);
+
+ pollfd->fd = pa_fdsem_get(f);
+ pollfd->events = POLLIN;
+
+ i->before_cb = fdsem_before;
+ i->after_cb = fdsem_after;
+ i->userdata = f;
+
+ return i;
+}
+
+static int asyncmsgq_read_before(pa_rtpoll_item *i) {
+ pa_assert(i);
+
+ if (pa_asyncmsgq_read_before_poll(i->userdata) < 0)
+ return 1; /* 1 means immediate restart of the loop */
+
+ return 0;
+}
+
+static void asyncmsgq_read_after(pa_rtpoll_item *i) {
+ pa_assert(i);
+
+ pa_assert((i->pollfd[0].revents & ~POLLIN) == 0);
+ pa_asyncmsgq_read_after_poll(i->userdata);
+}
+
+static int asyncmsgq_read_work(pa_rtpoll_item *i) {
+ pa_msgobject *object;
+ int code;
+ void *data;
+ pa_memchunk chunk;
+ int64_t offset;
+
+ pa_assert(i);
+
+ if (pa_asyncmsgq_get(i->userdata, &object, &code, &data, &offset, &chunk, 0) == 0) {
+ int ret;
+
+ if (!object && code == PA_MESSAGE_SHUTDOWN) {
+ pa_asyncmsgq_done(i->userdata, 0);
+ pa_rtpoll_quit(i->rtpoll);
+ return 1;
+ }
+
+ ret = pa_asyncmsgq_dispatch(object, code, data, offset, &chunk);
+ pa_asyncmsgq_done(i->userdata, ret);
+ return 1;
+ }
+
+ return 0;
+}
+
+pa_rtpoll_item *pa_rtpoll_item_new_asyncmsgq_read(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_asyncmsgq *q) {
+ pa_rtpoll_item *i;
+ struct pollfd *pollfd;
+
+ pa_assert(p);
+ pa_assert(q);
+
+ i = pa_rtpoll_item_new(p, prio, 1);
+
+ pollfd = pa_rtpoll_item_get_pollfd(i, NULL);
+ pollfd->fd = pa_asyncmsgq_read_fd(q);
+ pollfd->events = POLLIN;
+
+ i->before_cb = asyncmsgq_read_before;
+ i->after_cb = asyncmsgq_read_after;
+ i->work_cb = asyncmsgq_read_work;
+ i->userdata = q;
+
+ return i;
+}
+
+static int asyncmsgq_write_before(pa_rtpoll_item *i) {
+ pa_assert(i);
+
+ pa_asyncmsgq_write_before_poll(i->userdata);
+ return 0;
+}
+
+static void asyncmsgq_write_after(pa_rtpoll_item *i) {
+ pa_assert(i);
+
+ pa_assert((i->pollfd[0].revents & ~POLLIN) == 0);
+ pa_asyncmsgq_write_after_poll(i->userdata);
+}
+
+pa_rtpoll_item *pa_rtpoll_item_new_asyncmsgq_write(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_asyncmsgq *q) {
+ pa_rtpoll_item *i;
+ struct pollfd *pollfd;
+
+ pa_assert(p);
+ pa_assert(q);
+
+ i = pa_rtpoll_item_new(p, prio, 1);
+
+ pollfd = pa_rtpoll_item_get_pollfd(i, NULL);
+ pollfd->fd = pa_asyncmsgq_write_fd(q);
+ pollfd->events = POLLIN;
+
+ i->before_cb = asyncmsgq_write_before;
+ i->after_cb = asyncmsgq_write_after;
+ i->work_cb = NULL;
+ i->userdata = q;
+
+ return i;
+}
+
+void pa_rtpoll_quit(pa_rtpoll *p) {
+ pa_assert(p);
+
+ p->quit = TRUE;
+}
diff --git a/src/pulsecore/rtpoll.h b/src/pulsecore/rtpoll.h
new file mode 100644
index 00000000..08776ef0
--- /dev/null
+++ b/src/pulsecore/rtpoll.h
@@ -0,0 +1,114 @@
+#ifndef foopulsertpollhfoo
+#define foopulsertpollhfoo
+
+/***
+ 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.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <sys/types.h>
+#include <limits.h>
+
+#include <pulse/sample.h>
+#include <pulsecore/asyncmsgq.h>
+#include <pulsecore/fdsem.h>
+#include <pulsecore/macro.h>
+
+/* An implementation of a "real-time" poll loop. Basically, this is
+ * yet another wrapper around poll(). However it has certain
+ * advantages over pa_mainloop and suchlike:
+ *
+ * 1) It uses timer_create() and POSIX real time signals to guarantee
+ * optimal high-resolution timing. Starting with Linux 2.6.21 hrtimers
+ * are available, and since right now only nanosleep() and the POSIX
+ * clock and timer interfaces have been ported to hrtimers (and not
+ * ppoll/pselect!) we have to combine ppoll() with timer_create(). The
+ * fact that POSIX timers and POSIX rt signals are used internally is
+ * completely hidden.
+ *
+ * 2) It allows raw access to the pollfd data to users
+ *
+ * 3) It allows arbitrary functions to be run before entering the
+ * actual poll() and after it.
+ *
+ * Only a single interval timer is supported..*/
+
+typedef struct pa_rtpoll pa_rtpoll;
+typedef struct pa_rtpoll_item pa_rtpoll_item;
+
+typedef enum pa_rtpoll_priority {
+ PA_RTPOLL_EARLY = -100, /* For veeery important stuff, like handling control messages */
+ PA_RTPOLL_NORMAL = 0, /* For normal stuff */
+ PA_RTPOLL_LATE = +100, /* For housekeeping */
+ PA_RTPOLL_NEVER = INT_MAX, /* For stuff that doesn't register any callbacks, but only fds to listen on */
+} pa_rtpoll_priority_t;
+
+pa_rtpoll *pa_rtpoll_new(void);
+void pa_rtpoll_free(pa_rtpoll *p);
+
+/* Install the rtpoll in the current thread */
+void pa_rtpoll_install(pa_rtpoll *p);
+
+/* Sleep on the rtpoll until the time event, or any of the fd events
+ * is triggered. If "wait" is 0 we don't sleep but only update the
+ * struct pollfd. Returns negative on error, positive if the loop
+ * should continue to run, 0 when the loop should be terminated
+ * cleanly. */
+int pa_rtpoll_run(pa_rtpoll *f, pa_bool_t wait);
+
+void pa_rtpoll_set_timer_absolute(pa_rtpoll *p, pa_usec_t usec);
+void pa_rtpoll_set_timer_relative(pa_rtpoll *p, pa_usec_t usec);
+void pa_rtpoll_set_timer_disabled(pa_rtpoll *p);
+
+/* A new fd wakeup item for pa_rtpoll */
+pa_rtpoll_item *pa_rtpoll_item_new(pa_rtpoll *p, pa_rtpoll_priority_t prio, unsigned n_fds);
+void pa_rtpoll_item_free(pa_rtpoll_item *i);
+
+/* Please note that this pointer might change on every call and when
+ * pa_rtpoll_run() is called. Hence: call this immediately before
+ * using the pointer and don't save the result anywhere */
+struct pollfd *pa_rtpoll_item_get_pollfd(pa_rtpoll_item *i, unsigned *n_fds);
+
+/* Set the callback that shall be called when there's time to do some work: If the
+ * callback returns a value > 0, the poll is skipped and the next
+ * iteraton of the loop will start immediately. */
+void pa_rtpoll_item_set_work_callback(pa_rtpoll_item *i, int (*work_cb)(pa_rtpoll_item *i));
+
+/* Set the callback that shall be called immediately before entering
+ * the sleeping poll: If the callback returns a value > 0, the poll is
+ * skipped and the next iteraton of the loop will start
+ * immediately.. */
+void pa_rtpoll_item_set_before_callback(pa_rtpoll_item *i, int (*before_cb)(pa_rtpoll_item *i));
+
+/* Set the callback that shall be called immediately after having
+ * entered the sleeping poll */
+void pa_rtpoll_item_set_after_callback(pa_rtpoll_item *i, void (*after_cb)(pa_rtpoll_item *i));
+
+void pa_rtpoll_item_set_userdata(pa_rtpoll_item *i, void *userdata);
+void* pa_rtpoll_item_get_userdata(pa_rtpoll_item *i);
+
+pa_rtpoll_item *pa_rtpoll_item_new_fdsem(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_fdsem *s);
+pa_rtpoll_item *pa_rtpoll_item_new_asyncmsgq_read(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_asyncmsgq *q);
+pa_rtpoll_item *pa_rtpoll_item_new_asyncmsgq_write(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_asyncmsgq *q);
+
+/* Requests the loop to exit. Will cause the next iteration of
+ * pa_rtpoll_run() to return 0 */
+void pa_rtpoll_quit(pa_rtpoll *p);
+
+#endif
diff --git a/src/pulsecore/rtsig.c b/src/pulsecore/rtsig.c
new file mode 100644
index 00000000..4df217c3
--- /dev/null
+++ b/src/pulsecore/rtsig.c
@@ -0,0 +1,131 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+ Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <signal.h>
+
+#include <pulsecore/macro.h>
+#include <pulsecore/flist.h>
+#include <pulsecore/once.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/core-util.h>
+
+#include "rtsig.h"
+
+#ifdef SIGRTMIN
+
+static void _free_rtsig(void *p) {
+ pa_rtsig_put(PA_PTR_TO_INT(p));
+}
+
+PA_STATIC_FLIST_DECLARE(rtsig_flist, pa_make_power_of_two(SIGRTMAX-SIGRTMIN+1), NULL);
+PA_STATIC_TLS_DECLARE(rtsig_tls, _free_rtsig);
+
+static pa_atomic_t rtsig_current = PA_ATOMIC_INIT(-1);
+
+static int rtsig_start = -1, rtsig_end = -1;
+
+int pa_rtsig_get(void) {
+ void *p;
+ int sig;
+
+ if ((p = pa_flist_pop(PA_STATIC_FLIST_GET(rtsig_flist))))
+ return PA_PTR_TO_INT(p);
+
+ sig = pa_atomic_dec(&rtsig_current);
+
+ pa_assert(sig <= SIGRTMAX);
+ pa_assert(sig <= rtsig_end);
+
+ if (sig < rtsig_start) {
+ pa_atomic_inc(&rtsig_current);
+ return -1;
+ }
+
+ return sig;
+}
+
+int pa_rtsig_get_for_thread(void) {
+ int sig;
+ void *p;
+
+ if ((p = PA_STATIC_TLS_GET(rtsig_tls)))
+ return PA_PTR_TO_INT(p);
+
+ if ((sig = pa_rtsig_get()) < 0)
+ return -1;
+
+ PA_STATIC_TLS_SET(rtsig_tls, PA_INT_TO_PTR(sig));
+ return sig;
+}
+
+void pa_rtsig_put(int sig) {
+ pa_assert(sig >= rtsig_start);
+ pa_assert(sig <= rtsig_end);
+
+ pa_assert_se(pa_flist_push(PA_STATIC_FLIST_GET(rtsig_flist), PA_INT_TO_PTR(sig)) >= 0);
+}
+
+void pa_rtsig_configure(int start, int end) {
+ int s;
+ sigset_t ss;
+
+ pa_assert(pa_atomic_load(&rtsig_current) == -1);
+
+ pa_assert(SIGRTMIN <= start);
+ pa_assert(start <= end);
+ pa_assert(end <= SIGRTMAX);
+
+ rtsig_start = start;
+ rtsig_end = end;
+
+ sigemptyset(&ss);
+
+ for (s = rtsig_start; s <= rtsig_end; s++)
+ pa_assert_se(sigaddset(&ss, s) == 0);
+
+ pa_assert(pthread_sigmask(SIG_BLOCK, &ss, NULL) == 0);
+
+ /* We allocate starting from the end */
+ pa_atomic_store(&rtsig_current, rtsig_end);
+}
+
+#else /* SIGRTMIN */
+
+int pa_rtsig_get(void) {
+ return -1;
+}
+
+int pa_rtsig_get_for_thread(void) {
+ return -1;
+}
+
+void pa_rtsig_put(int sig) {
+}
+
+void pa_rtsig_configure(int start, int end) {
+}
+
+#endif /* SIGRTMIN */
diff --git a/src/pulsecore/rtsig.h b/src/pulsecore/rtsig.h
new file mode 100644
index 00000000..e414122d
--- /dev/null
+++ b/src/pulsecore/rtsig.h
@@ -0,0 +1,39 @@
+#ifndef foopulsertsighfoo
+#define foopulsertsighfoo
+
+/***
+ 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.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+/* Return the next unused POSIX Realtime signals */
+int pa_rtsig_get(void);
+
+/* If not called before in the current thread, return the next unused
+ * rtsig, and install it in a TLS region and give it up automatically
+ * when the thread shuts down */
+int pa_rtsig_get_for_thread(void);
+
+/* Give an rtsig back. */
+void pa_rtsig_put(int sig);
+
+/* Block all RT signals */
+void pa_rtsig_configure(int start, int end);
+
+#endif
diff --git a/src/pulsecore/sample-util.c b/src/pulsecore/sample-util.c
index 52023d31..b42b79d1 100644
--- a/src/pulsecore/sample-util.c
+++ b/src/pulsecore/sample-util.c
@@ -1,18 +1,19 @@
-/* $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
@@ -25,72 +26,127 @@
#include <stdio.h>
#include <string.h>
-#include <assert.h>
#include <stdlib.h>
#include <liboil/liboilfuncs.h>
+#include <liboil/liboil.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/g711.h>
#include "sample-util.h"
#include "endianmacros.h"
-pa_memblock *pa_silence_memblock_new(pa_mempool *pool, const pa_sample_spec *spec, size_t length) {
- assert(pool);
- assert(spec);
-
- if (length == 0)
- length = pa_bytes_per_second(spec)/20; /* 50 ms */
-
- return pa_silence_memblock(pa_memblock_new(pool, length), spec);
-}
+#define PA_SILENCE_MAX (PA_PAGE_SIZE*16)
pa_memblock *pa_silence_memblock(pa_memblock* b, const pa_sample_spec *spec) {
void *data;
- assert(b);
- assert(spec);
-
+ pa_assert(b);
+ pa_assert(spec);
+
data = pa_memblock_acquire(b);
pa_silence_memory(data, pa_memblock_get_length(b), spec);
pa_memblock_release(b);
+
return b;
}
-void pa_silence_memchunk(pa_memchunk *c, const pa_sample_spec *spec) {
+pa_memchunk* pa_silence_memchunk(pa_memchunk *c, const pa_sample_spec *spec) {
void *data;
-
- assert(c);
- assert(c->memblock);
- assert(spec);
+
+ pa_assert(c);
+ pa_assert(c->memblock);
+ pa_assert(spec);
data = pa_memblock_acquire(c->memblock);
pa_silence_memory((uint8_t*) data+c->index, c->length, spec);
pa_memblock_release(c->memblock);
-}
-void pa_silence_memory(void *p, size_t length, const pa_sample_spec *spec) {
- uint8_t c = 0;
- assert(p && length && spec);
+ return c;
+}
- switch (spec->format) {
+static uint8_t silence_byte(pa_sample_format_t format) {
+ switch (format) {
case PA_SAMPLE_U8:
- c = 0x80;
- break;
+ return 0x80;
case PA_SAMPLE_S16LE:
case PA_SAMPLE_S16BE:
- case PA_SAMPLE_FLOAT32:
- c = 0;
- break;
+ case PA_SAMPLE_S32LE:
+ case PA_SAMPLE_S32BE:
+ case PA_SAMPLE_FLOAT32LE:
+ case PA_SAMPLE_FLOAT32BE:
+ return 0;
case PA_SAMPLE_ALAW:
+ return 0xd5;
case PA_SAMPLE_ULAW:
- c = 80;
- break;
+ return 0xff;
default:
- assert(0);
+ pa_assert_not_reached();
+ }
+ return 0;
+}
+
+void* pa_silence_memory(void *p, size_t length, const pa_sample_spec *spec) {
+ pa_assert(p);
+ pa_assert(length > 0);
+ pa_assert(spec);
+
+ memset(p, silence_byte(spec->format), length);
+ return p;
+}
+
+static void calc_linear_integer_stream_volumes(pa_mix_info streams[], unsigned nstreams, const pa_sample_spec *spec) {
+ unsigned k;
+
+ pa_assert(streams);
+ pa_assert(spec);
+
+ for (k = 0; k < nstreams; k++) {
+ unsigned channel;
+
+ for (channel = 0; channel < spec->channels; channel++) {
+ pa_mix_info *m = streams + k;
+ m->linear[channel].i = (int32_t) (pa_sw_volume_to_linear(m->volume.values[channel]) * 0x10000);
+ }
+ }
+}
+
+static void calc_linear_integer_volume(int32_t linear[], const pa_cvolume *volume) {
+ unsigned channel;
+
+ pa_assert(linear);
+ pa_assert(volume);
+
+ for (channel = 0; channel < volume->channels; channel++)
+ linear[channel] = (int32_t) (pa_sw_volume_to_linear(volume->values[channel]) * 0x10000);
+}
+
+static void calc_linear_float_stream_volumes(pa_mix_info streams[], unsigned nstreams, const pa_sample_spec *spec) {
+ unsigned k;
+
+ pa_assert(streams);
+ pa_assert(spec);
+
+ for (k = 0; k < nstreams; k++) {
+ unsigned channel;
+
+ for (channel = 0; channel < spec->channels; channel++) {
+ pa_mix_info *m = streams + k;
+ m->linear[channel].f = pa_sw_volume_to_linear(m->volume.values[channel]);
+ }
}
-
- memset(p, c, length);
+}
+
+static void calc_linear_float_volume(float linear[], const pa_cvolume *volume) {
+ unsigned channel;
+
+ pa_assert(linear);
+ pa_assert(volume);
+
+ for (channel = 0; channel < volume->channels; channel++)
+ linear[channel] = pa_sw_volume_to_linear(volume->values[channel]);
}
size_t pa_mix(
@@ -100,226 +156,442 @@ size_t pa_mix(
size_t length,
const pa_sample_spec *spec,
const pa_cvolume *volume,
- int mute) {
+ pa_bool_t mute) {
pa_cvolume full_volume;
- size_t d = 0;
unsigned k;
-
- assert(streams);
- assert(data);
- assert(length);
- assert(spec);
+ size_t d = 0;
+
+ pa_assert(streams);
+ pa_assert(data);
+ pa_assert(length);
+ pa_assert(spec);
if (!volume)
volume = pa_cvolume_reset(&full_volume, spec->channels);
for (k = 0; k < nstreams; k++)
- streams[k].internal = pa_memblock_acquire(streams[k].chunk.memblock);
-
+ streams[k].ptr = (uint8_t*) pa_memblock_acquire(streams[k].chunk.memblock) + streams[k].chunk.index;
+
switch (spec->format) {
+
case PA_SAMPLE_S16NE:{
unsigned channel = 0;
-
+ int32_t linear[PA_CHANNELS_MAX];
+
+ calc_linear_integer_stream_volumes(streams, nstreams, spec);
+ calc_linear_integer_volume(linear, volume);
+
for (d = 0;; d += sizeof(int16_t)) {
int32_t sum = 0;
-
- if (d >= length)
+ unsigned i;
+
+ if (PA_UNLIKELY(d >= length))
goto finish;
- if (!mute && volume->values[channel] != PA_VOLUME_MUTED) {
- unsigned i;
-
- for (i = 0; i < nstreams; i++) {
- int32_t v;
- pa_volume_t cvolume = streams[i].volume.values[channel];
-
- if (d >= streams[i].chunk.length)
- goto finish;
-
- if (cvolume == PA_VOLUME_MUTED)
- v = 0;
- else {
- v = *((int16_t*) ((uint8_t*) streams[i].internal + streams[i].chunk.index + d));
-
- if (cvolume != PA_VOLUME_NORM)
- v = (int32_t) (v * pa_sw_volume_to_linear(cvolume));
- }
-
- sum += v;
- }
-
- if (volume->values[channel] != PA_VOLUME_NORM)
- sum = (int32_t) (sum * pa_sw_volume_to_linear(volume->values[channel]));
+ for (i = 0; i < nstreams; i++) {
+ pa_mix_info *m = streams + i;
+ int32_t v, cv = m->linear[channel].i;
- if (sum < -0x8000) sum = -0x8000;
- if (sum > 0x7FFF) sum = 0x7FFF;
+ if (PA_UNLIKELY(d >= m->chunk.length))
+ goto finish;
+
+ if (PA_UNLIKELY(cv <= 0) || PA_UNLIKELY(!!mute) || PA_UNLIKELY(linear[channel] <= 0))
+ v = 0;
+ else {
+ v = *((int16_t*) m->ptr);
+ v = (v * cv) / 0x10000;
+ }
+ sum += v;
+ m->ptr = (uint8_t*) m->ptr + sizeof(int16_t);
}
-
- *((int16_t*) data) = sum;
+
+ sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF);
+ sum = (sum * linear[channel]) / 0x10000;
+ *((int16_t*) data) = (int16_t) sum;
+
data = (uint8_t*) data + sizeof(int16_t);
-
- if (++channel >= spec->channels)
+
+ if (PA_UNLIKELY(++channel >= spec->channels))
channel = 0;
}
-
+
break;
}
case PA_SAMPLE_S16RE:{
unsigned channel = 0;
-
+ int32_t linear[PA_CHANNELS_MAX];
+
+ calc_linear_integer_stream_volumes(streams, nstreams, spec);
+ calc_linear_integer_volume(linear, volume);
+
for (d = 0;; d += sizeof(int16_t)) {
int32_t sum = 0;
-
- if (d >= length)
+ unsigned i;
+
+ if (PA_UNLIKELY(d >= length))
goto finish;
- if (!mute && volume->values[channel] != PA_VOLUME_MUTED) {
- unsigned i;
-
- for (i = 0; i < nstreams; i++) {
- int32_t v;
- pa_volume_t cvolume = streams[i].volume.values[channel];
-
- if (d >= streams[i].chunk.length)
- goto finish;
-
- if (cvolume == PA_VOLUME_MUTED)
- v = 0;
- else {
- v = INT16_SWAP(*((int16_t*) ((uint8_t*) streams[i].internal + streams[i].chunk.index + d)));
-
- if (cvolume != PA_VOLUME_NORM)
- v = (int32_t) (v * pa_sw_volume_to_linear(cvolume));
- }
-
- sum += v;
- }
-
- if (volume->values[channel] != PA_VOLUME_NORM)
- sum = (int32_t) (sum * pa_sw_volume_to_linear(volume->values[channel]));
+ for (i = 0; i < nstreams; i++) {
+ pa_mix_info *m = streams + i;
+ int32_t v, cv = m->linear[channel].i;
- if (sum < -0x8000) sum = -0x8000;
- if (sum > 0x7FFF) sum = 0x7FFF;
+ if (PA_UNLIKELY(d >= m->chunk.length))
+ goto finish;
+ if (PA_UNLIKELY(cv <= 0) || PA_UNLIKELY(!!mute) || PA_UNLIKELY(linear[channel] <= 0))
+ v = 0;
+ else {
+ v = PA_INT16_SWAP(*((int16_t*) m->ptr));
+ v = (v * cv) / 0x10000;
+ }
+
+ sum += v;
+ m->ptr = (uint8_t*) m->ptr + sizeof(int16_t);
}
-
- *((int16_t*) data) = INT16_SWAP(sum);
+
+ sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF);
+ sum = (sum * linear[channel]) / 0x10000;
+ *((int16_t*) data) = PA_INT16_SWAP((int16_t) sum);
+
data = (uint8_t*) data + sizeof(int16_t);
-
- if (++channel >= spec->channels)
+
+ if (PA_UNLIKELY(++channel >= spec->channels))
+ channel = 0;
+ }
+
+ break;
+ }
+
+ case PA_SAMPLE_S32NE:{
+ unsigned channel = 0;
+ int32_t linear[PA_CHANNELS_MAX];
+
+ calc_linear_integer_stream_volumes(streams, nstreams, spec);
+ calc_linear_integer_volume(linear, volume);
+
+ for (d = 0;; d += sizeof(int32_t)) {
+ int64_t sum = 0;
+ unsigned i;
+
+ if (PA_UNLIKELY(d >= length))
+ goto finish;
+
+ for (i = 0; i < nstreams; i++) {
+ pa_mix_info *m = streams + i;
+ int64_t v;
+ int32_t cv = m->linear[channel].i;
+
+ if (PA_UNLIKELY(d >= m->chunk.length))
+ goto finish;
+
+ if (PA_UNLIKELY(cv <= 0) || PA_UNLIKELY(!!mute) || PA_UNLIKELY(linear[channel] <= 0))
+ v = 0;
+ else {
+ v = *((int32_t*) m->ptr);
+ v = (v * cv) / 0x10000;
+ }
+
+ sum += v;
+ m->ptr = (uint8_t*) m->ptr + sizeof(int32_t);
+ }
+
+ sum = PA_CLAMP_UNLIKELY(sum, -0x80000000LL, 0x7FFFFFFFLL);
+ sum = (sum * linear[channel]) / 0x10000;
+ *((int32_t*) data) = (int32_t) sum;
+
+ data = (uint8_t*) data + sizeof(int32_t);
+
+ if (PA_UNLIKELY(++channel >= spec->channels))
channel = 0;
}
break;
}
-
+
+ case PA_SAMPLE_S32RE:{
+ unsigned channel = 0;
+ int32_t linear[PA_CHANNELS_MAX];
+
+ calc_linear_integer_stream_volumes(streams, nstreams, spec);
+ calc_linear_integer_volume(linear, volume);
+
+ for (d = 0;; d += sizeof(int32_t)) {
+ int64_t sum = 0;
+ unsigned i;
+
+ if (PA_UNLIKELY(d >= length))
+ goto finish;
+
+ for (i = 0; i < nstreams; i++) {
+ pa_mix_info *m = streams + i;
+ int64_t v;
+ int32_t cv = m->linear[channel].i;
+
+ if (PA_UNLIKELY(d >= m->chunk.length))
+ goto finish;
+
+ if (PA_UNLIKELY(cv <= 0) || PA_UNLIKELY(!!mute) || PA_UNLIKELY(linear[channel] <= 0))
+ v = 0;
+ else {
+ v = PA_INT32_SWAP(*((int32_t*) m->ptr));
+ v = (v * cv) / 0x10000;
+ }
+
+ sum += v;
+ m->ptr = (uint8_t*) m->ptr + sizeof(int32_t);
+ }
+
+ sum = PA_CLAMP_UNLIKELY(sum, -0x80000000LL, 0x7FFFFFFFLL);
+ sum = (sum * linear[channel]) / 0x10000;
+ *((int32_t*) data) = PA_INT32_SWAP((int32_t) sum);
+
+ data = (uint8_t*) data + sizeof(int32_t);
+
+ if (PA_UNLIKELY(++channel >= spec->channels))
+ channel = 0;
+ }
+
+ break;
+ }
+
case PA_SAMPLE_U8: {
unsigned channel = 0;
-
+ int32_t linear[PA_CHANNELS_MAX];
+
+ calc_linear_integer_stream_volumes(streams, nstreams, spec);
+ calc_linear_integer_volume(linear, volume);
+
for (d = 0;; d ++) {
int32_t sum = 0;
-
- if (d >= length)
+ unsigned i;
+
+ if (PA_UNLIKELY(d >= length))
goto finish;
- if (!mute && volume->values[channel] != PA_VOLUME_MUTED) {
- unsigned i;
-
- for (i = 0; i < nstreams; i++) {
- int32_t v;
- pa_volume_t cvolume = streams[i].volume.values[channel];
-
- if (d >= streams[i].chunk.length)
- goto finish;
-
- if (cvolume == PA_VOLUME_MUTED)
- v = 0;
- else {
- v = (int32_t) *((uint8_t*) streams[i].internal + streams[i].chunk.index + d) - 0x80;
-
- if (cvolume != PA_VOLUME_NORM)
- v = (int32_t) (v * pa_sw_volume_to_linear(cvolume));
- }
-
- sum += v;
- }
+ for (i = 0; i < nstreams; i++) {
+ pa_mix_info *m = streams + i;
+ int32_t v, cv = m->linear[channel].i;
- if (volume->values[channel] != PA_VOLUME_NORM)
- sum = (int32_t) (sum * pa_sw_volume_to_linear(volume->values[channel]));
+ if (PA_UNLIKELY(d >= m->chunk.length))
+ goto finish;
- if (sum < -0x80) sum = -0x80;
- if (sum > 0x7F) sum = 0x7F;
+ if (PA_UNLIKELY(cv <= 0) || PA_UNLIKELY(!!mute) || PA_UNLIKELY(linear[channel] <= 0))
+ v = 0;
+ else {
+ v = (int32_t) *((uint8_t*) m->ptr) - 0x80;
+ v = (v * cv) / 0x10000;
+ }
+ sum += v;
+ m->ptr = (uint8_t*) m->ptr + 1;
}
-
+
+ sum = (sum * linear[channel]) / 0x10000;
+ sum = PA_CLAMP_UNLIKELY(sum, -0x80, 0x7F);
*((uint8_t*) data) = (uint8_t) (sum + 0x80);
+
+ data = (uint8_t*) data + 1;
+
+ if (PA_UNLIKELY(++channel >= spec->channels))
+ channel = 0;
+ }
+
+ break;
+ }
+
+ case PA_SAMPLE_ULAW: {
+ unsigned channel = 0;
+ int32_t linear[PA_CHANNELS_MAX];
+
+ calc_linear_integer_stream_volumes(streams, nstreams, spec);
+ calc_linear_integer_volume(linear, volume);
+
+ for (d = 0;; d ++) {
+ int32_t sum = 0;
+ unsigned i;
+
+ if (PA_UNLIKELY(d >= length))
+ goto finish;
+
+ for (i = 0; i < nstreams; i++) {
+ pa_mix_info *m = streams + i;
+ int32_t v, cv = m->linear[channel].i;
+
+ if (PA_UNLIKELY(d >= m->chunk.length))
+ goto finish;
+
+ if (PA_UNLIKELY(cv <= 0) || PA_UNLIKELY(!!mute) || PA_UNLIKELY(linear[channel] <= 0))
+ v = 0;
+ else {
+ v = (int32_t) st_ulaw2linear16(*((uint8_t*) m->ptr));
+ v = (v * cv) / 0x10000;
+ }
+
+ sum += v;
+ m->ptr = (uint8_t*) m->ptr + 1;
+ }
+
+ sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF);
+ sum = (sum * linear[channel]) / 0x10000;
+ *((uint8_t*) data) = (uint8_t) st_14linear2ulaw(sum >> 2);
+
+ data = (uint8_t*) data + 1;
+
+ if (PA_UNLIKELY(++channel >= spec->channels))
+ channel = 0;
+ }
+
+ break;
+ }
+
+ case PA_SAMPLE_ALAW: {
+ unsigned channel = 0;
+ int32_t linear[PA_CHANNELS_MAX];
+
+ calc_linear_integer_stream_volumes(streams, nstreams, spec);
+ calc_linear_integer_volume(linear, volume);
+
+ for (d = 0;; d ++) {
+ int32_t sum = 0;
+ unsigned i;
+
+ if (PA_UNLIKELY(d >= length))
+ goto finish;
+
+ for (i = 0; i < nstreams; i++) {
+ pa_mix_info *m = streams + i;
+ int32_t v, cv = m->linear[channel].i;
+
+ if (PA_UNLIKELY(d >= m->chunk.length))
+ goto finish;
+
+ if (PA_UNLIKELY(cv <= 0) || PA_UNLIKELY(!!mute) || PA_UNLIKELY(linear[channel] <= 0))
+ v = 0;
+ else {
+ v = (int32_t) st_alaw2linear16(*((uint8_t*) m->ptr));
+ v = (v * cv) / 0x10000;
+ }
+
+ sum += v;
+ m->ptr = (uint8_t*) m->ptr + 1;
+ }
+
+ sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF);
+ sum = (sum * linear[channel]) / 0x10000;
+ *((uint8_t*) data) = (uint8_t) st_13linear2alaw(sum >> 3);
+
data = (uint8_t*) data + 1;
-
- if (++channel >= spec->channels)
+
+ if (PA_UNLIKELY(++channel >= spec->channels))
channel = 0;
}
-
+
break;
}
-
+
case PA_SAMPLE_FLOAT32NE: {
unsigned channel = 0;
-
+ float linear[PA_CHANNELS_MAX];
+
+ calc_linear_float_stream_volumes(streams, nstreams, spec);
+ calc_linear_float_volume(linear, volume);
+
for (d = 0;; d += sizeof(float)) {
float sum = 0;
-
- if (d >= length)
+ unsigned i;
+
+ if (PA_UNLIKELY(d >= length))
goto finish;
-
- if (!mute && volume->values[channel] != PA_VOLUME_MUTED) {
- unsigned i;
-
- for (i = 0; i < nstreams; i++) {
- float v;
- pa_volume_t cvolume = streams[i].volume.values[channel];
-
- if (d >= streams[i].chunk.length)
- goto finish;
-
- if (cvolume == PA_VOLUME_MUTED)
- v = 0;
- else {
- v = *((float*) ((uint8_t*) streams[i].internal + streams[i].chunk.index + d));
-
- if (cvolume != PA_VOLUME_NORM)
- v *= pa_sw_volume_to_linear(cvolume);
- }
-
- sum += v;
+
+ for (i = 0; i < nstreams; i++) {
+ pa_mix_info *m = streams + i;
+ float v, cv = m->linear[channel].f;
+
+ if (PA_UNLIKELY(d >= m->chunk.length))
+ goto finish;
+
+ if (PA_UNLIKELY(cv <= 0) || PA_UNLIKELY(!!mute) || PA_UNLIKELY(linear[channel] <= 0))
+ v = 0;
+ else {
+ v = *((float*) m->ptr);
+ v *= cv;
}
-
- if (volume->values[channel] != PA_VOLUME_NORM)
- sum *= pa_sw_volume_to_linear(volume->values[channel]);
+
+ sum += v;
+ m->ptr = (uint8_t*) m->ptr + sizeof(float);
}
-
+
+ sum *= linear[channel];
*((float*) data) = sum;
+
data = (uint8_t*) data + sizeof(float);
- if (++channel >= spec->channels)
+ if (PA_UNLIKELY(++channel >= spec->channels))
channel = 0;
}
break;
}
-
+
+ case PA_SAMPLE_FLOAT32RE: {
+ unsigned channel = 0;
+ float linear[PA_CHANNELS_MAX];
+
+ calc_linear_float_stream_volumes(streams, nstreams, spec);
+ calc_linear_float_volume(linear, volume);
+
+ for (d = 0;; d += sizeof(float)) {
+ float sum = 0;
+ unsigned i;
+
+ if (PA_UNLIKELY(d >= length))
+ goto finish;
+
+ for (i = 0; i < nstreams; i++) {
+ pa_mix_info *m = streams + i;
+ float v, cv = m->linear[channel].f;
+
+ if (PA_UNLIKELY(d >= m->chunk.length))
+ goto finish;
+
+ if (PA_UNLIKELY(cv <= 0) || PA_UNLIKELY(!!mute) || PA_UNLIKELY(linear[channel] <= 0))
+ v = 0;
+ else {
+ uint32_t z = *(uint32_t*) m->ptr;
+ z = PA_UINT32_SWAP(z);
+ v = *((float*) &z);
+ v *= cv;
+ }
+
+ sum += v;
+ m->ptr = (uint8_t*) m->ptr + sizeof(float);
+ }
+
+ sum *= linear[channel];
+ *((uint32_t*) data) = PA_UINT32_SWAP(*(uint32_t*) &sum);
+
+ data = (uint8_t*) data + sizeof(float);
+
+ if (PA_UNLIKELY(++channel >= spec->channels))
+ channel = 0;
+ }
+
+ break;
+ }
+
default:
pa_log_error("ERROR: Unable to mix audio data of format %s.", pa_sample_format_to_string(spec->format));
- abort();
+ pa_assert_not_reached();
}
finish:
for (k = 0; k < nstreams; k++)
pa_memblock_release(streams[k].chunk.memblock);
-
+
return d;
}
@@ -330,11 +602,14 @@ void pa_volume_memchunk(
const pa_cvolume *volume) {
void *ptr;
-
- assert(c);
- assert(spec);
- assert(c->length % pa_frame_size(spec) == 0);
- assert(volume);
+
+ pa_assert(c);
+ pa_assert(spec);
+ pa_assert(c->length % pa_frame_size(spec) == 0);
+ pa_assert(volume);
+
+ if (pa_memblock_is_silence(c->memblock))
+ return;
if (pa_cvolume_channels_equal_to(volume, PA_VOLUME_NORM))
return;
@@ -344,29 +619,27 @@ void pa_volume_memchunk(
return;
}
- ptr = pa_memblock_acquire(c->memblock);
+ ptr = (uint8_t*) pa_memblock_acquire(c->memblock) + c->index;
switch (spec->format) {
+
case PA_SAMPLE_S16NE: {
int16_t *d;
size_t n;
unsigned channel;
- double linear[PA_CHANNELS_MAX];
-
- for (channel = 0; channel < spec->channels; channel++)
- linear[channel] = pa_sw_volume_to_linear(volume->values[channel]);
-
- for (channel = 0, d = (int16_t*) ((uint8_t*) ptr + c->index), n = c->length/sizeof(int16_t); n > 0; d++, n--) {
- int32_t t = (int32_t)(*d);
-
- t = (int32_t) (t * linear[channel]);
-
- if (t < -0x8000) t = -0x8000;
- if (t > 0x7FFF) t = 0x7FFF;
-
+ int32_t linear[PA_CHANNELS_MAX];
+
+ calc_linear_integer_volume(linear, volume);
+
+ for (channel = 0, d = ptr, n = c->length/sizeof(int16_t); n > 0; d++, n--) {
+ int32_t t;
+
+ t = (int32_t)(*d);
+ t = (t * linear[channel]) / 0x10000;
+ t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
*d = (int16_t) t;
-
- if (++channel >= spec->channels)
+
+ if (PA_UNLIKELY(++channel >= spec->channels))
channel = 0;
}
break;
@@ -376,79 +649,379 @@ void pa_volume_memchunk(
int16_t *d;
size_t n;
unsigned channel;
- double linear[PA_CHANNELS_MAX];
-
- for (channel = 0; channel < spec->channels; channel++)
- linear[channel] = pa_sw_volume_to_linear(volume->values[channel]);
-
- for (channel = 0, d = (int16_t*) ((uint8_t*) ptr + c->index), n = c->length/sizeof(int16_t); n > 0; d++, n--) {
- int32_t t = (int32_t)(INT16_SWAP(*d));
-
- t = (int32_t) (t * linear[channel]);
-
- if (t < -0x8000) t = -0x8000;
- if (t > 0x7FFF) t = 0x7FFF;
-
- *d = INT16_SWAP((int16_t) t);
-
- if (++channel >= spec->channels)
+ int32_t linear[PA_CHANNELS_MAX];
+
+ calc_linear_integer_volume(linear, volume);
+
+ for (channel = 0, d = ptr, n = c->length/sizeof(int16_t); n > 0; d++, n--) {
+ int32_t t;
+
+ t = (int32_t)(PA_INT16_SWAP(*d));
+ t = (t * linear[channel]) / 0x10000;
+ t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
+ *d = PA_INT16_SWAP((int16_t) t);
+
+ if (PA_UNLIKELY(++channel >= spec->channels))
+ channel = 0;
+ }
+
+ break;
+ }
+
+ case PA_SAMPLE_S32NE: {
+ int32_t *d;
+ size_t n;
+ unsigned channel;
+ int32_t linear[PA_CHANNELS_MAX];
+
+ calc_linear_integer_volume(linear, volume);
+
+ for (channel = 0, d = ptr, n = c->length/sizeof(int32_t); n > 0; d++, n--) {
+ int64_t t;
+
+ t = (int64_t)(*d);
+ t = (t * linear[channel]) / 0x10000;
+ t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
+ *d = (int32_t) t;
+
+ if (PA_UNLIKELY(++channel >= spec->channels))
+ channel = 0;
+ }
+ break;
+ }
+
+ case PA_SAMPLE_S32RE: {
+ int32_t *d;
+ size_t n;
+ unsigned channel;
+ int32_t linear[PA_CHANNELS_MAX];
+
+ calc_linear_integer_volume(linear, volume);
+
+ for (channel = 0, d = ptr, n = c->length/sizeof(int32_t); n > 0; d++, n--) {
+ int64_t t;
+
+ t = (int64_t)(PA_INT32_SWAP(*d));
+ t = (t * linear[channel]) / 0x10000;
+ t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
+ *d = PA_INT32_SWAP((int32_t) t);
+
+ if (PA_UNLIKELY(++channel >= spec->channels))
channel = 0;
}
break;
}
-
+
case PA_SAMPLE_U8: {
uint8_t *d;
size_t n;
- unsigned channel = 0;
-
- for (d = (uint8_t*) ptr + c->index, n = c->length; n > 0; d++, n--) {
- int32_t t = (int32_t) *d - 0x80;
-
- t = (int32_t) (t * pa_sw_volume_to_linear(volume->values[channel]));
-
- if (t < -0x80) t = -0x80;
- if (t > 0x7F) t = 0x7F;
-
+ unsigned channel;
+ int32_t linear[PA_CHANNELS_MAX];
+
+ calc_linear_integer_volume(linear, volume);
+
+ for (channel = 0, d = ptr, n = c->length; n > 0; d++, n--) {
+ int32_t t;
+
+ t = (int32_t) *d - 0x80;
+ t = (t * linear[channel]) / 0x10000;
+ t = PA_CLAMP_UNLIKELY(t, -0x80, 0x7F);
*d = (uint8_t) (t + 0x80);
-
- if (++channel >= spec->channels)
+
+ if (PA_UNLIKELY(++channel >= spec->channels))
+ channel = 0;
+ }
+ break;
+ }
+
+ case PA_SAMPLE_ULAW: {
+ uint8_t *d;
+ size_t n;
+ unsigned channel;
+ int32_t linear[PA_CHANNELS_MAX];
+
+ calc_linear_integer_volume(linear, volume);
+
+ for (channel = 0, d = ptr, n = c->length; n > 0; d++, n--) {
+ int32_t t;
+
+ t = (int32_t) st_ulaw2linear16(*d);
+ t = (t * linear[channel]) / 0x10000;
+ t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
+ *d = (uint8_t) st_14linear2ulaw(t >> 2);
+
+ if (PA_UNLIKELY(++channel >= spec->channels))
channel = 0;
}
break;
}
-
+
+ case PA_SAMPLE_ALAW: {
+ uint8_t *d;
+ size_t n;
+ unsigned channel;
+ int32_t linear[PA_CHANNELS_MAX];
+
+ calc_linear_integer_volume(linear, volume);
+
+ for (channel = 0, d = ptr, n = c->length; n > 0; d++, n--) {
+ int32_t t;
+
+ t = (int32_t) st_alaw2linear16(*d);
+ t = (t * linear[channel]) / 0x10000;
+ t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
+ *d = (uint8_t) st_13linear2alaw(t >> 3);
+
+ if (PA_UNLIKELY(++channel >= spec->channels))
+ channel = 0;
+ }
+ break;
+ }
+
case PA_SAMPLE_FLOAT32NE: {
float *d;
int skip;
unsigned n;
unsigned channel;
-
- d = (float*) ((uint8_t*) ptr + c->index);
+
+ d = ptr;
skip = spec->channels * sizeof(float);
n = c->length/sizeof(float)/spec->channels;
-
- for (channel = 0; channel < spec->channels ; channel ++) {
+
+ for (channel = 0; channel < spec->channels; channel ++) {
float v, *t;
-
- if (volume->values[channel] == PA_VOLUME_NORM)
+
+ if (PA_UNLIKELY(volume->values[channel] == PA_VOLUME_NORM))
continue;
-
+
v = (float) pa_sw_volume_to_linear(volume->values[channel]);
-
t = d + channel;
oil_scalarmult_f32(t, skip, t, skip, &v, n);
}
break;
}
+ case PA_SAMPLE_FLOAT32RE: {
+ uint32_t *d;
+ size_t n;
+ unsigned channel;
+ float linear[PA_CHANNELS_MAX];
+
+ calc_linear_float_volume(linear, volume);
+
+ for (channel = 0, d = ptr, n = c->length/sizeof(float); n > 0; d++, n--) {
+ float t;
+ uint32_t z;
+
+ z = PA_UINT32_SWAP(*d);
+ t = *(float*) &z;
+ t *= linear[channel];
+ z = *(uint32_t*) &t;
+ *d = PA_UINT32_SWAP(z);
+
+ if (PA_UNLIKELY(++channel >= spec->channels))
+ channel = 0;
+ }
+
+ break;
+ }
+
+
default:
- pa_log_error("ERROR: Unable to change volume of format %s.",
- pa_sample_format_to_string(spec->format));
- abort();
+ pa_log_warn(" Unable to change volume of format %s.", pa_sample_format_to_string(spec->format));
+ /* If we cannot change the volume, we just don't do it */
}
pa_memblock_release(c->memblock);
}
+size_t pa_frame_align(size_t l, const pa_sample_spec *ss) {
+ size_t fs;
+
+ pa_assert(ss);
+
+ fs = pa_frame_size(ss);
+
+ return (l/fs) * fs;
+}
+
+int pa_frame_aligned(size_t l, const pa_sample_spec *ss) {
+ size_t fs;
+
+ pa_assert(ss);
+
+ fs = pa_frame_size(ss);
+
+ return l % fs == 0;
+}
+
+void pa_interleave(const void *src[], unsigned channels, void *dst, size_t ss, unsigned n) {
+ unsigned c;
+ size_t fs;
+
+ pa_assert(src);
+ pa_assert(channels > 0);
+ pa_assert(dst);
+ pa_assert(ss > 0);
+ pa_assert(n > 0);
+
+ fs = ss * channels;
+
+ for (c = 0; c < channels; c++) {
+ unsigned j;
+ void *d;
+ const void *s;
+
+ s = src[c];
+ d = (uint8_t*) dst + c * ss;
+
+ for (j = 0; j < n; j ++) {
+ oil_memcpy(d, s, ss);
+ s = (uint8_t*) s + ss;
+ d = (uint8_t*) d + fs;
+ }
+ }
+}
+
+void pa_deinterleave(const void *src, void *dst[], unsigned channels, size_t ss, unsigned n) {
+ size_t fs;
+ unsigned c;
+
+ pa_assert(src);
+ pa_assert(dst);
+ pa_assert(channels > 0);
+ pa_assert(ss > 0);
+ pa_assert(n > 0);
+
+ fs = ss * channels;
+
+ for (c = 0; c < channels; c++) {
+ unsigned j;
+ const void *s;
+ void *d;
+
+ s = (uint8_t*) src + c * ss;
+ d = dst[c];
+
+ for (j = 0; j < n; j ++) {
+ oil_memcpy(d, s, ss);
+ s = (uint8_t*) s + fs;
+ d = (uint8_t*) d + ss;
+ }
+ }
+}
+
+static pa_memblock *silence_memblock_new(pa_mempool *pool, uint8_t c) {
+ pa_memblock *b;
+ size_t length;
+ void *data;
+
+ pa_assert(pool);
+
+ length = PA_MIN(pa_mempool_block_size_max(pool), PA_SILENCE_MAX);
+
+ b = pa_memblock_new(pool, length);
+
+ data = pa_memblock_acquire(b);
+ memset(data, c, length);
+ pa_memblock_release(b);
+
+ pa_memblock_set_is_silence(b, TRUE);
+
+ return b;
+}
+
+void pa_silence_cache_init(pa_silence_cache *cache) {
+ pa_assert(cache);
+
+ memset(cache, 0, sizeof(pa_silence_cache));
+}
+
+void pa_silence_cache_done(pa_silence_cache *cache) {
+ pa_sample_format_t f;
+ pa_assert(cache);
+
+ for (f = 0; f < PA_SAMPLE_MAX; f++)
+ if (cache->blocks[f])
+ pa_memblock_unref(cache->blocks[f]);
+
+ memset(cache, 0, sizeof(pa_silence_cache));
+}
+
+pa_memchunk* pa_silence_memchunk_get(pa_silence_cache *cache, pa_mempool *pool, pa_memchunk* ret, const pa_sample_spec *spec, size_t length) {
+ pa_memblock *b;
+ size_t l;
+
+ pa_assert(cache);
+ pa_assert(pa_sample_spec_valid(spec));
+
+ if (!(b = cache->blocks[spec->format]))
+
+ switch (spec->format) {
+ case PA_SAMPLE_U8:
+ cache->blocks[PA_SAMPLE_U8] = b = silence_memblock_new(pool, 0x80);
+ break;
+ case PA_SAMPLE_S16LE:
+ case PA_SAMPLE_S16BE:
+ case PA_SAMPLE_S32LE:
+ case PA_SAMPLE_S32BE:
+ case PA_SAMPLE_FLOAT32LE:
+ case PA_SAMPLE_FLOAT32BE:
+ cache->blocks[PA_SAMPLE_S16LE] = b = silence_memblock_new(pool, 0);
+ cache->blocks[PA_SAMPLE_S16BE] = pa_memblock_ref(b);
+ cache->blocks[PA_SAMPLE_S32LE] = pa_memblock_ref(b);
+ cache->blocks[PA_SAMPLE_S32BE] = pa_memblock_ref(b);
+ cache->blocks[PA_SAMPLE_FLOAT32LE] = pa_memblock_ref(b);
+ cache->blocks[PA_SAMPLE_FLOAT32BE] = pa_memblock_ref(b);
+ break;
+ case PA_SAMPLE_ALAW:
+ cache->blocks[PA_SAMPLE_ALAW] = b = silence_memblock_new(pool, 0xd5);
+ break;
+ case PA_SAMPLE_ULAW:
+ cache->blocks[PA_SAMPLE_ULAW] = b = silence_memblock_new(pool, 0xff);
+ break;
+ default:
+ pa_assert_not_reached();
+ }
+
+ pa_assert(b);
+
+ ret->memblock = pa_memblock_ref(b);
+
+ l = pa_memblock_get_length(b);
+ if (length > l || length == 0)
+ length = l;
+
+ ret->length = pa_frame_align(length, spec);
+ ret->index = 0;
+
+ return ret;
+}
+
+void pa_sample_clamp(pa_sample_format_t format, void *dst, size_t dstr, const void *src, size_t sstr, unsigned n) {
+ const float *s;
+ float *d;
+
+ s = src; d = dst;
+
+ if (format == PA_SAMPLE_FLOAT32NE) {
+
+ float minus_one = -1.0, plus_one = 1.0;
+ oil_clip_f32(d, dstr, s, sstr, n, &minus_one, &plus_one);
+
+ } else {
+ pa_assert(format == PA_SAMPLE_FLOAT32RE);
+
+ for (; n > 0; n--) {
+ float f;
+
+ f = PA_FLOAT32_SWAP(*s);
+ f = PA_CLAMP_UNLIKELY(f, -1.0, 1.0);
+ *d = PA_FLOAT32_SWAP(f);
+
+ s = (const float*) ((const uint8_t*) s + sstr);
+ d = (float*) ((uint8_t*) d + dstr);
+ }
+ }
+}
diff --git a/src/pulsecore/sample-util.h b/src/pulsecore/sample-util.h
index 04c2f6b1..cef70750 100644
--- a/src/pulsecore/sample-util.h
+++ b/src/pulsecore/sample-util.h
@@ -1,21 +1,22 @@
#ifndef foosampleutilhfoo
#define foosampleutilhfoo
-/* $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
@@ -27,16 +28,31 @@
#include <pulsecore/memblock.h>
#include <pulsecore/memchunk.h>
-pa_memblock *pa_silence_memblock(pa_memblock* b, const pa_sample_spec *spec);
-pa_memblock *pa_silence_memblock_new(pa_mempool *pool, const pa_sample_spec *spec, size_t length);
-void pa_silence_memchunk(pa_memchunk *c, const pa_sample_spec *spec);
-void pa_silence_memory(void *p, size_t length, const pa_sample_spec *spec);
+typedef struct pa_silence_cache {
+ pa_memblock* blocks[PA_SAMPLE_MAX];
+} pa_silence_cache;
+
+void pa_silence_cache_init(pa_silence_cache *cache);
+void pa_silence_cache_done(pa_silence_cache *cache);
+
+void *pa_silence_memory(void *p, size_t length, const pa_sample_spec *spec);
+pa_memchunk* pa_silence_memchunk(pa_memchunk *c, const pa_sample_spec *spec);
+pa_memblock* pa_silence_memblock(pa_memblock *b, const pa_sample_spec *spec);
+
+pa_memchunk* pa_silence_memchunk_get(pa_silence_cache *cache, pa_mempool *pool, pa_memchunk* ret, const pa_sample_spec *spec, size_t length);
typedef struct pa_mix_info {
pa_memchunk chunk;
pa_cvolume volume;
void *userdata;
- void *internal; /* Used internally by pa_mix(), should not be initialised when calling pa_mix() */
+
+ /* The following fields are used internally by pa_mix(), should
+ * not be initialised by the caller of pa_mix(). */
+ void *ptr;
+ union {
+ int32_t i;
+ float f;
+ } linear[PA_CHANNELS_MAX];
} pa_mix_info;
size_t pa_mix(
@@ -46,11 +62,20 @@ size_t pa_mix(
size_t length,
const pa_sample_spec *spec,
const pa_cvolume *volume,
- int mute);
+ pa_bool_t mute);
void pa_volume_memchunk(
pa_memchunk*c,
const pa_sample_spec *spec,
const pa_cvolume *volume);
+size_t pa_frame_align(size_t l, const pa_sample_spec *ss) PA_GCC_PURE;
+
+int pa_frame_aligned(size_t l, const pa_sample_spec *ss) PA_GCC_PURE;
+
+void pa_interleave(const void *src[], unsigned channels, void *dst, size_t ss, unsigned n);
+void pa_deinterleave(const void *src, void *dst[], unsigned channels, size_t ss, unsigned n);
+
+void pa_sample_clamp(pa_sample_format_t format, void *dst, size_t dstr, const void *src, size_t sstr, unsigned n);
+
#endif
diff --git a/src/pulsecore/sconv-s16be.c b/src/pulsecore/sconv-s16be.c
index 5ac96320..e7e1449a 100644
--- a/src/pulsecore/sconv-s16be.c
+++ b/src/pulsecore/sconv-s16be.c
@@ -1,18 +1,18 @@
-/* $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
@@ -25,12 +25,30 @@
#include "endianmacros.h"
-#define INT16_FROM INT16_FROM_BE
-#define INT16_TO INT16_TO_BE
+#define INT16_FROM PA_INT16_FROM_BE
+#define INT16_TO PA_INT16_TO_BE
+
+#define INT32_FROM PA_INT32_FROM_BE
+#define INT32_TO PA_INT32_TO_BE
#define pa_sconv_s16le_to_float32ne pa_sconv_s16be_to_float32ne
#define pa_sconv_s16le_from_float32ne pa_sconv_s16be_from_float32ne
+#define pa_sconv_s16le_to_float32re pa_sconv_s16be_to_float32re
+#define pa_sconv_s16le_from_float32re pa_sconv_s16be_from_float32re
+
+#define pa_sconv_s32le_to_float32ne pa_sconv_s32be_to_float32ne
+#define pa_sconv_s32le_from_float32ne pa_sconv_s32be_from_float32ne
+
+#define pa_sconv_s32le_to_float32re pa_sconv_s32be_to_float32re
+#define pa_sconv_s32le_from_float32re pa_sconv_s32be_from_float32re
+
+#define pa_sconv_s32le_to_s16ne pa_sconv_s32be_to_s16ne
+#define pa_sconv_s32le_from_s16ne pa_sconv_s32be_from_s16ne
+
+#define pa_sconv_s32le_to_s16re pa_sconv_s32be_to_s16re
+#define pa_sconv_s32le_from_s16re pa_sconv_s32be_from_s16re
+
#ifdef WORDS_BIGENDIAN
#define SWAP_WORDS 0
#else
diff --git a/src/pulsecore/sconv-s16be.h b/src/pulsecore/sconv-s16be.h
index bd3fd345..60580815 100644
--- a/src/pulsecore/sconv-s16be.h
+++ b/src/pulsecore/sconv-s16be.h
@@ -1,28 +1,59 @@
#ifndef foosconv_s16befoo
#define foosconv_s16befoo
-/* $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_sconv_s16be_to_float32ne(unsigned n, const void *a, float *b);
-void pa_sconv_s16be_from_float32ne(unsigned n, const float *a, void *b);
+#include <inttypes.h>
+
+void pa_sconv_s16be_to_float32ne(unsigned n, const int16_t *a, float *b);
+void pa_sconv_s16be_from_float32ne(unsigned n, const float *a, int16_t *b);
+void pa_sconv_s16be_to_float32re(unsigned n, const int16_t *a, float *b);
+void pa_sconv_s16be_from_float32re(unsigned n, const float *a, int16_t *b);
+
+void pa_sconv_s32be_to_float32ne(unsigned n, const int32_t *a, float *b);
+void pa_sconv_s32be_from_float32ne(unsigned n, const float *a, int32_t *b);
+void pa_sconv_s32be_to_float32re(unsigned n, const int32_t *a, float *b);
+void pa_sconv_s32be_from_float32re(unsigned n, const float *a, int32_t *b);
+
+void pa_sconv_s32be_to_s16ne(unsigned n, const int32_t *a, int16_t *b);
+void pa_sconv_s32be_from_s16ne(unsigned n, const int16_t *a, int32_t *b);
+void pa_sconv_s32be_to_s16re(unsigned n, const int32_t *a, int16_t *b);
+void pa_sconv_s32be_from_s16re(unsigned n, const int16_t *a, int32_t *b);
+
+#ifdef WORDS_BIGENDIAN
+#define pa_sconv_float32be_to_s16ne pa_sconv_s16be_from_float32ne
+#define pa_sconv_float32be_from_s16ne pa_sconv_s16be_to_float32ne
+#define pa_sconv_float32le_to_s16ne pa_sconv_s16be_from_float32re
+#define pa_sconv_float32le_from_s16ne pa_sconv_s16be_to_float32re
+
+#define pa_sconv_float32be_to_s32ne pa_sconv_s32be_from_float32ne
+#define pa_sconv_float32be_from_s32ne pa_sconv_s32be_to_float32ne
+#define pa_sconv_float32le_to_s32ne pa_sconv_s32be_from_float32re
+#define pa_sconv_float32le_from_s32ne pa_sconv_s32be_to_float32re
+
+#define pa_sconv_s16be_to_s32ne pa_sconv_s32be_from_s16ne
+#define pa_sconv_s16be_from_s32ne pa_sconv_s32be_to_s16ne
+#define pa_sconv_s16le_to_s32ne pa_sconv_s32be_from_s16re
+#define pa_sconv_s16le_from_s32ne pa_sconv_s32be_to_s16re
+#endif
#endif
diff --git a/src/pulsecore/sconv-s16le.c b/src/pulsecore/sconv-s16le.c
index d8b93cbd..41670f27 100644
--- a/src/pulsecore/sconv-s16le.c
+++ b/src/pulsecore/sconv-s16le.c
@@ -1,18 +1,18 @@
-/* $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
@@ -23,12 +23,15 @@
#include <config.h>
#endif
-#include <assert.h>
+/* Despite the name of this file we implement S32 handling here, too. */
+
#include <inttypes.h>
+#include <stdio.h>
#include <liboil/liboilfuncs.h>
#include <pulsecore/sconv.h>
+#include <pulsecore/macro.h>
#include <pulsecore/log.h>
#include "endianmacros.h"
@@ -36,11 +39,19 @@
#include "sconv-s16le.h"
#ifndef INT16_FROM
-#define INT16_FROM INT16_FROM_LE
+#define INT16_FROM PA_INT16_FROM_LE
#endif
#ifndef INT16_TO
-#define INT16_TO INT16_TO_LE
+#define INT16_TO PA_INT16_TO_LE
+#endif
+
+#ifndef INT32_FROM
+#define INT32_FROM PA_INT32_FROM_LE
+#endif
+
+#ifndef INT32_TO
+#define INT32_TO PA_INT32_TO_LE
#endif
#ifndef SWAP_WORDS
@@ -51,53 +62,188 @@
#endif
#endif
-void pa_sconv_s16le_to_float32ne(unsigned n, const void *a, float *b) {
- const int16_t *ca = a;
-
- assert(a);
- assert(b);
+void pa_sconv_s16le_to_float32ne(unsigned n, const int16_t *a, float *b) {
+ pa_assert(a);
+ pa_assert(b);
#if SWAP_WORDS == 1
-
+
for (; n > 0; n--) {
- int16_t s = *(ca++);
+ int16_t s = *(a++);
*(b++) = ((float) INT16_FROM(s))/0x7FFF;
}
#else
{
static const double add = 0, factor = 1.0/0x7FFF;
- oil_scaleconv_f32_s16(b, ca, n, &add, &factor);
+ oil_scaleconv_f32_s16(b, a, n, &add, &factor);
+}
+#endif
+}
+
+void pa_sconv_s32le_to_float32ne(unsigned n, const int32_t *a, float *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+#if SWAP_WORDS == 1
+
+ for (; n > 0; n--) {
+ int32_t s = *(a++);
+ *(b++) = (float) (((double) INT32_FROM(s))/0x7FFFFFFF);
+ }
+
+#else
+{
+ static const double add = 0, factor = 1.0/0x7FFFFFFF;
+ oil_scaleconv_f32_s32(b, a, n, &add, &factor);
}
#endif
}
-void pa_sconv_s16le_from_float32ne(unsigned n, const float *a, void *b) {
- int16_t *cb = b;
-
- assert(a);
- assert(b);
+void pa_sconv_s16le_from_float32ne(unsigned n, const float *a, int16_t *b) {
+ pa_assert(a);
+ pa_assert(b);
#if SWAP_WORDS == 1
-
+
for (; n > 0; n--) {
int16_t s;
float v = *(a++);
- if (v > 1)
- v = 1;
-
- if (v < -1)
- v = -1;
-
+ v = PA_CLAMP_UNLIKELY(v, -1, 1);
s = (int16_t) (v * 0x7FFF);
- *(cb++) = INT16_TO(s);
+ *(b++) = INT16_TO(s);
}
#else
{
static const double add = 0, factor = 0x7FFF;
- oil_scaleconv_s16_f32(cb, a, n, &add, &factor);
+ oil_scaleconv_s16_f32(b, a, n, &add, &factor);
}
#endif
}
+
+void pa_sconv_s32le_from_float32ne(unsigned n, const float *a, int32_t *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+#if SWAP_WORDS == 1
+
+ for (; n > 0; n--) {
+ int32_t s;
+ float v = *(a++);
+
+ v = PA_CLAMP_UNLIKELY(v, -1, 1);
+ s = (int32_t) ((double) v * (double) 0x7FFFFFFF);
+ *(b++) = INT32_TO(s);
+ }
+
+#else
+{
+ static const double add = 0, factor = 0x7FFFFFFF;
+ oil_scaleconv_s32_f32(b, a, n, &add, &factor);
+}
+#endif
+}
+
+void pa_sconv_s16le_to_float32re(unsigned n, const int16_t *a, float *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--) {
+ int16_t s = *(a++);
+ float k = ((float) INT16_FROM(s))/0x7FFF;
+ uint32_t *j = (uint32_t*) &k;
+ *j = PA_UINT32_SWAP(*j);
+ *(b++) = k;
+ }
+}
+
+void pa_sconv_s32le_to_float32re(unsigned n, const int32_t *a, float *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--) {
+ int32_t s = *(a++);
+ float k = (float) (((double) INT32_FROM(s))/0x7FFFFFFF);
+ uint32_t *j = (uint32_t*) &k;
+ *j = PA_UINT32_SWAP(*j);
+ *(b++) = k;
+ }
+}
+
+void pa_sconv_s16le_from_float32re(unsigned n, const float *a, int16_t *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--) {
+ int16_t s;
+ float v = *(a++);
+ uint32_t *j = (uint32_t*) &v;
+ *j = PA_UINT32_SWAP(*j);
+ v = PA_CLAMP_UNLIKELY(v, -1, 1);
+ s = (int16_t) (v * 0x7FFF);
+ *(b++) = INT16_TO(s);
+ }
+}
+
+void pa_sconv_s32le_from_float32re(unsigned n, const float *a, int32_t *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--) {
+ int32_t s;
+ float v = *(a++);
+ uint32_t *j = (uint32_t*) &v;
+ *j = PA_UINT32_SWAP(*j);
+ v = PA_CLAMP_UNLIKELY(v, -1, 1);
+ s = (int32_t) ((double) v * 0x7FFFFFFF);
+ *(b++) = INT32_TO(s);
+ }
+}
+
+void pa_sconv_s32le_to_s16ne(unsigned n, const int32_t*a, int16_t *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--) {
+ *b = (int16_t) (INT32_FROM(*a) >> 16);
+ a++;
+ b++;
+ }
+}
+
+void pa_sconv_s32le_to_s16re(unsigned n, const int32_t*a, int16_t *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--) {
+ int16_t s = (int16_t) (INT32_FROM(*a) >> 16);
+ *b = PA_UINT32_SWAP(s);
+ a++;
+ b++;
+ }
+}
+
+void pa_sconv_s32le_from_s16ne(unsigned n, const int16_t *a, int32_t *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--) {
+ *b = INT32_TO(((int32_t) *a) << 16);
+ a++;
+ b++;
+ }
+}
+
+void pa_sconv_s32le_from_s16re(unsigned n, const int16_t *a, int32_t *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--) {
+ int32_t s = ((int32_t) PA_UINT16_SWAP(*a)) << 16;
+ *b = INT32_TO(s);
+ a++;
+ b++;
+ }
+}
diff --git a/src/pulsecore/sconv-s16le.h b/src/pulsecore/sconv-s16le.h
index ae6e22d2..8d4c87c3 100644
--- a/src/pulsecore/sconv-s16le.h
+++ b/src/pulsecore/sconv-s16le.h
@@ -1,28 +1,59 @@
#ifndef foosconv_s16lefoo
#define foosconv_s16lefoo
-/* $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_sconv_s16le_to_float32ne(unsigned n, const void *a, float *b);
-void pa_sconv_s16le_from_float32ne(unsigned n, const float *a, void *b);
+#include <inttypes.h>
+
+void pa_sconv_s16le_to_float32ne(unsigned n, const int16_t *a, float *b);
+void pa_sconv_s16le_from_float32ne(unsigned n, const float *a, int16_t *b);
+void pa_sconv_s16le_to_float32re(unsigned n, const int16_t *a, float *b);
+void pa_sconv_s16le_from_float32re(unsigned n, const float *a, int16_t *b);
+
+void pa_sconv_s32le_to_float32ne(unsigned n, const int32_t *a, float *b);
+void pa_sconv_s32le_from_float32ne(unsigned n, const float *a, int32_t *b);
+void pa_sconv_s32le_to_float32re(unsigned n, const int32_t *a, float *b);
+void pa_sconv_s32le_from_float32re(unsigned n, const float *a, int32_t *b);
+
+void pa_sconv_s32le_to_s16ne(unsigned n, const int32_t *a, int16_t *b);
+void pa_sconv_s32le_from_s16ne(unsigned n, const int16_t *a, int32_t *b);
+void pa_sconv_s32le_to_s16re(unsigned n, const int32_t *a, int16_t *b);
+void pa_sconv_s32le_from_s16re(unsigned n, const int16_t *a, int32_t *b);
+
+#ifndef WORDS_BIGENDIAN
+#define pa_sconv_float32be_to_s16ne pa_sconv_s16le_from_float32re
+#define pa_sconv_float32be_from_s16ne pa_sconv_s16le_to_float32re
+#define pa_sconv_float32le_to_s16ne pa_sconv_s16le_from_float32ne
+#define pa_sconv_float32le_from_s16ne pa_sconv_s16le_to_float32ne
+
+#define pa_sconv_float32be_to_s32ne pa_sconv_s32le_from_float32re
+#define pa_sconv_float32be_from_s32ne pa_sconv_s32le_to_float32re
+#define pa_sconv_float32le_to_s32ne pa_sconv_s32le_from_float32ne
+#define pa_sconv_float32le_from_s32ne pa_sconv_s32le_to_float32ne
+
+#define pa_sconv_s16be_to_s32ne pa_sconv_s32le_from_s16re
+#define pa_sconv_s16be_from_s32ne pa_sconv_s32le_to_s16re
+#define pa_sconv_s16le_to_s32ne pa_sconv_s32le_from_s16ne
+#define pa_sconv_s16le_from_s32ne pa_sconv_s32le_to_s16ne
+#endif
#endif
diff --git a/src/pulsecore/sconv.c b/src/pulsecore/sconv.c
index ff2a0110..581e4681 100644
--- a/src/pulsecore/sconv.c
+++ b/src/pulsecore/sconv.c
@@ -1,18 +1,19 @@
-/* $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
@@ -25,12 +26,12 @@
#include <stdio.h>
#include <stdlib.h>
-#include <assert.h>
#include <liboil/liboilfuncs.h>
#include <liboil/liboil.h>
#include <pulsecore/g711.h>
+#include <pulsecore/macro.h>
#include "endianmacros.h"
#include "sconv-s16le.h"
@@ -38,132 +39,231 @@
#include "sconv.h"
-static void u8_to_float32ne(unsigned n, const void *a, float *b) {
- const uint8_t *ca = a;
- static const double add = -128.0/127.0, factor = 1.0/127.0;
-
- assert(a);
- assert(b);
+/* u8 */
+static void u8_to_float32ne(unsigned n, const uint8_t *a, float *b) {
+ static const double add = -1, factor = 1.0/128.0;
+
+ pa_assert(a);
+ pa_assert(b);
+
+ oil_scaleconv_f32_u8(b, a, n, &add, &factor);
+}
+
+static void u8_from_float32ne(unsigned n, const float *a, uint8_t *b) {
+ static const double add = 128, factor = 127.0;
- oil_scaleconv_f32_u8(b, ca, n, &add, &factor);
-}
+ pa_assert(a);
+ pa_assert(b);
+
+ oil_scaleconv_u8_f32(b, a, n, &add, &factor);
+}
-static void u8_from_float32ne(unsigned n, const float *a, void *b) {
- uint8_t *cb = b;
- static const double add = 128.0, factor = 127.0;
+static void u8_to_s16ne(unsigned n, const uint8_t *a, int16_t *b) {
+ static const int16_t add = -0x80, factor = 0x100;
- assert(a);
- assert(b);
+ pa_assert(a);
+ pa_assert(b);
- oil_scaleconv_u8_f32(cb, a, n, &add, &factor);
+ oil_conv_s16_u8(b, 2, a, 1, n);
+ oil_scalaradd_s16(b, 2, b, 2, &add, n);
+ oil_scalarmult_s16(b, 2, b, 2, &factor, n);
}
-static void float32ne_to_float32ne(unsigned n, const void *a, float *b) {
- assert(a);
- assert(b);
+static void u8_from_s16ne(unsigned n, const int16_t *a, uint8_t *b) {
- oil_memcpy(b, a, sizeof(float) * n);
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--, a++, b++)
+ *b = (uint8_t) (*a / 0x100 + 0x80);
}
-static void float32ne_from_float32ne(unsigned n, const float *a, void *b) {
- assert(a);
- assert(b);
+/* float32 */
+
+static void float32ne_to_float32ne(unsigned n, const float *a, float *b) {
+ pa_assert(a);
+ pa_assert(b);
oil_memcpy(b, a, sizeof(float) * n);
}
-static void ulaw_to_float32ne(unsigned n, const void *a, float *b) {
- const uint8_t *ca = a;
+static void float32re_to_float32ne(unsigned n, const float *a, float *b) {
+ pa_assert(a);
+ pa_assert(b);
- assert(a);
- assert(b);
-
- for (; n > 0; n--)
- *(b++) = st_ulaw2linear16(*(ca++)) * 1.0F / 0x7FFF;
+ for (; n > 0; n--, a++, b++)
+ *((uint32_t *) b) = PA_UINT32_SWAP(*((uint32_t *) a));
}
-static void ulaw_from_float32ne(unsigned n, const float *a, void *b) {
- uint8_t *cb = b;
+/* s16 */
- assert(a);
- assert(b);
-
- for (; n > 0; n--) {
- float v = *(a++);
+static void s16ne_to_s16ne(unsigned n, const int16_t *a, int16_t *b) {
+ pa_assert(a);
+ pa_assert(b);
- if (v > 1)
- v = 1;
+ oil_memcpy(b, a, sizeof(int16_t) * n);
+}
- if (v < -1)
- v = -1;
+static void s16re_to_s16ne(unsigned n, const int16_t *a, int16_t *b) {
+ pa_assert(a);
+ pa_assert(b);
- *(cb++) = st_14linear2ulaw((int16_t) (v * 0x1FFF));
- }
+ for (; n > 0; n--, a++, b++)
+ *b = PA_UINT16_SWAP(*a);
}
-static void alaw_to_float32ne(unsigned n, const void *a, float *b) {
- const uint8_t *ca = a;
+/* ulaw */
- assert(a);
- assert(b);
+static void ulaw_to_float32ne(unsigned n, const uint8_t *a, float *b) {
+ pa_assert(a);
+ pa_assert(b);
for (; n > 0; n--)
- *(b++) = st_alaw2linear16(*(ca++)) * 1.0F / 0x7FFF;
+ *(b++) = (float) st_ulaw2linear16(*(a++)) / 0x8000;
}
-static void alaw_from_float32ne(unsigned n, const float *a, void *b) {
- uint8_t *cb = b;
+static void ulaw_from_float32ne(unsigned n, const float *a, uint8_t *b) {
+ pa_assert(a);
+ pa_assert(b);
- assert(a);
- assert(b);
-
for (; n > 0; n--) {
float v = *(a++);
+ v = PA_CLAMP_UNLIKELY(v, -1, 1);
+ v *= 0x1FFF;
+ *(b++) = st_14linear2ulaw((int16_t) v);
+ }
+}
- if (v > 1)
- v = 1;
+static void ulaw_to_s16ne(unsigned n, const uint8_t *a, int16_t *b) {
+ pa_assert(a);
+ pa_assert(b);
- if (v < -1)
- v = -1;
+ for (; n > 0; n--, a++, b++)
+ *b = st_ulaw2linear16(*a);
+}
- *(cb++) = st_13linear2alaw((int16_t) (v * 0xFFF));
- }
+static void ulaw_from_s16ne(unsigned n, const int16_t *a, uint8_t *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--, a++, b++)
+ *b = st_14linear2ulaw(*a >> 2);
}
-pa_convert_to_float32ne_func_t pa_get_convert_to_float32ne_function(pa_sample_format_t f) {
- switch(f) {
- case PA_SAMPLE_U8:
- return u8_to_float32ne;
- case PA_SAMPLE_S16LE:
- return pa_sconv_s16le_to_float32ne;
- case PA_SAMPLE_S16BE:
- return pa_sconv_s16be_to_float32ne;
- case PA_SAMPLE_FLOAT32NE:
- return float32ne_to_float32ne;
- case PA_SAMPLE_ALAW:
- return alaw_to_float32ne;
- case PA_SAMPLE_ULAW:
- return ulaw_to_float32ne;
- default:
- return NULL;
- }
+/* alaw */
+
+static void alaw_to_float32ne(unsigned n, const uint8_t *a, float *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--, a++, b++)
+ *b = (float) st_alaw2linear16(*a) / 0x8000;
}
-pa_convert_from_float32ne_func_t pa_get_convert_from_float32ne_function(pa_sample_format_t f) {
- switch(f) {
- case PA_SAMPLE_U8:
- return u8_from_float32ne;
- case PA_SAMPLE_S16LE:
- return pa_sconv_s16le_from_float32ne;
- case PA_SAMPLE_S16BE:
- return pa_sconv_s16be_from_float32ne;
- case PA_SAMPLE_FLOAT32NE:
- return float32ne_from_float32ne;
- case PA_SAMPLE_ALAW:
- return alaw_from_float32ne;
- case PA_SAMPLE_ULAW:
- return ulaw_from_float32ne;
- default:
- return NULL;
+static void alaw_from_float32ne(unsigned n, const float *a, uint8_t *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--, a++, b++) {
+ float v = *a;
+ v = PA_CLAMP_UNLIKELY(v, -1, 1);
+ v *= 0xFFF;
+ *b = st_13linear2alaw((int16_t) v);
}
}
+
+static void alaw_to_s16ne(unsigned n, const int8_t *a, int16_t *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--, a++, b++)
+ *b = st_alaw2linear16(*a);
+}
+
+static void alaw_from_s16ne(unsigned n, const int16_t *a, uint8_t *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--, a++, b++)
+ *b = st_13linear2alaw(*a >> 3);
+}
+
+pa_convert_func_t pa_get_convert_to_float32ne_function(pa_sample_format_t f) {
+
+ static const pa_convert_func_t table[] = {
+ [PA_SAMPLE_U8] = (pa_convert_func_t) u8_to_float32ne,
+ [PA_SAMPLE_ALAW] = (pa_convert_func_t) alaw_to_float32ne,
+ [PA_SAMPLE_ULAW] = (pa_convert_func_t) ulaw_to_float32ne,
+ [PA_SAMPLE_S16LE] = (pa_convert_func_t) pa_sconv_s16le_to_float32ne,
+ [PA_SAMPLE_S16BE] = (pa_convert_func_t) pa_sconv_s16be_to_float32ne,
+ [PA_SAMPLE_S32LE] = (pa_convert_func_t) pa_sconv_s32le_to_float32ne,
+ [PA_SAMPLE_S32BE] = (pa_convert_func_t) pa_sconv_s32be_to_float32ne,
+ [PA_SAMPLE_FLOAT32NE] = (pa_convert_func_t) float32ne_to_float32ne,
+ [PA_SAMPLE_FLOAT32RE] = (pa_convert_func_t) float32re_to_float32ne,
+ };
+
+ pa_assert(f >= 0);
+ pa_assert(f < PA_SAMPLE_MAX);
+
+ return table[f];
+}
+
+pa_convert_func_t pa_get_convert_from_float32ne_function(pa_sample_format_t f) {
+
+ static const pa_convert_func_t table[] = {
+ [PA_SAMPLE_U8] = (pa_convert_func_t) u8_from_float32ne,
+ [PA_SAMPLE_S16LE] = (pa_convert_func_t) pa_sconv_s16le_from_float32ne,
+ [PA_SAMPLE_S16BE] = (pa_convert_func_t) pa_sconv_s16be_from_float32ne,
+ [PA_SAMPLE_S32LE] = (pa_convert_func_t) pa_sconv_s32le_from_float32ne,
+ [PA_SAMPLE_S32BE] = (pa_convert_func_t) pa_sconv_s32be_from_float32ne,
+ [PA_SAMPLE_FLOAT32NE] = (pa_convert_func_t) float32ne_to_float32ne,
+ [PA_SAMPLE_FLOAT32RE] = (pa_convert_func_t) float32re_to_float32ne,
+ [PA_SAMPLE_ALAW] = (pa_convert_func_t) alaw_from_float32ne,
+ [PA_SAMPLE_ULAW] = (pa_convert_func_t) ulaw_from_float32ne
+ };
+
+ pa_assert(f >= 0);
+ pa_assert(f < PA_SAMPLE_MAX);
+
+ return table[f];
+}
+
+pa_convert_func_t pa_get_convert_to_s16ne_function(pa_sample_format_t f) {
+
+ static const pa_convert_func_t table[] = {
+ [PA_SAMPLE_U8] = (pa_convert_func_t) u8_to_s16ne,
+ [PA_SAMPLE_S16NE] = (pa_convert_func_t) s16ne_to_s16ne,
+ [PA_SAMPLE_S16RE] = (pa_convert_func_t) s16re_to_s16ne,
+ [PA_SAMPLE_FLOAT32BE] = (pa_convert_func_t) pa_sconv_float32be_to_s16ne,
+ [PA_SAMPLE_FLOAT32LE] = (pa_convert_func_t) pa_sconv_float32le_to_s16ne,
+ [PA_SAMPLE_S32BE] = (pa_convert_func_t) pa_sconv_s32be_to_s16ne,
+ [PA_SAMPLE_S32LE] = (pa_convert_func_t) pa_sconv_s32le_to_s16ne,
+ [PA_SAMPLE_ALAW] = (pa_convert_func_t) alaw_to_s16ne,
+ [PA_SAMPLE_ULAW] = (pa_convert_func_t) ulaw_to_s16ne
+ };
+
+ pa_assert(f >= 0);
+ pa_assert(f < PA_SAMPLE_MAX);
+
+ return table[f];
+}
+
+pa_convert_func_t pa_get_convert_from_s16ne_function(pa_sample_format_t f) {
+
+ static const pa_convert_func_t table[] = {
+ [PA_SAMPLE_U8] = (pa_convert_func_t) u8_from_s16ne,
+ [PA_SAMPLE_S16NE] = (pa_convert_func_t) s16ne_to_s16ne,
+ [PA_SAMPLE_S16RE] = (pa_convert_func_t) s16re_to_s16ne,
+ [PA_SAMPLE_FLOAT32BE] = (pa_convert_func_t) pa_sconv_float32be_from_s16ne,
+ [PA_SAMPLE_FLOAT32LE] = (pa_convert_func_t) pa_sconv_float32le_from_s16ne,
+ [PA_SAMPLE_S32BE] = (pa_convert_func_t) pa_sconv_s32be_from_s16ne,
+ [PA_SAMPLE_S32LE] = (pa_convert_func_t) pa_sconv_s32le_from_s16ne,
+ [PA_SAMPLE_ALAW] = (pa_convert_func_t) alaw_from_s16ne,
+ [PA_SAMPLE_ULAW] = (pa_convert_func_t) ulaw_from_s16ne,
+ };
+
+ pa_assert(f >= 0);
+ pa_assert(f < PA_SAMPLE_MAX);
+
+ return table[f];
+}
diff --git a/src/pulsecore/sconv.h b/src/pulsecore/sconv.h
index 4aba0694..59710369 100644
--- a/src/pulsecore/sconv.h
+++ b/src/pulsecore/sconv.h
@@ -1,21 +1,22 @@
#ifndef foosconvhfoo
#define foosconvhfoo
-/* $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
@@ -24,10 +25,12 @@
#include <pulse/sample.h>
-typedef void (*pa_convert_to_float32ne_func_t)(unsigned n, const void *a, float *b);
-typedef void (*pa_convert_from_float32ne_func_t)(unsigned n, const float *a, void *b);
+typedef void (*pa_convert_func_t)(unsigned n, const void *a, void *b);
+
+pa_convert_func_t pa_get_convert_to_float32ne_function(pa_sample_format_t f) PA_GCC_PURE;
+pa_convert_func_t pa_get_convert_from_float32ne_function(pa_sample_format_t f) PA_GCC_PURE;
-pa_convert_to_float32ne_func_t pa_get_convert_to_float32ne_function(pa_sample_format_t f);
-pa_convert_from_float32ne_func_t pa_get_convert_from_float32ne_function(pa_sample_format_t f);
+pa_convert_func_t pa_get_convert_to_s16ne_function(pa_sample_format_t f) PA_GCC_PURE;
+pa_convert_func_t pa_get_convert_from_s16ne_function(pa_sample_format_t f) PA_GCC_PURE;
#endif
diff --git a/src/pulsecore/semaphore-posix.c b/src/pulsecore/semaphore-posix.c
new file mode 100644
index 00000000..7c9f859d
--- /dev/null
+++ b/src/pulsecore/semaphore-posix.c
@@ -0,0 +1,67 @@
+/***
+ 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 <errno.h>
+#include <pthread.h>
+#include <semaphore.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+
+#include "semaphore.h"
+
+struct pa_semaphore {
+ sem_t sem;
+};
+
+pa_semaphore* pa_semaphore_new(unsigned value) {
+ pa_semaphore *s;
+
+ s = pa_xnew(pa_semaphore, 1);
+ pa_assert_se(sem_init(&s->sem, 0, value) == 0);
+ return s;
+}
+
+void pa_semaphore_free(pa_semaphore *s) {
+ pa_assert(s);
+ pa_assert_se(sem_destroy(&s->sem) == 0);
+ pa_xfree(s);
+}
+
+void pa_semaphore_post(pa_semaphore *s) {
+ pa_assert(s);
+ pa_assert_se(sem_post(&s->sem) == 0);
+}
+
+void pa_semaphore_wait(pa_semaphore *s) {
+ int ret;
+ pa_assert(s);
+
+ do {
+ ret = sem_wait(&s->sem);
+ } while (ret < 0 && errno == EINTR);
+
+ pa_assert(ret == 0);
+}
diff --git a/src/pulsecore/semaphore-win32.c b/src/pulsecore/semaphore-win32.c
new file mode 100644
index 00000000..41e0b8d4
--- /dev/null
+++ b/src/pulsecore/semaphore-win32.c
@@ -0,0 +1,63 @@
+/***
+ This file is part of PulseAudio.
+
+ 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 <windows.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+
+#include "semaphore.h"
+
+struct pa_semaphore
+{
+ HANDLE sema;
+};
+
+pa_semaphore* pa_semaphore_new(unsigned value) {
+ pa_semaphore *s;
+
+ s = pa_xnew(pa_semaphore, 1);
+
+ s->sema = CreateSemaphore(NULL, value, 32767, NULL);
+ pa_assert(s->sema != NULL);
+
+ return s;
+}
+
+void pa_semaphore_free(pa_semaphore *s) {
+ pa_assert(s);
+ CloseHandle(s->sema);
+ pa_xfree(s);
+}
+
+void pa_semaphore_post(pa_semaphore *s) {
+ pa_assert(s);
+ ReleaseSemaphore(s->sema, 1, NULL);
+}
+
+void pa_semaphore_wait(pa_semaphore *s) {
+ pa_assert(s);
+ WaitForSingleObject(s->sema, INFINITE);
+}
diff --git a/src/pulsecore/anotify.h b/src/pulsecore/semaphore.h
index 44e942f7..850ae817 100644
--- a/src/pulsecore/anotify.h
+++ b/src/pulsecore/semaphore.h
@@ -1,38 +1,33 @@
-#ifndef foopulseanotifyhfoo
-#define foopulseanotifyhfoo
-
-/* $Id$ */
+#ifndef foopulsesemaphorehfoo
+#define foopulsesemaphorehfoo
/***
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.
***/
-/* Asynchronous thread-safe notification of mainloops */
-
-
-#include <inttypes.h>
-#include <pulse/mainloop-api.h>
+typedef struct pa_semaphore pa_semaphore;
-typedef struct pa_anotify pa_anotify;
-typedef void (*pa_anotify_cb_t)(uint8_t event, void *userdata);
+pa_semaphore* pa_semaphore_new(unsigned value);
+void pa_semaphore_free(pa_semaphore *m);
-pa_anotify *pa_anotify_new(pa_mainloop_api*api, pa_anotify_cb_t cb, void *userdata);
-void pa_anotify_free(pa_anotify *a);
-int pa_anotify_signal(pa_anotify *a, uint8_t event);
+void pa_semaphore_post(pa_semaphore *m);
+void pa_semaphore_wait(pa_semaphore *m);
#endif
diff --git a/src/pulsecore/shm.c b/src/pulsecore/shm.c
index 19731b5f..298bf716 100644
--- a/src/pulsecore/shm.c
+++ b/src/pulsecore/shm.c
@@ -1,29 +1,29 @@
-/* $Id$ */
-
/***
- This file is part of PulseAudio.
-
- PulseAudio is free software; you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as
- published by the Free Software Foundation; either version 2.1 of the
- License, or (at your option) any later version.
-
- PulseAudio is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with PulseAudio; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- USA.
+ This file is part of PulseAudio.
+
+ Copyright 2006 Lennart Poettering
+ Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
-#include <assert.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
@@ -31,37 +31,74 @@
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <signal.h>
#ifdef HAVE_SYS_MMAN_H
#include <sys/mman.h>
#endif
+#include <pulse/xmalloc.h>
+#include <pulse/gccmacro.h>
+
#include <pulsecore/core-error.h>
#include <pulsecore/log.h>
#include <pulsecore/random.h>
-#include <pulse/xmalloc.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/atomic.h>
#include "shm.h"
#if defined(__linux__) && !defined(MADV_REMOVE)
#define MADV_REMOVE 9
-#endif
+#endif
-#define MAX_SHM_SIZE (1024*1024*20)
+#define MAX_SHM_SIZE (PA_ALIGN(1024*1024*64))
+
+#ifdef __linux__
+/* On Linux we know that the shared memory blocks are files in
+ * /dev/shm. We can use that information to list all blocks and
+ * cleanup unused ones */
+#define SHM_PATH "/dev/shm/"
+#else
+#undef SHM_PATH
+#endif
+
+#define SHM_MARKER ((int) 0xbeefcafe)
+
+/* We now put this SHM marker at the end of each segment. It's
+ * optional, to not require a reboot when upgrading, though */
+struct shm_marker PA_GCC_PACKED {
+ pa_atomic_t marker; /* 0xbeefcafe */
+ pa_atomic_t pid;
+ uint64_t *_reserverd1;
+ uint64_t *_reserverd2;
+ uint64_t *_reserverd3;
+ uint64_t *_reserverd4;
+};
static char *segment_name(char *fn, size_t l, unsigned id) {
- snprintf(fn, l, "/pulse-shm-%u", id);
+ pa_snprintf(fn, l, "/pulse-shm-%u", id);
return fn;
}
-int pa_shm_create_rw(pa_shm *m, size_t size, int shared, mode_t mode) {
+int pa_shm_create_rw(pa_shm *m, size_t size, pa_bool_t shared, mode_t mode) {
char fn[32];
int fd = -1;
-
- assert(m);
- assert(size > 0);
- assert(size < MAX_SHM_SIZE);
- assert(mode >= 0600);
+
+ pa_assert(m);
+ pa_assert(size > 0);
+ pa_assert(size <= MAX_SHM_SIZE);
+ pa_assert(mode >= 0600);
+
+ /* Each time we create a new SHM area, let's first drop all stale
+ * ones */
+ pa_shm_cleanup();
+
+ /* Round up to make it aligned */
+ size = PA_ALIGN(size);
if (!shared) {
m->id = 0;
@@ -75,8 +112,8 @@ int pa_shm_create_rw(pa_shm *m, size_t size, int shared, mode_t mode) {
#elif defined(HAVE_POSIX_MEMALIGN)
{
int r;
-
- if ((r = posix_memalign(&m->ptr, sysconf(_SC_PAGESIZE), size)) < 0) {
+
+ if ((r = posix_memalign(&m->ptr, PA_PAGE_SIZE, size)) < 0) {
pa_log("posix_memalign() failed: %s", pa_cstrerror(r));
goto fail;
}
@@ -84,11 +121,13 @@ int pa_shm_create_rw(pa_shm *m, size_t size, int shared, mode_t mode) {
#else
m->ptr = pa_xmalloc(m->size);
#endif
-
- m->do_unlink = 0;
-
+
+ m->do_unlink = FALSE;
+
} else {
#ifdef HAVE_SHM_OPEN
+ struct shm_marker *marker;
+
pa_random(&m->id, sizeof(m->id));
segment_name(fn, sizeof(fn), m->id);
@@ -96,34 +135,42 @@ int pa_shm_create_rw(pa_shm *m, size_t size, int shared, mode_t mode) {
pa_log("shm_open() failed: %s", pa_cstrerror(errno));
goto fail;
}
-
- if (ftruncate(fd, m->size = size) < 0) {
+
+ m->size = size + PA_ALIGN(sizeof(struct shm_marker));
+
+ if (ftruncate(fd, m->size) < 0) {
pa_log("ftruncate() failed: %s", pa_cstrerror(errno));
goto fail;
}
-
+
if ((m->ptr = mmap(NULL, m->size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
pa_log("mmap() failed: %s", pa_cstrerror(errno));
goto fail;
}
- close(fd);
- m->do_unlink = 1;
+ /* We store our PID at the end of the shm block, so that we
+ * can check for dead shm segments later */
+ marker = (struct shm_marker*) ((uint8_t*) m->ptr + m->size - PA_ALIGN(sizeof(struct shm_marker)));
+ pa_atomic_store(&marker->pid, (int) getpid());
+ pa_atomic_store(&marker->marker, SHM_MARKER);
+
+ pa_assert_se(close(fd) == 0);
+ m->do_unlink = TRUE;
#else
- return -1;
+ return -1;
#endif
}
m->shared = shared;
-
+
return 0;
-
+
fail:
#ifdef HAVE_SHM_OPEN
if (fd >= 0) {
shm_unlink(fn);
- close(fd);
+ pa_close(fd);
}
#endif
@@ -131,77 +178,71 @@ fail:
}
void pa_shm_free(pa_shm *m) {
- assert(m);
- assert(m->ptr);
- assert(m->size > 0);
+ pa_assert(m);
+ pa_assert(m->ptr);
+ pa_assert(m->size > 0);
#ifdef MAP_FAILED
- assert(m->ptr != MAP_FAILED);
+ pa_assert(m->ptr != MAP_FAILED);
#endif
- if (!m->shared) {
+ if (!m->shared) {
#ifdef MAP_ANONYMOUS
- if (munmap(m->ptr, m->size) < 0)
- pa_log("munmap() failed: %s", pa_cstrerror(errno));
+ if (munmap(m->ptr, m->size) < 0)
+ pa_log("munmap() failed: %s", pa_cstrerror(errno));
#elif defined(HAVE_POSIX_MEMALIGN)
free(m->ptr);
#else
pa_xfree(m->ptr);
#endif
- } else {
+ } else {
#ifdef HAVE_SHM_OPEN
- if (munmap(m->ptr, m->size) < 0)
- pa_log("munmap() failed: %s", pa_cstrerror(errno));
+ if (munmap(m->ptr, m->size) < 0)
+ pa_log("munmap() failed: %s", pa_cstrerror(errno));
+
+ if (m->do_unlink) {
+ char fn[32];
- if (m->do_unlink) {
- char fn[32];
+ segment_name(fn, sizeof(fn), m->id);
- segment_name(fn, sizeof(fn), m->id);
-
- if (shm_unlink(fn) < 0)
- pa_log(" shm_unlink(%s) failed: %s", fn, pa_cstrerror(errno));
- }
+ if (shm_unlink(fn) < 0)
+ pa_log(" shm_unlink(%s) failed: %s", fn, pa_cstrerror(errno));
+ }
#else
- /* We shouldn't be here without shm support */
- assert(0);
+ /* We shouldn't be here without shm support */
+ pa_assert_not_reached();
#endif
- }
+ }
memset(m, 0, sizeof(*m));
}
void pa_shm_punch(pa_shm *m, size_t offset, size_t size) {
void *ptr;
-
- assert(m);
- assert(m->ptr);
- assert(m->size > 0);
- assert(offset+size <= m->size);
+ size_t o, ps;
+
+ pa_assert(m);
+ pa_assert(m->ptr);
+ pa_assert(m->size > 0);
+ pa_assert(offset+size <= m->size);
#ifdef MAP_FAILED
- assert(m->ptr != MAP_FAILED);
+ pa_assert(m->ptr != MAP_FAILED);
#endif
/* You're welcome to implement this as NOOP on systems that don't
* support it */
+ /* Align this to multiples of the page size */
ptr = (uint8_t*) m->ptr + offset;
-
-#ifdef __linux__
-{
- /* On Linux ptr must be page aligned */
- long psz = sysconf(_SC_PAGESIZE);
- unsigned o;
+ o = (uint8_t*) ptr - (uint8_t*) PA_PAGE_ALIGN_PTR(ptr);
- o = ((unsigned long) ptr) - ((((unsigned long) ptr)/psz) * psz);
-
if (o > 0) {
- ptr = (uint8_t*) ptr + (psz - o);
- size -= psz - o;
+ ps = PA_PAGE_SIZE;
+ ptr = (uint8_t*) ptr + (ps - o);
+ size -= ps - o;
}
-}
-#endif
-
+
#ifdef MADV_REMOVE
if (madvise(ptr, size, MADV_REMOVE) >= 0)
return;
@@ -210,10 +251,12 @@ void pa_shm_punch(pa_shm *m, size_t offset, size_t size) {
#ifdef MADV_FREE
if (madvise(ptr, size, MADV_FREE) >= 0)
return;
-#endif
-
+#endif
+
#ifdef MADV_DONTNEED
- madvise(ptr, size, MADV_DONTNEED);
+ pa_assert_se(madvise(ptr, size, MADV_DONTNEED) == 0);
+#elif defined(POSIX_MADV_DONTNEED)
+ pa_assert_se(posix_madvise(ptr, size, POSIX_MADV_DONTNEED) == 0);
#endif
}
@@ -224,12 +267,13 @@ int pa_shm_attach_ro(pa_shm *m, unsigned id) {
int fd = -1;
struct stat st;
- assert(m);
+ pa_assert(m);
segment_name(fn, sizeof(fn), m->id = id);
if ((fd = shm_open(fn, O_RDONLY, 0)) < 0) {
- pa_log("shm_open() failed: %s", pa_cstrerror(errno));
+ if (errno != EACCES)
+ pa_log("shm_open() failed: %s", pa_cstrerror(errno));
goto fail;
}
@@ -238,13 +282,15 @@ int pa_shm_attach_ro(pa_shm *m, unsigned id) {
goto fail;
}
- if (st.st_size <= 0 || st.st_size > MAX_SHM_SIZE) {
+ if (st.st_size <= 0 ||
+ st.st_size > (off_t) (MAX_SHM_SIZE+PA_ALIGN(sizeof(struct shm_marker))) ||
+ PA_ALIGN((size_t) st.st_size) != (size_t) st.st_size) {
pa_log("Invalid shared memory segment size");
goto fail;
}
m->size = st.st_size;
-
+
if ((m->ptr = mmap(NULL, m->size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
pa_log("mmap() failed: %s", pa_cstrerror(errno));
goto fail;
@@ -252,14 +298,14 @@ int pa_shm_attach_ro(pa_shm *m, unsigned id) {
m->do_unlink = 0;
m->shared = 1;
-
- close(fd);
-
+
+ pa_assert_se(pa_close(fd) == 0);
+
return 0;
-
+
fail:
if (fd >= 0)
- close(fd);
+ pa_close(fd);
return -1;
}
@@ -267,7 +313,73 @@ fail:
#else /* HAVE_SHM_OPEN */
int pa_shm_attach_ro(pa_shm *m, unsigned id) {
- return -1;
+ return -1;
}
#endif /* HAVE_SHM_OPEN */
+
+int pa_shm_cleanup(void) {
+
+#ifdef HAVE_SHM_OPEN
+#ifdef SHM_PATH
+ DIR *d;
+ struct dirent *de;
+
+ if (!(d = opendir(SHM_PATH))) {
+ pa_log_warn("Failed to read "SHM_PATH": %s", pa_cstrerror(errno));
+ return -1;
+ }
+
+ while ((de = readdir(d))) {
+ pa_shm seg;
+ unsigned id;
+ pid_t pid;
+ char fn[128];
+ struct shm_marker *m;
+
+ if (strncmp(de->d_name, "pulse-shm-", 10))
+ continue;
+
+ if (pa_atou(de->d_name + 10, &id) < 0)
+ continue;
+
+ if (pa_shm_attach_ro(&seg, id) < 0)
+ continue;
+
+ if (seg.size < PA_ALIGN(sizeof(struct shm_marker))) {
+ pa_shm_free(&seg);
+ continue;
+ }
+
+ m = (struct shm_marker*) ((uint8_t*) seg.ptr + seg.size - PA_ALIGN(sizeof(struct shm_marker)));
+
+ if (pa_atomic_load(&m->marker) != SHM_MARKER) {
+ pa_shm_free(&seg);
+ continue;
+ }
+
+ if (!(pid = (pid_t) pa_atomic_load(&m->pid))) {
+ pa_shm_free(&seg);
+ continue;
+ }
+
+ if (kill(pid, 0) == 0 || errno != ESRCH) {
+ pa_shm_free(&seg);
+ continue;
+ }
+
+ pa_shm_free(&seg);
+
+ /* Ok, the owner of this shms segment is dead, so, let's remove the segment */
+ segment_name(fn, sizeof(fn), id);
+
+ if (shm_unlink(fn) < 0 && errno != EACCES && errno != ENOENT)
+ pa_log_warn("Failed to remove SHM segment %s: %s\n", fn, pa_cstrerror(errno));
+ }
+
+ closedir(d);
+#endif /* SHM_PATH */
+#endif /* HAVE_SHM_OPEN */
+
+ return 0;
+}
diff --git a/src/pulsecore/shm.h b/src/pulsecore/shm.h
index ea72403a..c2adbd07 100644
--- a/src/pulsecore/shm.h
+++ b/src/pulsecore/shm.h
@@ -1,21 +1,21 @@
#ifndef foopulseshmhfoo
#define foopulseshmhfoo
-/* $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.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
@@ -24,19 +24,23 @@
#include <sys/types.h>
+#include <pulsecore/macro.h>
+
typedef struct pa_shm {
unsigned id;
void *ptr;
size_t size;
- int do_unlink;
- int shared;
+ pa_bool_t do_unlink:1;
+ pa_bool_t shared:1;
} pa_shm;
-int pa_shm_create_rw(pa_shm *m, size_t size, int shared, mode_t mode);
+int pa_shm_create_rw(pa_shm *m, size_t size, pa_bool_t shared, mode_t mode);
int pa_shm_attach_ro(pa_shm *m, unsigned id);
void pa_shm_punch(pa_shm *m, size_t offset, size_t size);
void pa_shm_free(pa_shm *m);
+int pa_shm_cleanup(void);
+
#endif
diff --git a/src/pulsecore/shmasyncq.c b/src/pulsecore/shmasyncq.c
new file mode 100644
index 00000000..eac56adb
--- /dev/null
+++ b/src/pulsecore/shmasyncq.c
@@ -0,0 +1,220 @@
+/***
+ 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.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <errno.h>
+
+#include <pulsecore/atomic.h>
+#include <pulsecore/log.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulse/xmalloc.h>
+
+#include "fdsem.h"
+
+/* For debugging purposes we can define _Y to put and extra thread
+ * yield between each operation. */
+
+/* #define PROFILE */
+
+#ifdef PROFILE
+#define _Y pa_thread_yield()
+#else
+#define _Y do { } while(0)
+#endif
+
+
+struct pa_shmasyncq {
+ pa_fdsem *read_fdsem, *write_fdsem;
+ pa_shmasyncq_data *data;
+};
+
+static int is_power_of_two(unsigned size) {
+ return !(size & (size - 1));
+}
+
+static int reduce(pa_shmasyncq *l, int value) {
+ return value & (unsigned) (l->n_elements - 1);
+}
+
+static pa_atomic_t* get_cell(pa_shmasyncq *l, unsigned i) {
+ pa_assert(i < l->data->n_elements);
+
+ return (pa_atomic_t*) ((uint8*t) l->data + PA_ALIGN(sizeof(pa_shmasyncq_data)) + i * (PA_ALIGN(sizeof(pa_atomic_t)) + PA_ALIGN(element_size)))
+}
+
+static void *get_cell_data(pa_atomic_t *a) {
+ return (uint8_t*) a + PA_ALIGN(sizeof(atomic_t));
+}
+
+pa_shmasyncq *pa_shmasyncq_new(unsigned n_elements, size_t element_size, void *data, int fd[2]) {
+ pa_shmasyncq *l;
+
+ pa_assert(n_elements > 0);
+ pa_assert(is_power_of_two(n_elements));
+ pa_assert(element_size > 0);
+ pa_assert(data);
+ pa_assert(fd);
+
+ l = pa_xnew(pa_shmasyncq, 1);
+
+ l->data = data;
+ memset(data, 0, PA_SHMASYNCQ_SIZE(n_elements, element_size));
+
+ l->data->n_elements = n_elements;
+ l->data->element_size = element_size;
+
+ if (!(l->read_fdsem = pa_fdsem_new_shm(&d->read_fdsem_data, &fd[0]))) {
+ pa_xfree(l);
+ return NULL;
+ }
+
+ if (!(l->write_fdsem = pa_fdsem_new(&d->write_fdsem_data, &fd[1]))) {
+ pa_fdsem_free(l->read_fdsem);
+ pa_xfree(l);
+ return NULL;
+ }
+
+ return l;
+}
+
+void pa_shmasyncq_free(pa_shmasyncq *l, pa_free_cb_t free_cb) {
+ pa_assert(l);
+
+ if (free_cb) {
+ void *p;
+
+ while ((p = pa_shmasyncq_pop(l, 0)))
+ free_cb(p);
+ }
+
+ pa_fdsem_free(l->read_fdsem);
+ pa_fdsem_free(l->write_fdsem);
+ pa_xfree(l);
+}
+
+int pa_shmasyncq_push(pa_shmasyncq*l, void *p, int wait) {
+ int idx;
+ pa_atomic_ptr_t *cells;
+
+ pa_assert(l);
+ pa_assert(p);
+
+ cells = PA_SHMASYNCQ_CELLS(l);
+
+ _Y;
+ idx = reduce(l, l->write_idx);
+
+ if (!pa_atomic_ptr_cmpxchg(&cells[idx], NULL, p)) {
+
+ if (!wait)
+ return -1;
+
+/* pa_log("sleeping on push"); */
+
+ do {
+ pa_fdsem_wait(l->read_fdsem);
+ } while (!pa_atomic_ptr_cmpxchg(&cells[idx], NULL, p));
+ }
+
+ _Y;
+ l->write_idx++;
+
+ pa_fdsem_post(l->write_fdsem);
+
+ return 0;
+}
+
+void* pa_shmasyncq_pop(pa_shmasyncq*l, int wait) {
+ int idx;
+ void *ret;
+ pa_atomic_ptr_t *cells;
+
+ pa_assert(l);
+
+ cells = PA_SHMASYNCQ_CELLS(l);
+
+ _Y;
+ idx = reduce(l, l->read_idx);
+
+ if (!(ret = pa_atomic_ptr_load(&cells[idx]))) {
+
+ if (!wait)
+ return NULL;
+
+/* pa_log("sleeping on pop"); */
+
+ do {
+ pa_fdsem_wait(l->write_fdsem);
+ } while (!(ret = pa_atomic_ptr_load(&cells[idx])));
+ }
+
+ pa_assert(ret);
+
+ /* Guaranteed to succeed if we only have a single reader */
+ pa_assert_se(pa_atomic_ptr_cmpxchg(&cells[idx], ret, NULL));
+
+ _Y;
+ l->read_idx++;
+
+ pa_fdsem_post(l->read_fdsem);
+
+ return ret;
+}
+
+int pa_shmasyncq_get_fd(pa_shmasyncq *q) {
+ pa_assert(q);
+
+ return pa_fdsem_get(q->write_fdsem);
+}
+
+int pa_shmasyncq_before_poll(pa_shmasyncq *l) {
+ int idx;
+ pa_atomic_ptr_t *cells;
+
+ pa_assert(l);
+
+ cells = PA_SHMASYNCQ_CELLS(l);
+
+ _Y;
+ idx = reduce(l, l->read_idx);
+
+ for (;;) {
+ if (pa_atomic_ptr_load(&cells[idx]))
+ return -1;
+
+ if (pa_fdsem_before_poll(l->write_fdsem) >= 0)
+ return 0;
+ }
+
+ return 0;
+}
+
+void pa_shmasyncq_after_poll(pa_shmasyncq *l) {
+ pa_assert(l);
+
+ pa_fdsem_after_poll(l->write_fdsem);
+}
diff --git a/src/pulsecore/shmasyncq.h b/src/pulsecore/shmasyncq.h
new file mode 100644
index 00000000..9845c391
--- /dev/null
+++ b/src/pulsecore/shmasyncq.h
@@ -0,0 +1,60 @@
+#ifndef foopulseshmasyncqhfoo
+#define foopulseshmasyncqhfoo
+
+/***
+ 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.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <sys/types.h>
+
+#include <pulse/def.h>
+#include <pulsecore/macro.h>
+
+/* Similar to pa_asyncq, but stores data in a shared memory segment */
+
+
+struct pa_shmasyncq_data {
+ unsigned n_elements;
+ size_t element_size;
+ unsigned read_idx;
+ unsigned write_idx;
+ pa_fdsem_data read_fdsem_data, write_fdsem_data;
+};
+
+#define PA_SHMASYNCQ_DEFAULT_N_ELEMENTS 128
+#define PA_SHMASYNCQ_SIZE(n_elements, element_size) (PA_ALIGN(sizeof(pa_shmasyncq_data)) + (((n_elements) * (PA_ALIGN(sizeof(pa_atomic_t)) + PA_ALIGN(element_size)))))
+#define PA_SHMASYNCQ_DEFAULT_SIZE(element_size) PA_SHMASYNCQ_SIZE(PA_SHMASYNCQ_DEFAULT_N_ELEMENTS, element_size)
+
+typedef struct pa_shmasyncq pa_shmasyncq;
+
+pa_shmasyncq *pa_shmasyncq_new(unsigned n_elements, size_t element_size, void *data, int fd[2]);
+void pa_shmasyncq_free(pa_shmasyncq* q, pa_free_cb_t free_cb);
+
+void* pa_shmasyncq_pop_begin(pa_shmasyncq *q, pa_bool_t wait);
+void pa_shmasyncq_pop_commit(pa_shmasyncq *q);
+
+int* pa_shmasyncq_push_begin(pa_shmasyncq *q, pa_bool_t wait);
+void pa_shmasyncq_push_commit(pa_shmasyncq *q);
+
+int pa_shmasyncq_get_fd(pa_shmasyncq *q);
+int pa_shmasyncq_before_poll(pa_shmasyncq *a);
+void pa_shmasyncq_after_poll(pa_shmasyncq *a);
+
+#endif
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index c3cd4952..43e7bb21 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -1,18 +1,19 @@
-/* $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
@@ -24,7 +25,6 @@
#endif
#include <stdio.h>
-#include <assert.h>
#include <stdlib.h>
#include <string.h>
@@ -36,95 +36,154 @@
#include <pulsecore/log.h>
#include <pulsecore/play-memblockq.h>
#include <pulsecore/namereg.h>
+#include <pulsecore/core-util.h>
#include "sink-input.h"
-#define CONVERT_BUFFER_LENGTH 4096
-#define MOVE_BUFFER_LENGTH (1024*1024)
-#define SILENCE_BUFFER_LENGTH (64*1024)
+#define MEMBLOCKQ_MAXLENGTH (32*1024*1024)
+#define CONVERT_BUFFER_LENGTH (PA_PAGE_SIZE)
-#define CHECK_VALIDITY_RETURN_NULL(condition) \
-do {\
-if (!(condition)) \
- return NULL; \
-} while (0)
+static PA_DEFINE_CHECK_TYPE(pa_sink_input, pa_msgobject);
+
+static void sink_input_free(pa_object *o);
pa_sink_input_new_data* pa_sink_input_new_data_init(pa_sink_input_new_data *data) {
- assert(data);
-
+ pa_assert(data);
+
memset(data, 0, sizeof(*data));
data->resample_method = PA_RESAMPLER_INVALID;
+ data->proplist = pa_proplist_new();
+
return data;
}
+void pa_sink_input_new_data_set_sample_spec(pa_sink_input_new_data *data, const pa_sample_spec *spec) {
+ pa_assert(data);
+
+ if ((data->sample_spec_is_set = !!spec))
+ data->sample_spec = *spec;
+}
+
void pa_sink_input_new_data_set_channel_map(pa_sink_input_new_data *data, const pa_channel_map *map) {
- assert(data);
+ pa_assert(data);
if ((data->channel_map_is_set = !!map))
data->channel_map = *map;
}
void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cvolume *volume) {
- assert(data);
+ pa_assert(data);
if ((data->volume_is_set = !!volume))
data->volume = *volume;
}
-void pa_sink_input_new_data_set_sample_spec(pa_sink_input_new_data *data, const pa_sample_spec *spec) {
- assert(data);
+void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mute) {
+ pa_assert(data);
- if ((data->sample_spec_is_set = !!spec))
- data->sample_spec = *spec;
+ data->muted_is_set = TRUE;
+ data->muted = !!mute;
+}
+
+void pa_sink_input_new_data_done(pa_sink_input_new_data *data) {
+ pa_assert(data);
+
+ pa_proplist_free(data->proplist);
+}
+
+/* Called from main context */
+static void reset_callbacks(pa_sink_input *i) {
+ pa_assert(i);
+
+ i->pop = NULL;
+ i->process_rewind = NULL;
+ i->update_max_rewind = NULL;
+ i->update_max_request = NULL;
+ i->update_sink_requested_latency = NULL;
+ i->update_sink_latency_range = NULL;
+ i->attach = NULL;
+ i->detach = NULL;
+ i->suspend = NULL;
+ i->moved = NULL;
+ i->kill = NULL;
+ i->get_latency = NULL;
+ i->state_change = NULL;
}
+/* Called from main context */
pa_sink_input* pa_sink_input_new(
pa_core *core,
pa_sink_input_new_data *data,
pa_sink_input_flags_t flags) {
-
+
pa_sink_input *i;
pa_resampler *resampler = NULL;
- int r;
- char st[PA_SAMPLE_SPEC_SNPRINT_MAX];
+ char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
- assert(core);
- assert(data);
+ pa_assert(core);
+ pa_assert(data);
- if (!(flags & PA_SINK_INPUT_NO_HOOKS))
- if (pa_hook_fire(&core->hook_sink_input_new, data) < 0)
- return NULL;
+ if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], data) < 0)
+ return NULL;
- CHECK_VALIDITY_RETURN_NULL(!data->driver || pa_utf8_valid(data->driver));
- CHECK_VALIDITY_RETURN_NULL(!data->name || pa_utf8_valid(data->name));
+ pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver));
if (!data->sink)
- data->sink = pa_namereg_get(core, NULL, PA_NAMEREG_SINK, 1);
-
- CHECK_VALIDITY_RETURN_NULL(data->sink);
- CHECK_VALIDITY_RETURN_NULL(data->sink->state == PA_SINK_RUNNING);
+ data->sink = pa_namereg_get(core, NULL, PA_NAMEREG_SINK, TRUE);
+
+ pa_return_null_if_fail(data->sink);
+ pa_return_null_if_fail(pa_sink_get_state(data->sink) != PA_SINK_UNLINKED);
+ pa_return_null_if_fail(!data->sync_base || (data->sync_base->sink == data->sink && pa_sink_input_get_state(data->sync_base) == PA_SINK_INPUT_CORKED));
if (!data->sample_spec_is_set)
data->sample_spec = data->sink->sample_spec;
-
- CHECK_VALIDITY_RETURN_NULL(pa_sample_spec_valid(&data->sample_spec));
-
- if (!data->channel_map_is_set)
- pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
-
- CHECK_VALIDITY_RETURN_NULL(pa_channel_map_valid(&data->channel_map));
- CHECK_VALIDITY_RETURN_NULL(data->channel_map.channels == data->sample_spec.channels);
-
+
+ pa_return_null_if_fail(pa_sample_spec_valid(&data->sample_spec));
+
+ if (!data->channel_map_is_set) {
+ if (data->sink->channel_map.channels == data->sample_spec.channels)
+ data->channel_map = data->sink->channel_map;
+ else
+ pa_return_null_if_fail(pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT));
+ }
+
+ pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map));
+ pa_return_null_if_fail(data->channel_map.channels == data->sample_spec.channels);
+
if (!data->volume_is_set)
pa_cvolume_reset(&data->volume, data->sample_spec.channels);
- CHECK_VALIDITY_RETURN_NULL(pa_cvolume_valid(&data->volume));
- CHECK_VALIDITY_RETURN_NULL(data->volume.channels == data->sample_spec.channels);
+ pa_return_null_if_fail(pa_cvolume_valid(&data->volume));
+ pa_return_null_if_fail(data->volume.channels == data->sample_spec.channels);
+
+ if (!data->muted_is_set)
+ data->muted = FALSE;
+
+ if (flags & PA_SINK_INPUT_FIX_FORMAT)
+ data->sample_spec.format = data->sink->sample_spec.format;
+
+ if (flags & PA_SINK_INPUT_FIX_RATE)
+ data->sample_spec.rate = data->sink->sample_spec.rate;
+
+ if (flags & PA_SINK_INPUT_FIX_CHANNELS) {
+ data->sample_spec.channels = data->sink->sample_spec.channels;
+ data->channel_map = data->sink->channel_map;
+ }
+
+ pa_assert(pa_sample_spec_valid(&data->sample_spec));
+ pa_assert(pa_channel_map_valid(&data->channel_map));
+
+ /* Due to the fixing of the sample spec the volume might not match anymore */
+ if (data->volume.channels != data->sample_spec.channels)
+ pa_cvolume_set(&data->volume, data->sample_spec.channels, pa_cvolume_avg(&data->volume));
if (data->resample_method == PA_RESAMPLER_INVALID)
data->resample_method = core->resample_method;
- CHECK_VALIDITY_RETURN_NULL(data->resample_method < PA_RESAMPLER_MAX);
+ pa_return_null_if_fail(data->resample_method < PA_RESAMPLER_MAX);
+
+ if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], data) < 0)
+ return NULL;
if (pa_idxset_size(data->sink->inputs) >= PA_MAX_INPUTS_PER_SINK) {
pa_log_warn("Failed to create sink input: too many inputs per sink.");
@@ -133,541 +192,1011 @@ pa_sink_input* pa_sink_input_new(
if ((flags & PA_SINK_INPUT_VARIABLE_RATE) ||
!pa_sample_spec_equal(&data->sample_spec, &data->sink->sample_spec) ||
- !pa_channel_map_equal(&data->channel_map, &data->sink->channel_map))
-
+ !pa_channel_map_equal(&data->channel_map, &data->sink->channel_map)) {
+
if (!(resampler = pa_resampler_new(
- core->mempool,
+ core->mempool,
&data->sample_spec, &data->channel_map,
&data->sink->sample_spec, &data->sink->channel_map,
- data->resample_method))) {
+ data->resample_method,
+ ((flags & PA_SINK_INPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
+ ((flags & PA_SINK_INPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
+ (core->disable_remixing || (flags & PA_SINK_INPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0)))) {
pa_log_warn("Unsupported resampling operation.");
return NULL;
}
- i = pa_xnew(pa_sink_input, 1);
- i->ref = 1;
- i->state = PA_SINK_INPUT_DRAINED;
+ data->resample_method = pa_resampler_get_method(resampler);
+ }
+
+ i = pa_msgobject_new(pa_sink_input);
+ i->parent.parent.free = sink_input_free;
+ i->parent.process_msg = pa_sink_input_process_msg;
+
+ i->core = core;
+ i->state = PA_SINK_INPUT_INIT;
i->flags = flags;
- i->name = pa_xstrdup(data->name);
+ i->proplist = pa_proplist_copy(data->proplist);
i->driver = pa_xstrdup(data->driver);
i->module = data->module;
i->sink = data->sink;
i->client = data->client;
+ i->resample_method = data->resample_method;
i->sample_spec = data->sample_spec;
i->channel_map = data->channel_map;
+
i->volume = data->volume;
-
- i->peek = NULL;
- i->drop = NULL;
- i->kill = NULL;
- i->get_latency = NULL;
- i->underrun = NULL;
+ i->muted = data->muted;
+
+ if (data->sync_base) {
+ i->sync_next = data->sync_base->sync_next;
+ i->sync_prev = data->sync_base;
+
+ if (data->sync_base->sync_next)
+ data->sync_base->sync_next->sync_prev = i;
+ data->sync_base->sync_next = i;
+ } else
+ i->sync_next = i->sync_prev = NULL;
+
+ i->direct_outputs = pa_idxset_new(NULL, NULL);
+
+ reset_callbacks(i);
i->userdata = NULL;
-
- i->move_silence = 0;
- pa_memchunk_reset(&i->resampled_chunk);
- i->resampler = resampler;
- i->resample_method = data->resample_method;
- i->silence_memblock = NULL;
-
- r = pa_idxset_put(core->sink_inputs, i, &i->index);
- assert(r == 0);
- r = pa_idxset_put(i->sink->inputs, i, NULL);
- assert(r == 0);
-
- pa_log_info("created %u \"%s\" on %s with sample spec %s",
+ i->thread_info.state = i->state;
+ i->thread_info.attached = FALSE;
+ pa_atomic_store(&i->thread_info.drained, 1);
+ i->thread_info.sample_spec = i->sample_spec;
+ i->thread_info.resampler = resampler;
+ i->thread_info.volume = i->volume;
+ i->thread_info.muted = i->muted;
+ i->thread_info.requested_sink_latency = (pa_usec_t) -1;
+ i->thread_info.rewrite_nbytes = 0;
+ i->thread_info.rewrite_flush = FALSE;
+ i->thread_info.underrun_for = (uint64_t) -1;
+ i->thread_info.playing_for = 0;
+ i->thread_info.direct_outputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+
+ i->thread_info.render_memblockq = pa_memblockq_new(
+ 0,
+ MEMBLOCKQ_MAXLENGTH,
+ 0,
+ pa_frame_size(&i->sink->sample_spec),
+ 0,
+ 1,
+ 0,
+ &i->sink->silence);
+
+ pa_assert_se(pa_idxset_put(core->sink_inputs, pa_sink_input_ref(i), &i->index) == 0);
+ pa_assert_se(pa_idxset_put(i->sink->inputs, i, NULL) == 0);
+
+ pa_log_info("Created input %u \"%s\" on %s with sample spec %s and channel map %s",
i->index,
- i->name,
+ pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME)),
i->sink->name,
- pa_sample_spec_snprint(st, sizeof(st), &i->sample_spec));
-
- pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, i->index);
+ pa_sample_spec_snprint(st, sizeof(st), &i->sample_spec),
+ pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map));
+
+ /* Don't forget to call pa_sink_input_put! */
- /* We do not call pa_sink_notify() here, because the virtual
- * functions have not yet been initialized */
-
- return i;
+ return i;
+}
+
+/* Called from main context */
+static void update_n_corked(pa_sink_input *i, pa_sink_input_state_t state) {
+ pa_assert(i);
+
+ if (i->state == PA_SINK_INPUT_CORKED && state != PA_SINK_INPUT_CORKED)
+ pa_assert_se(i->sink->n_corked -- >= 1);
+ else if (i->state != PA_SINK_INPUT_CORKED && state == PA_SINK_INPUT_CORKED)
+ i->sink->n_corked++;
+
+ pa_sink_update_status(i->sink);
}
-void pa_sink_input_disconnect(pa_sink_input *i) {
- assert(i);
- assert(i->state != PA_SINK_INPUT_DISCONNECTED);
- assert(i->sink);
- assert(i->sink->core);
+/* Called from main context */
+static int sink_input_set_state(pa_sink_input *i, pa_sink_input_state_t state) {
+ pa_sink_input *ssync;
+ pa_assert(i);
+
+ if (state == PA_SINK_INPUT_DRAINED)
+ state = PA_SINK_INPUT_RUNNING;
+
+ if (i->state == state)
+ return 0;
+
+ pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) == 0);
+
+ update_n_corked(i, state);
+ i->state = state;
+
+ for (ssync = i->sync_prev; ssync; ssync = ssync->sync_prev) {
+ update_n_corked(ssync, state);
+ ssync->state = state;
+ }
+ for (ssync = i->sync_next; ssync; ssync = ssync->sync_next) {
+ update_n_corked(ssync, state);
+ ssync->state = state;
+ }
+
+ if (state != PA_SINK_INPUT_UNLINKED) {
+ pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], i);
+
+ for (ssync = i->sync_prev; ssync; ssync = ssync->sync_prev)
+ pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], ssync);
+
+ for (ssync = i->sync_next; ssync; ssync = ssync->sync_next)
+ pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], ssync);
+ }
+
+ return 0;
+}
+
+/* Called from main context */
+void pa_sink_input_unlink(pa_sink_input *i) {
+ pa_bool_t linked;
+ pa_source_output *o, *p = NULL;
+ pa_assert(i);
+
+ /* See pa_sink_unlink() for a couple of comments how this function
+ * works */
+
+ pa_sink_input_ref(i);
+
+ linked = PA_SINK_INPUT_IS_LINKED(i->state);
+
+ if (linked)
+ pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], i);
+
+ if (i->sync_prev)
+ i->sync_prev->sync_next = i->sync_next;
+ if (i->sync_next)
+ i->sync_next->sync_prev = i->sync_prev;
+
+ i->sync_prev = i->sync_next = NULL;
pa_idxset_remove_by_data(i->sink->core->sink_inputs, i, NULL);
- pa_idxset_remove_by_data(i->sink->inputs, i, NULL);
+ if (pa_idxset_remove_by_data(i->sink->inputs, i, NULL))
+ pa_sink_input_unref(i);
- pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_REMOVE, i->index);
- i->sink = NULL;
+ while ((o = pa_idxset_first(i->direct_outputs, NULL))) {
+ pa_assert(o != p);
+ pa_source_output_kill(o);
+ p = o;
+ }
- i->peek = NULL;
- i->drop = NULL;
- i->kill = NULL;
- i->get_latency = NULL;
- i->underrun = NULL;
+ update_n_corked(i, PA_SINK_INPUT_UNLINKED);
+ i->state = PA_SINK_INPUT_UNLINKED;
+
+ if (linked)
+ if (i->sink->asyncmsgq)
+ pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_REMOVE_INPUT, i, 0, NULL) == 0);
+
+ reset_callbacks(i);
- i->state = PA_SINK_INPUT_DISCONNECTED;
+ if (linked) {
+ pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_REMOVE, i->index);
+ pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK_POST], i);
+ }
+
+ i->sink = NULL;
+ pa_sink_input_unref(i);
}
-static void sink_input_free(pa_sink_input* i) {
- assert(i);
+/* Called from main context */
+static void sink_input_free(pa_object *o) {
+ pa_sink_input* i = PA_SINK_INPUT(o);
- if (i->state != PA_SINK_INPUT_DISCONNECTED)
- pa_sink_input_disconnect(i);
+ pa_assert(i);
+ pa_assert(pa_sink_input_refcnt(i) == 0);
- pa_log_info("freed %u \"%s\"", i->index, i->name);
-
- if (i->resampled_chunk.memblock)
- pa_memblock_unref(i->resampled_chunk.memblock);
-
- if (i->resampler)
- pa_resampler_free(i->resampler);
+ if (PA_SINK_INPUT_IS_LINKED(i->state))
+ pa_sink_input_unlink(i);
+
+ pa_log_info("Freeing input %u \"%s\"", i->index, pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME)));
+
+ pa_assert(!i->thread_info.attached);
+
+ if (i->thread_info.render_memblockq)
+ pa_memblockq_free(i->thread_info.render_memblockq);
+
+ if (i->thread_info.resampler)
+ pa_resampler_free(i->thread_info.resampler);
+
+ if (i->proplist)
+ pa_proplist_free(i->proplist);
+
+ if (i->direct_outputs)
+ pa_idxset_free(i->direct_outputs, NULL, NULL);
+
+ if (i->thread_info.direct_outputs)
+ pa_hashmap_free(i->thread_info.direct_outputs, NULL, NULL);
- if (i->silence_memblock)
- pa_memblock_unref(i->silence_memblock);
-
- pa_xfree(i->name);
pa_xfree(i->driver);
pa_xfree(i);
}
-void pa_sink_input_unref(pa_sink_input *i) {
- assert(i);
- assert(i->ref >= 1);
+/* Called from main context */
+void pa_sink_input_put(pa_sink_input *i) {
+ pa_sink_input_state_t state;
+ pa_sink_input_assert_ref(i);
- if (!(--i->ref))
- sink_input_free(i);
-}
+ pa_assert(i->state == PA_SINK_INPUT_INIT);
-pa_sink_input* pa_sink_input_ref(pa_sink_input *i) {
- assert(i);
- assert(i->ref >= 1);
-
- i->ref++;
- return i;
+ /* The following fields must be initialized properly */
+ pa_assert(i->pop);
+ pa_assert(i->process_rewind);
+ pa_assert(i->kill);
+
+ i->thread_info.volume = i->volume;
+ i->thread_info.muted = i->muted;
+
+ state = i->flags & PA_SINK_INPUT_START_CORKED ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING;
+
+ update_n_corked(i, state);
+ i->state = state;
+
+ pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_ADD_INPUT, i, 0, NULL) == 0);
+
+ pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, i->index);
+ pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], i);
}
+/* Called from main context */
void pa_sink_input_kill(pa_sink_input*i) {
- assert(i);
- assert(i->ref >= 1);
+ pa_sink_input_assert_ref(i);
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
- if (i->kill)
- i->kill(i);
+ i->kill(i);
}
-pa_usec_t pa_sink_input_get_latency(pa_sink_input *i) {
- pa_usec_t r = 0;
-
- assert(i);
- assert(i->ref >= 1);
-
- if (i->get_latency)
- r += i->get_latency(i);
+/* Called from main context */
+pa_usec_t pa_sink_input_get_latency(pa_sink_input *i, pa_usec_t *sink_latency) {
+ pa_usec_t r[2] = { 0, 0 };
- if (i->resampled_chunk.memblock)
- r += pa_bytes_to_usec(i->resampled_chunk.length, &i->sink->sample_spec);
+ pa_sink_input_assert_ref(i);
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
- if (i->move_silence)
- r += pa_bytes_to_usec(i->move_silence, &i->sink->sample_spec);
+ pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_GET_LATENCY, r, 0, NULL) == 0);
- return r;
+ if (i->get_latency)
+ r[0] += i->get_latency(i);
+
+ if (sink_latency)
+ *sink_latency = r[1];
+
+ return r[0];
}
-int pa_sink_input_peek(pa_sink_input *i, pa_memchunk *chunk, pa_cvolume *volume) {
- int ret = -1;
- int do_volume_adj_here;
- int volume_is_norm;
-
- assert(i);
- assert(i->ref >= 1);
- assert(chunk);
- assert(volume);
+/* Called from thread context */
+int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa_memchunk *chunk, pa_cvolume *volume) {
+ pa_bool_t do_volume_adj_here;
+ pa_bool_t volume_is_norm;
+ size_t block_size_max_sink, block_size_max_sink_input;
+ size_t ilength;
- pa_sink_input_ref(i);
+ pa_sink_input_assert_ref(i);
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state));
+ pa_assert(pa_frame_aligned(slength, &i->sink->sample_spec));
+ pa_assert(chunk);
+ pa_assert(volume);
- if (!i->peek || !i->drop || i->state == PA_SINK_INPUT_CORKED)
- goto finish;
+/* pa_log_debug("peek"); */
- assert(i->state == PA_SINK_INPUT_RUNNING || i->state == PA_SINK_INPUT_DRAINED);
+ if (!i->pop)
+ return -1;
- if (i->move_silence > 0) {
- size_t l;
+ pa_assert(i->thread_info.state == PA_SINK_INPUT_RUNNING ||
+ i->thread_info.state == PA_SINK_INPUT_CORKED ||
+ i->thread_info.state == PA_SINK_INPUT_DRAINED);
- /* We have just been moved and shall play some silence for a
- * while until the old sink has drained its playback buffer */
-
- if (!i->silence_memblock)
- i->silence_memblock = pa_silence_memblock_new(i->sink->core->mempool, &i->sink->sample_spec, SILENCE_BUFFER_LENGTH);
+ /* If there's still some rewrite request the handle, but the sink
+ didn't do this for us, we do it here. However, since the sink
+ apparently doesn't support rewinding, we pass 0 here. This still
+ allows rewinding through the render buffer. */
+ pa_sink_input_process_rewind(i, 0);
- chunk->memblock = pa_memblock_ref(i->silence_memblock);
- chunk->index = 0;
- l = pa_memblock_get_length(chunk->memblock);
- chunk->length = i->move_silence < l ? i->move_silence : l;
+ block_size_max_sink_input = i->thread_info.resampler ?
+ pa_resampler_max_block_size(i->thread_info.resampler) :
+ pa_frame_align(pa_mempool_block_size_max(i->sink->core->mempool), &i->sample_spec);
- ret = 0;
- do_volume_adj_here = 1;
- goto finish;
- }
-
- if (!i->resampler) {
- do_volume_adj_here = 0;
- ret = i->peek(i, chunk);
- goto finish;
- }
+ block_size_max_sink = pa_frame_align(pa_mempool_block_size_max(i->sink->core->mempool), &i->sink->sample_spec);
+
+ /* Default buffer size */
+ if (slength <= 0)
+ slength = pa_frame_align(CONVERT_BUFFER_LENGTH, &i->sink->sample_spec);
+
+ if (slength > block_size_max_sink)
+ slength = block_size_max_sink;
+
+ if (i->thread_info.resampler) {
+ ilength = pa_resampler_request(i->thread_info.resampler, slength);
+
+ if (ilength <= 0)
+ ilength = pa_frame_align(CONVERT_BUFFER_LENGTH, &i->sample_spec);
+ } else
+ ilength = slength;
+
+ if (ilength > block_size_max_sink_input)
+ ilength = block_size_max_sink_input;
+
+ /* If the channel maps of the sink and this stream differ, we need
+ * to adjust the volume *before* we resample. Otherwise we can do
+ * it after and leave it for the sink code */
do_volume_adj_here = !pa_channel_map_equal(&i->channel_map, &i->sink->channel_map);
- volume_is_norm = pa_cvolume_is_norm(&i->volume);
-
- while (!i->resampled_chunk.memblock) {
+ volume_is_norm = pa_cvolume_is_norm(&i->thread_info.volume) && !i->thread_info.muted;
+
+ while (!pa_memblockq_is_readable(i->thread_info.render_memblockq)) {
pa_memchunk tchunk;
- size_t l;
-
- if ((ret = i->peek(i, &tchunk)) < 0)
- goto finish;
- assert(tchunk.length);
-
- l = pa_resampler_request(i->resampler, CONVERT_BUFFER_LENGTH);
+ /* There's nothing in our render queue. We need to fill it up
+ * with data from the implementor. */
- if (l > tchunk.length)
- l = tchunk.length;
+ if (i->thread_info.state == PA_SINK_INPUT_CORKED ||
+ i->pop(i, ilength, &tchunk) < 0) {
- i->drop(i, &tchunk, l);
- tchunk.length = l;
+ /* OK, we're corked or the implementor didn't give us any
+ * data, so let's just hand out silence */
+ pa_atomic_store(&i->thread_info.drained, 1);
- /* It might be necessary to adjust the volume here */
- if (do_volume_adj_here && !volume_is_norm) {
- pa_memchunk_make_writable(&tchunk, 0);
- pa_volume_memchunk(&tchunk, &i->sample_spec, &i->volume);
+ pa_memblockq_seek(i->thread_info.render_memblockq, slength, PA_SEEK_RELATIVE);
+ i->thread_info.playing_for = 0;
+ if (i->thread_info.underrun_for != (uint64_t) -1)
+ i->thread_info.underrun_for += ilength;
+ break;
+ }
+
+ pa_atomic_store(&i->thread_info.drained, 0);
+
+ pa_assert(tchunk.length > 0);
+ pa_assert(tchunk.memblock);
+
+ i->thread_info.underrun_for = 0;
+ i->thread_info.playing_for += tchunk.length;
+
+ while (tchunk.length > 0) {
+ pa_memchunk wchunk;
+
+ wchunk = tchunk;
+ pa_memblock_ref(wchunk.memblock);
+
+ if (wchunk.length > block_size_max_sink_input)
+ wchunk.length = block_size_max_sink_input;
+
+ /* It might be necessary to adjust the volume here */
+ if (do_volume_adj_here && !volume_is_norm) {
+ pa_memchunk_make_writable(&wchunk, 0);
+
+ if (i->thread_info.muted)
+ pa_silence_memchunk(&wchunk, &i->thread_info.sample_spec);
+ else
+ pa_volume_memchunk(&wchunk, &i->thread_info.sample_spec, &i->thread_info.volume);
+ }
+
+ if (!i->thread_info.resampler)
+ pa_memblockq_push_align(i->thread_info.render_memblockq, &wchunk);
+ else {
+ pa_memchunk rchunk;
+ pa_resampler_run(i->thread_info.resampler, &wchunk, &rchunk);
+
+/* pa_log_debug("pushing %lu", (unsigned long) rchunk.length); */
+
+ if (rchunk.memblock) {
+ pa_memblockq_push_align(i->thread_info.render_memblockq, &rchunk);
+ pa_memblock_unref(rchunk.memblock);
+ }
+ }
+
+ pa_memblock_unref(wchunk.memblock);
+
+ tchunk.index += wchunk.length;
+ tchunk.length -= wchunk.length;
}
- pa_resampler_run(i->resampler, &tchunk, &i->resampled_chunk);
pa_memblock_unref(tchunk.memblock);
}
- assert(i->resampled_chunk.memblock);
- assert(i->resampled_chunk.length);
-
- *chunk = i->resampled_chunk;
- pa_memblock_ref(i->resampled_chunk.memblock);
+ pa_assert_se(pa_memblockq_peek(i->thread_info.render_memblockq, chunk) >= 0);
- ret = 0;
+ pa_assert(chunk->length > 0);
+ pa_assert(chunk->memblock);
-finish:
+/* pa_log_debug("peeking %lu", (unsigned long) chunk->length); */
- if (ret < 0 && i->state == PA_SINK_INPUT_RUNNING && i->underrun)
- i->underrun(i);
+ if (chunk->length > block_size_max_sink)
+ chunk->length = block_size_max_sink;
- if (ret >= 0)
- i->state = PA_SINK_INPUT_RUNNING;
- else if (ret < 0 && i->state == PA_SINK_INPUT_RUNNING)
- i->state = PA_SINK_INPUT_DRAINED;
+ /* Let's see if we had to apply the volume adjustment ourselves,
+ * or if this can be done by the sink for us */
- if (ret >= 0) {
- /* Let's see if we had to apply the volume adjustment
- * ourselves, or if this can be done by the sink for us */
+ if (do_volume_adj_here)
+ /* We had different channel maps, so we already did the adjustment */
+ pa_cvolume_reset(volume, i->sink->sample_spec.channels);
+ else if (i->thread_info.muted)
+ /* We've both the same channel map, so let's have the sink do the adjustment for us*/
+ pa_cvolume_mute(volume, i->sink->sample_spec.channels);
+ else
+ *volume = i->thread_info.volume;
- if (do_volume_adj_here)
- /* We had different channel maps, so we already did the adjustment */
- pa_cvolume_reset(volume, i->sink->sample_spec.channels);
- else
- /* We've both the same channel map, so let's have the sink do the adjustment for us*/
- *volume = i->volume;
- }
-
- pa_sink_input_unref(i);
-
- return ret;
+ return 0;
}
-void pa_sink_input_drop(pa_sink_input *i, const pa_memchunk *chunk, size_t length) {
- assert(i);
- assert(i->ref >= 1);
- assert(length > 0);
+/* Called from thread context */
+void pa_sink_input_drop(pa_sink_input *i, size_t nbytes /* in sink sample spec */) {
+ pa_sink_input_assert_ref(i);
- if (i->move_silence > 0) {
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state));
+ pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec));
+ pa_assert(nbytes > 0);
- if (chunk) {
- size_t l;
+/* pa_log_debug("dropping %lu", (unsigned long) nbytes); */
- l = pa_memblock_get_length(i->silence_memblock);
-
- if (chunk->memblock != i->silence_memblock ||
- chunk->index != 0 ||
- (chunk->memblock && (chunk->length != (l < i->move_silence ? l : i->move_silence))))
- return;
-
- }
+ /* If there's still some rewrite request the handle, but the sink
+ didn't do this for us, we do it here. However, since the sink
+ apparently doesn't support rewinding, we pass 0 here. This still
+ allows rewinding through the render buffer. */
+ pa_sink_input_process_rewind(i, 0);
- assert(i->move_silence >= length);
-
- i->move_silence -= length;
+ pa_memblockq_drop(i->thread_info.render_memblockq, nbytes);
+}
- if (i->move_silence <= 0) {
- assert(i->silence_memblock);
- pa_memblock_unref(i->silence_memblock);
- i->silence_memblock = NULL;
- }
+/* Called from thread context */
+void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sample spec */) {
+ size_t lbq;
+ pa_sink_input_assert_ref(i);
- return;
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state));
+ pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec));
+
+/* pa_log_debug("rewind(%lu, %lu)", (unsigned long) nbytes, (unsigned long) i->thread_info.rewrite_nbytes); */
+
+ lbq = pa_memblockq_get_length(i->thread_info.render_memblockq);
+
+ if (nbytes > 0) {
+ pa_log_debug("Have to rewind %lu bytes on render memblockq.", (unsigned long) nbytes);
+ pa_memblockq_rewind(i->thread_info.render_memblockq, nbytes);
}
- if (!i->resampler) {
- if (i->drop)
- i->drop(i, chunk, length);
- return;
+ if (i->thread_info.rewrite_nbytes == (size_t) -1) {
+
+ /* We were asked to drop all buffered data, and rerequest new
+ * data from implementor the next time push() is called */
+
+ pa_memblockq_flush(i->thread_info.render_memblockq);
+
+ } else if (i->thread_info.rewrite_nbytes > 0) {
+ size_t max_rewrite, amount;
+
+ /* Calculate how much make sense to rewrite at most */
+ max_rewrite = nbytes + lbq;
+
+ /* Transform into local domain */
+ if (i->thread_info.resampler)
+ max_rewrite = pa_resampler_request(i->thread_info.resampler, max_rewrite);
+
+ /* Calculate how much of the rewinded data should actually be rewritten */
+ amount = PA_MIN(i->thread_info.rewrite_nbytes, max_rewrite);
+
+ if (amount > 0) {
+ pa_log_debug("Have to rewind %lu bytes on implementor.", (unsigned long) amount);
+
+ /* Tell the implementor */
+ if (i->process_rewind)
+ i->process_rewind(i, amount);
+
+ /* Convert back to to sink domain */
+ if (i->thread_info.resampler)
+ amount = pa_resampler_result(i->thread_info.resampler, amount);
+
+ if (amount > 0)
+ /* Ok, now update the write pointer */
+ pa_memblockq_seek(i->thread_info.render_memblockq, - ((int64_t) amount), PA_SEEK_RELATIVE);
+
+ if (i->thread_info.rewrite_flush)
+ pa_memblockq_silence(i->thread_info.render_memblockq);
+
+ /* And reset the resampler */
+ if (i->thread_info.resampler)
+ pa_resampler_reset(i->thread_info.resampler);
+ }
}
-
- assert(i->resampled_chunk.memblock);
- assert(i->resampled_chunk.length >= length);
- i->resampled_chunk.index += length;
- i->resampled_chunk.length -= length;
+ i->thread_info.rewrite_nbytes = 0;
+ i->thread_info.rewrite_flush = FALSE;
+}
+
+/* Called from thread context */
+void pa_sink_input_update_max_rewind(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */) {
+ pa_sink_input_assert_ref(i);
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state));
+ pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec));
+
+ pa_memblockq_set_maxrewind(i->thread_info.render_memblockq, nbytes);
- if (i->resampled_chunk.length <= 0) {
- pa_memblock_unref(i->resampled_chunk.memblock);
- i->resampled_chunk.memblock = NULL;
- i->resampled_chunk.index = i->resampled_chunk.length = 0;
+ if (i->update_max_rewind)
+ i->update_max_rewind(i, i->thread_info.resampler ? pa_resampler_request(i->thread_info.resampler, nbytes) : nbytes);
+}
+
+/* Called from thread context */
+void pa_sink_input_update_max_request(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */) {
+ pa_sink_input_assert_ref(i);
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state));
+ pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec));
+
+ if (i->update_max_request)
+ i->update_max_request(i, i->thread_info.resampler ? pa_resampler_request(i->thread_info.resampler, nbytes) : nbytes);
+}
+
+/* Called from thread context */
+static pa_usec_t fixup_latency(pa_sink *s, pa_usec_t usec) {
+ pa_sink_assert_ref(s);
+
+ if (usec == (pa_usec_t) -1)
+ return usec;
+
+ if (s->thread_info.max_latency > 0 && usec > s->thread_info.max_latency)
+ usec = s->thread_info.max_latency;
+
+ if (s->thread_info.min_latency > 0 && usec < s->thread_info.min_latency)
+ usec = s->thread_info.min_latency;
+
+ return usec;
+}
+
+/* Called from thread context */
+pa_usec_t pa_sink_input_set_requested_latency_within_thread(pa_sink_input *i, pa_usec_t usec) {
+ pa_sink_input_assert_ref(i);
+
+ usec = fixup_latency(i->sink, usec);
+ i->thread_info.requested_sink_latency = usec;
+ pa_sink_invalidate_requested_latency(i->sink);
+
+ return usec;
+}
+
+/* Called from main context */
+pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec) {
+ pa_sink_input_assert_ref(i);
+
+ if (PA_SINK_INPUT_IS_LINKED(i->state))
+ pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
+ else {
+ /* If this sink input is not realized yet, we have to touch
+ * the thread info data directly */
+
+ usec = fixup_latency(i->sink, usec);
+ i->thread_info.requested_sink_latency = usec;
+ i->sink->thread_info.requested_latency_valid = FALSE;
}
+
+ return usec;
+}
+
+/* Called from main context */
+pa_usec_t pa_sink_input_get_requested_latency(pa_sink_input *i) {
+ pa_usec_t usec = 0;
+
+ pa_sink_input_assert_ref(i);
+
+ if (PA_SINK_INPUT_IS_LINKED(i->state))
+ pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
+ else
+ /* If this sink input is not realized yet, we have to touch
+ * the thread info data directly */
+ usec = i->thread_info.requested_sink_latency;
+
+ return usec;
}
+/* Called from main context */
void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume) {
- assert(i);
- assert(i->ref >= 1);
- assert(i->sink);
- assert(i->sink->core);
+ pa_sink_input_assert_ref(i);
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+ pa_assert(volume);
if (pa_cvolume_equal(&i->volume, volume))
return;
-
+
i->volume = *volume;
+
+ pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_VOLUME, &i->volume, 0, NULL) == 0);
pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
}
-const pa_cvolume * pa_sink_input_get_volume(pa_sink_input *i) {
- assert(i);
- assert(i->ref >= 1);
+/* Called from main context */
+const pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i) {
+ pa_sink_input_assert_ref(i);
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
return &i->volume;
}
-void pa_sink_input_cork(pa_sink_input *i, int b) {
- int n;
-
- assert(i);
- assert(i->ref >= 1);
+/* Called from main context */
+void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute) {
+ pa_assert(i);
+ pa_sink_input_assert_ref(i);
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+
+ if (!i->muted == !mute)
+ return;
- assert(i->state != PA_SINK_INPUT_DISCONNECTED);
+ i->muted = mute;
- n = i->state == PA_SINK_INPUT_CORKED && !b;
+ pa_asyncmsgq_post(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_MUTE, PA_UINT_TO_PTR(mute), 0, NULL, NULL);
+ pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+}
- if (b)
- i->state = PA_SINK_INPUT_CORKED;
- else if (i->state == PA_SINK_INPUT_CORKED)
- i->state = PA_SINK_INPUT_DRAINED;
+/* Called from main context */
+pa_bool_t pa_sink_input_get_mute(pa_sink_input *i) {
+ pa_sink_input_assert_ref(i);
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
- if (n)
- pa_sink_notify(i->sink);
+ return i->muted;
}
-void pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate) {
- assert(i);
- assert(i->resampler);
- assert(i->ref >= 1);
+/* Called from main context */
+void pa_sink_input_cork(pa_sink_input *i, pa_bool_t b) {
+ pa_sink_input_assert_ref(i);
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+
+ sink_input_set_state(i, b ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING);
+}
+
+/* Called from main context */
+int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate) {
+ pa_sink_input_assert_ref(i);
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+ pa_return_val_if_fail(i->thread_info.resampler, -1);
if (i->sample_spec.rate == rate)
- return;
+ return 0;
i->sample_spec.rate = rate;
- pa_resampler_set_input_rate(i->resampler, rate);
+
+ pa_asyncmsgq_post(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_RATE, PA_UINT_TO_PTR(rate), 0, NULL, NULL);
pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+ return 0;
}
+/* Called from main context */
void pa_sink_input_set_name(pa_sink_input *i, const char *name) {
- assert(i);
- assert(i->ref >= 1);
+ const char *old;
+ pa_sink_input_assert_ref(i);
- if (!i->name && !name)
+ if (!name && !pa_proplist_contains(i->proplist, PA_PROP_MEDIA_NAME))
return;
- if (i->name && name && !strcmp(i->name, name))
+ old = pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME);
+
+ if (old && name && !strcmp(old, name))
return;
-
- pa_xfree(i->name);
- i->name = pa_xstrdup(name);
- pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+ if (name)
+ pa_proplist_sets(i->proplist, PA_PROP_MEDIA_NAME, name);
+ else
+ pa_proplist_unset(i->proplist, PA_PROP_MEDIA_NAME);
+
+ if (PA_SINK_INPUT_IS_LINKED(i->state)) {
+ pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], i);
+ pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+ }
}
+/* Called from main context */
pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i) {
- assert(i);
- assert(i->ref >= 1);
-
- if (!i->resampler)
- return i->resample_method;
+ pa_sink_input_assert_ref(i);
- return pa_resampler_get_method(i->resampler);
+ return i->resample_method;
}
-int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately) {
- pa_resampler *new_resampler = NULL;
- pa_memblockq *buffer = NULL;
+/* Called from main context */
+int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest) {
+ pa_resampler *new_resampler;
pa_sink *origin;
-
- assert(i);
- assert(dest);
+ pa_sink_input_move_hook_data hook_data;
+ pa_source_output *o, *p = NULL;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+ pa_sink_assert_ref(dest);
origin = i->sink;
if (dest == origin)
return 0;
+ if (i->flags & PA_SINK_INPUT_DONT_MOVE)
+ return -1;
+
+ if (i->sync_next || i->sync_prev) {
+ pa_log_warn("Moving synchronised streams not supported.");
+ return -1;
+ }
+
if (pa_idxset_size(dest->inputs) >= PA_MAX_INPUTS_PER_SINK) {
pa_log_warn("Failed to move sink input: too many inputs per sink.");
return -1;
}
- if (i->resampler &&
+ /* Kill directly connected outputs */
+ while ((o = pa_idxset_first(i->direct_outputs, NULL))) {
+ pa_assert(o != p);
+ pa_source_output_kill(o);
+ p = o;
+ }
+
+ if (i->thread_info.resampler &&
pa_sample_spec_equal(&origin->sample_spec, &dest->sample_spec) &&
pa_channel_map_equal(&origin->channel_map, &dest->channel_map))
/* Try to reuse the old resampler if possible */
- new_resampler = i->resampler;
-
+ new_resampler = i->thread_info.resampler;
+
else if ((i->flags & PA_SINK_INPUT_VARIABLE_RATE) ||
- !pa_sample_spec_equal(&i->sample_spec, &dest->sample_spec) ||
- !pa_channel_map_equal(&i->channel_map, &dest->channel_map)) {
+ !pa_sample_spec_equal(&i->sample_spec, &dest->sample_spec) ||
+ !pa_channel_map_equal(&i->channel_map, &dest->channel_map)) {
/* Okey, we need a new resampler for the new sink */
-
+
if (!(new_resampler = pa_resampler_new(
dest->core->mempool,
&i->sample_spec, &i->channel_map,
&dest->sample_spec, &dest->channel_map,
- i->resample_method))) {
+ i->resample_method,
+ ((i->flags & PA_SINK_INPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
+ ((i->flags & PA_SINK_INPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
+ (i->core->disable_remixing || (i->flags & PA_SINK_INPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0)))) {
pa_log_warn("Unsupported resampling operation.");
return -1;
}
+ } else
+ new_resampler = NULL;
+
+ hook_data.sink_input = i;
+ hook_data.destination = dest;
+ pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE], &hook_data);
+
+ pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_START_MOVE, i, 0, NULL) == 0);
+
+ pa_idxset_remove_by_data(origin->inputs, i, NULL);
+ pa_idxset_put(dest->inputs, i, NULL);
+ i->sink = dest;
+
+ if (pa_sink_input_get_state(i) == PA_SINK_INPUT_CORKED) {
+ pa_assert_se(origin->n_corked-- >= 1);
+ dest->n_corked++;
}
- if (!immediately) {
- pa_usec_t old_latency, new_latency;
- pa_usec_t silence_usec = 0;
-
- buffer = pa_memblockq_new(0, MOVE_BUFFER_LENGTH, 0, pa_frame_size(&origin->sample_spec), 0, 0, NULL);
-
- /* Let's do a little bit of Voodoo for compensating latency
- * differences */
-
- old_latency = pa_sink_get_latency(origin);
- new_latency = pa_sink_get_latency(dest);
-
- /* The already resampled data should go to the old sink */
-
- if (old_latency >= new_latency) {
-
- /* The latency of the old sink is larger than the latency
- * of the new sink. Therefore to compensate for the
- * difference we to play silence on the new one for a
- * while */
-
- silence_usec = old_latency - new_latency;
-
- } else {
- size_t l;
- int volume_is_norm;
-
- /* The latency of new sink is larger than the latency of
- * the old sink. Therefore we have to precompute a little
- * and make sure that this is still played on the old
- * sink, until we can play the first sample on the new
- * sink.*/
-
- l = pa_usec_to_bytes(new_latency - old_latency, &origin->sample_spec);
-
- volume_is_norm = pa_cvolume_is_norm(&i->volume);
-
- while (l > 0) {
- pa_memchunk chunk;
- pa_cvolume volume;
- size_t n;
-
- if (pa_sink_input_peek(i, &chunk, &volume) < 0)
- break;
-
- n = chunk.length > l ? l : chunk.length;
- pa_sink_input_drop(i, &chunk, n);
- chunk.length = n;
-
- if (!volume_is_norm) {
- pa_memchunk_make_writable(&chunk, 0);
- pa_volume_memchunk(&chunk, &origin->sample_spec, &volume);
- }
+ /* Replace resampler and render queue */
+ if (new_resampler != i->thread_info.resampler) {
- if (pa_memblockq_push(buffer, &chunk) < 0) {
- pa_memblock_unref(chunk.memblock);
- break;
- }
+ if (i->thread_info.resampler)
+ pa_resampler_free(i->thread_info.resampler);
+ i->thread_info.resampler = new_resampler;
- pa_memblock_unref(chunk.memblock);
- l -= n;
- }
+ pa_memblockq_free(i->thread_info.render_memblockq);
+
+ i->thread_info.render_memblockq = pa_memblockq_new(
+ 0,
+ MEMBLOCKQ_MAXLENGTH,
+ 0,
+ pa_frame_size(&i->sink->sample_spec),
+ 0,
+ 1,
+ 0,
+ &i->sink->silence);
+ }
+
+ pa_sink_update_status(origin);
+ pa_sink_update_status(dest);
+
+ pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_FINISH_MOVE, i, 0, NULL) == 0);
+
+ if (i->moved)
+ i->moved(i);
+
+ pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_POST], i);
+
+ pa_log_debug("Successfully moved sink input %i from %s to %s.", i->index, origin->name, dest->name);
+
+ /* Notify everyone */
+ pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+
+ return 0;
+}
+
+/* Called from IO thread context */
+void pa_sink_input_set_state_within_thread(pa_sink_input *i, pa_sink_input_state_t state) {
+ pa_sink_input_assert_ref(i);
+
+ if (state == i->thread_info.state)
+ return;
+
+ if ((state == PA_SINK_INPUT_DRAINED || state == PA_SINK_INPUT_RUNNING) &&
+ !(i->thread_info.state == PA_SINK_INPUT_DRAINED || i->thread_info.state != PA_SINK_INPUT_RUNNING))
+ pa_atomic_store(&i->thread_info.drained, 1);
+
+ if (state == PA_SINK_INPUT_CORKED && i->thread_info.state != PA_SINK_INPUT_CORKED) {
+
+ /* This will tell the implementing sink input driver to rewind
+ * so that the unplayed already mixed data is not lost */
+ pa_sink_input_request_rewind(i, 0, TRUE, TRUE);
+
+ } else if (i->thread_info.state == PA_SINK_INPUT_CORKED && state != PA_SINK_INPUT_CORKED) {
+
+ /* OK, we're being uncorked. Make sure we're not rewound when
+ * the hw buffer is remixed and request a remix. */
+ pa_sink_input_request_rewind(i, 0, FALSE, TRUE);
+ }
+
+ if (i->state_change)
+ i->state_change(i, state);
+
+ i->thread_info.state = state;
+}
+
+/* Called from thread context, except when it is not. */
+int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
+ pa_sink_input *i = PA_SINK_INPUT(o);
+ pa_sink_input_assert_ref(i);
+
+ switch (code) {
+
+ case PA_SINK_INPUT_MESSAGE_SET_VOLUME:
+ i->thread_info.volume = *((pa_cvolume*) userdata);
+ pa_sink_input_request_rewind(i, 0, TRUE, FALSE);
+ return 0;
+
+ case PA_SINK_INPUT_MESSAGE_SET_MUTE:
+ i->thread_info.muted = PA_PTR_TO_UINT(userdata);
+ pa_sink_input_request_rewind(i, 0, TRUE, FALSE);
+ return 0;
+
+ case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
+ pa_usec_t *r = userdata;
+ pa_usec_t sink_usec = 0;
+
+ r[0] += pa_bytes_to_usec(pa_memblockq_get_length(i->thread_info.render_memblockq), &i->sink->sample_spec);
+
+ if (i->sink->parent.process_msg(PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_GET_LATENCY, &sink_usec, 0, NULL) >= 0)
+ r[1] += sink_usec;
+
+ return 0;
}
- if (i->resampled_chunk.memblock) {
+ case PA_SINK_INPUT_MESSAGE_SET_RATE:
+
+ i->thread_info.sample_spec.rate = PA_PTR_TO_UINT(userdata);
+ pa_resampler_set_input_rate(i->thread_info.resampler, PA_PTR_TO_UINT(userdata));
- /* There is still some data left in the already resampled
- * memory block. Hence, let's output it on the old sink
- * and sleep so long on the new sink */
+ return 0;
- pa_memblockq_push(buffer, &i->resampled_chunk);
- silence_usec += pa_bytes_to_usec(i->resampled_chunk.length, &origin->sample_spec);
+ case PA_SINK_INPUT_MESSAGE_SET_STATE: {
+ pa_sink_input *ssync;
+
+ pa_sink_input_set_state_within_thread(i, PA_PTR_TO_UINT(userdata));
+
+ for (ssync = i->thread_info.sync_prev; ssync; ssync = ssync->thread_info.sync_prev)
+ pa_sink_input_set_state_within_thread(ssync, PA_PTR_TO_UINT(userdata));
+
+ for (ssync = i->thread_info.sync_next; ssync; ssync = ssync->thread_info.sync_next)
+ pa_sink_input_set_state_within_thread(ssync, PA_PTR_TO_UINT(userdata));
+
+ return 0;
}
- /* Calculate the new sleeping time */
- i->move_silence = pa_usec_to_bytes(
- pa_bytes_to_usec(i->move_silence, &i->sample_spec) +
- silence_usec,
- &i->sample_spec);
- }
+ case PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY: {
+ pa_usec_t *usec = userdata;
- /* Okey, let's move it */
- pa_idxset_remove_by_data(origin->inputs, i, NULL);
- pa_idxset_put(dest->inputs, i, NULL);
- i->sink = dest;
+ *usec = pa_sink_input_set_requested_latency_within_thread(i, *usec);
+ return 0;
+ }
+
+ case PA_SINK_INPUT_MESSAGE_GET_REQUESTED_LATENCY: {
+ pa_usec_t *r = userdata;
- /* Replace resampler */
- if (new_resampler != i->resampler) {
- if (i->resampler)
- pa_resampler_free(i->resampler);
- i->resampler = new_resampler;
-
- /* if the resampler changed, the silence memblock is
- * probably invalid now, too */
- if (i->silence_memblock) {
- pa_memblock_unref(i->silence_memblock);
- i->silence_memblock = NULL;
+ *r = i->thread_info.requested_sink_latency;
+ return 0;
}
}
- /* Dump already resampled data */
- if (i->resampled_chunk.memblock) {
- pa_memblock_unref(i->resampled_chunk.memblock);
- i->resampled_chunk.memblock = NULL;
- i->resampled_chunk.index = i->resampled_chunk.length = 0;
+ return -1;
+}
+
+/* Called from main thread */
+pa_sink_input_state_t pa_sink_input_get_state(pa_sink_input *i) {
+ pa_sink_input_assert_ref(i);
+
+ if (i->state == PA_SINK_INPUT_RUNNING || i->state == PA_SINK_INPUT_DRAINED)
+ return pa_atomic_load(&i->thread_info.drained) ? PA_SINK_INPUT_DRAINED : PA_SINK_INPUT_RUNNING;
+
+ return i->state;
+}
+
+/* Called from IO context */
+pa_bool_t pa_sink_input_safe_to_remove(pa_sink_input *i) {
+ pa_sink_input_assert_ref(i);
+
+ if (PA_SINK_INPUT_IS_LINKED(i->thread_info.state))
+ return pa_memblockq_is_empty(i->thread_info.render_memblockq);
+
+ return TRUE;
+}
+
+/* Called from IO context */
+void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sample spec */, pa_bool_t rewrite, pa_bool_t flush) {
+ size_t lbq;
+
+ /* If 'rewrite' is TRUE the sink is rewound as far as requested
+ * and possible and the exact value of this is passed back the
+ * implementor via process_rewind(). If 'flush' is also TRUE all
+ * already rendered data is also dropped.
+ *
+ * If 'rewrite' is FALSE the sink is rewound as far as requested
+ * and possible and the already rendered data is dropped so that
+ * in the next iteration we read new data from the
+ * implementor. This implies 'flush' is TRUE. */
+
+ pa_sink_input_assert_ref(i);
+ pa_assert(i->thread_info.rewrite_nbytes == 0);
+
+ /* We don't take rewind requests while we are corked */
+ if (i->thread_info.state == PA_SINK_INPUT_CORKED)
+ return;
+
+ pa_assert(rewrite || flush);
+
+ /* Calculate how much we can rewind locally without having to
+ * touch the sink */
+ if (rewrite)
+ lbq = pa_memblockq_get_length(i->thread_info.render_memblockq);
+ else
+ lbq = 0;
+
+ /* Check if rewinding for the maximum is requested, and if so, fix up */
+ if (nbytes <= 0) {
+
+ /* Calculate maximum number of bytes that could be rewound in theory */
+ nbytes = i->sink->thread_info.max_rewind + lbq;
+
+ /* Transform from sink domain */
+ if (i->thread_info.resampler)
+ nbytes = pa_resampler_request(i->thread_info.resampler, nbytes);
}
-
- /* Notify everyone */
- pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
- pa_sink_notify(i->sink);
- /* Ok, no let's feed the precomputed buffer to the old sink */
- if (buffer)
- pa_play_memblockq(origin, "Ghost Stream", &origin->sample_spec, &origin->channel_map, buffer, NULL);
+ if (rewrite) {
+ /* Make sure to not overwrite over underruns */
+ if (nbytes > i->thread_info.playing_for)
+ nbytes = (size_t) i->thread_info.playing_for;
- return 0;
+ i->thread_info.rewrite_nbytes = nbytes;
+ } else
+ i->thread_info.rewrite_nbytes = (size_t) -1;
+
+ i->thread_info.rewrite_flush = flush && i->thread_info.rewrite_nbytes != 0;
+
+ /* Transform to sink domain */
+ if (i->thread_info.resampler)
+ nbytes = pa_resampler_result(i->thread_info.resampler, nbytes);
+
+ if (nbytes > lbq)
+ pa_sink_request_rewind(i->sink, nbytes - lbq);
+}
+
+/* Called from main context */
+pa_memchunk* pa_sink_input_get_silence(pa_sink_input *i, pa_memchunk *ret) {
+ pa_sink_input_assert_ref(i);
+ pa_assert(ret);
+
+ pa_silence_memchunk_get(
+ &i->sink->core->silence_cache,
+ i->sink->core->mempool,
+ ret,
+ &i->sample_spec,
+ i->thread_info.resampler ? pa_resampler_max_block_size(i->thread_info.resampler) : 0);
+
+ return ret;
}
diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
index 2943dfae..c07a7404 100644
--- a/src/pulsecore/sink-input.h
+++ b/src/pulsecore/sink-input.h
@@ -1,21 +1,22 @@
-#ifndef foosinkinputhfoo
-#define foosinkinputhfoo
-
-/* $Id$ */
+#ifndef foopulsesinkinputhfoo
+#define foopulsesinkinputhfoo
/***
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
@@ -36,106 +37,271 @@ typedef struct pa_sink_input pa_sink_input;
#include <pulsecore/core.h>
typedef enum pa_sink_input_state {
- PA_SINK_INPUT_RUNNING, /*< The stream is alive and kicking */
+ PA_SINK_INPUT_INIT, /*< The stream is not active yet, because pa_sink_put() has not been called yet */
PA_SINK_INPUT_DRAINED, /*< The stream stopped playing because there was no data to play */
+ PA_SINK_INPUT_RUNNING, /*< The stream is alive and kicking */
PA_SINK_INPUT_CORKED, /*< The stream was corked on user request */
- PA_SINK_INPUT_DISCONNECTED /*< The stream is dead */
+ PA_SINK_INPUT_UNLINKED /*< The stream is dead */
} pa_sink_input_state_t;
+static inline pa_bool_t PA_SINK_INPUT_IS_LINKED(pa_sink_input_state_t x) {
+ return x == PA_SINK_INPUT_DRAINED || x == PA_SINK_INPUT_RUNNING || x == PA_SINK_INPUT_CORKED;
+}
+
typedef enum pa_sink_input_flags {
PA_SINK_INPUT_VARIABLE_RATE = 1,
- PA_SINK_INPUT_NO_HOOKS = 2
+ PA_SINK_INPUT_DONT_MOVE = 2,
+ PA_SINK_INPUT_START_CORKED = 4,
+ PA_SINK_INPUT_NO_REMAP = 8,
+ PA_SINK_INPUT_NO_REMIX = 16,
+ PA_SINK_INPUT_FIX_FORMAT = 32,
+ PA_SINK_INPUT_FIX_RATE = 64,
+ PA_SINK_INPUT_FIX_CHANNELS = 128
} pa_sink_input_flags_t;
struct pa_sink_input {
- int ref;
+ pa_msgobject parent;
+
uint32_t index;
+ pa_core *core;
+
+ /* Please note that this state should only be read with
+ * pa_sink_input_get_state(). That function will transparently
+ * merge the thread_info.drained value in. */
pa_sink_input_state_t state;
pa_sink_input_flags_t flags;
-
- char *name, *driver; /* may be NULL */
- pa_module *module; /* may be NULL */
- pa_client *client; /* may be NULL */
+
+ char *driver; /* may be NULL */
+ pa_proplist *proplist;
+
+ pa_module *module; /* may be NULL */
+ pa_client *client; /* may be NULL */
pa_sink *sink;
-
+
+ /* A sink input may be connected to multiple source outputs
+ * directly, so that they don't get mixed data of the entire
+ * source. */
+ pa_idxset *direct_outputs;
+
pa_sample_spec sample_spec;
pa_channel_map channel_map;
+
+ pa_sink_input *sync_prev, *sync_next;
+
pa_cvolume volume;
+ pa_bool_t muted;
+
+ pa_resample_method_t resample_method;
- /* Some silence to play before the actual data. This is used to
- * compensate for latency differences when moving a sink input
- * "hot" between sinks. */
- size_t move_silence;
-
- int (*peek) (pa_sink_input *i, pa_memchunk *chunk);
- void (*drop) (pa_sink_input *i, const pa_memchunk *chunk, size_t length);
- void (*kill) (pa_sink_input *i); /* may be NULL */
+ /* Returns the chunk of audio data and drops it from the
+ * queue. Returns -1 on failure. Called from IO thread context. If
+ * data needs to be generated from scratch then please in the
+ * specified length request_nbytes. This is an optimization
+ * only. If less data is available, it's fine to return a smaller
+ * block. If more data is already ready, it is better to return
+ * the full block. */
+ int (*pop) (pa_sink_input *i, size_t request_nbytes, pa_memchunk *chunk); /* may NOT be NULL */
+
+ /* Rewind the queue by the specified number of bytes. Called just
+ * before peek() if it is called at all. Only called if the sink
+ * input driver ever plans to call
+ * pa_sink_input_request_rewind(). Called from IO context. */
+ void (*process_rewind) (pa_sink_input *i, size_t nbytes); /* may NOT be NULL */
+
+ /* Called whenever the maximum rewindable size of the sink
+ * changes. Called from IO context. */
+ void (*update_max_rewind) (pa_sink_input *i, size_t nbytes); /* may be NULL */
+
+ /* Called whenever the maxiumum request size of the sink
+ * changes. Called from IO context. */
+ void (*update_max_request) (pa_sink_input *i, size_t nbytes); /* may be NULL */
+
+ /* Called whenever the configured latency of the sink
+ * changes. Called from IO context. */
+ void (*update_sink_requested_latency) (pa_sink_input *i); /* may be NULL */
+
+ /* Called whenver the latency range of the sink changes. Called
+ * from IO context. */
+ void (*update_sink_latency_range) (pa_sink_input *i); /* may be NULL */
+
+ /* If non-NULL this function is called when the input is first
+ * connected to a sink or when the rtpoll/asyncmsgq fields
+ * change. You usually don't need to implement this function
+ * unless you rewrite a sink that is piggy-backed onto
+ * another. Called from IO thread context */
+ void (*attach) (pa_sink_input *i); /* may be NULL */
+
+ /* If non-NULL this function is called when the output is
+ * disconnected from its sink. Called from IO thread context */
+ void (*detach) (pa_sink_input *i); /* may be NULL */
+
+ /* If non-NULL called whenever the the sink this input is attached
+ * to suspends or resumes. Called from main context */
+ void (*suspend) (pa_sink_input *i, pa_bool_t b); /* may be NULL */
+
+ /* If non-NULL called whenever the the sink this input is attached
+ * to changes. Called from main context */
+ void (*moved) (pa_sink_input *i); /* may be NULL */
+
+ /* Supposed to unlink and destroy this stream. Called from main
+ * context. */
+ void (*kill) (pa_sink_input *i); /* may NOT be NULL */
+
+ /* Return the current latency (i.e. length of bufferd audio) of
+ this stream. Called from main context. This is added to what the
+ PA_SINK_INPUT_MESSAGE_GET_LATENCY message sent to the IO thread
+ returns */
pa_usec_t (*get_latency) (pa_sink_input *i); /* may be NULL */
- void (*underrun) (pa_sink_input *i); /* may be NULL */
- void *userdata;
+ /* If non_NULL this function is called from thread context if the
+ * state changes. The old state is found in thread_info.state. */
+ void (*state_change) (pa_sink_input *i, pa_sink_input_state_t state); /* may be NULL */
- pa_memchunk resampled_chunk;
- pa_resampler *resampler; /* may be NULL */
+ struct {
+ pa_sink_input_state_t state;
+ pa_atomic_t drained;
- pa_resample_method_t resample_method;
+ pa_bool_t attached; /* True only between ->attach() and ->detach() calls */
+
+ pa_sample_spec sample_spec;
+
+ pa_resampler *resampler; /* may be NULL */
+
+ /* We maintain a history of resampled audio data here. */
+ pa_memblockq *render_memblockq;
+
+ size_t rewrite_nbytes;
+ pa_bool_t rewrite_flush;
+ uint64_t underrun_for, playing_for;
- pa_memblock *silence_memblock; /* may be NULL */
+ pa_sink_input *sync_prev, *sync_next;
+
+ pa_cvolume volume;
+ pa_bool_t muted;
+
+ /* The requested latency for the sink */
+ pa_usec_t requested_sink_latency;
+
+ pa_hashmap *direct_outputs;
+ } thread_info;
+
+ void *userdata;
+};
+
+PA_DECLARE_CLASS(pa_sink_input);
+#define PA_SINK_INPUT(o) pa_sink_input_cast(o)
+
+enum {
+ PA_SINK_INPUT_MESSAGE_SET_VOLUME,
+ PA_SINK_INPUT_MESSAGE_SET_MUTE,
+ PA_SINK_INPUT_MESSAGE_GET_LATENCY,
+ PA_SINK_INPUT_MESSAGE_SET_RATE,
+ PA_SINK_INPUT_MESSAGE_SET_STATE,
+ PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY,
+ PA_SINK_INPUT_MESSAGE_GET_REQUESTED_LATENCY,
+ PA_SINK_INPUT_MESSAGE_MAX
};
typedef struct pa_sink_input_new_data {
- const char *name, *driver;
+ pa_proplist *proplist;
+
+ const char *driver;
pa_module *module;
pa_client *client;
-
+
pa_sink *sink;
-
+
pa_sample_spec sample_spec;
- int sample_spec_is_set;
+ pa_bool_t sample_spec_is_set;
pa_channel_map channel_map;
- int channel_map_is_set;
+ pa_bool_t channel_map_is_set;
+
pa_cvolume volume;
- int volume_is_set;
-
+ pa_bool_t volume_is_set;
+ pa_bool_t muted;
+ pa_bool_t muted_is_set;
+
pa_resample_method_t resample_method;
+
+ pa_sink_input *sync_base;
} pa_sink_input_new_data;
pa_sink_input_new_data* pa_sink_input_new_data_init(pa_sink_input_new_data *data);
void pa_sink_input_new_data_set_sample_spec(pa_sink_input_new_data *data, const pa_sample_spec *spec);
void pa_sink_input_new_data_set_channel_map(pa_sink_input_new_data *data, const pa_channel_map *map);
void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cvolume *volume);
+void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mute);
+void pa_sink_input_new_data_done(pa_sink_input_new_data *data);
+
+typedef struct pa_sink_input_move_hook_data {
+ pa_sink_input *sink_input;
+ pa_sink *destination;
+} pa_sink_input_move_hook_data;
+
+/* To be called by the implementing module only */
pa_sink_input* pa_sink_input_new(
pa_core *core,
pa_sink_input_new_data *data,
pa_sink_input_flags_t flags);
-void pa_sink_input_unref(pa_sink_input* i);
-pa_sink_input* pa_sink_input_ref(pa_sink_input* i);
+void pa_sink_input_put(pa_sink_input *i);
+void pa_sink_input_unlink(pa_sink_input* i);
-/* To be called by the implementing module only */
-void pa_sink_input_disconnect(pa_sink_input* i);
+void pa_sink_input_set_name(pa_sink_input *i, const char *name);
-/* External code may request disconnection with this funcion */
-void pa_sink_input_kill(pa_sink_input*i);
+pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec);
-pa_usec_t pa_sink_input_get_latency(pa_sink_input *i);
+/* Request that the specified number of bytes already written out to
+the hw device is rewritten, if possible. Please note that this is
+only a kind request. The sink driver may not be able to fulfill it
+fully -- or at all. If the request for a rewrite was successful, the
+sink driver will call ->rewind() and pass the number of bytes that
+could be rewound in the HW device. This functionality is required for
+implementing the "zero latency" write-through functionality. */
+void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes, pa_bool_t rewrite, pa_bool_t flush);
-int pa_sink_input_peek(pa_sink_input *i, pa_memchunk *chunk, pa_cvolume *volume);
-void pa_sink_input_drop(pa_sink_input *i, const pa_memchunk *chunk, size_t length);
+void pa_sink_input_cork(pa_sink_input *i, pa_bool_t b);
-void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume);
-const pa_cvolume * pa_sink_input_get_volume(pa_sink_input *i);
+int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate);
-void pa_sink_input_cork(pa_sink_input *i, int b);
+/* Callable by everyone from main thread*/
-void pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate);
+/* External code may request disconnection with this function */
+void pa_sink_input_kill(pa_sink_input*i);
-void pa_sink_input_set_name(pa_sink_input *i, const char *name);
+pa_usec_t pa_sink_input_get_latency(pa_sink_input *i, pa_usec_t *sink_latency);
+
+void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume);
+const pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i);
+void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute);
+pa_bool_t pa_sink_input_get_mute(pa_sink_input *i);
pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i);
-int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately);
+int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest);
+
+pa_sink_input_state_t pa_sink_input_get_state(pa_sink_input *i);
+
+pa_usec_t pa_sink_input_get_requested_latency(pa_sink_input *i);
+
+/* To be used exclusively by the sink driver IO thread */
+
+int pa_sink_input_peek(pa_sink_input *i, size_t length, pa_memchunk *chunk, pa_cvolume *volume);
+void pa_sink_input_drop(pa_sink_input *i, size_t length);
+void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */);
+void pa_sink_input_update_max_rewind(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */);
+void pa_sink_input_update_max_request(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */);
+
+void pa_sink_input_set_state_within_thread(pa_sink_input *i, pa_sink_input_state_t state);
+
+int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
+
+pa_usec_t pa_sink_input_set_requested_latency_within_thread(pa_sink_input *i, pa_usec_t usec);
+
+pa_bool_t pa_sink_input_safe_to_remove(pa_sink_input *i);
+
+pa_memchunk* pa_sink_input_get_silence(pa_sink_input *i, pa_memchunk *ret);
#endif
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index 04795e39..edb023b2 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -1,18 +1,19 @@
-/* $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
@@ -24,13 +25,13 @@
#endif
#include <stdlib.h>
-#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <pulse/introspect.h>
#include <pulse/utf8.h>
#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
#include <pulsecore/sink-input.h>
#include <pulsecore/namereg.h>
@@ -38,254 +39,609 @@
#include <pulsecore/sample-util.h>
#include <pulsecore/core-subscribe.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/play-memblockq.h>
#include "sink.h"
#define MAX_MIX_CHANNELS 32
+#define MIX_BUFFER_LENGTH (PA_PAGE_SIZE)
+#define DEFAULT_MIN_LATENCY (4*PA_USEC_PER_MSEC)
+
+static PA_DEFINE_CHECK_TYPE(pa_sink, pa_msgobject);
+
+static void sink_free(pa_object *s);
+
+pa_sink_new_data* pa_sink_new_data_init(pa_sink_new_data *data) {
+ pa_assert(data);
+
+ memset(data, 0, sizeof(*data));
+ data->proplist = pa_proplist_new();
+
+ return data;
+}
+
+void pa_sink_new_data_set_name(pa_sink_new_data *data, const char *name) {
+ pa_assert(data);
+
+ pa_xfree(data->name);
+ data->name = pa_xstrdup(name);
+}
+
+void pa_sink_new_data_set_sample_spec(pa_sink_new_data *data, const pa_sample_spec *spec) {
+ pa_assert(data);
+
+ if ((data->sample_spec_is_set = !!spec))
+ data->sample_spec = *spec;
+}
+
+void pa_sink_new_data_set_channel_map(pa_sink_new_data *data, const pa_channel_map *map) {
+ pa_assert(data);
+
+ if ((data->channel_map_is_set = !!map))
+ data->channel_map = *map;
+}
+
+void pa_sink_new_data_set_volume(pa_sink_new_data *data, const pa_cvolume *volume) {
+ pa_assert(data);
+
+ if ((data->volume_is_set = !!volume))
+ data->volume = *volume;
+}
+
+void pa_sink_new_data_set_muted(pa_sink_new_data *data, pa_bool_t mute) {
+ pa_assert(data);
+
+ data->muted_is_set = TRUE;
+ data->muted = !!mute;
+}
+
+void pa_sink_new_data_done(pa_sink_new_data *data) {
+ pa_assert(data);
+
+ pa_xfree(data->name);
+ pa_proplist_free(data->proplist);
+}
-#define CHECK_VALIDITY_RETURN_NULL(condition) \
-do {\
-if (!(condition)) \
- return NULL; \
-} while (0)
+/* Called from main context */
+static void reset_callbacks(pa_sink *s) {
+ pa_assert(s);
+
+ s->set_state = NULL;
+ s->get_volume = NULL;
+ s->set_volume = NULL;
+ s->get_mute = NULL;
+ s->set_mute = NULL;
+ s->request_rewind = NULL;
+ s->update_requested_latency = NULL;
+}
+/* Called from main context */
pa_sink* pa_sink_new(
pa_core *core,
- const char *driver,
- const char *name,
- int fail,
- const pa_sample_spec *spec,
- const pa_channel_map *map) {
-
+ pa_sink_new_data *data,
+ pa_sink_flags_t flags) {
+
pa_sink *s;
- char *n = NULL;
- char st[256];
- int r;
- pa_channel_map tmap;
-
- assert(core);
- assert(name);
- assert(spec);
-
- CHECK_VALIDITY_RETURN_NULL(pa_sample_spec_valid(spec));
-
- if (!map)
- map = pa_channel_map_init_auto(&tmap, spec->channels, PA_CHANNEL_MAP_DEFAULT);
-
- CHECK_VALIDITY_RETURN_NULL(map && pa_channel_map_valid(map));
- CHECK_VALIDITY_RETURN_NULL(map->channels == spec->channels);
- CHECK_VALIDITY_RETURN_NULL(!driver || pa_utf8_valid(driver));
- CHECK_VALIDITY_RETURN_NULL(pa_utf8_valid(name) && *name);
-
- s = pa_xnew(pa_sink, 1);
-
- if (!(name = pa_namereg_register(core, name, PA_NAMEREG_SINK, s, fail))) {
+ const char *name;
+ char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+ pa_source_new_data source_data;
+ const char *dn;
+
+ pa_assert(core);
+ pa_assert(data);
+ pa_assert(data->name);
+
+ s = pa_msgobject_new(pa_sink);
+
+ if (!(name = pa_namereg_register(core, data->name, PA_NAMEREG_SINK, s, data->namereg_fail))) {
+ pa_xfree(s);
+ return NULL;
+ }
+
+ pa_sink_new_data_set_name(data, name);
+
+ if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_NEW], data) < 0) {
+ pa_xfree(s);
+ pa_namereg_unregister(core, name);
+ return NULL;
+ }
+
+ pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver));
+ pa_return_null_if_fail(data->name && pa_utf8_valid(data->name) && data->name[0]);
+
+ pa_return_null_if_fail(data->sample_spec_is_set && pa_sample_spec_valid(&data->sample_spec));
+
+ if (!data->channel_map_is_set)
+ pa_return_null_if_fail(pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT));
+
+ pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map));
+ pa_return_null_if_fail(data->channel_map.channels == data->sample_spec.channels);
+
+ if (!data->volume_is_set)
+ pa_cvolume_reset(&data->volume, data->sample_spec.channels);
+
+ pa_return_null_if_fail(pa_cvolume_valid(&data->volume));
+ pa_return_null_if_fail(data->volume.channels == data->sample_spec.channels);
+
+ if (!data->muted_is_set)
+ data->muted = FALSE;
+
+ if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_FIXATE], data) < 0) {
pa_xfree(s);
+ pa_namereg_unregister(core, name);
return NULL;
}
- s->ref = 1;
+ s->parent.parent.free = sink_free;
+ s->parent.process_msg = pa_sink_process_msg;
+
s->core = core;
- s->state = PA_SINK_RUNNING;
+ s->state = PA_SINK_INIT;
+ s->flags = flags;
s->name = pa_xstrdup(name);
- s->description = NULL;
- s->driver = pa_xstrdup(driver);
- s->owner = NULL;
+ s->proplist = pa_proplist_copy(data->proplist);
+ s->driver = pa_xstrdup(data->driver);
+ s->module = data->module;
+
+ s->sample_spec = data->sample_spec;
+ s->channel_map = data->channel_map;
- s->sample_spec = *spec;
- s->channel_map = *map;
-
s->inputs = pa_idxset_new(NULL, NULL);
+ s->n_corked = 0;
+
+ s->volume = data->volume;
+ s->muted = data->muted;
+ s->refresh_volume = s->refresh_muted = FALSE;
- pa_cvolume_reset(&s->sw_volume, spec->channels);
- pa_cvolume_reset(&s->hw_volume, spec->channels);
- s->sw_muted = 0;
- s->hw_muted = 0;
-
- s->is_hardware = 0;
-
- s->get_latency = NULL;
- s->notify = NULL;
- s->set_hw_volume = NULL;
- s->get_hw_volume = NULL;
- s->set_hw_mute = NULL;
- s->get_hw_mute = NULL;
+ reset_callbacks(s);
s->userdata = NULL;
- r = pa_idxset_put(core->sinks, s, &s->index);
- assert(s->index != PA_IDXSET_INVALID && r >= 0);
-
- pa_sample_spec_snprint(st, sizeof(st), spec);
- pa_log_info("created %u \"%s\" with sample spec \"%s\"", s->index, s->name, st);
-
- n = pa_sprintf_malloc("%s.monitor", name);
-
- if (!(s->monitor_source = pa_source_new(core, driver, n, 0, spec, map)))
- pa_log_warn("failed to create monitor source.");
- else {
- char *d;
- s->monitor_source->monitor_of = s;
- d = pa_sprintf_malloc("Monitor Source of %s", s->name);
- pa_source_set_description(s->monitor_source, d);
- pa_xfree(d);
+ s->asyncmsgq = NULL;
+ s->rtpoll = NULL;
+
+ pa_silence_memchunk_get(
+ &core->silence_cache,
+ core->mempool,
+ &s->silence,
+ &s->sample_spec,
+ 0);
+
+ s->thread_info.inputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+ pa_cvolume_reset(&s->thread_info.soft_volume, s->sample_spec.channels);
+ s->thread_info.soft_muted = FALSE;
+ s->thread_info.state = s->state;
+ s->thread_info.rewind_nbytes = 0;
+ s->thread_info.max_rewind = 0;
+ s->thread_info.max_request = 0;
+ s->thread_info.requested_latency_valid = FALSE;
+ s->thread_info.requested_latency = 0;
+ s->thread_info.min_latency = DEFAULT_MIN_LATENCY;
+ s->thread_info.max_latency = 0;
+
+ pa_assert_se(pa_idxset_put(core->sinks, s, &s->index) >= 0);
+
+ pa_log_info("Created sink %u \"%s\" with sample spec %s and channel map %s",
+ s->index,
+ s->name,
+ pa_sample_spec_snprint(st, sizeof(st), &s->sample_spec),
+ pa_channel_map_snprint(cm, sizeof(cm), &s->channel_map));
+
+ pa_source_new_data_init(&source_data);
+ pa_source_new_data_set_sample_spec(&source_data, &s->sample_spec);
+ pa_source_new_data_set_channel_map(&source_data, &s->channel_map);
+ source_data.name = pa_sprintf_malloc("%s.monitor", name);
+ source_data.driver = data->driver;
+ source_data.module = data->module;
+
+ dn = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
+ pa_proplist_setf(source_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Monitor of %s", dn ? dn : s->name);
+ pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_CLASS, "monitor");
+
+ s->monitor_source = pa_source_new(core, &source_data, 0);
+
+ pa_source_new_data_done(&source_data);
+
+ if (!s->monitor_source) {
+ pa_sink_unlink(s);
+ pa_sink_unref(s);
+ return NULL;
}
- pa_xfree(n);
-
- pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_NEW, s->index);
-
+ s->monitor_source->monitor_of = s;
+
+ pa_source_set_latency_range(s->monitor_source, s->thread_info.min_latency, s->thread_info.max_latency);
+ pa_source_set_max_rewind(s->monitor_source, s->thread_info.max_rewind);
+
return s;
}
-void pa_sink_disconnect(pa_sink* s) {
+/* Called from main context */
+static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
+ int ret;
+ pa_bool_t suspend_change;
+
+ pa_assert(s);
+
+ if (s->state == state)
+ return 0;
+
+ suspend_change =
+ (s->state == PA_SINK_SUSPENDED && PA_SINK_IS_OPENED(state)) ||
+ (PA_SINK_IS_OPENED(s->state) && state == PA_SINK_SUSPENDED);
+
+ if (s->set_state)
+ if ((ret = s->set_state(s, state)) < 0)
+ return -1;
+
+ if (s->asyncmsgq)
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) == 0);
+
+ s->state = state;
+
+ if (suspend_change) {
+ pa_sink_input *i;
+ uint32_t idx;
+
+ /* We're suspending or resuming, tell everyone about it */
+
+ for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx)))
+ if (i->suspend)
+ i->suspend(i, state == PA_SINK_SUSPENDED);
+ }
+
+ if (state != PA_SINK_UNLINKED) /* if we enter UNLINKED state pa_sink_unlink() will fire the apropriate events */
+ pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED], s);
+
+ return 0;
+}
+
+/* Called from main context */
+void pa_sink_put(pa_sink* s) {
+ pa_sink_assert_ref(s);
+
+ pa_assert(s->state == PA_SINK_INIT);
+
+ /* The following fields must be initialized properly when calling _put() */
+ pa_assert(s->asyncmsgq);
+ pa_assert(s->rtpoll);
+ pa_assert(!s->thread_info.min_latency || !s->thread_info.max_latency ||
+ s->thread_info.min_latency <= s->thread_info.max_latency);
+
+ if (!(s->flags & PA_SINK_HW_VOLUME_CTRL)) {
+ s->flags |= PA_SINK_DECIBEL_VOLUME;
+
+ s->thread_info.soft_volume = s->volume;
+ s->thread_info.soft_muted = s->muted;
+ }
+
+ pa_assert_se(sink_set_state(s, PA_SINK_IDLE) == 0);
+
+ pa_source_put(s->monitor_source);
+
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_NEW, s->index);
+ pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PUT], s);
+}
+
+/* Called from main context */
+void pa_sink_unlink(pa_sink* s) {
+ pa_bool_t linked;
pa_sink_input *i, *j = NULL;
-
- assert(s);
- assert(s->state == PA_SINK_RUNNING);
- s->state = PA_SINK_DISCONNECTED;
- pa_namereg_unregister(s->core, s->name);
+ pa_assert(s);
+
+ /* Please note that pa_sink_unlink() does more than simply
+ * reversing pa_sink_put(). It also undoes the registrations
+ * already done in pa_sink_new()! */
+
+ /* All operations here shall be idempotent, i.e. pa_sink_unlink()
+ * may be called multiple times on the same sink without bad
+ * effects. */
+
+ linked = PA_SINK_IS_LINKED(s->state);
+
+ if (linked)
+ pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_UNLINK], s);
+
+ if (s->state != PA_SINK_UNLINKED)
+ pa_namereg_unregister(s->core, s->name);
+ pa_idxset_remove_by_data(s->core->sinks, s, NULL);
- pa_hook_fire(&s->core->hook_sink_disconnect, s);
-
while ((i = pa_idxset_first(s->inputs, NULL))) {
- assert(i != j);
+ pa_assert(i != j);
pa_sink_input_kill(i);
j = i;
}
- if (s->monitor_source)
- pa_source_disconnect(s->monitor_source);
+ if (linked)
+ sink_set_state(s, PA_SINK_UNLINKED);
+ else
+ s->state = PA_SINK_UNLINKED;
- pa_idxset_remove_by_data(s->core->sinks, s, NULL);
+ reset_callbacks(s);
+
+ if (s->monitor_source)
+ pa_source_unlink(s->monitor_source);
- s->get_latency = NULL;
- s->notify = NULL;
- s->get_hw_volume = NULL;
- s->set_hw_volume = NULL;
- s->set_hw_mute = NULL;
- s->get_hw_mute = NULL;
-
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_REMOVE, s->index);
+ if (linked) {
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_REMOVE, s->index);
+ pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_UNLINK_POST], s);
+ }
}
-static void sink_free(pa_sink *s) {
- assert(s);
- assert(!s->ref);
-
- if (s->state != PA_SINK_DISCONNECTED)
- pa_sink_disconnect(s);
+/* Called from main context */
+static void sink_free(pa_object *o) {
+ pa_sink *s = PA_SINK(o);
+ pa_sink_input *i;
+
+ pa_assert(s);
+ pa_assert(pa_sink_refcnt(s) == 0);
- pa_log_info("freed %u \"%s\"", s->index, s->name);
+ if (PA_SINK_IS_LINKED(s->state))
+ pa_sink_unlink(s);
+
+ pa_log_info("Freeing sink %u \"%s\"", s->index, s->name);
if (s->monitor_source) {
pa_source_unref(s->monitor_source);
s->monitor_source = NULL;
}
-
+
pa_idxset_free(s->inputs, NULL, NULL);
+ while ((i = pa_hashmap_steal_first(s->thread_info.inputs)))
+ pa_sink_input_unref(i);
+
+ pa_hashmap_free(s->thread_info.inputs, NULL, NULL);
+
+ if (s->silence.memblock)
+ pa_memblock_unref(s->silence.memblock);
+
pa_xfree(s->name);
- pa_xfree(s->description);
pa_xfree(s->driver);
+
+ if (s->proplist)
+ pa_proplist_free(s->proplist);
+
pa_xfree(s);
}
-void pa_sink_unref(pa_sink*s) {
- assert(s);
- assert(s->ref >= 1);
+/* Called from main context */
+void pa_sink_set_asyncmsgq(pa_sink *s, pa_asyncmsgq *q) {
+ pa_sink_assert_ref(s);
+
+ s->asyncmsgq = q;
+
+ if (s->monitor_source)
+ pa_source_set_asyncmsgq(s->monitor_source, q);
+}
+
+/* Called from main context */
+void pa_sink_set_rtpoll(pa_sink *s, pa_rtpoll *p) {
+ pa_sink_assert_ref(s);
- if (!(--s->ref))
- sink_free(s);
+ s->rtpoll = p;
+ if (s->monitor_source)
+ pa_source_set_rtpoll(s->monitor_source, p);
}
-pa_sink* pa_sink_ref(pa_sink *s) {
- assert(s);
- assert(s->ref >= 1);
-
- s->ref++;
- return s;
+/* Called from main context */
+int pa_sink_update_status(pa_sink*s) {
+ pa_sink_assert_ref(s);
+ pa_assert(PA_SINK_IS_LINKED(s->state));
+
+ if (s->state == PA_SINK_SUSPENDED)
+ return 0;
+
+ return sink_set_state(s, pa_sink_used_by(s) ? PA_SINK_RUNNING : PA_SINK_IDLE);
}
-void pa_sink_notify(pa_sink*s) {
- assert(s);
- assert(s->ref >= 1);
+/* Called from main context */
+int pa_sink_suspend(pa_sink *s, pa_bool_t suspend) {
+ pa_sink_assert_ref(s);
+ pa_assert(PA_SINK_IS_LINKED(s->state));
+
+ if (suspend)
+ return sink_set_state(s, PA_SINK_SUSPENDED);
+ else
+ return sink_set_state(s, pa_sink_used_by(s) ? PA_SINK_RUNNING : PA_SINK_IDLE);
+}
+
+/* Called from IO thread context */
+void pa_sink_process_rewind(pa_sink *s, size_t nbytes) {
+ pa_sink_input *i;
+ void *state = NULL;
+ pa_sink_assert_ref(s);
+ pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
+
+ /* Make sure the sink code already reset the counter! */
+ pa_assert(s->thread_info.rewind_nbytes <= 0);
- if (s->notify)
- s->notify(s);
+ if (nbytes <= 0)
+ return;
+
+ pa_log_debug("Processing rewind...");
+
+ while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) {
+ pa_sink_input_assert_ref(i);
+ pa_sink_input_process_rewind(i, nbytes);
+ }
+
+ if (s->monitor_source && PA_SOURCE_IS_OPENED(s->monitor_source->thread_info.state))
+ pa_source_process_rewind(s->monitor_source, nbytes);
}
-static unsigned fill_mix_info(pa_sink *s, pa_mix_info *info, unsigned maxinfo) {
- uint32_t idx = PA_IDXSET_INVALID;
+/* Called from IO thread context */
+static unsigned fill_mix_info(pa_sink *s, size_t *length, pa_mix_info *info, unsigned maxinfo) {
pa_sink_input *i;
unsigned n = 0;
-
- assert(s);
- assert(s->ref >= 1);
- assert(info);
-
- for (i = pa_idxset_first(s->inputs, &idx); maxinfo > 0 && i; i = pa_idxset_next(s->inputs, &idx)) {
- /* Increase ref counter, to make sure that this input doesn't
- * vanish while we still need it */
- pa_sink_input_ref(i);
-
- if (pa_sink_input_peek(i, &info->chunk, &info->volume) < 0) {
- pa_sink_input_unref(i);
+ void *state = NULL;
+ size_t mixlength = *length;
+
+ pa_sink_assert_ref(s);
+ pa_assert(info);
+
+ while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)) && maxinfo > 0) {
+ pa_sink_input_assert_ref(i);
+
+ if (pa_sink_input_peek(i, *length, &info->chunk, &info->volume) < 0)
+ continue;
+
+ if (mixlength == 0 || info->chunk.length < mixlength)
+ mixlength = info->chunk.length;
+
+ if (pa_memblock_is_silence(info->chunk.memblock)) {
+ pa_memblock_unref(info->chunk.memblock);
continue;
}
- info->userdata = i;
-
- assert(info->chunk.memblock);
- assert(info->chunk.length);
-
+ info->userdata = pa_sink_input_ref(i);
+
+ pa_assert(info->chunk.memblock);
+ pa_assert(info->chunk.length > 0);
+
info++;
- maxinfo--;
n++;
+ maxinfo--;
}
+ if (mixlength > 0)
+ *length = mixlength;
+
return n;
}
-static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned maxinfo, size_t length) {
- assert(s);
- assert(s->ref >= 1);
- assert(info);
+/* Called from IO thread context */
+static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned n, pa_memchunk *result) {
+ pa_sink_input *i;
+ void *state = NULL;
+ unsigned p = 0;
+ unsigned n_unreffed = 0;
+
+ pa_sink_assert_ref(s);
+ pa_assert(result);
+ pa_assert(result->memblock);
+ pa_assert(result->length > 0);
+
+ /* We optimize for the case where the order of the inputs has not changed */
+
+ while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) {
+ unsigned j;
+ pa_mix_info* m = NULL;
+
+ pa_sink_input_assert_ref(i);
- for (; maxinfo > 0; maxinfo--, info++) {
- pa_sink_input *i = info->userdata;
-
- assert(i);
- assert(info->chunk.memblock);
+ /* Let's try to find the matching entry info the pa_mix_info array */
+ for (j = 0; j < n; j ++) {
+
+ if (info[p].userdata == i) {
+ m = info + p;
+ break;
+ }
+
+ p++;
+ if (p >= n)
+ p = 0;
+ }
/* Drop read data */
- pa_sink_input_drop(i, &info->chunk, length);
- pa_memblock_unref(info->chunk.memblock);
+ pa_sink_input_drop(i, result->length);
+
+ if (s->monitor_source && PA_SOURCE_IS_OPENED(pa_source_get_state(s->monitor_source))) {
+
+ if (pa_hashmap_size(i->thread_info.direct_outputs) > 0) {
+ void *ostate = NULL;
+ pa_source_output *o;
+ pa_memchunk c;
+
+ if (m && m->chunk.memblock) {
+ c = m->chunk;
+ pa_memblock_ref(c.memblock);
+ pa_assert(result->length <= c.length);
+ c.length = result->length;
+
+ pa_memchunk_make_writable(&c, 0);
+ pa_volume_memchunk(&c, &s->sample_spec, &m->volume);
+ } else {
+ c = s->silence;
+ pa_memblock_ref(c.memblock);
+ pa_assert(result->length <= c.length);
+ c.length = result->length;
+ }
+
+ while ((o = pa_hashmap_iterate(i->thread_info.direct_outputs, &ostate, NULL))) {
+ pa_source_output_assert_ref(o);
+ pa_assert(o->direct_on_input == i);
+ pa_source_post_direct(s->monitor_source, o, &c);
+ }
+
+ pa_memblock_unref(c.memblock);
+ }
+ }
- /* Decrease ref counter */
- pa_sink_input_unref(i);
- info->userdata = NULL;
+ if (m) {
+ if (m->chunk.memblock)
+ pa_memblock_unref(m->chunk.memblock);
+ pa_memchunk_reset(&m->chunk);
+
+ pa_sink_input_unref(m->userdata);
+ m->userdata = NULL;
+
+ n_unreffed += 1;
+ }
}
+
+ /* Now drop references to entries that are included in the
+ * pa_mix_info array but don't exist anymore */
+
+ if (n_unreffed < n) {
+ for (; n > 0; info++, n--) {
+ if (info->userdata)
+ pa_sink_input_unref(info->userdata);
+ if (info->chunk.memblock)
+ pa_memblock_unref(info->chunk.memblock);
+ }
+ }
+
+ if (s->monitor_source && PA_SOURCE_IS_OPENED(pa_source_get_state(s->monitor_source)))
+ pa_source_post(s->monitor_source, result);
}
-
-int pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
+
+/* Called from IO thread context */
+void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
pa_mix_info info[MAX_MIX_CHANNELS];
unsigned n;
- int r = -1;
-
- assert(s);
- assert(s->ref >= 1);
- assert(length);
- assert(result);
+ size_t block_size_max;
+
+ pa_sink_assert_ref(s);
+ pa_assert(PA_SINK_IS_OPENED(s->thread_info.state));
+ pa_assert(pa_frame_aligned(length, &s->sample_spec));
+ pa_assert(result);
pa_sink_ref(s);
-
- n = fill_mix_info(s, info, MAX_MIX_CHANNELS);
- if (n <= 0)
- goto finish;
+ s->thread_info.rewind_nbytes = 0;
+
+ if (length <= 0)
+ length = pa_frame_align(MIX_BUFFER_LENGTH, &s->sample_spec);
+
+ block_size_max = pa_mempool_block_size_max(s->core->mempool);
+ if (length > block_size_max)
+ length = pa_frame_align(block_size_max, &s->sample_spec);
+
+ pa_assert(length > 0);
+
+ n = s->thread_info.state == PA_SINK_RUNNING ? fill_mix_info(s, &length, info, MAX_MIX_CHANNELS) : 0;
+
+ if (n == 0) {
- if (n == 1) {
+ *result = s->silence;
+ pa_memblock_ref(result->memblock);
+
+ if (result->length > length)
+ result->length = length;
+
+ } else if (n == 1) {
pa_cvolume volume;
*result = info[0].chunk;
@@ -294,11 +650,12 @@ int pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
if (result->length > length)
result->length = length;
- pa_sw_cvolume_multiply(&volume, &s->sw_volume, &info[0].volume);
-
- if (s->sw_muted || !pa_cvolume_is_norm(&volume)) {
+ pa_sw_cvolume_multiply(&volume, &s->thread_info.soft_volume, &info[0].volume);
+
+ if (s->thread_info.soft_muted || !pa_cvolume_is_norm(&volume)) {
+ pa_log("adjusting volume ");
pa_memchunk_make_writable(result, 0);
- if (s->sw_muted)
+ if (s->thread_info.soft_muted || pa_cvolume_is_muted(&volume))
pa_silence_memchunk(result, &s->sample_spec);
else
pa_volume_memchunk(result, &s->sample_spec, &volume);
@@ -306,279 +663,869 @@ int pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
} else {
void *ptr;
result->memblock = pa_memblock_new(s->core->mempool, length);
- assert(result->memblock);
-
-/* pa_log("mixing %i", n); */
ptr = pa_memblock_acquire(result->memblock);
- result->length = pa_mix(info, n, ptr, length, &s->sample_spec, &s->sw_volume, s->sw_muted);
+ result->length = pa_mix(info, n,
+ ptr, length,
+ &s->sample_spec,
+ &s->thread_info.soft_volume,
+ s->thread_info.soft_muted);
pa_memblock_release(result->memblock);
-
+
result->index = 0;
}
- inputs_drop(s, info, n, result->length);
+ if (s->thread_info.state == PA_SINK_RUNNING)
+ inputs_drop(s, info, n, result);
- if (s->monitor_source)
- pa_source_post(s->monitor_source, result);
-
- r = 0;
-
-finish:
pa_sink_unref(s);
-
- return r;
}
-int pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
+/* Called from IO thread context */
+void pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
pa_mix_info info[MAX_MIX_CHANNELS];
unsigned n;
- int r = -1;
- void *ptr;
-
- assert(s);
- assert(s->ref >= 1);
- assert(target);
- assert(target->memblock);
- assert(target->length);
+ size_t length, block_size_max;
+
+ pa_sink_assert_ref(s);
+ pa_assert(PA_SINK_IS_OPENED(s->thread_info.state));
+ pa_assert(target);
+ pa_assert(target->memblock);
+ pa_assert(target->length > 0);
+ pa_assert(pa_frame_aligned(target->length, &s->sample_spec));
pa_sink_ref(s);
-
- n = fill_mix_info(s, info, MAX_MIX_CHANNELS);
- if (n <= 0)
- goto finish;
+ s->thread_info.rewind_nbytes = 0;
+
+ length = target->length;
+ block_size_max = pa_mempool_block_size_max(s->core->mempool);
+ if (length > block_size_max)
+ length = pa_frame_align(block_size_max, &s->sample_spec);
+
+ pa_assert(length > 0);
- ptr = pa_memblock_acquire(target->memblock);
-
- if (n == 1) {
- void *src;
+ n = s->thread_info.state == PA_SINK_RUNNING ? fill_mix_info(s, &length, info, MAX_MIX_CHANNELS) : 0;
+
+ if (n == 0) {
+ if (target->length > length)
+ target->length = length;
+
+ pa_silence_memchunk(target, &s->sample_spec);
+ } else if (n == 1) {
pa_cvolume volume;
- if (target->length > info[0].chunk.length)
- target->length = info[0].chunk.length;
+ if (target->length > length)
+ target->length = length;
+
+ pa_sw_cvolume_multiply(&volume, &s->thread_info.soft_volume, &info[0].volume);
- src = pa_memblock_acquire(info[0].chunk.memblock);
-
- memcpy((uint8_t*) ptr + target->index,
- (uint8_t*) src + info[0].chunk.index,
- target->length);
+ if (s->thread_info.soft_muted || pa_cvolume_is_muted(&volume))
+ pa_silence_memchunk(target, &s->sample_spec);
+ else {
+ pa_memchunk vchunk;
- pa_memblock_release(info[0].chunk.memblock);
+ vchunk = info[0].chunk;
+ pa_memblock_ref(vchunk.memblock);
- pa_sw_cvolume_multiply(&volume, &s->sw_volume, &info[0].volume);
+ if (vchunk.length > length)
+ vchunk.length = length;
+
+ if (!pa_cvolume_is_norm(&volume)) {
+ pa_memchunk_make_writable(&vchunk, 0);
+ pa_volume_memchunk(&vchunk, &s->sample_spec, &volume);
+ }
+
+ pa_memchunk_memcpy(target, &vchunk);
+ pa_memblock_unref(vchunk.memblock);
+ }
+
+ } else {
+ void *ptr;
+
+ ptr = pa_memblock_acquire(target->memblock);
- if (s->sw_muted)
- pa_silence_memchunk(target, &s->sample_spec);
- else if (!pa_cvolume_is_norm(&volume))
- pa_volume_memchunk(target, &s->sample_spec, &volume);
- } else
target->length = pa_mix(info, n,
- (uint8_t*) ptr + target->index,
- target->length,
+ (uint8_t*) ptr + target->index, length,
&s->sample_spec,
- &s->sw_volume,
- s->sw_muted);
+ &s->thread_info.soft_volume,
+ s->thread_info.soft_muted);
- pa_memblock_release(target->memblock);
-
- inputs_drop(s, info, n, target->length);
-
- if (s->monitor_source)
- pa_source_post(s->monitor_source, target);
+ pa_memblock_release(target->memblock);
+ }
- r = 0;
+ if (s->thread_info.state == PA_SINK_RUNNING)
+ inputs_drop(s, info, n, target);
-finish:
pa_sink_unref(s);
-
- return r;
}
+/* Called from IO thread context */
void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) {
pa_memchunk chunk;
size_t l, d;
-
- assert(s);
- assert(s->ref >= 1);
- assert(target);
- assert(target->memblock);
- assert(target->length);
+
+ pa_sink_assert_ref(s);
+ pa_assert(PA_SINK_IS_OPENED(s->thread_info.state));
+ pa_assert(target);
+ pa_assert(target->memblock);
+ pa_assert(target->length > 0);
+ pa_assert(pa_frame_aligned(target->length, &s->sample_spec));
pa_sink_ref(s);
-
+
+ s->thread_info.rewind_nbytes = 0;
+
l = target->length;
d = 0;
while (l > 0) {
chunk = *target;
chunk.index += d;
chunk.length -= d;
-
- if (pa_sink_render_into(s, &chunk) < 0)
- break;
+
+ pa_sink_render_into(s, &chunk);
d += chunk.length;
l -= chunk.length;
}
- if (l > 0) {
- chunk = *target;
- chunk.index += d;
- chunk.length -= d;
- pa_silence_memchunk(&chunk, &s->sample_spec);
- }
-
pa_sink_unref(s);
}
+/* Called from IO thread context */
void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) {
- assert(s);
- assert(s->ref >= 1);
- assert(length);
- assert(result);
+ pa_sink_assert_ref(s);
+ pa_assert(PA_SINK_IS_OPENED(s->thread_info.state));
+ pa_assert(length > 0);
+ pa_assert(pa_frame_aligned(length, &s->sample_spec));
+ pa_assert(result);
+
+ s->thread_info.rewind_nbytes = 0;
/*** This needs optimization ***/
-
- result->memblock = pa_memblock_new(s->core->mempool, result->length = length);
+
result->index = 0;
+ result->length = length;
+ result->memblock = pa_memblock_new(s->core->mempool, length);
pa_sink_render_into_full(s, result);
}
+/* Called from main thread */
pa_usec_t pa_sink_get_latency(pa_sink *s) {
- assert(s);
- assert(s->ref >= 1);
+ pa_usec_t usec = 0;
+
+ pa_sink_assert_ref(s);
+ pa_assert(PA_SINK_IS_LINKED(s->state));
- if (!s->get_latency)
+ /* The returned value is supposed to be in the time domain of the sound card! */
+
+ if (!PA_SINK_IS_OPENED(s->state))
return 0;
- return s->get_latency(s);
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) == 0);
+
+ return usec;
}
-void pa_sink_set_owner(pa_sink *s, pa_module *m) {
- assert(s);
- assert(s->ref >= 1);
+/* Called from main thread */
+void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume) {
+ pa_bool_t changed;
- if (s->owner == m)
- return;
-
- s->owner = m;
+ pa_sink_assert_ref(s);
+ pa_assert(PA_SINK_IS_LINKED(s->state));
+ pa_assert(volume);
- if (s->monitor_source)
- pa_source_set_owner(s->monitor_source, m);
+ changed = !pa_cvolume_equal(volume, &s->volume);
+ s->volume = *volume;
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
-}
+ if (s->set_volume && s->set_volume(s) < 0)
+ s->set_volume = NULL;
-void pa_sink_set_volume(pa_sink *s, pa_mixer_t m, const pa_cvolume *volume) {
- pa_cvolume *v;
-
- assert(s);
- assert(s->ref >= 1);
- assert(volume);
+ if (!s->set_volume)
+ pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME, volume, 0, NULL);
- if (m == PA_MIXER_HARDWARE && s->set_hw_volume)
- v = &s->hw_volume;
- else
- v = &s->sw_volume;
+ if (changed)
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+}
- if (pa_cvolume_equal(v, volume))
- return;
-
- *v = *volume;
+/* Called from main thread */
+const pa_cvolume *pa_sink_get_volume(pa_sink *s) {
+ pa_sink_assert_ref(s);
+ pa_assert(PA_SINK_IS_LINKED(s->state));
- if (v == &s->hw_volume)
- if (s->set_hw_volume(s) < 0)
- s->sw_volume = *volume;
+ if (s->refresh_volume) {
+ struct pa_cvolume old_volume = s->volume;
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
-}
+ if (s->get_volume && s->get_volume(s) < 0)
+ s->get_volume = NULL;
-const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_mixer_t m) {
- assert(s);
- assert(s->ref >= 1);
+ if (!s->get_volume)
+ pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_VOLUME, &s->volume, 0, NULL);
- if (m == PA_MIXER_HARDWARE && s->set_hw_volume) {
+ if (!pa_cvolume_equal(&old_volume, &s->volume))
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+ }
- if (s->get_hw_volume)
- s->get_hw_volume(s);
-
- return &s->hw_volume;
- } else
- return &s->sw_volume;
+ return &s->volume;
}
-void pa_sink_set_mute(pa_sink *s, pa_mixer_t m, int mute) {
- int *t;
-
- assert(s);
- assert(s->ref >= 1);
+/* Called from main thread */
+void pa_sink_set_mute(pa_sink *s, pa_bool_t mute) {
+ pa_bool_t changed;
- if (m == PA_MIXER_HARDWARE && s->set_hw_mute)
- t = &s->hw_muted;
- else
- t = &s->sw_muted;
+ pa_sink_assert_ref(s);
+ pa_assert(PA_SINK_IS_LINKED(s->state));
- if (!!*t == !!mute)
- return;
-
- *t = !!mute;
+ changed = s->muted != mute;
+ s->muted = mute;
- if (t == &s->hw_muted)
- if (s->set_hw_mute(s) < 0)
- s->sw_muted = !!mute;
+ if (s->set_mute && s->set_mute(s) < 0)
+ s->set_mute = NULL;
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+ if (!s->set_mute)
+ pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MUTE, PA_UINT_TO_PTR(mute), 0, NULL, NULL);
+
+ if (changed)
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
}
-int pa_sink_get_mute(pa_sink *s, pa_mixer_t m) {
- assert(s);
- assert(s->ref >= 1);
+/* Called from main thread */
+pa_bool_t pa_sink_get_mute(pa_sink *s) {
+
+ pa_sink_assert_ref(s);
+ pa_assert(PA_SINK_IS_LINKED(s->state));
+
+ if (s->refresh_muted) {
+ pa_bool_t old_muted = s->muted;
+
+ if (s->get_mute && s->get_mute(s) < 0)
+ s->get_mute = NULL;
- if (m == PA_MIXER_HARDWARE && s->set_hw_mute) {
+ if (!s->get_mute)
+ pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MUTE, &s->muted, 0, NULL);
- if (s->get_hw_mute)
- s->get_hw_mute(s);
-
- return s->hw_muted;
- } else
- return s->sw_muted;
+ if (old_muted != s->muted)
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+ }
+
+ return s->muted;
}
+/* Called from main thread */
void pa_sink_set_description(pa_sink *s, const char *description) {
- assert(s);
- assert(s->ref >= 1);
+ const char *old;
+ pa_sink_assert_ref(s);
- if (!description && !s->description)
+ if (!description && !pa_proplist_contains(s->proplist, PA_PROP_DEVICE_DESCRIPTION))
return;
- if (description && s->description && !strcmp(description, s->description))
+ old = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
+
+ if (old && description && !strcmp(old, description))
return;
-
- pa_xfree(s->description);
- s->description = pa_xstrdup(description);
+
+ if (description)
+ pa_proplist_sets(s->proplist, PA_PROP_DEVICE_DESCRIPTION, description);
+ else
+ pa_proplist_unset(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
if (s->monitor_source) {
char *n;
-
- n = pa_sprintf_malloc("Monitor Source of %s", s->description? s->description : s->name);
+
+ n = pa_sprintf_malloc("Monitor Source of %s", description ? description : s->name);
pa_source_set_description(s->monitor_source, n);
pa_xfree(n);
}
-
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+
+ if (PA_SINK_IS_LINKED(s->state)) {
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+ pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], s);
+ }
}
-unsigned pa_sink_used_by(pa_sink *s) {
+/* Called from main thread */
+unsigned pa_sink_linked_by(pa_sink *s) {
unsigned ret;
- assert(s);
- assert(s->ref >= 1);
+ pa_sink_assert_ref(s);
+ pa_assert(PA_SINK_IS_LINKED(s->state));
ret = pa_idxset_size(s->inputs);
+ /* We add in the number of streams connected to us here. Please
+ * not the asymmmetry to pa_sink_used_by()! */
+
if (s->monitor_source)
- ret += pa_source_used_by(s->monitor_source);
+ ret += pa_source_linked_by(s->monitor_source);
+
+ return ret;
+}
+
+/* Called from main thread */
+unsigned pa_sink_used_by(pa_sink *s) {
+ unsigned ret;
+
+ pa_sink_assert_ref(s);
+ pa_assert(PA_SINK_IS_LINKED(s->state));
+
+ ret = pa_idxset_size(s->inputs);
+ pa_assert(ret >= s->n_corked);
+
+ /* Streams connected to our monitor source do not matter for
+ * pa_sink_used_by()!.*/
+
+ return ret - s->n_corked;
+}
+
+/* Called from IO thread, except when it is not */
+int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
+ pa_sink *s = PA_SINK(o);
+ pa_sink_assert_ref(s);
+
+ switch ((pa_sink_message_t) code) {
+
+ case PA_SINK_MESSAGE_ADD_INPUT: {
+ pa_sink_input *i = PA_SINK_INPUT(userdata);
+
+ /* If you change anything here, make sure to change the
+ * sink input handling a few lines down at
+ * PA_SINK_MESSAGE_FINISH_MOVE, too. */
+
+ pa_hashmap_put(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index), pa_sink_input_ref(i));
+
+ /* Since the caller sleeps in pa_sink_input_put(), we can
+ * safely access data outside of thread_info even though
+ * it is mutable */
+
+ if ((i->thread_info.sync_prev = i->sync_prev)) {
+ pa_assert(i->sink == i->thread_info.sync_prev->sink);
+ pa_assert(i->sync_prev->sync_next == i);
+ i->thread_info.sync_prev->thread_info.sync_next = i;
+ }
+
+ if ((i->thread_info.sync_next = i->sync_next)) {
+ pa_assert(i->sink == i->thread_info.sync_next->sink);
+ pa_assert(i->sync_next->sync_prev == i);
+ i->thread_info.sync_next->thread_info.sync_prev = i;
+ }
+
+ pa_assert(!i->thread_info.attached);
+ i->thread_info.attached = TRUE;
+
+ if (i->attach)
+ i->attach(i);
+
+ pa_sink_input_set_state_within_thread(i, i->state);
+
+ pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind);
+ pa_sink_input_update_max_request(i, s->thread_info.max_request);
+
+ pa_sink_invalidate_requested_latency(s);
+
+ /* We don't rewind here automatically. This is left to the
+ * sink input implementor because some sink inputs need a
+ * slow start, i.e. need some time to buffer client
+ * samples before beginning streaming. */
+
+ return 0;
+ }
+
+ case PA_SINK_MESSAGE_REMOVE_INPUT: {
+ pa_sink_input *i = PA_SINK_INPUT(userdata);
+
+ /* If you change anything here, make sure to change the
+ * sink input handling a few lines down at
+ * PA_SINK_MESSAGE_PREPAPRE_MOVE, too. */
+
+ if (i->detach)
+ i->detach(i);
+
+ pa_sink_input_set_state_within_thread(i, i->state);
+
+ pa_assert(i->thread_info.attached);
+ i->thread_info.attached = FALSE;
+
+ /* Since the caller sleeps in pa_sink_input_unlink(),
+ * we can safely access data outside of thread_info even
+ * though it is mutable */
+
+ pa_assert(!i->thread_info.sync_prev);
+ pa_assert(!i->thread_info.sync_next);
+
+ if (i->thread_info.sync_prev) {
+ i->thread_info.sync_prev->thread_info.sync_next = i->thread_info.sync_prev->sync_next;
+ i->thread_info.sync_prev = NULL;
+ }
+
+ if (i->thread_info.sync_next) {
+ i->thread_info.sync_next->thread_info.sync_prev = i->thread_info.sync_next->sync_prev;
+ i->thread_info.sync_next = NULL;
+ }
+
+ if (pa_hashmap_remove(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index)))
+ pa_sink_input_unref(i);
+
+ pa_sink_invalidate_requested_latency(s);
+ pa_sink_request_rewind(s, 0);
+
+ return 0;
+ }
+
+ case PA_SINK_MESSAGE_START_MOVE: {
+ pa_sink_input *i = PA_SINK_INPUT(userdata);
+
+ /* We don't support moving synchronized streams. */
+ pa_assert(!i->sync_prev);
+ pa_assert(!i->sync_next);
+ pa_assert(!i->thread_info.sync_next);
+ pa_assert(!i->thread_info.sync_prev);
+
+ if (i->thread_info.state != PA_SINK_INPUT_CORKED) {
+ pa_usec_t usec = 0;
+ size_t sink_nbytes, total_nbytes;
+
+ /* Get the latency of the sink */
+ if (PA_MSGOBJECT(s)->process_msg(PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
+ usec = 0;
+
+ sink_nbytes = pa_usec_to_bytes(usec, &s->sample_spec);
+ total_nbytes = sink_nbytes + pa_memblockq_get_length(i->thread_info.render_memblockq);
+
+ if (total_nbytes > 0) {
+ i->thread_info.rewrite_nbytes = i->thread_info.resampler ? pa_resampler_request(i->thread_info.resampler, total_nbytes) : total_nbytes;
+ i->thread_info.rewrite_flush = TRUE;
+ pa_sink_input_process_rewind(i, sink_nbytes);
+ }
+ }
+
+ if (i->detach)
+ i->detach(i);
+
+ pa_assert(i->thread_info.attached);
+ i->thread_info.attached = FALSE;
+
+ /* Let's remove the sink input ...*/
+ if (pa_hashmap_remove(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index)))
+ pa_sink_input_unref(i);
+
+ pa_sink_invalidate_requested_latency(s);
+
+ pa_log_debug("Requesting rewind due to started move");
+ pa_sink_request_rewind(s, 0);
+
+ return 0;
+ }
+
+ case PA_SINK_MESSAGE_FINISH_MOVE: {
+ pa_sink_input *i = PA_SINK_INPUT(userdata);
+
+ /* We don't support moving synchronized streams. */
+ pa_assert(!i->sync_prev);
+ pa_assert(!i->sync_next);
+ pa_assert(!i->thread_info.sync_next);
+ pa_assert(!i->thread_info.sync_prev);
+
+ pa_hashmap_put(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index), pa_sink_input_ref(i));
+
+ pa_assert(!i->thread_info.attached);
+ i->thread_info.attached = TRUE;
+
+ if (i->attach)
+ i->attach(i);
+
+ pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind);
+ pa_sink_input_update_max_request(i, s->thread_info.max_request);
+
+ pa_sink_input_set_requested_latency_within_thread(i, i->thread_info.requested_sink_latency);
+
+ if (i->thread_info.state != PA_SINK_INPUT_CORKED) {
+ pa_usec_t usec = 0;
+ size_t nbytes;
+
+ /* Get the latency of the sink */
+ if (PA_MSGOBJECT(s)->process_msg(PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
+ usec = 0;
+
+ nbytes = pa_usec_to_bytes(usec, &s->sample_spec);
+
+ if (nbytes > 0)
+ pa_sink_input_drop(i, nbytes);
+
+ pa_log_debug("Requesting rewind due to finished move");
+ pa_sink_request_rewind(s, nbytes);
+ }
+
+ return 0;
+ }
+
+ case PA_SINK_MESSAGE_SET_VOLUME:
+ s->thread_info.soft_volume = *((pa_cvolume*) userdata);
+
+ pa_sink_request_rewind(s, 0);
+ return 0;
+
+ case PA_SINK_MESSAGE_SET_MUTE:
+ s->thread_info.soft_muted = PA_PTR_TO_UINT(userdata);
+
+ pa_sink_request_rewind(s, 0);
+ return 0;
+
+ case PA_SINK_MESSAGE_GET_VOLUME:
+ *((pa_cvolume*) userdata) = s->thread_info.soft_volume;
+ return 0;
+
+ case PA_SINK_MESSAGE_GET_MUTE:
+ *((pa_bool_t*) userdata) = s->thread_info.soft_muted;
+ return 0;
+
+ case PA_SINK_MESSAGE_SET_STATE:
+
+ s->thread_info.state = PA_PTR_TO_UINT(userdata);
+ return 0;
+
+ case PA_SINK_MESSAGE_DETACH:
+
+ /* Detach all streams */
+ pa_sink_detach_within_thread(s);
+ return 0;
+
+ case PA_SINK_MESSAGE_ATTACH:
+
+ /* Reattach all streams */
+ pa_sink_attach_within_thread(s);
+ return 0;
+
+ case PA_SINK_MESSAGE_GET_REQUESTED_LATENCY: {
+
+ pa_usec_t *usec = userdata;
+ *usec = pa_sink_get_requested_latency_within_thread(s);
+
+ if (*usec == (pa_usec_t) -1)
+ *usec = s->thread_info.max_latency;
+
+ return 0;
+ }
+
+ case PA_SINK_MESSAGE_SET_LATENCY_RANGE: {
+ pa_usec_t *r = userdata;
+
+ pa_sink_update_latency_range(s, r[0], r[1]);
+
+ return 0;
+ }
+
+ case PA_SINK_MESSAGE_GET_LATENCY_RANGE: {
+ pa_usec_t *r = userdata;
+
+ r[0] = s->thread_info.min_latency;
+ r[1] = s->thread_info.max_latency;
+
+ return 0;
+ }
+
+ case PA_SINK_MESSAGE_GET_MAX_REWIND:
+
+ *((size_t*) userdata) = s->thread_info.max_rewind;
+ return 0;
+
+ case PA_SINK_MESSAGE_GET_MAX_REQUEST:
+
+ *((size_t*) userdata) = s->thread_info.max_request;
+ return 0;
+
+ case PA_SINK_MESSAGE_GET_LATENCY:
+ case PA_SINK_MESSAGE_MAX:
+ ;
+ }
+
+ return -1;
+}
+
+/* Called from main thread */
+int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend) {
+ pa_sink *sink;
+ uint32_t idx;
+ int ret = 0;
+
+ pa_core_assert_ref(c);
+
+ for (sink = PA_SINK(pa_idxset_first(c->sinks, &idx)); sink; sink = PA_SINK(pa_idxset_next(c->sinks, &idx)))
+ ret -= pa_sink_suspend(sink, suspend) < 0;
return ret;
}
+
+/* Called from main thread */
+void pa_sink_detach(pa_sink *s) {
+ pa_sink_assert_ref(s);
+ pa_assert(PA_SINK_IS_LINKED(s->state));
+
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_DETACH, NULL, 0, NULL) == 0);
+}
+
+/* Called from main thread */
+void pa_sink_attach(pa_sink *s) {
+ pa_sink_assert_ref(s);
+ pa_assert(PA_SINK_IS_LINKED(s->state));
+
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_ATTACH, NULL, 0, NULL) == 0);
+}
+
+/* Called from IO thread */
+void pa_sink_detach_within_thread(pa_sink *s) {
+ pa_sink_input *i;
+ void *state = NULL;
+
+ pa_sink_assert_ref(s);
+ pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
+
+ while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
+ if (i->detach)
+ i->detach(i);
+
+ if (s->monitor_source)
+ pa_source_detach_within_thread(s->monitor_source);
+}
+
+/* Called from IO thread */
+void pa_sink_attach_within_thread(pa_sink *s) {
+ pa_sink_input *i;
+ void *state = NULL;
+
+ pa_sink_assert_ref(s);
+ pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
+
+ while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
+ if (i->attach)
+ i->attach(i);
+
+ if (s->monitor_source)
+ pa_source_attach_within_thread(s->monitor_source);
+}
+
+/* Called from IO thread */
+void pa_sink_request_rewind(pa_sink*s, size_t nbytes) {
+ pa_sink_assert_ref(s);
+ pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
+
+ if (nbytes <= 0)
+ nbytes = s->thread_info.max_rewind;
+
+ nbytes = PA_MIN(nbytes, s->thread_info.max_rewind);
+
+ if (nbytes <= s->thread_info.rewind_nbytes)
+ return;
+
+ s->thread_info.rewind_nbytes = nbytes;
+
+ if (s->request_rewind)
+ s->request_rewind(s);
+}
+
+/* Called from IO thread */
+pa_usec_t pa_sink_get_requested_latency_within_thread(pa_sink *s) {
+ pa_usec_t result = (pa_usec_t) -1;
+ pa_sink_input *i;
+ void *state = NULL;
+ pa_usec_t monitor_latency;
+
+ pa_sink_assert_ref(s);
+
+ if (s->thread_info.requested_latency_valid)
+ return s->thread_info.requested_latency;
+
+ while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
+
+ if (i->thread_info.requested_sink_latency != (pa_usec_t) -1 &&
+ (result == (pa_usec_t) -1 || result > i->thread_info.requested_sink_latency))
+ result = i->thread_info.requested_sink_latency;
+
+ monitor_latency = pa_source_get_requested_latency_within_thread(s->monitor_source);
+
+ if (monitor_latency != (pa_usec_t) -1 &&
+ (result == (pa_usec_t) -1 || result > monitor_latency))
+ result = monitor_latency;
+
+ if (result != (pa_usec_t) -1) {
+ if (s->thread_info.max_latency > 0 && result > s->thread_info.max_latency)
+ result = s->thread_info.max_latency;
+
+ if (s->thread_info.min_latency > 0 && result < s->thread_info.min_latency)
+ result = s->thread_info.min_latency;
+ }
+
+ s->thread_info.requested_latency = result;
+ s->thread_info.requested_latency_valid = TRUE;
+
+ return result;
+}
+
+/* Called from main thread */
+pa_usec_t pa_sink_get_requested_latency(pa_sink *s) {
+ pa_usec_t usec = 0;
+
+ pa_sink_assert_ref(s);
+ pa_assert(PA_SINK_IS_LINKED(s->state));
+
+ if (!PA_SINK_IS_OPENED(s->state))
+ return 0;
+
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
+ return usec;
+}
+
+/* Called from IO thread */
+void pa_sink_set_max_rewind(pa_sink *s, size_t max_rewind) {
+ pa_sink_input *i;
+ void *state = NULL;
+
+ pa_sink_assert_ref(s);
+
+ if (max_rewind == s->thread_info.max_rewind)
+ return;
+
+ s->thread_info.max_rewind = max_rewind;
+
+ if (PA_SINK_IS_LINKED(s->thread_info.state)) {
+ while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
+ pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind);
+ }
+
+ if (s->monitor_source)
+ pa_source_set_max_rewind(s->monitor_source, s->thread_info.max_rewind);
+}
+
+/* Called from IO thread */
+void pa_sink_set_max_request(pa_sink *s, size_t max_request) {
+ pa_sink_input *i;
+ void *state = NULL;
+
+ pa_sink_assert_ref(s);
+
+ if (max_request == s->thread_info.max_request)
+ return;
+
+ s->thread_info.max_request = max_request;
+
+ if (PA_SINK_IS_LINKED(s->thread_info.state)) {
+ while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
+ pa_sink_input_update_max_request(i, s->thread_info.max_request);
+ }
+}
+
+/* Called from IO thread */
+void pa_sink_invalidate_requested_latency(pa_sink *s) {
+ pa_sink_input *i;
+ void *state = NULL;
+
+ pa_sink_assert_ref(s);
+
+ s->thread_info.requested_latency_valid = FALSE;
+
+ if (s->update_requested_latency)
+ s->update_requested_latency(s);
+
+ while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
+ if (i->update_sink_requested_latency)
+ i->update_sink_requested_latency(i);
+}
+
+/* Called from main thread */
+void pa_sink_set_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency) {
+ pa_sink_assert_ref(s);
+
+ /* min_latency == 0: no limit
+ * min_latency == (size_t) -1: default limit
+ * min_latency anything else: specified limit
+ *
+ * Similar for max_latency */
+
+ if (min_latency == (pa_usec_t) -1)
+ min_latency = DEFAULT_MIN_LATENCY;
+
+ if (max_latency == (pa_usec_t) -1)
+ max_latency = min_latency;
+
+ pa_assert(!min_latency || !max_latency ||
+ min_latency <= max_latency);
+
+ if (PA_SINK_IS_LINKED(s->state)) {
+ pa_usec_t r[2];
+
+ r[0] = min_latency;
+ r[1] = max_latency;
+
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_LATENCY_RANGE, r, 0, NULL) == 0);
+ } else {
+ s->thread_info.min_latency = min_latency;
+ s->thread_info.max_latency = max_latency;
+
+ s->monitor_source->thread_info.min_latency = min_latency;
+ s->monitor_source->thread_info.max_latency = max_latency;
+
+ s->thread_info.requested_latency_valid = s->monitor_source->thread_info.requested_latency_valid = FALSE;
+ }
+}
+
+/* Called from main thread */
+void pa_sink_get_latency_range(pa_sink *s, pa_usec_t *min_latency, pa_usec_t *max_latency) {
+ pa_sink_assert_ref(s);
+ pa_assert(min_latency);
+ pa_assert(max_latency);
+
+ if (PA_SINK_IS_LINKED(s->state)) {
+ pa_usec_t r[2] = { 0, 0 };
+
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY_RANGE, r, 0, NULL) == 0);
+
+ *min_latency = r[0];
+ *max_latency = r[1];
+ } else {
+ *min_latency = s->thread_info.min_latency;
+ *max_latency = s->thread_info.max_latency;
+ }
+}
+
+/* Called from IO thread */
+void pa_sink_update_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency) {
+ pa_sink_input *i;
+ void *state = NULL;
+
+ pa_sink_assert_ref(s);
+
+ s->thread_info.min_latency = min_latency;
+ s->thread_info.max_latency = max_latency;
+
+ while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
+ if (i->update_sink_latency_range)
+ i->update_sink_latency_range(i);
+
+ pa_sink_invalidate_requested_latency(s);
+
+ pa_source_update_latency_range(s->monitor_source, min_latency, max_latency);
+}
+
+size_t pa_sink_get_max_rewind(pa_sink *s) {
+ size_t r;
+ pa_sink_assert_ref(s);
+
+ if (!PA_SINK_IS_LINKED(s->state))
+ return s->thread_info.max_rewind;
+
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MAX_REWIND, &r, 0, NULL) == 0);
+
+ return r;
+}
+
+size_t pa_sink_get_max_request(pa_sink *s) {
+ size_t r;
+ pa_sink_assert_ref(s);
+
+ if (!PA_SINK_IS_LINKED(s->state))
+ return s->thread_info.max_request;
+
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MAX_REQUEST, &r, 0, NULL) == 0);
+
+ return r;
+}
diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
index 1d870620..b73944e8 100644
--- a/src/pulsecore/sink.h
+++ b/src/pulsecore/sink.h
@@ -1,108 +1,276 @@
-#ifndef foosinkhfoo
-#define foosinkhfoo
-
-/* $Id$ */
+#ifndef foopulsesinkhfoo
+#define foopulsesinkhfoo
/***
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.
***/
-#include <inttypes.h>
-
typedef struct pa_sink pa_sink;
+#include <inttypes.h>
+
#include <pulse/sample.h>
#include <pulse/channelmap.h>
#include <pulse/volume.h>
-#include <pulsecore/core-def.h>
+
#include <pulsecore/core.h>
#include <pulsecore/idxset.h>
#include <pulsecore/source.h>
#include <pulsecore/module.h>
+#include <pulsecore/refcnt.h>
+#include <pulsecore/msgobject.h>
+#include <pulsecore/rtpoll.h>
#define PA_MAX_INPUTS_PER_SINK 32
typedef enum pa_sink_state {
+ PA_SINK_INIT,
PA_SINK_RUNNING,
- PA_SINK_DISCONNECTED
+ PA_SINK_SUSPENDED,
+ PA_SINK_IDLE,
+ PA_SINK_UNLINKED
} pa_sink_state_t;
+static inline pa_bool_t PA_SINK_IS_OPENED(pa_sink_state_t x) {
+ return x == PA_SINK_RUNNING || x == PA_SINK_IDLE;
+}
+
+static inline pa_bool_t PA_SINK_IS_LINKED(pa_sink_state_t x) {
+ return x == PA_SINK_RUNNING || x == PA_SINK_IDLE || x == PA_SINK_SUSPENDED;
+}
+
struct pa_sink {
- int ref;
+ pa_msgobject parent;
+
uint32_t index;
pa_core *core;
pa_sink_state_t state;
+ pa_sink_flags_t flags;
char *name;
- char *description, *driver; /* may be NULL */
- int is_hardware;
+ char *driver; /* may be NULL */
+ pa_proplist *proplist;
- pa_module *owner; /* may be NULL */
+ pa_module *module; /* may be NULL */
pa_sample_spec sample_spec;
pa_channel_map channel_map;
pa_idxset *inputs;
- pa_source *monitor_source; /* may be NULL */
-
- pa_cvolume hw_volume, sw_volume;
- int hw_muted, sw_muted;
-
- void (*notify)(pa_sink*sink); /* may be NULL */
- pa_usec_t (*get_latency)(pa_sink *s); /* dito */
- int (*set_hw_volume)(pa_sink *s); /* dito */
- int (*get_hw_volume)(pa_sink *s); /* dito */
- int (*set_hw_mute)(pa_sink *s); /* dito */
- int (*get_hw_mute)(pa_sink *s); /* dito */
-
+ unsigned n_corked;
+ pa_source *monitor_source;
+
+ pa_cvolume volume;
+ pa_bool_t muted;
+
+ pa_bool_t refresh_volume:1;
+ pa_bool_t refresh_muted:1;
+
+ pa_asyncmsgq *asyncmsgq;
+ pa_rtpoll *rtpoll;
+
+ pa_memchunk silence;
+
+ /* Called when the main loop requests a state change. Called from
+ * main loop context. If returns -1 the state change will be
+ * inhibited */
+ int (*set_state)(pa_sink *s, pa_sink_state_t state); /* may be NULL */
+
+ /* Callled when the volume is queried. Called from main loop
+ * context. If this is NULL a PA_SINK_MESSAGE_GET_VOLUME message
+ * will be sent to the IO thread instead. If refresh_volume is
+ * FALSE neither this function is called nor a message is sent. */
+ int (*get_volume)(pa_sink *s); /* may be NULL */
+
+ /* Called when the volume shall be changed. Called from main loop
+ * context. If this is NULL a PA_SINK_MESSAGE_SET_VOLUME message
+ * will be sent to the IO thread instead. */
+ int (*set_volume)(pa_sink *s); /* dito */
+
+ /* Called when the mute setting is queried. Called from main loop
+ * context. If this is NULL a PA_SINK_MESSAGE_GET_MUTE message
+ * will be sent to the IO thread instead. If refresh_mute is
+ * FALSE neither this function is called nor a message is sent.*/
+ int (*get_mute)(pa_sink *s); /* dito */
+
+ /* Called when the mute setting shall be changed. Called from main
+ * loop context. If this is NULL a PA_SINK_MESSAGE_SET_MUTE
+ * message will be sent to the IO thread instead. */
+ int (*set_mute)(pa_sink *s); /* dito */
+
+ /* Called when a rewind request is issued. Called from IO thread
+ * context. */
+ void (*request_rewind)(pa_sink *s); /* dito */
+
+ /* Called when a the requested latency is changed. Called from IO
+ * thread context. */
+ void (*update_requested_latency)(pa_sink *s); /* dito */
+
+ /* Contains copies of the above data so that the real-time worker
+ * thread can work without access locking */
+ struct {
+ pa_sink_state_t state;
+ pa_hashmap *inputs;
+ pa_cvolume soft_volume;
+ pa_bool_t soft_muted:1;
+
+ pa_bool_t requested_latency_valid:1;
+ pa_usec_t requested_latency;
+
+ /* The number of bytes streams need to keep around as history to
+ * be able to satisfy every DMA buffer rewrite */
+ size_t max_rewind;
+
+ /* The number of bytes streams need to keep around to satisfy
+ * every DMA write request */
+ size_t max_request;
+
+ /* Maximum of what clients requested to rewind in this cycle */
+ size_t rewind_nbytes;
+
+ pa_usec_t min_latency; /* we won't go below this latency */
+ pa_usec_t max_latency; /* An upper limit for the latencies */
+ } thread_info;
+
void *userdata;
};
+PA_DECLARE_CLASS(pa_sink);
+#define PA_SINK(s) (pa_sink_cast(s))
+
+typedef enum pa_sink_message {
+ PA_SINK_MESSAGE_ADD_INPUT,
+ PA_SINK_MESSAGE_REMOVE_INPUT,
+ PA_SINK_MESSAGE_GET_VOLUME,
+ PA_SINK_MESSAGE_SET_VOLUME,
+ PA_SINK_MESSAGE_GET_MUTE,
+ PA_SINK_MESSAGE_SET_MUTE,
+ PA_SINK_MESSAGE_GET_LATENCY,
+ PA_SINK_MESSAGE_GET_REQUESTED_LATENCY,
+ PA_SINK_MESSAGE_SET_STATE,
+ PA_SINK_MESSAGE_START_MOVE,
+ PA_SINK_MESSAGE_FINISH_MOVE,
+ PA_SINK_MESSAGE_ATTACH,
+ PA_SINK_MESSAGE_DETACH,
+ PA_SINK_MESSAGE_SET_LATENCY_RANGE,
+ PA_SINK_MESSAGE_GET_LATENCY_RANGE,
+ PA_SINK_MESSAGE_GET_MAX_REWIND,
+ PA_SINK_MESSAGE_GET_MAX_REQUEST,
+ PA_SINK_MESSAGE_MAX
+} pa_sink_message_t;
+
+typedef struct pa_sink_new_data {
+ char *name;
+ pa_bool_t namereg_fail;
+ pa_proplist *proplist;
+
+ const char *driver;
+ pa_module *module;
+
+ pa_sample_spec sample_spec;
+ pa_bool_t sample_spec_is_set;
+ pa_channel_map channel_map;
+ pa_bool_t channel_map_is_set;
+
+ pa_cvolume volume;
+ pa_bool_t volume_is_set;
+ pa_bool_t muted;
+ pa_bool_t muted_is_set;
+} pa_sink_new_data;
+
+pa_sink_new_data* pa_sink_new_data_init(pa_sink_new_data *data);
+void pa_sink_new_data_set_name(pa_sink_new_data *data, const char *name);
+void pa_sink_new_data_set_sample_spec(pa_sink_new_data *data, const pa_sample_spec *spec);
+void pa_sink_new_data_set_channel_map(pa_sink_new_data *data, const pa_channel_map *map);
+void pa_sink_new_data_set_volume(pa_sink_new_data *data, const pa_cvolume *volume);
+void pa_sink_new_data_set_muted(pa_sink_new_data *data, pa_bool_t mute);
+void pa_sink_new_data_done(pa_sink_new_data *data);
+
+/* To be called exclusively by the sink driver, from main context */
+
pa_sink* pa_sink_new(
- pa_core *core,
- const char *driver,
- const char *name,
- int namereg_fail,
- const pa_sample_spec *spec,
- const pa_channel_map *map);
-
-void pa_sink_disconnect(pa_sink* s);
-void pa_sink_unref(pa_sink*s);
-pa_sink* pa_sink_ref(pa_sink *s);
-
-int pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result);
+ pa_core *core,
+ pa_sink_new_data *data,
+ pa_sink_flags_t flags);
+
+void pa_sink_put(pa_sink *s);
+void pa_sink_unlink(pa_sink* s);
+
+void pa_sink_set_description(pa_sink *s, const char *description);
+void pa_sink_set_asyncmsgq(pa_sink *s, pa_asyncmsgq *q);
+void pa_sink_set_rtpoll(pa_sink *s, pa_rtpoll *p);
+
+void pa_sink_set_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency);
+
+void pa_sink_detach(pa_sink *s);
+void pa_sink_attach(pa_sink *s);
+
+/* May be called by everyone, from main context */
+
+/* The returned value is supposed to be in the time domain of the sound card! */
+pa_usec_t pa_sink_get_latency(pa_sink *s);
+pa_usec_t pa_sink_get_requested_latency(pa_sink *s);
+void pa_sink_get_latency_range(pa_sink *s, pa_usec_t *min_latency, pa_usec_t *max_latency);
+
+size_t pa_sink_get_max_rewind(pa_sink *s);
+size_t pa_sink_get_max_request(pa_sink *s);
+
+int pa_sink_update_status(pa_sink*s);
+int pa_sink_suspend(pa_sink *s, pa_bool_t suspend);
+int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend);
+
+void pa_sink_set_volume(pa_sink *sink, const pa_cvolume *volume);
+const pa_cvolume *pa_sink_get_volume(pa_sink *sink);
+void pa_sink_set_mute(pa_sink *sink, pa_bool_t mute);
+pa_bool_t pa_sink_get_mute(pa_sink *sink);
+
+unsigned pa_sink_linked_by(pa_sink *s); /* Number of connected streams */
+unsigned pa_sink_used_by(pa_sink *s); /* Number of connected streams which are not corked */
+#define pa_sink_get_state(s) ((s)->state)
+
+/* To be called exclusively by the sink driver, from IO context */
+
+void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result);
void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result);
-int pa_sink_render_into(pa_sink*s, pa_memchunk *target);
+void pa_sink_render_into(pa_sink*s, pa_memchunk *target);
void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target);
-
-pa_usec_t pa_sink_get_latency(pa_sink *s);
-void pa_sink_notify(pa_sink*s);
+void pa_sink_process_rewind(pa_sink *s, size_t nbytes);
-void pa_sink_set_owner(pa_sink *sink, pa_module *m);
+int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
-void pa_sink_set_volume(pa_sink *sink, pa_mixer_t m, const pa_cvolume *volume);
-const pa_cvolume *pa_sink_get_volume(pa_sink *sink, pa_mixer_t m);
-void pa_sink_set_mute(pa_sink *sink, pa_mixer_t m, int mute);
-int pa_sink_get_mute(pa_sink *sink, pa_mixer_t m);
+void pa_sink_attach_within_thread(pa_sink *s);
+void pa_sink_detach_within_thread(pa_sink *s);
-void pa_sink_set_description(pa_sink *s, const char *description);
+pa_usec_t pa_sink_get_requested_latency_within_thread(pa_sink *s);
+
+void pa_sink_set_max_rewind(pa_sink *s, size_t max_rewind);
+void pa_sink_set_max_request(pa_sink *s, size_t max_request);
+
+void pa_sink_update_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency);
+
+/* To be called exclusively by sink input drivers, from IO context */
+
+void pa_sink_request_rewind(pa_sink*s, size_t nbytes);
-unsigned pa_sink_used_by(pa_sink *s);
+void pa_sink_invalidate_requested_latency(pa_sink *s);
#endif
diff --git a/src/pulsecore/sioman.c b/src/pulsecore/sioman.c
index d84010ee..7e5b186c 100644
--- a/src/pulsecore/sioman.c
+++ b/src/pulsecore/sioman.c
@@ -1,18 +1,18 @@
-/* $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
@@ -23,21 +23,17 @@
#include <config.h>
#endif
-#include <assert.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/atomic.h>
#include "sioman.h"
-static int stdio_inuse = 0;
+static pa_atomic_t stdio_inuse = PA_ATOMIC_INIT(0);
int pa_stdio_acquire(void) {
- if (stdio_inuse)
- return -1;
-
- stdio_inuse = 1;
- return 0;
+ return pa_atomic_cmpxchg(&stdio_inuse, 0, 1) ? 0 : -1;
}
void pa_stdio_release(void) {
- assert(stdio_inuse);
- stdio_inuse = 0;
-}
+ pa_assert_se(pa_atomic_cmpxchg(&stdio_inuse, 1, 0));
+}
diff --git a/src/pulsecore/sioman.h b/src/pulsecore/sioman.h
index cd04d140..d0cacc9b 100644
--- a/src/pulsecore/sioman.h
+++ b/src/pulsecore/sioman.h
@@ -1,21 +1,21 @@
#ifndef foosiomanhfoo
#define foosiomanhfoo
-/* $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
diff --git a/src/pulsecore/socket-client.c b/src/pulsecore/socket-client.c
index 2ceaf5c3..e69a63da 100644
--- a/src/pulsecore/socket-client.c
+++ b/src/pulsecore/socket-client.c
@@ -1,22 +1,23 @@
-/* $Id$ */
-
/***
- This file is part of PulseAudio.
-
- PulseAudio is free software; you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as
- published by the Free Software Foundation; either version 2.1 of the
- License, or (at your option) any later version.
-
- PulseAudio is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with PulseAudio; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- USA.
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+ Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
***/
#ifdef HAVE_CONFIG_H
@@ -29,7 +30,6 @@
#include <stdio.h>
#include <errno.h>
#include <string.h>
-#include <assert.h>
#include <stdlib.h>
#ifdef HAVE_SYS_SOCKET_H
@@ -52,31 +52,33 @@
#include <asyncns.h>
#endif
-#include "winsock.h"
-
#include <pulse/timeval.h>
#include <pulse/xmalloc.h>
+#include <pulsecore/winsock.h>
#include <pulsecore/core-error.h>
#include <pulsecore/socket-util.h>
#include <pulsecore/core-util.h>
+#include <pulsecore/socket-util.h>
#include <pulsecore/log.h>
#include <pulsecore/parseaddr.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/refcnt.h>
#include "socket-client.h"
#define CONNECT_TIMEOUT 5
struct pa_socket_client {
- int ref;
+ PA_REFCNT_DECLARE;
pa_mainloop_api *mainloop;
int fd;
pa_io_event *io_event;
pa_time_event *timeout_event;
pa_defer_event *defer_event;
- void (*callback)(pa_socket_client*c, pa_iochannel *io, void *userdata);
+ pa_socket_client_cb_t callback;
void *userdata;
- int local;
+ pa_bool_t local;
#ifdef HAVE_LIBASYNCNS
asyncns_t *asyncns;
asyncns_query_t * asyncns_query;
@@ -84,20 +86,20 @@ struct pa_socket_client {
#endif
};
-static pa_socket_client*pa_socket_client_new(pa_mainloop_api *m) {
+static pa_socket_client* socket_client_new(pa_mainloop_api *m) {
pa_socket_client *c;
- assert(m);
+ pa_assert(m);
- c = pa_xmalloc(sizeof(pa_socket_client));
- c->ref = 1;
+ c = pa_xnew(pa_socket_client, 1);
+ PA_REFCNT_INIT(c);
c->mainloop = m;
c->fd = -1;
c->io_event = NULL;
- c->defer_event = NULL;
c->timeout_event = NULL;
+ c->defer_event = NULL;
c->callback = NULL;
c->userdata = NULL;
- c->local = 0;
+ c->local = FALSE;
#ifdef HAVE_LIBASYNCNS
c->asyncns = NULL;
@@ -109,35 +111,38 @@ static pa_socket_client*pa_socket_client_new(pa_mainloop_api *m) {
}
static void free_events(pa_socket_client *c) {
- assert(c);
-
+ pa_assert(c);
+
if (c->io_event) {
c->mainloop->io_free(c->io_event);
c->io_event = NULL;
}
-
- if (c->defer_event) {
- c->mainloop->defer_free(c->defer_event);
- c->defer_event = NULL;
- }
-
+
if (c->timeout_event) {
c->mainloop->time_free(c->timeout_event);
c->timeout_event = NULL;
}
+
+ if (c->defer_event) {
+ c->mainloop->defer_free(c->defer_event);
+ c->defer_event = NULL;
+ }
}
static void do_call(pa_socket_client *c) {
pa_iochannel *io = NULL;
int error;
socklen_t lerror;
- assert(c && c->callback);
+
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+ pa_assert(c->callback);
pa_socket_client_ref(c);
if (c->fd < 0)
goto finish;
-
+
lerror = sizeof(error);
if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, (void*)&error, &lerror) < 0) {
pa_log("getsockopt(): %s", pa_cstrerror(errno));
@@ -150,45 +155,60 @@ static void do_call(pa_socket_client *c) {
}
if (error != 0) {
- pa_log_debug("connect(): %s", pa_cstrerror(errno));
+ pa_log_debug("connect(): %s", pa_cstrerror(error));
errno = error;
goto finish;
}
io = pa_iochannel_new(c->mainloop, c->fd, c->fd);
- assert(io);
-
+ pa_assert(io);
+
finish:
if (!io && c->fd >= 0)
- close(c->fd);
+ pa_close(c->fd);
c->fd = -1;
free_events(c);
-
- assert(c->callback);
+
+ pa_assert(c->callback);
c->callback(c, io, c->userdata);
-
+
pa_socket_client_unref(c);
}
-static void connect_fixed_cb(pa_mainloop_api *m, pa_defer_event *e, void *userdata) {
+static void connect_defer_cb(pa_mainloop_api *m, pa_defer_event *e, void *userdata) {
pa_socket_client *c = userdata;
- assert(m && c && c->defer_event == e);
+
+ pa_assert(m);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+ pa_assert(c->defer_event == e);
+
do_call(c);
}
static void connect_io_cb(pa_mainloop_api*m, pa_io_event *e, int fd, PA_GCC_UNUSED pa_io_event_flags_t f, void *userdata) {
pa_socket_client *c = userdata;
- assert(m && c && c->io_event == e && fd >= 0);
+
+ pa_assert(m);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+ pa_assert(c->io_event == e);
+ pa_assert(fd >= 0);
+
do_call(c);
}
static int do_connect(pa_socket_client *c, const struct sockaddr *sa, socklen_t len) {
int r;
- assert(c && sa && len);
-
- pa_make_nonblock_fd(c->fd);
-
+
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+ pa_assert(sa);
+ pa_assert(len > 0);
+
+ pa_make_fd_nonblock(c->fd);
+
if ((r = connect(c->fd, sa, len)) < 0) {
#ifdef OS_IS_WIN32
if (WSAGetLastError() != EWOULDBLOCK) {
@@ -200,19 +220,18 @@ static int do_connect(pa_socket_client *c, const struct sockaddr *sa, socklen_t
return -1;
}
- c->io_event = c->mainloop->io_new(c->mainloop, c->fd, PA_IO_EVENT_OUTPUT, connect_io_cb, c);
- assert(c->io_event);
- } else {
- c->defer_event = c->mainloop->defer_new(c->mainloop, connect_fixed_cb, c);
- assert(c->defer_event);
- }
+ pa_assert_se(c->io_event = c->mainloop->io_new(c->mainloop, c->fd, PA_IO_EVENT_OUTPUT, connect_io_cb, c));
+ } else
+ pa_assert_se(c->defer_event = c->mainloop->defer_new(c->mainloop, connect_defer_cb, c));
return 0;
}
pa_socket_client* pa_socket_client_new_ipv4(pa_mainloop_api *m, uint32_t address, uint16_t port) {
struct sockaddr_in sa;
- assert(m && port > 0);
+
+ pa_assert(m);
+ pa_assert(port > 0);
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
@@ -226,12 +245,13 @@ pa_socket_client* pa_socket_client_new_ipv4(pa_mainloop_api *m, uint32_t address
pa_socket_client* pa_socket_client_new_unix(pa_mainloop_api *m, const char *filename) {
struct sockaddr_un sa;
- assert(m && filename);
-
+
+ pa_assert(m);
+ pa_assert(filename);
+
memset(&sa, 0, sizeof(sa));
sa.sun_family = AF_UNIX;
- strncpy(sa.sun_path, filename, sizeof(sa.sun_path)-1);
- sa.sun_path[sizeof(sa.sun_path) - 1] = 0;
+ pa_strlcpy(sa.sun_path, filename, sizeof(sa.sun_path));
return pa_socket_client_new_sockaddr(m, (struct sockaddr*) &sa, sizeof(sa));
}
@@ -245,37 +265,23 @@ pa_socket_client* pa_socket_client_new_unix(pa_mainloop_api *m, const char *file
#endif /* HAVE_SYS_UN_H */
static int sockaddr_prepare(pa_socket_client *c, const struct sockaddr *sa, size_t salen) {
- assert(c);
- assert(sa);
- assert(salen);
-
- switch (sa->sa_family) {
- case AF_UNIX:
- c->local = 1;
- break;
-
- case AF_INET:
- c->local = ((const struct sockaddr_in*) sa)->sin_addr.s_addr == INADDR_LOOPBACK;
- break;
-
- case AF_INET6:
- c->local = memcmp(&((const struct sockaddr_in6*) sa)->sin6_addr, &in6addr_loopback, sizeof(struct in6_addr)) == 0;
- break;
-
- default:
- c->local = 0;
- }
-
+ pa_assert(c);
+ pa_assert(sa);
+ pa_assert(salen);
+
+ c->local = pa_socket_address_is_local(sa);
+
if ((c->fd = socket(sa->sa_family, SOCK_STREAM, 0)) < 0) {
pa_log("socket(): %s", pa_cstrerror(errno));
return -1;
}
- pa_fd_set_cloexec(c->fd, 1);
+ pa_make_fd_cloexec(c->fd);
+
if (sa->sa_family == AF_INET || sa->sa_family == AF_INET6)
- pa_socket_tcp_low_delay(c->fd);
+ pa_make_tcp_socket_low_delay(c->fd);
else
- pa_socket_low_delay(c->fd);
+ pa_make_socket_low_delay(c->fd);
if (do_connect(c, sa, salen) < 0)
return -1;
@@ -285,29 +291,31 @@ static int sockaddr_prepare(pa_socket_client *c, const struct sockaddr *sa, size
pa_socket_client* pa_socket_client_new_sockaddr(pa_mainloop_api *m, const struct sockaddr *sa, size_t salen) {
pa_socket_client *c;
- assert(m && sa);
- c = pa_socket_client_new(m);
- assert(c);
+
+ pa_assert(m);
+ pa_assert(sa);
+ pa_assert(salen > 0);
+
+ pa_assert_se(c = socket_client_new(m));
if (sockaddr_prepare(c, sa, salen) < 0)
goto fail;
-
+
return c;
fail:
pa_socket_client_unref(c);
return NULL;
-
}
static void socket_client_free(pa_socket_client *c) {
- assert(c && c->mainloop);
-
+ pa_assert(c);
+ pa_assert(c->mainloop);
free_events(c);
-
+
if (c->fd >= 0)
- close(c->fd);
+ pa_close(c->fd);
#ifdef HAVE_LIBASYNCNS
if (c->asyncns_query)
@@ -317,32 +325,41 @@ static void socket_client_free(pa_socket_client *c) {
if (c->asyncns_io_event)
c->mainloop->io_free(c->asyncns_io_event);
#endif
-
+
pa_xfree(c);
}
void pa_socket_client_unref(pa_socket_client *c) {
- assert(c && c->ref >= 1);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
- if (!(--(c->ref)))
+ if (PA_REFCNT_DEC(c) <= 0)
socket_client_free(c);
}
pa_socket_client* pa_socket_client_ref(pa_socket_client *c) {
- assert(c && c->ref >= 1);
- c->ref++;
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ PA_REFCNT_INC(c);
return c;
}
-void pa_socket_client_set_callback(pa_socket_client *c, void (*on_connection)(pa_socket_client *c, pa_iochannel*io, void *userdata), void *userdata) {
- assert(c);
+void pa_socket_client_set_callback(pa_socket_client *c, pa_socket_client_cb_t on_connection, void *userdata) {
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
c->callback = on_connection;
c->userdata = userdata;
}
pa_socket_client* pa_socket_client_new_ipv6(pa_mainloop_api *m, uint8_t address[16], uint16_t port) {
struct sockaddr_in6 sa;
-
+
+ pa_assert(m);
+ pa_assert(address);
+ pa_assert(port > 0);
+
memset(&sa, 0, sizeof(sa));
sa.sin6_family = AF_INET6;
sa.sin6_port = htons(port);
@@ -357,7 +374,12 @@ static void asyncns_cb(pa_mainloop_api*m, pa_io_event *e, int fd, PA_GCC_UNUSED
pa_socket_client *c = userdata;
struct addrinfo *res = NULL;
int ret;
- assert(m && c && c->asyncns_io_event == e && fd >= 0);
+
+ pa_assert(m);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+ pa_assert(c->asyncns_io_event == e);
+ pa_assert(fd >= 0);
if (asyncns_wait(c->asyncns, 0) < 0)
goto fail;
@@ -370,37 +392,38 @@ static void asyncns_cb(pa_mainloop_api*m, pa_io_event *e, int fd, PA_GCC_UNUSED
if (ret != 0 || !res)
goto fail;
-
+
if (res->ai_addr)
sockaddr_prepare(c, res->ai_addr, res->ai_addrlen);
-
+
asyncns_freeaddrinfo(res);
m->io_free(c->asyncns_io_event);
c->asyncns_io_event = NULL;
return;
-
+
fail:
m->io_free(c->asyncns_io_event);
c->asyncns_io_event = NULL;
-
+
errno = EHOSTUNREACH;
do_call(c);
return;
-
+
}
#endif
static void timeout_cb(pa_mainloop_api *m, pa_time_event *e, const struct timeval *tv, void *userdata) {
pa_socket_client *c = userdata;
- assert(m);
- assert(e);
- assert(tv);
- assert(c);
+
+ pa_assert(m);
+ pa_assert(e);
+ pa_assert(tv);
+ pa_assert(c);
if (c->fd >= 0) {
- close(c->fd);
+ pa_close(c->fd);
c->fd = -1;
}
@@ -410,8 +433,8 @@ static void timeout_cb(pa_mainloop_api *m, pa_time_event *e, const struct timeva
static void start_timeout(pa_socket_client *c) {
struct timeval tv;
- assert(c);
- assert(!c->timeout_event);
+ pa_assert(c);
+ pa_assert(!c->timeout_event);
pa_gettimeofday(&tv);
pa_timeval_add(&tv, CONNECT_TIMEOUT * 1000000);
@@ -421,18 +444,20 @@ static void start_timeout(pa_socket_client *c) {
pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char*name, uint16_t default_port) {
pa_socket_client *c = NULL;
pa_parsed_address a;
- assert(m && name);
+
+ pa_assert(m);
+ pa_assert(name);
if (pa_parse_address(name, &a) < 0)
return NULL;
if (!a.port)
a.port = default_port;
-
+
switch (a.type) {
case PA_PARSED_ADDRESS_UNIX:
if ((c = pa_socket_client_new_unix(m, a.path_or_host)))
- start_timeout(c);
+ start_timeout(c);
break;
case PA_PARSED_ADDRESS_TCP4: /* Fallthrough */
@@ -442,44 +467,45 @@ pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char*nam
struct addrinfo hints;
char port[12];
- snprintf(port, sizeof(port), "%u", (unsigned) a.port);
+ pa_snprintf(port, sizeof(port), "%u", (unsigned) a.port);
memset(&hints, 0, sizeof(hints));
hints.ai_family = a.type == PA_PARSED_ADDRESS_TCP4 ? PF_INET : (a.type == PA_PARSED_ADDRESS_TCP6 ? PF_INET6 : PF_UNSPEC);
hints.ai_socktype = SOCK_STREAM;
-
-#ifdef HAVE_LIBASYNCNS
+
+#if defined(HAVE_LIBASYNCNS)
{
asyncns_t *asyncns;
-
+
if (!(asyncns = asyncns_new(1)))
goto finish;
- c = pa_socket_client_new(m);
+ pa_assert_se(c = socket_client_new(m));
c->asyncns = asyncns;
c->asyncns_io_event = m->io_new(m, asyncns_fd(c->asyncns), PA_IO_EVENT_INPUT, asyncns_cb, c);
c->asyncns_query = asyncns_getaddrinfo(c->asyncns, a.path_or_host, port, &hints);
- assert(c->asyncns_query);
+ pa_assert(c->asyncns_query);
start_timeout(c);
}
-#else /* HAVE_LIBASYNCNS */
+#elif defined(HAVE_GETADDRINFO)
{
-#ifdef HAVE_GETADDRINFO
int ret;
struct addrinfo *res = NULL;
ret = getaddrinfo(a.path_or_host, port, &hints, &res);
-
+
if (ret < 0 || !res)
goto finish;
if (res->ai_addr) {
if ((c = pa_socket_client_new_sockaddr(m, res->ai_addr, res->ai_addrlen)))
start_timeout(c);
- }
-
+ }
+
freeaddrinfo(res);
-#else /* HAVE_GETADDRINFO */
+ }
+#else
+ {
struct hostent *host = NULL;
struct sockaddr_in s;
@@ -504,8 +530,7 @@ pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char*nam
s.sin_port = htons(a.port);
if ((c = pa_socket_client_new_sockaddr(m, (struct sockaddr*)&s, sizeof(s))))
- start_timeout(c);
-#endif /* HAVE_GETADDRINFO */
+ start_timeout(c);
}
#endif /* HAVE_LIBASYNCNS */
}
@@ -514,13 +539,15 @@ pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char*nam
finish:
pa_xfree(a.path_or_host);
return c;
-
+
}
/* Return non-zero when the target sockaddr is considered
local. "local" means UNIX socket or TCP socket on localhost. Other
local IP addresses are not considered local. */
-int pa_socket_client_is_local(pa_socket_client *c) {
- assert(c);
+pa_bool_t pa_socket_client_is_local(pa_socket_client *c) {
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
return c->local;
}
diff --git a/src/pulsecore/socket-client.h b/src/pulsecore/socket-client.h
index 47e7cd5a..9ceeaddc 100644
--- a/src/pulsecore/socket-client.h
+++ b/src/pulsecore/socket-client.h
@@ -1,21 +1,22 @@
#ifndef foosocketclienthfoo
#define foosocketclienthfoo
-/* $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.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
@@ -31,17 +32,19 @@ struct sockaddr;
typedef struct pa_socket_client pa_socket_client;
+typedef void (*pa_socket_client_cb_t)(pa_socket_client *c, pa_iochannel*io, void *userdata);
+
pa_socket_client* pa_socket_client_new_ipv4(pa_mainloop_api *m, uint32_t address, uint16_t port);
pa_socket_client* pa_socket_client_new_ipv6(pa_mainloop_api *m, uint8_t address[16], uint16_t port);
pa_socket_client* pa_socket_client_new_unix(pa_mainloop_api *m, const char *filename);
pa_socket_client* pa_socket_client_new_sockaddr(pa_mainloop_api *m, const struct sockaddr *sa, size_t salen);
pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char *a, uint16_t default_port);
-void pa_socket_client_unref(pa_socket_client *c);
pa_socket_client* pa_socket_client_ref(pa_socket_client *c);
+void pa_socket_client_unref(pa_socket_client *c);
-void pa_socket_client_set_callback(pa_socket_client *c, void (*on_connection)(pa_socket_client *c, pa_iochannel*io, void *userdata), void *userdata);
+void pa_socket_client_set_callback(pa_socket_client *c, pa_socket_client_cb_t on_connection, void *userdata);
-int pa_socket_client_is_local(pa_socket_client *c);
+pa_bool_t pa_socket_client_is_local(pa_socket_client *c);
#endif
diff --git a/src/pulsecore/socket-server.c b/src/pulsecore/socket-server.c
index 4d69b8a4..9885a02b 100644
--- a/src/pulsecore/socket-server.c
+++ b/src/pulsecore/socket-server.c
@@ -1,18 +1,19 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
+ Copyright 2004-2006 Lennart Poettering
+ Copyright 2006-2007 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
@@ -24,7 +25,6 @@
#endif
#include <stdlib.h>
-#include <assert.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
@@ -69,17 +69,19 @@
#include <pulsecore/socket-util.h>
#include <pulsecore/core-util.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
#include <pulsecore/core-error.h>
+#include <pulsecore/refcnt.h>
#include "socket-server.h"
struct pa_socket_server {
- int ref;
+ PA_REFCNT_DECLARE;
int fd;
char *filename;
char *tcpwrap_service;
- void (*on_connection)(pa_socket_server*s, pa_iochannel *io, void *userdata);
+ pa_socket_server_on_connection_cb_t on_connection;
void *userdata;
pa_io_event *io_event;
@@ -87,23 +89,30 @@ struct pa_socket_server {
enum { SOCKET_SERVER_GENERIC, SOCKET_SERVER_IPV4, SOCKET_SERVER_UNIX, SOCKET_SERVER_IPV6 } type;
};
-static void callback(pa_mainloop_api *mainloop, pa_io_event *e, int fd, PA_GCC_UNUSED pa_io_event_flags_t f, void *userdata) {
+static void callback(pa_mainloop_api *mainloop, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
pa_socket_server *s = userdata;
pa_iochannel *io;
int nfd;
- assert(s && s->mainloop == mainloop && s->io_event == e && e && fd >= 0 && fd == s->fd);
+
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+ pa_assert(s->mainloop == mainloop);
+ pa_assert(s->io_event == e);
+ pa_assert(e);
+ pa_assert(fd >= 0);
+ pa_assert(fd == s->fd);
pa_socket_server_ref(s);
-
+
if ((nfd = accept(fd, NULL, NULL)) < 0) {
pa_log("accept(): %s", pa_cstrerror(errno));
goto finish;
}
- pa_fd_set_cloexec(nfd, 1);
-
+ pa_make_fd_cloexec(nfd);
+
if (!s->on_connection) {
- close(nfd);
+ pa_close(nfd);
goto finish;
}
@@ -116,22 +125,21 @@ static void callback(pa_mainloop_api *mainloop, pa_io_event *e, int fd, PA_GCC_U
fromhost(&req);
if (!hosts_access(&req)) {
pa_log_warn("TCP connection refused by tcpwrap.");
- close(nfd);
+ pa_close(nfd);
goto finish;
}
pa_log_info("TCP connection accepted by tcpwrap.");
}
#endif
-
+
/* There should be a check for socket type here */
- if (s->type == SOCKET_SERVER_IPV4)
- pa_socket_tcp_low_delay(fd);
+ if (s->type == SOCKET_SERVER_IPV4)
+ pa_make_tcp_socket_low_delay(fd);
else
- pa_socket_low_delay(fd);
-
- io = pa_iochannel_new(s->mainloop, nfd, nfd);
- assert(io);
+ pa_make_socket_low_delay(fd);
+
+ pa_assert_se(io = pa_iochannel_new(s->mainloop, nfd, nfd));
s->on_connection(s, io, s->userdata);
finish:
@@ -140,10 +148,12 @@ finish:
pa_socket_server* pa_socket_server_new(pa_mainloop_api *m, int fd) {
pa_socket_server *s;
- assert(m && fd >= 0);
-
- s = pa_xmalloc(sizeof(pa_socket_server));
- s->ref = 1;
+
+ pa_assert(m);
+ pa_assert(fd >= 0);
+
+ s = pa_xnew(pa_socket_server, 1);
+ PA_REFCNT_INIT(s);
s->fd = fd;
s->filename = NULL;
s->on_connection = NULL;
@@ -151,17 +161,18 @@ pa_socket_server* pa_socket_server_new(pa_mainloop_api *m, int fd) {
s->tcpwrap_service = NULL;
s->mainloop = m;
- s->io_event = m->io_new(m, fd, PA_IO_EVENT_INPUT, callback, s);
- assert(s->io_event);
+ pa_assert_se(s->io_event = m->io_new(m, fd, PA_IO_EVENT_INPUT, callback, s));
s->type = SOCKET_SERVER_GENERIC;
-
+
return s;
}
pa_socket_server* pa_socket_server_ref(pa_socket_server *s) {
- assert(s && s->ref >= 1);
- s->ref++;
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+ PA_REFCNT_INC(s);
return s;
}
@@ -171,21 +182,22 @@ pa_socket_server* pa_socket_server_new_unix(pa_mainloop_api *m, const char *file
int fd = -1;
struct sockaddr_un sa;
pa_socket_server *s;
-
- assert(m && filename);
+
+ pa_assert(m);
+ pa_assert(filename);
if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
pa_log("socket(): %s", pa_cstrerror(errno));
goto fail;
}
- pa_fd_set_cloexec(fd, 1);
+ pa_make_fd_cloexec(fd);
+ memset(&sa, 0, sizeof(sa));
sa.sun_family = AF_UNIX;
- strncpy(sa.sun_path, filename, sizeof(sa.sun_path)-1);
- sa.sun_path[sizeof(sa.sun_path) - 1] = 0;
+ pa_strlcpy(sa.sun_path, filename, sizeof(sa.sun_path));
- pa_socket_low_delay(fd);
+ pa_make_socket_low_delay(fd);
if (bind(fd, (struct sockaddr*) &sa, SUN_LEN(&sa)) < 0) {
pa_log("bind(): %s", pa_cstrerror(errno));
@@ -197,23 +209,22 @@ pa_socket_server* pa_socket_server_new_unix(pa_mainloop_api *m, const char *file
* because not all OS check the access rights on the socket
* inodes. */
chmod(filename, 0777);
-
+
if (listen(fd, 5) < 0) {
pa_log("listen(): %s", pa_cstrerror(errno));
goto fail;
}
- s = pa_socket_server_new(m, fd);
- assert(s);
+ pa_assert_se(s = pa_socket_server_new(m, fd));
s->filename = pa_xstrdup(filename);
s->type = SOCKET_SERVER_UNIX;
-
+
return s;
-
+
fail:
if (fd >= 0)
- close(fd);
+ pa_close(fd);
return NULL;
}
@@ -232,22 +243,23 @@ pa_socket_server* pa_socket_server_new_ipv4(pa_mainloop_api *m, uint32_t address
struct sockaddr_in sa;
int on = 1;
- assert(m && port);
+ pa_assert(m);
+ pa_assert(port);
if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
pa_log("socket(PF_INET): %s", pa_cstrerror(errno));
goto fail;
}
- pa_fd_set_cloexec(fd, 1);
+ pa_make_fd_cloexec(fd);
#ifdef SO_REUSEADDR
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
pa_log("setsockopt(): %s", pa_cstrerror(errno));
#endif
- pa_socket_tcp_low_delay(fd);
-
+ pa_make_tcp_socket_low_delay(fd);
+
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_port = htons(port);
@@ -269,10 +281,10 @@ pa_socket_server* pa_socket_server_new_ipv4(pa_mainloop_api *m, uint32_t address
}
return ss;
-
+
fail:
if (fd >= 0)
- close(fd);
+ pa_close(fd);
return NULL;
}
@@ -281,28 +293,31 @@ pa_socket_server* pa_socket_server_new_ipv6(pa_mainloop_api *m, const uint8_t ad
pa_socket_server *ss;
int fd = -1;
struct sockaddr_in6 sa;
- int on = 1;
+ int on;
- assert(m && port);
+ pa_assert(m);
+ pa_assert(port > 0);
if ((fd = socket(PF_INET6, SOCK_STREAM, 0)) < 0) {
pa_log("socket(PF_INET6): %s", pa_cstrerror(errno));
goto fail;
}
- pa_fd_set_cloexec(fd, 1);
+ pa_make_fd_cloexec(fd);
#ifdef IPV6_V6ONLY
+ on = 1;
if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0)
pa_log("setsockopt(IPPROTO_IPV6, IPV6_V6ONLY): %s", pa_cstrerror(errno));
#endif
#ifdef SO_REUSEADDR
+ on = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
pa_log("setsockopt(SOL_SOCKET, SO_REUSEADDR, 1): %s", pa_cstrerror(errno));
#endif
- pa_socket_tcp_low_delay(fd);
+ pa_make_tcp_socket_low_delay(fd);
memset(&sa, 0, sizeof(sa));
sa.sin6_family = AF_INET6;
@@ -323,50 +338,50 @@ pa_socket_server* pa_socket_server_new_ipv6(pa_mainloop_api *m, const uint8_t ad
ss->type = SOCKET_SERVER_IPV6;
ss->tcpwrap_service = pa_xstrdup(tcpwrap_service);
}
-
+
return ss;
-
+
fail:
if (fd >= 0)
- close(fd);
+ pa_close(fd);
return NULL;
}
pa_socket_server* pa_socket_server_new_ipv4_loopback(pa_mainloop_api *m, uint16_t port, const char *tcpwrap_service) {
- assert(m);
- assert(port > 0);
+ pa_assert(m);
+ pa_assert(port > 0);
return pa_socket_server_new_ipv4(m, INADDR_LOOPBACK, port, tcpwrap_service);
}
pa_socket_server* pa_socket_server_new_ipv6_loopback(pa_mainloop_api *m, uint16_t port, const char *tcpwrap_service) {
- assert(m);
- assert(port > 0);
+ pa_assert(m);
+ pa_assert(port > 0);
return pa_socket_server_new_ipv6(m, in6addr_loopback.s6_addr, port, tcpwrap_service);
}
pa_socket_server* pa_socket_server_new_ipv4_any(pa_mainloop_api *m, uint16_t port, const char *tcpwrap_service) {
- assert(m);
- assert(port > 0);
-
+ pa_assert(m);
+ pa_assert(port > 0);
+
return pa_socket_server_new_ipv4(m, INADDR_ANY, port, tcpwrap_service);
}
pa_socket_server* pa_socket_server_new_ipv6_any(pa_mainloop_api *m, uint16_t port, const char *tcpwrap_service) {
- assert(m);
- assert(port > 0);
-
+ pa_assert(m);
+ pa_assert(port > 0);
+
return pa_socket_server_new_ipv6(m, in6addr_any.s6_addr, port, tcpwrap_service);
}
pa_socket_server* pa_socket_server_new_ipv4_string(pa_mainloop_api *m, const char *name, uint16_t port, const char *tcpwrap_service) {
struct in_addr ipv4;
-
- assert(m);
- assert(name);
- assert(port > 0);
+
+ pa_assert(m);
+ pa_assert(name);
+ pa_assert(port > 0);
if (inet_pton(AF_INET, name, &ipv4) > 0)
return pa_socket_server_new_ipv4(m, ntohl(ipv4.s_addr), port, tcpwrap_service);
@@ -376,10 +391,10 @@ pa_socket_server* pa_socket_server_new_ipv4_string(pa_mainloop_api *m, const cha
pa_socket_server* pa_socket_server_new_ipv6_string(pa_mainloop_api *m, const char *name, uint16_t port, const char *tcpwrap_service) {
struct in6_addr ipv6;
-
- assert(m);
- assert(name);
- assert(port > 0);
+
+ pa_assert(m);
+ pa_assert(name);
+ pa_assert(port > 0);
if (inet_pton(AF_INET6, name, &ipv6) > 0)
return pa_socket_server_new_ipv6(m, ipv6.s6_addr, port, tcpwrap_service);
@@ -388,14 +403,14 @@ pa_socket_server* pa_socket_server_new_ipv6_string(pa_mainloop_api *m, const cha
}
static void socket_server_free(pa_socket_server*s) {
- assert(s);
+ pa_assert(s);
if (s->filename) {
unlink(s->filename);
pa_xfree(s->filename);
}
- close(s->fd);
+ pa_close(s->fd);
pa_xfree(s->tcpwrap_service);
@@ -404,22 +419,27 @@ static void socket_server_free(pa_socket_server*s) {
}
void pa_socket_server_unref(pa_socket_server *s) {
- assert(s && s->ref >= 1);
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
- if (!(--(s->ref)))
+ if (PA_REFCNT_DEC(s) <= 0)
socket_server_free(s);
}
-void pa_socket_server_set_callback(pa_socket_server*s, void (*on_connection)(pa_socket_server*s, pa_iochannel *io, void *userdata), void *userdata) {
- assert(s && s->ref >= 1);
+void pa_socket_server_set_callback(pa_socket_server*s, pa_socket_server_on_connection_cb_t on_connection, void *userdata) {
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
s->on_connection = on_connection;
s->userdata = userdata;
}
char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l) {
- assert(s && c && l > 0);
-
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+ pa_assert(c);
+ pa_assert(l > 0);
+
switch (s->type) {
case SOCKET_SERVER_IPV6: {
struct sockaddr_in6 sa;
@@ -434,24 +454,24 @@ char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l) {
char fqdn[256];
if (!pa_get_fqdn(fqdn, sizeof(fqdn)))
return NULL;
-
- snprintf(c, l, "tcp6:%s:%u", fqdn, (unsigned) ntohs(sa.sin6_port));
-
+
+ pa_snprintf(c, l, "tcp6:%s:%u", fqdn, (unsigned) ntohs(sa.sin6_port));
+
} else if (memcmp(&in6addr_loopback, &sa.sin6_addr, sizeof(in6addr_loopback)) == 0) {
char hn[256];
if (!pa_get_host_name(hn, sizeof(hn)))
return NULL;
-
- snprintf(c, l, "{%s}tcp6:localhost:%u", hn, (unsigned) ntohs(sa.sin6_port));
+
+ pa_snprintf(c, l, "{%s}tcp6:localhost:%u", hn, (unsigned) ntohs(sa.sin6_port));
} else {
char ip[INET6_ADDRSTRLEN];
-
+
if (!inet_ntop(AF_INET6, &sa.sin6_addr, ip, sizeof(ip))) {
pa_log("inet_ntop(): %s", pa_cstrerror(errno));
return NULL;
}
-
- snprintf(c, l, "tcp6:[%s]:%u", ip, (unsigned) ntohs(sa.sin6_port));
+
+ pa_snprintf(c, l, "tcp6:[%s]:%u", ip, (unsigned) ntohs(sa.sin6_port));
}
return c;
@@ -470,14 +490,14 @@ char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l) {
char fqdn[256];
if (!pa_get_fqdn(fqdn, sizeof(fqdn)))
return NULL;
-
- snprintf(c, l, "tcp:%s:%u", fqdn, (unsigned) ntohs(sa.sin_port));
+
+ pa_snprintf(c, l, "tcp:%s:%u", fqdn, (unsigned) ntohs(sa.sin_port));
} else if (sa.sin_addr.s_addr == INADDR_LOOPBACK) {
char hn[256];
if (!pa_get_host_name(hn, sizeof(hn)))
return NULL;
-
- snprintf(c, l, "{%s}tcp:localhost:%u", hn, (unsigned) ntohs(sa.sin_port));
+
+ pa_snprintf(c, l, "{%s}tcp:localhost:%u", hn, (unsigned) ntohs(sa.sin_port));
} else {
char ip[INET_ADDRSTRLEN];
@@ -485,11 +505,10 @@ char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l) {
pa_log("inet_ntop(): %s", pa_cstrerror(errno));
return NULL;
}
-
- snprintf(c, l, "tcp:[%s]:%u", ip, (unsigned) ntohs(sa.sin_port));
+ pa_snprintf(c, l, "tcp:[%s]:%u", ip, (unsigned) ntohs(sa.sin_port));
}
-
+
return c;
}
@@ -498,11 +517,11 @@ char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l) {
if (!s->filename)
return NULL;
-
+
if (!pa_get_host_name(hn, sizeof(hn)))
return NULL;
- snprintf(c, l, "{%s}unix:%s", hn, s->filename);
+ pa_snprintf(c, l, "{%s}unix:%s", hn, s->filename);
return c;
}
diff --git a/src/pulsecore/socket-server.h b/src/pulsecore/socket-server.h
index d90c8194..1edfb432 100644
--- a/src/pulsecore/socket-server.h
+++ b/src/pulsecore/socket-server.h
@@ -1,21 +1,22 @@
#ifndef foosocketserverhfoo
#define foosocketserverhfoo
-/* $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
@@ -44,7 +45,9 @@ pa_socket_server* pa_socket_server_new_ipv6_string(pa_mainloop_api *m, const cha
void pa_socket_server_unref(pa_socket_server*s);
pa_socket_server* pa_socket_server_ref(pa_socket_server *s);
-void pa_socket_server_set_callback(pa_socket_server*s, void (*on_connection)(pa_socket_server*s, pa_iochannel *io, void *userdata), void *userdata);
+typedef void (*pa_socket_server_on_connection_cb_t)(pa_socket_server*s, pa_iochannel *io, void *userdata);
+
+void pa_socket_server_set_callback(pa_socket_server*s, pa_socket_server_on_connection_cb_t connection_cb, void *userdata);
char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l);
diff --git a/src/pulsecore/socket-util.c b/src/pulsecore/socket-util.c
index 856c28e8..f721f699 100644
--- a/src/pulsecore/socket-util.c
+++ b/src/pulsecore/socket-util.c
@@ -1,18 +1,20 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
+ Copyright 2004-2006 Lennart Poettering
+ Copyright 2004 Joe Marcus Clarke
+ Copyright 2006-2007 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
@@ -27,7 +29,6 @@
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
-#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
@@ -71,24 +72,24 @@
#include <pulsecore/core-error.h>
#include <pulsecore/core-util.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
#include "socket-util.h"
void pa_socket_peer_to_string(int fd, char *c, size_t l) {
struct stat st;
- assert(c && l && fd >= 0);
-
+ pa_assert(fd >= 0);
+ pa_assert(c);
+ pa_assert(l > 0);
+
#ifndef OS_IS_WIN32
- if (fstat(fd, &st) < 0) {
- snprintf(c, l, "Invalid client fd");
- return;
- }
+ pa_assert_se(fstat(fd, &st) == 0);
#endif
#ifndef OS_IS_WIN32
if (S_ISSOCK(st.st_mode)) {
-#endif
+#endif
union {
struct sockaddr sa;
struct sockaddr_in in;
@@ -98,18 +99,18 @@ void pa_socket_peer_to_string(int fd, char *c, size_t l) {
#endif
} sa;
socklen_t sa_len = sizeof(sa);
-
+
if (getpeername(fd, &sa.sa, &sa_len) >= 0) {
if (sa.sa.sa_family == AF_INET) {
uint32_t ip = ntohl(sa.in.sin_addr.s_addr);
-
- snprintf(c, l, "TCP/IP client from %i.%i.%i.%i:%u",
- ip >> 24,
- (ip >> 16) & 0xFF,
- (ip >> 8) & 0xFF,
- ip & 0xFF,
- ntohs(sa.in.sin_port));
+
+ pa_snprintf(c, l, "TCP/IP client from %i.%i.%i.%i:%u",
+ ip >> 24,
+ (ip >> 16) & 0xFF,
+ (ip >> 8) & 0xFF,
+ ip & 0xFF,
+ ntohs(sa.in.sin_port));
return;
} else if (sa.sa.sa_family == AF_INET6) {
char buf[INET6_ADDRSTRLEN];
@@ -117,94 +118,107 @@ void pa_socket_peer_to_string(int fd, char *c, size_t l) {
res = inet_ntop(AF_INET6, &sa.in6.sin6_addr, buf, sizeof(buf));
if (res) {
- snprintf(c, l, "TCP/IP client from [%s]:%u", buf, ntohs(sa.in6.sin6_port));
+ pa_snprintf(c, l, "TCP/IP client from [%s]:%u", buf, ntohs(sa.in6.sin6_port));
return;
}
#ifdef HAVE_SYS_UN_H
} else if (sa.sa.sa_family == AF_UNIX) {
- snprintf(c, l, "UNIX socket client");
+ pa_snprintf(c, l, "UNIX socket client");
return;
#endif
}
-
}
+
#ifndef OS_IS_WIN32
- snprintf(c, l, "Unknown network client");
+ pa_snprintf(c, l, "Unknown network client");
return;
} else if (S_ISCHR(st.st_mode) && (fd == 0 || fd == 1)) {
- snprintf(c, l, "STDIN/STDOUT client");
+ pa_snprintf(c, l, "STDIN/STDOUT client");
return;
}
#endif /* OS_IS_WIN32 */
- snprintf(c, l, "Unknown client");
+ pa_snprintf(c, l, "Unknown client");
}
-int pa_socket_low_delay(int fd) {
+void pa_make_socket_low_delay(int fd) {
+
#ifdef SO_PRIORITY
int priority;
- assert(fd >= 0);
+ pa_assert(fd >= 0);
- priority = 7;
+ priority = 6;
if (setsockopt(fd, SOL_SOCKET, SO_PRIORITY, (void*)&priority, sizeof(priority)) < 0)
- return -1;
+ pa_log_warn("SO_PRIORITY failed: %s", pa_cstrerror(errno));
#endif
-
- return 0;
}
-int pa_socket_tcp_low_delay(int fd) {
- int ret, tos, on;
+void pa_make_tcp_socket_low_delay(int fd) {
+ pa_assert(fd >= 0);
- assert(fd >= 0);
-
- ret = pa_socket_low_delay(fd);
-
- on = 1;
- tos = 0;
+ pa_make_socket_low_delay(fd);
#if defined(SOL_TCP) || defined(IPPROTO_TCP)
+ {
+ int on = 1;
#if defined(SOL_TCP)
- if (setsockopt(fd, SOL_TCP, TCP_NODELAY, (void*)&on, sizeof(on)) < 0)
+ if (setsockopt(fd, SOL_TCP, TCP_NODELAY, (void*)&on, sizeof(on)) < 0)
#else
- if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void*)&on, sizeof(on)) < 0)
+ if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void*)&on, sizeof(on)) < 0)
#endif
- ret = -1;
+ pa_log_warn("TCP_NODELAY failed: %s", pa_cstrerror(errno));
+ }
#endif
-#if defined(IPTOS_LOWDELAY) && defined(IP_TOS) && (defined(SOL_IP) || \
- defined(IPPROTO_IP))
- tos = IPTOS_LOWDELAY;
+#if defined(IPTOS_LOWDELAY) && defined(IP_TOS) && (defined(SOL_IP) || defined(IPPROTO_IP))
+ {
+ int tos = IPTOS_LOWDELAY;
#ifdef SOL_IP
- if (setsockopt(fd, SOL_IP, IP_TOS, (void*)&tos, sizeof(tos)) < 0)
+ if (setsockopt(fd, SOL_IP, IP_TOS, (void*)&tos, sizeof(tos)) < 0)
#else
- if (setsockopt(fd, IPPROTO_IP, IP_TOS, (void*)&tos, sizeof(tos)) < 0)
+ if (setsockopt(fd, IPPROTO_IP, IP_TOS, (void*)&tos, sizeof(tos)) < 0)
#endif
- ret = -1;
+ pa_log_warn("IP_TOS failed: %s", pa_cstrerror(errno));
+ }
#endif
+}
- return ret;
+void pa_make_udp_socket_low_delay(int fd) {
+ pa_assert(fd >= 0);
+
+ pa_make_socket_low_delay(fd);
+#if defined(IPTOS_LOWDELAY) && defined(IP_TOS) && (defined(SOL_IP) || defined(IPPROTO_IP))
+ {
+ int tos = IPTOS_LOWDELAY;
+#ifdef SOL_IP
+ if (setsockopt(fd, SOL_IP, IP_TOS, (void*)&tos, sizeof(tos)) < 0)
+#else
+ if (setsockopt(fd, IPPROTO_IP, IP_TOS, (void*)&tos, sizeof(tos)) < 0)
+#endif
+ pa_log_warn("IP_TOS failed: %s", pa_cstrerror(errno));
+ }
+#endif
}
int pa_socket_set_rcvbuf(int fd, size_t l) {
- assert(fd >= 0);
+ pa_assert(fd >= 0);
-/* if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void*)&l, sizeof(l)) < 0) { */
-/* pa_log("SO_RCVBUF: %s", strerror(errno)); */
-/* return -1; */
-/* } */
+ if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void*)&l, sizeof(l)) < 0) {
+ pa_log_warn("SO_RCVBUF: %s", pa_cstrerror(errno));
+ return -1;
+ }
return 0;
}
int pa_socket_set_sndbuf(int fd, size_t l) {
- assert(fd >= 0);
+ pa_assert(fd >= 0);
-/* if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (void*)&l, sizeof(l)) < 0) { */
-/* pa_log("SO_SNDBUF: %s", strerror(errno)); */
-/* return -1; */
-/* } */
+ if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (void*)&l, sizeof(l)) < 0) {
+ pa_log("SO_SNDBUF: %s", pa_cstrerror(errno));
+ return -1;
+ }
return 0;
}
@@ -215,6 +229,8 @@ int pa_unix_socket_is_stale(const char *fn) {
struct sockaddr_un sa;
int fd = -1, ret = -1;
+ pa_assert(fn);
+
if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
pa_log("socket(): %s", pa_cstrerror(errno));
goto finish;
@@ -232,20 +248,22 @@ int pa_unix_socket_is_stale(const char *fn) {
finish:
if (fd >= 0)
- close(fd);
+ pa_close(fd);
return ret;
}
int pa_unix_socket_remove_stale(const char *fn) {
int r;
-
+
+ pa_assert(fn);
+
if ((r = pa_unix_socket_is_stale(fn)) < 0)
return errno != ENOENT ? -1 : 0;
if (!r)
return 0;
-
+
/* Yes, here is a race condition. But who cares? */
if (unlink(fn) < 0)
return -1;
@@ -264,3 +282,40 @@ int pa_unix_socket_remove_stale(const char *fn) {
}
#endif /* HAVE_SYS_UN_H */
+
+
+pa_bool_t pa_socket_address_is_local(const struct sockaddr *sa) {
+ pa_assert(sa);
+
+ switch (sa->sa_family) {
+ case AF_UNIX:
+ return TRUE;
+
+ case AF_INET:
+ return ((const struct sockaddr_in*) sa)->sin_addr.s_addr == INADDR_LOOPBACK;
+
+ case AF_INET6:
+ return memcmp(&((const struct sockaddr_in6*) sa)->sin6_addr, &in6addr_loopback, sizeof(struct in6_addr)) == 0;
+
+ default:
+ return FALSE;
+ }
+}
+
+pa_bool_t pa_socket_is_local(int fd) {
+
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in in;
+ struct sockaddr_in6 in6;
+#ifdef HAVE_SYS_UN_H
+ struct sockaddr_un un;
+#endif
+ } sa;
+ socklen_t sa_len = sizeof(sa);
+
+ if (getpeername(fd, &sa.sa, &sa_len) < 0)
+ return FALSE;
+
+ return pa_socket_address_is_local(&sa.sa);
+}
diff --git a/src/pulsecore/socket-util.h b/src/pulsecore/socket-util.h
index f8248ae7..7a40285a 100644
--- a/src/pulsecore/socket-util.h
+++ b/src/pulsecore/socket-util.h
@@ -1,21 +1,22 @@
#ifndef foosocketutilhfoo
#define foosocketutilhfoo
-/* $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
@@ -23,11 +24,15 @@
***/
#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <pulsecore/macro.h>
void pa_socket_peer_to_string(int fd, char *c, size_t l);
-int pa_socket_low_delay(int fd);
-int pa_socket_tcp_low_delay(int fd);
+void pa_make_socket_low_delay(int fd);
+void pa_make_tcp_socket_low_delay(int fd);
+void pa_make_udp_socket_low_delay(int fd);
int pa_socket_set_sndbuf(int fd, size_t l);
int pa_socket_set_rcvbuf(int fd, size_t l);
@@ -35,4 +40,7 @@ int pa_socket_set_rcvbuf(int fd, size_t l);
int pa_unix_socket_is_stale(const char *fn);
int pa_unix_socket_remove_stale(const char *fn);
+pa_bool_t pa_socket_address_is_local(const struct sockaddr *sa);
+pa_bool_t pa_socket_is_local(int fd);
+
#endif
diff --git a/src/pulsecore/sound-file-stream.c b/src/pulsecore/sound-file-stream.c
index d2ffeeed..8eedf830 100644
--- a/src/pulsecore/sound-file-stream.c
+++ b/src/pulsecore/sound-file-stream.c
@@ -1,18 +1,18 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
+ Copyright 2004-2008 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
@@ -24,135 +24,272 @@
#endif
#include <stdlib.h>
-#include <assert.h>
#include <stdio.h>
#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
#include <sndfile.h>
#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+#include <pulsecore/core-error.h>
#include <pulsecore/sink-input.h>
#include <pulsecore/log.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/sample-util.h>
#include "sound-file-stream.h"
-#define BUF_SIZE (1024*10)
+#define MEMBLOCKQ_MAXLENGTH (16*1024*1024)
-struct userdata {
- SNDFILE *sndfile;
+typedef struct file_stream {
+ pa_msgobject parent;
+ pa_core *core;
pa_sink_input *sink_input;
- pa_memchunk memchunk;
+
+ SNDFILE *sndfile;
sf_count_t (*readf_function)(SNDFILE *sndfile, void *ptr, sf_count_t frames);
+
+ /* We need this memblockq here to easily fulfill rewind requests
+ * (even beyond the file start!) */
+ pa_memblockq *memblockq;
+} file_stream;
+
+enum {
+ FILE_STREAM_MESSAGE_UNLINK
};
-static void free_userdata(struct userdata *u) {
- assert(u);
- if (u->sink_input) {
- pa_sink_input_disconnect(u->sink_input);
- pa_sink_input_unref(u->sink_input);
- }
-
- if (u->memchunk.memblock)
- pa_memblock_unref(u->memchunk.memblock);
+PA_DECLARE_CLASS(file_stream);
+#define FILE_STREAM(o) (file_stream_cast(o))
+static PA_DEFINE_CHECK_TYPE(file_stream, pa_msgobject);
+
+/* Called from main context */
+static void file_stream_unlink(file_stream *u) {
+ pa_assert(u);
+
+ if (!u->sink_input)
+ return;
+
+ pa_sink_input_unlink(u->sink_input);
+ pa_sink_input_unref(u->sink_input);
+ u->sink_input = NULL;
+
+ /* Make sure we don't decrease the ref count twice. */
+ file_stream_unref(u);
+}
+
+/* Called from main context */
+static void file_stream_free(pa_object *o) {
+ file_stream *u = FILE_STREAM(o);
+ pa_assert(u);
+
+ if (u->memblockq)
+ pa_memblockq_free(u->memblockq);
+
if (u->sndfile)
sf_close(u->sndfile);
pa_xfree(u);
}
-static void sink_input_kill(pa_sink_input *i) {
- assert(i && i->userdata);
- free_userdata(i->userdata);
+/* Called from main context */
+static int file_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
+ file_stream *u = FILE_STREAM(o);
+ file_stream_assert_ref(u);
+
+ switch (code) {
+ case FILE_STREAM_MESSAGE_UNLINK:
+ file_stream_unlink(u);
+ break;
+ }
+
+ return 0;
}
-static int sink_input_peek(pa_sink_input *i, pa_memchunk *chunk) {
- struct userdata *u;
- assert(i && chunk && i->userdata);
- u = i->userdata;
+/* Called from main context */
+static void sink_input_kill_cb(pa_sink_input *i) {
+ file_stream *u;
- if (!u->memchunk.memblock) {
- uint32_t fs = pa_frame_size(&i->sample_spec);
- sf_count_t n;
+ pa_sink_input_assert_ref(i);
+ u = FILE_STREAM(i->userdata);
+ file_stream_assert_ref(u);
+
+ file_stream_unlink(u);
+}
+
+/* Called from IO thread context */
+static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) {
+ file_stream *u;
+
+ pa_sink_input_assert_ref(i);
+ u = FILE_STREAM(i->userdata);
+ file_stream_assert_ref(u);
+
+ /* If we are added for the first time, ask for a rewinding so that
+ * we are heard right-away. */
+ if (PA_SINK_INPUT_IS_LINKED(state) &&
+ i->thread_info.state == PA_SINK_INPUT_INIT)
+ pa_sink_input_request_rewind(i, 0, FALSE, TRUE);
+}
+
+/* Called from IO thread context */
+static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
+ file_stream *u;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert(chunk);
+ u = FILE_STREAM(i->userdata);
+ file_stream_assert_ref(u);
+
+ if (!u->memblockq)
+ return -1;
+
+ for (;;) {
+ pa_memchunk tchunk;
+ size_t fs;
void *p;
+ sf_count_t n;
- u->memchunk.memblock = pa_memblock_new(i->sink->core->mempool, BUF_SIZE);
- u->memchunk.index = 0;
+ if (pa_memblockq_peek(u->memblockq, chunk) >= 0) {
+ chunk->length = PA_MIN(chunk->length, length);
+ pa_memblockq_drop(u->memblockq, chunk->length);
+ return 0;
+ }
- p = pa_memblock_acquire(u->memchunk.memblock);
-
- if (u->readf_function) {
- if ((n = u->readf_function(u->sndfile, p, BUF_SIZE/fs)) <= 0)
- n = 0;
+ if (!u->sndfile)
+ break;
+
+ tchunk.memblock = pa_memblock_new(i->sink->core->mempool, length);
+ tchunk.index = 0;
- u->memchunk.length = n * fs;
+ p = pa_memblock_acquire(tchunk.memblock);
+
+ if (u->readf_function) {
+ fs = pa_frame_size(&i->sample_spec);
+ n = u->readf_function(u->sndfile, p, length/fs);
} else {
- if ((n = sf_read_raw(u->sndfile, p, BUF_SIZE)) <= 0)
- n = 0;
-
- u->memchunk.length = n;
+ fs = 1;
+ n = sf_read_raw(u->sndfile, p, length);
}
- pa_memblock_release(u->memchunk.memblock);
-
- if (!u->memchunk.length) {
- free_userdata(u);
- return -1;
+ pa_memblock_release(tchunk.memblock);
+
+ if (n <= 0) {
+ pa_memblock_unref(tchunk.memblock);
+
+ sf_close(u->sndfile);
+ u->sndfile = NULL;
+ break;
}
+
+ tchunk.length = n * fs;
+
+ pa_memblockq_push(u->memblockq, &tchunk);
+ pa_memblock_unref(tchunk.memblock);
}
- *chunk = u->memchunk;
- pa_memblock_ref(chunk->memblock);
- assert(chunk->length);
- return 0;
+ if (pa_sink_input_safe_to_remove(i)) {
+ pa_memblockq_free(u->memblockq);
+ u->memblockq = NULL;
+
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u), FILE_STREAM_MESSAGE_UNLINK, NULL, 0, NULL, NULL);
+ }
+
+ return -1;
+ }
+
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
+ file_stream *u;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert(nbytes > 0);
+ u = FILE_STREAM(i->userdata);
+ file_stream_assert_ref(u);
+
+ pa_log("backwards %lu", (unsigned long) nbytes);
+
+ if (!u->memblockq)
+ return;
+
+ pa_memblockq_rewind(u->memblockq, nbytes);
}
-static void sink_input_drop(pa_sink_input *i, const pa_memchunk*chunk, size_t length) {
- struct userdata *u;
- assert(i && chunk && length && i->userdata);
- u = i->userdata;
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
+ file_stream *u;
- assert(!memcmp(chunk, &u->memchunk, sizeof(chunk)));
- assert(length <= u->memchunk.length);
+ pa_sink_input_assert_ref(i);
+ u = FILE_STREAM(i->userdata);
+ file_stream_assert_ref(u);
- u->memchunk.index += length;
- u->memchunk.length -= length;
+ if (!u->memblockq)
+ return;
- if (u->memchunk.length <= 0) {
- pa_memblock_unref(u->memchunk.memblock);
- u->memchunk.memblock = NULL;
- u->memchunk.index = u->memchunk.length = 0;
- }
+ pa_memblockq_set_maxrewind(u->memblockq, nbytes);
}
int pa_play_file(
pa_sink *sink,
const char *fname,
const pa_cvolume *volume) {
-
- struct userdata *u = NULL;
+
+ file_stream *u = NULL;
SF_INFO sfinfo;
pa_sample_spec ss;
pa_sink_input_new_data data;
-
- assert(sink);
- assert(fname);
+ int fd;
+
+ pa_assert(sink);
+ pa_assert(fname);
- u = pa_xnew(struct userdata, 1);
+ u = pa_msgobject_new(file_stream);
+ u->parent.parent.free = file_stream_free;
+ u->parent.process_msg = file_stream_process_msg;
+ u->core = sink->core;
u->sink_input = NULL;
- u->memchunk.memblock = NULL;
- u->memchunk.index = u->memchunk.length = 0;
u->sndfile = NULL;
+ u->readf_function = NULL;
+ u->memblockq = NULL;
memset(&sfinfo, 0, sizeof(sfinfo));
- if (!(u->sndfile = sf_open(fname, SFM_READ, &sfinfo))) {
+ if ((fd = open(fname, O_RDONLY
+#ifdef O_NOCTTY
+ |O_NOCTTY
+#endif
+ )) < 0) {
+ pa_log("Failed to open file %s: %s", fname, pa_cstrerror(errno));
+ goto fail;
+ }
+
+ /* FIXME: For now we just use posix_fadvise to avoid page faults
+ * when accessing the file data. Eventually we should move the
+ * file reader into the main event loop and pass the data over the
+ * asyncmsgq. */
+
+#ifdef HAVE_POSIX_FADVISE
+ if (posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL) < 0) {
+ pa_log_warn("POSIX_FADV_SEQUENTIAL failed: %s", pa_cstrerror(errno));
+ goto fail;
+ } else
+ pa_log_debug("POSIX_FADV_SEQUENTIAL succeeded.");
+
+ if (posix_fadvise(fd, 0, 0, POSIX_FADV_WILLNEED) < 0) {
+ pa_log_warn("POSIX_FADV_WILLNEED failed: %s", pa_cstrerror(errno));
+ goto fail;
+ } else
+ pa_log_debug("POSIX_FADV_WILLNEED succeeded.");
+#endif
+
+ if (!(u->sndfile = sf_open_fd(fd, SFM_READ, &sfinfo, 1))) {
pa_log("Failed to open file %s", fname);
+ pa_close(fd);
goto fail;
}
- u->readf_function = NULL;
-
switch (sfinfo.format & 0xFF) {
case SF_FORMAT_PCM_16:
case SF_FORMAT_PCM_U8:
@@ -164,7 +301,7 @@ int pa_play_file(
case SF_FORMAT_ULAW:
ss.format = PA_SAMPLE_ULAW;
break;
-
+
case SF_FORMAT_ALAW:
ss.format = PA_SAMPLE_ALAW;
break;
@@ -175,7 +312,7 @@ int pa_play_file(
u->readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *ptr, sf_count_t frames)) sf_readf_float;
break;
}
-
+
ss.rate = sfinfo.samplerate;
ss.channels = sfinfo.channels;
@@ -187,25 +324,36 @@ int pa_play_file(
pa_sink_input_new_data_init(&data);
data.sink = sink;
data.driver = __FILE__;
- data.name = fname;
pa_sink_input_new_data_set_sample_spec(&data, &ss);
pa_sink_input_new_data_set_volume(&data, volume);
-
- if (!(u->sink_input = pa_sink_input_new(sink->core, &data, 0)))
+ pa_proplist_sets(data.proplist, PA_PROP_MEDIA_NAME, pa_path_get_filename(fname));
+ pa_proplist_sets(data.proplist, PA_PROP_MEDIA_FILENAME, fname);
+
+ u->sink_input = pa_sink_input_new(sink->core, &data, 0);
+ pa_sink_input_new_data_done(&data);
+
+ if (!u->sink_input)
goto fail;
- u->sink_input->peek = sink_input_peek;
- u->sink_input->drop = sink_input_drop;
- u->sink_input->kill = sink_input_kill;
+ u->sink_input->pop = sink_input_pop_cb;
+ u->sink_input->process_rewind = sink_input_process_rewind_cb;
+ u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
+ u->sink_input->kill = sink_input_kill_cb;
+ u->sink_input->state_change = sink_input_state_change_cb;
u->sink_input->userdata = u;
-
- pa_sink_notify(u->sink_input->sink);
+
+ u->memblockq = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, pa_frame_size(&ss), 1, 1, 0, NULL);
+
+ pa_sink_input_put(u->sink_input);
+
+ /* The reference to u is dangling here, because we want to keep
+ * this stream around until it is fully played. */
return 0;
fail:
if (u)
- free_userdata(u);
-
+ file_stream_unref(u);
+
return -1;
}
diff --git a/src/pulsecore/sound-file-stream.h b/src/pulsecore/sound-file-stream.h
index 28e6a8ba..4cc69146 100644
--- a/src/pulsecore/sound-file-stream.h
+++ b/src/pulsecore/sound-file-stream.h
@@ -1,21 +1,21 @@
#ifndef foosoundfilestreamhfoo
#define foosoundfilestreamhfoo
-/* $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
diff --git a/src/pulsecore/sound-file.c b/src/pulsecore/sound-file.c
index c74a1586..3183ede6 100644
--- a/src/pulsecore/sound-file.c
+++ b/src/pulsecore/sound-file.c
@@ -1,18 +1,18 @@
-/* $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
@@ -24,35 +24,63 @@
#endif
#include <string.h>
-#include <assert.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
#include <sndfile.h>
#include <pulse/sample.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/core-util.h>
#include "sound-file.h"
#include "core-scache.h"
-int pa_sound_file_load(pa_mempool *pool, const char *fname, pa_sample_spec *ss, pa_channel_map *map, pa_memchunk *chunk) {
- SNDFILE*sf = NULL;
+int pa_sound_file_load(
+ pa_mempool *pool,
+ const char *fname,
+ pa_sample_spec *ss,
+ pa_channel_map *map,
+ pa_memchunk *chunk) {
+
+ SNDFILE *sf = NULL;
SF_INFO sfinfo;
int ret = -1;
size_t l;
sf_count_t (*readf_function)(SNDFILE *sndfile, void *ptr, sf_count_t frames) = NULL;
void *ptr = NULL;
-
- assert(fname);
- assert(ss);
- assert(chunk);
+ int fd;
- chunk->memblock = NULL;
- chunk->index = chunk->length = 0;
+ pa_assert(fname);
+ pa_assert(ss);
+ pa_assert(chunk);
+ pa_memchunk_reset(chunk);
memset(&sfinfo, 0, sizeof(sfinfo));
- if (!(sf = sf_open(fname, SFM_READ, &sfinfo))) {
+ if ((fd = open(fname, O_RDONLY
+#ifdef O_NOCTTY
+ |O_NOCTTY
+#endif
+ )) < 0) {
+ pa_log("Failed to open file %s: %s", fname, pa_cstrerror(errno));
+ goto finish;
+ }
+
+#ifdef HAVE_POSIX_FADVISE
+ if (posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL) < 0) {
+ pa_log_warn("POSIX_FADV_SEQUENTIAL failed: %s", pa_cstrerror(errno));
+ goto finish;
+ } else
+ pa_log_debug("POSIX_FADV_SEQUENTIAL succeeded.");
+#endif
+
+ if (!(sf = sf_open_fd(fd, SFM_READ, &sfinfo, 1))) {
pa_log("Failed to open file %s", fname);
+ pa_close(fd);
goto finish;
}
@@ -67,7 +95,7 @@ int pa_sound_file_load(pa_mempool *pool, const char *fname, pa_sample_spec *ss,
case SF_FORMAT_ULAW:
ss->format = PA_SAMPLE_ULAW;
break;
-
+
case SF_FORMAT_ALAW:
ss->format = PA_SAMPLE_ALAW;
break;
@@ -89,26 +117,25 @@ int pa_sound_file_load(pa_mempool *pool, const char *fname, pa_sample_spec *ss,
}
if (map)
- pa_channel_map_init_auto(map, ss->channels, PA_CHANNEL_MAP_DEFAULT);
-
- if ((l = pa_frame_size(ss)*sfinfo.frames) > PA_SCACHE_ENTRY_SIZE_MAX) {
+ pa_channel_map_init_extend(map, ss->channels, PA_CHANNEL_MAP_DEFAULT);
+
+ if ((l = pa_frame_size(ss) * sfinfo.frames) > PA_SCACHE_ENTRY_SIZE_MAX) {
pa_log("File too large");
goto finish;
}
chunk->memblock = pa_memblock_new(pool, l);
- assert(chunk->memblock);
chunk->index = 0;
chunk->length = l;
ptr = pa_memblock_acquire(chunk->memblock);
-
+
if ((readf_function && readf_function(sf, ptr, sfinfo.frames) != sfinfo.frames) ||
- (!readf_function && sf_read_raw(sf, ptr, l) != l)) {
+ (!readf_function && sf_read_raw(sf, ptr, l) != (sf_count_t) l)) {
pa_log("Premature file end");
goto finish;
}
-
+
ret = 0;
finish:
@@ -118,22 +145,24 @@ finish:
if (ptr)
pa_memblock_release(chunk->memblock);
-
+
if (ret != 0 && chunk->memblock)
pa_memblock_unref(chunk->memblock);
-
+
return ret;
-
}
int pa_sound_file_too_big_to_cache(const char *fname) {
+
SNDFILE*sf = NULL;
SF_INFO sfinfo;
pa_sample_spec ss;
+ pa_assert(fname);
+
if (!(sf = sf_open(fname, SFM_READ, &sfinfo))) {
pa_log("Failed to open file %s", fname);
- return 0;
+ return -1;
}
sf_close(sf);
@@ -148,7 +177,7 @@ int pa_sound_file_too_big_to_cache(const char *fname) {
case SF_FORMAT_ULAW:
ss.format = PA_SAMPLE_ULAW;
break;
-
+
case SF_FORMAT_ALAW:
ss.format = PA_SAMPLE_ALAW;
break;
@@ -163,8 +192,13 @@ int pa_sound_file_too_big_to_cache(const char *fname) {
ss.rate = sfinfo.samplerate;
ss.channels = sfinfo.channels;
+ if (!pa_sample_spec_valid(&ss)) {
+ pa_log("Unsupported sample format in file %s", fname);
+ return -1;
+ }
+
if ((pa_frame_size(&ss) * sfinfo.frames) > PA_SCACHE_ENTRY_SIZE_MAX) {
- pa_log("File too large %s", fname);
+ pa_log("File too large: %s", fname);
return 1;
}
diff --git a/src/pulsecore/sound-file.h b/src/pulsecore/sound-file.h
index 7e3c82ea..e4d703d3 100644
--- a/src/pulsecore/sound-file.h
+++ b/src/pulsecore/sound-file.h
@@ -1,21 +1,21 @@
#ifndef soundfilehfoo
#define soundfilehfoo
-/* $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
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index 352fce14..3d1abe30 100644
--- a/src/pulsecore/source-output.c
+++ b/src/pulsecore/source-output.c
@@ -1,18 +1,18 @@
-/* $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
@@ -24,336 +24,741 @@
#endif
#include <stdio.h>
-#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <pulse/utf8.h>
#include <pulse/xmalloc.h>
+#include <pulsecore/sample-util.h>
#include <pulsecore/core-subscribe.h>
#include <pulsecore/log.h>
#include <pulsecore/namereg.h>
+#include <pulsecore/core-util.h>
#include "source-output.h"
-#define CHECK_VALIDITY_RETURN_NULL(condition) \
-do {\
-if (!(condition)) \
- return NULL; \
-} while (0)
+#define MEMBLOCKQ_MAXLENGTH (32*1024*1024)
+
+static PA_DEFINE_CHECK_TYPE(pa_source_output, pa_msgobject);
+
+static void source_output_free(pa_object* mo);
pa_source_output_new_data* pa_source_output_new_data_init(pa_source_output_new_data *data) {
- assert(data);
-
+ pa_assert(data);
+
memset(data, 0, sizeof(*data));
data->resample_method = PA_RESAMPLER_INVALID;
+ data->proplist = pa_proplist_new();
+
return data;
}
+void pa_source_output_new_data_set_sample_spec(pa_source_output_new_data *data, const pa_sample_spec *spec) {
+ pa_assert(data);
+
+ if ((data->sample_spec_is_set = !!spec))
+ data->sample_spec = *spec;
+}
+
void pa_source_output_new_data_set_channel_map(pa_source_output_new_data *data, const pa_channel_map *map) {
- assert(data);
+ pa_assert(data);
if ((data->channel_map_is_set = !!map))
data->channel_map = *map;
}
-void pa_source_output_new_data_set_sample_spec(pa_source_output_new_data *data, const pa_sample_spec *spec) {
- assert(data);
+void pa_source_output_new_data_done(pa_source_output_new_data *data) {
+ pa_assert(data);
- if ((data->sample_spec_is_set = !!spec))
- data->sample_spec = *spec;
+ pa_proplist_free(data->proplist);
}
+/* Called from main context */
+static void reset_callbacks(pa_source_output *o) {
+ pa_assert(o);
+
+ o->push = NULL;
+ o->process_rewind = NULL;
+ o->update_max_rewind = NULL;
+ o->update_source_requested_latency = NULL;
+ o->update_source_latency_range = NULL;
+ o->attach = NULL;
+ o->detach = NULL;
+ o->suspend = NULL;
+ o->moved = NULL;
+ o->kill = NULL;
+ o->get_latency = NULL;
+ o->state_change = NULL;
+}
+
+/* Called from main context */
pa_source_output* pa_source_output_new(
pa_core *core,
pa_source_output_new_data *data,
pa_source_output_flags_t flags) {
-
+
pa_source_output *o;
pa_resampler *resampler = NULL;
- int r;
- char st[PA_SAMPLE_SPEC_SNPRINT_MAX];
+ char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
- assert(core);
- assert(data);
+ pa_assert(core);
+ pa_assert(data);
- if (!(flags & PA_SOURCE_OUTPUT_NO_HOOKS))
- if (pa_hook_fire(&core->hook_source_output_new, data) < 0)
- return NULL;
+ if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], data) < 0)
+ return NULL;
- CHECK_VALIDITY_RETURN_NULL(!data->driver || pa_utf8_valid(data->driver));
- CHECK_VALIDITY_RETURN_NULL(!data->name || pa_utf8_valid(data->name));
+ pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver));
if (!data->source)
- data->source = pa_namereg_get(core, NULL, PA_NAMEREG_SOURCE, 1);
+ data->source = pa_namereg_get(core, NULL, PA_NAMEREG_SOURCE, TRUE);
+
+ pa_return_null_if_fail(data->source);
+ pa_return_null_if_fail(pa_source_get_state(data->source) != PA_SOURCE_UNLINKED);
+
+ pa_return_null_if_fail(!data->direct_on_input || data->direct_on_input->sink == data->source->monitor_of);
- CHECK_VALIDITY_RETURN_NULL(data->source);
- CHECK_VALIDITY_RETURN_NULL(data->source->state == PA_SOURCE_RUNNING);
-
if (!data->sample_spec_is_set)
data->sample_spec = data->source->sample_spec;
-
- CHECK_VALIDITY_RETURN_NULL(pa_sample_spec_valid(&data->sample_spec));
- if (!data->channel_map_is_set)
- pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
-
- CHECK_VALIDITY_RETURN_NULL(pa_channel_map_valid(&data->channel_map));
- CHECK_VALIDITY_RETURN_NULL(data->channel_map.channels == data->sample_spec.channels);
+ pa_return_null_if_fail(pa_sample_spec_valid(&data->sample_spec));
+
+ if (!data->channel_map_is_set) {
+ if (data->source->channel_map.channels == data->sample_spec.channels)
+ data->channel_map = data->source->channel_map;
+ else
+ pa_return_null_if_fail(pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT));
+ }
+
+ pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map));
+ pa_return_null_if_fail(data->channel_map.channels == data->sample_spec.channels);
+
+ if (flags & PA_SOURCE_OUTPUT_FIX_FORMAT)
+ data->sample_spec.format = data->source->sample_spec.format;
+
+ if (flags & PA_SOURCE_OUTPUT_FIX_RATE)
+ data->sample_spec.rate = data->source->sample_spec.rate;
+
+ if (flags & PA_SOURCE_OUTPUT_FIX_CHANNELS) {
+ data->sample_spec.channels = data->source->sample_spec.channels;
+ data->channel_map = data->source->channel_map;
+ }
+
+ pa_assert(pa_sample_spec_valid(&data->sample_spec));
+ pa_assert(pa_channel_map_valid(&data->channel_map));
if (data->resample_method == PA_RESAMPLER_INVALID)
data->resample_method = core->resample_method;
- CHECK_VALIDITY_RETURN_NULL(data->resample_method < PA_RESAMPLER_MAX);
-
+ pa_return_null_if_fail(data->resample_method < PA_RESAMPLER_MAX);
+
+ if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE], data) < 0)
+ return NULL;
+
if (pa_idxset_size(data->source->outputs) >= PA_MAX_OUTPUTS_PER_SOURCE) {
pa_log("Failed to create source output: too many outputs per source.");
return NULL;
}
- if (!pa_sample_spec_equal(&data->sample_spec, &data->source->sample_spec) ||
- !pa_channel_map_equal(&data->channel_map, &data->source->channel_map))
+ if ((flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ||
+ !pa_sample_spec_equal(&data->sample_spec, &data->source->sample_spec) ||
+ !pa_channel_map_equal(&data->channel_map, &data->source->channel_map)) {
+
if (!(resampler = pa_resampler_new(
core->mempool,
&data->source->sample_spec, &data->source->channel_map,
&data->sample_spec, &data->channel_map,
- data->resample_method))) {
+ data->resample_method,
+ ((flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
+ ((flags & PA_SOURCE_OUTPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
+ (core->disable_remixing || (flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0)))) {
pa_log_warn("Unsupported resampling operation.");
return NULL;
}
-
- o = pa_xnew(pa_source_output, 1);
- o->ref = 1;
- o->state = PA_SOURCE_OUTPUT_RUNNING;
- o->name = pa_xstrdup(data->name);
+
+ data->resample_method = pa_resampler_get_method(resampler);
+ }
+
+ o = pa_msgobject_new(pa_source_output);
+ o->parent.parent.free = source_output_free;
+ o->parent.process_msg = pa_source_output_process_msg;
+
+ o->core = core;
+ o->state = PA_SOURCE_OUTPUT_INIT;
+ o->flags = flags;
+ o->proplist = pa_proplist_copy(data->proplist);
o->driver = pa_xstrdup(data->driver);
o->module = data->module;
o->source = data->source;
o->client = data->client;
-
+
+ o->resample_method = data->resample_method;
o->sample_spec = data->sample_spec;
o->channel_map = data->channel_map;
- o->push = NULL;
- o->kill = NULL;
- o->get_latency = NULL;
+ o->direct_on_input = data->direct_on_input;
+
+ reset_callbacks(o);
o->userdata = NULL;
-
- o->resampler = resampler;
- o->resample_method = data->resample_method;
-
- r = pa_idxset_put(core->source_outputs, o, &o->index);
- assert(r == 0);
- r = pa_idxset_put(o->source->outputs, o, NULL);
- assert(r == 0);
- pa_log_info("created %u \"%s\" on %s with sample spec %s",
+ o->thread_info.state = o->state;
+ o->thread_info.attached = FALSE;
+ o->thread_info.sample_spec = o->sample_spec;
+ o->thread_info.resampler = resampler;
+ o->thread_info.requested_source_latency = (pa_usec_t) -1;
+ o->thread_info.direct_on_input = o->direct_on_input;
+
+ o->thread_info.delay_memblockq = pa_memblockq_new(
+ 0,
+ MEMBLOCKQ_MAXLENGTH,
+ 0,
+ pa_frame_size(&o->source->sample_spec),
+ 0,
+ 1,
+ 0,
+ &o->source->silence);
+
+ pa_assert_se(pa_idxset_put(core->source_outputs, o, &o->index) == 0);
+ pa_assert_se(pa_idxset_put(o->source->outputs, pa_source_output_ref(o), NULL) == 0);
+
+ if (o->direct_on_input)
+ pa_assert_se(pa_idxset_put(o->direct_on_input->direct_outputs, o, NULL) == 0);
+
+ pa_log_info("Created output %u \"%s\" on %s with sample spec %s and channel map %s",
o->index,
- o->name,
+ pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_MEDIA_NAME)),
o->source->name,
- pa_sample_spec_snprint(st, sizeof(st), &o->sample_spec));
-
- pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW, o->index);
-
- /* We do not call pa_source_notify() here, because the virtual
- * functions have not yet been initialized */
-
- return o;
+ pa_sample_spec_snprint(st, sizeof(st), &o->sample_spec),
+ pa_channel_map_snprint(cm, sizeof(cm), &o->channel_map));
+
+ /* Don't forget to call pa_source_output_put! */
+
+ return o;
}
-void pa_source_output_disconnect(pa_source_output*o) {
- assert(o);
- assert(o->state != PA_SOURCE_OUTPUT_DISCONNECTED);
- assert(o->source);
- assert(o->source->core);
-
+/* Called from main context */
+static void update_n_corked(pa_source_output *o, pa_source_output_state_t state) {
+ pa_assert(o);
+
+ if (o->state == PA_SOURCE_OUTPUT_CORKED && state != PA_SOURCE_OUTPUT_CORKED)
+ pa_assert_se(o->source->n_corked -- >= 1);
+ else if (o->state != PA_SOURCE_OUTPUT_CORKED && state == PA_SOURCE_OUTPUT_CORKED)
+ o->source->n_corked++;
+
+ pa_source_update_status(o->source);
+}
+
+/* Called from main context */
+static int source_output_set_state(pa_source_output *o, pa_source_output_state_t state) {
+ pa_assert(o);
+
+ if (o->state == state)
+ return 0;
+
+ pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) == 0);
+
+ update_n_corked(o, state);
+ o->state = state;
+
+ if (state != PA_SOURCE_OUTPUT_UNLINKED)
+ pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED], o);
+
+ return 0;
+}
+
+/* Called from main context */
+void pa_source_output_unlink(pa_source_output*o) {
+ pa_bool_t linked;
+ pa_assert(o);
+
+ /* See pa_sink_unlink() for a couple of comments how this function
+ * works */
+
+ pa_source_output_ref(o);
+
+ linked = PA_SOURCE_OUTPUT_IS_LINKED(o->state);
+
+ if (linked)
+ pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK], o);
+
+ if (o->direct_on_input)
+ pa_idxset_remove_by_data(o->direct_on_input->direct_outputs, o, NULL);
pa_idxset_remove_by_data(o->source->core->source_outputs, o, NULL);
- pa_idxset_remove_by_data(o->source->outputs, o, NULL);
+ if (pa_idxset_remove_by_data(o->source->outputs, o, NULL))
+ pa_source_output_unref(o);
- pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_REMOVE, o->index);
- o->source = NULL;
+ update_n_corked(o, PA_SOURCE_OUTPUT_UNLINKED);
+ o->state = PA_SOURCE_OUTPUT_UNLINKED;
- o->push = NULL;
- o->kill = NULL;
- o->get_latency = NULL;
-
- o->state = PA_SOURCE_OUTPUT_DISCONNECTED;
+ if (linked)
+ if (o->source->asyncmsgq)
+ pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL) == 0);
+
+ reset_callbacks(o);
+
+ if (linked) {
+ pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_REMOVE, o->index);
+ pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST], o);
+ }
+
+ o->source = NULL;
+ pa_source_output_unref(o);
}
-static void source_output_free(pa_source_output* o) {
- assert(o);
+/* Called from main context */
+static void source_output_free(pa_object* mo) {
+ pa_source_output *o = PA_SOURCE_OUTPUT(mo);
+
+ pa_assert(o);
+ pa_assert(pa_source_output_refcnt(o) == 0);
+
+ if (PA_SOURCE_OUTPUT_IS_LINKED(o->state))
+ pa_source_output_unlink(o);
+
+ pa_log_info("Freeing output %u \"%s\"", o->index, pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_MEDIA_NAME)));
+
+ pa_assert(!o->thread_info.attached);
+
+ if (o->thread_info.delay_memblockq)
+ pa_memblockq_free(o->thread_info.delay_memblockq);
- if (o->state != PA_SOURCE_OUTPUT_DISCONNECTED)
- pa_source_output_disconnect(o);
+ if (o->thread_info.resampler)
+ pa_resampler_free(o->thread_info.resampler);
- pa_log_info("freed %u \"%s\"", o->index, o->name);
-
- if (o->resampler)
- pa_resampler_free(o->resampler);
+ if (o->proplist)
+ pa_proplist_free(o->proplist);
- pa_xfree(o->name);
pa_xfree(o->driver);
pa_xfree(o);
}
-void pa_source_output_unref(pa_source_output* o) {
- assert(o);
- assert(o->ref >= 1);
+/* Called from main context */
+void pa_source_output_put(pa_source_output *o) {
+ pa_source_output_state_t state;
+ pa_source_output_assert_ref(o);
- if (!(--o->ref))
- source_output_free(o);
-}
+ pa_assert(o->state == PA_SOURCE_OUTPUT_INIT);
-pa_source_output* pa_source_output_ref(pa_source_output *o) {
- assert(o);
- assert(o->ref >= 1);
-
- o->ref++;
- return o;
+ /* The following fields must be initialized properly */
+ pa_assert(o->push);
+ pa_assert(o->kill);
+
+ state = o->flags & PA_SOURCE_OUTPUT_START_CORKED ? PA_SOURCE_OUTPUT_CORKED : PA_SOURCE_OUTPUT_RUNNING;
+
+ update_n_corked(o, state);
+ o->state = state;
+
+ pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_ADD_OUTPUT, o, 0, NULL) == 0);
+
+ pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW, o->index);
+ pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PUT], o);
}
+/* Called from main context */
void pa_source_output_kill(pa_source_output*o) {
- assert(o);
- assert(o->ref >= 1);
+ pa_source_output_assert_ref(o);
+ pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
- if (o->kill)
- o->kill(o);
+ o->kill(o);
}
+/* Called from main context */
+pa_usec_t pa_source_output_get_latency(pa_source_output *o, pa_usec_t *source_latency) {
+ pa_usec_t r[2] = { 0, 0 };
+
+ pa_source_output_assert_ref(o);
+ pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
+
+ pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY, r, 0, NULL) == 0);
+
+ if (o->get_latency)
+ r[0] += o->get_latency(o);
+
+ if (source_latency)
+ *source_latency = r[1];
+
+ return r[0];
+}
+
+/* Called from thread context */
void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) {
- pa_memchunk rchunk;
-
- assert(o);
- assert(chunk);
- assert(chunk->length);
- assert(o->push);
-
- if (o->state == PA_SOURCE_OUTPUT_CORKED)
- return;
-
- if (!o->resampler) {
- o->push(o, chunk);
+ size_t length;
+ size_t limit, mbs = 0;
+
+ pa_source_output_assert_ref(o);
+ pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state));
+ pa_assert(chunk);
+ pa_assert(pa_frame_aligned(chunk->length, &o->source->sample_spec));
+
+ if (!o->push || o->thread_info.state == PA_SOURCE_OUTPUT_CORKED)
return;
+
+ pa_assert(o->thread_info.state == PA_SOURCE_OUTPUT_RUNNING);
+
+ if (pa_memblockq_push(o->thread_info.delay_memblockq, chunk) < 0) {
+ pa_log_debug("Delay queue overflow!");
+ pa_memblockq_seek(o->thread_info.delay_memblockq, chunk->length, PA_SEEK_RELATIVE);
}
- pa_resampler_run(o->resampler, chunk, &rchunk);
- if (!rchunk.length)
- return;
-
- assert(rchunk.memblock);
- o->push(o, &rchunk);
- pa_memblock_unref(rchunk.memblock);
+ limit = o->process_rewind ? 0 : o->source->thread_info.max_rewind;
+
+ /* Implement the delay queue */
+ while ((length = pa_memblockq_get_length(o->thread_info.delay_memblockq)) > limit) {
+ pa_memchunk qchunk;
+
+ length -= limit;
+
+ pa_assert_se(pa_memblockq_peek(o->thread_info.delay_memblockq, &qchunk) >= 0);
+
+ if (qchunk.length > length)
+ qchunk.length = length;
+
+ pa_assert(qchunk.length > 0);
+
+ if (!o->thread_info.resampler)
+ o->push(o, &qchunk);
+ else {
+ pa_memchunk rchunk;
+
+ if (mbs == 0)
+ mbs = pa_resampler_max_block_size(o->thread_info.resampler);
+
+ if (qchunk.length > mbs)
+ qchunk.length = mbs;
+
+ pa_resampler_run(o->thread_info.resampler, &qchunk, &rchunk);
+
+ if (rchunk.length > 0)
+ o->push(o, &rchunk);
+
+ if (rchunk.memblock)
+ pa_memblock_unref(rchunk.memblock);
+ }
+
+ pa_memblock_unref(qchunk.memblock);
+ pa_memblockq_drop(o->thread_info.delay_memblockq, qchunk.length);
+ }
}
-void pa_source_output_set_name(pa_source_output *o, const char *name) {
- assert(o);
- assert(o->ref >= 1);
+/* Called from thread context */
+void pa_source_output_process_rewind(pa_source_output *o, size_t nbytes /* in sink sample spec */) {
+ pa_source_output_assert_ref(o);
- if (!o->name && !name)
- return;
+ pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state));
+ pa_assert(pa_frame_aligned(nbytes, &o->source->sample_spec));
- if (o->name && name && !strcmp(o->name, name))
+ if (nbytes <= 0)
return;
-
- pa_xfree(o->name);
- o->name = pa_xstrdup(name);
- pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
+ if (o->process_rewind) {
+ pa_assert(pa_memblockq_get_length(o->thread_info.delay_memblockq) == 0);
+
+ if (o->thread_info.resampler)
+ nbytes = pa_resampler_result(o->thread_info.resampler, nbytes);
+
+ pa_log_debug("Have to rewind %lu bytes on implementor.", (unsigned long) nbytes);
+
+ if (nbytes > 0)
+ o->process_rewind(o, nbytes);
+
+ if (o->thread_info.resampler)
+ pa_resampler_reset(o->thread_info.resampler);
+
+ } else
+ pa_memblockq_rewind(o->thread_info.delay_memblockq, nbytes);
}
-pa_usec_t pa_source_output_get_latency(pa_source_output *o) {
- assert(o);
- assert(o->ref >= 1);
-
- if (o->get_latency)
- return o->get_latency(o);
+/* Called from thread context */
+void pa_source_output_update_max_rewind(pa_source_output *o, size_t nbytes /* in the source's sample spec */) {
+ pa_source_output_assert_ref(o);
+ pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state));
+ pa_assert(pa_frame_aligned(nbytes, &o->source->sample_spec));
+
+ if (o->update_max_rewind)
+ o->update_max_rewind(o, o->thread_info.resampler ? pa_resampler_result(o->thread_info.resampler, nbytes) : nbytes);
+}
+
+/* Called from thread context */
+static pa_usec_t fixup_latency(pa_source *s, pa_usec_t usec) {
+ pa_source_assert_ref(s);
+
+ if (usec == (pa_usec_t) -1)
+ return usec;
+
+ if (s->thread_info.max_latency > 0 && usec > s->thread_info.max_latency)
+ usec = s->thread_info.max_latency;
+
+ if (s->thread_info.min_latency > 0 && usec < s->thread_info.min_latency)
+ usec = s->thread_info.min_latency;
+
+ return usec;
+}
+
+/* Called from thread context */
+pa_usec_t pa_source_output_set_requested_latency_within_thread(pa_source_output *o, pa_usec_t usec) {
+ pa_source_output_assert_ref(o);
+
+ usec = fixup_latency(o->source, usec);
+ o->thread_info.requested_source_latency = usec;
+ pa_source_invalidate_requested_latency(o->source);
+
+ return usec;
+}
+
+/* Called from main context */
+pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t usec) {
+ pa_source_output_assert_ref(o);
+
+ if (PA_SOURCE_OUTPUT_IS_LINKED(o->state))
+ pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
+ else {
+ /* If this sink input is not realized yet, we have to touch
+ * the thread info data directly */
+
+ usec = fixup_latency(o->source, usec);
+ o->thread_info.requested_source_latency = usec;
+ o->source->thread_info.requested_latency_valid = FALSE;
+ }
+
+ return usec;
+}
+
+/* Called from main context */
+pa_usec_t pa_source_output_get_requested_latency(pa_source_output *o) {
+ pa_usec_t usec = 0;
+
+ pa_source_output_assert_ref(o);
+
+ if (PA_SOURCE_OUTPUT_IS_LINKED(o->state))
+ pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
+ else
+ /* If this sink input is not realized yet, we have to touch
+ * the thread info data directly */
+ usec = o->thread_info.requested_source_latency;
+
+ return usec;
+}
+
+/* Called from main context */
+void pa_source_output_cork(pa_source_output *o, pa_bool_t b) {
+ pa_source_output_assert_ref(o);
+ pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
+
+ source_output_set_state(o, b ? PA_SOURCE_OUTPUT_CORKED : PA_SOURCE_OUTPUT_RUNNING);
+}
+
+/* Called from main context */
+int pa_source_output_set_rate(pa_source_output *o, uint32_t rate) {
+ pa_source_output_assert_ref(o);
+ pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
+ pa_return_val_if_fail(o->thread_info.resampler, -1);
+
+ if (o->sample_spec.rate == rate)
+ return 0;
+ o->sample_spec.rate = rate;
+
+ pa_asyncmsgq_post(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_RATE, PA_UINT_TO_PTR(rate), 0, NULL, NULL);
+
+ pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
return 0;
}
-void pa_source_output_cork(pa_source_output *o, int b) {
- int n;
-
- assert(o);
- assert(o->ref >= 1);
+/* Called from main context */
+void pa_source_output_set_name(pa_source_output *o, const char *name) {
+ const char *old;
+ pa_source_output_assert_ref(o);
+
+ if (!name && !pa_proplist_contains(o->proplist, PA_PROP_MEDIA_NAME))
+ return;
+
+ old = pa_proplist_gets(o->proplist, PA_PROP_MEDIA_NAME);
- if (o->state == PA_SOURCE_OUTPUT_DISCONNECTED)
+ if (old && name && !strcmp(old, name))
return;
- n = o->state == PA_SOURCE_OUTPUT_CORKED && !b;
-
- o->state = b ? PA_SOURCE_OUTPUT_CORKED : PA_SOURCE_OUTPUT_RUNNING;
-
- if (n)
- pa_source_notify(o->source);
+ if (name)
+ pa_proplist_sets(o->proplist, PA_PROP_MEDIA_NAME, name);
+ else
+ pa_proplist_unset(o->proplist, PA_PROP_MEDIA_NAME);
+
+ if (PA_SOURCE_OUTPUT_IS_LINKED(o->state)) {
+ pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED], o);
+ pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
+ }
}
+/* Called from main context */
pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o) {
- assert(o);
- assert(o->ref >= 1);
-
- if (!o->resampler)
- return o->resample_method;
+ pa_source_output_assert_ref(o);
- return pa_resampler_get_method(o->resampler);
+ return o->resample_method;
}
+/* Called from main context */
int pa_source_output_move_to(pa_source_output *o, pa_source *dest) {
pa_source *origin;
- pa_resampler *new_resampler = NULL;
+ pa_resampler *new_resampler;
+ pa_source_output_move_hook_data hook_data;
- assert(o);
- assert(o->ref >= 1);
- assert(dest);
+ pa_source_output_assert_ref(o);
+ pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
+ pa_source_assert_ref(dest);
origin = o->source;
if (dest == origin)
return 0;
+ if (o->flags & PA_SOURCE_OUTPUT_DONT_MOVE)
+ return -1;
+
+ if (o->direct_on_input)
+ return -1;
+
if (pa_idxset_size(dest->outputs) >= PA_MAX_OUTPUTS_PER_SOURCE) {
pa_log_warn("Failed to move source output: too many outputs per source.");
return -1;
}
- if (o->resampler &&
+ if (o->thread_info.resampler &&
pa_sample_spec_equal(&origin->sample_spec, &dest->sample_spec) &&
pa_channel_map_equal(&origin->channel_map, &dest->channel_map))
/* Try to reuse the old resampler if possible */
- new_resampler = o->resampler;
-
- else if (!pa_sample_spec_equal(&o->sample_spec, &dest->sample_spec) ||
- !pa_channel_map_equal(&o->channel_map, &dest->channel_map)) {
+ new_resampler = o->thread_info.resampler;
+
+ else if ((o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ||
+ !pa_sample_spec_equal(&o->sample_spec, &dest->sample_spec) ||
+ !pa_channel_map_equal(&o->channel_map, &dest->channel_map)) {
+
+ /* Okey, we need a new resampler for the new source */
- /* Okey, we need a new resampler for the new sink */
-
if (!(new_resampler = pa_resampler_new(
dest->core->mempool,
&dest->sample_spec, &dest->channel_map,
&o->sample_spec, &o->channel_map,
- o->resample_method))) {
+ o->resample_method,
+ ((o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
+ ((o->flags & PA_SOURCE_OUTPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
+ (o->core->disable_remixing || (o->flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0)))) {
pa_log_warn("Unsupported resampling operation.");
return -1;
}
- }
+ } else
+ new_resampler = NULL;
+
+ hook_data.source_output = o;
+ hook_data.destination = dest;
+ pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE], &hook_data);
/* Okey, let's move it */
+ pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL) == 0);
+
pa_idxset_remove_by_data(origin->outputs, o, NULL);
pa_idxset_put(dest->outputs, o, NULL);
o->source = dest;
+ if (pa_source_output_get_state(o) == PA_SOURCE_OUTPUT_CORKED) {
+ pa_assert_se(origin->n_corked-- >= 1);
+ dest->n_corked++;
+ }
+
/* Replace resampler */
- if (new_resampler != o->resampler) {
- if (o->resampler)
- pa_resampler_free(o->resampler);
- o->resampler = new_resampler;
+ if (new_resampler != o->thread_info.resampler) {
+ if (o->thread_info.resampler)
+ pa_resampler_free(o->thread_info.resampler);
+ o->thread_info.resampler = new_resampler;
+
+ pa_memblockq_free(o->thread_info.delay_memblockq);
+
+ o->thread_info.delay_memblockq = pa_memblockq_new(
+ 0,
+ MEMBLOCKQ_MAXLENGTH,
+ 0,
+ pa_frame_size(&o->source->sample_spec),
+ 0,
+ 1,
+ 0,
+ &o->source->silence);
}
+ pa_source_update_status(origin);
+ pa_source_update_status(dest);
+
+ pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_ADD_OUTPUT, o, 0, NULL) == 0);
+
+ if (o->moved)
+ o->moved(o);
+
+ pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_POST], o);
+
+ pa_log_debug("Successfully moved source output %i from %s to %s.", o->index, origin->name, dest->name);
+
/* Notify everyone */
pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
- pa_source_notify(o->source);
return 0;
}
+
+/* Called from IO thread context */
+void pa_source_output_set_state_within_thread(pa_source_output *o, pa_source_output_state_t state) {
+ pa_source_output_assert_ref(o);
+
+ if (state == o->thread_info.state)
+ return;
+
+ if (o->state_change)
+ o->state_change(o, state);
+
+ o->thread_info.state = state;
+}
+
+/* Called from IO thread context, except when it is not */
+int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int64_t offset, pa_memchunk* chunk) {
+ pa_source_output *o = PA_SOURCE_OUTPUT(mo);
+ pa_source_output_assert_ref(o);
+
+ switch (code) {
+
+ case PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY: {
+ pa_usec_t *r = userdata;
+ pa_usec_t source_usec = 0;
+
+ r[0] += pa_bytes_to_usec(pa_memblockq_get_length(o->thread_info.delay_memblockq), &o->source->sample_spec);
+
+ if (o->source->parent.process_msg(PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_GET_LATENCY, &source_usec, 0, NULL) >= 0)
+ r[1] += source_usec;
+
+ return 0;
+ }
+
+ case PA_SOURCE_OUTPUT_MESSAGE_SET_RATE:
+
+ o->thread_info.sample_spec.rate = PA_PTR_TO_UINT(userdata);
+ pa_resampler_set_output_rate(o->thread_info.resampler, PA_PTR_TO_UINT(userdata));
+ return 0;
+
+ case PA_SOURCE_OUTPUT_MESSAGE_SET_STATE:
+
+ pa_source_output_set_state_within_thread(o, PA_PTR_TO_UINT(userdata));
+ return 0;
+
+ case PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY: {
+ pa_usec_t *usec = userdata;
+
+ *usec = pa_source_output_set_requested_latency_within_thread(o, *usec);
+
+ return 0;
+ }
+
+ case PA_SINK_INPUT_MESSAGE_GET_REQUESTED_LATENCY: {
+ pa_usec_t *r = userdata;
+
+ *r = o->thread_info.requested_source_latency;
+ return 0;
+ }
+ }
+
+ return -1;
+}
diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h
index f7396a19..61825b22 100644
--- a/src/pulsecore/source-output.h
+++ b/src/pulsecore/source-output.h
@@ -1,21 +1,21 @@
-#ifndef foosourceoutputhfoo
-#define foosourceoutputhfoo
-
-/* $Id$ */
+#ifndef foopulsesourceoutputhfoo
+#define foopulsesourceoutputhfoo
/***
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
@@ -33,51 +33,151 @@ typedef struct pa_source_output pa_source_output;
#include <pulsecore/module.h>
#include <pulsecore/client.h>
-typedef enum {
+typedef enum pa_source_output_state {
+ PA_SOURCE_OUTPUT_INIT,
PA_SOURCE_OUTPUT_RUNNING,
PA_SOURCE_OUTPUT_CORKED,
- PA_SOURCE_OUTPUT_DISCONNECTED
+ PA_SOURCE_OUTPUT_UNLINKED
} pa_source_output_state_t;
+static inline pa_bool_t PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_state_t x) {
+ return x == PA_SOURCE_OUTPUT_RUNNING || x == PA_SOURCE_OUTPUT_CORKED;
+}
+
typedef enum pa_source_output_flags {
- PA_SOURCE_OUTPUT_NO_HOOKS = 1
+ PA_SOURCE_OUTPUT_VARIABLE_RATE = 1,
+ PA_SOURCE_OUTPUT_DONT_MOVE = 2,
+ PA_SOURCE_OUTPUT_START_CORKED = 4,
+ PA_SOURCE_OUTPUT_NO_REMAP = 8,
+ PA_SOURCE_OUTPUT_NO_REMIX = 16,
+ PA_SOURCE_OUTPUT_FIX_FORMAT = 32,
+ PA_SOURCE_OUTPUT_FIX_RATE = 64,
+ PA_SOURCE_OUTPUT_FIX_CHANNELS = 128
} pa_source_output_flags_t;
struct pa_source_output {
- int ref;
+ pa_msgobject parent;
+
uint32_t index;
+ pa_core *core;
+
pa_source_output_state_t state;
-
- char *name, *driver; /* may be NULL */
+ pa_source_output_flags_t flags;
+
+ char *driver; /* may be NULL */
+ pa_proplist *proplist;
+
pa_module *module; /* may be NULL */
+ pa_client *client; /* may be NULL */
pa_source *source;
- pa_client *client; /* may be NULL */
+
+ /* A source output can monitor just a single input of a sink, in which case we find it here */
+ pa_sink_input *direct_on_input; /* may be NULL */
pa_sample_spec sample_spec;
pa_channel_map channel_map;
-
- void (*push)(pa_source_output *o, const pa_memchunk *chunk);
- void (*kill)(pa_source_output* o); /* may be NULL */
- pa_usec_t (*get_latency) (pa_source_output *o); /* may be NULL */
- pa_resampler* resampler; /* may be NULL */
pa_resample_method_t resample_method;
-
+
+ /* Pushes a new memchunk into the output. Called from IO thread
+ * context. */
+ void (*push)(pa_source_output *o, const pa_memchunk *chunk); /* may NOT be NULL */
+
+ /* Only relevant for monitor sources right now: called when the
+ * recorded stream is rewound. Called from IO context */
+ void (*process_rewind)(pa_source_output *o, size_t nbytes); /* may be NULL */
+
+ /* Called whenever the maximum rewindable size of the source
+ * changes. Called from IO thread context. */
+ void (*update_max_rewind) (pa_source_output *o, size_t nbytes); /* may be NULL */
+
+ /* Called whenever the configured latency of the source
+ * changes. Called from IO context. */
+ void (*update_source_requested_latency) (pa_source_output *o); /* may be NULL */
+
+ /* Called whenver the latency range of the source changes. Called
+ * from IO context. */
+ void (*update_source_latency_range) (pa_source_output *o); /* may be NULL */
+
+ /* If non-NULL this function is called when the output is first
+ * connected to a source. Called from IO thread context */
+ void (*attach) (pa_source_output *o); /* may be NULL */
+
+ /* If non-NULL this function is called when the output is
+ * disconnected from its source. Called from IO thread context */
+ void (*detach) (pa_source_output *o); /* may be NULL */
+
+ /* If non-NULL called whenever the the source this output is attached
+ * to suspends or resumes. Called from main context */
+ void (*suspend) (pa_source_output *o, pa_bool_t b); /* may be NULL */
+
+ /* If non-NULL called whenever the the source this output is attached
+ * to changes. Called from main context */
+ void (*moved) (pa_source_output *o); /* may be NULL */
+
+ /* Supposed to unlink and destroy this stream. Called from main
+ * context. */
+ void (*kill)(pa_source_output* o); /* may NOT be NULL */
+
+ /* Return the current latency (i.e. length of bufferd audio) of
+ this stream. Called from main context. This is added to what the
+ PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY message sent to the IO thread
+ returns */
+ pa_usec_t (*get_latency) (pa_source_output *o); /* may be NULL */
+
+ /* If non_NULL this function is called from thread context if the
+ * state changes. The old state is found in thread_info.state. */
+ void (*state_change) (pa_source_output *o, pa_source_output_state_t state); /* may be NULL */
+
+ struct {
+ pa_source_output_state_t state;
+
+ pa_bool_t attached; /* True only between ->attach() and ->detach() calls */
+
+ pa_sample_spec sample_spec;
+
+ pa_resampler* resampler; /* may be NULL */
+
+ /* We maintain a delay memblockq here for source outputs that
+ * don't implement rewind() */
+ pa_memblockq *delay_memblockq;
+
+ /* The requested latency for the source */
+ pa_usec_t requested_source_latency;
+
+ pa_sink_input *direct_on_input; /* may be NULL */
+ } thread_info;
+
void *userdata;
};
+PA_DECLARE_CLASS(pa_source_output);
+#define PA_SOURCE_OUTPUT(o) pa_source_output_cast(o)
+
+enum {
+ PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY,
+ PA_SOURCE_OUTPUT_MESSAGE_SET_RATE,
+ PA_SOURCE_OUTPUT_MESSAGE_SET_STATE,
+ PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY,
+ PA_SOURCE_OUTPUT_MESSAGE_GET_REQUESTED_LATENCY,
+ PA_SOURCE_OUTPUT_MESSAGE_MAX
+};
+
typedef struct pa_source_output_new_data {
- const char *name, *driver;
+ pa_proplist *proplist;
+ pa_sink_input *direct_on_input;
+
+ const char *driver;
pa_module *module;
pa_client *client;
pa_source *source;
pa_sample_spec sample_spec;
- int sample_spec_is_set;
+ pa_bool_t sample_spec_is_set;
pa_channel_map channel_map;
- int channel_map_is_set;
+ pa_bool_t channel_map_is_set;
pa_resample_method_t resample_method;
} pa_source_output_new_data;
@@ -85,32 +185,56 @@ typedef struct pa_source_output_new_data {
pa_source_output_new_data* pa_source_output_new_data_init(pa_source_output_new_data *data);
void pa_source_output_new_data_set_sample_spec(pa_source_output_new_data *data, const pa_sample_spec *spec);
void pa_source_output_new_data_set_channel_map(pa_source_output_new_data *data, const pa_channel_map *map);
-void pa_source_output_new_data_set_volume(pa_source_output_new_data *data, const pa_cvolume *volume);
+void pa_source_output_new_data_done(pa_source_output_new_data *data);
+
+typedef struct pa_source_output_move_hook_data {
+ pa_source_output *source_output;
+ pa_source *destination;
+} pa_source_output_move_hook_data;
+
+/* To be called by the implementing module only */
pa_source_output* pa_source_output_new(
pa_core *core,
pa_source_output_new_data *data,
pa_source_output_flags_t flags);
-void pa_source_output_unref(pa_source_output* o);
-pa_source_output* pa_source_output_ref(pa_source_output *o);
+void pa_source_output_put(pa_source_output *o);
+void pa_source_output_unlink(pa_source_output*o);
-/* To be called by the implementing module only */
-void pa_source_output_disconnect(pa_source_output*o);
+void pa_source_output_set_name(pa_source_output *i, const char *name);
-/* External code may request disconnection with this funcion */
-void pa_source_output_kill(pa_source_output*o);
+pa_usec_t pa_source_output_set_requested_latency(pa_source_output *i, pa_usec_t usec);
-void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk);
+void pa_source_output_cork(pa_source_output *i, pa_bool_t b);
-void pa_source_output_set_name(pa_source_output *i, const char *name);
+int pa_source_output_set_rate(pa_source_output *o, uint32_t rate);
-pa_usec_t pa_source_output_get_latency(pa_source_output *i);
+/* Callable by everyone */
-void pa_source_output_cork(pa_source_output *i, int b);
+/* External code may request disconnection with this funcion */
+void pa_source_output_kill(pa_source_output*o);
+
+pa_usec_t pa_source_output_get_latency(pa_source_output *i, pa_usec_t *source_latency);
pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o);
int pa_source_output_move_to(pa_source_output *o, pa_source *dest);
+#define pa_source_output_get_state(o) ((o)->state)
+
+pa_usec_t pa_source_output_get_requested_latency(pa_source_output *o);
+
+/* To be used exclusively by the source driver thread */
+
+void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk);
+void pa_source_output_process_rewind(pa_source_output *o, size_t nbytes);
+void pa_source_output_update_max_rewind(pa_source_output *o, size_t nbytes);
+
+void pa_source_output_set_state_within_thread(pa_source_output *o, pa_source_output_state_t state);
+
+int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
+
+pa_usec_t pa_source_output_set_requested_latency_within_thread(pa_source_output *o, pa_usec_t usec);
+
#endif
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index c48d6aaa..8256a988 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -1,18 +1,19 @@
-/* $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
@@ -24,12 +25,12 @@
#endif
#include <stdio.h>
-#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <pulse/utf8.h>
#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
#include <pulsecore/source-output.h>
#include <pulsecore/namereg.h>
@@ -39,306 +40,946 @@
#include "source.h"
-#define CHECK_VALIDITY_RETURN_NULL(condition) \
-do {\
-if (!(condition)) \
- return NULL; \
-} while (0)
+#define DEFAULT_MIN_LATENCY (4*PA_USEC_PER_MSEC)
+
+static PA_DEFINE_CHECK_TYPE(pa_source, pa_msgobject);
+
+static void source_free(pa_object *o);
+
+pa_source_new_data* pa_source_new_data_init(pa_source_new_data *data) {
+ pa_assert(data);
+
+ memset(data, 0, sizeof(*data));
+ data->proplist = pa_proplist_new();
+
+ return data;
+}
+
+void pa_source_new_data_set_name(pa_source_new_data *data, const char *name) {
+ pa_assert(data);
+
+ pa_xfree(data->name);
+ data->name = pa_xstrdup(name);
+}
+
+void pa_source_new_data_set_sample_spec(pa_source_new_data *data, const pa_sample_spec *spec) {
+ pa_assert(data);
+
+ if ((data->sample_spec_is_set = !!spec))
+ data->sample_spec = *spec;
+}
+
+void pa_source_new_data_set_channel_map(pa_source_new_data *data, const pa_channel_map *map) {
+ pa_assert(data);
+ if ((data->channel_map_is_set = !!map))
+ data->channel_map = *map;
+}
+
+void pa_source_new_data_set_volume(pa_source_new_data *data, const pa_cvolume *volume) {
+ pa_assert(data);
+
+ if ((data->volume_is_set = !!volume))
+ data->volume = *volume;
+}
+
+void pa_source_new_data_set_muted(pa_source_new_data *data, pa_bool_t mute) {
+ pa_assert(data);
+
+ data->muted_is_set = TRUE;
+ data->muted = !!mute;
+}
+
+void pa_source_new_data_done(pa_source_new_data *data) {
+ pa_assert(data);
+
+ pa_xfree(data->name);
+ pa_proplist_free(data->proplist);
+}
+
+/* Called from main context */
+static void reset_callbacks(pa_source *s) {
+ pa_assert(s);
+
+ s->set_state = NULL;
+ s->get_volume = NULL;
+ s->set_volume = NULL;
+ s->get_mute = NULL;
+ s->set_mute = NULL;
+ s->update_requested_latency = NULL;
+}
+
+/* Called from main context */
pa_source* pa_source_new(
pa_core *core,
- const char *driver,
- const char *name,
- int fail,
- const pa_sample_spec *spec,
- const pa_channel_map *map) {
-
+ pa_source_new_data *data,
+ pa_source_flags_t flags) {
+
pa_source *s;
- char st[256];
- int r;
- pa_channel_map tmap;
-
- assert(core);
- assert(name);
- assert(spec);
+ const char *name;
+ char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+
+ pa_assert(core);
+ pa_assert(data);
+ pa_assert(data->name);
+
+ s = pa_msgobject_new(pa_source);
+
+ if (!(name = pa_namereg_register(core, data->name, PA_NAMEREG_SOURCE, s, data->namereg_fail))) {
+ pa_xfree(s);
+ return NULL;
+ }
+
+ pa_source_new_data_set_name(data, name);
+
+ if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_NEW], data) < 0) {
+ pa_xfree(s);
+ pa_namereg_unregister(core, name);
+ return NULL;
+ }
+
+ pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver));
+ pa_return_null_if_fail(data->name && pa_utf8_valid(data->name) && data->name[0]);
- CHECK_VALIDITY_RETURN_NULL(pa_sample_spec_valid(spec));
+ pa_return_null_if_fail(data->sample_spec_is_set && pa_sample_spec_valid(&data->sample_spec));
- if (!map)
- map = pa_channel_map_init_auto(&tmap, spec->channels, PA_CHANNEL_MAP_DEFAULT);
+ if (!data->channel_map_is_set)
+ pa_return_null_if_fail(pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT));
- CHECK_VALIDITY_RETURN_NULL(map && pa_channel_map_valid(map));
- CHECK_VALIDITY_RETURN_NULL(map->channels == spec->channels);
- CHECK_VALIDITY_RETURN_NULL(!driver || pa_utf8_valid(driver));
- CHECK_VALIDITY_RETURN_NULL(pa_utf8_valid(name) && *name);
+ pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map));
+ pa_return_null_if_fail(data->channel_map.channels == data->sample_spec.channels);
- s = pa_xnew(pa_source, 1);
+ if (!data->volume_is_set)
+ pa_cvolume_reset(&data->volume, data->sample_spec.channels);
- if (!(name = pa_namereg_register(core, name, PA_NAMEREG_SOURCE, s, fail))) {
+ pa_return_null_if_fail(pa_cvolume_valid(&data->volume));
+ pa_return_null_if_fail(data->volume.channels == data->sample_spec.channels);
+
+ if (!data->muted_is_set)
+ data->muted = FALSE;
+
+ if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_FIXATE], data) < 0) {
pa_xfree(s);
+ pa_namereg_unregister(core, name);
return NULL;
}
- s->ref = 1;
+ s->parent.parent.free = source_free;
+ s->parent.process_msg = pa_source_process_msg;
+
s->core = core;
- s->state = PA_SOURCE_RUNNING;
+ s->state = PA_SOURCE_INIT;
+ s->flags = flags;
s->name = pa_xstrdup(name);
- s->description = NULL;
- s->driver = pa_xstrdup(driver);
- s->owner = NULL;
-
- s->sample_spec = *spec;
- s->channel_map = *map;
+ s->proplist = pa_proplist_copy(data->proplist);
+ s->driver = pa_xstrdup(data->driver);
+ s->module = data->module;
+
+ s->sample_spec = data->sample_spec;
+ s->channel_map = data->channel_map;
s->outputs = pa_idxset_new(NULL, NULL);
+ s->n_corked = 0;
s->monitor_of = NULL;
- pa_cvolume_reset(&s->sw_volume, spec->channels);
- pa_cvolume_reset(&s->hw_volume, spec->channels);
- s->sw_muted = 0;
- s->hw_muted = 0;
-
- s->is_hardware = 0;
+ s->volume = data->volume;
+ s->muted = data->muted;
+ s->refresh_volume = s->refresh_muted = FALSE;
- s->get_latency = NULL;
- s->notify = NULL;
- s->set_hw_volume = NULL;
- s->get_hw_volume = NULL;
- s->set_hw_mute = NULL;
- s->get_hw_mute = NULL;
+ reset_callbacks(s);
s->userdata = NULL;
- r = pa_idxset_put(core->sources, s, &s->index);
- assert(s->index != PA_IDXSET_INVALID && r >= 0);
+ s->asyncmsgq = NULL;
+ s->rtpoll = NULL;
+
+ pa_silence_memchunk_get(
+ &core->silence_cache,
+ core->mempool,
+ &s->silence,
+ &s->sample_spec,
+ 0);
+
+ s->thread_info.outputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+ pa_cvolume_reset(&s->thread_info.soft_volume, s->sample_spec.channels);
+ s->thread_info.soft_muted = FALSE;
+ s->thread_info.state = s->state;
+ s->thread_info.max_rewind = 0;
+ s->thread_info.requested_latency_valid = FALSE;
+ s->thread_info.requested_latency = 0;
+ s->thread_info.min_latency = DEFAULT_MIN_LATENCY;
+ s->thread_info.max_latency = 0;
+
+ pa_assert_se(pa_idxset_put(core->sources, s, &s->index) >= 0);
+
+ pa_log_info("Created source %u \"%s\" with sample spec %s and channel map %s",
+ s->index,
+ s->name,
+ pa_sample_spec_snprint(st, sizeof(st), &s->sample_spec),
+ pa_channel_map_snprint(cm, sizeof(cm), &s->channel_map));
- pa_sample_spec_snprint(st, sizeof(st), spec);
- pa_log_info("created %u \"%s\" with sample spec \"%s\"", s->index, s->name, st);
-
- pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_NEW, s->index);
-
return s;
}
-void pa_source_disconnect(pa_source *s) {
+/* Called from main context */
+static int source_set_state(pa_source *s, pa_source_state_t state) {
+ int ret;
+ pa_bool_t suspend_change;
+
+ pa_assert(s);
+
+ if (s->state == state)
+ return 0;
+
+ suspend_change =
+ (s->state == PA_SOURCE_SUSPENDED && PA_SOURCE_IS_OPENED(state)) ||
+ (PA_SOURCE_IS_OPENED(s->state) && state == PA_SOURCE_SUSPENDED);
+
+ if (s->set_state)
+ if ((ret = s->set_state(s, state)) < 0)
+ return -1;
+
+ if (s->asyncmsgq)
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) == 0);
+
+ s->state = state;
+
+ if (suspend_change) {
+ pa_source_output *o;
+ uint32_t idx;
+
+ /* We're suspending or resuming, tell everyone about it */
+
+ for (o = PA_SOURCE_OUTPUT(pa_idxset_first(s->outputs, &idx)); o; o = PA_SOURCE_OUTPUT(pa_idxset_next(s->outputs, &idx)))
+ if (o->suspend)
+ o->suspend(o, state == PA_SINK_SUSPENDED);
+ }
+
+ if (state != PA_SOURCE_UNLINKED) /* if we enter UNLINKED state pa_source_unlink() will fire the apropriate events */
+ pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_STATE_CHANGED], s);
+
+ return 0;
+}
+
+/* Called from main context */
+void pa_source_put(pa_source *s) {
+ pa_source_assert_ref(s);
+
+ pa_assert(s->state == PA_SINK_INIT);
+
+ /* The following fields must be initialized properly when calling _put() */
+ pa_assert(s->asyncmsgq);
+ pa_assert(s->rtpoll);
+ pa_assert(!s->thread_info.min_latency || !s->thread_info.max_latency ||
+ s->thread_info.min_latency <= s->thread_info.max_latency);
+
+ if (!(s->flags & PA_SOURCE_HW_VOLUME_CTRL)) {
+ s->flags |= PA_SOURCE_DECIBEL_VOLUME;
+
+ s->thread_info.soft_volume = s->volume;
+ s->thread_info.soft_muted = s->muted;
+ }
+
+ pa_assert_se(source_set_state(s, PA_SOURCE_IDLE) == 0);
+
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_NEW, s->index);
+ pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_PUT], s);
+}
+
+/* Called from main context */
+void pa_source_unlink(pa_source *s) {
+ pa_bool_t linked;
pa_source_output *o, *j = NULL;
-
- assert(s);
- assert(s->state == PA_SOURCE_RUNNING);
- s->state = PA_SOURCE_DISCONNECTED;
- pa_namereg_unregister(s->core, s->name);
+ pa_assert(s);
+
+ /* See pa_sink_unlink() for a couple of comments how this function
+ * works. */
+
+ linked = PA_SOURCE_IS_LINKED(s->state);
+
+ if (linked)
+ pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], s);
+
+ if (s->state != PA_SOURCE_UNLINKED)
+ pa_namereg_unregister(s->core, s->name);
+ pa_idxset_remove_by_data(s->core->sources, s, NULL);
- pa_hook_fire(&s->core->hook_source_disconnect, s);
-
while ((o = pa_idxset_first(s->outputs, NULL))) {
- assert(o != j);
+ pa_assert(o != j);
pa_source_output_kill(o);
j = o;
}
- pa_idxset_remove_by_data(s->core->sources, s, NULL);
+ if (linked)
+ source_set_state(s, PA_SOURCE_UNLINKED);
+ else
+ s->state = PA_SOURCE_UNLINKED;
+
+ reset_callbacks(s);
+
+ if (linked) {
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_REMOVE, s->index);
+ pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK_POST], s);
+ }
+}
+
+/* Called from main context */
+static void source_free(pa_object *o) {
+ pa_source_output *so;
+ pa_source *s = PA_SOURCE(o);
+
+ pa_assert(s);
+ pa_assert(pa_source_refcnt(s) == 0);
+
+ if (PA_SOURCE_IS_LINKED(s->state))
+ pa_source_unlink(s);
- s->get_latency = NULL;
- s->notify = NULL;
- s->get_hw_volume = NULL;
- s->set_hw_volume = NULL;
- s->set_hw_mute = NULL;
- s->get_hw_mute = NULL;
-
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_REMOVE, s->index);
-}
-
-static void source_free(pa_source *s) {
- assert(s);
- assert(!s->ref);
-
- if (s->state != PA_SOURCE_DISCONNECTED)
- pa_source_disconnect(s);
-
- pa_log_info("freed %u \"%s\"", s->index, s->name);
+ pa_log_info("Freeing source %u \"%s\"", s->index, s->name);
pa_idxset_free(s->outputs, NULL, NULL);
+ while ((so = pa_hashmap_steal_first(s->thread_info.outputs)))
+ pa_source_output_unref(so);
+
+ pa_hashmap_free(s->thread_info.outputs, NULL, NULL);
+
+ if (s->silence.memblock)
+ pa_memblock_unref(s->silence.memblock);
+
pa_xfree(s->name);
- pa_xfree(s->description);
pa_xfree(s->driver);
+
+ if (s->proplist)
+ pa_proplist_free(s->proplist);
+
pa_xfree(s);
}
-void pa_source_unref(pa_source *s) {
- assert(s);
- assert(s->ref >= 1);
+/* Called from main context */
+void pa_source_set_asyncmsgq(pa_source *s, pa_asyncmsgq *q) {
+ pa_source_assert_ref(s);
- if (!(--s->ref))
- source_free(s);
+ s->asyncmsgq = q;
}
-pa_source* pa_source_ref(pa_source *s) {
- assert(s);
- assert(s->ref >= 1);
-
- s->ref++;
- return s;
+/* Called from main context */
+void pa_source_set_rtpoll(pa_source *s, pa_rtpoll *p) {
+ pa_source_assert_ref(s);
+
+ s->rtpoll = p;
+}
+
+/* Called from main context */
+int pa_source_update_status(pa_source*s) {
+ pa_source_assert_ref(s);
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
+
+ if (s->state == PA_SOURCE_SUSPENDED)
+ return 0;
+
+ return source_set_state(s, pa_source_used_by(s) ? PA_SOURCE_RUNNING : PA_SOURCE_IDLE);
}
-void pa_source_notify(pa_source*s) {
- assert(s);
- assert(s->ref >= 1);
+/* Called from main context */
+int pa_source_suspend(pa_source *s, pa_bool_t suspend) {
+ pa_source_assert_ref(s);
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
- if (s->notify)
- s->notify(s);
+ if (suspend)
+ return source_set_state(s, PA_SOURCE_SUSPENDED);
+ else
+ return source_set_state(s, pa_source_used_by(s) ? PA_SOURCE_RUNNING : PA_SOURCE_IDLE);
}
-static int do_post(void *p, PA_GCC_UNUSED uint32_t idx, PA_GCC_UNUSED int *del, void*userdata) {
- pa_source_output *o = p;
- const pa_memchunk *chunk = userdata;
-
- assert(o);
- assert(chunk);
+/* Called from IO thread context */
+void pa_source_process_rewind(pa_source *s, size_t nbytes) {
+ pa_source_output *o;
+ void *state = NULL;
- pa_source_output_push(o, chunk);
- return 0;
+ pa_source_assert_ref(s);
+ pa_assert(PA_SOURCE_IS_OPENED(s->thread_info.state));
+
+ if (nbytes <= 0)
+ return;
+
+ pa_log_debug("Processing rewind...");
+
+ while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) {
+ pa_source_output_assert_ref(o);
+ pa_source_output_process_rewind(o, nbytes);
+ }
}
+/* Called from IO thread context */
void pa_source_post(pa_source*s, const pa_memchunk *chunk) {
- assert(s);
- assert(s->ref >= 1);
- assert(chunk);
+ pa_source_output *o;
+ void *state = NULL;
+
+ pa_source_assert_ref(s);
+ pa_assert(PA_SOURCE_IS_OPENED(s->thread_info.state));
+ pa_assert(chunk);
- pa_source_ref(s);
+ if (s->thread_info.state != PA_SOURCE_RUNNING)
+ return;
- if (s->sw_muted || !pa_cvolume_is_norm(&s->sw_volume)) {
+ if (s->thread_info.soft_muted || !pa_cvolume_is_norm(&s->thread_info.soft_volume)) {
pa_memchunk vchunk = *chunk;
-
+
pa_memblock_ref(vchunk.memblock);
pa_memchunk_make_writable(&vchunk, 0);
- if (s->sw_muted)
+
+ if (s->thread_info.soft_muted || pa_cvolume_is_muted(&s->thread_info.soft_volume))
pa_silence_memchunk(&vchunk, &s->sample_spec);
else
- pa_volume_memchunk(&vchunk, &s->sample_spec, &s->sw_volume);
- pa_idxset_foreach(s->outputs, do_post, &vchunk);
+ pa_volume_memchunk(&vchunk, &s->sample_spec, &s->thread_info.soft_volume);
+
+ while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) {
+ pa_source_output_assert_ref(o);
+
+ if (!o->thread_info.direct_on_input)
+ pa_source_output_push(o, &vchunk);
+ }
+
pa_memblock_unref(vchunk.memblock);
- } else
- pa_idxset_foreach(s->outputs, do_post, (void*) chunk);
+ } else {
- pa_source_unref(s);
+ while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) {
+ pa_source_output_assert_ref(o);
+
+ if (!o->thread_info.direct_on_input)
+ pa_source_output_push(o, chunk);
+ }
+ }
}
-void pa_source_set_owner(pa_source *s, pa_module *m) {
- assert(s);
- assert(s->ref >= 1);
+/* Called from IO thread context */
+void pa_source_post_direct(pa_source*s, pa_source_output *o, const pa_memchunk *chunk) {
+ pa_source_assert_ref(s);
+ pa_assert(PA_SOURCE_IS_OPENED(s->thread_info.state));
+ pa_source_output_assert_ref(o);
+ pa_assert(o->thread_info.direct_on_input);
+ pa_assert(chunk);
- if (m == s->owner)
+ if (s->thread_info.state != PA_SOURCE_RUNNING)
return;
-
- s->owner = m;
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+
+ if (s->thread_info.soft_muted || !pa_cvolume_is_norm(&s->thread_info.soft_volume)) {
+ pa_memchunk vchunk = *chunk;
+
+ pa_memblock_ref(vchunk.memblock);
+ pa_memchunk_make_writable(&vchunk, 0);
+
+ if (s->thread_info.soft_muted || pa_cvolume_is_muted(&s->thread_info.soft_volume))
+ pa_silence_memchunk(&vchunk, &s->sample_spec);
+ else
+ pa_volume_memchunk(&vchunk, &s->sample_spec, &s->thread_info.soft_volume);
+
+ pa_source_output_push(o, &vchunk);
+
+ pa_memblock_unref(vchunk.memblock);
+ } else
+ pa_source_output_push(o, chunk);
}
+/* Called from main thread */
pa_usec_t pa_source_get_latency(pa_source *s) {
- assert(s);
- assert(s->ref >= 1);
+ pa_usec_t usec;
- if (!s->get_latency)
+ pa_source_assert_ref(s);
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
+
+ if (!PA_SOURCE_IS_OPENED(s->state))
return 0;
- return s->get_latency(s);
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_LATENCY, &usec, 0, NULL) == 0);
+
+ return usec;
}
-void pa_source_set_volume(pa_source *s, pa_mixer_t m, const pa_cvolume *volume) {
- pa_cvolume *v;
-
- assert(s);
- assert(s->ref >= 1);
- assert(volume);
+/* Called from main thread */
+void pa_source_set_volume(pa_source *s, const pa_cvolume *volume) {
+ pa_bool_t changed;
- if (m == PA_MIXER_HARDWARE && s->set_hw_volume)
- v = &s->hw_volume;
- else
- v = &s->sw_volume;
+ pa_source_assert_ref(s);
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
+ pa_assert(volume);
- if (pa_cvolume_equal(v, volume))
- return;
-
- *v = *volume;
+ changed = !pa_cvolume_equal(volume, &s->volume);
+ s->volume = *volume;
- if (v == &s->hw_volume)
- if (s->set_hw_volume(s) < 0)
- s->sw_volume = *volume;
+ if (s->set_volume && s->set_volume(s) < 0)
+ s->set_volume = NULL;
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+ if (!s->set_volume)
+ pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_VOLUME, volume, 0, NULL);
+
+ if (changed)
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
}
-const pa_cvolume *pa_source_get_volume(pa_source *s, pa_mixer_t m) {
- assert(s);
- assert(s->ref >= 1);
+/* Called from main thread */
+const pa_cvolume *pa_source_get_volume(pa_source *s) {
+ pa_source_assert_ref(s);
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
- if (m == PA_MIXER_HARDWARE && s->set_hw_volume) {
+ if (s->refresh_volume) {
+ pa_cvolume old_volume = s->volume;
- if (s->get_hw_volume)
- s->get_hw_volume(s);
-
- return &s->hw_volume;
- } else
- return &s->sw_volume;
+ if (s->get_volume && s->get_volume(s) < 0)
+ s->get_volume = NULL;
+
+ if (!s->get_volume)
+ pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_VOLUME, &s->volume, 0, NULL);
+
+ if (!pa_cvolume_equal(&old_volume, &s->volume))
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+ }
+
+ return &s->volume;
}
-void pa_source_set_mute(pa_source *s, pa_mixer_t m, int mute) {
- int *t;
-
- assert(s);
- assert(s->ref >= 1);
+/* Called from main thread */
+void pa_source_set_mute(pa_source *s, pa_bool_t mute) {
+ pa_bool_t changed;
- if (m == PA_MIXER_HARDWARE && s->set_hw_mute)
- t = &s->hw_muted;
- else
- t = &s->sw_muted;
+ pa_source_assert_ref(s);
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
- if (!!*t == !!mute)
- return;
-
- *t = !!mute;
+ changed = s->muted != mute;
+ s->muted = mute;
+
+ if (s->set_mute && s->set_mute(s) < 0)
+ s->set_mute = NULL;
- if (t == &s->hw_muted)
- if (s->set_hw_mute(s) < 0)
- s->sw_muted = !!mute;
+ if (!s->set_mute)
+ pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_MUTE, PA_UINT_TO_PTR(mute), 0, NULL, NULL);
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+ if (changed)
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
}
-int pa_source_get_mute(pa_source *s, pa_mixer_t m) {
- assert(s);
- assert(s->ref >= 1);
+/* Called from main thread */
+pa_bool_t pa_source_get_mute(pa_source *s) {
- if (m == PA_MIXER_HARDWARE && s->set_hw_mute) {
+ pa_source_assert_ref(s);
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
- if (s->get_hw_mute)
- s->get_hw_mute(s);
-
- return s->hw_muted;
- } else
- return s->sw_muted;
+ if (s->refresh_muted) {
+ pa_bool_t old_muted = s->muted;
+
+ if (s->get_mute && s->get_mute(s) < 0)
+ s->get_mute = NULL;
+
+ if (!s->get_mute)
+ pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_MUTE, &s->muted, 0, NULL);
+
+ if (old_muted != s->muted)
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+ }
+
+ return s->muted;
}
+/* Called from main thread */
void pa_source_set_description(pa_source *s, const char *description) {
- assert(s);
- assert(s->ref >= 1);
+ const char *old;
+ pa_source_assert_ref(s);
- if (!description && !s->description)
+ if (!description && !pa_proplist_contains(s->proplist, PA_PROP_DEVICE_DESCRIPTION))
return;
- if (description && s->description && !strcmp(description, s->description))
+ old = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
+
+ if (old && description && !strcmp(old, description))
return;
-
- pa_xfree(s->description);
- s->description = pa_xstrdup(description);
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+ if (description)
+ pa_proplist_sets(s->proplist, PA_PROP_DEVICE_DESCRIPTION, description);
+ else
+ pa_proplist_unset(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
+
+ if (PA_SOURCE_IS_LINKED(s->state)) {
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+ pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED], s);
+ }
}
-unsigned pa_source_used_by(pa_source *s) {
- assert(s);
- assert(s->ref >= 1);
+/* Called from main thread */
+unsigned pa_source_linked_by(pa_source *s) {
+ pa_source_assert_ref(s);
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
return pa_idxset_size(s->outputs);
}
+
+/* Called from main thread */
+unsigned pa_source_used_by(pa_source *s) {
+ unsigned ret;
+
+ pa_source_assert_ref(s);
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
+
+ ret = pa_idxset_size(s->outputs);
+ pa_assert(ret >= s->n_corked);
+
+ return ret - s->n_corked;
+}
+
+/* Called from IO thread, except when it is not */
+int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
+ pa_source *s = PA_SOURCE(object);
+ pa_source_assert_ref(s);
+
+ switch ((pa_source_message_t) code) {
+
+ case PA_SOURCE_MESSAGE_ADD_OUTPUT: {
+ pa_source_output *o = PA_SOURCE_OUTPUT(userdata);
+
+ pa_hashmap_put(s->thread_info.outputs, PA_UINT32_TO_PTR(o->index), pa_source_output_ref(o));
+
+ if (o->direct_on_input) {
+ o->thread_info.direct_on_input = o->direct_on_input;
+ pa_hashmap_put(o->thread_info.direct_on_input->thread_info.direct_outputs, PA_UINT32_TO_PTR(o->index), o);
+ }
+
+ pa_assert(!o->thread_info.attached);
+ o->thread_info.attached = TRUE;
+
+ if (o->attach)
+ o->attach(o);
+
+ pa_source_output_set_state_within_thread(o, o->state);
+
+ pa_source_output_update_max_rewind(o, s->thread_info.max_rewind);
+
+ /* We don't just invalidate the requested latency here,
+ * because if we are in a move we might need to fix up the
+ * requested latency. */
+ pa_source_output_set_requested_latency_within_thread(o, o->thread_info.requested_source_latency);
+
+ return 0;
+ }
+
+ case PA_SOURCE_MESSAGE_REMOVE_OUTPUT: {
+ pa_source_output *o = PA_SOURCE_OUTPUT(userdata);
+
+ pa_source_output_set_state_within_thread(o, o->state);
+
+ if (o->detach)
+ o->detach(o);
+
+ pa_assert(o->thread_info.attached);
+ o->thread_info.attached = FALSE;
+
+ if (o->thread_info.direct_on_input) {
+ pa_hashmap_remove(o->thread_info.direct_on_input->thread_info.direct_outputs, PA_UINT32_TO_PTR(o->index));
+ o->thread_info.direct_on_input = NULL;
+ }
+
+ if (pa_hashmap_remove(s->thread_info.outputs, PA_UINT32_TO_PTR(o->index)))
+ pa_source_output_unref(o);
+
+ pa_source_invalidate_requested_latency(s);
+
+ return 0;
+ }
+
+ case PA_SOURCE_MESSAGE_SET_VOLUME:
+ s->thread_info.soft_volume = *((pa_cvolume*) userdata);
+ return 0;
+
+ case PA_SOURCE_MESSAGE_SET_MUTE:
+ s->thread_info.soft_muted = PA_PTR_TO_UINT(userdata);
+ return 0;
+
+ case PA_SOURCE_MESSAGE_GET_VOLUME:
+ *((pa_cvolume*) userdata) = s->thread_info.soft_volume;
+ return 0;
+
+ case PA_SOURCE_MESSAGE_GET_MUTE:
+ *((pa_bool_t*) userdata) = s->thread_info.soft_muted;
+ return 0;
+
+ case PA_SOURCE_MESSAGE_SET_STATE:
+ s->thread_info.state = PA_PTR_TO_UINT(userdata);
+ return 0;
+
+ case PA_SOURCE_MESSAGE_DETACH:
+
+ /* Detach all streams */
+ pa_source_detach_within_thread(s);
+ return 0;
+
+ case PA_SOURCE_MESSAGE_ATTACH:
+
+ /* Reattach all streams */
+ pa_source_attach_within_thread(s);
+ return 0;
+
+ case PA_SOURCE_MESSAGE_GET_REQUESTED_LATENCY: {
+
+ pa_usec_t *usec = userdata;
+ *usec = pa_source_get_requested_latency_within_thread(s);
+
+ if (*usec == (pa_usec_t) -1)
+ *usec = s->thread_info.max_latency;
+
+ return 0;
+ }
+
+ case PA_SOURCE_MESSAGE_SET_LATENCY_RANGE: {
+ pa_usec_t *r = userdata;
+
+ pa_source_update_latency_range(s, r[0], r[1]);
+
+ return 0;
+ }
+
+ case PA_SOURCE_MESSAGE_GET_LATENCY_RANGE: {
+ pa_usec_t *r = userdata;
+
+ r[0] = s->thread_info.min_latency;
+ r[1] = s->thread_info.max_latency;
+
+ return 0;
+ }
+
+ case PA_SOURCE_MESSAGE_GET_MAX_REWIND:
+
+ *((size_t*) userdata) = s->thread_info.max_rewind;
+ return 0;
+
+ case PA_SOURCE_MESSAGE_GET_LATENCY:
+
+ if (s->monitor_of) {
+ *((pa_usec_t*) userdata) = 0;
+ return 0;
+ }
+
+ /* Implementors need to overwrite this implementation! */
+ return -1;
+
+ case PA_SOURCE_MESSAGE_MAX:
+ ;
+ }
+
+ return -1;
+}
+
+/* Called from main thread */
+int pa_source_suspend_all(pa_core *c, pa_bool_t suspend) {
+ uint32_t idx;
+ pa_source *source;
+ int ret = 0;
+
+ pa_core_assert_ref(c);
+
+ for (source = PA_SOURCE(pa_idxset_first(c->sources, &idx)); source; source = PA_SOURCE(pa_idxset_next(c->sources, &idx)))
+ ret -= pa_source_suspend(source, suspend) < 0;
+
+ return ret;
+}
+
+/* Called from main thread */
+void pa_source_detach(pa_source *s) {
+ pa_source_assert_ref(s);
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
+
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_DETACH, NULL, 0, NULL) == 0);
+}
+
+/* Called from main thread */
+void pa_source_attach(pa_source *s) {
+ pa_source_assert_ref(s);
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
+
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_ATTACH, NULL, 0, NULL) == 0);
+}
+
+/* Called from IO thread */
+void pa_source_detach_within_thread(pa_source *s) {
+ pa_source_output *o;
+ void *state = NULL;
+
+ pa_source_assert_ref(s);
+ pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state));
+
+ while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
+ if (o->detach)
+ o->detach(o);
+}
+
+/* Called from IO thread */
+void pa_source_attach_within_thread(pa_source *s) {
+ pa_source_output *o;
+ void *state = NULL;
+
+ pa_source_assert_ref(s);
+ pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state));
+
+ while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
+ if (o->attach)
+ o->attach(o);
+}
+
+/* Called from IO thread */
+pa_usec_t pa_source_get_requested_latency_within_thread(pa_source *s) {
+ pa_usec_t result = (pa_usec_t) -1;
+ pa_source_output *o;
+ void *state = NULL;
+
+ pa_source_assert_ref(s);
+
+ if (s->thread_info.requested_latency_valid)
+ return s->thread_info.requested_latency;
+
+ while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
+
+ if (o->thread_info.requested_source_latency != (pa_usec_t) -1 &&
+ (result == (pa_usec_t) -1 || result > o->thread_info.requested_source_latency))
+ result = o->thread_info.requested_source_latency;
+
+ if (result != (pa_usec_t) -1) {
+ if (s->thread_info.max_latency > 0 && result > s->thread_info.max_latency)
+ result = s->thread_info.max_latency;
+
+ if (s->thread_info.min_latency > 0 && result < s->thread_info.min_latency)
+ result = s->thread_info.min_latency;
+ }
+
+ s->thread_info.requested_latency = result;
+ s->thread_info.requested_latency_valid = TRUE;
+
+ return result;
+}
+
+/* Called from main thread */
+pa_usec_t pa_source_get_requested_latency(pa_source *s) {
+ pa_usec_t usec;
+
+ pa_source_assert_ref(s);
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
+
+ if (!PA_SOURCE_IS_OPENED(s->state))
+ return 0;
+
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
+
+ return usec;
+}
+
+/* Called from IO thread */
+void pa_source_set_max_rewind(pa_source *s, size_t max_rewind) {
+ pa_source_output *o;
+ void *state = NULL;
+
+ pa_source_assert_ref(s);
+
+ if (max_rewind == s->thread_info.max_rewind)
+ return;
+
+ s->thread_info.max_rewind = max_rewind;
+
+ if (PA_SOURCE_IS_LINKED(s->thread_info.state)) {
+ while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
+ pa_source_output_update_max_rewind(o, s->thread_info.max_rewind);
+ }
+}
+
+void pa_source_invalidate_requested_latency(pa_source *s) {
+ pa_source_output *o;
+ void *state = NULL;
+
+ pa_source_assert_ref(s);
+
+ s->thread_info.requested_latency_valid = FALSE;
+
+ if (s->update_requested_latency)
+ s->update_requested_latency(s);
+
+ while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
+ o->update_source_requested_latency(o);
+
+ if (s->monitor_of)
+ pa_sink_invalidate_requested_latency(s->monitor_of);
+}
+
+void pa_source_set_latency_range(pa_source *s, pa_usec_t min_latency, pa_usec_t max_latency) {
+ pa_source_assert_ref(s);
+
+ /* min_latency == 0: no limit
+ * min_latency == (size_t) -1: default limit
+ * min_latency anything else: specified limit
+ *
+ * Similar for max_latency */
+
+ if (min_latency == (pa_usec_t) -1)
+ min_latency = DEFAULT_MIN_LATENCY;
+
+ if (max_latency == (pa_usec_t) -1)
+ max_latency = min_latency;
+
+ pa_assert(!min_latency || !max_latency ||
+ min_latency <= max_latency);
+
+ if (PA_SINK_IS_LINKED(s->state)) {
+ pa_usec_t r[2];
+
+ r[0] = min_latency;
+ r[1] = max_latency;
+
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_LATENCY_RANGE, r, 0, NULL) == 0);
+ } else {
+ s->thread_info.min_latency = min_latency;
+ s->thread_info.max_latency = max_latency;
+
+ s->thread_info.requested_latency_valid = FALSE;
+ }
+}
+
+void pa_source_get_latency_range(pa_source *s, pa_usec_t *min_latency, pa_usec_t *max_latency) {
+ pa_source_assert_ref(s);
+ pa_assert(min_latency);
+ pa_assert(max_latency);
+
+ if (PA_SOURCE_IS_LINKED(s->state)) {
+ pa_usec_t r[2] = { 0, 0 };
+
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_LATENCY_RANGE, r, 0, NULL) == 0);
+
+ *min_latency = r[0];
+ *max_latency = r[1];
+ } else {
+ *min_latency = s->thread_info.min_latency;
+ *max_latency = s->thread_info.max_latency;
+ }
+}
+
+/* Called from IO thread */
+void pa_source_update_latency_range(pa_source *s, pa_usec_t min_latency, pa_usec_t max_latency) {
+ pa_source_output *o;
+ void *state = NULL;
+
+ pa_source_assert_ref(s);
+
+ s->thread_info.min_latency = min_latency;
+ s->thread_info.max_latency = max_latency;
+
+ while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
+ if (o->update_source_latency_range)
+ o->update_source_latency_range(o);
+
+ pa_source_invalidate_requested_latency(s);
+}
+
+size_t pa_source_get_max_rewind(pa_source *s) {
+ size_t r;
+ pa_source_assert_ref(s);
+
+ if (!PA_SOURCE_IS_LINKED(s->state))
+ return s->thread_info.max_rewind;
+
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_MAX_REWIND, &r, 0, NULL) == 0);
+
+ return r;
+}
diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h
index 4dbe4e01..f4a17e8d 100644
--- a/src/pulsecore/source.h
+++ b/src/pulsecore/source.h
@@ -1,21 +1,22 @@
-#ifndef foosourcehfoo
-#define foosourcehfoo
-
-/* $Id$ */
+#ifndef foopulsesourcehfoo
+#define foopulsesourcehfoo
/***
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
@@ -29,80 +30,228 @@ typedef struct pa_source pa_source;
#include <pulse/sample.h>
#include <pulse/channelmap.h>
#include <pulse/volume.h>
-#include <pulsecore/core-def.h>
+
#include <pulsecore/core.h>
#include <pulsecore/idxset.h>
#include <pulsecore/memblock.h>
#include <pulsecore/memchunk.h>
#include <pulsecore/sink.h>
#include <pulsecore/module.h>
+#include <pulsecore/asyncmsgq.h>
+#include <pulsecore/msgobject.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/source-output.h>
-#define PA_MAX_OUTPUTS_PER_SOURCE 16
+#define PA_MAX_OUTPUTS_PER_SOURCE 32
typedef enum pa_source_state {
+ PA_SOURCE_INIT,
PA_SOURCE_RUNNING,
- PA_SOURCE_DISCONNECTED
+ PA_SOURCE_SUSPENDED,
+ PA_SOURCE_IDLE,
+ PA_SOURCE_UNLINKED
} pa_source_state_t;
+static inline pa_bool_t PA_SOURCE_IS_OPENED(pa_source_state_t x) {
+ return x == PA_SOURCE_RUNNING || x == PA_SOURCE_IDLE;
+}
+
+static inline pa_bool_t PA_SOURCE_IS_LINKED(pa_source_state_t x) {
+ return x == PA_SOURCE_RUNNING || x == PA_SOURCE_IDLE || x == PA_SOURCE_SUSPENDED;
+}
+
struct pa_source {
- int ref;
+ pa_msgobject parent;
+
uint32_t index;
pa_core *core;
pa_source_state_t state;
-
+ pa_source_flags_t flags;
+
char *name;
- char *description, *driver; /* may be NULL */
-
- pa_module *owner; /* may be NULL */
+ char *driver; /* may be NULL */
+ pa_proplist *proplist;
+
+ pa_module *module; /* may be NULL */
pa_sample_spec sample_spec;
pa_channel_map channel_map;
pa_idxset *outputs;
+ unsigned n_corked;
pa_sink *monitor_of; /* may be NULL */
- pa_cvolume hw_volume, sw_volume;
- int hw_muted, sw_muted;
-
- int is_hardware;
-
- void (*notify)(pa_source*source); /* may be NULL */
- pa_usec_t (*get_latency)(pa_source *s); /* dito */
- int (*set_hw_volume)(pa_source *s); /* dito */
- int (*get_hw_volume)(pa_source *s); /* dito */
- int (*set_hw_mute)(pa_source *s); /* dito */
- int (*get_hw_mute)(pa_source *s); /* dito */
-
+ pa_cvolume volume;
+ pa_bool_t muted;
+
+ pa_bool_t refresh_volume:1;
+ pa_bool_t refresh_muted:1;
+
+ pa_asyncmsgq *asyncmsgq;
+ pa_rtpoll *rtpoll;
+
+ pa_memchunk silence;
+
+ /* Called when the main loop requests a state change. Called from
+ * main loop context. If returns -1 the state change will be
+ * inhibited */
+ int (*set_state)(pa_source*source, pa_source_state_t state); /* may be NULL */
+
+ /* Callled when the volume is queried. Called from main loop
+ * context. If this is NULL a PA_SOURCE_MESSAGE_GET_VOLUME message
+ * will be sent to the IO thread instead. If refresh_volume is
+ * FALSE neither this function is called nor a message is sent. */
+ int (*get_volume)(pa_source *s); /* dito */
+
+ /* Called when the volume shall be changed. Called from main loop
+ * context. If this is NULL a PA_SOURCE_MESSAGE_SET_VOLUME message
+ * will be sent to the IO thread instead. */
+ int (*set_volume)(pa_source *s); /* dito */
+
+ /* Called when the mute setting is queried. Called from main loop
+ * context. If this is NULL a PA_SOURCE_MESSAGE_GET_MUTE message
+ * will be sent to the IO thread instead. If refresh_mute is
+ * FALSE neither this function is called nor a message is sent.*/
+ int (*get_mute)(pa_source *s); /* dito */
+
+ /* Called when the mute setting shall be changed. Called from main
+ * loop context. If this is NULL a PA_SOURCE_MESSAGE_SET_MUTE
+ * message will be sent to the IO thread instead. */
+ int (*set_mute)(pa_source *s); /* dito */
+
+ /* Called when a the requested latency is changed. Called from IO
+ * thread context. */
+ void (*update_requested_latency)(pa_source *s); /* dito */
+
+ /* Contains copies of the above data so that the real-time worker
+ * thread can work without access locking */
+ struct {
+ pa_source_state_t state;
+ pa_hashmap *outputs;
+ pa_cvolume soft_volume;
+ pa_bool_t soft_muted:1;
+
+ pa_bool_t requested_latency_valid:1;
+ pa_usec_t requested_latency;
+
+ /* Then number of bytes this source will be rewound for at
+ * max. (Only used on monitor sources) */
+ size_t max_rewind;
+
+ pa_usec_t min_latency; /* we won't go below this latency */
+ pa_usec_t max_latency; /* An upper limit for the latencies */
+ } thread_info;
+
void *userdata;
};
+PA_DECLARE_CLASS(pa_source);
+#define PA_SOURCE(s) pa_source_cast(s)
+
+typedef enum pa_source_message {
+ PA_SOURCE_MESSAGE_ADD_OUTPUT,
+ PA_SOURCE_MESSAGE_REMOVE_OUTPUT,
+ PA_SOURCE_MESSAGE_GET_VOLUME,
+ PA_SOURCE_MESSAGE_SET_VOLUME,
+ PA_SOURCE_MESSAGE_GET_MUTE,
+ PA_SOURCE_MESSAGE_SET_MUTE,
+ PA_SOURCE_MESSAGE_GET_LATENCY,
+ PA_SOURCE_MESSAGE_GET_REQUESTED_LATENCY,
+ PA_SOURCE_MESSAGE_SET_STATE,
+ PA_SOURCE_MESSAGE_ATTACH,
+ PA_SOURCE_MESSAGE_DETACH,
+ PA_SOURCE_MESSAGE_SET_LATENCY_RANGE,
+ PA_SOURCE_MESSAGE_GET_LATENCY_RANGE,
+ PA_SOURCE_MESSAGE_GET_MAX_REWIND,
+ PA_SOURCE_MESSAGE_MAX
+} pa_source_message_t;
+
+typedef struct pa_source_new_data {
+ char *name;
+ pa_bool_t namereg_fail;
+ pa_proplist *proplist;
+
+ const char *driver;
+ pa_module *module;
+
+ pa_sample_spec sample_spec;
+ pa_bool_t sample_spec_is_set;
+ pa_channel_map channel_map;
+ pa_bool_t channel_map_is_set;
+
+ pa_cvolume volume;
+ pa_bool_t volume_is_set;
+ pa_bool_t muted;
+ pa_bool_t muted_is_set;
+} pa_source_new_data;
+
+pa_source_new_data* pa_source_new_data_init(pa_source_new_data *data);
+void pa_source_new_data_set_name(pa_source_new_data *data, const char *name);
+void pa_source_new_data_set_sample_spec(pa_source_new_data *data, const pa_sample_spec *spec);
+void pa_source_new_data_set_channel_map(pa_source_new_data *data, const pa_channel_map *map);
+void pa_source_new_data_set_volume(pa_source_new_data *data, const pa_cvolume *volume);
+void pa_source_new_data_set_muted(pa_source_new_data *data, pa_bool_t mute);
+void pa_source_new_data_done(pa_source_new_data *data);
+
+/* To be called exclusively by the source driver, from main context */
+
pa_source* pa_source_new(
- pa_core *core,
- const char *driver,
- const char *name,
- int namereg_fail,
- const pa_sample_spec *spec,
- const pa_channel_map *map);
+ pa_core *core,
+ pa_source_new_data *data,
+ pa_source_flags_t flags);
+
+void pa_source_put(pa_source *s);
+void pa_source_unlink(pa_source *s);
-void pa_source_disconnect(pa_source *s);
-void pa_source_unref(pa_source *s);
-pa_source* pa_source_ref(pa_source *c);
+void pa_source_set_description(pa_source *s, const char *description);
+void pa_source_set_asyncmsgq(pa_source *s, pa_asyncmsgq *q);
+void pa_source_set_rtpoll(pa_source *s, pa_rtpoll *p);
-/* Pass a new memory block to all output streams */
-void pa_source_post(pa_source*s, const pa_memchunk *b);
+void pa_source_set_latency_range(pa_source *s, pa_usec_t min_latency, pa_usec_t max_latency);
-void pa_source_notify(pa_source *s);
+void pa_source_detach(pa_source *s);
+void pa_source_attach(pa_source *s);
-void pa_source_set_owner(pa_source *s, pa_module *m);
+/* May be called by everyone, from main context */
+/* The returned value is supposed to be in the time domain of the sound card! */
pa_usec_t pa_source_get_latency(pa_source *s);
+pa_usec_t pa_source_get_requested_latency(pa_source *s);
+void pa_source_get_latency_range(pa_source *s, pa_usec_t *min_latency, pa_usec_t *max_latency);
-void pa_source_set_volume(pa_source *source, pa_mixer_t m, const pa_cvolume *volume);
-const pa_cvolume *pa_source_get_volume(pa_source *source, pa_mixer_t m);
-void pa_source_set_mute(pa_source *source, pa_mixer_t m, int mute);
-int pa_source_get_mute(pa_source *source, pa_mixer_t m);
+size_t pa_source_get_max_rewind(pa_source *s);
-void pa_source_set_description(pa_source *s, const char *description);
+int pa_source_update_status(pa_source*s);
+int pa_source_suspend(pa_source *s, pa_bool_t suspend);
+int pa_source_suspend_all(pa_core *c, pa_bool_t suspend);
+
+void pa_source_set_volume(pa_source *source, const pa_cvolume *volume);
+const pa_cvolume *pa_source_get_volume(pa_source *source);
+void pa_source_set_mute(pa_source *source, pa_bool_t mute);
+pa_bool_t pa_source_get_mute(pa_source *source);
+
+unsigned pa_source_linked_by(pa_source *s); /* Number of connected streams */
+unsigned pa_source_used_by(pa_source *s); /* Number of connected streams that are not corked */
+#define pa_source_get_state(s) ((pa_source_state_t) (s)->state)
+
+/* To be called exclusively by the source driver, from IO context */
+
+void pa_source_post(pa_source*s, const pa_memchunk *chunk);
+void pa_source_post_direct(pa_source*s, pa_source_output *o, const pa_memchunk *chunk);
+void pa_source_process_rewind(pa_source *s, size_t nbytes);
+
+int pa_source_process_msg(pa_msgobject *o, int code, void *userdata, int64_t, pa_memchunk *chunk);
+
+void pa_source_attach_within_thread(pa_source *s);
+void pa_source_detach_within_thread(pa_source *s);
+
+pa_usec_t pa_source_get_requested_latency_within_thread(pa_source *s);
+
+void pa_source_set_max_rewind(pa_source *s, size_t max_rewind);
+void pa_source_update_latency_range(pa_source *s, pa_usec_t min_latency, pa_usec_t max_latency);
+
+/* To be called exclusively by source output drivers, from IO context */
+
+void pa_source_invalidate_requested_latency(pa_source *s);
-unsigned pa_source_used_by(pa_source *s);
#endif
diff --git a/src/pulsecore/speex/Makefile b/src/pulsecore/speex/Makefile
new file mode 100644
index 00000000..316beb72
--- /dev/null
+++ b/src/pulsecore/speex/Makefile
@@ -0,0 +1,13 @@
+# This is a dirty trick just to ease compilation with emacs
+#
+# This file is not intended to be distributed or anything
+#
+# So: don't touch it, even better ignore it!
+
+all:
+ $(MAKE) -C ../..
+
+clean:
+ $(MAKE) -C ../.. clean
+
+.PHONY: all clean
diff --git a/src/pulsecore/speex/arch.h b/src/pulsecore/speex/arch.h
new file mode 100644
index 00000000..9987c8fb
--- /dev/null
+++ b/src/pulsecore/speex/arch.h
@@ -0,0 +1,241 @@
+/* Copyright (C) 2003 Jean-Marc Valin */
+/**
+ @file arch.h
+ @brief Various architecture definitions Speex
+*/
+/*
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ - Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ - Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ - Neither the name of the Xiph.org Foundation nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef ARCH_H
+#define ARCH_H
+
+#ifndef SPEEX_VERSION
+#define SPEEX_MAJOR_VERSION 1 /**< Major Speex version. */
+#define SPEEX_MINOR_VERSION 1 /**< Minor Speex version. */
+#define SPEEX_MICRO_VERSION 15 /**< Micro Speex version. */
+#define SPEEX_EXTRA_VERSION "" /**< Extra Speex version. */
+#define SPEEX_VERSION "speex-1.2beta3" /**< Speex version string. */
+#endif
+
+/* A couple test to catch stupid option combinations */
+#ifdef FIXED_POINT
+
+#ifdef FLOATING_POINT
+#error You cannot compile as floating point and fixed point at the same time
+#endif
+#ifdef _USE_SSE
+#error SSE is only for floating-point
+#endif
+#if ((defined (ARM4_ASM)||defined (ARM4_ASM)) && defined(BFIN_ASM)) || (defined (ARM4_ASM)&&defined(ARM5E_ASM))
+#error Make up your mind. What CPU do you have?
+#endif
+#ifdef VORBIS_PSYCHO
+#error Vorbis-psy model currently not implemented in fixed-point
+#endif
+
+#else
+
+#ifndef FLOATING_POINT
+#error You now need to define either FIXED_POINT or FLOATING_POINT
+#endif
+#if defined (ARM4_ASM) || defined(ARM5E_ASM) || defined(BFIN_ASM)
+#error I suppose you can have a [ARM4/ARM5E/Blackfin] that has float instructions?
+#endif
+#ifdef FIXED_POINT_DEBUG
+#error "Don't you think enabling fixed-point is a good thing to do if you want to debug that?"
+#endif
+
+
+#endif
+
+#ifndef OUTSIDE_SPEEX
+#include "speex/speex_types.h"
+#endif
+
+#define ABS(x) ((x) < 0 ? (-(x)) : (x)) /**< Absolute integer value. */
+#define ABS16(x) ((x) < 0 ? (-(x)) : (x)) /**< Absolute 16-bit value. */
+#define MIN16(a,b) ((a) < (b) ? (a) : (b)) /**< Maximum 16-bit value. */
+#define MAX16(a,b) ((a) > (b) ? (a) : (b)) /**< Maximum 16-bit value. */
+#define ABS32(x) ((x) < 0 ? (-(x)) : (x)) /**< Absolute 32-bit value. */
+#define MIN32(a,b) ((a) < (b) ? (a) : (b)) /**< Maximum 32-bit value. */
+#define MAX32(a,b) ((a) > (b) ? (a) : (b)) /**< Maximum 32-bit value. */
+
+#ifdef FIXED_POINT
+
+typedef spx_int16_t spx_word16_t;
+typedef spx_int32_t spx_word32_t;
+typedef spx_word32_t spx_mem_t;
+typedef spx_word16_t spx_coef_t;
+typedef spx_word16_t spx_lsp_t;
+typedef spx_word32_t spx_sig_t;
+
+#define Q15ONE 32767
+
+#define LPC_SCALING 8192
+#define SIG_SCALING 16384
+#define LSP_SCALING 8192.
+#define GAMMA_SCALING 32768.
+#define GAIN_SCALING 64
+#define GAIN_SCALING_1 0.015625
+
+#define LPC_SHIFT 13
+#define LSP_SHIFT 13
+#define SIG_SHIFT 14
+#define GAIN_SHIFT 6
+
+#define VERY_SMALL 0
+#define VERY_LARGE32 ((spx_word32_t)2147483647)
+#define VERY_LARGE16 ((spx_word16_t)32767)
+#define Q15_ONE ((spx_word16_t)32767)
+
+
+#ifdef FIXED_DEBUG
+#include "fixed_debug.h"
+#else
+
+#include "fixed_generic.h"
+
+#ifdef ARM5E_ASM
+#include "fixed_arm5e.h"
+#elif defined (ARM4_ASM)
+#include "fixed_arm4.h"
+#elif defined (ARM5E_ASM)
+#include "fixed_arm5e.h"
+#elif defined (BFIN_ASM)
+#include "fixed_bfin.h"
+#endif
+
+#endif
+
+
+#else
+
+typedef float spx_mem_t;
+typedef float spx_coef_t;
+typedef float spx_lsp_t;
+typedef float spx_sig_t;
+typedef float spx_word16_t;
+typedef float spx_word32_t;
+
+#define Q15ONE 1.0f
+#define LPC_SCALING 1.f
+#define SIG_SCALING 1.f
+#define LSP_SCALING 1.f
+#define GAMMA_SCALING 1.f
+#define GAIN_SCALING 1.f
+#define GAIN_SCALING_1 1.f
+
+
+#define VERY_SMALL 1e-15f
+#define VERY_LARGE32 1e15f
+#define VERY_LARGE16 1e15f
+#define Q15_ONE ((spx_word16_t)1.f)
+
+#define QCONST16(x,bits) (x)
+#define QCONST32(x,bits) (x)
+
+#define NEG16(x) (-(x))
+#define NEG32(x) (-(x))
+#define EXTRACT16(x) (x)
+#define EXTEND32(x) (x)
+#define SHR16(a,shift) (a)
+#define SHL16(a,shift) (a)
+#define SHR32(a,shift) (a)
+#define SHL32(a,shift) (a)
+#define PSHR16(a,shift) (a)
+#define PSHR32(a,shift) (a)
+#define VSHR32(a,shift) (a)
+#define SATURATE16(x,a) (x)
+#define SATURATE32(x,a) (x)
+
+#define PSHR(a,shift) (a)
+#define SHR(a,shift) (a)
+#define SHL(a,shift) (a)
+#define SATURATE(x,a) (x)
+
+#define ADD16(a,b) ((a)+(b))
+#define SUB16(a,b) ((a)-(b))
+#define ADD32(a,b) ((a)+(b))
+#define SUB32(a,b) ((a)-(b))
+#define MULT16_16_16(a,b) ((a)*(b))
+#define MULT16_16(a,b) ((spx_word32_t)(a)*(spx_word32_t)(b))
+#define MAC16_16(c,a,b) ((c)+(spx_word32_t)(a)*(spx_word32_t)(b))
+
+#define MULT16_32_Q11(a,b) ((a)*(b))
+#define MULT16_32_Q13(a,b) ((a)*(b))
+#define MULT16_32_Q14(a,b) ((a)*(b))
+#define MULT16_32_Q15(a,b) ((a)*(b))
+#define MULT16_32_P15(a,b) ((a)*(b))
+
+#define MAC16_32_Q11(c,a,b) ((c)+(a)*(b))
+#define MAC16_32_Q15(c,a,b) ((c)+(a)*(b))
+
+#define MAC16_16_Q11(c,a,b) ((c)+(a)*(b))
+#define MAC16_16_Q13(c,a,b) ((c)+(a)*(b))
+#define MAC16_16_P13(c,a,b) ((c)+(a)*(b))
+#define MULT16_16_Q11_32(a,b) ((a)*(b))
+#define MULT16_16_Q13(a,b) ((a)*(b))
+#define MULT16_16_Q14(a,b) ((a)*(b))
+#define MULT16_16_Q15(a,b) ((a)*(b))
+#define MULT16_16_P15(a,b) ((a)*(b))
+#define MULT16_16_P13(a,b) ((a)*(b))
+#define MULT16_16_P14(a,b) ((a)*(b))
+
+#define DIV32_16(a,b) (((spx_word32_t)(a))/(spx_word16_t)(b))
+#define PDIV32_16(a,b) (((spx_word32_t)(a))/(spx_word16_t)(b))
+#define DIV32(a,b) (((spx_word32_t)(a))/(spx_word32_t)(b))
+#define PDIV32(a,b) (((spx_word32_t)(a))/(spx_word32_t)(b))
+
+
+#endif
+
+
+#if defined (CONFIG_TI_C54X) || defined (CONFIG_TI_C55X)
+
+/* 2 on TI C5x DSP */
+#define BYTES_PER_CHAR 2
+#define BITS_PER_CHAR 16
+#define LOG2_BITS_PER_CHAR 4
+
+#else
+
+#define BYTES_PER_CHAR 1
+#define BITS_PER_CHAR 8
+#define LOG2_BITS_PER_CHAR 3
+
+#endif
+
+
+
+#ifdef FIXED_DEBUG
+long long spx_mips=0;
+#endif
+
+
+#endif
diff --git a/src/pulsecore/speex/fixed_generic.h b/src/pulsecore/speex/fixed_generic.h
new file mode 100644
index 00000000..547e22c7
--- /dev/null
+++ b/src/pulsecore/speex/fixed_generic.h
@@ -0,0 +1,106 @@
+/* Copyright (C) 2003 Jean-Marc Valin */
+/**
+ @file fixed_generic.h
+ @brief Generic fixed-point operations
+*/
+/*
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ - Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ - Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ - Neither the name of the Xiph.org Foundation nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef FIXED_GENERIC_H
+#define FIXED_GENERIC_H
+
+#define QCONST16(x,bits) ((spx_word16_t)(.5+(x)*(((spx_word32_t)1)<<(bits))))
+#define QCONST32(x,bits) ((spx_word32_t)(.5+(x)*(((spx_word32_t)1)<<(bits))))
+
+#define NEG16(x) (-(x))
+#define NEG32(x) (-(x))
+#define EXTRACT16(x) ((spx_word16_t)(x))
+#define EXTEND32(x) ((spx_word32_t)(x))
+#define SHR16(a,shift) ((a) >> (shift))
+#define SHL16(a,shift) ((a) << (shift))
+#define SHR32(a,shift) ((a) >> (shift))
+#define SHL32(a,shift) ((a) << (shift))
+#define PSHR16(a,shift) (SHR16((a)+((1<<((shift))>>1)),shift))
+#define PSHR32(a,shift) (SHR32((a)+((1<<((shift))>>1)),shift))
+#define VSHR32(a, shift) (((shift)>0) ? SHR32(a, shift) : SHL32(a, -(shift)))
+#define SATURATE16(x,a) (((x)>(a) ? (a) : (x)<-(a) ? -(a) : (x)))
+#define SATURATE32(x,a) (((x)>(a) ? (a) : (x)<-(a) ? -(a) : (x)))
+
+#define SHR(a,shift) ((a) >> (shift))
+#define SHL(a,shift) ((spx_word32_t)(a) << (shift))
+#define PSHR(a,shift) (SHR((a)+((1<<((shift))>>1)),shift))
+#define SATURATE(x,a) (((x)>(a) ? (a) : (x)<-(a) ? -(a) : (x)))
+
+
+#define ADD16(a,b) ((spx_word16_t)((spx_word16_t)(a)+(spx_word16_t)(b)))
+#define SUB16(a,b) ((spx_word16_t)(a)-(spx_word16_t)(b))
+#define ADD32(a,b) ((spx_word32_t)(a)+(spx_word32_t)(b))
+#define SUB32(a,b) ((spx_word32_t)(a)-(spx_word32_t)(b))
+
+
+/* result fits in 16 bits */
+#define MULT16_16_16(a,b) ((((spx_word16_t)(a))*((spx_word16_t)(b))))
+
+/* (spx_word32_t)(spx_word16_t) gives TI compiler a hint that it's 16x16->32 multiply */
+#define MULT16_16(a,b) (((spx_word32_t)(spx_word16_t)(a))*((spx_word32_t)(spx_word16_t)(b)))
+
+#define MAC16_16(c,a,b) (ADD32((c),MULT16_16((a),(b))))
+#define MULT16_32_Q12(a,b) ADD32(MULT16_16((a),SHR((b),12)), SHR(MULT16_16((a),((b)&0x00000fff)),12))
+#define MULT16_32_Q13(a,b) ADD32(MULT16_16((a),SHR((b),13)), SHR(MULT16_16((a),((b)&0x00001fff)),13))
+#define MULT16_32_Q14(a,b) ADD32(MULT16_16((a),SHR((b),14)), SHR(MULT16_16((a),((b)&0x00003fff)),14))
+
+#define MULT16_32_Q11(a,b) ADD32(MULT16_16((a),SHR((b),11)), SHR(MULT16_16((a),((b)&0x000007ff)),11))
+#define MAC16_32_Q11(c,a,b) ADD32(c,ADD32(MULT16_16((a),SHR((b),11)), SHR(MULT16_16((a),((b)&0x000007ff)),11)))
+
+#define MULT16_32_P15(a,b) ADD32(MULT16_16((a),SHR((b),15)), PSHR(MULT16_16((a),((b)&0x00007fff)),15))
+#define MULT16_32_Q15(a,b) ADD32(MULT16_16((a),SHR((b),15)), SHR(MULT16_16((a),((b)&0x00007fff)),15))
+#define MAC16_32_Q15(c,a,b) ADD32(c,ADD32(MULT16_16((a),SHR((b),15)), SHR(MULT16_16((a),((b)&0x00007fff)),15)))
+
+
+#define MAC16_16_Q11(c,a,b) (ADD32((c),SHR(MULT16_16((a),(b)),11)))
+#define MAC16_16_Q13(c,a,b) (ADD32((c),SHR(MULT16_16((a),(b)),13)))
+#define MAC16_16_P13(c,a,b) (ADD32((c),SHR(ADD32(4096,MULT16_16((a),(b))),13)))
+
+#define MULT16_16_Q11_32(a,b) (SHR(MULT16_16((a),(b)),11))
+#define MULT16_16_Q13(a,b) (SHR(MULT16_16((a),(b)),13))
+#define MULT16_16_Q14(a,b) (SHR(MULT16_16((a),(b)),14))
+#define MULT16_16_Q15(a,b) (SHR(MULT16_16((a),(b)),15))
+
+#define MULT16_16_P13(a,b) (SHR(ADD32(4096,MULT16_16((a),(b))),13))
+#define MULT16_16_P14(a,b) (SHR(ADD32(8192,MULT16_16((a),(b))),14))
+#define MULT16_16_P15(a,b) (SHR(ADD32(16384,MULT16_16((a),(b))),15))
+
+#define MUL_16_32_R15(a,bh,bl) ADD32(MULT16_16((a),(bh)), SHR(MULT16_16((a),(bl)),15))
+
+#define DIV32_16(a,b) ((spx_word16_t)(((spx_word32_t)(a))/((spx_word16_t)(b))))
+#define PDIV32_16(a,b) ((spx_word16_t)(((spx_word32_t)(a)+((spx_word16_t)(b)>>1))/((spx_word16_t)(b))))
+#define DIV32(a,b) (((spx_word32_t)(a))/((spx_word32_t)(b)))
+#define PDIV32(a,b) (((spx_word32_t)(a)+((spx_word16_t)(b)>>1))/((spx_word32_t)(b)))
+
+#endif
diff --git a/src/pulsecore/speex/resample.c b/src/pulsecore/speex/resample.c
new file mode 100644
index 00000000..1e592002
--- /dev/null
+++ b/src/pulsecore/speex/resample.c
@@ -0,0 +1,1121 @@
+/* Copyright (C) 2007 Jean-Marc Valin
+
+ File: resample.c
+ Arbitrary resampling code
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ 3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/*
+ The design goals of this code are:
+ - Very fast algorithm
+ - SIMD-friendly algorithm
+ - Low memory requirement
+ - Good *perceptual* quality (and not best SNR)
+
+ Warning: This resampler is relatively new. Although I think I got rid of
+ all the major bugs and I don't expect the API to change anymore, there
+ may be something I've missed. So use with caution.
+
+ This algorithm is based on this original resampling algorithm:
+ Smith, Julius O. Digital Audio Resampling Home Page
+ Center for Computer Research in Music and Acoustics (CCRMA),
+ Stanford University, 2007.
+ Web published at http://www-ccrma.stanford.edu/~jos/resample/.
+
+ There is one main difference, though. This resampler uses cubic
+ interpolation instead of linear interpolation in the above paper. This
+ makes the table much smaller and makes it possible to compute that table
+ on a per-stream basis. In turn, being able to tweak the table for each
+ stream makes it possible to both reduce complexity on simple ratios
+ (e.g. 2/3), and get rid of the rounding operations in the inner loop.
+ The latter both reduces CPU time and makes the algorithm more SIMD-friendly.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef OUTSIDE_SPEEX
+#include <stdlib.h>
+static void *speex_alloc (int size) {return calloc(size,1);}
+static void *speex_realloc (void *ptr, int size) {return realloc(ptr, size);}
+static void speex_free (void *ptr) {free(ptr);}
+#include "speex_resampler.h"
+#include "arch.h"
+#else /* OUTSIDE_SPEEX */
+
+#include "speex/speex_resampler.h"
+#include "arch.h"
+#include "os_support.h"
+#endif /* OUTSIDE_SPEEX */
+
+#include <math.h>
+
+#ifndef M_PI
+#define M_PI 3.14159263
+#endif
+
+#ifdef FIXED_POINT
+#define WORD2INT(x) ((x) < -32767 ? -32768 : ((x) > 32766 ? 32767 : (x)))
+#else
+#define WORD2INT(x) ((x) < -32767.5f ? -32768 : ((x) > 32766.5f ? 32767 : floor(.5+(x))))
+#endif
+
+/*#define float double*/
+#define FILTER_SIZE 64
+#define OVERSAMPLE 8
+
+#define IMAX(a,b) ((a) > (b) ? (a) : (b))
+#define IMIN(a,b) ((a) < (b) ? (a) : (b))
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+typedef int (*resampler_basic_func)(SpeexResamplerState *, spx_uint32_t , const spx_word16_t *, spx_uint32_t *, spx_word16_t *, spx_uint32_t *);
+
+struct SpeexResamplerState_ {
+ spx_uint32_t in_rate;
+ spx_uint32_t out_rate;
+ spx_uint32_t num_rate;
+ spx_uint32_t den_rate;
+
+ int quality;
+ spx_uint32_t nb_channels;
+ spx_uint32_t filt_len;
+ spx_uint32_t mem_alloc_size;
+ int int_advance;
+ int frac_advance;
+ float cutoff;
+ spx_uint32_t oversample;
+ int initialised;
+ int started;
+
+ /* These are per-channel */
+ spx_int32_t *last_sample;
+ spx_uint32_t *samp_frac_num;
+ spx_uint32_t *magic_samples;
+
+ spx_word16_t *mem;
+ spx_word16_t *sinc_table;
+ spx_uint32_t sinc_table_length;
+ resampler_basic_func resampler_ptr;
+
+ int in_stride;
+ int out_stride;
+} ;
+
+static double kaiser12_table[68] = {
+ 0.99859849, 1.00000000, 0.99859849, 0.99440475, 0.98745105, 0.97779076,
+ 0.96549770, 0.95066529, 0.93340547, 0.91384741, 0.89213598, 0.86843014,
+ 0.84290116, 0.81573067, 0.78710866, 0.75723148, 0.72629970, 0.69451601,
+ 0.66208321, 0.62920216, 0.59606986, 0.56287762, 0.52980938, 0.49704014,
+ 0.46473455, 0.43304576, 0.40211431, 0.37206735, 0.34301800, 0.31506490,
+ 0.28829195, 0.26276832, 0.23854851, 0.21567274, 0.19416736, 0.17404546,
+ 0.15530766, 0.13794294, 0.12192957, 0.10723616, 0.09382272, 0.08164178,
+ 0.07063950, 0.06075685, 0.05193064, 0.04409466, 0.03718069, 0.03111947,
+ 0.02584161, 0.02127838, 0.01736250, 0.01402878, 0.01121463, 0.00886058,
+ 0.00691064, 0.00531256, 0.00401805, 0.00298291, 0.00216702, 0.00153438,
+ 0.00105297, 0.00069463, 0.00043489, 0.00025272, 0.00013031, 0.0000527734,
+ 0.00001000, 0.00000000};
+/*
+static double kaiser12_table[36] = {
+ 0.99440475, 1.00000000, 0.99440475, 0.97779076, 0.95066529, 0.91384741,
+ 0.86843014, 0.81573067, 0.75723148, 0.69451601, 0.62920216, 0.56287762,
+ 0.49704014, 0.43304576, 0.37206735, 0.31506490, 0.26276832, 0.21567274,
+ 0.17404546, 0.13794294, 0.10723616, 0.08164178, 0.06075685, 0.04409466,
+ 0.03111947, 0.02127838, 0.01402878, 0.00886058, 0.00531256, 0.00298291,
+ 0.00153438, 0.00069463, 0.00025272, 0.0000527734, 0.00000500, 0.00000000};
+*/
+static double kaiser10_table[36] = {
+ 0.99537781, 1.00000000, 0.99537781, 0.98162644, 0.95908712, 0.92831446,
+ 0.89005583, 0.84522401, 0.79486424, 0.74011713, 0.68217934, 0.62226347,
+ 0.56155915, 0.50119680, 0.44221549, 0.38553619, 0.33194107, 0.28205962,
+ 0.23636152, 0.19515633, 0.15859932, 0.12670280, 0.09935205, 0.07632451,
+ 0.05731132, 0.04193980, 0.02979584, 0.02044510, 0.01345224, 0.00839739,
+ 0.00488951, 0.00257636, 0.00115101, 0.00035515, 0.00000000, 0.00000000};
+
+static double kaiser8_table[36] = {
+ 0.99635258, 1.00000000, 0.99635258, 0.98548012, 0.96759014, 0.94302200,
+ 0.91223751, 0.87580811, 0.83439927, 0.78875245, 0.73966538, 0.68797126,
+ 0.63451750, 0.58014482, 0.52566725, 0.47185369, 0.41941150, 0.36897272,
+ 0.32108304, 0.27619388, 0.23465776, 0.19672670, 0.16255380, 0.13219758,
+ 0.10562887, 0.08273982, 0.06335451, 0.04724088, 0.03412321, 0.02369490,
+ 0.01563093, 0.00959968, 0.00527363, 0.00233883, 0.00050000, 0.00000000};
+
+static double kaiser6_table[36] = {
+ 0.99733006, 1.00000000, 0.99733006, 0.98935595, 0.97618418, 0.95799003,
+ 0.93501423, 0.90755855, 0.87598009, 0.84068475, 0.80211977, 0.76076565,
+ 0.71712752, 0.67172623, 0.62508937, 0.57774224, 0.53019925, 0.48295561,
+ 0.43647969, 0.39120616, 0.34752997, 0.30580127, 0.26632152, 0.22934058,
+ 0.19505503, 0.16360756, 0.13508755, 0.10953262, 0.08693120, 0.06722600,
+ 0.05031820, 0.03607231, 0.02432151, 0.01487334, 0.00752000, 0.00000000};
+
+struct FuncDef {
+ double *table;
+ int oversample;
+};
+
+static struct FuncDef _KAISER12 = {kaiser12_table, 64};
+#define KAISER12 (&_KAISER12)
+/*static struct FuncDef _KAISER12 = {kaiser12_table, 32};
+#define KAISER12 (&_KAISER12)*/
+static struct FuncDef _KAISER10 = {kaiser10_table, 32};
+#define KAISER10 (&_KAISER10)
+static struct FuncDef _KAISER8 = {kaiser8_table, 32};
+#define KAISER8 (&_KAISER8)
+static struct FuncDef _KAISER6 = {kaiser6_table, 32};
+#define KAISER6 (&_KAISER6)
+
+struct QualityMapping {
+ int base_length;
+ int oversample;
+ float downsample_bandwidth;
+ float upsample_bandwidth;
+ struct FuncDef *window_func;
+};
+
+
+/* This table maps conversion quality to internal parameters. There are two
+ reasons that explain why the up-sampling bandwidth is larger than the
+ down-sampling bandwidth:
+ 1) When up-sampling, we can assume that the spectrum is already attenuated
+ close to the Nyquist rate (from an A/D or a previous resampling filter)
+ 2) Any aliasing that occurs very close to the Nyquist rate will be masked
+ by the sinusoids/noise just below the Nyquist rate (guaranteed only for
+ up-sampling).
+*/
+static const struct QualityMapping quality_map[11] = {
+ { 8, 4, 0.830f, 0.860f, KAISER6 }, /* Q0 */
+ { 16, 4, 0.850f, 0.880f, KAISER6 }, /* Q1 */
+ { 32, 4, 0.882f, 0.910f, KAISER6 }, /* Q2 */ /* 82.3% cutoff ( ~60 dB stop) 6 */
+ { 48, 8, 0.895f, 0.917f, KAISER8 }, /* Q3 */ /* 84.9% cutoff ( ~80 dB stop) 8 */
+ { 64, 8, 0.921f, 0.940f, KAISER8 }, /* Q4 */ /* 88.7% cutoff ( ~80 dB stop) 8 */
+ { 80, 16, 0.922f, 0.940f, KAISER10}, /* Q5 */ /* 89.1% cutoff (~100 dB stop) 10 */
+ { 96, 16, 0.940f, 0.945f, KAISER10}, /* Q6 */ /* 91.5% cutoff (~100 dB stop) 10 */
+ {128, 16, 0.950f, 0.950f, KAISER10}, /* Q7 */ /* 93.1% cutoff (~100 dB stop) 10 */
+ {160, 16, 0.960f, 0.960f, KAISER10}, /* Q8 */ /* 94.5% cutoff (~100 dB stop) 10 */
+ {192, 32, 0.968f, 0.968f, KAISER12}, /* Q9 */ /* 95.5% cutoff (~100 dB stop) 10 */
+ {256, 32, 0.975f, 0.975f, KAISER12}, /* Q10 */ /* 96.6% cutoff (~100 dB stop) 10 */
+};
+/*8,24,40,56,80,104,128,160,200,256,320*/
+static double compute_func(float x, struct FuncDef *func)
+{
+ float y, frac;
+ double interp[4];
+ int ind;
+ y = x*func->oversample;
+ ind = (int)floor(y);
+ frac = (y-ind);
+ /* CSE with handle the repeated powers */
+ interp[3] = -0.1666666667*frac + 0.1666666667*(frac*frac*frac);
+ interp[2] = frac + 0.5*(frac*frac) - 0.5*(frac*frac*frac);
+ /*interp[2] = 1.f - 0.5f*frac - frac*frac + 0.5f*frac*frac*frac;*/
+ interp[0] = -0.3333333333*frac + 0.5*(frac*frac) - 0.1666666667*(frac*frac*frac);
+ /* Just to make sure we don't have rounding problems */
+ interp[1] = 1.f-interp[3]-interp[2]-interp[0];
+
+ /*sum = frac*accum[1] + (1-frac)*accum[2];*/
+ return interp[0]*func->table[ind] + interp[1]*func->table[ind+1] + interp[2]*func->table[ind+2] + interp[3]*func->table[ind+3];
+}
+
+#if 0
+#include <stdio.h>
+int main(int argc, char **argv)
+{
+ int i;
+ for (i=0;i<256;i++)
+ {
+ printf ("%f\n", compute_func(i/256., KAISER12));
+ }
+ return 0;
+}
+#endif
+
+#ifdef FIXED_POINT
+/* The slow way of computing a sinc for the table. Should improve that some day */
+static spx_word16_t sinc(float cutoff, float x, int N, struct FuncDef *window_func)
+{
+ /*fprintf (stderr, "%f ", x);*/
+ float xx = x * cutoff;
+ if (fabs(x)<1e-6f)
+ return WORD2INT(32768.*cutoff);
+ else if (fabs(x) > .5f*N)
+ return 0;
+ /*FIXME: Can it really be any slower than this? */
+ return WORD2INT(32768.*cutoff*sin(M_PI*xx)/(M_PI*xx) * compute_func(fabs(2.*x/N), window_func));
+}
+#else
+/* The slow way of computing a sinc for the table. Should improve that some day */
+static spx_word16_t sinc(float cutoff, float x, int N, struct FuncDef *window_func)
+{
+ /*fprintf (stderr, "%f ", x);*/
+ float xx = x * cutoff;
+ if (fabs(x)<1e-6)
+ return cutoff;
+ else if (fabs(x) > .5*N)
+ return 0;
+ /*FIXME: Can it really be any slower than this? */
+ return cutoff*sin(M_PI*xx)/(M_PI*xx) * compute_func(fabs(2.*x/N), window_func);
+}
+#endif
+
+#ifdef FIXED_POINT
+static void cubic_coef(spx_word16_t x, spx_word16_t interp[4])
+{
+ /* Compute interpolation coefficients. I'm not sure whether this corresponds to cubic interpolation
+ but I know it's MMSE-optimal on a sinc */
+ spx_word16_t x2, x3;
+ x2 = MULT16_16_P15(x, x);
+ x3 = MULT16_16_P15(x, x2);
+ interp[0] = PSHR32(MULT16_16(QCONST16(-0.16667f, 15),x) + MULT16_16(QCONST16(0.16667f, 15),x3),15);
+ interp[1] = EXTRACT16(EXTEND32(x) + SHR32(SUB32(EXTEND32(x2),EXTEND32(x3)),1));
+ interp[3] = PSHR32(MULT16_16(QCONST16(-0.33333f, 15),x) + MULT16_16(QCONST16(.5f,15),x2) - MULT16_16(QCONST16(0.16667f, 15),x3),15);
+ /* Just to make sure we don't have rounding problems */
+ interp[2] = Q15_ONE-interp[0]-interp[1]-interp[3];
+ if (interp[2]<32767)
+ interp[2]+=1;
+}
+#else
+static void cubic_coef(spx_word16_t frac, spx_word16_t interp[4])
+{
+ /* Compute interpolation coefficients. I'm not sure whether this corresponds to cubic interpolation
+ but I know it's MMSE-optimal on a sinc */
+ interp[0] = -0.16667f*frac + 0.16667f*frac*frac*frac;
+ interp[1] = frac + 0.5f*frac*frac - 0.5f*frac*frac*frac;
+ /*interp[2] = 1.f - 0.5f*frac - frac*frac + 0.5f*frac*frac*frac;*/
+ interp[3] = -0.33333f*frac + 0.5f*frac*frac - 0.16667f*frac*frac*frac;
+ /* Just to make sure we don't have rounding problems */
+ interp[2] = 1.-interp[0]-interp[1]-interp[3];
+}
+#endif
+
+static int resampler_basic_direct_single(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len)
+{
+ int N = st->filt_len;
+ int out_sample = 0;
+ spx_word16_t *mem;
+ int last_sample = st->last_sample[channel_index];
+ spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index];
+ mem = st->mem + channel_index * st->mem_alloc_size;
+ while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len))
+ {
+ int j;
+ spx_word32_t sum=0;
+
+ /* We already have all the filter coefficients pre-computed in the table */
+ const spx_word16_t *ptr;
+ /* Do the memory part */
+ for (j=0;last_sample-N+1+j < 0;j++)
+ {
+ sum += MULT16_16(mem[last_sample+j],st->sinc_table[samp_frac_num*st->filt_len+j]);
+ }
+
+ /* Do the new part */
+ ptr = in+st->in_stride*(last_sample-N+1+j);
+ for (;j<N;j++)
+ {
+ sum += MULT16_16(*ptr,st->sinc_table[samp_frac_num*st->filt_len+j]);
+ ptr += st->in_stride;
+ }
+
+ *out = PSHR32(sum,15);
+ out += st->out_stride;
+ out_sample++;
+ last_sample += st->int_advance;
+ samp_frac_num += st->frac_advance;
+ if (samp_frac_num >= st->den_rate)
+ {
+ samp_frac_num -= st->den_rate;
+ last_sample++;
+ }
+ }
+ st->last_sample[channel_index] = last_sample;
+ st->samp_frac_num[channel_index] = samp_frac_num;
+ return out_sample;
+}
+
+#ifdef FIXED_POINT
+#else
+/* This is the same as the previous function, except with a double-precision accumulator */
+static int resampler_basic_direct_double(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len)
+{
+ int N = st->filt_len;
+ int out_sample = 0;
+ spx_word16_t *mem;
+ int last_sample = st->last_sample[channel_index];
+ spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index];
+ mem = st->mem + channel_index * st->mem_alloc_size;
+ while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len))
+ {
+ int j;
+ double sum=0;
+
+ /* We already have all the filter coefficients pre-computed in the table */
+ const spx_word16_t *ptr;
+ /* Do the memory part */
+ for (j=0;last_sample-N+1+j < 0;j++)
+ {
+ sum += MULT16_16(mem[last_sample+j],(double)st->sinc_table[samp_frac_num*st->filt_len+j]);
+ }
+
+ /* Do the new part */
+ ptr = in+st->in_stride*(last_sample-N+1+j);
+ for (;j<N;j++)
+ {
+ sum += MULT16_16(*ptr,(double)st->sinc_table[samp_frac_num*st->filt_len+j]);
+ ptr += st->in_stride;
+ }
+
+ *out = sum;
+ out += st->out_stride;
+ out_sample++;
+ last_sample += st->int_advance;
+ samp_frac_num += st->frac_advance;
+ if (samp_frac_num >= st->den_rate)
+ {
+ samp_frac_num -= st->den_rate;
+ last_sample++;
+ }
+ }
+ st->last_sample[channel_index] = last_sample;
+ st->samp_frac_num[channel_index] = samp_frac_num;
+ return out_sample;
+}
+#endif
+
+static int resampler_basic_interpolate_single(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len)
+{
+ int N = st->filt_len;
+ int out_sample = 0;
+ spx_word16_t *mem;
+ int last_sample = st->last_sample[channel_index];
+ spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index];
+ mem = st->mem + channel_index * st->mem_alloc_size;
+ while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len))
+ {
+ int j;
+ spx_word32_t sum=0;
+
+ /* We need to interpolate the sinc filter */
+ spx_word32_t accum[4] = {0.f,0.f, 0.f, 0.f};
+ spx_word16_t interp[4];
+ const spx_word16_t *ptr;
+ int offset;
+ spx_word16_t frac;
+ offset = samp_frac_num*st->oversample/st->den_rate;
+#ifdef FIXED_POINT
+ frac = PDIV32(SHL32((samp_frac_num*st->oversample) % st->den_rate,15),st->den_rate);
+#else
+ frac = ((float)((samp_frac_num*st->oversample) % st->den_rate))/st->den_rate;
+#endif
+ /* This code is written like this to make it easy to optimise with SIMD.
+ For most DSPs, it would be best to split the loops in two because most DSPs
+ have only two accumulators */
+ for (j=0;last_sample-N+1+j < 0;j++)
+ {
+ spx_word16_t curr_mem = mem[last_sample+j];
+ accum[0] += MULT16_16(curr_mem,st->sinc_table[4+(j+1)*st->oversample-offset-2]);
+ accum[1] += MULT16_16(curr_mem,st->sinc_table[4+(j+1)*st->oversample-offset-1]);
+ accum[2] += MULT16_16(curr_mem,st->sinc_table[4+(j+1)*st->oversample-offset]);
+ accum[3] += MULT16_16(curr_mem,st->sinc_table[4+(j+1)*st->oversample-offset+1]);
+ }
+ ptr = in+st->in_stride*(last_sample-N+1+j);
+ /* Do the new part */
+ for (;j<N;j++)
+ {
+ spx_word16_t curr_in = *ptr;
+ ptr += st->in_stride;
+ accum[0] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset-2]);
+ accum[1] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset-1]);
+ accum[2] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset]);
+ accum[3] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset+1]);
+ }
+ cubic_coef(frac, interp);
+ sum = MULT16_32_Q15(interp[0],accum[0]) + MULT16_32_Q15(interp[1],accum[1]) + MULT16_32_Q15(interp[2],accum[2]) + MULT16_32_Q15(interp[3],accum[3]);
+
+ *out = PSHR32(sum,15);
+ out += st->out_stride;
+ out_sample++;
+ last_sample += st->int_advance;
+ samp_frac_num += st->frac_advance;
+ if (samp_frac_num >= st->den_rate)
+ {
+ samp_frac_num -= st->den_rate;
+ last_sample++;
+ }
+ }
+ st->last_sample[channel_index] = last_sample;
+ st->samp_frac_num[channel_index] = samp_frac_num;
+ return out_sample;
+}
+
+#ifdef FIXED_POINT
+#else
+/* This is the same as the previous function, except with a double-precision accumulator */
+static int resampler_basic_interpolate_double(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len)
+{
+ int N = st->filt_len;
+ int out_sample = 0;
+ spx_word16_t *mem;
+ int last_sample = st->last_sample[channel_index];
+ spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index];
+ mem = st->mem + channel_index * st->mem_alloc_size;
+ while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len))
+ {
+ int j;
+ spx_word32_t sum=0;
+
+ /* We need to interpolate the sinc filter */
+ double accum[4] = {0.f,0.f, 0.f, 0.f};
+ float interp[4];
+ const spx_word16_t *ptr;
+ float alpha = ((float)samp_frac_num)/st->den_rate;
+ int offset = samp_frac_num*st->oversample/st->den_rate;
+ float frac = alpha*st->oversample - offset;
+ /* This code is written like this to make it easy to optimise with SIMD.
+ For most DSPs, it would be best to split the loops in two because most DSPs
+ have only two accumulators */
+ for (j=0;last_sample-N+1+j < 0;j++)
+ {
+ double curr_mem = mem[last_sample+j];
+ accum[0] += MULT16_16(curr_mem,st->sinc_table[4+(j+1)*st->oversample-offset-2]);
+ accum[1] += MULT16_16(curr_mem,st->sinc_table[4+(j+1)*st->oversample-offset-1]);
+ accum[2] += MULT16_16(curr_mem,st->sinc_table[4+(j+1)*st->oversample-offset]);
+ accum[3] += MULT16_16(curr_mem,st->sinc_table[4+(j+1)*st->oversample-offset+1]);
+ }
+ ptr = in+st->in_stride*(last_sample-N+1+j);
+ /* Do the new part */
+ for (;j<N;j++)
+ {
+ double curr_in = *ptr;
+ ptr += st->in_stride;
+ accum[0] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset-2]);
+ accum[1] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset-1]);
+ accum[2] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset]);
+ accum[3] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset+1]);
+ }
+ cubic_coef(frac, interp);
+ sum = interp[0]*accum[0] + interp[1]*accum[1] + interp[2]*accum[2] + interp[3]*accum[3];
+
+ *out = PSHR32(sum,15);
+ out += st->out_stride;
+ out_sample++;
+ last_sample += st->int_advance;
+ samp_frac_num += st->frac_advance;
+ if (samp_frac_num >= st->den_rate)
+ {
+ samp_frac_num -= st->den_rate;
+ last_sample++;
+ }
+ }
+ st->last_sample[channel_index] = last_sample;
+ st->samp_frac_num[channel_index] = samp_frac_num;
+ return out_sample;
+}
+#endif
+
+static void update_filter(SpeexResamplerState *st)
+{
+ spx_uint32_t old_length;
+
+ old_length = st->filt_len;
+ st->oversample = quality_map[st->quality].oversample;
+ st->filt_len = quality_map[st->quality].base_length;
+
+ if (st->num_rate > st->den_rate)
+ {
+ /* down-sampling */
+ st->cutoff = quality_map[st->quality].downsample_bandwidth * st->den_rate / st->num_rate;
+ /* FIXME: divide the numerator and denominator by a certain amount if they're too large */
+ st->filt_len = st->filt_len*st->num_rate / st->den_rate;
+ /* Round down to make sure we have a multiple of 4 */
+ st->filt_len &= (~0x3);
+ if (2*st->den_rate < st->num_rate)
+ st->oversample >>= 1;
+ if (4*st->den_rate < st->num_rate)
+ st->oversample >>= 1;
+ if (8*st->den_rate < st->num_rate)
+ st->oversample >>= 1;
+ if (16*st->den_rate < st->num_rate)
+ st->oversample >>= 1;
+ if (st->oversample < 1)
+ st->oversample = 1;
+ } else {
+ /* up-sampling */
+ st->cutoff = quality_map[st->quality].upsample_bandwidth;
+ }
+
+ /* Choose the resampling type that requires the least amount of memory */
+ if (st->den_rate <= st->oversample)
+ {
+ spx_uint32_t i;
+ if (!st->sinc_table)
+ st->sinc_table = (spx_word16_t *)speex_alloc(st->filt_len*st->den_rate*sizeof(spx_word16_t));
+ else if (st->sinc_table_length < st->filt_len*st->den_rate)
+ {
+ st->sinc_table = (spx_word16_t *)speex_realloc(st->sinc_table,st->filt_len*st->den_rate*sizeof(spx_word16_t));
+ st->sinc_table_length = st->filt_len*st->den_rate;
+ }
+ for (i=0;i<st->den_rate;i++)
+ {
+ spx_int32_t j;
+ for (j=0;j<st->filt_len;j++)
+ {
+ st->sinc_table[i*st->filt_len+j] = sinc(st->cutoff,((j-(spx_int32_t)st->filt_len/2+1)-((float)i)/st->den_rate), st->filt_len, quality_map[st->quality].window_func);
+ }
+ }
+#ifdef FIXED_POINT
+ st->resampler_ptr = resampler_basic_direct_single;
+#else
+ if (st->quality>8)
+ st->resampler_ptr = resampler_basic_direct_double;
+ else
+ st->resampler_ptr = resampler_basic_direct_single;
+#endif
+ /*fprintf (stderr, "resampler uses direct sinc table and normalised cutoff %f\n", cutoff);*/
+ } else {
+ spx_int32_t i;
+ if (!st->sinc_table)
+ st->sinc_table = (spx_word16_t *)speex_alloc((st->filt_len*st->oversample+8)*sizeof(spx_word16_t));
+ else if (st->sinc_table_length < st->filt_len*st->oversample+8)
+ {
+ st->sinc_table = (spx_word16_t *)speex_realloc(st->sinc_table,(st->filt_len*st->oversample+8)*sizeof(spx_word16_t));
+ st->sinc_table_length = st->filt_len*st->oversample+8;
+ }
+ for (i=-4;i<(spx_int32_t)(st->oversample*st->filt_len+4);i++)
+ st->sinc_table[i+4] = sinc(st->cutoff,(i/(float)st->oversample - st->filt_len/2), st->filt_len, quality_map[st->quality].window_func);
+#ifdef FIXED_POINT
+ st->resampler_ptr = resampler_basic_interpolate_single;
+#else
+ if (st->quality>8)
+ st->resampler_ptr = resampler_basic_interpolate_double;
+ else
+ st->resampler_ptr = resampler_basic_interpolate_single;
+#endif
+ /*fprintf (stderr, "resampler uses interpolated sinc table and normalised cutoff %f\n", cutoff);*/
+ }
+ st->int_advance = st->num_rate/st->den_rate;
+ st->frac_advance = st->num_rate%st->den_rate;
+
+
+ /* Here's the place where we update the filter memory to take into account
+ the change in filter length. It's probably the messiest part of the code
+ due to handling of lots of corner cases. */
+ if (!st->mem)
+ {
+ spx_uint32_t i;
+ st->mem = (spx_word16_t*)speex_alloc(st->nb_channels*(st->filt_len-1) * sizeof(spx_word16_t));
+ for (i=0;i<st->nb_channels*(st->filt_len-1);i++)
+ st->mem[i] = 0;
+ st->mem_alloc_size = st->filt_len-1;
+ /*speex_warning("init filter");*/
+ } else if (!st->started)
+ {
+ spx_uint32_t i;
+ st->mem = (spx_word16_t*)speex_realloc(st->mem, st->nb_channels*(st->filt_len-1) * sizeof(spx_word16_t));
+ for (i=0;i<st->nb_channels*(st->filt_len-1);i++)
+ st->mem[i] = 0;
+ st->mem_alloc_size = st->filt_len-1;
+ /*speex_warning("reinit filter");*/
+ } else if (st->filt_len > old_length)
+ {
+ spx_int32_t i;
+ /* Increase the filter length */
+ /*speex_warning("increase filter size");*/
+ int old_alloc_size = st->mem_alloc_size;
+ if (st->filt_len-1 > st->mem_alloc_size)
+ {
+ st->mem = (spx_word16_t*)speex_realloc(st->mem, st->nb_channels*(st->filt_len-1) * sizeof(spx_word16_t));
+ st->mem_alloc_size = st->filt_len-1;
+ }
+ for (i=st->nb_channels-1;i>=0;i--)
+ {
+ spx_int32_t j;
+ spx_uint32_t olen = old_length;
+ /*if (st->magic_samples[i])*/
+ {
+ /* Try and remove the magic samples as if nothing had happened */
+
+ /* FIXME: This is wrong but for now we need it to avoid going over the array bounds */
+ olen = old_length + 2*st->magic_samples[i];
+ for (j=old_length-2+st->magic_samples[i];j>=0;j--)
+ st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]] = st->mem[i*old_alloc_size+j];
+ for (j=0;j<st->magic_samples[i];j++)
+ st->mem[i*st->mem_alloc_size+j] = 0;
+ st->magic_samples[i] = 0;
+ }
+ if (st->filt_len > olen)
+ {
+ /* If the new filter length is still bigger than the "augmented" length */
+ /* Copy data going backward */
+ for (j=0;j<olen-1;j++)
+ st->mem[i*st->mem_alloc_size+(st->filt_len-2-j)] = st->mem[i*st->mem_alloc_size+(olen-2-j)];
+ /* Then put zeros for lack of anything better */
+ for (;j<st->filt_len-1;j++)
+ st->mem[i*st->mem_alloc_size+(st->filt_len-2-j)] = 0;
+ /* Adjust last_sample */
+ st->last_sample[i] += (st->filt_len - olen)/2;
+ } else {
+ /* Put back some of the magic! */
+ st->magic_samples[i] = (olen - st->filt_len)/2;
+ for (j=0;j<st->filt_len-1+st->magic_samples[i];j++)
+ st->mem[i*st->mem_alloc_size+j] = st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]];
+ }
+ }
+ } else if (st->filt_len < old_length)
+ {
+ spx_uint32_t i;
+ /* Reduce filter length, this a bit tricky. We need to store some of the memory as "magic"
+ samples so they can be used directly as input the next time(s) */
+ for (i=0;i<st->nb_channels;i++)
+ {
+ spx_uint32_t j;
+ spx_uint32_t old_magic = st->magic_samples[i];
+ st->magic_samples[i] = (old_length - st->filt_len)/2;
+ /* We must copy some of the memory that's no longer used */
+ /* Copy data going backward */
+ for (j=0;j<st->filt_len-1+st->magic_samples[i]+old_magic;j++)
+ st->mem[i*st->mem_alloc_size+j] = st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]];
+ st->magic_samples[i] += old_magic;
+ }
+ }
+
+}
+
+SpeexResamplerState *speex_resampler_init(spx_uint32_t nb_channels, spx_uint32_t in_rate, spx_uint32_t out_rate, int quality, int *err)
+{
+ return speex_resampler_init_frac(nb_channels, in_rate, out_rate, in_rate, out_rate, quality, err);
+}
+
+SpeexResamplerState *speex_resampler_init_frac(spx_uint32_t nb_channels, spx_uint32_t ratio_num, spx_uint32_t ratio_den, spx_uint32_t in_rate, spx_uint32_t out_rate, int quality, int *err)
+{
+ spx_uint32_t i;
+ SpeexResamplerState *st;
+ if (quality > 10 || quality < 0)
+ {
+ if (err)
+ *err = RESAMPLER_ERR_INVALID_ARG;
+ return NULL;
+ }
+ st = (SpeexResamplerState *)speex_alloc(sizeof(SpeexResamplerState));
+ st->initialised = 0;
+ st->started = 0;
+ st->in_rate = 0;
+ st->out_rate = 0;
+ st->num_rate = 0;
+ st->den_rate = 0;
+ st->quality = -1;
+ st->sinc_table_length = 0;
+ st->mem_alloc_size = 0;
+ st->filt_len = 0;
+ st->mem = 0;
+ st->resampler_ptr = 0;
+
+ st->cutoff = 1.f;
+ st->nb_channels = nb_channels;
+ st->in_stride = 1;
+ st->out_stride = 1;
+
+ /* Per channel data */
+ st->last_sample = (spx_int32_t*)speex_alloc(nb_channels*sizeof(int));
+ st->magic_samples = (spx_uint32_t*)speex_alloc(nb_channels*sizeof(int));
+ st->samp_frac_num = (spx_uint32_t*)speex_alloc(nb_channels*sizeof(int));
+ for (i=0;i<nb_channels;i++)
+ {
+ st->last_sample[i] = 0;
+ st->magic_samples[i] = 0;
+ st->samp_frac_num[i] = 0;
+ }
+
+ speex_resampler_set_quality(st, quality);
+ speex_resampler_set_rate_frac(st, ratio_num, ratio_den, in_rate, out_rate);
+
+
+ update_filter(st);
+
+ st->initialised = 1;
+ if (err)
+ *err = RESAMPLER_ERR_SUCCESS;
+
+ return st;
+}
+
+void speex_resampler_destroy(SpeexResamplerState *st)
+{
+ speex_free(st->mem);
+ speex_free(st->sinc_table);
+ speex_free(st->last_sample);
+ speex_free(st->magic_samples);
+ speex_free(st->samp_frac_num);
+ speex_free(st);
+}
+
+
+
+static int speex_resampler_process_native(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len)
+{
+ int j=0;
+ int N = st->filt_len;
+ int out_sample = 0;
+ spx_word16_t *mem;
+ spx_uint32_t tmp_out_len = 0;
+ mem = st->mem + channel_index * st->mem_alloc_size;
+ st->started = 1;
+
+ /* Handle the case where we have samples left from a reduction in filter length */
+ if (st->magic_samples[channel_index])
+ {
+ int istride_save;
+ spx_uint32_t tmp_in_len;
+ spx_uint32_t tmp_magic;
+
+ istride_save = st->in_stride;
+ tmp_in_len = st->magic_samples[channel_index];
+ tmp_out_len = *out_len;
+ /* magic_samples needs to be set to zero to avoid infinite recursion */
+ tmp_magic = st->magic_samples[channel_index];
+ st->magic_samples[channel_index] = 0;
+ st->in_stride = 1;
+ speex_resampler_process_native(st, channel_index, mem+N-1, &tmp_in_len, out, &tmp_out_len);
+ st->in_stride = istride_save;
+ /*speex_warning_int("extra samples:", tmp_out_len);*/
+ /* If we couldn't process all "magic" input samples, save the rest for next time */
+ if (tmp_in_len < tmp_magic)
+ {
+ spx_uint32_t i;
+ st->magic_samples[channel_index] = tmp_magic-tmp_in_len;
+ for (i=0;i<st->magic_samples[channel_index];i++)
+ mem[N-1+i]=mem[N-1+i+tmp_in_len];
+ }
+ out += tmp_out_len*st->out_stride;
+ *out_len -= tmp_out_len;
+ }
+
+ /* Call the right resampler through the function ptr */
+ out_sample = st->resampler_ptr(st, channel_index, in, in_len, out, out_len);
+
+ if (st->last_sample[channel_index] < (spx_int32_t)*in_len)
+ *in_len = st->last_sample[channel_index];
+ *out_len = out_sample+tmp_out_len;
+ st->last_sample[channel_index] -= *in_len;
+
+ for (j=0;j<N-1-(spx_int32_t)*in_len;j++)
+ mem[j] = mem[j+*in_len];
+ for (;j<N-1;j++)
+ mem[j] = in[st->in_stride*(j+*in_len-N+1)];
+
+ return RESAMPLER_ERR_SUCCESS;
+}
+
+#define FIXED_STACK_ALLOC 1024
+
+#ifdef FIXED_POINT
+int speex_resampler_process_float(SpeexResamplerState *st, spx_uint32_t channel_index, const float *in, spx_uint32_t *in_len, float *out, spx_uint32_t *out_len)
+{
+ spx_uint32_t i;
+ int istride_save, ostride_save;
+#ifdef VAR_ARRAYS
+ spx_word16_t x[*in_len];
+ spx_word16_t y[*out_len];
+ /*VARDECL(spx_word16_t *x);
+ VARDECL(spx_word16_t *y);
+ ALLOC(x, *in_len, spx_word16_t);
+ ALLOC(y, *out_len, spx_word16_t);*/
+ istride_save = st->in_stride;
+ ostride_save = st->out_stride;
+ for (i=0;i<*in_len;i++)
+ x[i] = WORD2INT(in[i*st->in_stride]);
+ st->in_stride = st->out_stride = 1;
+ speex_resampler_process_native(st, channel_index, x, in_len, y, out_len);
+ st->in_stride = istride_save;
+ st->out_stride = ostride_save;
+ for (i=0;i<*out_len;i++)
+ out[i*st->out_stride] = y[i];
+#else
+ spx_word16_t x[FIXED_STACK_ALLOC];
+ spx_word16_t y[FIXED_STACK_ALLOC];
+ spx_uint32_t ilen=*in_len, olen=*out_len;
+ istride_save = st->in_stride;
+ ostride_save = st->out_stride;
+ while (ilen && olen)
+ {
+ spx_uint32_t ichunk, ochunk;
+ ichunk = ilen;
+ ochunk = olen;
+ if (ichunk>FIXED_STACK_ALLOC)
+ ichunk=FIXED_STACK_ALLOC;
+ if (ochunk>FIXED_STACK_ALLOC)
+ ochunk=FIXED_STACK_ALLOC;
+ for (i=0;i<ichunk;i++)
+ x[i] = WORD2INT(in[i*st->in_stride]);
+ st->in_stride = st->out_stride = 1;
+ speex_resampler_process_native(st, channel_index, x, &ichunk, y, &ochunk);
+ st->in_stride = istride_save;
+ st->out_stride = ostride_save;
+ for (i=0;i<ochunk;i++)
+ out[i*st->out_stride] = y[i];
+ out += ochunk;
+ in += ichunk;
+ ilen -= ichunk;
+ olen -= ochunk;
+ }
+ *in_len -= ilen;
+ *out_len -= olen;
+#endif
+ return RESAMPLER_ERR_SUCCESS;
+}
+int speex_resampler_process_int(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_int16_t *in, spx_uint32_t *in_len, spx_int16_t *out, spx_uint32_t *out_len)
+{
+ return speex_resampler_process_native(st, channel_index, in, in_len, out, out_len);
+}
+#else
+int speex_resampler_process_float(SpeexResamplerState *st, spx_uint32_t channel_index, const float *in, spx_uint32_t *in_len, float *out, spx_uint32_t *out_len)
+{
+ return speex_resampler_process_native(st, channel_index, in, in_len, out, out_len);
+}
+int speex_resampler_process_int(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_int16_t *in, spx_uint32_t *in_len, spx_int16_t *out, spx_uint32_t *out_len)
+{
+ spx_uint32_t i;
+ int istride_save, ostride_save;
+#ifdef VAR_ARRAYS
+ spx_word16_t x[*in_len];
+ spx_word16_t y[*out_len];
+ /*VARDECL(spx_word16_t *x);
+ VARDECL(spx_word16_t *y);
+ ALLOC(x, *in_len, spx_word16_t);
+ ALLOC(y, *out_len, spx_word16_t);*/
+ istride_save = st->in_stride;
+ ostride_save = st->out_stride;
+ for (i=0;i<*in_len;i++)
+ x[i] = in[i*st->in_stride];
+ st->in_stride = st->out_stride = 1;
+ speex_resampler_process_native(st, channel_index, x, in_len, y, out_len);
+ st->in_stride = istride_save;
+ st->out_stride = ostride_save;
+ for (i=0;i<*out_len;i++)
+ out[i*st->out_stride] = WORD2INT(y[i]);
+#else
+ spx_word16_t x[FIXED_STACK_ALLOC];
+ spx_word16_t y[FIXED_STACK_ALLOC];
+ spx_uint32_t ilen=*in_len, olen=*out_len;
+ istride_save = st->in_stride;
+ ostride_save = st->out_stride;
+ while (ilen && olen)
+ {
+ spx_uint32_t ichunk, ochunk;
+ ichunk = ilen;
+ ochunk = olen;
+ if (ichunk>FIXED_STACK_ALLOC)
+ ichunk=FIXED_STACK_ALLOC;
+ if (ochunk>FIXED_STACK_ALLOC)
+ ochunk=FIXED_STACK_ALLOC;
+ for (i=0;i<ichunk;i++)
+ x[i] = in[i*st->in_stride];
+ st->in_stride = st->out_stride = 1;
+ speex_resampler_process_native(st, channel_index, x, &ichunk, y, &ochunk);
+ st->in_stride = istride_save;
+ st->out_stride = ostride_save;
+ for (i=0;i<ochunk;i++)
+ out[i*st->out_stride] = WORD2INT(y[i]);
+ out += ochunk;
+ in += ichunk;
+ ilen -= ichunk;
+ olen -= ochunk;
+ }
+ *in_len -= ilen;
+ *out_len -= olen;
+#endif
+ return RESAMPLER_ERR_SUCCESS;
+}
+#endif
+
+int speex_resampler_process_interleaved_float(SpeexResamplerState *st, const float *in, spx_uint32_t *in_len, float *out, spx_uint32_t *out_len)
+{
+ spx_uint32_t i;
+ int istride_save, ostride_save;
+ spx_uint32_t bak_len = *out_len;
+ istride_save = st->in_stride;
+ ostride_save = st->out_stride;
+ st->in_stride = st->out_stride = st->nb_channels;
+ for (i=0;i<st->nb_channels;i++)
+ {
+ *out_len = bak_len;
+ speex_resampler_process_float(st, i, in+i, in_len, out+i, out_len);
+ }
+ st->in_stride = istride_save;
+ st->out_stride = ostride_save;
+ return RESAMPLER_ERR_SUCCESS;
+}
+
+
+int speex_resampler_process_interleaved_int(SpeexResamplerState *st, const spx_int16_t *in, spx_uint32_t *in_len, spx_int16_t *out, spx_uint32_t *out_len)
+{
+ spx_uint32_t i;
+ int istride_save, ostride_save;
+ spx_uint32_t bak_len = *out_len;
+ istride_save = st->in_stride;
+ ostride_save = st->out_stride;
+ st->in_stride = st->out_stride = st->nb_channels;
+ for (i=0;i<st->nb_channels;i++)
+ {
+ *out_len = bak_len;
+ speex_resampler_process_int(st, i, in+i, in_len, out+i, out_len);
+ }
+ st->in_stride = istride_save;
+ st->out_stride = ostride_save;
+ return RESAMPLER_ERR_SUCCESS;
+}
+
+int speex_resampler_set_rate(SpeexResamplerState *st, spx_uint32_t in_rate, spx_uint32_t out_rate)
+{
+ return speex_resampler_set_rate_frac(st, in_rate, out_rate, in_rate, out_rate);
+}
+
+void speex_resampler_get_rate(SpeexResamplerState *st, spx_uint32_t *in_rate, spx_uint32_t *out_rate)
+{
+ *in_rate = st->in_rate;
+ *out_rate = st->out_rate;
+}
+
+int speex_resampler_set_rate_frac(SpeexResamplerState *st, spx_uint32_t ratio_num, spx_uint32_t ratio_den, spx_uint32_t in_rate, spx_uint32_t out_rate)
+{
+ spx_uint32_t fact;
+ spx_uint32_t old_den;
+ spx_uint32_t i;
+ if (st->in_rate == in_rate && st->out_rate == out_rate && st->num_rate == ratio_num && st->den_rate == ratio_den)
+ return RESAMPLER_ERR_SUCCESS;
+
+ old_den = st->den_rate;
+ st->in_rate = in_rate;
+ st->out_rate = out_rate;
+ st->num_rate = ratio_num;
+ st->den_rate = ratio_den;
+ /* FIXME: This is terribly inefficient, but who cares (at least for now)? */
+ for (fact=2;fact<=IMIN(st->num_rate, st->den_rate);fact++)
+ {
+ while ((st->num_rate % fact == 0) && (st->den_rate % fact == 0))
+ {
+ st->num_rate /= fact;
+ st->den_rate /= fact;
+ }
+ }
+
+ if (old_den > 0)
+ {
+ for (i=0;i<st->nb_channels;i++)
+ {
+ st->samp_frac_num[i]=st->samp_frac_num[i]*st->den_rate/old_den;
+ /* Safety net */
+ if (st->samp_frac_num[i] >= st->den_rate)
+ st->samp_frac_num[i] = st->den_rate-1;
+ }
+ }
+
+ if (st->initialised)
+ update_filter(st);
+ return RESAMPLER_ERR_SUCCESS;
+}
+
+void speex_resampler_get_ratio(SpeexResamplerState *st, spx_uint32_t *ratio_num, spx_uint32_t *ratio_den)
+{
+ *ratio_num = st->num_rate;
+ *ratio_den = st->den_rate;
+}
+
+int speex_resampler_set_quality(SpeexResamplerState *st, int quality)
+{
+ if (quality > 10 || quality < 0)
+ return RESAMPLER_ERR_INVALID_ARG;
+ if (st->quality == quality)
+ return RESAMPLER_ERR_SUCCESS;
+ st->quality = quality;
+ if (st->initialised)
+ update_filter(st);
+ return RESAMPLER_ERR_SUCCESS;
+}
+
+void speex_resampler_get_quality(SpeexResamplerState *st, int *quality)
+{
+ *quality = st->quality;
+}
+
+void speex_resampler_set_input_stride(SpeexResamplerState *st, spx_uint32_t stride)
+{
+ st->in_stride = stride;
+}
+
+void speex_resampler_get_input_stride(SpeexResamplerState *st, spx_uint32_t *stride)
+{
+ *stride = st->in_stride;
+}
+
+void speex_resampler_set_output_stride(SpeexResamplerState *st, spx_uint32_t stride)
+{
+ st->out_stride = stride;
+}
+
+void speex_resampler_get_output_stride(SpeexResamplerState *st, spx_uint32_t *stride)
+{
+ *stride = st->out_stride;
+}
+
+int speex_resampler_skip_zeros(SpeexResamplerState *st)
+{
+ spx_uint32_t i;
+ for (i=0;i<st->nb_channels;i++)
+ st->last_sample[i] = st->filt_len/2;
+ return RESAMPLER_ERR_SUCCESS;
+}
+
+int speex_resampler_reset_mem(SpeexResamplerState *st)
+{
+ spx_uint32_t i;
+ for (i=0;i<st->nb_channels*(st->filt_len-1);i++)
+ st->mem[i] = 0;
+ return RESAMPLER_ERR_SUCCESS;
+}
+
+const char *speex_resampler_strerror(int err)
+{
+ switch (err)
+ {
+ case RESAMPLER_ERR_SUCCESS:
+ return "Success.";
+ case RESAMPLER_ERR_ALLOC_FAILED:
+ return "Memory allocation failed.";
+ case RESAMPLER_ERR_BAD_STATE:
+ return "Bad resampler state.";
+ case RESAMPLER_ERR_INVALID_ARG:
+ return "Invalid argument.";
+ case RESAMPLER_ERR_PTR_OVERLAP:
+ return "Input and output buffers overlap.";
+ default:
+ return "Unknown error. Bad error code or strange version mismatch.";
+ }
+}
diff --git a/src/pulsecore/speex/speex_resampler.h b/src/pulsecore/speex/speex_resampler.h
new file mode 100644
index 00000000..8629eeb3
--- /dev/null
+++ b/src/pulsecore/speex/speex_resampler.h
@@ -0,0 +1,328 @@
+/* Copyright (C) 2007 Jean-Marc Valin
+
+ File: speex_resampler.h
+ Resampling code
+
+ The design goals of this code are:
+ - Very fast algorithm
+ - Low memory requirement
+ - Good *perceptual* quality (and not best SNR)
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ 3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef SPEEX_RESAMPLER_H
+#define SPEEX_RESAMPLER_H
+
+#ifdef OUTSIDE_SPEEX
+
+/********* WARNING: MENTAL SANITY ENDS HERE *************/
+
+/* If the resampler is defined outside of Speex, we change the symbol names so that
+ there won't be any clash if linking with Speex later on. */
+
+/* #define RANDOM_PREFIX your software name here */
+#ifndef RANDOM_PREFIX
+#error "Please define RANDOM_PREFIX (above) to something specific to your project to prevent symbol name clashes"
+#endif
+
+#define CAT_PREFIX2(a,b) a ## b
+#define CAT_PREFIX(a,b) CAT_PREFIX2(a, b)
+
+#define speex_resampler_init CAT_PREFIX(RANDOM_PREFIX,_resampler_init)
+#define speex_resampler_init_frac CAT_PREFIX(RANDOM_PREFIX,_resampler_init_frac)
+#define speex_resampler_destroy CAT_PREFIX(RANDOM_PREFIX,_resampler_destroy)
+#define speex_resampler_process_float CAT_PREFIX(RANDOM_PREFIX,_resampler_process_float)
+#define speex_resampler_process_int CAT_PREFIX(RANDOM_PREFIX,_resampler_process_int)
+#define speex_resampler_process_interleaved_float CAT_PREFIX(RANDOM_PREFIX,_resampler_process_interleaved_float)
+#define speex_resampler_process_interleaved_int CAT_PREFIX(RANDOM_PREFIX,_resampler_process_interleaved_int)
+#define speex_resampler_set_rate CAT_PREFIX(RANDOM_PREFIX,_resampler_set_rate)
+#define speex_resampler_get_rate CAT_PREFIX(RANDOM_PREFIX,_resampler_get_rate)
+#define speex_resampler_set_rate_frac CAT_PREFIX(RANDOM_PREFIX,_resampler_set_rate_frac)
+#define speex_resampler_get_ratio CAT_PREFIX(RANDOM_PREFIX,_resampler_get_ratio)
+#define speex_resampler_set_quality CAT_PREFIX(RANDOM_PREFIX,_resampler_set_quality)
+#define speex_resampler_get_quality CAT_PREFIX(RANDOM_PREFIX,_resampler_get_quality)
+#define speex_resampler_set_input_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_set_input_stride)
+#define speex_resampler_get_input_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_get_input_stride)
+#define speex_resampler_set_output_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_set_output_stride)
+#define speex_resampler_get_output_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_get_output_stride)
+#define speex_resampler_skip_zeros CAT_PREFIX(RANDOM_PREFIX,_resampler_skip_zeros)
+#define speex_resampler_reset_mem CAT_PREFIX(RANDOM_PREFIX,_resampler_reset_mem)
+#define speex_resampler_strerror CAT_PREFIX(RANDOM_PREFIX,_resampler_strerror)
+
+#define spx_int16_t short
+#define spx_int32_t int
+#define spx_uint16_t unsigned short
+#define spx_uint32_t unsigned int
+
+#else /* OUTSIDE_SPEEX */
+
+#include "speex/speex_types.h"
+
+#endif /* OUTSIDE_SPEEX */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SPEEX_RESAMPLER_QUALITY_MAX 10
+#define SPEEX_RESAMPLER_QUALITY_MIN 0
+#define SPEEX_RESAMPLER_QUALITY_DEFAULT 4
+#define SPEEX_RESAMPLER_QUALITY_VOIP 3
+#define SPEEX_RESAMPLER_QUALITY_DESKTOP 5
+
+enum {
+ RESAMPLER_ERR_SUCCESS = 0,
+ RESAMPLER_ERR_ALLOC_FAILED = 1,
+ RESAMPLER_ERR_BAD_STATE = 2,
+ RESAMPLER_ERR_INVALID_ARG = 3,
+ RESAMPLER_ERR_PTR_OVERLAP = 4,
+
+ RESAMPLER_ERR_MAX_ERROR
+};
+
+struct SpeexResamplerState_;
+typedef struct SpeexResamplerState_ SpeexResamplerState;
+
+/** Create a new resampler with integer input and output rates.
+ * @param nb_channels Number of channels to be processed
+ * @param in_rate Input sampling rate (integer number of Hz).
+ * @param out_rate Output sampling rate (integer number of Hz).
+ * @param quality Resampling quality between 0 and 10, where 0 has poor quality
+ * and 10 has very high quality.
+ * @return Newly created resampler state
+ * @retval NULL Error: not enough memory
+ */
+SpeexResamplerState *speex_resampler_init(spx_uint32_t nb_channels,
+ spx_uint32_t in_rate,
+ spx_uint32_t out_rate,
+ int quality,
+ int *err);
+
+/** Create a new resampler with fractional input/output rates. The sampling
+ * rate ratio is an arbitrary rational number with both the numerator and
+ * denominator being 32-bit integers.
+ * @param nb_channels Number of channels to be processed
+ * @param ratio_num Numerator of the sampling rate ratio
+ * @param ratio_den Denominator of the sampling rate ratio
+ * @param in_rate Input sampling rate rounded to the nearest integer (in Hz).
+ * @param out_rate Output sampling rate rounded to the nearest integer (in Hz).
+ * @param quality Resampling quality between 0 and 10, where 0 has poor quality
+ * and 10 has very high quality.
+ * @return Newly created resampler state
+ * @retval NULL Error: not enough memory
+ */
+SpeexResamplerState *speex_resampler_init_frac(spx_uint32_t nb_channels,
+ spx_uint32_t ratio_num,
+ spx_uint32_t ratio_den,
+ spx_uint32_t in_rate,
+ spx_uint32_t out_rate,
+ int quality,
+ int *err);
+
+/** Destroy a resampler state.
+ * @param st Resampler state
+ */
+void speex_resampler_destroy(SpeexResamplerState *st);
+
+/** Resample a float array. The input and output buffers must *not* overlap.
+ * @param st Resampler state
+ * @param channel_index Index of the channel to process for the multi-channel
+ * base (0 otherwise)
+ * @param in Input buffer
+ * @param in_len Number of input samples in the input buffer. Returns the
+ * number of samples processed
+ * @param out Output buffer
+ * @param out_len Size of the output buffer. Returns the number of samples written
+ */
+int speex_resampler_process_float(SpeexResamplerState *st,
+ spx_uint32_t channel_index,
+ const float *in,
+ spx_uint32_t *in_len,
+ float *out,
+ spx_uint32_t *out_len);
+
+/** Resample an int array. The input and output buffers must *not* overlap.
+ * @param st Resampler state
+ * @param channel_index Index of the channel to process for the multi-channel
+ * base (0 otherwise)
+ * @param in Input buffer
+ * @param in_len Number of input samples in the input buffer. Returns the number
+ * of samples processed
+ * @param out Output buffer
+ * @param out_len Size of the output buffer. Returns the number of samples written
+ */
+int speex_resampler_process_int(SpeexResamplerState *st,
+ spx_uint32_t channel_index,
+ const spx_int16_t *in,
+ spx_uint32_t *in_len,
+ spx_int16_t *out,
+ spx_uint32_t *out_len);
+
+/** Resample an interleaved float array. The input and output buffers must *not* overlap.
+ * @param st Resampler state
+ * @param in Input buffer
+ * @param in_len Number of input samples in the input buffer. Returns the number
+ * of samples processed. This is all per-channel.
+ * @param out Output buffer
+ * @param out_len Size of the output buffer. Returns the number of samples written.
+ * This is all per-channel.
+ */
+int speex_resampler_process_interleaved_float(SpeexResamplerState *st,
+ const float *in,
+ spx_uint32_t *in_len,
+ float *out,
+ spx_uint32_t *out_len);
+
+/** Resample an interleaved int array. The input and output buffers must *not* overlap.
+ * @param st Resampler state
+ * @param in Input buffer
+ * @param in_len Number of input samples in the input buffer. Returns the number
+ * of samples processed. This is all per-channel.
+ * @param out Output buffer
+ * @param out_len Size of the output buffer. Returns the number of samples written.
+ * This is all per-channel.
+ */
+int speex_resampler_process_interleaved_int(SpeexResamplerState *st,
+ const spx_int16_t *in,
+ spx_uint32_t *in_len,
+ spx_int16_t *out,
+ spx_uint32_t *out_len);
+
+/** Set (change) the input/output sampling rates (integer value).
+ * @param st Resampler state
+ * @param in_rate Input sampling rate (integer number of Hz).
+ * @param out_rate Output sampling rate (integer number of Hz).
+ */
+int speex_resampler_set_rate(SpeexResamplerState *st,
+ spx_uint32_t in_rate,
+ spx_uint32_t out_rate);
+
+/** Get the current input/output sampling rates (integer value).
+ * @param st Resampler state
+ * @param in_rate Input sampling rate (integer number of Hz) copied.
+ * @param out_rate Output sampling rate (integer number of Hz) copied.
+ */
+void speex_resampler_get_rate(SpeexResamplerState *st,
+ spx_uint32_t *in_rate,
+ spx_uint32_t *out_rate);
+
+/** Set (change) the input/output sampling rates and resampling ratio
+ * (fractional values in Hz supported).
+ * @param st Resampler state
+ * @param ratio_num Numerator of the sampling rate ratio
+ * @param ratio_den Denominator of the sampling rate ratio
+ * @param in_rate Input sampling rate rounded to the nearest integer (in Hz).
+ * @param out_rate Output sampling rate rounded to the nearest integer (in Hz).
+ */
+int speex_resampler_set_rate_frac(SpeexResamplerState *st,
+ spx_uint32_t ratio_num,
+ spx_uint32_t ratio_den,
+ spx_uint32_t in_rate,
+ spx_uint32_t out_rate);
+
+/** Get the current resampling ratio. This will be reduced to the least
+ * common denominator.
+ * @param st Resampler state
+ * @param ratio_num Numerator of the sampling rate ratio copied
+ * @param ratio_den Denominator of the sampling rate ratio copied
+ */
+void speex_resampler_get_ratio(SpeexResamplerState *st,
+ spx_uint32_t *ratio_num,
+ spx_uint32_t *ratio_den);
+
+/** Set (change) the conversion quality.
+ * @param st Resampler state
+ * @param quality Resampling quality between 0 and 10, where 0 has poor
+ * quality and 10 has very high quality.
+ */
+int speex_resampler_set_quality(SpeexResamplerState *st,
+ int quality);
+
+/** Get the conversion quality.
+ * @param st Resampler state
+ * @param quality Resampling quality between 0 and 10, where 0 has poor
+ * quality and 10 has very high quality.
+ */
+void speex_resampler_get_quality(SpeexResamplerState *st,
+ int *quality);
+
+/** Set (change) the input stride.
+ * @param st Resampler state
+ * @param stride Input stride
+ */
+void speex_resampler_set_input_stride(SpeexResamplerState *st,
+ spx_uint32_t stride);
+
+/** Get the input stride.
+ * @param st Resampler state
+ * @param stride Input stride copied
+ */
+void speex_resampler_get_input_stride(SpeexResamplerState *st,
+ spx_uint32_t *stride);
+
+/** Set (change) the output stride.
+ * @param st Resampler state
+ * @param stride Output stride
+ */
+void speex_resampler_set_output_stride(SpeexResamplerState *st,
+ spx_uint32_t stride);
+
+/** Get the output stride.
+ * @param st Resampler state copied
+ * @param stride Output stride
+ */
+void speex_resampler_get_output_stride(SpeexResamplerState *st,
+ spx_uint32_t *stride);
+
+/** Make sure that the first samples to go out of the resamplers don't have
+ * leading zeros. This is only useful before starting to use a newly created
+ * resampler. It is recommended to use that when resampling an audio file, as
+ * it will generate a file with the same length. For real-time processing,
+ * it is probably easier not to use this call (so that the output duration
+ * is the same for the first frame).
+ * @param st Resampler state
+ */
+int speex_resampler_skip_zeros(SpeexResamplerState *st);
+
+/** Reset a resampler so a new (unrelated) stream can be processed.
+ * @param st Resampler state
+ */
+int speex_resampler_reset_mem(SpeexResamplerState *st);
+
+/** Returns the English meaning for an error code
+ * @param err Error code
+ * @return English string
+ */
+const char *speex_resampler_strerror(int err);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/pulsecore/speexwrap.h b/src/pulsecore/speexwrap.h
new file mode 100644
index 00000000..617e4afb
--- /dev/null
+++ b/src/pulsecore/speexwrap.h
@@ -0,0 +1,48 @@
+#ifndef foopulsespeexwraphfoo
+#define foopulsespeexwraphfoo
+
+/***
+ 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.
+***/
+
+/* We define a minimal version of speex_resampler.h however define one
+ * version for fixed and another one for float. Yes, somewhat ugly */
+
+#define spx_int16_t short
+#define spx_int32_t int
+#define spx_uint16_t unsigned short
+#define spx_uint32_t unsigned int
+
+typedef struct SpeexResamplerState_ SpeexResamplerState;
+
+SpeexResamplerState *paspfx_resampler_init(spx_uint32_t nb_channels, spx_uint32_t in_rate, spx_uint32_t out_rate, int quality, int *err);
+void paspfx_resampler_destroy(SpeexResamplerState *st);
+int paspfx_resampler_process_interleaved_int(SpeexResamplerState *st, const spx_int16_t *in, spx_uint32_t *in_len, spx_int16_t *out, spx_uint32_t *out_len);
+int paspfx_resampler_set_rate(SpeexResamplerState *st, spx_uint32_t in_rate, spx_uint32_t out_rate);
+int paspfx_resampler_reset_mem(SpeexResamplerState *st);
+
+SpeexResamplerState *paspfl_resampler_init(spx_uint32_t nb_channels, spx_uint32_t in_rate, spx_uint32_t out_rate, int quality, int *err);
+void paspfl_resampler_destroy(SpeexResamplerState *st);
+int paspfl_resampler_process_interleaved_float(SpeexResamplerState *st, const float *in, spx_uint32_t *in_len, float *out, spx_uint32_t *out_len);
+int paspfl_resampler_set_rate(SpeexResamplerState *st, spx_uint32_t in_rate, spx_uint32_t out_rate);
+int paspfl_resampler_reset_mem(SpeexResamplerState *st);
+
+#endif
diff --git a/src/pulsecore/start-child.c b/src/pulsecore/start-child.c
new file mode 100644
index 00000000..1661383d
--- /dev/null
+++ b/src/pulsecore/start-child.c
@@ -0,0 +1,160 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2007 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#ifdef HAVE_SYS_PRCTL_H
+#include <sys/prctl.h>
+#endif
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/core-error.h>
+
+#include "start-child.h"
+
+int pa_start_child_for_read(const char *name, const char *argv1, pid_t *pid) {
+ pid_t child;
+ int pipe_fds[2] = { -1, -1 };
+
+ if (pipe(pipe_fds) < 0) {
+ pa_log("pipe() failed: %s", pa_cstrerror(errno));
+ goto fail;
+ }
+
+ if ((child = fork()) == (pid_t) -1) {
+ pa_log("fork() failed: %s", pa_cstrerror(errno));
+ goto fail;
+
+ } else if (child != 0) {
+
+ /* Parent */
+ pa_assert_se(pa_close(pipe_fds[1]) == 0);
+
+ if (pid)
+ *pid = child;
+
+ return pipe_fds[0];
+ } else {
+#ifdef __linux__
+ DIR* d;
+#endif
+ int max_fd, i;
+
+ /* child */
+
+ pa_reset_priority();
+
+ pa_assert_se(pa_close(pipe_fds[0]) == 0);
+ pa_assert_se(dup2(pipe_fds[1], 1) == 1);
+
+ if (pipe_fds[1] != 1)
+ pa_assert_se(pa_close(pipe_fds[1]) == 0);
+
+ pa_close(0);
+ pa_assert_se(open("/dev/null", O_RDONLY) == 0);
+
+ pa_close(2);
+ pa_assert_se(open("/dev/null", O_WRONLY) == 2);
+
+#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)
+ pa_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;
+ }
+#endif
+
+ for (i = 3; i < max_fd; i++)
+ pa_close(i);
+
+#ifdef __linux__
+ }
+#endif
+
+#ifdef PR_SET_PDEATHSIG
+ /* On Linux we can use PR_SET_PDEATHSIG to have the helper
+ process killed when the daemon dies abnormally. On non-Linux
+ machines the client will die as soon as it writes data to
+ stdout again (SIGPIPE) */
+
+ prctl(PR_SET_PDEATHSIG, SIGTERM, 0, 0, 0);
+#endif
+
+#ifdef SIGPIPE
+ /* Make sure that SIGPIPE kills the child process */
+ signal(SIGPIPE, SIG_DFL);
+#endif
+
+#ifdef SIGTERM
+ /* Make sure that SIGTERM kills the child process */
+ signal(SIGTERM, SIG_DFL);
+#endif
+
+ execl(name, name, argv1, NULL);
+ _exit(1);
+ }
+
+fail:
+ pa_close_pipe(pipe_fds);
+
+ return -1;
+}
diff --git a/src/pulsecore/start-child.h b/src/pulsecore/start-child.h
new file mode 100644
index 00000000..0b5ff660
--- /dev/null
+++ b/src/pulsecore/start-child.h
@@ -0,0 +1,30 @@
+#ifndef foopulsestartchildhfoo
+#define foopulsestartchildhfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2007 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <sys/types.h>
+#include <unistd.h>
+
+int pa_start_child_for_read(const char *name, const char *argv1, pid_t *pid);
+
+#endif
diff --git a/src/pulsecore/strbuf.c b/src/pulsecore/strbuf.c
index ef8160dc..b59b6f49 100644
--- a/src/pulsecore/strbuf.c
+++ b/src/pulsecore/strbuf.c
@@ -1,18 +1,18 @@
-/* $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
@@ -25,12 +25,12 @@
#include <sys/types.h>
#include <stdlib.h>
-#include <assert.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
#include "strbuf.h"
@@ -40,7 +40,7 @@ struct chunk {
size_t length;
};
-#define CHUNK_TO_TEXT(c) ((char*) (c) + sizeof(struct chunk))
+#define CHUNK_TO_TEXT(c) ((char*) (c) + PA_ALIGN(sizeof(struct chunk)))
struct pa_strbuf {
size_t length;
@@ -48,14 +48,18 @@ struct pa_strbuf {
};
pa_strbuf *pa_strbuf_new(void) {
- pa_strbuf *sb = pa_xmalloc(sizeof(pa_strbuf));
+ pa_strbuf *sb;
+
+ sb = pa_xnew(pa_strbuf, 1);
sb->length = 0;
sb->head = sb->tail = NULL;
+
return sb;
}
void pa_strbuf_free(pa_strbuf *sb) {
- assert(sb);
+ pa_assert(sb);
+
while (sb->head) {
struct chunk *c = sb->head;
sb->head = sb->head->next;
@@ -70,12 +74,13 @@ void pa_strbuf_free(pa_strbuf *sb) {
char *pa_strbuf_tostring(pa_strbuf *sb) {
char *t, *e;
struct chunk *c;
- assert(sb);
- e = t = pa_xmalloc(sb->length+1);
+ pa_assert(sb);
+
+ e = t = pa_xnew(char, sb->length+1);
for (c = sb->head; c; c = c->next) {
- assert((size_t) (e-t) <= sb->length);
+ pa_assert((size_t) (e-t) <= sb->length);
memcpy(e, CHUNK_TO_TEXT(c), c->length);
e += c->length;
}
@@ -83,35 +88,41 @@ char *pa_strbuf_tostring(pa_strbuf *sb) {
/* Trailing NUL */
*e = 0;
- assert(e == t+sb->length);
-
+ pa_assert(e == t+sb->length);
+
return t;
}
/* Combination of pa_strbuf_free() and pa_strbuf_tostring() */
char *pa_strbuf_tostring_free(pa_strbuf *sb) {
char *t;
- assert(sb);
+
+ pa_assert(sb);
t = pa_strbuf_tostring(sb);
pa_strbuf_free(sb);
+
return t;
}
/* Append a string to the string buffer */
void pa_strbuf_puts(pa_strbuf *sb, const char *t) {
- assert(sb && t);
+
+ pa_assert(sb);
+ pa_assert(t);
+
pa_strbuf_putsn(sb, t, strlen(t));
}
/* Append a new chunk to the linked list */
static void append(pa_strbuf *sb, struct chunk *c) {
- assert(sb && c);
+ pa_assert(sb);
+ pa_assert(c);
if (sb->tail) {
- assert(sb->head);
+ pa_assert(sb->head);
sb->tail->next = c;
} else {
- assert(!sb->head);
+ pa_assert(!sb->head);
sb->head = c;
}
@@ -123,12 +134,14 @@ static void append(pa_strbuf *sb, struct chunk *c) {
/* Append up to l bytes of a string to the string buffer */
void pa_strbuf_putsn(pa_strbuf *sb, const char *t, size_t l) {
struct chunk *c;
- assert(sb && t);
-
+
+ pa_assert(sb);
+ pa_assert(t);
+
if (!l)
return;
-
- c = pa_xmalloc(sizeof(struct chunk)+l);
+
+ c = pa_xmalloc(PA_ALIGN(sizeof(struct chunk)) + l);
c->length = l;
memcpy(CHUNK_TO_TEXT(c), t, l);
@@ -141,18 +154,20 @@ int pa_strbuf_printf(pa_strbuf *sb, const char *format, ...) {
int size = 100;
struct chunk *c = NULL;
- assert(sb);
-
+ pa_assert(sb);
+ pa_assert(format);
+
for(;;) {
va_list ap;
int r;
- c = pa_xrealloc(c, sizeof(struct chunk)+size);
+ c = pa_xrealloc(c, PA_ALIGN(sizeof(struct chunk)) + size);
va_start(ap, format);
r = vsnprintf(CHUNK_TO_TEXT(c), size, format, ap);
+ CHUNK_TO_TEXT(c)[size-1] = 0;
va_end(ap);
-
+
if (r > -1 && r < size) {
c->length = r;
append(sb, c);
@@ -160,7 +175,7 @@ int pa_strbuf_printf(pa_strbuf *sb, const char *format, ...) {
}
if (r > -1) /* glibc 2.1 */
- size = r+1;
+ size = r+1;
else /* glibc 2.0 */
size *= 2;
}
diff --git a/src/pulsecore/strbuf.h b/src/pulsecore/strbuf.h
index c45fb15f..24c876d5 100644
--- a/src/pulsecore/strbuf.h
+++ b/src/pulsecore/strbuf.h
@@ -1,28 +1,28 @@
#ifndef foostrbufhfoo
#define foostrbufhfoo
-/* $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.
***/
-#include <pulsecore/gccmacro.h>
+#include <pulse/gccmacro.h>
typedef struct pa_strbuf pa_strbuf;
diff --git a/src/pulsecore/strlist.c b/src/pulsecore/strlist.c
index df3a0275..f587a2f8 100644
--- a/src/pulsecore/strlist.c
+++ b/src/pulsecore/strlist.c
@@ -1,18 +1,18 @@
-/* $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
@@ -24,26 +24,31 @@
#endif
#include <string.h>
-#include <assert.h>
#include <pulse/xmalloc.h>
#include <pulsecore/strbuf.h>
+#include <pulsecore/macro.h>
#include <pulsecore/core-util.h>
#include "strlist.h"
struct pa_strlist {
pa_strlist *next;
- char *str;
};
+#define ITEM_TO_TEXT(c) ((char*) (c) + PA_ALIGN(sizeof(pa_strlist)))
+
pa_strlist* pa_strlist_prepend(pa_strlist *l, const char *s) {
pa_strlist *n;
- assert(s);
- n = pa_xmalloc(sizeof(pa_strlist));
- n->str = pa_xstrdup(s);
+ size_t size;
+
+ pa_assert(s);
+ size = strlen(s);
+ n = pa_xmalloc(PA_ALIGN(sizeof(pa_strlist)) + size + 1);
+ memcpy(ITEM_TO_TEXT(n), s, size + 1);
n->next = l;
+
return n;
}
@@ -56,7 +61,7 @@ char *pa_strlist_tostring(pa_strlist *l) {
if (!first)
pa_strbuf_puts(b, " ");
first = 0;
- pa_strbuf_puts(b, l->str);
+ pa_strbuf_puts(b, ITEM_TO_TEXT(l));
}
return pa_strbuf_tostring_free(b);
@@ -64,23 +69,24 @@ char *pa_strlist_tostring(pa_strlist *l) {
pa_strlist* pa_strlist_remove(pa_strlist *l, const char *s) {
pa_strlist *ret = l, *prev = NULL;
- assert(l && s);
+
+ pa_assert(l);
+ pa_assert(s);
while (l) {
- if (!strcmp(l->str, s)) {
+ if (!strcmp(ITEM_TO_TEXT(l), s)) {
pa_strlist *n = l->next;
-
+
if (!prev) {
- assert(ret == l);
+ pa_assert(ret == l);
ret = n;
} else
prev->next = n;
- pa_xfree(l->str);
pa_xfree(l);
l = n;
-
+
} else {
prev = l;
l = l->next;
@@ -94,22 +100,21 @@ void pa_strlist_free(pa_strlist *l) {
while (l) {
pa_strlist *c = l;
l = l->next;
-
- pa_xfree(c->str);
pa_xfree(c);
}
}
pa_strlist* pa_strlist_pop(pa_strlist *l, char **s) {
pa_strlist *r;
- assert(s);
-
+
+ pa_assert(s);
+
if (!l) {
*s = NULL;
return NULL;
}
-
- *s = l->str;
+
+ *s = pa_xstrdup(ITEM_TO_TEXT(l));
r = l->next;
pa_xfree(l);
return r;
@@ -122,10 +127,12 @@ pa_strlist* pa_strlist_parse(const char *s) {
while ((r = pa_split_spaces(s, &state))) {
pa_strlist *n;
+ size_t size = strlen(r);
- n = pa_xmalloc(sizeof(pa_strlist));
- n->str = r;
+ n = pa_xmalloc(PA_ALIGN(sizeof(pa_strlist)) + size + 1);
n->next = NULL;
+ memcpy(ITEM_TO_TEXT(n), r, size+1);
+ pa_xfree(r);
if (p)
p->next = n;
@@ -137,3 +144,18 @@ pa_strlist* pa_strlist_parse(const char *s) {
return head;
}
+
+pa_strlist *pa_strlist_reverse(pa_strlist *l) {
+ pa_strlist *r = NULL;
+
+ while (l) {
+ pa_strlist *n;
+
+ n = l->next;
+ l->next = r;
+ r = l;
+ l = n;
+ }
+
+ return r;
+}
diff --git a/src/pulsecore/strlist.h b/src/pulsecore/strlist.h
index 87925d5e..1cb7537a 100644
--- a/src/pulsecore/strlist.h
+++ b/src/pulsecore/strlist.h
@@ -1,21 +1,21 @@
#ifndef foostrlisthfoo
#define foostrlisthfoo
-/* $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
@@ -44,4 +44,7 @@ pa_strlist* pa_strlist_pop(pa_strlist *l, char **s);
/* Parse a whitespace separated server list */
pa_strlist* pa_strlist_parse(const char *s);
+/* Reverse string list */
+pa_strlist *pa_strlist_reverse(pa_strlist *l);
+
#endif
diff --git a/src/pulsecore/tagstruct.c b/src/pulsecore/tagstruct.c
index 11e85c19..b0ed59ef 100644
--- a/src/pulsecore/tagstruct.c
+++ b/src/pulsecore/tagstruct.c
@@ -1,18 +1,18 @@
-/* $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.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
@@ -27,43 +27,46 @@
#include <string.h>
#include <unistd.h>
#include <sys/time.h>
-#include <assert.h>
#include <stdarg.h>
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
-#include "winsock.h"
-
#include <pulse/xmalloc.h>
+#include <pulsecore/winsock.h>
+#include <pulsecore/macro.h>
+
#include "tagstruct.h"
+#define MAX_TAG_SIZE (64*1024)
struct pa_tagstruct {
uint8_t *data;
size_t length, allocated;
size_t rindex;
- int dynamic;
+ pa_bool_t dynamic;
};
pa_tagstruct *pa_tagstruct_new(const uint8_t* data, size_t length) {
pa_tagstruct*t;
- assert(!data || (data && length));
-
- t = pa_xmalloc(sizeof(pa_tagstruct));
+ pa_assert(!data || (data && length));
+
+ t = pa_xnew(pa_tagstruct, 1);
t->data = (uint8_t*) data;
t->allocated = t->length = data ? length : 0;
t->rindex = 0;
t->dynamic = !data;
+
return t;
}
-
+
void pa_tagstruct_free(pa_tagstruct*t) {
- assert(t);
+ pa_assert(t);
+
if (t->dynamic)
pa_xfree(t->data);
pa_xfree(t);
@@ -71,7 +74,11 @@ void pa_tagstruct_free(pa_tagstruct*t) {
uint8_t* pa_tagstruct_free_data(pa_tagstruct*t, size_t *l) {
uint8_t *p;
- assert(t && t->dynamic && l);
+
+ pa_assert(t);
+ pa_assert(t->dynamic);
+ pa_assert(l);
+
p = t->data;
*l = t->length;
pa_xfree(t);
@@ -79,8 +86,8 @@ uint8_t* pa_tagstruct_free_data(pa_tagstruct*t, size_t *l) {
}
static void extend(pa_tagstruct*t, size_t l) {
- assert(t);
- assert(t->dynamic);
+ pa_assert(t);
+ pa_assert(t->dynamic);
if (t->length+l <= t->allocated)
return;
@@ -90,7 +97,8 @@ static void extend(pa_tagstruct*t, size_t l) {
void pa_tagstruct_puts(pa_tagstruct*t, const char *s) {
size_t l;
- assert(t);
+ pa_assert(t);
+
if (s) {
l = strlen(s)+2;
extend(t, l);
@@ -105,7 +113,8 @@ void pa_tagstruct_puts(pa_tagstruct*t, const char *s) {
}
void pa_tagstruct_putu32(pa_tagstruct*t, uint32_t i) {
- assert(t);
+ pa_assert(t);
+
extend(t, 5);
t->data[t->length] = PA_TAG_U32;
i = htonl(i);
@@ -114,7 +123,8 @@ void pa_tagstruct_putu32(pa_tagstruct*t, uint32_t i) {
}
void pa_tagstruct_putu8(pa_tagstruct*t, uint8_t c) {
- assert(t);
+ pa_assert(t);
+
extend(t, 2);
t->data[t->length] = PA_TAG_U8;
*(t->data+t->length+1) = c;
@@ -123,7 +133,10 @@ void pa_tagstruct_putu8(pa_tagstruct*t, uint8_t c) {
void pa_tagstruct_put_sample_spec(pa_tagstruct *t, const pa_sample_spec *ss) {
uint32_t rate;
- assert(t && ss);
+
+ pa_assert(t);
+ pa_assert(ss);
+
extend(t, 7);
t->data[t->length] = PA_TAG_SAMPLE_SPEC;
t->data[t->length+1] = (uint8_t) ss->format;
@@ -135,7 +148,9 @@ void pa_tagstruct_put_sample_spec(pa_tagstruct *t, const pa_sample_spec *ss) {
void pa_tagstruct_put_arbitrary(pa_tagstruct *t, const void *p, size_t length) {
uint32_t tmp;
- assert(t && p);
+
+ pa_assert(t);
+ pa_assert(p);
extend(t, 5+length);
t->data[t->length] = PA_TAG_ARBITRARY;
@@ -146,8 +161,9 @@ void pa_tagstruct_put_arbitrary(pa_tagstruct *t, const void *p, size_t length) {
t->length += 5+length;
}
-void pa_tagstruct_put_boolean(pa_tagstruct*t, int b) {
- assert(t);
+void pa_tagstruct_put_boolean(pa_tagstruct*t, pa_bool_t b) {
+ pa_assert(t);
+
extend(t, 1);
t->data[t->length] = b ? PA_TAG_BOOLEAN_TRUE : PA_TAG_BOOLEAN_FALSE;
t->length += 1;
@@ -155,7 +171,8 @@ void pa_tagstruct_put_boolean(pa_tagstruct*t, int b) {
void pa_tagstruct_put_timeval(pa_tagstruct*t, const struct timeval *tv) {
uint32_t tmp;
- assert(t);
+ pa_assert(t);
+
extend(t, 9);
t->data[t->length] = PA_TAG_TIMEVAL;
tmp = htonl(tv->tv_sec);
@@ -167,7 +184,9 @@ void pa_tagstruct_put_timeval(pa_tagstruct*t, const struct timeval *tv) {
void pa_tagstruct_put_usec(pa_tagstruct*t, pa_usec_t u) {
uint32_t tmp;
- assert(t);
+
+ pa_assert(t);
+
extend(t, 9);
t->data[t->length] = PA_TAG_USEC;
tmp = htonl((uint32_t) (u >> 32));
@@ -179,7 +198,9 @@ void pa_tagstruct_put_usec(pa_tagstruct*t, pa_usec_t u) {
void pa_tagstruct_putu64(pa_tagstruct*t, uint64_t u) {
uint32_t tmp;
- assert(t);
+
+ pa_assert(t);
+
extend(t, 9);
t->data[t->length] = PA_TAG_U64;
tmp = htonl((uint32_t) (u >> 32));
@@ -191,7 +212,9 @@ void pa_tagstruct_putu64(pa_tagstruct*t, uint64_t u) {
void pa_tagstruct_puts64(pa_tagstruct*t, int64_t u) {
uint32_t tmp;
- assert(t);
+
+ pa_assert(t);
+
extend(t, 9);
t->data[t->length] = PA_TAG_S64;
tmp = htonl((uint32_t) ((uint64_t) u >> 32));
@@ -203,13 +226,13 @@ void pa_tagstruct_puts64(pa_tagstruct*t, int64_t u) {
void pa_tagstruct_put_channel_map(pa_tagstruct *t, const pa_channel_map *map) {
unsigned i;
-
- assert(t);
+
+ pa_assert(t);
extend(t, 2 + map->channels);
t->data[t->length++] = PA_TAG_CHANNEL_MAP;
t->data[t->length++] = map->channels;
-
+
for (i = 0; i < map->channels; i ++)
t->data[t->length++] = (uint8_t) map->map[i];
}
@@ -217,13 +240,13 @@ void pa_tagstruct_put_channel_map(pa_tagstruct *t, const pa_channel_map *map) {
void pa_tagstruct_put_cvolume(pa_tagstruct *t, const pa_cvolume *cvolume) {
unsigned i;
pa_volume_t vol;
-
- assert(t);
+
+ pa_assert(t);
extend(t, 2 + cvolume->channels * sizeof(pa_volume_t));
t->data[t->length++] = PA_TAG_CVOLUME;
t->data[t->length++] = cvolume->channels;
-
+
for (i = 0; i < cvolume->channels; i ++) {
vol = htonl(cvolume->values[i]);
memcpy(t->data + t->length, &vol, sizeof(pa_volume_t));
@@ -231,11 +254,39 @@ void pa_tagstruct_put_cvolume(pa_tagstruct *t, const pa_cvolume *cvolume) {
}
}
+void pa_tagstruct_put_proplist(pa_tagstruct *t, pa_proplist *p) {
+ void *state = NULL;
+ pa_assert(t);
+ pa_assert(p);
+
+ extend(t, 1);
+
+ t->data[t->length++] = PA_TAG_PROPLIST;
+
+ for (;;) {
+ const char *k;
+ const void *d;
+ size_t l;
+
+ if (!(k = pa_proplist_iterate(p, &state)))
+ break;
+
+ pa_tagstruct_puts(t, k);
+ pa_assert_se(pa_proplist_get(p, k, &d, &l) >= 0);
+ pa_tagstruct_putu32(t, (uint32_t) l);
+ pa_tagstruct_put_arbitrary(t, d, l);
+ }
+
+ pa_tagstruct_puts(t, NULL);
+}
+
int pa_tagstruct_gets(pa_tagstruct*t, const char **s) {
int error = 0;
size_t n;
char *c;
- assert(t && s);
+
+ pa_assert(t);
+ pa_assert(s);
if (t->rindex+1 > t->length)
return -1;
@@ -245,10 +296,10 @@ int pa_tagstruct_gets(pa_tagstruct*t, const char **s) {
*s = NULL;
return 0;
}
-
+
if (t->rindex+2 > t->length)
return -1;
-
+
if (t->data[t->rindex] != PA_TAG_STRING)
return -1;
@@ -269,7 +320,8 @@ int pa_tagstruct_gets(pa_tagstruct*t, const char **s) {
}
int pa_tagstruct_getu32(pa_tagstruct*t, uint32_t *i) {
- assert(t && i);
+ pa_assert(t);
+ pa_assert(i);
if (t->rindex+5 > t->length)
return -1;
@@ -284,7 +336,8 @@ int pa_tagstruct_getu32(pa_tagstruct*t, uint32_t *i) {
}
int pa_tagstruct_getu8(pa_tagstruct*t, uint8_t *c) {
- assert(t && c);
+ pa_assert(t);
+ pa_assert(c);
if (t->rindex+2 > t->length)
return -1;
@@ -298,14 +351,15 @@ int pa_tagstruct_getu8(pa_tagstruct*t, uint8_t *c) {
}
int pa_tagstruct_get_sample_spec(pa_tagstruct *t, pa_sample_spec *ss) {
- assert(t && ss);
+ pa_assert(t);
+ pa_assert(ss);
if (t->rindex+7 > t->length)
return -1;
if (t->data[t->rindex] != PA_TAG_SAMPLE_SPEC)
return -1;
-
+
ss->format = t->data[t->rindex+1];
ss->channels = t->data[t->rindex+2];
memcpy(&ss->rate, t->data+t->rindex+3, 4);
@@ -317,8 +371,10 @@ int pa_tagstruct_get_sample_spec(pa_tagstruct *t, pa_sample_spec *ss) {
int pa_tagstruct_get_arbitrary(pa_tagstruct *t, const void **p, size_t length) {
uint32_t len;
- assert(t && p);
-
+
+ pa_assert(t);
+ pa_assert(p);
+
if (t->rindex+5+length > t->length)
return -1;
@@ -335,35 +391,43 @@ int pa_tagstruct_get_arbitrary(pa_tagstruct *t, const void **p, size_t length) {
}
int pa_tagstruct_eof(pa_tagstruct*t) {
- assert(t);
+ pa_assert(t);
+
return t->rindex >= t->length;
}
const uint8_t* pa_tagstruct_data(pa_tagstruct*t, size_t *l) {
- assert(t && t->dynamic && l);
+ pa_assert(t);
+ pa_assert(t->dynamic);
+ pa_assert(l);
+
*l = t->length;
return t->data;
}
-int pa_tagstruct_get_boolean(pa_tagstruct*t, int *b) {
- assert(t && b);
+int pa_tagstruct_get_boolean(pa_tagstruct*t, pa_bool_t *b) {
+ pa_assert(t);
+ pa_assert(b);
if (t->rindex+1 > t->length)
return -1;
if (t->data[t->rindex] == PA_TAG_BOOLEAN_TRUE)
- *b = 1;
+ *b = TRUE;
else if (t->data[t->rindex] == PA_TAG_BOOLEAN_FALSE)
- *b = 0;
+ *b = FALSE;
else
return -1;
-
+
t->rindex +=1;
return 0;
}
int pa_tagstruct_get_timeval(pa_tagstruct*t, struct timeval *tv) {
+ pa_assert(t);
+ pa_assert(tv);
+
if (t->rindex+9 > t->length)
return -1;
@@ -380,7 +444,9 @@ int pa_tagstruct_get_timeval(pa_tagstruct*t, struct timeval *tv) {
int pa_tagstruct_get_usec(pa_tagstruct*t, pa_usec_t *u) {
uint32_t tmp;
- assert(t && u);
+
+ pa_assert(t);
+ pa_assert(u);
if (t->rindex+9 > t->length)
return -1;
@@ -398,7 +464,9 @@ int pa_tagstruct_get_usec(pa_tagstruct*t, pa_usec_t *u) {
int pa_tagstruct_getu64(pa_tagstruct*t, uint64_t *u) {
uint32_t tmp;
- assert(t && u);
+
+ pa_assert(t);
+ pa_assert(u);
if (t->rindex+9 > t->length)
return -1;
@@ -416,7 +484,9 @@ int pa_tagstruct_getu64(pa_tagstruct*t, uint64_t *u) {
int pa_tagstruct_gets64(pa_tagstruct*t, int64_t *u) {
uint32_t tmp;
- assert(t && u);
+
+ pa_assert(t);
+ pa_assert(u);
if (t->rindex+9 > t->length)
return -1;
@@ -434,9 +504,9 @@ int pa_tagstruct_gets64(pa_tagstruct*t, int64_t *u) {
int pa_tagstruct_get_channel_map(pa_tagstruct *t, pa_channel_map *map) {
unsigned i;
-
- assert(t);
- assert(map);
+
+ pa_assert(t);
+ pa_assert(map);
if (t->rindex+2 > t->length)
return -1;
@@ -449,7 +519,7 @@ int pa_tagstruct_get_channel_map(pa_tagstruct *t, pa_channel_map *map) {
if (t->rindex+2+map->channels > t->length)
return -1;
-
+
for (i = 0; i < map->channels; i ++)
map->map[i] = (int8_t) t->data[t->rindex + 2 + i];
@@ -460,9 +530,9 @@ int pa_tagstruct_get_channel_map(pa_tagstruct *t, pa_channel_map *map) {
int pa_tagstruct_get_cvolume(pa_tagstruct *t, pa_cvolume *cvolume) {
unsigned i;
pa_volume_t vol;
-
- assert(t);
- assert(cvolume);
+
+ pa_assert(t);
+ pa_assert(cvolume);
if (t->rindex+2 > t->length)
return -1;
@@ -475,7 +545,7 @@ int pa_tagstruct_get_cvolume(pa_tagstruct *t, pa_cvolume *cvolume) {
if (t->rindex+2+cvolume->channels*sizeof(pa_volume_t) > t->length)
return -1;
-
+
for (i = 0; i < cvolume->channels; i ++) {
memcpy(&vol, t->data + t->rindex + 2 + i * sizeof(pa_volume_t), sizeof(pa_volume_t));
cvolume->values[i] = (pa_volume_t) ntohl(vol);
@@ -485,10 +555,56 @@ int pa_tagstruct_get_cvolume(pa_tagstruct *t, pa_cvolume *cvolume) {
return 0;
}
+int pa_tagstruct_get_proplist(pa_tagstruct *t, pa_proplist *p) {
+ size_t saved_rindex;
+
+ pa_assert(t);
+ pa_assert(p);
+
+ if (t->rindex+1 > t->length)
+ return -1;
+
+ if (t->data[t->rindex] != PA_TAG_PROPLIST)
+ return -1;
+
+ saved_rindex = t->rindex;
+ t->rindex++;
+
+ for (;;) {
+ const char *k;
+ const void *d;
+ uint32_t length;
+
+ if (pa_tagstruct_gets(t, &k) < 0)
+ goto fail;
+
+ if (!k)
+ break;
+
+ if (pa_tagstruct_getu32(t, &length) < 0)
+ goto fail;
+
+ if (length > MAX_TAG_SIZE)
+ goto fail;
+
+ if (pa_tagstruct_get_arbitrary(t, &d, length) < 0)
+ goto fail;
+
+ if (pa_proplist_set(p, k, d, length) < 0)
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ t->rindex = saved_rindex;
+ return -1;
+}
+
void pa_tagstruct_put(pa_tagstruct *t, ...) {
va_list va;
- assert(t);
-
+ pa_assert(t);
+
va_start(va, t);
for (;;) {
@@ -547,20 +663,24 @@ void pa_tagstruct_put(pa_tagstruct *t, ...) {
pa_tagstruct_put_cvolume(t, va_arg(va, pa_cvolume *));
break;
+ case PA_TAG_PROPLIST:
+ pa_tagstruct_put_proplist(t, va_arg(va, pa_proplist *));
+ break;
+
default:
- abort();
+ pa_assert_not_reached();
}
}
-
+
va_end(va);
}
int pa_tagstruct_get(pa_tagstruct *t, ...) {
va_list va;
int ret = 0;
-
- assert(t);
-
+
+ pa_assert(t);
+
va_start(va, t);
while (ret == 0) {
int tag = va_arg(va, int);
@@ -599,7 +719,7 @@ int pa_tagstruct_get(pa_tagstruct *t, ...) {
case PA_TAG_BOOLEAN_TRUE:
case PA_TAG_BOOLEAN_FALSE:
- ret = pa_tagstruct_get_boolean(t, va_arg(va, int*));
+ ret = pa_tagstruct_get_boolean(t, va_arg(va, pa_bool_t*));
break;
case PA_TAG_TIMEVAL:
@@ -618,11 +738,14 @@ int pa_tagstruct_get(pa_tagstruct *t, ...) {
ret = pa_tagstruct_get_cvolume(t, va_arg(va, pa_cvolume *));
break;
-
+ case PA_TAG_PROPLIST:
+ ret = pa_tagstruct_get_proplist(t, va_arg(va, pa_proplist *));
+ break;
+
default:
- abort();
+ pa_assert_not_reached();
}
-
+
}
va_end(va);
diff --git a/src/pulsecore/tagstruct.h b/src/pulsecore/tagstruct.h
index 4c56f328..e7d07054 100644
--- a/src/pulsecore/tagstruct.h
+++ b/src/pulsecore/tagstruct.h
@@ -1,21 +1,21 @@
#ifndef footagstructhfoo
#define footagstructhfoo
-/* $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.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
@@ -30,6 +30,10 @@
#include <pulse/sample.h>
#include <pulse/channelmap.h>
#include <pulse/volume.h>
+#include <pulse/proplist.h>
+#include <pulse/gccmacro.h>
+
+#include <pulsecore/macro.h>
typedef struct pa_tagstruct pa_tagstruct;
@@ -49,7 +53,8 @@ enum {
PA_TAG_TIMEVAL = 'T',
PA_TAG_USEC = 'U' /* 64bit unsigned */,
PA_TAG_CHANNEL_MAP = 'm',
- PA_TAG_CVOLUME = 'v'
+ PA_TAG_CVOLUME = 'v',
+ PA_TAG_PROPLIST = 'P'
};
pa_tagstruct *pa_tagstruct_new(const uint8_t* data, size_t length);
@@ -68,11 +73,12 @@ void pa_tagstruct_putu64(pa_tagstruct*t, uint64_t i);
void pa_tagstruct_puts64(pa_tagstruct*t, int64_t i);
void pa_tagstruct_put_sample_spec(pa_tagstruct *t, const pa_sample_spec *ss);
void pa_tagstruct_put_arbitrary(pa_tagstruct*t, const void *p, size_t length);
-void pa_tagstruct_put_boolean(pa_tagstruct*t, int b);
+void pa_tagstruct_put_boolean(pa_tagstruct*t, pa_bool_t b);
void pa_tagstruct_put_timeval(pa_tagstruct*t, const struct timeval *tv);
void pa_tagstruct_put_usec(pa_tagstruct*t, pa_usec_t u);
void pa_tagstruct_put_channel_map(pa_tagstruct *t, const pa_channel_map *map);
void pa_tagstruct_put_cvolume(pa_tagstruct *t, const pa_cvolume *cvolume);
+void pa_tagstruct_put_proplist(pa_tagstruct *t, pa_proplist *p);
int pa_tagstruct_get(pa_tagstruct *t, ...);
@@ -83,11 +89,12 @@ int pa_tagstruct_getu64(pa_tagstruct*t, uint64_t *i);
int pa_tagstruct_gets64(pa_tagstruct*t, int64_t *i);
int pa_tagstruct_get_sample_spec(pa_tagstruct *t, pa_sample_spec *ss);
int pa_tagstruct_get_arbitrary(pa_tagstruct *t, const void **p, size_t length);
-int pa_tagstruct_get_boolean(pa_tagstruct *t, int *b);
+int pa_tagstruct_get_boolean(pa_tagstruct *t, pa_bool_t *b);
int pa_tagstruct_get_timeval(pa_tagstruct*t, struct timeval *tv);
int pa_tagstruct_get_usec(pa_tagstruct*t, pa_usec_t *u);
int pa_tagstruct_get_channel_map(pa_tagstruct *t, pa_channel_map *map);
int pa_tagstruct_get_cvolume(pa_tagstruct *t, pa_cvolume *v);
+int pa_tagstruct_get_proplist(pa_tagstruct *t, pa_proplist *p);
#endif
diff --git a/src/pulsecore/thread-mq.c b/src/pulsecore/thread-mq.c
new file mode 100644
index 00000000..34f92a7e
--- /dev/null
+++ b/src/pulsecore/thread-mq.c
@@ -0,0 +1,127 @@
+/***
+ 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.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <errno.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/atomic.h>
+#include <pulsecore/once.h>
+#include <pulsecore/log.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/semaphore.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/flist.h>
+
+#include "thread-mq.h"
+
+PA_STATIC_TLS_DECLARE_NO_FREE(thread_mq);
+
+static void asyncmsgq_read_cb(pa_mainloop_api*api, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata) {
+ pa_thread_mq *q = userdata;
+ pa_asyncmsgq *aq;
+
+ pa_assert(pa_asyncmsgq_read_fd(q->outq) == fd);
+ pa_assert(events == PA_IO_EVENT_INPUT);
+
+ pa_asyncmsgq_ref(aq = q->outq);
+ pa_asyncmsgq_write_after_poll(aq);
+
+ for (;;) {
+ pa_msgobject *object;
+ int code;
+ void *data;
+ int64_t offset;
+ pa_memchunk chunk;
+
+ /* Check whether there is a message for us to process */
+ while (pa_asyncmsgq_get(aq, &object, &code, &data, &offset, &chunk, 0) == 0) {
+ int ret;
+
+ ret = pa_asyncmsgq_dispatch(object, code, data, offset, &chunk);
+ pa_asyncmsgq_done(aq, ret);
+ }
+
+ if (pa_asyncmsgq_read_before_poll(aq) == 0)
+ break;
+ }
+
+ pa_asyncmsgq_unref(aq);
+}
+
+static void asyncmsgq_write_cb(pa_mainloop_api*api, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata) {
+ pa_thread_mq *q = userdata;
+
+ pa_assert(pa_asyncmsgq_write_fd(q->inq) == fd);
+ pa_assert(events == PA_IO_EVENT_INPUT);
+
+ pa_asyncmsgq_write_after_poll(q->inq);
+ pa_asyncmsgq_write_before_poll(q->inq);
+}
+
+void pa_thread_mq_init(pa_thread_mq *q, pa_mainloop_api *mainloop, pa_rtpoll *rtpoll) {
+ pa_assert(q);
+ pa_assert(mainloop);
+
+ q->mainloop = mainloop;
+ pa_assert_se(q->inq = pa_asyncmsgq_new(0));
+ pa_assert_se(q->outq = pa_asyncmsgq_new(0));
+
+ pa_assert_se(pa_asyncmsgq_read_before_poll(q->outq) == 0);
+ pa_assert_se(q->read_event = mainloop->io_new(mainloop, pa_asyncmsgq_read_fd(q->outq), PA_IO_EVENT_INPUT, asyncmsgq_read_cb, q));
+
+ pa_asyncmsgq_write_before_poll(q->inq);
+ pa_assert_se(q->write_event = mainloop->io_new(mainloop, pa_asyncmsgq_write_fd(q->inq), PA_IO_EVENT_INPUT, asyncmsgq_write_cb, q));
+
+ pa_rtpoll_item_new_asyncmsgq_read(rtpoll, PA_RTPOLL_EARLY, q->inq);
+ pa_rtpoll_item_new_asyncmsgq_write(rtpoll, PA_RTPOLL_LATE, q->outq);
+}
+
+void pa_thread_mq_done(pa_thread_mq *q) {
+ pa_assert(q);
+
+ q->mainloop->io_free(q->read_event);
+ q->mainloop->io_free(q->write_event);
+ q->read_event = q->write_event = NULL;
+
+ pa_asyncmsgq_unref(q->inq);
+ pa_asyncmsgq_unref(q->outq);
+ q->inq = q->outq = NULL;
+
+ q->mainloop = NULL;
+}
+
+void pa_thread_mq_install(pa_thread_mq *q) {
+ pa_assert(q);
+
+ pa_assert(!(PA_STATIC_TLS_GET(thread_mq)));
+ PA_STATIC_TLS_SET(thread_mq, q);
+}
+
+pa_thread_mq *pa_thread_mq_get(void) {
+ return PA_STATIC_TLS_GET(thread_mq);
+}
diff --git a/src/pulsecore/thread-mq.h b/src/pulsecore/thread-mq.h
new file mode 100644
index 00000000..3b5e0e78
--- /dev/null
+++ b/src/pulsecore/thread-mq.h
@@ -0,0 +1,48 @@
+#ifndef foopulsethreadmqhfoo
+#define foopulsethreadmqhfoo
+
+/***
+ 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.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <pulse/mainloop-api.h>
+#include <pulsecore/asyncmsgq.h>
+#include <pulsecore/rtpoll.h>
+
+/* Two way communication between a thread and a mainloop. Before the
+ * thread is started a pa_pthread_mq should be initialized and than
+ * attached to the thread using pa_thread_mq_install(). */
+
+typedef struct pa_thread_mq {
+ pa_mainloop_api *mainloop;
+ pa_asyncmsgq *inq, *outq;
+ pa_io_event *read_event, *write_event;
+} pa_thread_mq;
+
+void pa_thread_mq_init(pa_thread_mq *q, pa_mainloop_api *mainloop, pa_rtpoll *rtpoll);
+void pa_thread_mq_done(pa_thread_mq *q);
+
+/* Install the specified pa_thread_mq object for the current thread */
+void pa_thread_mq_install(pa_thread_mq *q);
+
+/* Return the pa_thread_mq object that is set for the current thread */
+pa_thread_mq *pa_thread_mq_get(void);
+
+#endif
diff --git a/src/pulsecore/thread-posix.c b/src/pulsecore/thread-posix.c
index d69790a5..20ed16d9 100644
--- a/src/pulsecore/thread-posix.c
+++ b/src/pulsecore/thread-posix.c
@@ -1,18 +1,19 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
+ Copyright 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
@@ -23,7 +24,6 @@
#include <config.h>
#endif
-#include <assert.h>
#include <pthread.h>
#include <sched.h>
#include <errno.h>
@@ -32,65 +32,53 @@
#include <pulsecore/mutex.h>
#include <pulsecore/once.h>
#include <pulsecore/atomic.h>
+#include <pulsecore/macro.h>
#include "thread.h"
-#define ASSERT_SUCCESS(x) do { \
- int _r = (x); \
- assert(_r == 0); \
-} while(0)
-
struct pa_thread {
pthread_t id;
pa_thread_func_t thread_func;
void *userdata;
- pa_atomic_int_t running;
+ pa_atomic_t running;
};
struct pa_tls {
pthread_key_t key;
};
-static pa_tls *thread_tls;
-static pa_once_t thread_tls_once = PA_ONCE_INIT;
-
-static void tls_free_cb(void *p) {
+static void thread_free_cb(void *p) {
pa_thread *t = p;
- assert(t);
-
- if (!t->thread_func)
+ pa_assert(t);
+
+ if (!t->thread_func)
/* This is a foreign thread, we need to free the struct */
pa_xfree(t);
}
-static void thread_tls_once_func(void) {
- thread_tls = pa_tls_new(tls_free_cb);
- assert(thread_tls);
-}
+PA_STATIC_TLS_DECLARE(current_thread, thread_free_cb);
static void* internal_thread_func(void *userdata) {
pa_thread *t = userdata;
- assert(t);
+ pa_assert(t);
t->id = pthread_self();
- pa_once(&thread_tls_once, thread_tls_once_func);
-
- pa_tls_set(thread_tls, t);
-
+ PA_STATIC_TLS_SET(current_thread, t);
+
pa_atomic_inc(&t->running);
t->thread_func(t->userdata);
- pa_atomic_add(&t->running, -2);
-
+ pa_atomic_sub(&t->running, 2);
+
return NULL;
}
pa_thread* pa_thread_new(pa_thread_func_t thread_func, void *userdata) {
pa_thread *t;
- assert(thread_func);
-
+ pa_assert(thread_func);
+
t = pa_xnew(pa_thread, 1);
t->thread_func = thread_func;
t->userdata = userdata;
@@ -107,65 +95,58 @@ pa_thread* pa_thread_new(pa_thread_func_t thread_func, void *userdata) {
}
int pa_thread_is_running(pa_thread *t) {
- assert(t);
-
- if (!t->thread_func) {
- /* Mhmm, this is a foreign thread, t->running is not
- * necessarily valid. We misuse pthread_getschedparam() to
- * check if the thread is valid. This might not be portable. */
+ pa_assert(t);
- int policy;
- struct sched_param param;
-
- return pthread_getschedparam(t->id, &policy, &param) >= 0 || errno != ESRCH;
- }
+ /* Unfortunately there is no way to tell whether a "foreign"
+ * thread is still running. See
+ * http://udrepper.livejournal.com/16844.html for more
+ * information */
+ pa_assert(t->thread_func);
return pa_atomic_load(&t->running) > 0;
}
void pa_thread_free(pa_thread *t) {
- assert(t);
-
+ pa_assert(t);
+
pa_thread_join(t);
pa_xfree(t);
}
int pa_thread_join(pa_thread *t) {
- assert(t);
-
+ pa_assert(t);
+
return pthread_join(t->id, NULL);
}
pa_thread* pa_thread_self(void) {
pa_thread *t;
-
- pa_once(&thread_tls_once, thread_tls_once_func);
- if ((t = pa_tls_get(thread_tls)))
+ if ((t = PA_STATIC_TLS_GET(current_thread)))
return t;
/* This is a foreign thread, let's create a pthread structure to
* make sure that we can always return a sensible pointer */
-
+
t = pa_xnew(pa_thread, 1);
t->id = pthread_self();
t->thread_func = NULL;
t->userdata = NULL;
pa_atomic_store(&t->running, 2);
- pa_tls_set(thread_tls, t);
-
+ PA_STATIC_TLS_SET(current_thread, t);
+
return t;
}
void* pa_thread_get_data(pa_thread *t) {
- assert(t);
+ pa_assert(t);
return t->userdata;
}
void pa_thread_set_data(pa_thread *t, void *userdata) {
- assert(t);
+ pa_assert(t);
t->userdata = userdata;
}
@@ -174,7 +155,7 @@ void pa_thread_yield(void) {
#ifdef HAVE_PTHREAD_YIELD
pthread_yield();
#else
- ASSERT_SUCCESS(sched_yield());
+ pa_assert_se(sched_yield() == 0);
#endif
}
@@ -187,28 +168,28 @@ pa_tls* pa_tls_new(pa_free_cb_t free_cb) {
pa_xfree(t);
return NULL;
}
-
+
return t;
}
void pa_tls_free(pa_tls *t) {
- assert(t);
+ pa_assert(t);
- ASSERT_SUCCESS(pthread_key_delete(t->key));
+ pa_assert_se(pthread_key_delete(t->key) == 0);
pa_xfree(t);
}
void *pa_tls_get(pa_tls *t) {
- assert(t);
-
+ pa_assert(t);
+
return pthread_getspecific(t->key);
}
void *pa_tls_set(pa_tls *t, void *userdata) {
void *r;
-
+
r = pthread_getspecific(t->key);
- ASSERT_SUCCESS(pthread_setspecific(t->key, userdata));
+ pa_assert_se(pthread_setspecific(t->key, userdata) == 0);
return r;
}
diff --git a/src/pulsecore/thread-win32.c b/src/pulsecore/thread-win32.c
index 98ea0691..c40d3342 100644
--- a/src/pulsecore/thread-win32.c
+++ b/src/pulsecore/thread-win32.c
@@ -1,18 +1,18 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
+ 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
@@ -51,9 +51,8 @@ struct pa_tls_monitor {
};
static pa_tls *thread_tls;
-static pa_once_t thread_tls_once = PA_ONCE_INIT;
+static pa_once thread_tls_once = PA_ONCE_INIT;
static pa_tls *monitor_tls;
-static pa_once_t monitor_tls_once = PA_ONCE_INIT;
static void thread_tls_once_func(void) {
thread_tls = pa_tls_new(NULL);
@@ -64,7 +63,7 @@ static DWORD WINAPI internal_thread_func(LPVOID param) {
pa_thread *t = param;
assert(t);
- pa_once(&thread_tls_once, thread_tls_once_func);
+ pa_run_once(&thread_tls_once, thread_tls_once_func);
pa_tls_set(thread_tls, t);
t->thread_func(t->userdata);
@@ -120,7 +119,7 @@ int pa_thread_join(pa_thread *t) {
}
pa_thread* pa_thread_self(void) {
- pa_once(&thread_tls_once, thread_tls_once_func);
+ pa_run_once(&thread_tls_once, thread_tls_once_func);
return pa_tls_get(thread_tls);
}
@@ -128,12 +127,6 @@ void pa_thread_yield(void) {
Sleep(0);
}
-static void monitor_tls_once_func(void) {
- monitor_tls = pa_tls_new(NULL);
- assert(monitor_tls);
- pa_tls_set(monitor_tls, NULL);
-}
-
static DWORD WINAPI monitor_thread_func(LPVOID param) {
struct pa_tls_monitor *m = param;
assert(m);
@@ -189,7 +182,11 @@ void *pa_tls_set(pa_tls *t, void *userdata) {
if (t->free_func) {
struct pa_tls_monitor *m;
- pa_once(&monitor_tls_once, monitor_tls_once_func);
+ PA_ONCE_BEGIN {
+ monitor_tls = pa_tls_new(NULL);
+ assert(monitor_tls);
+ pa_tls_set(monitor_tls, NULL);
+ } PA_ONCE_END;
m = pa_tls_get(monitor_tls);
if (!m) {
diff --git a/src/pulsecore/thread.h b/src/pulsecore/thread.h
index d08990a2..f3aca13e 100644
--- a/src/pulsecore/thread.h
+++ b/src/pulsecore/thread.h
@@ -1,21 +1,22 @@
#ifndef foopulsethreadhfoo
#define foopulsethreadhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
+ Copyright 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
@@ -23,6 +24,11 @@
***/
#include <pulse/def.h>
+#include <pulsecore/once.h>
+
+#ifndef PACKAGE
+#error "Please include config.h before including this file!"
+#endif
typedef struct pa_thread pa_thread;
@@ -45,4 +51,60 @@ void pa_tls_free(pa_tls *t);
void * pa_tls_get(pa_tls *t);
void *pa_tls_set(pa_tls *t, void *userdata);
+#define PA_STATIC_TLS_DECLARE(name, free_cb) \
+ static struct { \
+ pa_once once; \
+ pa_tls *tls; \
+ } name##_tls = { \
+ .once = PA_ONCE_INIT, \
+ .tls = NULL \
+ }; \
+ static void name##_tls_init(void) { \
+ name##_tls.tls = pa_tls_new(free_cb); \
+ } \
+ static inline pa_tls* name##_tls_obj(void) { \
+ pa_run_once(&name##_tls.once, name##_tls_init); \
+ return name##_tls.tls; \
+ } \
+ static void name##_tls_destructor(void) PA_GCC_DESTRUCTOR; \
+ static void name##_tls_destructor(void) { \
+ static void (*_free_cb)(void*) = free_cb; \
+ if (!name##_tls.tls) \
+ return; \
+ if (_free_cb) { \
+ void *p; \
+ if ((p = pa_tls_get(name##_tls.tls))) \
+ _free_cb(p); \
+ } \
+ pa_tls_free(name##_tls.tls); \
+ } \
+ static inline void* name##_tls_get(void) { \
+ return pa_tls_get(name##_tls_obj()); \
+ } \
+ static inline void* name##_tls_set(void *p) { \
+ return pa_tls_set(name##_tls_obj(), p); \
+ } \
+ struct __stupid_useless_struct_to_allow_trailing_semicolon
+
+#ifdef HAVE_TLS_BUILTIN
+/* An optimized version of the above that requires no dynamic
+ * allocation if the compiler supports __thread */
+#define PA_STATIC_TLS_DECLARE_NO_FREE(name) \
+ static __thread void *name##_tls = NULL; \
+ static inline void* name##_tls_get(void) { \
+ return name##_tls; \
+ } \
+ static inline void* name##_tls_set(void *p) { \
+ void *r = name##_tls; \
+ name##_tls = p; \
+ return r; \
+ } \
+ struct __stupid_useless_struct_to_allow_trailing_semicolon
+#else
+#define PA_STATIC_TLS_DECLARE_NO_FREE(name) PA_STATIC_TLS_DECLARE(name, NULL)
+#endif
+
+#define PA_STATIC_TLS_GET(name) (name##_tls_get())
+#define PA_STATIC_TLS_SET(name, p) (name##_tls_set(p))
+
#endif
diff --git a/src/pulsecore/time-smoother.c b/src/pulsecore/time-smoother.c
new file mode 100644
index 00000000..fe5a4f18
--- /dev/null
+++ b/src/pulsecore/time-smoother.c
@@ -0,0 +1,459 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2007 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+
+#include <pulse/sample.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/macro.h>
+
+#include "time-smoother.h"
+
+#define HISTORY_MAX 64
+
+/*
+ * Implementation of a time smoothing algorithm to synchronize remote
+ * clocks to a local one. Evens out noise, adjusts to clock skew and
+ * allows cheap estimations of the remote time while clock updates may
+ * be seldom and recieved in non-equidistant intervals.
+ *
+ * Basically, we estimate the gradient of received clock samples in a
+ * certain history window (of size 'history_time') with linear
+ * regression. With that info we estimate the remote time in
+ * 'adjust_time' ahead and smoothen our current estimation function
+ * towards that point with a 3rd order polynomial interpolation with
+ * fitting derivatives. (more or less a b-spline)
+ *
+ * The larger 'history_time' is chosen the better we will surpress
+ * noise -- but we'll adjust to clock skew slower..
+ *
+ * The larger 'adjust_time' is chosen the smoother our estimation
+ * function will be -- but we'll adjust to clock skew slower, too.
+ *
+ * If 'monotonic' is TRUE the resulting estimation function is
+ * guaranteed to be monotonic.
+ */
+
+struct pa_smoother {
+ pa_usec_t adjust_time, history_time;
+
+ pa_usec_t time_offset;
+
+ pa_usec_t px, py; /* Point p, where we want to reach stability */
+ double dp; /* Gradient we want at point p */
+
+ pa_usec_t ex, ey; /* Point e, which we estimated before and need to smooth to */
+ double de; /* Gradient we estimated for point e */
+ pa_usec_t ry; /* The original y value for ex */
+
+ /* History of last measurements */
+ pa_usec_t history_x[HISTORY_MAX], history_y[HISTORY_MAX];
+ unsigned history_idx, n_history;
+
+ /* To even out for monotonicity */
+ pa_usec_t last_y, last_x;
+
+ /* Cached parameters for our interpolation polynomial y=ax^3+b^2+cx */
+ double a, b, c;
+ pa_bool_t abc_valid;
+
+ pa_bool_t monotonic:1;
+ pa_bool_t paused:1;
+
+ pa_usec_t pause_time;
+
+ unsigned min_history;
+};
+
+pa_smoother* pa_smoother_new(pa_usec_t adjust_time, pa_usec_t history_time, pa_bool_t monotonic, unsigned min_history) {
+ pa_smoother *s;
+
+ pa_assert(adjust_time > 0);
+ pa_assert(history_time > 0);
+ pa_assert(min_history >= 2);
+ pa_assert(min_history <= HISTORY_MAX);
+
+ s = pa_xnew(pa_smoother, 1);
+ s->adjust_time = adjust_time;
+ s->history_time = history_time;
+ s->time_offset = 0;
+ s->monotonic = monotonic;
+
+ s->px = s->py = 0;
+ s->dp = 1;
+
+ s->ex = s->ey = s->ry = 0;
+ s->de = 1;
+
+ s->history_idx = 0;
+ s->n_history = 0;
+
+ s->last_y = s->last_x = 0;
+
+ s->abc_valid = FALSE;
+
+ s->paused = FALSE;
+
+ s->min_history = min_history;
+
+ return s;
+}
+
+void pa_smoother_free(pa_smoother* s) {
+ pa_assert(s);
+
+ pa_xfree(s);
+}
+
+#define REDUCE(x) \
+ do { \
+ x = (x) % HISTORY_MAX; \
+ } while(FALSE)
+
+#define REDUCE_INC(x) \
+ do { \
+ x = ((x)+1) % HISTORY_MAX; \
+ } while(FALSE)
+
+
+static void drop_old(pa_smoother *s, pa_usec_t x) {
+
+ /* Drop items from history which are too old, but make sure to
+ * always keep min_history in the history */
+
+ while (s->n_history > s->min_history) {
+
+ if (s->history_x[s->history_idx] + s->history_time >= x)
+ /* This item is still valid, and thus all following ones
+ * are too, so let's quit this loop */
+ break;
+
+ /* Item is too old, let's drop it */
+ REDUCE_INC(s->history_idx);
+
+ s->n_history --;
+ }
+}
+
+static void add_to_history(pa_smoother *s, pa_usec_t x, pa_usec_t y) {
+ unsigned j, i;
+ pa_assert(s);
+
+ /* First try to update an existing history entry */
+ i = s->history_idx;
+ for (j = s->n_history; j > 0; j--) {
+
+ if (s->history_x[i] == x) {
+ s->history_y[i] = y;
+ return;
+ }
+
+ REDUCE_INC(i);
+ }
+
+ /* Drop old entries */
+ drop_old(s, x);
+
+ /* Calculate position for new entry */
+ j = s->history_idx + s->n_history;
+ REDUCE(j);
+
+ /* Fill in entry */
+ s->history_x[j] = x;
+ s->history_y[j] = y;
+
+ /* Adjust counter */
+ s->n_history ++;
+
+ /* And make sure we don't store more entries than fit in */
+ if (s->n_history > HISTORY_MAX) {
+ s->history_idx += s->n_history - HISTORY_MAX;
+ REDUCE(s->history_idx);
+ s->n_history = HISTORY_MAX;
+ }
+}
+
+static double avg_gradient(pa_smoother *s, pa_usec_t x) {
+ unsigned i, j, c = 0;
+ int64_t ax = 0, ay = 0, k, t;
+ double r;
+
+ /* Too few measurements, assume gradient of 1 */
+ if (s->n_history < s->min_history)
+ return 1;
+
+ /* First, calculate average of all measurements */
+ i = s->history_idx;
+ for (j = s->n_history; j > 0; j--) {
+
+ ax += s->history_x[i];
+ ay += s->history_y[i];
+ c++;
+
+ REDUCE_INC(i);
+ }
+
+ pa_assert(c >= s->min_history);
+ ax /= c;
+ ay /= c;
+
+ /* Now, do linear regression */
+ k = t = 0;
+
+ i = s->history_idx;
+ for (j = s->n_history; j > 0; j--) {
+ int64_t dx, dy;
+
+ dx = (int64_t) s->history_x[i] - ax;
+ dy = (int64_t) s->history_y[i] - ay;
+
+ k += dx*dy;
+ t += dx*dx;
+
+ REDUCE_INC(i);
+ }
+
+ r = (double) k / t;
+
+ return (s->monotonic && r < 0) ? 0 : r;
+}
+
+static void calc_abc(pa_smoother *s) {
+ pa_usec_t ex, ey, px, py;
+ int64_t kx, ky;
+ double de, dp;
+
+ pa_assert(s);
+
+ if (s->abc_valid)
+ return;
+
+ /* We have two points: (ex|ey) and (px|py) with two gradients at
+ * these points de and dp. We do a polynomial
+ * interpolation of degree 3 with these 6 values */
+
+ ex = s->ex; ey = s->ey;
+ px = s->px; py = s->py;
+ de = s->de; dp = s->dp;
+
+ pa_assert(ex < px);
+
+ /* To increase the dynamic range and symplify calculation, we
+ * move these values to the origin */
+ kx = (int64_t) px - (int64_t) ex;
+ ky = (int64_t) py - (int64_t) ey;
+
+ /* Calculate a, b, c for y=ax^3+bx^2+cx */
+ s->c = de;
+ s->b = (((double) (3*ky)/kx - dp - 2*de)) / kx;
+ s->a = (dp/kx - 2*s->b - de/kx) / (3*kx);
+
+ s->abc_valid = TRUE;
+}
+
+static void estimate(pa_smoother *s, pa_usec_t x, pa_usec_t *y, double *deriv) {
+ pa_assert(s);
+ pa_assert(y);
+
+ if (x >= s->px) {
+ int64_t t;
+
+ /* The requested point is right of the point where we wanted
+ * to be on track again, thus just linearly estimate */
+
+ t = (int64_t) s->py + (int64_t) (s->dp * (x - s->px));
+
+ if (t < 0)
+ t = 0;
+
+ *y = (pa_usec_t) t;
+
+ if (deriv)
+ *deriv = s->dp;
+
+ } else {
+
+ /* Ok, we're not yet on track, thus let's interpolate, and
+ * make sure that the first derivative is smooth */
+
+ calc_abc(s);
+
+ /* Move to origin */
+ x -= s->ex;
+
+ /* Horner scheme */
+ *y = (pa_usec_t) ((double) x * (s->c + (double) x * (s->b + (double) x * s->a)));
+
+ /* Move back from origin */
+ *y += s->ey;
+
+ /* Horner scheme */
+ if (deriv)
+ *deriv = s->c + ((double) x * (s->b*2 + (double) x * s->a*3));
+ }
+
+ /* Guarantee monotonicity */
+ if (s->monotonic) {
+
+ if (deriv && *deriv < 0)
+ *deriv = 0;
+ }
+}
+
+void pa_smoother_put(pa_smoother *s, pa_usec_t x, pa_usec_t y) {
+ pa_usec_t ney;
+ double nde;
+ pa_bool_t is_new;
+
+ pa_assert(s);
+
+ /* Fix up x value */
+ if (s->paused)
+ x = s->pause_time;
+
+ x = PA_LIKELY(x >= s->time_offset) ? x - s->time_offset : 0;
+
+ is_new = x >= s->ex;
+
+ if (is_new) {
+ /* First, we calculate the position we'd estimate for x, so that
+ * we can adjust our position smoothly from this one */
+ estimate(s, x, &ney, &nde);
+ s->ex = x; s->ey = ney; s->de = nde;
+
+ s->ry = y;
+ }
+
+ /* Then, we add the new measurement to our history */
+ add_to_history(s, x, y);
+
+ /* And determine the average gradient of the history */
+ s->dp = avg_gradient(s, x);
+
+ /* And calculate when we want to be on track again */
+ s->px = s->ex + s->adjust_time;
+ s->py = s->ry + s->dp *s->adjust_time;
+
+ s->abc_valid = FALSE;
+
+/* pa_log_debug("put(%llu | %llu) = %llu", (unsigned long long) (x + s->time_offset), (unsigned long long) x, (unsigned long long) y); */
+}
+
+pa_usec_t pa_smoother_get(pa_smoother *s, pa_usec_t x) {
+ pa_usec_t y;
+
+ pa_assert(s);
+
+ /* Fix up x value */
+ if (s->paused)
+ x = s->pause_time;
+
+ x = PA_LIKELY(x >= s->time_offset) ? x - s->time_offset : 0;
+
+ estimate(s, x, &y, NULL);
+
+ if (s->monotonic) {
+
+ /* Make sure the querier doesn't jump forth and back. */
+ pa_assert(x >= s->last_x);
+ s->last_x = x;
+
+ if (y < s->last_y)
+ y = s->last_y;
+ else
+ s->last_y = y;
+ }
+
+/* pa_log_debug("get(%llu | %llu) = %llu", (unsigned long long) (x + s->time_offset), (unsigned long long) x, (unsigned long long) y); */
+
+ return y;
+}
+
+void pa_smoother_set_time_offset(pa_smoother *s, pa_usec_t offset) {
+ pa_assert(s);
+
+ s->time_offset = offset;
+
+/* pa_log_debug("offset(%llu)", (unsigned long long) offset); */
+}
+
+void pa_smoother_pause(pa_smoother *s, pa_usec_t x) {
+ pa_assert(s);
+
+ if (s->paused)
+ return;
+
+/* pa_log_debug("pause(%llu)", (unsigned long long) x); */
+
+ s->paused = TRUE;
+ s->pause_time = x;
+}
+
+void pa_smoother_resume(pa_smoother *s, pa_usec_t x) {
+ pa_assert(s);
+
+ if (!s->paused)
+ return;
+
+ pa_assert(x >= s->pause_time);
+
+/* pa_log_debug("resume(%llu)", (unsigned long long) x); */
+
+ s->paused = FALSE;
+ s->time_offset += x - s->pause_time;
+}
+
+pa_usec_t pa_smoother_translate(pa_smoother *s, pa_usec_t x, pa_usec_t y_delay) {
+ pa_usec_t ney;
+ double nde;
+
+ pa_assert(s);
+
+ /* Fix up x value */
+ if (s->paused)
+ x = s->pause_time;
+
+ x = PA_LIKELY(x >= s->time_offset) ? x - s->time_offset : 0;
+
+ estimate(s, x, &ney, &nde);
+
+ /* Play safe and take the larger gradient, so that we wakeup
+ * earlier when this is used for sleeping */
+ if (s->dp > nde)
+ nde = s->dp;
+
+/* pa_log_debug("translate(%llu) = %llu (%0.2f)", (unsigned long long) y_delay, (unsigned long long) ((double) y_delay / nde), nde); */
+
+ return (pa_usec_t) ((double) y_delay / nde);
+}
+
+void pa_smoother_reset(pa_smoother *s) {
+ pa_assert(s);
+
+ s->n_history = 0;
+ s->abc_valid = FALSE;
+
+}
diff --git a/src/pulsecore/time-smoother.h b/src/pulsecore/time-smoother.h
new file mode 100644
index 00000000..2051e640
--- /dev/null
+++ b/src/pulsecore/time-smoother.h
@@ -0,0 +1,49 @@
+#ifndef foopulsetimesmootherhfoo
+#define foopulsetimesmootherhfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2007 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <pulsecore/macro.h>
+#include <pulse/sample.h>
+
+typedef struct pa_smoother pa_smoother;
+
+pa_smoother* pa_smoother_new(pa_usec_t x_adjust_time, pa_usec_t x_history_time, pa_bool_t monotonic, unsigned min_history);
+void pa_smoother_free(pa_smoother* s);
+
+/* Adds a new value to our dataset. x = local/system time, y = remote time */
+void pa_smoother_put(pa_smoother *s, pa_usec_t x, pa_usec_t y);
+
+/* Returns an interpolated value based on the dataset. x = local/system time, return value = remote time */
+pa_usec_t pa_smoother_get(pa_smoother *s, pa_usec_t x);
+
+/* Translates a time span from the remote time domain to the local one. x = local/system time when to estimate, y_delay = remote time span */
+pa_usec_t pa_smoother_translate(pa_smoother *s, pa_usec_t x, pa_usec_t y_delay);
+
+void pa_smoother_set_time_offset(pa_smoother *s, pa_usec_t x_offset);
+
+void pa_smoother_pause(pa_smoother *s, pa_usec_t x);
+void pa_smoother_resume(pa_smoother *s, pa_usec_t x);
+
+void pa_smoother_reset(pa_smoother *s);
+
+#endif
diff --git a/src/pulsecore/tokenizer.c b/src/pulsecore/tokenizer.c
index e799c1e6..d1e0836b 100644
--- a/src/pulsecore/tokenizer.c
+++ b/src/pulsecore/tokenizer.c
@@ -1,18 +1,18 @@
-/* $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
@@ -24,20 +24,16 @@
#endif
#include <string.h>
-#include <assert.h>
#include <stdlib.h>
#include <pulse/xmalloc.h>
+#include <pulse/gccmacro.h>
#include <pulsecore/dynarray.h>
-#include <pulsecore/gccmacro.h>
+#include <pulsecore/macro.h>
#include "tokenizer.h"
-struct pa_tokenizer {
- pa_dynarray *dynarray;
-};
-
static void token_free(void *p, PA_GCC_UNUSED void *userdata) {
pa_xfree(p);
}
@@ -46,7 +42,9 @@ static void parse(pa_dynarray*a, const char *s, unsigned args) {
int infty = 0;
const char delimiter[] = " \t\n\r";
const char *p;
- assert(a && s);
+
+ pa_assert(a);
+ pa_assert(s);
if (args == 0)
infty = 1;
@@ -68,23 +66,23 @@ static void parse(pa_dynarray*a, const char *s, unsigned args) {
}
pa_tokenizer* pa_tokenizer_new(const char *s, unsigned args) {
- pa_tokenizer *t;
-
- t = pa_xmalloc(sizeof(pa_tokenizer));
- t->dynarray = pa_dynarray_new();
- assert(t->dynarray);
-
- parse(t->dynarray, s, args);
- return t;
+ pa_dynarray *a;
+
+ a = pa_dynarray_new();
+ parse(a, s, args);
+ return (pa_tokenizer*) a;
}
void pa_tokenizer_free(pa_tokenizer *t) {
- assert(t);
- pa_dynarray_free(t->dynarray, token_free, NULL);
- pa_xfree(t);
+ pa_dynarray *a = (pa_dynarray*) t;
+
+ pa_assert(a);
+ pa_dynarray_free(a, token_free, NULL);
}
const char *pa_tokenizer_get(pa_tokenizer *t, unsigned i) {
- assert(t);
- return pa_dynarray_get(t->dynarray, i);
+ pa_dynarray *a = (pa_dynarray*) t;
+
+ pa_assert(a);
+ return pa_dynarray_get(a, i);
}
diff --git a/src/pulsecore/tokenizer.h b/src/pulsecore/tokenizer.h
index b9a5c55b..d51cd73e 100644
--- a/src/pulsecore/tokenizer.h
+++ b/src/pulsecore/tokenizer.h
@@ -1,21 +1,21 @@
#ifndef footokenizerhfoo
#define footokenizerhfoo
-/* $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
diff --git a/src/pulsecore/winsock.h b/src/pulsecore/winsock.h
index ae868b38..0352bf4d 100644
--- a/src/pulsecore/winsock.h
+++ b/src/pulsecore/winsock.h
@@ -15,6 +15,8 @@
#define EHOSTUNREACH WSAEHOSTUNREACH
#define EWOULDBLOCK WSAEWOULDBLOCK
+typedef long suseconds_t;
+
#endif
#ifdef HAVE_WS2TCPIP_H
diff --git a/src/pulsecore/x11prop.c b/src/pulsecore/x11prop.c
index dd4ff99e..9e75f63a 100644
--- a/src/pulsecore/x11prop.c
+++ b/src/pulsecore/x11prop.c
@@ -1,8 +1,8 @@
-/* $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,
@@ -30,7 +30,6 @@
#include "x11prop.h"
-
void pa_x11_set_prop(Display *d, const char *name, const char *data) {
Atom a = XInternAtom(d, name, False);
XChangeProperty(d, RootWindow(d, 0), a, XA_STRING, 8, PropModeReplace, (const unsigned char*) data, strlen(data)+1);
@@ -48,7 +47,7 @@ char* pa_x11_get_prop(Display *d, const char *name, char *p, size_t l) {
unsigned long nbytes_after;
unsigned char *prop = NULL;
char *ret = NULL;
-
+
Atom a = XInternAtom(d, name, False);
if (XGetWindowProperty(d, RootWindow(d, 0), a, 0, (l+2)/4, False, XA_STRING, &actual_type, &actual_format, &nitems, &nbytes_after, &prop) != Success)
goto finish;
@@ -65,6 +64,6 @@ finish:
if (prop)
XFree(prop);
-
+
return ret;
}
diff --git a/src/pulsecore/x11prop.h b/src/pulsecore/x11prop.h
index bd24951a..c5998d3e 100644
--- a/src/pulsecore/x11prop.h
+++ b/src/pulsecore/x11prop.h
@@ -1,11 +1,11 @@
#ifndef foox11prophfoo
#define foox11prophfoo
-/* $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,
diff --git a/src/pulsecore/x11wrap.c b/src/pulsecore/x11wrap.c
index cc993e78..00b6a157 100644
--- a/src/pulsecore/x11wrap.c
+++ b/src/pulsecore/x11wrap.c
@@ -1,25 +1,28 @@
-/* $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.
***/
-#include <assert.h>
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
#include <stdio.h>
#include <pulse/xmalloc.h>
@@ -27,6 +30,8 @@
#include <pulsecore/llist.h>
#include <pulsecore/log.h>
#include <pulsecore/props.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
#include "x11wrap.h"
@@ -40,9 +45,9 @@ struct pa_x11_internal {
};
struct pa_x11_wrapper {
+ PA_REFCNT_DECLARE;
pa_core *core;
- int ref;
-
+
char *property_name;
Display *display;
@@ -56,48 +61,71 @@ struct pa_x11_wrapper {
struct pa_x11_client {
PA_LLIST_FIELDS(pa_x11_client);
pa_x11_wrapper *wrapper;
- int (*callback)(pa_x11_wrapper *w, XEvent *e, void *userdata);
+ pa_x11_event_cb_t event_cb;
+ pa_x11_kill_cb_t kill_cb;
void *userdata;
};
/* Dispatch all pending X11 events */
static void work(pa_x11_wrapper *w) {
- assert(w && w->ref >= 1);
-
+ pa_assert(w);
+ pa_assert(PA_REFCNT_VALUE(w) >= 1);
+
+ pa_x11_wrapper_ref(w);
+
while (XPending(w->display)) {
- pa_x11_client *c;
+ pa_x11_client *c, *n;
XEvent e;
XNextEvent(w->display, &e);
- for (c = w->clients; c; c = c->next) {
- assert(c->callback);
- if (c->callback(w, &e, c->userdata) != 0)
- break;
+ for (c = w->clients; c; c = n) {
+ n = c->next;
+
+ if (c->event_cb)
+ if (c->event_cb(w, &e, c->userdata) != 0)
+ break;
}
}
+
+ pa_x11_wrapper_unref(w);
}
/* IO notification event for the X11 display connection */
static void display_io_event(pa_mainloop_api *m, pa_io_event *e, int fd, PA_GCC_UNUSED pa_io_event_flags_t f, void *userdata) {
pa_x11_wrapper *w = userdata;
- assert(m && e && fd >= 0 && w && w->ref >= 1);
+
+ pa_assert(m);
+ pa_assert(e);
+ pa_assert(fd >= 0);
+ pa_assert(w);
+ pa_assert(PA_REFCNT_VALUE(w) >= 1);
+
work(w);
}
/* Deferred notification event. Called once each main loop iteration */
static void defer_event(pa_mainloop_api *m, pa_defer_event *e, void *userdata) {
pa_x11_wrapper *w = userdata;
- assert(m && e && w && w->ref >= 1);
+
+ pa_assert(m);
+ pa_assert(e);
+ pa_assert(w);
+ pa_assert(PA_REFCNT_VALUE(w) >= 1);
m->defer_enable(e, 0);
-
+
work(w);
}
/* IO notification event for X11 internal connections */
static void internal_io_event(pa_mainloop_api *m, pa_io_event *e, int fd, PA_GCC_UNUSED pa_io_event_flags_t f, void *userdata) {
pa_x11_wrapper *w = userdata;
- assert(m && e && fd >= 0 && w && w->ref >= 1);
+
+ pa_assert(m);
+ pa_assert(e);
+ pa_assert(fd >= 0);
+ pa_assert(w);
+ pa_assert(PA_REFCNT_VALUE(w) >= 1);
XProcessInternalConnection(w->display, fd);
@@ -107,10 +135,9 @@ static void internal_io_event(pa_mainloop_api *m, pa_io_event *e, int fd, PA_GCC
/* Add a new IO source for the specified X11 internal connection */
static pa_x11_internal* x11_internal_add(pa_x11_wrapper *w, int fd) {
pa_x11_internal *i;
- assert(fd >= 0);
+ pa_assert(fd >= 0);
- i = pa_xmalloc(sizeof(pa_x11_internal));
- assert(i);
+ i = pa_xnew(pa_x11_internal, 1);
i->wrapper = w;
i->io_event = w->core->mainloop->io_new(w->core->mainloop, fd, PA_IO_EVENT_INPUT, internal_io_event, w);
i->fd = fd;
@@ -121,7 +148,7 @@ static pa_x11_internal* x11_internal_add(pa_x11_wrapper *w, int fd) {
/* Remove an IO source for an X11 internal connection */
static void x11_internal_remove(pa_x11_wrapper *w, pa_x11_internal *i) {
- assert(i);
+ pa_assert(i);
PA_LLIST_REMOVE(pa_x11_internal, w->internals, i);
w->core->mainloop->io_free(i->io_event);
@@ -131,7 +158,10 @@ static void x11_internal_remove(pa_x11_wrapper *w, pa_x11_internal *i) {
/* Implementation of XConnectionWatchProc */
static void x11_watch(Display *display, XPointer userdata, int fd, Bool opening, XPointer *watch_data) {
pa_x11_wrapper *w = (pa_x11_wrapper*) userdata;
- assert(display && w && fd >= 0);
+
+ pa_assert(display);
+ pa_assert(w);
+ pa_assert(fd >= 0);
if (opening)
*watch_data = (XPointer) x11_internal_add(w, fd);
@@ -142,19 +172,18 @@ static void x11_watch(Display *display, XPointer userdata, int fd, Bool opening,
static pa_x11_wrapper* x11_wrapper_new(pa_core *c, const char *name, const char *t) {
pa_x11_wrapper*w;
Display *d;
- int r;
if (!(d = XOpenDisplay(name))) {
pa_log("XOpenDisplay() failed");
return NULL;
}
- w = pa_xmalloc(sizeof(pa_x11_wrapper));
+ w = pa_xnew(pa_x11_wrapper, 1);
+ PA_REFCNT_INIT(w);
w->core = c;
- w->ref = 1;
w->property_name = pa_xstrdup(t);
w->display = d;
-
+
PA_LLIST_HEAD_INIT(pa_x11_client, w->clients);
PA_LLIST_HEAD_INIT(pa_x11_internal, w->internals);
@@ -162,31 +191,28 @@ static pa_x11_wrapper* x11_wrapper_new(pa_core *c, const char *name, const char
w->io_event = c->mainloop->io_new(c->mainloop, ConnectionNumber(d), PA_IO_EVENT_INPUT, display_io_event, w);
XAddConnectionWatch(d, x11_watch, (XPointer) w);
-
- r = pa_property_set(c, w->property_name, w);
- assert(r >= 0);
-
+
+ pa_assert_se(pa_property_set(c, w->property_name, w) >= 0);
+
return w;
}
static void x11_wrapper_free(pa_x11_wrapper*w) {
- int r;
- assert(w);
+ pa_assert(w);
- r = pa_property_remove(w->core, w->property_name);
- assert(r >= 0);
+ pa_assert_se(pa_property_remove(w->core, w->property_name) >= 0);
- assert(!w->clients);
+ pa_assert(!w->clients);
XRemoveConnectionWatch(w->display, x11_watch, (XPointer) w);
XCloseDisplay(w->display);
-
+
w->core->mainloop->io_free(w->io_event);
w->core->mainloop->defer_free(w->defer_event);
while (w->internals)
x11_internal_remove(w, w->internals);
-
+
pa_xfree(w->property_name);
pa_xfree(w);
}
@@ -194,9 +220,10 @@ static void x11_wrapper_free(pa_x11_wrapper*w) {
pa_x11_wrapper* pa_x11_wrapper_get(pa_core *c, const char *name) {
char t[256];
pa_x11_wrapper *w;
- assert(c);
-
- snprintf(t, sizeof(t), "x11-wrapper%s%s", name ? "-" : "", name ? name : "");
+
+ pa_core_assert_ref(c);
+
+ pa_snprintf(t, sizeof(t), "x11-wrapper%s%s", name ? "-" : "", name ? name : "");
if ((w = pa_property_get(c, t)))
return pa_x11_wrapper_ref(w);
@@ -204,34 +231,58 @@ pa_x11_wrapper* pa_x11_wrapper_get(pa_core *c, const char *name) {
}
pa_x11_wrapper* pa_x11_wrapper_ref(pa_x11_wrapper *w) {
- assert(w && w->ref >= 1);
- w->ref++;
+ pa_assert(w);
+ pa_assert(PA_REFCNT_VALUE(w) >= 1);
+
+ PA_REFCNT_INC(w);
return w;
}
void pa_x11_wrapper_unref(pa_x11_wrapper* w) {
- assert(w && w->ref >= 1);
+ pa_assert(w);
+ pa_assert(PA_REFCNT_VALUE(w) >= 1);
- if (!(--w->ref))
+ if (PA_REFCNT_DEC(w) <= 0)
x11_wrapper_free(w);
}
Display *pa_x11_wrapper_get_display(pa_x11_wrapper *w) {
- assert(w && w->ref >= 1);
+ pa_assert(w);
+ pa_assert(PA_REFCNT_VALUE(w) >= 1);
/* Somebody is using us, schedule a output buffer flush */
w->core->mainloop->defer_enable(w->defer_event, 1);
-
+
return w->display;
}
-pa_x11_client* pa_x11_client_new(pa_x11_wrapper *w, int (*cb)(pa_x11_wrapper *w, XEvent *e, void *userdata), void *userdata) {
+void pa_x11_wrapper_kill(pa_x11_wrapper *w) {
+ pa_x11_client *c, *n;
+
+ pa_assert(w);
+
+ pa_x11_wrapper_ref(w);
+
+ for (c = w->clients; c; c = n) {
+ n = c->next;
+
+ if (c->kill_cb)
+ c->kill_cb(w, c->userdata);
+ }
+
+ pa_x11_wrapper_unref(w);
+}
+
+pa_x11_client* pa_x11_client_new(pa_x11_wrapper *w, pa_x11_event_cb_t event_cb, pa_x11_kill_cb_t kill_cb, void *userdata) {
pa_x11_client *c;
- assert(w && w->ref >= 1);
- c = pa_xmalloc(sizeof(pa_x11_client));
+ pa_assert(w);
+ pa_assert(PA_REFCNT_VALUE(w) >= 1);
+
+ c = pa_xnew(pa_x11_client, 1);
c->wrapper = w;
- c->callback = cb;
+ c->event_cb = event_cb;
+ c->kill_cb = kill_cb;
c->userdata = userdata;
PA_LLIST_PREPEND(pa_x11_client, w->clients, c);
@@ -240,7 +291,9 @@ pa_x11_client* pa_x11_client_new(pa_x11_wrapper *w, int (*cb)(pa_x11_wrapper *w,
}
void pa_x11_client_free(pa_x11_client *c) {
- assert(c && c->wrapper && c->wrapper->ref >= 1);
+ pa_assert(c);
+ pa_assert(c->wrapper);
+ pa_assert(PA_REFCNT_VALUE(c->wrapper) >= 1);
PA_LLIST_REMOVE(pa_x11_client, c->wrapper->clients, c);
pa_xfree(c);
diff --git a/src/pulsecore/x11wrap.h b/src/pulsecore/x11wrap.h
index fcdd9f6c..badc3a1f 100644
--- a/src/pulsecore/x11wrap.h
+++ b/src/pulsecore/x11wrap.h
@@ -1,21 +1,21 @@
#ifndef foox11wraphfoo
#define foox11wraphfoo
-/* $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
@@ -28,6 +28,11 @@
typedef struct pa_x11_wrapper pa_x11_wrapper;
+typedef struct pa_x11_client pa_x11_client;
+
+typedef int (*pa_x11_event_cb_t)(pa_x11_wrapper *w, XEvent *e, void *userdata);
+typedef void (*pa_x11_kill_cb_t)(pa_x11_wrapper *w, void *userdata);
+
/* Return the X11 wrapper for this core. In case no wrapper was
existant before, allocate a new one */
pa_x11_wrapper* pa_x11_wrapper_get(pa_core *c, const char *name);
@@ -41,10 +46,11 @@ void pa_x11_wrapper_unref(pa_x11_wrapper* w);
/* Return the X11 display object for this connection */
Display *pa_x11_wrapper_get_display(pa_x11_wrapper *w);
-typedef struct pa_x11_client pa_x11_client;
+/* Kill the connection to the X11 display */
+void pa_x11_wrapper_kill(pa_x11_wrapper *w);
/* Register an X11 client, that is called for each X11 event */
-pa_x11_client* pa_x11_client_new(pa_x11_wrapper *w, int (*cb)(pa_x11_wrapper *w, XEvent *e, void *userdata), void *userdata);
+pa_x11_client* pa_x11_client_new(pa_x11_wrapper *w, pa_x11_event_cb_t event_cb, pa_x11_kill_cb_t kill_cb, void *userdata);
/* Free an X11 client object */
void pa_x11_client_free(pa_x11_client *c);
diff --git a/src/tests/asyncmsgq-test.c b/src/tests/asyncmsgq-test.c
new file mode 100644
index 00000000..08ad3dd4
--- /dev/null
+++ b/src/tests/asyncmsgq-test.c
@@ -0,0 +1,108 @@
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 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 <assert.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <pulse/util.h>
+#include <pulse/xmalloc.h>
+#include <pulsecore/asyncmsgq.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+
+enum {
+ OPERATION_A,
+ OPERATION_B,
+ OPERATION_C,
+ QUIT
+};
+
+static void the_thread(void *_q) {
+ pa_asyncmsgq *q = _q;
+ int quit = 0;
+
+ do {
+ int code = 0;
+
+ pa_assert_se(pa_asyncmsgq_get(q, NULL, &code, NULL, NULL, NULL, 1) == 0);
+
+ switch (code) {
+
+ case OPERATION_A:
+ printf("Operation A\n");
+ break;
+
+ case OPERATION_B:
+ printf("Operation B\n");
+ break;
+
+ case OPERATION_C:
+ printf("Operation C\n");
+ break;
+
+ case QUIT:
+ printf("quit\n");
+ quit = 1;
+ break;
+ }
+
+ pa_asyncmsgq_done(q, 0);
+
+ } while (!quit);
+}
+
+int main(int argc, char *argv[]) {
+ pa_asyncmsgq *q;
+ pa_thread *t;
+
+ pa_assert_se(q = pa_asyncmsgq_new(0));
+
+ pa_assert_se(t = pa_thread_new(the_thread, q));
+
+ printf("Operation A post\n");
+ pa_asyncmsgq_post(q, NULL, OPERATION_A, NULL, 0, NULL, NULL);
+
+ pa_thread_yield();
+
+ printf("Operation B post\n");
+ pa_asyncmsgq_post(q, NULL, OPERATION_B, NULL, 0, NULL, NULL);
+
+ pa_thread_yield();
+
+ printf("Operation C send\n");
+ pa_asyncmsgq_send(q, NULL, OPERATION_C, NULL, 0, NULL);
+
+ pa_thread_yield();
+
+ printf("Quit post\n");
+ pa_asyncmsgq_post(q, NULL, QUIT, NULL, 0, NULL, NULL);
+
+ pa_thread_free(t);
+
+ pa_asyncmsgq_unref(q);
+
+ return 0;
+}
diff --git a/src/tests/asyncq-test.c b/src/tests/asyncq-test.c
new file mode 100644
index 00000000..4e8a1207
--- /dev/null
+++ b/src/tests/asyncq-test.c
@@ -0,0 +1,85 @@
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 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 <assert.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <pulse/util.h>
+#include <pulse/xmalloc.h>
+#include <pulsecore/asyncq.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+
+static void producer(void *_q) {
+ pa_asyncq *q = _q;
+ int i;
+
+ for (i = 0; i < 1000; i++) {
+ printf("pushing %i\n", i);
+ pa_asyncq_push(q, PA_UINT_TO_PTR(i+1), 1);
+ }
+
+ pa_asyncq_push(q, PA_UINT_TO_PTR(-1), TRUE);
+ printf("pushed end\n");
+}
+
+static void consumer(void *_q) {
+ pa_asyncq *q = _q;
+ void *p;
+ int i;
+
+ sleep(1);
+
+ for (i = 0;; i++) {
+ p = pa_asyncq_pop(q, TRUE);
+
+ if (p == PA_UINT_TO_PTR(-1))
+ break;
+
+ pa_assert(p == PA_UINT_TO_PTR(i+1));
+
+ printf("popped %i\n", i);
+ }
+
+ printf("popped end\n");
+}
+
+int main(int argc, char *argv[]) {
+ pa_asyncq *q;
+ pa_thread *t1, *t2;
+
+ pa_assert_se(q = pa_asyncq_new(0));
+
+ pa_assert_se(t1 = pa_thread_new(producer, q));
+ pa_assert_se(t2 = pa_thread_new(consumer, q));
+
+ pa_thread_free(t1);
+ pa_thread_free(t2);
+
+ pa_asyncq_free(q, NULL);
+
+ return 0;
+}
diff --git a/src/tests/channelmap-test.c b/src/tests/channelmap-test.c
index 124ce576..9c234602 100644
--- a/src/tests/channelmap-test.c
+++ b/src/tests/channelmap-test.c
@@ -1,33 +1,34 @@
-/* $Id$ */
-
#include <stdio.h>
#include <assert.h>
#include <pulse/channelmap.h>
-#include <pulsecore/gccmacro.h>
+#include <pulse/gccmacro.h>
int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) {
char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
pa_channel_map map, map2;
pa_channel_map_init_auto(&map, 6, PA_CHANNEL_MAP_AIFF);
-
+
fprintf(stderr, "map: <%s>\n", pa_channel_map_snprint(cm, sizeof(cm), &map));
pa_channel_map_init_auto(&map, 6, PA_CHANNEL_MAP_AUX);
-
+
fprintf(stderr, "map: <%s>\n", pa_channel_map_snprint(cm, sizeof(cm), &map));
pa_channel_map_init_auto(&map, 6, PA_CHANNEL_MAP_ALSA);
-
+
fprintf(stderr, "map: <%s>\n", pa_channel_map_snprint(cm, sizeof(cm), &map));
-
+
+ pa_channel_map_init_extend(&map, 14, PA_CHANNEL_MAP_ALSA);
+
+ fprintf(stderr, "map: <%s>\n", pa_channel_map_snprint(cm, sizeof(cm), &map));
+
pa_channel_map_parse(&map2, cm);
assert(pa_channel_map_equal(&map, &map2));
pa_channel_map_parse(&map2, "left,test");
-
return 0;
}
diff --git a/src/tests/close-test.c b/src/tests/close-test.c
new file mode 100644
index 00000000..7a6fec57
--- /dev/null
+++ b/src/tests/close-test.c
@@ -0,0 +1,20 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <pulsecore/core-util.h>
+
+int main(int argc, char *argv[]) {
+
+ open("/dev/null", O_RDONLY);
+ open("/dev/null", O_RDONLY);
+ open("/dev/null", O_RDONLY);
+ open("/dev/null", O_RDONLY);
+
+ pa_close_all(5, -1);
+
+ return 0;
+}
diff --git a/src/tests/cpulimit-test.c b/src/tests/cpulimit-test.c
index 2302a26d..b7145e8a 100644
--- a/src/tests/cpulimit-test.c
+++ b/src/tests/cpulimit-test.c
@@ -1,18 +1,16 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2 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
@@ -30,7 +28,7 @@
#include <signal.h>
#include <pulse/mainloop.h>
-#include <pulsecore/gccmacro.h>
+#include <pulse/gccmacro.h>
#ifdef TEST2
#include <pulse/mainloop-signal.h>
@@ -47,7 +45,7 @@ static time_t start;
static void func(pa_mainloop_api *m, PA_GCC_UNUSED pa_signal_event *e, PA_GCC_UNUSED int sig, PA_GCC_UNUSED void *userdata) {
time_t now;
time(&now);
-
+
if ((now - start) >= 30) {
m->quit(m, 1);
fprintf(stderr, "Test failed\n");
@@ -59,7 +57,7 @@ static void func(pa_mainloop_api *m, PA_GCC_UNUSED pa_signal_event *e, PA_GCC_UN
int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) {
pa_mainloop *m;
-
+
m = pa_mainloop_new();
assert(m);
@@ -77,7 +75,7 @@ int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) {
for (;;) {
time_t now;
time(&now);
-
+
if ((now - start) >= 30) {
fprintf(stderr, "Test failed\n");
break;
@@ -86,7 +84,7 @@ int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) {
#endif
pa_cpu_limit_done();
-
+
pa_mainloop_free(m);
return 0;
diff --git a/src/tests/envelope-test.c b/src/tests/envelope-test.c
new file mode 100644
index 00000000..9f914553
--- /dev/null
+++ b/src/tests/envelope-test.c
@@ -0,0 +1,246 @@
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <pulse/sample.h>
+#include <pulse/volume.h>
+#include <pulse/timeval.h>
+
+#include <pulsecore/envelope.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/endianmacros.h>
+#include <pulsecore/memblock.h>
+#include <pulsecore/sample-util.h>
+
+#include <liboil/liboil.h>
+
+const pa_envelope_def ramp_down = {
+ .n_points = 2,
+ .points_x = { 100*PA_USEC_PER_MSEC, 300*PA_USEC_PER_MSEC },
+ .points_y = {
+ .f = { 1.0, 0.2 },
+ .i = { 0x10000, 0x10000/5 }
+ }
+};
+
+const pa_envelope_def ramp_up = {
+ .n_points = 2,
+ .points_x = { 100*PA_USEC_PER_MSEC, 300*PA_USEC_PER_MSEC },
+ .points_y = {
+ .f = { 0.2, 1.0 },
+ .i = { 0x10000/5, 0x10000 }
+ }
+};
+
+const pa_envelope_def ramp_down2 = {
+ .n_points = 2,
+ .points_x = { 50*PA_USEC_PER_MSEC, 900*PA_USEC_PER_MSEC },
+ .points_y = {
+ .f = { 0.8, 0.7 },
+ .i = { 0x10000*4/5, 0x10000*7/10 }
+ }
+};
+
+const pa_envelope_def ramp_up2 = {
+ .n_points = 2,
+ .points_x = { 50*PA_USEC_PER_MSEC, 900*PA_USEC_PER_MSEC },
+ .points_y = {
+ .f = { 0.7, 0.9 },
+ .i = { 0x10000*7/10, 0x10000*9/10 }
+ }
+};
+
+static void dump_block(const pa_sample_spec *ss, const pa_memchunk *chunk) {
+ void *d;
+ unsigned i;
+
+ static unsigned j = 0;
+
+ d = pa_memblock_acquire(chunk->memblock);
+
+ switch (ss->format) {
+
+ case PA_SAMPLE_U8:
+ case PA_SAMPLE_ULAW:
+ case PA_SAMPLE_ALAW: {
+ uint8_t *u = d;
+
+ for (i = 0; i < chunk->length / pa_frame_size(ss); i++)
+ printf("0x%02x ", *(u++));
+
+ break;
+ }
+
+ case PA_SAMPLE_S16NE:
+ case PA_SAMPLE_S16RE: {
+ int16_t *u = d;
+
+ for (i = 0; i < chunk->length / pa_frame_size(ss); i++)
+ printf("%i\t%i\n", j++, *(u++));
+
+ break;
+ }
+
+ case PA_SAMPLE_S32NE:
+ case PA_SAMPLE_S32RE: {
+ int32_t *u = d;
+
+ for (i = 0; i < chunk->length / pa_frame_size(ss); i++)
+ printf("%i\t%i\n", j++, *(u++));
+
+ break;
+ }
+
+ case PA_SAMPLE_FLOAT32NE:
+ case PA_SAMPLE_FLOAT32RE: {
+ float *u = d;
+
+ for (i = 0; i < chunk->length / pa_frame_size(ss); i++) {
+ printf("%i\t%1.3g\n", j++, PA_MAYBE_FLOAT32_SWAP(ss->format == PA_SAMPLE_FLOAT32RE, *u));
+ u++;
+ }
+
+ break;
+ }
+
+ default:
+ pa_assert_not_reached();
+ }
+
+ printf("\n");
+
+ pa_memblock_release(chunk->memblock);
+}
+
+static pa_memblock * generate_block(pa_mempool *pool, const pa_sample_spec *ss) {
+ pa_memblock *block;
+ void *d;
+ unsigned n_samples;
+
+ block = pa_memblock_new(pool, pa_bytes_per_second(ss));
+ n_samples = pa_memblock_get_length(block) / pa_sample_size(ss);
+
+ d = pa_memblock_acquire(block);
+
+ switch (ss->format) {
+
+ case PA_SAMPLE_S16NE:
+ case PA_SAMPLE_S16RE: {
+ int16_t *i;
+
+ for (i = d; n_samples > 0; n_samples--, i++)
+ *i = 0x7FFF;
+
+ break;
+ }
+
+ case PA_SAMPLE_S32NE:
+ case PA_SAMPLE_S32RE: {
+ int32_t *i;
+
+ for (i = d; n_samples > 0; n_samples--, i++)
+ *i = 0x7FFFFFFF;
+
+ break;
+ }
+
+ case PA_SAMPLE_FLOAT32RE:
+ case PA_SAMPLE_FLOAT32NE: {
+ float *f;
+
+ for (f = d; n_samples > 0; n_samples--, f++)
+ *f = PA_MAYBE_FLOAT32_SWAP(ss->format == PA_SAMPLE_FLOAT32RE, 1.0);
+
+ break;
+ }
+
+ default:
+ pa_assert_not_reached();
+ }
+
+ pa_memblock_release(block);
+ return block;
+}
+
+int main(int argc, char *argv[]) {
+ pa_mempool *pool;
+ pa_memblock *block;
+ pa_memchunk chunk;
+ pa_envelope *envelope;
+ pa_envelope_item *item1, *item2;
+
+ const pa_sample_spec ss = {
+ .format = PA_SAMPLE_S16NE,
+ .channels = 1,
+ .rate = 200
+ };
+
+ const pa_cvolume v = {
+ .channels = 1,
+ .values = { PA_VOLUME_NORM, PA_VOLUME_NORM/2 }
+ };
+
+ oil_init();
+ pa_log_set_maximal_level(PA_LOG_DEBUG);
+
+ pa_assert_se(pool = pa_mempool_new(FALSE));
+ pa_assert_se(envelope = pa_envelope_new(&ss));
+
+ block = generate_block(pool, &ss);
+
+ chunk.memblock = pa_memblock_ref(block);
+ chunk.length = pa_memblock_get_length(block);
+ chunk.index = 0;
+
+ pa_volume_memchunk(&chunk, &ss, &v);
+
+ item1 = pa_envelope_add(envelope, &ramp_down);
+ item2 = pa_envelope_add(envelope, &ramp_down2);
+ pa_envelope_apply(envelope, &chunk);
+ dump_block(&ss, &chunk);
+
+ pa_memblock_unref(chunk.memblock);
+
+ chunk.memblock = pa_memblock_ref(block);
+ chunk.length = pa_memblock_get_length(block);
+ chunk.index = 0;
+
+ item1 = pa_envelope_replace(envelope, item1, &ramp_up);
+ item2 = pa_envelope_replace(envelope, item2, &ramp_up2);
+ pa_envelope_apply(envelope, &chunk);
+ dump_block(&ss, &chunk);
+
+ pa_memblock_unref(chunk.memblock);
+
+ pa_envelope_remove(envelope, item1);
+ pa_envelope_remove(envelope, item2);
+ pa_envelope_free(envelope);
+
+ pa_memblock_unref(block);
+
+ pa_mempool_free(pool);
+
+ return 0;
+}
diff --git a/src/tests/flist-test.c b/src/tests/flist-test.c
index 06d68311..b2c648da 100644
--- a/src/tests/flist-test.c
+++ b/src/tests/flist-test.c
@@ -1,18 +1,16 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 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
@@ -41,7 +39,7 @@ static int quit = 0;
static void spin(void) {
int k;
-
+
/* Spin a little */
k = rand() % 10000;
for (; k > 0; k--)
@@ -100,6 +98,6 @@ int main(int argc, char* argv[]) {
pa_thread_free(threads[i]);
pa_flist_free(flist, pa_xfree);
-
+
return 0;
}
diff --git a/src/tests/get-binary-name-test.c b/src/tests/get-binary-name-test.c
index 0cea2b6d..7c7a8996 100644
--- a/src/tests/get-binary-name-test.c
+++ b/src/tests/get-binary-name-test.c
@@ -1,18 +1,16 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 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
diff --git a/src/tests/hook-list-test.c b/src/tests/hook-list-test.c
index d68d1b7d..60b965cd 100644
--- a/src/tests/hook-list-test.c
+++ b/src/tests/hook-list-test.c
@@ -1,14 +1,16 @@
-/* $Id$ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
#include <pulsecore/hook-list.h>
#include <pulsecore/log.h>
-static pa_hook_result_t func1(const char*hook_data, const char*call_data, const char*slot_data) {
+static pa_hook_result_t func1(const char *hook_data, const char *call_data, const char *slot_data) {
pa_log("(func1) hook=%s call=%s slot=%s", hook_data, call_data, slot_data);
return PA_HOOK_OK;
}
-static pa_hook_result_t func2(const char*hook_data, const char*call_data, const char*slot_data) {
+static pa_hook_result_t func2(const char *hook_data, const char *call_data, const char *slot_data) {
pa_log("(func2) hook=%s call=%s slot=%s", hook_data, call_data, slot_data);
return PA_HOOK_OK;
}
@@ -19,10 +21,10 @@ int main(int argc, char *argv[]) {
pa_hook_init(&hook, (void*) "hook");
- pa_hook_connect(&hook, (pa_hook_cb_t) func1, (void*) "slot1");
- slot = pa_hook_connect(&hook, (pa_hook_cb_t) func2, (void*) "slot2");
- pa_hook_connect(&hook, (pa_hook_cb_t) func1, (void*) "slot3");
-
+ pa_hook_connect(&hook, PA_HOOK_LATE, (pa_hook_cb_t) func1, (void*) "slot1");
+ slot = pa_hook_connect(&hook, PA_HOOK_NORMAL, (pa_hook_cb_t) func2, (void*) "slot2");
+ pa_hook_connect(&hook, PA_HOOK_NORMAL, (pa_hook_cb_t) func1, (void*) "slot3");
+
pa_hook_fire(&hook, (void*) "call1");
pa_hook_slot_free(slot);
@@ -30,6 +32,6 @@ int main(int argc, char *argv[]) {
pa_hook_fire(&hook, (void*) "call2");
pa_hook_free(&hook);
-
+
return 0;
}
diff --git a/src/tests/interpol-test.c b/src/tests/interpol-test.c
index 54bdd775..9d930774 100644
--- a/src/tests/interpol-test.c
+++ b/src/tests/interpol-test.c
@@ -1,18 +1,16 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 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
@@ -42,10 +40,9 @@ static pa_context *context = NULL;
static pa_stream *stream = NULL;
static pa_mainloop_api *mainloop_api = NULL;
-static void stream_write_cb(pa_stream *p, size_t length, void *userdata) {
-
+static void stream_write_cb(pa_stream *p, size_t nbytes, void *userdata) {
/* Just some silence */
- pa_stream_write(p, pa_xmalloc0(length), length, pa_xfree, 0, PA_SEEK_RELATIVE);
+ pa_stream_write(p, pa_xmalloc0(nbytes), nbytes, pa_xfree, 0, PA_SEEK_RELATIVE);
}
/* This is called whenever the context status changes */
@@ -57,26 +54,26 @@ static void context_state_callback(pa_context *c, void *userdata) {
case PA_CONTEXT_AUTHORIZING:
case PA_CONTEXT_SETTING_NAME:
break;
-
+
case PA_CONTEXT_READY: {
static const pa_sample_spec ss = {
.format = PA_SAMPLE_S16LE,
.rate = 44100,
- .channels = 1
+ .channels = 2
};
-
+
fprintf(stderr, "Connection established.\n");
stream = pa_stream_new(c, "interpol-test", &ss, NULL);
assert(stream);
-
+
pa_stream_connect_playback(stream, NULL, NULL, PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL);
pa_stream_set_write_callback(stream, stream_write_cb, NULL);
-
+
break;
}
-
+
case PA_CONTEXT_TERMINATED:
break;
@@ -108,36 +105,41 @@ int main(int argc, char *argv[]) {
assert(r >= 0);
pa_gettimeofday(&start);
-
+
pa_threaded_mainloop_start(m);
for (k = 0; k < 5000; k++) {
- int success = 0, changed = 0;
+ pa_bool_t success = FALSE, changed = FALSE;
pa_usec_t t, rtc;
struct timeval now, tv;
-
+ pa_bool_t playing = FALSE;
+
pa_threaded_mainloop_lock(m);
if (stream) {
const pa_timing_info *info;
-
+
if (pa_stream_get_time(stream, &t) >= 0)
- success = 1;
+ success = TRUE;
- if ((info = pa_stream_get_timing_info(stream)))
- if (last_info.tv_usec != info->timestamp.tv_usec || last_info.tv_sec != info->timestamp.tv_sec) {
- changed = 1;
+ if ((info = pa_stream_get_timing_info(stream))) {
+ if (memcmp(&last_info, &info->timestamp, sizeof(struct timeval))) {
+ changed = TRUE;
last_info = info->timestamp;
}
+ if (info->playing)
+ playing = TRUE;
+ }
}
-
+
pa_threaded_mainloop_unlock(m);
-
- if (success) {
- pa_gettimeofday(&now);
+ pa_gettimeofday(&now);
+
+ if (success) {
rtc = pa_timeval_diff(&now, &start);
- printf("%i\t%llu\t%llu\t%llu\t%llu\t%u\n", k, rtc, t, rtc-old_rtc, t-old_t, changed);
+ printf("%i\t%llu\t%llu\t%llu\t%llu\t%u\t%u\n", k, (unsigned long long) rtc, (unsigned long long) t, (unsigned long long) (rtc-old_rtc), (unsigned long long) (t-old_t), changed, playing);
+ fflush(stdout);
old_t = t;
old_rtc = rtc;
}
@@ -156,7 +158,7 @@ int main(int argc, char *argv[]) {
pa_stream_disconnect(stream);
pa_stream_unref(stream);
}
-
+
if (context) {
pa_context_disconnect(context);
pa_context_unref(context);
@@ -164,6 +166,6 @@ int main(int argc, char *argv[]) {
if (m)
pa_threaded_mainloop_free(m);
-
+
return 0;
}
diff --git a/src/tests/ipacl-test.c b/src/tests/ipacl-test.c
index 2566b038..bcdd469a 100644
--- a/src/tests/ipacl-test.c
+++ b/src/tests/ipacl-test.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
@@ -37,13 +35,13 @@ int main(int argc, char *argv[]) {
int r;
pa_ip_acl *acl;
- fd = socket(PF_INET, SOCK_STREAM, 0);
+ fd = socket(PF_INET, SOCK_STREAM, 0);
assert(fd >= 0);
-
+
sa.sin_family = AF_INET;
sa.sin_port = htons(22);
sa.sin_addr.s_addr = inet_addr("127.0.0.1");
-
+
r = connect(fd, (struct sockaddr*) &sa, sizeof(sa));
assert(r >= 0);
@@ -66,7 +64,7 @@ int main(int argc, char *argv[]) {
assert(acl);
printf("result=%u (should be 1)\n", pa_ip_acl_check(acl, fd));
pa_ip_acl_free(acl);
-
+
acl = pa_ip_acl_new("127.0.0.2");
assert(acl);
printf("result=%u (should be 0)\n", pa_ip_acl_check(acl, fd));
@@ -86,7 +84,7 @@ int main(int argc, char *argv[]) {
assert(acl);
printf("result=%u (should be 0)\n", pa_ip_acl_check(acl, fd));
pa_ip_acl_free(acl);
-
+
close(fd);
fd = socket(PF_INET6, SOCK_STREAM, 0);
@@ -96,7 +94,7 @@ int main(int argc, char *argv[]) {
sa6.sin6_family = AF_INET6;
sa6.sin6_port = htons(22);
inet_pton(AF_INET6, "::1", &sa6.sin6_addr);
-
+
r = connect(fd, (struct sockaddr*) &sa6, sizeof(sa6));
assert(r >= 0);
@@ -131,6 +129,6 @@ int main(int argc, char *argv[]) {
pa_ip_acl_free(acl);
close(fd);
-
+
return 0;
}
diff --git a/src/tests/mainloop-test.c b/src/tests/mainloop-test.c
index b06d0ed1..9fa2e466 100644
--- a/src/tests/mainloop-test.c
+++ b/src/tests/mainloop-test.c
@@ -1,18 +1,16 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 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
@@ -29,9 +27,9 @@
#include <assert.h>
#include <pulse/timeval.h>
+#include <pulse/gccmacro.h>
#include <pulsecore/core-util.h>
-#include <pulsecore/gccmacro.h>
#ifdef GLIB_MAIN_LOOP
@@ -121,6 +119,6 @@ int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) {
#else
pa_mainloop_free(m);
#endif
-
+
return 0;
}
diff --git a/src/tests/mcalign-test.c b/src/tests/mcalign-test.c
index 1584256c..9e358359 100644
--- a/src/tests/mcalign-test.c
+++ b/src/tests/mcalign-test.c
@@ -1,18 +1,16 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
-
+
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
@@ -31,9 +29,10 @@
#include <stdlib.h>
#include <time.h>
+#include <pulse/gccmacro.h>
+
#include <pulsecore/core-util.h>
#include <pulsecore/mcalign.h>
-#include <pulsecore/gccmacro.h>
/* A simple program for testing pa_mcalign */
@@ -45,7 +44,7 @@ int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) {
p = pa_mempool_new(0);
a = pa_mcalign_new(11);
-
+
pa_memchunk_reset(&c);
srand(time(NULL));
@@ -66,13 +65,15 @@ int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) {
l = l <= 1 ? l : rand() % (l-1) +1 ;
p = pa_memblock_acquire(c.memblock);
+
if ((r = read(STDIN_FILENO, (uint8_t*) p + c.index, l)) <= 0) {
pa_memblock_release(c.memblock);
fprintf(stderr, "read() failed: %s\n", r < 0 ? strerror(errno) : "EOF");
break;
}
+
pa_memblock_release(c.memblock);
-
+
c.length = r;
pa_mcalign_push(a, &c);
fprintf(stderr, "Read %ld bytes\n", (long)r);
diff --git a/src/tests/memblock-test.c b/src/tests/memblock-test.c
index c2dd2efa..6da1b1e9 100644
--- a/src/tests/memblock-test.c
+++ b/src/tests/memblock-test.c
@@ -1,18 +1,16 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 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
@@ -23,11 +21,11 @@
#include <config.h>
#endif
-#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <pulsecore/memblock.h>
+#include <pulsecore/macro.h>
#include <pulse/xmalloc.h>
static void release_cb(pa_memimport *i, uint32_t block_id, void *userdata) {
@@ -46,24 +44,24 @@ static void print_stats(pa_mempool *p, const char *text) {
"n_accumulated = %u\n"
"n_imported = %u\n"
"n_exported = %u\n"
- "allocated_size = %lu\n"
- "accumulated_size = %lu\n"
- "imported_size = %lu\n"
- "exported_size = %lu\n"
+ "allocated_size = %u\n"
+ "accumulated_size = %u\n"
+ "imported_size = %u\n"
+ "exported_size = %u\n"
"n_too_large_for_pool = %u\n"
"n_pool_full = %u\n"
"}\n",
text,
- (unsigned) AO_load_acquire_read((AO_t*) &s->n_allocated),
- (unsigned) AO_load_acquire_read((AO_t*) &s->n_accumulated),
- (unsigned) AO_load_acquire_read((AO_t*) &s->n_imported),
- (unsigned) AO_load_acquire_read((AO_t*) &s->n_exported),
- (unsigned long) AO_load_acquire_read((AO_t*) &s->allocated_size),
- (unsigned long) AO_load_acquire_read((AO_t*) &s->accumulated_size),
- (unsigned long) AO_load_acquire_read((AO_t*) &s->imported_size),
- (unsigned long) AO_load_acquire_read((AO_t*) &s->exported_size),
- (unsigned) AO_load_acquire_read((AO_t*) &s->n_too_large_for_pool),
- (unsigned) AO_load_acquire_read((AO_t*) &s->n_pool_full));
+ (unsigned) pa_atomic_load(&s->n_allocated),
+ (unsigned) pa_atomic_load(&s->n_accumulated),
+ (unsigned) pa_atomic_load(&s->n_imported),
+ (unsigned) pa_atomic_load(&s->n_exported),
+ (unsigned) pa_atomic_load(&s->allocated_size),
+ (unsigned) pa_atomic_load(&s->accumulated_size),
+ (unsigned) pa_atomic_load(&s->imported_size),
+ (unsigned) pa_atomic_load(&s->exported_size),
+ (unsigned) pa_atomic_load(&s->n_too_large_for_pool),
+ (unsigned) pa_atomic_load(&s->n_pool_full));
}
int main(int argc, char *argv[]) {
@@ -79,7 +77,7 @@ int main(int argc, char *argv[]) {
char *x;
const char txt[] = "This is a test!";
-
+
pool_a = pa_mempool_new(1);
pool_b = pa_mempool_new(1);
pool_c = pa_mempool_new(1);
@@ -87,20 +85,20 @@ int main(int argc, char *argv[]) {
pa_mempool_get_shm_id(pool_a, &id_a);
pa_mempool_get_shm_id(pool_b, &id_b);
pa_mempool_get_shm_id(pool_c, &id_c);
-
- assert(pool_a && pool_b && pool_c);
-
+
+ pa_assert(pool_a && pool_b && pool_c);
+
blocks[0] = pa_memblock_new_fixed(pool_a, (void*) txt, sizeof(txt), 1);
blocks[1] = pa_memblock_new(pool_a, sizeof(txt));
x = pa_memblock_acquire(blocks[1]);
snprintf(x, pa_memblock_get_length(blocks[1]), "%s", txt);
pa_memblock_release(blocks[1]);
-
+
blocks[2] = pa_memblock_new_pool(pool_a, sizeof(txt));
x = pa_memblock_acquire(blocks[2]);
snprintf(x, pa_memblock_get_length(blocks[2]), "%s", txt);
- pa_memblock_release(blocks[1]);
+ pa_memblock_release(blocks[2]);
blocks[3] = pa_memblock_new_malloced(pool_a, pa_xstrdup(txt), sizeof(txt));
blocks[4] = NULL;
@@ -109,35 +107,35 @@ int main(int argc, char *argv[]) {
printf("Memory block %u\n", i);
mb_a = blocks[i];
- assert(mb_a);
-
+ pa_assert(mb_a);
+
export_a = pa_memexport_new(pool_a, revoke_cb, (void*) "A");
export_b = pa_memexport_new(pool_b, revoke_cb, (void*) "B");
-
- assert(export_a && export_b);
-
+
+ pa_assert(export_a && export_b);
+
import_b = pa_memimport_new(pool_b, release_cb, (void*) "B");
import_c = pa_memimport_new(pool_c, release_cb, (void*) "C");
-
- assert(import_b && import_c);
-
+
+ pa_assert(import_b && import_c);
+
r = pa_memexport_put(export_a, mb_a, &id, &shm_id, &offset, &size);
- assert(r >= 0);
- assert(shm_id == id_a);
+ pa_assert(r >= 0);
+ pa_assert(shm_id == id_a);
printf("A: Memory block exported as %u\n", id);
-
+
mb_b = pa_memimport_get(import_b, id, shm_id, offset, size);
- assert(mb_b);
+ pa_assert(mb_b);
r = pa_memexport_put(export_b, mb_b, &id, &shm_id, &offset, &size);
- assert(r >= 0);
- assert(shm_id == id_a || shm_id == id_b);
+ pa_assert(r >= 0);
+ pa_assert(shm_id == id_a || shm_id == id_b);
pa_memblock_unref(mb_b);
printf("B: Memory block exported as %u\n", id);
-
+
mb_c = pa_memimport_get(import_c, id, shm_id, offset, size);
- assert(mb_c);
+ pa_assert(mb_c);
x = pa_memblock_acquire(mb_c);
printf("1 data=%s\n", x);
pa_memblock_release(mb_c);
@@ -145,23 +143,23 @@ int main(int argc, char *argv[]) {
print_stats(pool_a, "A");
print_stats(pool_b, "B");
print_stats(pool_c, "C");
-
+
pa_memexport_free(export_b);
x = pa_memblock_acquire(mb_c);
printf("2 data=%s\n", x);
pa_memblock_release(mb_c);
pa_memblock_unref(mb_c);
-
+
pa_memimport_free(import_b);
-
+
pa_memblock_unref(mb_a);
-
+
pa_memimport_free(import_c);
pa_memexport_free(export_a);
}
printf("vaccuuming...\n");
-
+
pa_mempool_vacuum(pool_a);
pa_mempool_vacuum(pool_b);
pa_mempool_vacuum(pool_c);
diff --git a/src/tests/memblockq-test.c b/src/tests/memblockq-test.c
index 02848eb2..7bf992a1 100644
--- a/src/tests/memblockq-test.c
+++ b/src/tests/memblockq-test.c
@@ -1,18 +1,16 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 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
@@ -26,58 +24,85 @@
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
+#include <signal.h>
#include <pulsecore/memblockq.h>
#include <pulsecore/log.h>
+static void dump(pa_memblockq *bq) {
+ printf(">");
+
+ for (;;) {
+ pa_memchunk out;
+ char *e;
+ size_t n;
+ void *q;
+
+ if (pa_memblockq_peek(bq, &out) < 0)
+ break;
+
+ q = pa_memblock_acquire(out.memblock);
+ for (e = (char*) q + out.index, n = 0; n < out.length; n++)
+ printf("%c", *e);
+ pa_memblock_release(out.memblock);
+
+ pa_memblock_unref(out.memblock);
+ pa_memblockq_drop(bq, out.length);
+ }
+
+ printf("<\n");
+}
+
int main(int argc, char *argv[]) {
int ret;
pa_mempool *p;
pa_memblockq *bq;
pa_memchunk chunk1, chunk2, chunk3, chunk4;
- pa_memblock *silence;
+ pa_memchunk silence;
pa_log_set_maximal_level(PA_LOG_DEBUG);
p = pa_mempool_new(0);
-
- silence = pa_memblock_new_fixed(p, (char*) "__", 2, 1);
- assert(silence);
- bq = pa_memblockq_new(0, 40, 10, 2, 4, 4, silence);
+ silence.memblock = pa_memblock_new_fixed(p, (char*) "__", 2, 1);
+ assert(silence.memblock);
+ silence.index = 0;
+ silence.length = pa_memblock_get_length(silence.memblock);
+
+ bq = pa_memblockq_new(0, 40, 10, 2, 4, 4, 40, &silence);
assert(bq);
- chunk1.memblock = pa_memblock_new_fixed(p, (char*) "AA", 2, 1);
+ chunk1.memblock = pa_memblock_new_fixed(p, (char*) "11", 2, 1);
chunk1.index = 0;
chunk1.length = 2;
assert(chunk1.memblock);
-
- chunk2.memblock = pa_memblock_new_fixed(p, (char*) "TTBB", 4, 1);
+
+ chunk2.memblock = pa_memblock_new_fixed(p, (char*) "XX22", 4, 1);
chunk2.index = 2;
chunk2.length = 2;
assert(chunk2.memblock);
- chunk3.memblock = pa_memblock_new_fixed(p, (char*) "ZZZZ", 4, 1);
+ chunk3.memblock = pa_memblock_new_fixed(p, (char*) "3333", 4, 1);
chunk3.index = 0;
chunk3.length = 4;
assert(chunk3.memblock);
- chunk4.memblock = pa_memblock_new_fixed(p, (char*) "KKKKKKKK", 8, 1);
+ chunk4.memblock = pa_memblock_new_fixed(p, (char*) "44444444", 8, 1);
chunk4.index = 0;
chunk4.length = 8;
assert(chunk4.memblock);
ret = pa_memblockq_push(bq, &chunk1);
assert(ret == 0);
-
- ret = pa_memblockq_push(bq, &chunk1);
- assert(ret == 0);
-
+
ret = pa_memblockq_push(bq, &chunk2);
assert(ret == 0);
-
- ret = pa_memblockq_push(bq, &chunk2);
+
+ ret = pa_memblockq_push(bq, &chunk3);
+ assert(ret == 0);
+
+ ret = pa_memblockq_push(bq, &chunk4);
assert(ret == 0);
pa_memblockq_seek(bq, -6, 0);
@@ -85,7 +110,7 @@ int main(int argc, char *argv[]) {
assert(ret == 0);
pa_memblockq_seek(bq, -2, 0);
- ret = pa_memblockq_push(bq, &chunk3);
+ ret = pa_memblockq_push(bq, &chunk1);
assert(ret == 0);
pa_memblockq_seek(bq, -10, 0);
@@ -115,39 +140,25 @@ int main(int argc, char *argv[]) {
chunk3.index += 2;
chunk3.length -= 2;
-
ret = pa_memblockq_push(bq, &chunk3);
assert(ret == 0);
-
- printf(">");
- pa_memblockq_shorten(bq, 6);
-
- for (;;) {
- pa_memchunk out;
- char *e;
- size_t n;
-
- if (pa_memblockq_peek(bq, &out) < 0)
- break;
+ pa_memblockq_seek(bq, 30, PA_SEEK_RELATIVE);
- p = pa_memblock_acquire(out.memblock);
- for (e = (char*) p + out.index, n = 0; n < out.length; n++)
- printf("%c", *e);
- pa_memblock_release(out.memblock);
+ dump(bq);
+
+ pa_memblockq_rewind(bq, 52);
+
+ dump(bq);
- pa_memblock_unref(out.memblock);
- pa_memblockq_drop(bq, &out, out.length);
- }
-
- printf("<\n");
-
pa_memblockq_free(bq);
- pa_memblock_unref(silence);
+ pa_memblock_unref(silence.memblock);
pa_memblock_unref(chunk1.memblock);
pa_memblock_unref(chunk2.memblock);
pa_memblock_unref(chunk3.memblock);
pa_memblock_unref(chunk4.memblock);
-
+
+ pa_mempool_free(p);
+
return 0;
}
diff --git a/src/tests/mix-test.c b/src/tests/mix-test.c
new file mode 100644
index 00000000..f3f6f829
--- /dev/null
+++ b/src/tests/mix-test.c
@@ -0,0 +1,259 @@
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+
+#include <pulse/sample.h>
+#include <pulse/volume.h>
+
+#include <pulsecore/resampler.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/endianmacros.h>
+#include <pulsecore/memblock.h>
+#include <pulsecore/sample-util.h>
+
+#include <liboil/liboil.h>
+
+static float swap_float(float a) {
+ uint32_t *b = (uint32_t*) &a;
+ *b = PA_UINT32_SWAP(*b);
+ return a;
+}
+
+static void dump_block(const pa_sample_spec *ss, const pa_memchunk *chunk) {
+ void *d;
+ unsigned i;
+
+ d = pa_memblock_acquire(chunk->memblock);
+
+ switch (ss->format) {
+
+ case PA_SAMPLE_U8:
+ case PA_SAMPLE_ULAW:
+ case PA_SAMPLE_ALAW: {
+ uint8_t *u = d;
+
+ for (i = 0; i < chunk->length / pa_frame_size(ss); i++)
+ printf("0x%02x ", *(u++));
+
+ break;
+ }
+
+ case PA_SAMPLE_S16NE:
+ case PA_SAMPLE_S16RE: {
+ uint16_t *u = d;
+
+ for (i = 0; i < chunk->length / pa_frame_size(ss); i++)
+ printf("0x%04x ", *(u++));
+
+ break;
+ }
+
+ case PA_SAMPLE_S32NE:
+ case PA_SAMPLE_S32RE: {
+ uint32_t *u = d;
+
+ for (i = 0; i < chunk->length / pa_frame_size(ss); i++)
+ printf("0x%08x ", *(u++));
+
+ break;
+ }
+
+ case PA_SAMPLE_FLOAT32NE:
+ case PA_SAMPLE_FLOAT32RE: {
+ float *u = d;
+
+ for (i = 0; i < chunk->length / pa_frame_size(ss); i++) {
+ printf("%1.5f ", ss->format == PA_SAMPLE_FLOAT32NE ? *u : swap_float(*u));
+ u++;
+ }
+
+ break;
+ }
+
+ default:
+ pa_assert_not_reached();
+ }
+
+ printf("\n");
+
+ pa_memblock_release(chunk->memblock);
+}
+
+static pa_memblock* generate_block(pa_mempool *pool, const pa_sample_spec *ss) {
+ pa_memblock *r;
+ void *d;
+ unsigned i;
+
+ pa_assert_se(r = pa_memblock_new(pool, pa_frame_size(ss) * 10));
+ d = pa_memblock_acquire(r);
+
+ switch (ss->format) {
+
+ case PA_SAMPLE_U8:
+ case PA_SAMPLE_ULAW:
+ case PA_SAMPLE_ALAW: {
+ uint8_t *u = d;
+
+ u[0] = 0x00;
+ u[1] = 0xFF;
+ u[2] = 0x7F;
+ u[3] = 0x80;
+ u[4] = 0x9f;
+ u[5] = 0x3f;
+ u[6] = 0x1;
+ u[7] = 0xF0;
+ u[8] = 0x20;
+ u[9] = 0x21;
+ break;
+ }
+
+ case PA_SAMPLE_S16NE:
+ case PA_SAMPLE_S16RE: {
+ uint16_t *u = d;
+
+ u[0] = 0x0000;
+ u[1] = 0xFFFF;
+ u[2] = 0x7FFF;
+ u[3] = 0x8000;
+ u[4] = 0x9fff;
+ u[5] = 0x3fff;
+ u[6] = 0x1;
+ u[7] = 0xF000;
+ u[8] = 0x20;
+ u[9] = 0x21;
+ break;
+ }
+
+ case PA_SAMPLE_S32NE:
+ case PA_SAMPLE_S32RE: {
+ uint32_t *u = d;
+
+ u[0] = 0x00000001;
+ u[1] = 0xFFFF0002;
+ u[2] = 0x7FFF0003;
+ u[3] = 0x80000004;
+ u[4] = 0x9fff0005;
+ u[5] = 0x3fff0006;
+ u[6] = 0x10007;
+ u[7] = 0xF0000008;
+ u[8] = 0x200009;
+ u[9] = 0x21000A;
+ break;
+ }
+
+ case PA_SAMPLE_FLOAT32NE:
+ case PA_SAMPLE_FLOAT32RE: {
+ float *u = d;
+
+ u[0] = 0.0;
+ u[1] = -1.0;
+ u[2] = 1.0;
+ u[3] = 4711;
+ u[4] = 0.222;
+ u[5] = 0.33;
+ u[6] = -.3;
+ u[7] = 99;
+ u[8] = -0.555;
+ u[9] = -.123;
+
+ if (ss->format == PA_SAMPLE_FLOAT32RE)
+ for (i = 0; i < 10; i++)
+ u[i] = swap_float(u[i]);
+
+ break;
+ }
+
+ default:
+ pa_assert_not_reached();
+ }
+
+ pa_memblock_release(r);
+
+ return r;
+}
+
+int main(int argc, char *argv[]) {
+ pa_mempool *pool;
+ pa_sample_spec a;
+ pa_cvolume v;
+
+ oil_init();
+ pa_log_set_maximal_level(PA_LOG_DEBUG);
+
+ pa_assert_se(pool = pa_mempool_new(FALSE));
+
+ a.channels = 1;
+ a.rate = 44100;
+
+ v.channels = a.channels;
+ v.values[0] = pa_sw_volume_from_linear(0.9);
+
+ for (a.format = 0; a.format < PA_SAMPLE_MAX; a.format ++) {
+ pa_memchunk i, j, k;
+ pa_mix_info m[2];
+ void *ptr;
+
+ printf("=== mixing: %s\n", pa_sample_format_to_string(a.format));
+
+ /* Generate block */
+ i.memblock = generate_block(pool, &a);
+ i.length = pa_memblock_get_length(i.memblock);
+ i.index = 0;
+
+ /* Make a copy */
+ j = i;
+ pa_memblock_ref(j.memblock);
+ pa_memchunk_make_writable(&j, 0);
+
+ /* Adjust volume of the copy */
+ pa_volume_memchunk(&j, &a, &v);
+
+ m[0].chunk = i;
+ m[0].volume.values[0] = PA_VOLUME_NORM;
+ m[0].volume.channels = a.channels;
+ m[1].chunk = j;
+ m[1].volume.values[0] = PA_VOLUME_NORM;
+ m[1].volume.channels = a.channels;
+
+ k.memblock = pa_memblock_new(pool, i.length);
+ k.length = i.length;
+ k.index = 0;
+
+ ptr = (uint8_t*) pa_memblock_acquire(k.memblock) + k.index;
+ pa_mix(m, 2, ptr, k.length, &a, NULL, FALSE);
+ pa_memblock_release(k.memblock);
+
+ dump_block(&a, &i);
+ dump_block(&a, &j);
+ dump_block(&a, &k);
+
+ pa_memblock_unref(i.memblock);
+ pa_memblock_unref(j.memblock);
+ pa_memblock_unref(k.memblock);
+ }
+
+ pa_mempool_free(pool);
+
+ return 0;
+}
diff --git a/src/tests/pacat-simple.c b/src/tests/pacat-simple.c
index 364e1ad6..b26e4b68 100644
--- a/src/tests/pacat-simple.c
+++ b/src/tests/pacat-simple.c
@@ -1,18 +1,16 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 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
@@ -31,7 +29,7 @@
#include <pulse/simple.h>
#include <pulse/error.h>
-#include <pulsecore/gccmacro.h>
+#include <pulse/gccmacro.h>
#define BUFSIZE 1024
@@ -43,7 +41,7 @@ int main(PA_GCC_UNUSED int argc, char*argv[]) {
.rate = 44100,
.channels = 2
};
-
+
pa_simple *s = NULL;
int ret = 1;
int error;
@@ -61,10 +59,10 @@ int main(PA_GCC_UNUSED int argc, char*argv[]) {
fprintf(stderr, __FILE__": dup2() failed: %s\n", strerror(errno));
goto finish;
}
-
+
close(fd);
}
-
+
/* Create a new playback stream */
if (!(s = pa_simple_new(NULL, argv[0], PA_STREAM_PLAYBACK, NULL, "playback", &ss, NULL, NULL, &error))) {
fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error));
@@ -90,7 +88,7 @@ int main(PA_GCC_UNUSED int argc, char*argv[]) {
if ((r = read(STDIN_FILENO, buf, sizeof(buf))) <= 0) {
if (r == 0) /* EOF */
break;
-
+
fprintf(stderr, __FILE__": read() failed: %s\n", strerror(errno));
goto finish;
}
@@ -114,6 +112,6 @@ finish:
if (s)
pa_simple_free(s);
-
+
return ret;
}
diff --git a/src/tests/parec-simple.c b/src/tests/parec-simple.c
index 45a52288..6c0d529b 100644
--- a/src/tests/parec-simple.c
+++ b/src/tests/parec-simple.c
@@ -1,18 +1,16 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 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
@@ -30,7 +28,7 @@
#include <pulse/simple.h>
#include <pulse/error.h>
-#include <pulsecore/gccmacro.h>
+#include <pulse/gccmacro.h>
#define BUFSIZE 1024
@@ -46,7 +44,7 @@ static ssize_t loop_write(int fd, const void*data, size_t size) {
if (r == 0)
break;
-
+
ret += r;
data = (const uint8_t*) data + r;
size -= r;
@@ -95,6 +93,6 @@ finish:
if (s)
pa_simple_free(s);
-
+
return ret;
}
diff --git a/src/tests/proplist-test.c b/src/tests/proplist-test.c
new file mode 100644
index 00000000..20041af6
--- /dev/null
+++ b/src/tests/proplist-test.c
@@ -0,0 +1,60 @@
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+
+#include <pulse/proplist.h>
+#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+
+int main(int argc, char*argv[]) {
+ pa_proplist *a, *b;
+ char *s, *t;
+
+ a = pa_proplist_new();
+ pa_assert_se(pa_proplist_sets(a, PA_PROP_MEDIA_TITLE, "Brandenburgische Konzerte") == 0);
+ pa_assert_se(pa_proplist_sets(a, PA_PROP_MEDIA_ARTIST, "Johann Sebastian Bach") == 0);
+
+ b = pa_proplist_new();
+ pa_assert_se(pa_proplist_sets(b, PA_PROP_MEDIA_TITLE, "Goldbergvariationen") == 0);
+ pa_assert_se(pa_proplist_set(b, PA_PROP_MEDIA_ICON, "\0\1\2\3\4\5\6\7", 8) == 0);
+
+ pa_proplist_update(a, PA_UPDATE_MERGE, b);
+
+ pa_assert_se(!pa_proplist_gets(a, PA_PROP_MEDIA_ICON));
+
+ printf("%s\n", pa_strnull(pa_proplist_gets(a, PA_PROP_MEDIA_TITLE)));
+ pa_assert_se(pa_proplist_unset(b, PA_PROP_MEDIA_TITLE) == 0);
+
+ s = pa_proplist_to_string(a);
+ t = pa_proplist_to_string(b);
+ printf("---\n%s---\n%s", s, t);
+ pa_xfree(s);
+ pa_xfree(t);
+
+ pa_proplist_free(a);
+ pa_proplist_free(b);
+
+ return 0;
+}
diff --git a/src/tests/queue-test.c b/src/tests/queue-test.c
new file mode 100644
index 00000000..105f094a
--- /dev/null
+++ b/src/tests/queue-test.c
@@ -0,0 +1,67 @@
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 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 <assert.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <pulse/util.h>
+#include <pulse/xmalloc.h>
+#include <pulsecore/queue.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+
+int main(int argc, char *argv[]) {
+ pa_queue *q;
+
+ pa_assert_se(q = pa_queue_new());
+
+ pa_assert(pa_queue_is_empty(q));
+
+ pa_queue_push(q, (void*) "eins");
+ pa_log("%s\n", (char*) pa_queue_pop(q));
+
+ pa_assert(pa_queue_is_empty(q));
+
+ pa_queue_push(q, (void*) "zwei");
+ pa_queue_push(q, (void*) "drei");
+ pa_queue_push(q, (void*) "vier");
+
+ pa_log("%s\n", (char*) pa_queue_pop(q));
+ pa_log("%s\n", (char*) pa_queue_pop(q));
+
+ pa_queue_push(q, (void*) "fuenf");
+
+ pa_log("%s\n", (char*) pa_queue_pop(q));
+ pa_log("%s\n", (char*) pa_queue_pop(q));
+
+ pa_assert(pa_queue_is_empty(q));
+
+ pa_queue_push(q, (void*) "sechs");
+ pa_queue_push(q, (void*) "sieben");
+
+ pa_queue_free(q, NULL, NULL);
+
+ return 0;
+}
diff --git a/src/tests/remix-test.c b/src/tests/remix-test.c
new file mode 100644
index 00000000..4777c150
--- /dev/null
+++ b/src/tests/remix-test.c
@@ -0,0 +1,89 @@
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+
+#include <pulse/sample.h>
+#include <pulse/volume.h>
+
+#include <pulsecore/resampler.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/endianmacros.h>
+#include <pulsecore/memblock.h>
+#include <pulsecore/sample-util.h>
+
+#include <liboil/liboil.h>
+
+int main(int argc, char *argv[]) {
+
+ static const pa_channel_map maps[] = {
+ { 1, { PA_CHANNEL_POSITION_MONO } },
+ { 2, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT } },
+ { 3, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, PA_CHANNEL_POSITION_CENTER } },
+ { 3, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, PA_CHANNEL_POSITION_LFE } },
+ { 3, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, PA_CHANNEL_POSITION_REAR_CENTER } },
+ { 4, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, PA_CHANNEL_POSITION_CENTER, PA_CHANNEL_POSITION_LFE } },
+ { 4, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, PA_CHANNEL_POSITION_CENTER, PA_CHANNEL_POSITION_REAR_CENTER } },
+ { 4, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT } },
+ { 5, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, PA_CHANNEL_POSITION_CENTER } },
+ { 5, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, PA_CHANNEL_POSITION_LFE } },
+ { 6, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, PA_CHANNEL_POSITION_LFE, PA_CHANNEL_POSITION_CENTER } },
+ { 8, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, PA_CHANNEL_POSITION_LFE, PA_CHANNEL_POSITION_CENTER, PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT } },
+ { 0, { 0 } }
+ };
+
+ unsigned i, j;
+ pa_mempool *pool;
+
+ oil_init();
+ pa_log_set_maximal_level(PA_LOG_DEBUG);
+
+ pa_assert_se(pool = pa_mempool_new(FALSE));
+
+ for (i = 0; maps[i].channels > 0; i++)
+ for (j = 0; maps[j].channels > 0; j++) {
+ char a[PA_CHANNEL_MAP_SNPRINT_MAX], b[PA_CHANNEL_MAP_SNPRINT_MAX];
+ pa_resampler *r;
+ pa_sample_spec ss1, ss2;
+
+ pa_log_info("Converting from '%s' to '%s'.\n", pa_channel_map_snprint(a, sizeof(a), &maps[i]), pa_channel_map_snprint(b, sizeof(b), &maps[j]));
+
+ ss1.channels = maps[i].channels;
+ ss2.channels = maps[j].channels;
+
+ ss1.rate = ss2.rate = 44100;
+ ss1.format = ss2.format = PA_SAMPLE_S16NE;
+
+ r = pa_resampler_new(pool, &ss1, &maps[i], &ss2, &maps[j], PA_RESAMPLER_AUTO, 0);
+
+ /* We don't really care for the resampler. We just want to
+ * see the remixing debug output. */
+
+ pa_resampler_free(r);
+ }
+
+
+ pa_mempool_free(pool);
+
+ return 0;
+}
diff --git a/src/tests/resampler-test.c b/src/tests/resampler-test.c
new file mode 100644
index 00000000..1a20be2c
--- /dev/null
+++ b/src/tests/resampler-test.c
@@ -0,0 +1,252 @@
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+
+#include <pulse/sample.h>
+#include <pulse/volume.h>
+
+#include <pulsecore/resampler.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/endianmacros.h>
+#include <pulsecore/memblock.h>
+#include <pulsecore/sample-util.h>
+
+#include <liboil/liboil.h>
+
+static float swap_float(float a) {
+ uint32_t *b = (uint32_t*) &a;
+ *b = PA_UINT32_SWAP(*b);
+ return a;
+}
+
+static void dump_block(const pa_sample_spec *ss, const pa_memchunk *chunk) {
+ void *d;
+ unsigned i;
+
+ d = pa_memblock_acquire(chunk->memblock);
+
+ switch (ss->format) {
+
+ case PA_SAMPLE_U8:
+ case PA_SAMPLE_ULAW:
+ case PA_SAMPLE_ALAW: {
+ uint8_t *u = d;
+
+ for (i = 0; i < chunk->length / pa_frame_size(ss); i++)
+ printf("0x%02x ", *(u++));
+
+ break;
+ }
+
+ case PA_SAMPLE_S16NE:
+ case PA_SAMPLE_S16RE: {
+ uint16_t *u = d;
+
+ for (i = 0; i < chunk->length / pa_frame_size(ss); i++)
+ printf("0x%04x ", *(u++));
+
+ break;
+ }
+
+ case PA_SAMPLE_S32NE:
+ case PA_SAMPLE_S32RE: {
+ uint32_t *u = d;
+
+ for (i = 0; i < chunk->length / pa_frame_size(ss); i++)
+ printf("0x%08x ", *(u++));
+
+ break;
+ }
+
+ case PA_SAMPLE_FLOAT32NE:
+ case PA_SAMPLE_FLOAT32RE: {
+ float *u = d;
+
+ for (i = 0; i < chunk->length / pa_frame_size(ss); i++) {
+ printf("%1.3g ", ss->format == PA_SAMPLE_FLOAT32NE ? *u : swap_float(*u));
+ u++;
+ }
+
+ break;
+ }
+
+ default:
+ pa_assert_not_reached();
+ }
+
+ printf("\n");
+
+ pa_memblock_release(chunk->memblock);
+}
+
+static pa_memblock* generate_block(pa_mempool *pool, const pa_sample_spec *ss) {
+ pa_memblock *r;
+ void *d;
+ unsigned i;
+
+ pa_assert_se(r = pa_memblock_new(pool, pa_frame_size(ss) * 10));
+ d = pa_memblock_acquire(r);
+
+ switch (ss->format) {
+
+ case PA_SAMPLE_U8:
+ case PA_SAMPLE_ULAW:
+ case PA_SAMPLE_ALAW: {
+ uint8_t *u = d;
+
+ u[0] = 0x00;
+ u[1] = 0xFF;
+ u[2] = 0x7F;
+ u[3] = 0x80;
+ u[4] = 0x9f;
+ u[5] = 0x3f;
+ u[6] = 0x1;
+ u[7] = 0xF0;
+ u[8] = 0x20;
+ u[9] = 0x21;
+ break;
+ }
+
+ case PA_SAMPLE_S16NE:
+ case PA_SAMPLE_S16RE: {
+ uint16_t *u = d;
+
+ u[0] = 0x0000;
+ u[1] = 0xFFFF;
+ u[2] = 0x7FFF;
+ u[3] = 0x8000;
+ u[4] = 0x9fff;
+ u[5] = 0x3fff;
+ u[6] = 0x1;
+ u[7] = 0xF000;
+ u[8] = 0x20;
+ u[9] = 0x21;
+ break;
+ }
+
+ case PA_SAMPLE_S32NE:
+ case PA_SAMPLE_S32RE: {
+ uint32_t *u = d;
+
+ u[0] = 0x00000001;
+ u[1] = 0xFFFF0002;
+ u[2] = 0x7FFF0003;
+ u[3] = 0x80000004;
+ u[4] = 0x9fff0005;
+ u[5] = 0x3fff0006;
+ u[6] = 0x10007;
+ u[7] = 0xF0000008;
+ u[8] = 0x200009;
+ u[9] = 0x21000A;
+ break;
+ }
+
+ case PA_SAMPLE_FLOAT32NE:
+ case PA_SAMPLE_FLOAT32RE: {
+ float *u = d;
+
+ u[0] = 0.0;
+ u[1] = -1.0;
+ u[2] = 1.0;
+ u[3] = 4711;
+ u[4] = 0.222;
+ u[5] = 0.33;
+ u[6] = -.3;
+ u[7] = 99;
+ u[8] = -0.555;
+ u[9] = -.123;
+
+ if (ss->format == PA_SAMPLE_FLOAT32RE)
+ for (i = 0; i < 10; i++)
+ u[i] = swap_float(u[i]);
+
+ break;
+ }
+
+ default:
+ pa_assert_not_reached();
+ }
+
+ pa_memblock_release(r);
+
+ return r;
+}
+
+int main(int argc, char *argv[]) {
+ pa_mempool *pool;
+ pa_sample_spec a, b;
+ pa_cvolume v;
+
+ oil_init();
+ pa_log_set_maximal_level(PA_LOG_DEBUG);
+
+ pa_assert_se(pool = pa_mempool_new(FALSE));
+
+ a.channels = b.channels = 1;
+ a.rate = b.rate = 44100;
+
+ v.channels = a.channels;
+ v.values[0] = pa_sw_volume_from_linear(0.5);
+
+ for (a.format = 0; a.format < PA_SAMPLE_MAX; a.format ++) {
+ for (b.format = 0; b.format < PA_SAMPLE_MAX; b.format ++) {
+
+ pa_resampler *forth, *back;
+ pa_memchunk i, j, k;
+
+ printf("=== %s -> %s -> %s -> /2\n",
+ pa_sample_format_to_string(a.format),
+ pa_sample_format_to_string(b.format),
+ pa_sample_format_to_string(a.format));
+
+ pa_assert_se(forth = pa_resampler_new(pool, &a, NULL, &b, NULL, PA_RESAMPLER_AUTO, 0));
+ pa_assert_se(back = pa_resampler_new(pool, &b, NULL, &a, NULL, PA_RESAMPLER_AUTO, 0));
+
+ i.memblock = generate_block(pool, &a);
+ i.length = pa_memblock_get_length(i.memblock);
+ i.index = 0;
+ pa_resampler_run(forth, &i, &j);
+ pa_resampler_run(back, &j, &k);
+
+ dump_block(&a, &i);
+ dump_block(&b, &j);
+ dump_block(&a, &k);
+
+ pa_memblock_unref(j.memblock);
+ pa_memblock_unref(k.memblock);
+
+ pa_volume_memchunk(&i, &a, &v);
+ dump_block(&a, &i);
+
+ pa_memblock_unref(i.memblock);
+
+ pa_resampler_free(forth);
+ pa_resampler_free(back);
+ }
+ }
+
+ pa_mempool_free(pool);
+
+ return 0;
+}
diff --git a/src/tests/rtpoll-test.c b/src/tests/rtpoll-test.c
new file mode 100644
index 00000000..953fd61d
--- /dev/null
+++ b/src/tests/rtpoll-test.c
@@ -0,0 +1,91 @@
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 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 <signal.h>
+#include <poll.h>
+
+#include <pulsecore/log.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/rtsig.h>
+
+static int before(pa_rtpoll_item *i) {
+ pa_log("before");
+ return 0;
+}
+
+static void after(pa_rtpoll_item *i) {
+ pa_log("after");
+}
+
+static int worker(pa_rtpoll_item *w) {
+ pa_log("worker");
+ return 0;
+}
+
+int main(int argc, char *argv[]) {
+ pa_rtpoll *p;
+ pa_rtpoll_item *i, *w;
+ struct pollfd *pollfd;
+
+#ifdef SIGRTMIN
+ pa_rtsig_configure(SIGRTMIN+10, SIGRTMAX);
+#endif
+
+ p = pa_rtpoll_new();
+
+ i = pa_rtpoll_item_new(p, PA_RTPOLL_EARLY, 1);
+ pa_rtpoll_item_set_before_callback(i, before);
+ pa_rtpoll_item_set_after_callback(i, after);
+
+ pollfd = pa_rtpoll_item_get_pollfd(i, NULL);
+ pollfd->fd = 0;
+ pollfd->events = POLLIN;
+
+ w = pa_rtpoll_item_new(p, PA_RTPOLL_NORMAL, 0);
+ pa_rtpoll_item_set_before_callback(w, worker);
+
+ pa_rtpoll_install(p);
+ pa_rtpoll_set_timer_relative(p, 10000000); /* 10 s */
+
+ pa_rtpoll_run(p, 1);
+
+ pa_rtpoll_item_free(i);
+
+ i = pa_rtpoll_item_new(p, PA_RTPOLL_EARLY, 1);
+ pa_rtpoll_item_set_before_callback(i, before);
+ pa_rtpoll_item_set_after_callback(i, after);
+
+ pollfd = pa_rtpoll_item_get_pollfd(i, NULL);
+ pollfd->fd = 0;
+ pollfd->events = POLLIN;
+
+ pa_rtpoll_run(p, 1);
+
+ pa_rtpoll_item_free(i);
+
+ pa_rtpoll_item_free(w);
+
+ pa_rtpoll_free(p);
+
+ return 0;
+}
diff --git a/src/tests/rtstutter.c b/src/tests/rtstutter.c
new file mode 100644
index 00000000..91e85c36
--- /dev/null
+++ b/src/tests/rtstutter.c
@@ -0,0 +1,117 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2008 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 <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <sched.h>
+#include <inttypes.h>
+#include <string.h>
+#include <pthread.h>
+
+#include <pulse/timeval.h>
+#include <pulse/gccmacro.h>
+
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+
+static int msec_lower, msec_upper;
+
+static void* work(void *p) PA_GCC_NORETURN;
+
+static void* work(void *p) {
+ cpu_set_t mask;
+ struct sched_param param;
+
+ pa_log_notice("CPU%i: Created thread.", PA_PTR_TO_INT(p));
+
+ memset(&param, 0, sizeof(param));
+ param.sched_priority = 12;
+ pa_assert_se(pthread_setschedparam(pthread_self(), SCHED_FIFO, &param) == 0);
+
+ CPU_ZERO(&mask);
+ CPU_SET(PA_PTR_TO_INT(p), &mask);
+ pa_assert_se(pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask) == 0);
+
+ for (;;) {
+ struct timespec now, end;
+ uint64_t nsec;
+
+ pa_log_notice("CPU%i: Sleeping for 1s", PA_PTR_TO_INT(p));
+ sleep(1);
+
+ pa_assert_se(clock_gettime(CLOCK_REALTIME, &end) == 0);
+
+ nsec =
+ (uint64_t) ((((double) rand())*(msec_upper-msec_lower)*PA_NSEC_PER_MSEC)/RAND_MAX) +
+ (uint64_t) (msec_lower*PA_NSEC_PER_MSEC);
+
+ pa_log_notice("CPU%i: Freezing for %ims", PA_PTR_TO_INT(p), (int) (nsec/PA_NSEC_PER_MSEC));
+
+ end.tv_sec += nsec / PA_NSEC_PER_SEC;
+ end.tv_nsec += nsec % PA_NSEC_PER_SEC;
+
+ while ((pa_usec_t) end.tv_nsec > PA_NSEC_PER_SEC) {
+ end.tv_sec++;
+ end.tv_nsec -= PA_NSEC_PER_SEC;
+ }
+
+ do {
+ pa_assert_se(clock_gettime(CLOCK_REALTIME, &now) == 0);
+ } while (now.tv_sec < end.tv_sec ||
+ (now.tv_sec == end.tv_sec && now.tv_nsec < end.tv_nsec));
+ }
+}
+
+int main(int argc, char*argv[]) {
+ int n;
+
+ srand(time(NULL));
+
+ if (argc >= 3) {
+ msec_lower = atoi(argv[1]);
+ msec_upper = atoi(argv[2]);
+ } else if (argc >= 2) {
+ msec_lower = 0;
+ msec_upper = atoi(argv[1]);
+ } else {
+ msec_lower = 0;
+ msec_upper = 1000;
+ }
+
+ pa_assert(msec_upper > 0);
+ pa_assert(msec_upper >= msec_lower);
+
+ pa_log_notice("Creating random latencies in the range of %ims to %ims.", msec_lower, msec_upper);
+
+ for (n = 1; n < sysconf(_SC_NPROCESSORS_CONF); n++) {
+ pthread_t t;
+ pa_assert_se(pthread_create(&t, NULL, work, PA_INT_TO_PTR(n)) == 0);
+ }
+
+ work(PA_INT_TO_PTR(0));
+
+ return 0;
+}
diff --git a/src/tests/sig2str-test.c b/src/tests/sig2str-test.c
new file mode 100644
index 00000000..d64a8902
--- /dev/null
+++ b/src/tests/sig2str-test.c
@@ -0,0 +1,37 @@
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 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 <signal.h>
+#include <stdio.h>
+
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+
+int main(int argc, char *argv[]) {
+ int sig;
+
+ for (sig = -1; sig <= NSIG; sig++)
+ printf("%i = %s\n", sig, pa_sig2str(sig));
+
+ return 0;
+}
diff --git a/src/tests/smoother-test.c b/src/tests/smoother-test.c
new file mode 100644
index 00000000..b78f3c91
--- /dev/null
+++ b/src/tests/smoother-test.c
@@ -0,0 +1,78 @@
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <pulsecore/time-smoother.h>
+#include <pulse/timeval.h>
+
+int main(int argc, char*argv[]) {
+ pa_usec_t x;
+ unsigned u = 0;
+ pa_smoother *s;
+ int m;
+
+/* unsigned msec[] = { */
+/* 200, 200, */
+/* 300, 320, */
+/* 400, 400, */
+/* 500, 480, */
+/* 0, 0 */
+/* }; */
+
+ int msec[200];
+
+ srand(0);
+
+ for (m = 0, u = 0; u < PA_ELEMENTSOF(msec); u+= 2) {
+
+ msec[u] = m+1 + (rand() % 100) - 50;
+ msec[u+1] = m + (rand() % 2000) - 1000;
+
+ m += rand() % 100;
+
+ if (msec[u] < 0)
+ msec[u] = 0;
+
+ if (msec[u+1] < 0)
+ msec[u+1] = 0;
+ }
+
+ s = pa_smoother_new(700*PA_USEC_PER_MSEC, 2000*PA_USEC_PER_MSEC, TRUE, 6);
+
+ for (x = 0, u = 0; x < PA_USEC_PER_SEC * 10; x += PA_USEC_PER_MSEC) {
+
+ while (u < PA_ELEMENTSOF(msec) && (pa_usec_t) msec[u]*PA_USEC_PER_MSEC < x) {
+ pa_smoother_put(s, msec[u]*PA_USEC_PER_MSEC, msec[u+1]*PA_USEC_PER_MSEC);
+ printf("%i\t\t%i\n", msec[u], msec[u+1]);
+ u += 2;
+ }
+
+ printf("%llu\t%llu\n", (unsigned long long) (x/PA_USEC_PER_MSEC), (unsigned long long) (pa_smoother_get(s, x)/PA_USEC_PER_MSEC));
+ }
+
+ pa_smoother_free(s);
+
+ return 0;
+}
diff --git a/src/tests/stripnul.c b/src/tests/stripnul.c
new file mode 100644
index 00000000..0ab06776
--- /dev/null
+++ b/src/tests/stripnul.c
@@ -0,0 +1,70 @@
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+
+int main(int argc, char *argv[]) {
+ FILE *i, *o;
+ size_t granularity;
+ pa_bool_t found;
+ uint8_t *zero;
+
+ pa_assert_se(argc >= 2);
+ pa_assert_se((granularity = atoi(argv[1])) >= 1);
+ pa_assert_se((i = (argc >= 3) ? fopen(argv[2], "r") : stdin));
+ pa_assert_se((o = (argc >= 4) ? fopen(argv[3], "w") : stdout));
+
+ zero = pa_xmalloc0(granularity);
+
+ for (;;) {
+ uint8_t buffer[16*1024], *p;
+ size_t k;
+
+ k = fread(buffer, granularity, sizeof(buffer)/granularity, i);
+
+ if (k <= 0)
+ break;
+
+ if (found)
+ pa_assert_se(fwrite(buffer, granularity, k, o) == k);
+ else {
+ for (p = buffer; (p-buffer)/granularity < k; p += granularity)
+ if (memcmp(p, zero, granularity)) {
+ size_t left;
+ found = TRUE;
+ left = k - (p-buffer)/granularity;
+ pa_assert_se(fwrite(p, granularity, left, o) == left);
+ break;
+ }
+ }
+ }
+
+ fflush(o);
+
+ return 0;
+}
diff --git a/src/tests/strlist-test.c b/src/tests/strlist-test.c
index 4262a001..2bd1645c 100644
--- a/src/tests/strlist-test.c
+++ b/src/tests/strlist-test.c
@@ -1,8 +1,9 @@
#include <stdio.h>
#include <pulse/xmalloc.h>
+#include <pulse/gccmacro.h>
+
#include <pulsecore/strlist.h>
-#include <pulsecore/gccmacro.h>
int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char* argv[]) {
char *t, *u;
@@ -16,7 +17,7 @@ int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char* argv[]) {
t = pa_strlist_tostring(l);
pa_strlist_free(l);
-
+
fprintf(stderr, "1: %s\n", t);
l = pa_strlist_parse(t);
@@ -29,9 +30,9 @@ int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char* argv[]) {
l = pa_strlist_pop(l, &u);
fprintf(stderr, "3: %s\n", u);
pa_xfree(u);
-
+
l = pa_strlist_remove(l, "c");
-
+
t = pa_strlist_tostring(l);
fprintf(stderr, "4: %s\n", t);
pa_xfree(t);
diff --git a/src/tests/sync-playback.c b/src/tests/sync-playback.c
index 39c6aac4..7ab3a25c 100644
--- a/src/tests/sync-playback.c
+++ b/src/tests/sync-playback.c
@@ -1,18 +1,16 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 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
@@ -67,7 +65,7 @@ static void underflow_cb(struct pa_stream *s, void *userdata) {
int i = (int) (long) userdata;
fprintf(stderr, "Stream %i finished\n", i);
-
+
if (++n_streams_ready >= 2*NSTREAMS) {
fprintf(stderr, "We're done\n");
mainloop_api->quit(mainloop_api, 0);
@@ -89,19 +87,19 @@ static void stream_state_callback(pa_stream *s, void *userdata) {
int r, i = (int) (long) userdata;
fprintf(stderr, "Writing data to stream %i.\n", i);
-
+
r = pa_stream_write(s, data, sizeof(data), nop_free_cb, sizeof(data) * i, PA_SEEK_ABSOLUTE);
assert(r == 0);
/* Be notified when this stream is drained */
pa_stream_set_underflow_callback(s, underflow_cb, userdata);
-
+
/* All streams have been set up, let's go! */
if (++n_streams_ready >= NSTREAMS) {
fprintf(stderr, "Uncorking\n");
pa_operation_unref(pa_stream_cork(s, 0, NULL, NULL));
}
-
+
break;
}
@@ -121,7 +119,7 @@ static void context_state_callback(pa_context *c, void *userdata) {
case PA_CONTEXT_AUTHORIZING:
case PA_CONTEXT_SETTING_NAME:
break;
-
+
case PA_CONTEXT_READY: {
int i;
@@ -131,18 +129,18 @@ static void context_state_callback(pa_context *c, void *userdata) {
char name[64];
fprintf(stderr, "Creating stream %i\n", i);
-
+
snprintf(name, sizeof(name), "stream #%i", i);
-
+
streams[i] = pa_stream_new(c, name, &sample_spec, NULL);
assert(streams[i]);
pa_stream_set_state_callback(streams[i], stream_state_callback, (void*) (long) i);
pa_stream_connect_playback(streams[i], NULL, &buffer_attr, PA_STREAM_START_CORKED, NULL, i == 0 ? NULL : streams[0]);
}
-
+
break;
}
-
+
case PA_CONTEXT_TERMINATED:
mainloop_api->quit(mainloop_api, 0);
break;
@@ -163,7 +161,7 @@ int main(int argc, char *argv[]) {
for (i = 0; i < NSTREAMS; i++)
streams[i] = NULL;
-
+
/* Set up a new main loop */
m = pa_mainloop_new();
assert(m);
@@ -187,6 +185,6 @@ int main(int argc, char *argv[]) {
pa_stream_unref(streams[i]);
pa_mainloop_free(m);
-
+
return ret;
}
diff --git a/src/tests/thread-mainloop-test.c b/src/tests/thread-mainloop-test.c
index bf3d4cd2..7a62f85a 100644
--- a/src/tests/thread-mainloop-test.c
+++ b/src/tests/thread-mainloop-test.c
@@ -1,18 +1,16 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 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
@@ -23,18 +21,19 @@
#include <config.h>
#endif
-#include <assert.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <pulse/timeval.h>
#include <pulse/util.h>
-
-#include <pulsecore/gccmacro.h>
#include <pulse/thread-mainloop.h>
+#include <pulse/gccmacro.h>
+
+#include <pulsecore/macro.h>
static void tcb(pa_mainloop_api*a, pa_time_event *e, const struct timeval *tv, void *userdata) {
+ pa_assert_se(pa_threaded_mainloop_in_thread(userdata));
fprintf(stderr, "TIME EVENT START\n");
pa_threaded_mainloop_signal(userdata, 1);
fprintf(stderr, "TIME EVENT END\n");
@@ -45,27 +44,27 @@ int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) {
pa_threaded_mainloop *m;
struct timeval tv;
- m = pa_threaded_mainloop_new();
- assert(m);
- a = pa_threaded_mainloop_get_api(m);
- assert(a);
+ pa_assert_se(m = pa_threaded_mainloop_new());
+ pa_assert_se(a = pa_threaded_mainloop_get_api(m));
pa_threaded_mainloop_start(m);
pa_threaded_mainloop_lock(m);
-
+
+ pa_assert_se(!pa_threaded_mainloop_in_thread(m));
+
pa_gettimeofday(&tv);
tv.tv_sec += 5;
a->time_new(a, &tv, tcb, m);
-
+
fprintf(stderr, "waiting 5s (signal)\n");
pa_threaded_mainloop_wait(m);
fprintf(stderr, "wait completed\n");
pa_threaded_mainloop_accept(m);
fprintf(stderr, "signal accepted\n");
-
+
pa_threaded_mainloop_unlock(m);
-
+
fprintf(stderr, "waiting 5s (sleep)\n");
pa_msleep(5000);
diff --git a/src/tests/thread-test.c b/src/tests/thread-test.c
index 9559cdbb..f29b5e71 100644
--- a/src/tests/thread-test.c
+++ b/src/tests/thread-test.c
@@ -1,18 +1,16 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 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
@@ -42,13 +40,13 @@ static void once_func(void) {
pa_log("once!");
}
-static pa_once_t once = PA_ONCE_INIT;
+static pa_once once = PA_ONCE_INIT;
static void thread_func(void *data) {
pa_tls_set(tls, data);
pa_log("thread_func() for %s starting...", (char*) pa_tls_get(tls));
-
+
pa_mutex_lock(mutex);
for (;;) {
@@ -57,13 +55,13 @@ static void thread_func(void *data) {
pa_log("%s waiting ...", (char*) pa_tls_get(tls));
for (;;) {
-
+
if (magic_number < 0)
goto quit;
if (magic_number != 0)
break;
-
+
pa_cond_wait(cond1, mutex);
}
@@ -72,21 +70,21 @@ static void thread_func(void *data) {
pa_mutex_unlock(mutex);
- pa_once(&once, once_func);
+ pa_run_once(&once, once_func);
pa_cond_signal(cond2, 0);
-
+
pa_log("%s got number %i", (char*) pa_tls_get(tls), k);
-
+
/* Spin! */
for (n = 0; n < k; n++)
pa_thread_yield();
-
+
pa_mutex_lock(mutex);
}
quit:
-
+
pa_mutex_unlock(mutex);
pa_log("thread_func() for %s done...", (char*) pa_tls_get(tls));
@@ -97,25 +95,25 @@ int main(int argc, char *argv[]) {
pa_thread* t[THREADS_MAX];
assert(pa_thread_is_running(pa_thread_self()));
-
- mutex = pa_mutex_new(0);
+
+ mutex = pa_mutex_new(FALSE, FALSE);
cond1 = pa_cond_new();
cond2 = pa_cond_new();
tls = pa_tls_new(pa_xfree);
-
+
for (i = 0; i < THREADS_MAX; i++) {
t[i] = pa_thread_new(thread_func, pa_sprintf_malloc("Thread #%i", i+1));
assert(t[i]);
}
pa_mutex_lock(mutex);
-
+
pa_log("loop-init");
for (k = 0; k < 100; k++) {
assert(magic_number == 0);
-
+
magic_number = (int) rand() % 0x10000;
pa_log("iteration %i (%i)", k, magic_number);
@@ -126,10 +124,10 @@ int main(int argc, char *argv[]) {
}
pa_log("loop-exit");
-
+
magic_number = -1;
pa_cond_signal(cond1, 1);
-
+
pa_mutex_unlock(mutex);
for (i = 0; i < THREADS_MAX; i++)
diff --git a/src/tests/utf8-test.c b/src/tests/utf8-test.c
index 2e9f128a..f1708ad4 100644
--- a/src/tests/utf8-test.c
+++ b/src/tests/utf8-test.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
#include <stdio.h>
#include <assert.h>
@@ -8,13 +6,13 @@
int main(int argc, char *argv[]) {
char *c;
-
+
assert(pa_utf8_valid("hallo"));
assert(pa_utf8_valid("hallo\n"));
assert(!pa_utf8_valid("hüpfburg\n"));
assert(pa_utf8_valid("hallo\n"));
assert(pa_utf8_valid("hüpfburg\n"));
-
+
printf("LATIN1: %s\n", c = pa_utf8_filter("hüpfburg"));
pa_xfree(c);
printf("UTF8: %sx\n", c = pa_utf8_filter("hüpfburg"));
diff --git a/src/tests/voltest.c b/src/tests/voltest.c
index 3de884af..d2c0ff69 100644
--- a/src/tests/voltest.c
+++ b/src/tests/voltest.c
@@ -1,9 +1,7 @@
-/* $Id$ */
-
#include <stdio.h>
#include <pulse/volume.h>
-#include <pulsecore/gccmacro.h>
+#include <pulse/gccmacro.h>
int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) {
pa_volume_t v;
@@ -12,7 +10,7 @@ int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) {
double dB = pa_sw_volume_to_dB(v);
double f = pa_sw_volume_to_linear(v);
-
+
printf("Volume: %3i; percent: %i%%; decibel %0.2f; linear = %0.2f; volume(decibel): %3i; volume(linear): %3i\n",
v, (v*100)/PA_VOLUME_NORM, dB, f, pa_sw_volume_from_dB(dB), pa_sw_volume_from_linear(f));
diff --git a/src/utils/pabrowse.c b/src/utils/pabrowse.c
index 450182f5..f2ed9553 100644
--- a/src/utils/pabrowse.c
+++ b/src/utils/pabrowse.c
@@ -1,18 +1,18 @@
-/* $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
@@ -40,7 +40,7 @@ static void dump_server(const pa_browse_info *i) {
if (i->cookie)
snprintf(t, sizeof(t), "0x%08x", *i->cookie);
-
+
printf("server: %s\n"
"server-version: %s\n"
"user-name: %s\n"
@@ -65,7 +65,7 @@ static void dump_device(const pa_browse_info *i) {
i->device,
i->description ? i->description : "n/a",
i->sample_spec ? ss : "n/a");
-
+
}
static void browser_callback(pa_browser *b, pa_browse_opcode_t c, const pa_browse_info *i, void *userdata) {
@@ -89,7 +89,7 @@ static void browser_callback(pa_browser *b, pa_browse_opcode_t c, const pa_brows
dump_server(i);
dump_device(i);
break;
-
+
case PA_BROWSE_REMOVE_SERVER:
printf("\n=> removed server <%s>\n", i->name);
break;
@@ -109,7 +109,7 @@ static void browser_callback(pa_browser *b, pa_browse_opcode_t c, const pa_brows
static void error_callback(pa_browser *b, const char *s, void *userdata) {
pa_mainloop_api*m = userdata;
-
+
fprintf(stderr, "Failure: %s\n", s);
m->quit(m, 1);
}
@@ -128,7 +128,7 @@ int main(int argc, char *argv[]) {
pa_signal_new(SIGINT, exit_signal_callback, NULL);
pa_signal_new(SIGTERM, exit_signal_callback, NULL);
signal(SIGPIPE, SIG_IGN);
-
+
if (!(browser = pa_browser_new_full(pa_mainloop_get_api(mainloop), PA_BROWSE_FOR_SERVERS|PA_BROWSE_FOR_SINKS|PA_BROWSE_FOR_SOURCES, &s))) {
fprintf(stderr, "pa_browse_new_full(): %s\n", s);
goto finish;
@@ -136,7 +136,7 @@ int main(int argc, char *argv[]) {
pa_browser_set_callback(browser, browser_callback, NULL);
pa_browser_set_error_callback(browser, error_callback, pa_mainloop_get_api(mainloop));
-
+
ret = 0;
pa_mainloop_run(mainloop, &ret);
diff --git a/src/utils/pacat.c b/src/utils/pacat.c
index 1c581f4d..ee784a99 100644
--- a/src/utils/pacat.c
+++ b/src/utils/pacat.c
@@ -1,18 +1,19 @@
-/* $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
@@ -37,10 +38,12 @@
#define TIME_EVENT_USEC 50000
-#if PA_API_VERSION < 9
+#if PA_API_VERSION < 10
#error Invalid PulseAudio API version
#endif
+#define CLEAR_LINE "\x1B[K"
+
static enum { RECORD, PLAYBACK } mode = PLAYBACK;
static pa_context *context = NULL;
@@ -66,6 +69,10 @@ static pa_sample_spec sample_spec = {
static pa_channel_map channel_map;
static int channel_map_set = 0;
+static pa_stream_flags_t flags = 0;
+
+static size_t latency = 0, process_time=0;
+
/* A shortcut for terminating the application */
static void quit(int ret) {
assert(mainloop_api);
@@ -79,20 +86,20 @@ static void do_stream_write(size_t length) {
if (!buffer || !buffer_length)
return;
-
+
l = length;
if (l > buffer_length)
l = buffer_length;
-
+
if (pa_stream_write(stream, (uint8_t*) buffer + buffer_index, l, NULL, 0, PA_SEEK_RELATIVE) < 0) {
fprintf(stderr, "pa_stream_write() failed: %s\n", pa_strerror(pa_context_errno(context)));
quit(1);
return;
}
-
+
buffer_length -= l;
buffer_index += l;
-
+
if (!buffer_length) {
pa_xfree(buffer);
buffer = NULL;
@@ -102,7 +109,8 @@ static void do_stream_write(size_t length) {
/* This is called whenever new data may be written to the stream */
static void stream_write_callback(pa_stream *s, size_t length, void *userdata) {
- assert(s && length);
+ assert(s);
+ assert(length > 0);
if (stdio_event)
mainloop_api->io_enable(stdio_event, PA_IO_EVENT_INPUT);
@@ -116,7 +124,8 @@ static void stream_write_callback(pa_stream *s, size_t length, void *userdata) {
/* This is called whenever new data may is available */
static void stream_read_callback(pa_stream *s, size_t length, void *userdata) {
const void *data;
- assert(s && length);
+ assert(s);
+ assert(length > 0);
if (stdio_event)
mainloop_api->io_enable(stdio_event, PA_IO_EVENT_OUTPUT);
@@ -126,8 +135,9 @@ static void stream_read_callback(pa_stream *s, size_t length, void *userdata) {
quit(1);
return;
}
-
- assert(data && length);
+
+ assert(data);
+ assert(length > 0);
if (buffer) {
fprintf(stderr, "Buffer overrun, dropping incoming data\n");
@@ -156,7 +166,8 @@ static void stream_state_callback(pa_stream *s, void *userdata) {
case PA_STREAM_READY:
if (verbose) {
const pa_buffer_attr *a;
-
+ char cmt[PA_CHANNEL_MAP_SNPRINT_MAX], sst[PA_SAMPLE_SPEC_SNPRINT_MAX];
+
fprintf(stderr, "Stream successfully created.\n");
if (!(a = pa_stream_get_buffer_attr(s)))
@@ -169,13 +180,20 @@ static void stream_state_callback(pa_stream *s, void *userdata) {
assert(mode == RECORD);
fprintf(stderr, "Buffer metrics: maxlength=%u, fragsize=%u\n", a->maxlength, a->fragsize);
}
-
}
+ fprintf(stderr, "Using sample spec '%s', channel map '%s'.\n",
+ pa_sample_spec_snprint(sst, sizeof(sst), pa_stream_get_sample_spec(s)),
+ pa_channel_map_snprint(cmt, sizeof(cmt), pa_stream_get_channel_map(s)));
+
+ fprintf(stderr, "Connected to device %s (%u, %ssuspended).\n",
+ pa_stream_get_device_name(s),
+ pa_stream_get_device_index(s),
+ pa_stream_is_suspended(s) ? "" : "not ");
}
-
+
break;
-
+
case PA_STREAM_FAILED:
default:
fprintf(stderr, "Stream error: %s\n", pa_strerror(pa_context_errno(pa_stream_get_context(s))));
@@ -183,6 +201,45 @@ static void stream_state_callback(pa_stream *s, void *userdata) {
}
}
+static void stream_suspended_callback(pa_stream *s, void *userdata) {
+ assert(s);
+
+ if (verbose) {
+ if (pa_stream_is_suspended(s))
+ fprintf(stderr, "Stream device suspended." CLEAR_LINE " \n");
+ else
+ fprintf(stderr, "Stream device resumed." CLEAR_LINE " \n");
+ }
+}
+
+static void stream_underflow_callback(pa_stream *s, void *userdata) {
+ assert(s);
+
+ if (verbose)
+ fprintf(stderr, "Stream underrun." CLEAR_LINE " \n");
+}
+
+static void stream_overflow_callback(pa_stream *s, void *userdata) {
+ assert(s);
+
+ if (verbose)
+ fprintf(stderr, "Stream overrun." CLEAR_LINE " \n");
+}
+
+static void stream_started_callback(pa_stream *s, void *userdata) {
+ assert(s);
+
+ if (verbose)
+ fprintf(stderr, "Stream started." CLEAR_LINE " \n");
+}
+
+static void stream_moved_callback(pa_stream *s, void *userdata) {
+ assert(s);
+
+ if (verbose)
+ fprintf(stderr, "Stream moved to device %s (%u, %ssuspended)." CLEAR_LINE " \n", pa_stream_get_device_name(s), pa_stream_get_device_index(s), pa_stream_is_suspended(s) ? "" : "not ");
+}
+
/* This is called whenever the context status changes */
static void context_state_callback(pa_context *c, void *userdata) {
assert(c);
@@ -192,14 +249,16 @@ static void context_state_callback(pa_context *c, void *userdata) {
case PA_CONTEXT_AUTHORIZING:
case PA_CONTEXT_SETTING_NAME:
break;
-
+
case PA_CONTEXT_READY: {
int r;
-
- assert(c && !stream);
+ pa_buffer_attr buffer_attr;
+
+ assert(c);
+ assert(!stream);
if (verbose)
- fprintf(stderr, "Connection established.\n");
+ fprintf(stderr, "Connection established." CLEAR_LINE " \n");
if (!(stream = pa_stream_new(c, stream_name, &sample_spec, channel_map_set ? &channel_map : NULL))) {
fprintf(stderr, "pa_stream_new() failed: %s\n", pa_strerror(pa_context_errno(c)));
@@ -209,24 +268,36 @@ static void context_state_callback(pa_context *c, void *userdata) {
pa_stream_set_state_callback(stream, stream_state_callback, NULL);
pa_stream_set_write_callback(stream, stream_write_callback, NULL);
pa_stream_set_read_callback(stream, stream_read_callback, NULL);
+ pa_stream_set_suspended_callback(stream, stream_suspended_callback, NULL);
+ pa_stream_set_moved_callback(stream, stream_moved_callback, NULL);
+ pa_stream_set_underflow_callback(stream, stream_underflow_callback, NULL);
+ pa_stream_set_overflow_callback(stream, stream_overflow_callback, NULL);
+ pa_stream_set_started_callback(stream, stream_started_callback, NULL);
+
+ if (latency > 0) {
+ memset(&buffer_attr, 0, sizeof(buffer_attr));
+ buffer_attr.tlength = latency;
+ buffer_attr.minreq = process_time;
+ flags |= PA_STREAM_ADJUST_LATENCY;
+ }
if (mode == PLAYBACK) {
pa_cvolume cv;
- if ((r = pa_stream_connect_playback(stream, device, NULL, 0, pa_cvolume_set(&cv, sample_spec.channels, volume), NULL)) < 0) {
+ if ((r = pa_stream_connect_playback(stream, device, latency > 0 ? &buffer_attr : NULL, flags, pa_cvolume_set(&cv, sample_spec.channels, volume), NULL)) < 0) {
fprintf(stderr, "pa_stream_connect_playback() failed: %s\n", pa_strerror(pa_context_errno(c)));
goto fail;
}
-
+
} else {
- if ((r = pa_stream_connect_record(stream, device, NULL, 0)) < 0) {
+ if ((r = pa_stream_connect_record(stream, device, latency > 0 ? &buffer_attr : NULL, flags)) < 0) {
fprintf(stderr, "pa_stream_connect_record() failed: %s\n", pa_strerror(pa_context_errno(c)));
goto fail;
}
}
-
+
break;
}
-
+
case PA_CONTEXT_TERMINATED:
quit(0);
break;
@@ -238,10 +309,10 @@ static void context_state_callback(pa_context *c, void *userdata) {
}
return;
-
+
fail:
quit(1);
-
+
}
/* Connection draining complete */
@@ -257,14 +328,14 @@ static void stream_drain_complete(pa_stream*s, int success, void *userdata) {
fprintf(stderr, "Failed to drain stream: %s\n", pa_strerror(pa_context_errno(context)));
quit(1);
}
-
- if (verbose)
+
+ if (verbose)
fprintf(stderr, "Playback stream drained.\n");
pa_stream_disconnect(stream);
pa_stream_unref(stream);
stream = NULL;
-
+
if (!(o = pa_context_drain(context, context_drain_complete, NULL)))
pa_context_disconnect(context);
else {
@@ -277,7 +348,10 @@ static void stream_drain_complete(pa_stream*s, int success, void *userdata) {
static void stdin_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
size_t l, w = 0;
ssize_t r;
- assert(a == mainloop_api && e && stdio_event == e);
+
+ assert(a == mainloop_api);
+ assert(e);
+ assert(stdio_event == e);
if (buffer) {
mainloop_api->io_enable(stdio_event, PA_IO_EVENT_NULL);
@@ -286,7 +360,7 @@ static void stdin_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_even
if (!stream || pa_stream_get_state(stream) != PA_STREAM_READY || !(l = w = pa_stream_writable_size(stream)))
l = 4096;
-
+
buffer = pa_xmalloc(l);
if ((r = read(fd, buffer, l)) <= 0) {
@@ -296,17 +370,17 @@ static void stdin_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_even
if (stream) {
pa_operation *o;
-
+
if (!(o = pa_stream_drain(stream, stream_drain_complete, NULL))) {
fprintf(stderr, "pa_stream_drain(): %s\n", pa_strerror(pa_context_errno(context)));
quit(1);
return;
}
-
+
pa_operation_unref(o);
} else
quit(0);
-
+
} else {
fprintf(stderr, "read() failed: %s\n", strerror(errno));
quit(1);
@@ -327,7 +401,10 @@ static void stdin_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_even
/* Some data may be written to STDOUT */
static void stdout_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
ssize_t r;
- assert(a == mainloop_api && e && stdio_event == e);
+
+ assert(a == mainloop_api);
+ assert(e);
+ assert(stdio_event == e);
if (!buffer) {
mainloop_api->io_enable(stdio_event, PA_IO_EVENT_NULL);
@@ -335,7 +412,7 @@ static void stdout_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_eve
}
assert(buffer_length);
-
+
if ((r = write(fd, (uint8_t*) buffer+buffer_index, buffer_length)) <= 0) {
fprintf(stderr, "write() failed: %s\n", strerror(errno));
quit(1);
@@ -364,14 +441,14 @@ static void exit_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig,
/* Show the current latency */
static void stream_update_timing_callback(pa_stream *s, int success, void *userdata) {
- pa_usec_t latency, usec;
+ pa_usec_t l, usec;
int negative = 0;
-
+
assert(s);
if (!success ||
pa_stream_get_time(s, &usec) < 0 ||
- pa_stream_get_latency(s, &latency, &negative) < 0) {
+ pa_stream_get_latency(s, &l, &negative) < 0) {
fprintf(stderr, "Failed to get latency: %s\n", pa_strerror(pa_context_errno(context)));
quit(1);
return;
@@ -379,7 +456,7 @@ static void stream_update_timing_callback(pa_stream *s, int success, void *userd
fprintf(stderr, "Time: %0.3f sec; Latency: %0.0f usec. \r",
(float) usec / 1000000,
- (float) latency * (negative?-1:1));
+ (float) l * (negative?-1:1));
}
/* Someone requested that the latency is shown */
@@ -387,13 +464,13 @@ static void sigusr1_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int s
if (!stream)
return;
-
+
pa_operation_unref(pa_stream_update_timing_info(stream, stream_update_timing_callback, NULL));
}
static void time_event_callback(pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata) {
struct timeval next;
-
+
if (stream && pa_stream_get_state(stream) == PA_STREAM_READY) {
pa_operation *o;
if (!(o = pa_stream_update_timing_info(stream, stream_update_timing_callback, NULL)))
@@ -426,7 +503,18 @@ static void help(const char *argv0) {
" float32be, ulaw, alaw (defaults to s16ne)\n"
" --channels=CHANNELS The number of channels, 1 for mono, 2 for stereo\n"
" (defaults to 2)\n"
- " --channel-map=CHANNELMAP Channel map to use instead of the default\n",
+ " --channel-map=CHANNELMAP Channel map to use instead of the default\n"
+ " --fix-format Take the sample format from the sink the stream is\n"
+ " being connected to.\n"
+ " --fix-rate Take the sampling rate from the sink the stream is\n"
+ " being connected to.\n"
+ " --fix-channels Take the number of channels and the channel map\n"
+ " from the sink the stream is being connected to.\n"
+ " --no-remix Don't upmix or downmix channels.\n"
+ " --no-remap Map channels by index instead of name.\n"
+ " --latency=BYTES Request the specified latency in bytes.\n"
+ " --process-time=BYTES Request the specified process time per request in bytes.\n"
+ ,
argv0);
}
@@ -438,6 +526,13 @@ enum {
ARG_SAMPLEFORMAT,
ARG_CHANNELS,
ARG_CHANNELMAP,
+ ARG_FIX_FORMAT,
+ ARG_FIX_RATE,
+ ARG_FIX_CHANNELS,
+ ARG_NO_REMAP,
+ ARG_NO_REMIX,
+ ARG_LATENCY,
+ ARG_PROCESS_TIME
};
int main(int argc, char *argv[]) {
@@ -447,21 +542,28 @@ int main(int argc, char *argv[]) {
pa_time_event *time_event = NULL;
static const struct option long_options[] = {
- {"record", 0, NULL, 'r'},
- {"playback", 0, NULL, 'p'},
- {"device", 1, NULL, 'd'},
- {"server", 1, NULL, 's'},
- {"client-name", 1, NULL, 'n'},
- {"stream-name", 1, NULL, ARG_STREAM_NAME},
- {"version", 0, NULL, ARG_VERSION},
- {"help", 0, NULL, 'h'},
- {"verbose", 0, NULL, 'v'},
- {"volume", 1, NULL, ARG_VOLUME},
- {"rate", 1, NULL, ARG_SAMPLERATE},
- {"format", 1, NULL, ARG_SAMPLEFORMAT},
- {"channels", 1, NULL, ARG_CHANNELS},
- {"channel-map", 1, NULL, ARG_CHANNELMAP},
- {NULL, 0, NULL, 0}
+ {"record", 0, NULL, 'r'},
+ {"playback", 0, NULL, 'p'},
+ {"device", 1, NULL, 'd'},
+ {"server", 1, NULL, 's'},
+ {"client-name", 1, NULL, 'n'},
+ {"stream-name", 1, NULL, ARG_STREAM_NAME},
+ {"version", 0, NULL, ARG_VERSION},
+ {"help", 0, NULL, 'h'},
+ {"verbose", 0, NULL, 'v'},
+ {"volume", 1, NULL, ARG_VOLUME},
+ {"rate", 1, NULL, ARG_SAMPLERATE},
+ {"format", 1, NULL, ARG_SAMPLEFORMAT},
+ {"channels", 1, NULL, ARG_CHANNELS},
+ {"channel-map", 1, NULL, ARG_CHANNELMAP},
+ {"fix-format", 0, NULL, ARG_FIX_FORMAT},
+ {"fix-rate", 0, NULL, ARG_FIX_RATE},
+ {"fix-channels", 0, NULL, ARG_FIX_CHANNELS},
+ {"no-remap", 0, NULL, ARG_NO_REMAP},
+ {"no-remix", 0, NULL, ARG_NO_REMIX},
+ {"latency", 1, NULL, ARG_LATENCY},
+ {"process-time", 1, NULL, ARG_PROCESS_TIME},
+ {NULL, 0, NULL, 0}
};
if (!(bn = strrchr(argv[0], '/')))
@@ -481,7 +583,7 @@ int main(int argc, char *argv[]) {
help(bn);
ret = 0;
goto quit;
-
+
case ARG_VERSION:
printf("pacat "PACKAGE_VERSION"\nCompiled with libpulse %s\nLinked with libpulse %s\n", pa_get_headers_version(), pa_get_library_version());
ret = 0;
@@ -525,7 +627,7 @@ int main(int argc, char *argv[]) {
break;
}
- case ARG_CHANNELS:
+ case ARG_CHANNELS:
sample_spec.channels = atoi(optarg);
break;
@@ -539,13 +641,47 @@ int main(int argc, char *argv[]) {
case ARG_CHANNELMAP:
if (!pa_channel_map_parse(&channel_map, optarg)) {
- fprintf(stderr, "Invalid channel map\n");
+ fprintf(stderr, "Invalid channel map '%s'\n", optarg);
goto quit;
}
channel_map_set = 1;
break;
-
+
+ case ARG_FIX_CHANNELS:
+ flags |= PA_STREAM_FIX_CHANNELS;
+ break;
+
+ case ARG_FIX_RATE:
+ flags |= PA_STREAM_FIX_RATE;
+ break;
+
+ case ARG_FIX_FORMAT:
+ flags |= PA_STREAM_FIX_FORMAT;
+ break;
+
+ case ARG_NO_REMIX:
+ flags |= PA_STREAM_NO_REMIX_CHANNELS;
+ break;
+
+ case ARG_NO_REMAP:
+ flags |= PA_STREAM_NO_REMAP_CHANNELS;
+ break;
+
+ case ARG_LATENCY:
+ if (((latency = atoi(optarg))) <= 0) {
+ fprintf(stderr, "Invalid latency specification '%s'\n", optarg);
+ goto quit;
+ }
+ break;
+
+ case ARG_PROCESS_TIME:
+ if (((process_time = atoi(optarg))) <= 0) {
+ fprintf(stderr, "Invalid process time specification '%s'\n", optarg);
+ goto quit;
+ }
+ break;
+
default:
goto quit;
}
@@ -560,7 +696,7 @@ int main(int argc, char *argv[]) {
fprintf(stderr, "Channel map doesn't match sample specification\n");
goto quit;
}
-
+
if (verbose) {
char t[PA_SAMPLE_SPEC_SNPRINT_MAX];
pa_sample_spec_snprint(t, sizeof(t), &sample_spec);
@@ -570,22 +706,22 @@ int main(int argc, char *argv[]) {
if (!(optind >= argc)) {
if (optind+1 == argc) {
int fd;
-
+
if ((fd = open(argv[optind], mode == PLAYBACK ? O_RDONLY : O_WRONLY|O_TRUNC|O_CREAT, 0666)) < 0) {
fprintf(stderr, "open(): %s\n", strerror(errno));
goto quit;
}
-
+
if (dup2(fd, mode == PLAYBACK ? 0 : 1) < 0) {
fprintf(stderr, "dup2(): %s\n", strerror(errno));
goto quit;
}
-
+
close(fd);
if (!stream_name)
stream_name = pa_xstrdup(argv[optind]);
-
+
} else {
fprintf(stderr, "Too many arguments.\n");
goto quit;
@@ -616,7 +752,7 @@ int main(int argc, char *argv[]) {
#ifdef SIGPIPE
signal(SIGPIPE, SIG_IGN);
#endif
-
+
if (!(stdio_event = mainloop_api->io_new(mainloop_api,
mode == PLAYBACK ? STDIN_FILENO : STDOUT_FILENO,
mode == PLAYBACK ? PA_IO_EVENT_INPUT : PA_IO_EVENT_OUTPUT,
@@ -641,7 +777,7 @@ int main(int argc, char *argv[]) {
pa_gettimeofday(&tv);
pa_timeval_add(&tv, TIME_EVENT_USEC);
-
+
if (!(time_event = mainloop_api->time_new(mainloop_api, &tv, time_event_callback, NULL))) {
fprintf(stderr, "time_new() failed.\n");
goto quit;
@@ -653,7 +789,7 @@ int main(int argc, char *argv[]) {
fprintf(stderr, "pa_mainloop_run() failed.\n");
goto quit;
}
-
+
quit:
if (stream)
pa_stream_unref(stream);
@@ -670,7 +806,7 @@ quit:
assert(mainloop_api);
mainloop_api->time_free(time_event);
}
-
+
if (m) {
pa_signal_done();
pa_mainloop_free(m);
@@ -682,6 +818,6 @@ quit:
pa_xfree(device);
pa_xfree(client_name);
pa_xfree(stream_name);
-
+
return ret;
}
diff --git a/src/utils/pacmd.c b/src/utils/pacmd.c
index b9912701..67d95252 100644
--- a/src/utils/pacmd.c
+++ b/src/utils/pacmd.c
@@ -1,18 +1,18 @@
-/* $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
@@ -34,6 +34,7 @@
#include <pulse/error.h>
#include <pulse/util.h>
+#include <pulse/xmalloc.h>
#include <pulsecore/core-util.h>
#include <pulsecore/log.h>
@@ -47,8 +48,9 @@ int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char*argv[]) {
char ibuf[256], obuf[256];
size_t ibuf_index, ibuf_length, obuf_index, obuf_length;
fd_set ifds, ofds;
+ char *cli;
- if (pa_pid_file_check_running(&pid) < 0) {
+ if (pa_pid_file_check_running(&pid, "pulseaudio") < 0) {
pa_log("no PulseAudio daemon running");
goto fail;
}
@@ -60,25 +62,30 @@ int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char*argv[]) {
memset(&sa, 0, sizeof(sa));
sa.sun_family = AF_UNIX;
- pa_runtime_path("cli", sa.sun_path, sizeof(sa.sun_path));
+
+ if (!(cli = pa_runtime_path("cli")))
+ goto fail;
+
+ pa_strlcpy(sa.sun_path, cli, sizeof(sa.sun_path));
+ pa_xfree(cli);
for (i = 0; i < 5; i++) {
int r;
-
+
if ((r = connect(fd, (struct sockaddr*) &sa, sizeof(sa))) < 0 && (errno != ECONNREFUSED && errno != ENOENT)) {
pa_log("connect(): %s", strerror(errno));
goto fail;
}
-
+
if (r >= 0)
break;
- if (pa_pid_file_kill(SIGUSR2, NULL) < 0) {
+ if (pa_pid_file_kill(SIGUSR2, NULL, "pulseaudio") < 0) {
pa_log("failed to kill PulseAudio daemon.");
goto fail;
}
- pa_msleep(50);
+ pa_msleep(300);
}
if (i >= 5) {
@@ -94,7 +101,7 @@ int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char*argv[]) {
FD_SET(fd, &ifds);
FD_ZERO(&ofds);
-
+
for (;;) {
if (select(FD_SETSIZE, &ifds, &ofds, NULL, NULL) < 0) {
pa_log("select(): %s", strerror(errno));
@@ -104,19 +111,19 @@ int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char*argv[]) {
if (FD_ISSET(0, &ifds)) {
ssize_t r;
assert(!ibuf_length);
-
+
if ((r = read(0, ibuf, sizeof(ibuf))) <= 0) {
if (r == 0)
break;
-
+
pa_log("read(): %s", strerror(errno));
goto fail;
}
-
+
ibuf_length = (size_t) r;
ibuf_index = 0;
}
-
+
if (FD_ISSET(fd, &ifds)) {
ssize_t r;
assert(!obuf_length);
@@ -124,7 +131,7 @@ int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char*argv[]) {
if ((r = read(fd, obuf, sizeof(obuf))) <= 0) {
if (r == 0)
break;
-
+
pa_log("read(): %s", strerror(errno));
goto fail;
}
@@ -136,12 +143,12 @@ int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char*argv[]) {
if (FD_ISSET(1, &ofds)) {
ssize_t r;
assert(obuf_length);
-
+
if ((r = write(1, obuf + obuf_index, obuf_length)) < 0) {
pa_log("write(): %s", strerror(errno));
goto fail;
}
-
+
obuf_length -= (size_t) r;
obuf_index += obuf_index;
@@ -150,12 +157,12 @@ int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char*argv[]) {
if (FD_ISSET(fd, &ofds)) {
ssize_t r;
assert(ibuf_length);
-
+
if ((r = write(fd, ibuf + ibuf_index, ibuf_length)) < 0) {
pa_log("write(): %s", strerror(errno));
goto fail;
}
-
+
ibuf_length -= (size_t) r;
ibuf_index += obuf_index;
@@ -163,24 +170,24 @@ int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char*argv[]) {
FD_ZERO(&ifds);
FD_ZERO(&ofds);
-
+
if (obuf_length <= 0)
FD_SET(fd, &ifds);
else
FD_SET(1, &ofds);
-
+
if (ibuf_length <= 0)
FD_SET(0, &ifds);
else
FD_SET(fd, &ofds);
}
-
+
ret = 0;
-
+
fail:
if (fd >= 0)
close(fd);
-
+
return ret;
}
diff --git a/src/utils/pactl.c b/src/utils/pactl.c
index 110585f7..4cca2f86 100644
--- a/src/utils/pactl.c
+++ b/src/utils/pactl.c
@@ -1,18 +1,18 @@
-/* $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
@@ -36,6 +36,7 @@
#include <sndfile.h>
#include <pulse/pulseaudio.h>
+#include <pulsecore/core-util.h>
#if PA_API_VERSION < 10
#error Invalid PulseAudio API version
@@ -46,8 +47,10 @@
static pa_context *context = NULL;
static pa_mainloop_api *mainloop_api = NULL;
-static char *device = NULL, *sample_name = NULL, *sink_name = NULL, *source_name = NULL;
+static char *device = NULL, *sample_name = NULL, *sink_name = NULL, *source_name = NULL, *module_name = NULL, *module_args = NULL;
static uint32_t sink_input_idx = PA_INVALID_INDEX, source_output_idx = PA_INVALID_INDEX;
+static uint32_t module_index;
+static int suspend;
static SNDFILE *sndfile = NULL;
static pa_stream *sample_stream = NULL;
@@ -67,7 +70,11 @@ static enum {
REMOVE_SAMPLE,
LIST,
MOVE_SINK_INPUT,
- MOVE_SOURCE_OUTPUT
+ MOVE_SOURCE_OUTPUT,
+ LOAD_MODULE,
+ UNLOAD_MODULE,
+ SUSPEND_SINK,
+ SUSPEND_SOURCE,
} action = NONE;
static void quit(int ret) {
@@ -112,13 +119,13 @@ static void stat_callback(pa_context *c, const pa_stat_info *i, void *userdata)
pa_bytes_snprint(s, sizeof(s), i->scache_size);
printf("Sample cache size: %s\n", s);
-
+
complete_action();
}
static void get_server_info_callback(pa_context *c, const pa_server_info *i, void *useerdata) {
char s[PA_SAMPLE_SPEC_SNPRINT_MAX];
-
+
if (!i) {
fprintf(stderr, "Failed to get server information: %s\n", pa_strerror(pa_context_errno(c)));
quit(1);
@@ -149,7 +156,8 @@ static void get_server_info_callback(pa_context *c, const pa_server_info *i, voi
static void get_sink_info_callback(pa_context *c, const pa_sink_info *i, int is_last, void *userdata) {
char s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
-
+ char *pl;
+
if (is_last < 0) {
fprintf(stderr, "Failed to get sink information: %s\n", pa_strerror(pa_context_errno(c)));
quit(1);
@@ -160,7 +168,7 @@ static void get_sink_info_callback(pa_context *c, const pa_sink_info *i, int is_
complete_action();
return;
}
-
+
assert(i);
if (nl)
@@ -170,32 +178,37 @@ static void get_sink_info_callback(pa_context *c, const pa_sink_info *i, int is_
printf("*** Sink #%u ***\n"
"Name: %s\n"
"Driver: %s\n"
- "Description: %s\n"
"Sample Specification: %s\n"
"Channel Map: %s\n"
"Owner Module: %u\n"
"Volume: %s\n"
- "Monitor Source: %u\n"
- "Latency: %0.0f usec\n"
- "Flags: %s%s%s\n",
+ "Monitor Source: %s\n"
+ "Latency: %0.0f usec, configured %0.0f usec\n"
+ "Flags: %s%s%s%s%s%s\n"
+ "Properties:\n%s",
i->index,
i->name,
- i->driver,
- i->description,
+ pa_strnull(i->driver),
pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
i->owner_module,
i->mute ? "muted" : pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
- i->monitor_source,
- (double) i->latency,
+ pa_strnull(i->monitor_source_name),
+ (double) i->latency, (double) i->configured_latency,
+ i->flags & PA_SINK_HARDWARE ? "HARDWARE " : "",
+ i->flags & PA_SINK_NETWORK ? "NETWORK " : "",
+ i->flags & PA_SINK_HW_MUTE_CTRL ? "HW_MUTE_CTRL " : "",
i->flags & PA_SINK_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
+ i->flags & PA_SINK_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "",
i->flags & PA_SINK_LATENCY ? "LATENCY " : "",
- i->flags & PA_SINK_HARDWARE ? "HARDWARE" : "");
+ pl = pa_proplist_to_string(i->proplist));
+ pa_xfree(pl);
}
static void get_source_info_callback(pa_context *c, const pa_source_info *i, int is_last, void *userdata) {
- char s[PA_SAMPLE_SPEC_SNPRINT_MAX], t[32], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+ char s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+ char *pl;
if (is_last < 0) {
fprintf(stderr, "Failed to get source information: %s\n", pa_strerror(pa_context_errno(c)));
@@ -207,40 +220,42 @@ static void get_source_info_callback(pa_context *c, const pa_source_info *i, int
complete_action();
return;
}
-
+
assert(i);
if (nl)
printf("\n");
nl = 1;
- snprintf(t, sizeof(t), "%u", i->monitor_of_sink);
-
printf("*** Source #%u ***\n"
"Name: %s\n"
"Driver: %s\n"
- "Description: %s\n"
"Sample Specification: %s\n"
"Channel Map: %s\n"
"Owner Module: %u\n"
"Volume: %s\n"
"Monitor of Sink: %s\n"
- "Latency: %0.0f usec\n"
- "Flags: %s%s%s\n",
+ "Latency: %0.0f usec, configured %0.0f usec\n"
+ "Flags: %s%s%s%s%s%s\n"
+ "Properties:\n%s",
i->index,
i->name,
- i->driver,
- i->description,
+ pa_strnull(i->driver),
pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
i->owner_module,
i->mute ? "muted" : pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
- i->monitor_of_sink != PA_INVALID_INDEX ? t : "no",
- (double) i->latency,
+ i->monitor_of_sink_name ? i->monitor_of_sink_name : "n/a",
+ (double) i->latency, (double) i->configured_latency,
+ i->flags & PA_SOURCE_HARDWARE ? "HARDWARE " : "",
+ i->flags & PA_SOURCE_NETWORK ? "NETWORK " : "",
+ i->flags & PA_SOURCE_HW_MUTE_CTRL ? "HW_MUTE_CTRL " : "",
i->flags & PA_SOURCE_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
+ i->flags & PA_SOURCE_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "",
i->flags & PA_SOURCE_LATENCY ? "LATENCY " : "",
- i->flags & PA_SOURCE_HARDWARE ? "HARDWARE" : "");
+ pl = pa_proplist_to_string(i->proplist));
+ pa_xfree(pl);
}
static void get_module_info_callback(pa_context *c, const pa_module_info *i, int is_last, void *userdata) {
@@ -256,7 +271,7 @@ static void get_module_info_callback(pa_context *c, const pa_module_info *i, int
complete_action();
return;
}
-
+
assert(i);
if (nl)
@@ -264,7 +279,7 @@ static void get_module_info_callback(pa_context *c, const pa_module_info *i, int
nl = 1;
snprintf(t, sizeof(t), "%u", i->n_used);
-
+
printf("*** Module #%u ***\n"
"Name: %s\n"
"Argument: %s\n"
@@ -272,13 +287,14 @@ static void get_module_info_callback(pa_context *c, const pa_module_info *i, int
"Auto unload: %s\n",
i->index,
i->name,
- i->argument,
+ i->argument ? i->argument : "",
i->n_used != PA_INVALID_INDEX ? t : "n/a",
- i->auto_unload ? "yes" : "no");
+ pa_yes_no(i->auto_unload));
}
static void get_client_info_callback(pa_context *c, const pa_client_info *i, int is_last, void *userdata) {
char t[32];
+ char *pl;
if (is_last < 0) {
fprintf(stderr, "Failed to get client information: %s\n", pa_strerror(pa_context_errno(c)));
@@ -290,7 +306,7 @@ static void get_client_info_callback(pa_context *c, const pa_client_info *i, int
complete_action();
return;
}
-
+
assert(i);
if (nl)
@@ -298,19 +314,22 @@ static void get_client_info_callback(pa_context *c, const pa_client_info *i, int
nl = 1;
snprintf(t, sizeof(t), "%u", i->owner_module);
-
+
printf("*** Client #%u ***\n"
- "Name: %s\n"
"Driver: %s\n"
- "Owner Module: %s\n",
+ "Owner Module: %s\n"
+ "Properties:\n%s",
i->index,
- i->name,
- i->driver,
- i->owner_module != PA_INVALID_INDEX ? t : "n/a");
+ pa_strnull(i->driver),
+ i->owner_module != PA_INVALID_INDEX ? t : "n/a",
+ pl = pa_proplist_to_string(i->proplist));
+
+ pa_xfree(pl);
}
static void get_sink_input_info_callback(pa_context *c, const pa_sink_input_info *i, int is_last, void *userdata) {
char t[32], k[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+ char *pl;
if (is_last < 0) {
fprintf(stderr, "Failed to get sink input information: %s\n", pa_strerror(pa_context_errno(c)));
@@ -322,7 +341,7 @@ static void get_sink_input_info_callback(pa_context *c, const pa_sink_input_info
complete_action();
return;
}
-
+
assert(i);
if (nl)
@@ -331,9 +350,8 @@ static void get_sink_input_info_callback(pa_context *c, const pa_sink_input_info
snprintf(t, sizeof(t), "%u", i->owner_module);
snprintf(k, sizeof(k), "%u", i->client);
-
+
printf("*** Sink Input #%u ***\n"
- "Name: %s\n"
"Driver: %s\n"
"Owner Module: %s\n"
"Client: %s\n"
@@ -343,24 +361,27 @@ static void get_sink_input_info_callback(pa_context *c, const pa_sink_input_info
"Volume: %s\n"
"Buffer Latency: %0.0f usec\n"
"Sink Latency: %0.0f usec\n"
- "Resample method: %s\n",
+ "Resample method: %s\n"
+ "Properties:\n%s",
i->index,
- i->name,
- i->driver,
+ pa_strnull(i->driver),
i->owner_module != PA_INVALID_INDEX ? t : "n/a",
i->client != PA_INVALID_INDEX ? k : "n/a",
i->sink,
pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
- pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
+ i->mute ? "muted" : pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
(double) i->buffer_usec,
(double) i->sink_usec,
- i->resample_method ? i->resample_method : "n/a");
-}
+ i->resample_method ? i->resample_method : "n/a",
+ pl = pa_proplist_to_string(i->proplist));
+ pa_xfree(pl);
+}
static void get_source_output_info_callback(pa_context *c, const pa_source_output_info *i, int is_last, void *userdata) {
char t[32], k[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+ char *pl;
if (is_last < 0) {
fprintf(stderr, "Failed to get source output information: %s\n", pa_strerror(pa_context_errno(c)));
@@ -372,19 +393,18 @@ static void get_source_output_info_callback(pa_context *c, const pa_source_outpu
complete_action();
return;
}
-
+
assert(i);
if (nl)
printf("\n");
nl = 1;
-
+
snprintf(t, sizeof(t), "%u", i->owner_module);
snprintf(k, sizeof(k), "%u", i->client);
-
+
printf("*** Source Output #%u ***\n"
- "Name: %s\n"
"Driver: %s\n"
"Owner Module: %s\n"
"Client: %s\n"
@@ -393,10 +413,10 @@ static void get_source_output_info_callback(pa_context *c, const pa_source_outpu
"Channel Map: %s\n"
"Buffer Latency: %0.0f usec\n"
"Source Latency: %0.0f usec\n"
- "Resample method: %s\n",
+ "Resample method: %s\n"
+ "Properties:\n%s",
i->index,
- i->name,
- i->driver,
+ pa_strnull(i->driver),
i->owner_module != PA_INVALID_INDEX ? t : "n/a",
i->client != PA_INVALID_INDEX ? k : "n/a",
i->source,
@@ -404,11 +424,15 @@ static void get_source_output_info_callback(pa_context *c, const pa_source_outpu
pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
(double) i->buffer_usec,
(double) i->source_usec,
- i->resample_method ? i->resample_method : "n/a");
+ i->resample_method ? i->resample_method : "n/a",
+ pl = pa_proplist_to_string(i->proplist));
+
+ pa_xfree(pl);
}
static void get_sample_info_callback(pa_context *c, const pa_sample_info *i, int is_last, void *userdata) {
char t[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+ char *pl;
if (is_last < 0) {
fprintf(stderr, "Failed to get sample information: %s\n", pa_strerror(pa_context_errno(c)));
@@ -420,16 +444,16 @@ static void get_sample_info_callback(pa_context *c, const pa_sample_info *i, int
complete_action();
return;
}
-
+
assert(i);
if (nl)
printf("\n");
nl = 1;
-
+
pa_bytes_snprint(t, sizeof(t), i->bytes);
-
+
printf("*** Sample #%u ***\n"
"Name: %s\n"
"Volume: %s\n"
@@ -438,7 +462,8 @@ static void get_sample_info_callback(pa_context *c, const pa_sample_info *i, int
"Duration: %0.1fs\n"
"Size: %s\n"
"Lazy: %s\n"
- "Filename: %s\n",
+ "Filename: %s\n"
+ "Properties:\n%s",
i->index,
i->name,
pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
@@ -446,8 +471,11 @@ static void get_sample_info_callback(pa_context *c, const pa_sample_info *i, int
pa_sample_spec_valid(&i->sample_spec) ? pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map) : "n/a",
(double) i->duration/1000000,
t,
- i->lazy ? "yes" : "no",
- i->filename ? i->filename : "n/a");
+ pa_yes_no(i->lazy),
+ i->filename ? i->filename : "n/a",
+ pl = pa_proplist_to_string(i->proplist));
+
+ pa_xfree(pl);
}
static void get_autoload_info_callback(pa_context *c, const pa_autoload_info *i, int is_last, void *userdata) {
@@ -461,7 +489,7 @@ static void get_autoload_info_callback(pa_context *c, const pa_autoload_info *i,
complete_action();
return;
}
-
+
assert(i);
if (nl)
@@ -477,7 +505,7 @@ static void get_autoload_info_callback(pa_context *c, const pa_autoload_info *i,
i->name,
i->type == PA_AUTOLOAD_SINK ? "sink" : "source",
i->module,
- i->argument);
+ i->argument ? i->argument : "");
}
static void simple_callback(pa_context *c, int success, void *userdata) {
@@ -490,6 +518,18 @@ static void simple_callback(pa_context *c, int success, void *userdata) {
complete_action();
}
+static void index_callback(pa_context *c, uint32_t idx, void *userdata) {
+ if (idx == PA_INVALID_INDEX) {
+ fprintf(stderr, "Failure: %s\n", pa_strerror(pa_context_errno(c)));
+ quit(1);
+ return;
+ }
+
+ printf("%u\n", idx);
+
+ complete_action();
+}
+
static void stream_state_callback(pa_stream *s, void *userdata) {
assert(s);
@@ -497,11 +537,11 @@ static void stream_state_callback(pa_stream *s, void *userdata) {
case PA_STREAM_CREATING:
case PA_STREAM_READY:
break;
-
+
case PA_STREAM_TERMINATED:
drain();
break;
-
+
case PA_STREAM_FAILED:
default:
fprintf(stderr, "Failed to upload sample: %s\n", pa_strerror(pa_context_errno(pa_stream_get_context(s))));
@@ -524,7 +564,7 @@ static void stream_write_callback(pa_stream *s, size_t length, void *userdata) {
fprintf(stderr, "Premature end of file\n");
quit(1);
}
-
+
pa_stream_write(s, d, length, pa_xfree, 0, PA_SEEK_RELATIVE);
sample_length -= length;
@@ -551,7 +591,7 @@ static void context_state_callback(pa_context *c, void *userdata) {
pa_operation_unref(pa_context_get_server_info(c, get_server_info_callback, NULL));
break;
- case PLAY_SAMPLE:
+ case PLAY_SAMPLE:
pa_operation_unref(pa_context_play_sample(c, sample_name, device, PA_VOLUME_NORM, simple_callback, NULL));
break;
@@ -562,12 +602,12 @@ static void context_state_callback(pa_context *c, void *userdata) {
case UPLOAD_SAMPLE:
sample_stream = pa_stream_new(c, sample_name, &sample_spec, NULL);
assert(sample_stream);
-
+
pa_stream_set_state_callback(sample_stream, stream_state_callback, NULL);
pa_stream_set_write_callback(sample_stream, stream_write_callback, NULL);
pa_stream_connect_upload(sample_stream, sample_length);
break;
-
+
case EXIT:
pa_operation_unref(pa_context_exit_daemon(c, NULL, NULL));
drain();
@@ -578,7 +618,7 @@ static void context_state_callback(pa_context *c, void *userdata) {
pa_operation_unref(pa_context_get_sink_info_list(c, get_sink_info_callback, NULL));
pa_operation_unref(pa_context_get_source_info_list(c, get_source_info_callback, NULL));
pa_operation_unref(pa_context_get_sink_input_info_list(c, get_sink_input_info_callback, NULL));
- pa_operation_unref(pa_context_get_source_output_info_list(c, get_source_output_info_callback, NULL));
+ pa_operation_unref(pa_context_get_source_output_info_list(c, get_source_output_info_callback, NULL));
pa_operation_unref(pa_context_get_client_info_list(c, get_client_info_callback, NULL));
pa_operation_unref(pa_context_get_sample_info_list(c, get_sample_info_callback, NULL));
pa_operation_unref(pa_context_get_autoload_info_list(c, get_autoload_info_callback, NULL));
@@ -591,7 +631,29 @@ static void context_state_callback(pa_context *c, void *userdata) {
case MOVE_SOURCE_OUTPUT:
pa_operation_unref(pa_context_move_source_output_by_name(c, source_output_idx, source_name, simple_callback, NULL));
break;
-
+
+ case LOAD_MODULE:
+ pa_operation_unref(pa_context_load_module(c, module_name, module_args, index_callback, NULL));
+ break;
+
+ case UNLOAD_MODULE:
+ pa_operation_unref(pa_context_unload_module(c, module_index, simple_callback, NULL));
+ break;
+
+ case SUSPEND_SINK:
+ if (sink_name)
+ pa_operation_unref(pa_context_suspend_sink_by_name(c, sink_name, suspend, simple_callback, NULL));
+ else
+ pa_operation_unref(pa_context_suspend_sink_by_index(c, PA_INVALID_INDEX, suspend, simple_callback, NULL));
+ break;
+
+ case SUSPEND_SOURCE:
+ if (source_name)
+ pa_operation_unref(pa_context_suspend_source_by_name(c, source_name, suspend, simple_callback, NULL));
+ else
+ pa_operation_unref(pa_context_suspend_source_by_index(c, PA_INVALID_INDEX, suspend, simple_callback, NULL));
+ break;
+
default:
assert(0);
}
@@ -620,14 +682,18 @@ static void help(const char *argv0) {
"%s [options] exit\n"
"%s [options] upload-sample FILENAME [NAME]\n"
"%s [options] play-sample NAME [SINK]\n"
+ "%s [options] remove-sample NAME\n"
"%s [options] move-sink-input ID SINK\n"
"%s [options] move-source-output ID SOURCE\n"
- "%s [options] remove-sample NAME\n\n"
+ "%s [options] load-module NAME [ARGS ...]\n"
+ "%s [options] unload-module ID\n"
+ "%s [options] suspend-sink [SINK] 1|0\n"
+ "%s [options] suspend-source [SOURCE] 1|0\n\n"
" -h, --help Show this help\n"
" --version Show version\n\n"
" -s, --server=SERVER The name of the server to connect to\n"
" -n, --client-name=NAME How to call this client on the server\n",
- argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0);
+ argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0);
}
enum { ARG_VERSION = 256 };
@@ -650,14 +716,14 @@ int main(int argc, char *argv[]) {
bn = argv[0];
else
bn++;
-
+
while ((c = getopt_long(argc, argv, "s:n:h", long_options, NULL)) != -1) {
switch (c) {
case 'h' :
help(bn);
ret = 0;
goto quit;
-
+
case ARG_VERSION:
printf("pactl "PACKAGE_VERSION"\nCompiled with libpulse %s\nLinked with libpulse %s\n", pa_get_headers_version(), pa_get_library_version());
ret = 0;
@@ -680,7 +746,7 @@ int main(int argc, char *argv[]) {
if (!client_name)
client_name = pa_xstrdup(bn);
-
+
if (optind < argc) {
if (!strcmp(argv[optind], "stat"))
action = STAT;
@@ -712,13 +778,13 @@ int main(int argc, char *argv[]) {
tmp[n] = 0;
sample_name = pa_xstrdup(tmp);
}
-
+
memset(&sfinfo, 0, sizeof(sfinfo));
if (!(sndfile = sf_open(argv[optind+1], SFM_READ, &sfinfo))) {
fprintf(stderr, "Failed to open sound file.\n");
goto quit;
}
-
+
sample_spec.format = PA_SAMPLE_FLOAT32;
sample_spec.rate = sfinfo.samplerate;
sample_spec.channels = sfinfo.channels;
@@ -726,7 +792,7 @@ int main(int argc, char *argv[]) {
sample_length = sfinfo.frames*pa_frame_size(&sample_spec);
} else if (!strcmp(argv[optind], "play-sample")) {
action = PLAY_SAMPLE;
- if (optind+1 >= argc) {
+ if (argc != optind+2 && argc != optind+3) {
fprintf(stderr, "You have to specify a sample name to play\n");
goto quit;
}
@@ -735,10 +801,10 @@ int main(int argc, char *argv[]) {
if (optind+2 < argc)
device = pa_xstrdup(argv[optind+2]);
-
+
} else if (!strcmp(argv[optind], "remove-sample")) {
action = REMOVE_SAMPLE;
- if (optind+1 >= argc) {
+ if (argc != optind+2) {
fprintf(stderr, "You have to specify a sample name to remove\n");
goto quit;
}
@@ -746,7 +812,7 @@ int main(int argc, char *argv[]) {
sample_name = pa_xstrdup(argv[optind+1]);
} else if (!strcmp(argv[optind], "move-sink-input")) {
action = MOVE_SINK_INPUT;
- if (optind+2 >= argc) {
+ if (argc != optind+3) {
fprintf(stderr, "You have to specify a sink input index and a sink\n");
goto quit;
}
@@ -755,13 +821,76 @@ int main(int argc, char *argv[]) {
sink_name = pa_xstrdup(argv[optind+2]);
} else if (!strcmp(argv[optind], "move-source-output")) {
action = MOVE_SOURCE_OUTPUT;
- if (optind+2 >= argc) {
+ if (argc != optind+3) {
fprintf(stderr, "You have to specify a source output index and a source\n");
goto quit;
}
source_output_idx = atoi(argv[optind+1]);
source_name = pa_xstrdup(argv[optind+2]);
+ } else if (!strcmp(argv[optind], "load-module")) {
+ int i;
+ size_t n = 0;
+ char *p;
+
+ action = LOAD_MODULE;
+
+ if (argc <= optind+1) {
+ fprintf(stderr, "You have to specify a module name and arguments.\n");
+ goto quit;
+ }
+
+ module_name = argv[optind+1];
+
+ for (i = optind+2; i < argc; i++)
+ n += strlen(argv[i])+1;
+
+ if (n > 0) {
+ p = module_args = pa_xnew0(char, n);
+
+ for (i = optind+2; i < argc; i++)
+ p += sprintf(p, "%s%s", p == module_args ? "" : " ", argv[i]);
+ }
+
+ } else if (!strcmp(argv[optind], "unload-module")) {
+ action = UNLOAD_MODULE;
+
+ if (argc != optind+2) {
+ fprintf(stderr, "You have to specify a module index\n");
+ goto quit;
+ }
+
+ module_index = atoi(argv[optind+1]);
+
+ } else if (!strcmp(argv[optind], "suspend-sink")) {
+ action = SUSPEND_SINK;
+
+ if (argc > optind+3 || optind+1 >= argc) {
+ fprintf(stderr, "You may not specify more than one sink. You have to specify at least one boolean value.\n");
+ goto quit;
+ }
+
+ suspend = pa_parse_boolean(argv[argc-1]);
+
+ if (argc > optind+2)
+ sink_name = pa_xstrdup(argv[optind+1]);
+
+ } else if (!strcmp(argv[optind], "suspend-source")) {
+ action = SUSPEND_SOURCE;
+
+ if (argc > optind+3 || optind+1 >= argc) {
+ fprintf(stderr, "You may not specify more than one source. You have to specify at least one boolean value.\n");
+ goto quit;
+ }
+
+ suspend = pa_parse_boolean(argv[argc-1]);
+
+ if (argc > optind+2)
+ source_name = pa_xstrdup(argv[optind+1]);
+ } else if (!strcmp(argv[optind], "help")) {
+ help(bn);
+ ret = 0;
+ goto quit;
}
}
@@ -783,7 +912,7 @@ int main(int argc, char *argv[]) {
#ifdef SIGPIPE
signal(SIGPIPE, SIG_IGN);
#endif
-
+
if (!(context = pa_context_new(mainloop_api, client_name))) {
fprintf(stderr, "pa_context_new() failed.\n");
goto quit;
@@ -808,7 +937,7 @@ quit:
pa_signal_done();
pa_mainloop_free(m);
}
-
+
if (sndfile)
sf_close(sndfile);
@@ -817,6 +946,8 @@ quit:
pa_xfree(sample_name);
pa_xfree(sink_name);
pa_xfree(source_name);
+ pa_xfree(module_args);
+ pa_xfree(client_name);
return ret;
}
diff --git a/src/utils/padsp b/src/utils/padsp
index bae5a728..4fe175c2 100755
--- a/src/utils/padsp
+++ b/src/utils/padsp
@@ -1,9 +1,10 @@
#!/bin/sh
-# $Id$
-#
# This file is part of PulseAudio.
#
+# Copyright 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
diff --git a/src/utils/padsp.c b/src/utils/padsp.c
index 5d96a984..d650707e 100644
--- a/src/utils/padsp.c
+++ b/src/utils/padsp.c
@@ -1,18 +1,19 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
-
+
+ Copyright 2006 Lennart Poettering
+ Copyright 2006-2007 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
@@ -50,14 +51,18 @@
#endif
#include <pulse/pulseaudio.h>
+#include <pulse/gccmacro.h>
#include <pulsecore/llist.h>
-#include <pulsecore/gccmacro.h>
/* On some systems SIOCINQ isn't defined, but FIONREAD is just an alias */
#if !defined(SIOCINQ) && defined(FIONREAD)
# define SIOCINQ FIONREAD
#endif
+/* make sure gcc doesn't redefine open and friends as macros */
+#undef open
+#undef open64
+
typedef enum {
FD_INFO_MIXER,
FD_INFO_STREAM,
@@ -69,7 +74,7 @@ struct fd_info {
pthread_mutex_t mutex;
int ref;
int unusable;
-
+
fd_info_type_t type;
int app_fd, thread_fd;
@@ -81,6 +86,8 @@ struct fd_info {
pa_context *context;
pa_stream *play_stream;
pa_stream *rec_stream;
+ int play_precork;
+ int rec_precork;
pa_io_event *io_event;
pa_io_event_flags_t io_flags;
@@ -95,7 +102,7 @@ struct fd_info {
int volume_modify_count;
int optr_n_blocks;
-
+
PA_LLIST_FIELDS(fd_info);
};
@@ -111,9 +118,17 @@ static int (*_ioctl)(int, int, void*) = NULL;
static int (*_close)(int) = NULL;
static int (*_open)(const char *, int, mode_t) = NULL;
static FILE* (*_fopen)(const char *path, const char *mode) = NULL;
+static int (*_stat)(const char *, struct stat *) = NULL;
+#ifdef _STAT_VER
+static int (*___xstat)(int, const char *, struct stat *) = NULL;
+#endif
#ifdef HAVE_OPEN64
static int (*_open64)(const char *, int, mode_t) = NULL;
static FILE* (*_fopen64)(const char *path, const char *mode) = NULL;
+static int (*_stat64)(const char *, struct stat64 *) = NULL;
+#ifdef _STAT_VER
+static int (*___xstat64)(int, const char *, struct stat64 *) = NULL;
+#endif
#endif
static int (*_fclose)(FILE *f) = NULL;
static int (*_access)(const char *, int) = NULL;
@@ -165,6 +180,38 @@ do { \
pthread_mutex_unlock(&func_mutex); \
} while(0)
+#define LOAD_STAT_FUNC() \
+do { \
+ pthread_mutex_lock(&func_mutex); \
+ if (!_stat) \
+ _stat = (int (*)(const char *, struct stat *)) dlsym_fn(RTLD_NEXT, "stat"); \
+ pthread_mutex_unlock(&func_mutex); \
+} while(0)
+
+#define LOAD_STAT64_FUNC() \
+do { \
+ pthread_mutex_lock(&func_mutex); \
+ if (!_stat64) \
+ _stat64 = (int (*)(const char *, struct stat64 *)) dlsym_fn(RTLD_NEXT, "stat64"); \
+ pthread_mutex_unlock(&func_mutex); \
+} while(0)
+
+#define LOAD_XSTAT_FUNC() \
+do { \
+ pthread_mutex_lock(&func_mutex); \
+ if (!___xstat) \
+ ___xstat = (int (*)(int, const char *, struct stat *)) dlsym_fn(RTLD_NEXT, "__xstat"); \
+ pthread_mutex_unlock(&func_mutex); \
+} while(0)
+
+#define LOAD_XSTAT64_FUNC() \
+do { \
+ pthread_mutex_lock(&func_mutex); \
+ if (!___xstat64) \
+ ___xstat64 = (int (*)(int, const char *, struct stat64 *)) dlsym_fn(RTLD_NEXT, "__xstat64"); \
+ pthread_mutex_unlock(&func_mutex); \
+} while(0)
+
#define LOAD_FOPEN_FUNC() \
do { \
pthread_mutex_lock(&func_mutex); \
@@ -191,32 +238,32 @@ do { \
#define CONTEXT_CHECK_DEAD_GOTO(i, label) do { \
if (!(i)->context || pa_context_get_state((i)->context) != PA_CONTEXT_READY) { \
- debug(DEBUG_LEVEL_NORMAL, __FILE__": Not connected: %s", (i)->context ? pa_strerror(pa_context_errno((i)->context)) : "NULL"); \
+ debug(DEBUG_LEVEL_NORMAL, __FILE__": Not connected: %s\n", (i)->context ? pa_strerror(pa_context_errno((i)->context)) : "NULL"); \
goto label; \
} \
-} while(0);
+} while(0)
#define PLAYBACK_STREAM_CHECK_DEAD_GOTO(i, label) do { \
if (!(i)->context || pa_context_get_state((i)->context) != PA_CONTEXT_READY || \
!(i)->play_stream || pa_stream_get_state((i)->play_stream) != PA_STREAM_READY) { \
- debug(DEBUG_LEVEL_NORMAL, __FILE__": Not connected: %s", (i)->context ? pa_strerror(pa_context_errno((i)->context)) : "NULL"); \
+ debug(DEBUG_LEVEL_NORMAL, __FILE__": Not connected: %s\n", (i)->context ? pa_strerror(pa_context_errno((i)->context)) : "NULL"); \
goto label; \
} \
-} while(0);
+} while(0)
#define RECORD_STREAM_CHECK_DEAD_GOTO(i, label) do { \
if (!(i)->context || pa_context_get_state((i)->context) != PA_CONTEXT_READY || \
!(i)->rec_stream || pa_stream_get_state((i)->rec_stream) != PA_STREAM_READY) { \
- debug(DEBUG_LEVEL_NORMAL, __FILE__": Not connected: %s", (i)->context ? pa_strerror(pa_context_errno((i)->context)) : "NULL"); \
+ debug(DEBUG_LEVEL_NORMAL, __FILE__": Not connected: %s\n", (i)->context ? pa_strerror(pa_context_errno((i)->context)) : "NULL"); \
goto label; \
} \
-} while(0);
+} while(0)
static void debug(int level, const char *format, ...) PA_GCC_PRINTF_ATTR(2,3);
-#define DEBUG_LEVEL_ALWAYS 0
-#define DEBUG_LEVEL_NORMAL 1
-#define DEBUG_LEVEL_VERBOSE 2
+#define DEBUG_LEVEL_ALWAYS 0
+#define DEBUG_LEVEL_NORMAL 1
+#define DEBUG_LEVEL_VERBOSE 2
static void debug(int level, const char *format, ...) {
va_list ap;
@@ -248,26 +295,25 @@ static int padsp_disabled(void) {
* -> disable /dev/dsp emulation, bit 2 -> disable /dev/sndstat
* emulation, bit 3 -> disable /dev/mixer emulation. Hence a value
* of 7 disables padsp entirely. */
-
+
pthread_mutex_lock(&func_mutex);
if (!sym_resolved) {
sym = (int*) dlsym(RTLD_DEFAULT, "__padsp_disabled__");
sym_resolved = 1;
-
}
pthread_mutex_unlock(&func_mutex);
if (!sym)
return 0;
-
+
return *sym;
}
static int dsp_cloak_enable(void) {
if (padsp_disabled() & 1)
return 0;
-
- if (getenv("PADSP_NO_DSP"))
+
+ if (getenv("PADSP_NO_DSP") || getenv("PULSE_INTERNAL"))
return 0;
return 1;
@@ -277,7 +323,7 @@ static int sndstat_cloak_enable(void) {
if (padsp_disabled() & 2)
return 0;
- if (getenv("PADSP_NO_SNDSTAT"))
+ if (getenv("PADSP_NO_SNDSTAT") || getenv("PULSE_INTERNAL"))
return 0;
return 1;
@@ -287,7 +333,7 @@ static int mixer_cloak_enable(void) {
if (padsp_disabled() & 4)
return 0;
- if (getenv("PADSP_NO_MIXER"))
+ if (getenv("PADSP_NO_MIXER") || getenv("PULSE_INTERNAL"))
return 0;
return 1;
@@ -302,7 +348,7 @@ static int function_enter(void) {
/* Avoid recursive calls */
static pthread_once_t recursion_key_once = PTHREAD_ONCE_INIT;
pthread_once(&recursion_key_once, recursion_key_alloc);
-
+
if (pthread_getspecific(recursion_key))
return 0;
@@ -320,10 +366,10 @@ static void fd_info_free(fd_info *i) {
debug(DEBUG_LEVEL_NORMAL, __FILE__": freeing fd info (fd=%i)\n", i->app_fd);
dsp_drain(i);
-
+
if (i->mainloop)
pa_threaded_mainloop_stop(i->mainloop);
-
+
if (i->play_stream) {
pa_stream_disconnect(i->play_stream);
pa_stream_unref(i->play_stream);
@@ -338,7 +384,7 @@ static void fd_info_free(fd_info *i) {
pa_context_disconnect(i->context);
pa_context_unref(i->context);
}
-
+
if (i->mainloop)
pa_threaded_mainloop_free(i->mainloop);
@@ -360,7 +406,7 @@ static void fd_info_free(fd_info *i) {
static fd_info *fd_info_ref(fd_info *i) {
assert(i);
-
+
pthread_mutex_lock(&i->mutex);
assert(i->ref >= 1);
i->ref++;
@@ -376,7 +422,7 @@ static void fd_info_unref(fd_info *i) {
pthread_mutex_lock(&i->mutex);
assert(i->ref >= 1);
r = --i->ref;
- debug(DEBUG_LEVEL_VERBOSE, __FILE__": ref--, now %i\n", i->ref);
+ debug(DEBUG_LEVEL_VERBOSE, __FILE__": ref--, now %i\n", i->ref);
pthread_mutex_unlock(&i->mutex);
if (r <= 0)
@@ -404,7 +450,7 @@ static void context_state_cb(pa_context *c, void *userdata) {
static void reset_params(fd_info *i) {
assert(i);
-
+
i->sample_spec.format = PA_SAMPLE_U8;
i->sample_spec.channels = 1;
i->sample_spec.rate = 8000;
@@ -418,7 +464,7 @@ static const char *client_name(char *buf, size_t n) {
if ((e = getenv("PADSP_CLIENT_NAME")))
return e;
-
+
if (pa_get_binary_name(p, sizeof(p)))
snprintf(buf, n, "OSS Emulation[%s]", p);
else
@@ -440,7 +486,7 @@ static void atfork_prepare(void) {
fd_info *i;
debug(DEBUG_LEVEL_NORMAL, __FILE__": atfork_prepare() enter\n");
-
+
function_enter();
pthread_mutex_lock(&fd_infos_mutex);
@@ -452,13 +498,13 @@ static void atfork_prepare(void) {
pthread_mutex_lock(&func_mutex);
-
+
debug(DEBUG_LEVEL_NORMAL, __FILE__": atfork_prepare() exit\n");
}
static void atfork_parent(void) {
fd_info *i;
-
+
debug(DEBUG_LEVEL_NORMAL, __FILE__": atfork_parent() enter\n");
pthread_mutex_unlock(&func_mutex);
@@ -471,19 +517,19 @@ static void atfork_parent(void) {
pthread_mutex_unlock(&fd_infos_mutex);
function_exit();
-
+
debug(DEBUG_LEVEL_NORMAL, __FILE__": atfork_parent() exit\n");
}
static void atfork_child(void) {
fd_info *i;
-
+
debug(DEBUG_LEVEL_NORMAL, __FILE__": atfork_child() enter\n");
/* We do only the bare minimum to get all fds closed */
pthread_mutex_init(&func_mutex, NULL);
pthread_mutex_init(&fd_infos_mutex, NULL);
-
+
for (i = fd_infos; i; i = i->next) {
pthread_mutex_init(&i->mutex, NULL);
@@ -556,7 +602,7 @@ static fd_info* fd_info_new(fd_info_type_t type, int *_errno) {
signal(SIGPIPE, SIG_IGN); /* Yes, ugly as hell */
pthread_once(&install_atfork_once, install_atfork);
-
+
if (!(i = malloc(sizeof(fd_info)))) {
*_errno = ENOMEM;
goto fail;
@@ -569,6 +615,8 @@ static fd_info* fd_info_new(fd_info_type_t type, int *_errno) {
i->context = NULL;
i->play_stream = NULL;
i->rec_stream = NULL;
+ i->play_precork = 0;
+ i->rec_precork = 0;
i->io_event = NULL;
i->io_flags = 0;
pthread_mutex_init(&i->mutex, NULL);
@@ -638,12 +686,12 @@ static fd_info* fd_info_new(fd_info_type_t type, int *_errno) {
unlock_and_fail:
pa_threaded_mainloop_unlock(i->mainloop);
-
+
fail:
if (i)
fd_info_unref(i);
-
+
return NULL;
}
@@ -671,7 +719,7 @@ static fd_info* fd_info_find(int fd) {
fd_info *i;
pthread_mutex_lock(&fd_infos_mutex);
-
+
for (i = fd_infos; i; i = i->next)
if (i->app_fd == fd && !i->unusable) {
fd_info_ref(i);
@@ -679,7 +727,7 @@ static fd_info* fd_info_find(int fd) {
}
pthread_mutex_unlock(&fd_infos_mutex);
-
+
return i;
}
@@ -907,9 +955,21 @@ static void stream_state_cb(pa_stream *s, void * userdata) {
case PA_STREAM_READY:
debug(DEBUG_LEVEL_NORMAL, __FILE__": stream established.\n");
break;
-
+
case PA_STREAM_FAILED:
- debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_connect_playback() failed: %s\n", pa_strerror(pa_context_errno(i->context)));
+ if (s == i->play_stream) {
+ debug(DEBUG_LEVEL_NORMAL,
+ __FILE__": pa_stream_connect_playback() failed: %s\n",
+ pa_strerror(pa_context_errno(i->context)));
+ pa_stream_unref(i->play_stream);
+ i->play_stream = NULL;
+ } else if (s == i->rec_stream) {
+ debug(DEBUG_LEVEL_NORMAL,
+ __FILE__": pa_stream_connect_record() failed: %s\n",
+ pa_strerror(pa_context_errno(i->context)));
+ pa_stream_unref(i->rec_stream);
+ i->rec_stream = NULL;
+ }
fd_info_shutdown(i);
break;
@@ -922,8 +982,8 @@ static void stream_state_cb(pa_stream *s, void * userdata) {
static int create_playback_stream(fd_info *i) {
pa_buffer_attr attr;
- int n;
-
+ int n, flags;
+
assert(i);
fix_metrics(i);
@@ -942,8 +1002,13 @@ static int create_playback_stream(fd_info *i) {
attr.tlength = i->fragment_size * i->n_fragments;
attr.prebuf = i->fragment_size;
attr.minreq = i->fragment_size;
-
- if (pa_stream_connect_playback(i->play_stream, NULL, &attr, PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL) < 0) {
+
+ flags = PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE;
+ if (i->play_precork) {
+ flags |= PA_STREAM_START_CORKED;
+ debug(DEBUG_LEVEL_NORMAL, __FILE__": creating stream corked\n");
+ }
+ if (pa_stream_connect_playback(i->play_stream, NULL, &attr, flags, NULL, NULL) < 0) {
debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_connect_playback() failed: %s\n", pa_strerror(pa_context_errno(i->context)));
goto fail;
}
@@ -952,7 +1017,7 @@ static int create_playback_stream(fd_info *i) {
setsockopt(i->app_fd, SOL_SOCKET, SO_SNDBUF, &n, sizeof(n));
n = i->fragment_size;
setsockopt(i->thread_fd, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n));
-
+
return 0;
fail:
@@ -961,8 +1026,8 @@ fail:
static int create_record_stream(fd_info *i) {
pa_buffer_attr attr;
- int n;
-
+ int n, flags;
+
assert(i);
fix_metrics(i);
@@ -979,9 +1044,14 @@ static int create_record_stream(fd_info *i) {
memset(&attr, 0, sizeof(attr));
attr.maxlength = i->fragment_size * (i->n_fragments+1);
attr.fragsize = i->fragment_size;
-
- if (pa_stream_connect_record(i->rec_stream, NULL, &attr, PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE) < 0) {
- debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_connect_playback() failed: %s\n", pa_strerror(pa_context_errno(i->context)));
+
+ flags = PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE;
+ if (i->rec_precork) {
+ flags |= PA_STREAM_START_CORKED;
+ debug(DEBUG_LEVEL_NORMAL, __FILE__": creating stream corked\n");
+ }
+ if (pa_stream_connect_record(i->rec_stream, NULL, &attr, flags) < 0) {
+ debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_connect_record() failed: %s\n", pa_strerror(pa_context_errno(i->context)));
goto fail;
}
@@ -989,7 +1059,7 @@ static int create_record_stream(fd_info *i) {
setsockopt(i->app_fd, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n));
n = i->fragment_size;
setsockopt(i->thread_fd, SOL_SOCKET, SO_SNDBUF, &n, sizeof(n));
-
+
return 0;
fail:
@@ -1025,7 +1095,7 @@ static void io_event_cb(pa_mainloop_api *api, pa_io_event *e, int fd, pa_io_even
fd_info *i = userdata;
pa_threaded_mainloop_signal(i->mainloop, 0);
-
+
if (flags & PA_IO_EVENT_INPUT) {
if (!i->play_stream) {
@@ -1035,7 +1105,7 @@ static void io_event_cb(pa_mainloop_api *api, pa_io_event *e, int fd, pa_io_even
if (fd_info_copy_data(i, 0) < 0)
goto fail;
}
-
+
} else if (flags & PA_IO_EVENT_OUTPUT) {
if (!i->rec_stream) {
@@ -1050,7 +1120,7 @@ static void io_event_cb(pa_mainloop_api *api, pa_io_event *e, int fd, pa_io_even
goto fail;
return;
-
+
fail:
/* We can't do anything better than removing the event source */
fd_info_shutdown(i);
@@ -1100,7 +1170,7 @@ static int dsp_open(int flags, int *_errno) {
if (!(i->io_event = api->io_new(api, i->thread_fd, i->io_flags, io_event_cb, i)))
goto fail;
-
+
pa_threaded_mainloop_unlock(i->mainloop);
debug(DEBUG_LEVEL_NORMAL, __FILE__": dsp_open() succeeded, fd=%i\n", i->app_fd);
@@ -1108,7 +1178,7 @@ static int dsp_open(int flags, int *_errno) {
fd_info_add_to_list(i);
ret = i->app_fd;
fd_info_unref(i);
-
+
return ret;
fail:
@@ -1116,7 +1186,7 @@ fail:
if (i)
fd_info_unref(i);
-
+
*_errno = EIO;
debug(DEBUG_LEVEL_NORMAL, __FILE__": dsp_open() failed\n");
@@ -1138,7 +1208,7 @@ static void sink_info_cb(pa_context *context, const pa_sink_info *si, int eol, v
if (!pa_cvolume_equal(&i->sink_volume, &si->volume))
i->volume_modify_count++;
-
+
i->sink_volume = si->volume;
i->sink_index = si->index;
@@ -1160,7 +1230,7 @@ static void source_info_cb(pa_context *context, const pa_source_info *si, int eo
if (!pa_cvolume_equal(&i->source_volume, &si->volume))
i->volume_modify_count++;
-
+
i->source_volume = si->volume;
i->source_index = si->index;
@@ -1193,13 +1263,13 @@ static int mixer_open(int flags, int *_errno) {
debug(DEBUG_LEVEL_NORMAL, __FILE__": mixer_open()\n");
- if (!(i = fd_info_new(FD_INFO_MIXER, _errno)))
+ if (!(i = fd_info_new(FD_INFO_MIXER, _errno)))
return -1;
-
+
pa_threaded_mainloop_lock(i->mainloop);
pa_context_set_subscribe_callback(i->context, subscribe_cb, i);
-
+
if (!(o = pa_context_subscribe(i->context, PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE, context_success_cb, i))) {
debug(DEBUG_LEVEL_NORMAL, __FILE__": Failed to subscribe to events: %s", pa_strerror(pa_context_errno(i->context)));
*_errno = EIO;
@@ -1274,7 +1344,7 @@ static int mixer_open(int flags, int *_errno) {
fd_info_add_to_list(i);
ret = i->app_fd;
fd_info_unref(i);
-
+
return ret;
fail:
@@ -1285,7 +1355,7 @@ fail:
if (i)
fd_info_unref(i);
-
+
*_errno = EIO;
debug(DEBUG_LEVEL_NORMAL, __FILE__": mixer_open() failed\n");
@@ -1323,10 +1393,10 @@ static int sndstat_open(int flags, int *_errno) {
int e;
debug(DEBUG_LEVEL_NORMAL, __FILE__": sndstat_open()\n");
-
+
if (flags != O_RDONLY
#ifdef O_LARGEFILE
- && flags != (O_RDONLY|O_LARGEFILE)
+ && flags != (O_RDONLY|O_LARGEFILE)
#endif
) {
*_errno = EACCES;
@@ -1367,50 +1437,55 @@ fail:
return -1;
}
-int open(const char *filename, int flags, ...) {
- va_list args;
- mode_t mode = 0;
+static int real_open(const char *filename, int flags, mode_t mode) {
int r, _errno = 0;
- debug(DEBUG_LEVEL_VERBOSE, __FILE__": open(%s)\n", filename);
-
- va_start(args, flags);
- if (flags & O_CREAT) {
- if (sizeof(mode_t) < sizeof(int))
- mode = va_arg(args, int);
- else
- mode = va_arg(args, mode_t);
- }
- va_end(args);
+ debug(DEBUG_LEVEL_VERBOSE, __FILE__": open(%s)\n", filename?filename:"NULL");
if (!function_enter()) {
LOAD_OPEN_FUNC();
return _open(filename, flags, mode);
}
- if (dsp_cloak_enable() && (strcmp(filename, "/dev/dsp") == 0 || strcmp(filename, "/dev/adsp") == 0)) {
+ if (filename && dsp_cloak_enable() && (strcmp(filename, "/dev/dsp") == 0 || strcmp(filename, "/dev/adsp") == 0))
r = dsp_open(flags, &_errno);
- } else if (mixer_cloak_enable() && strcmp(filename, "/dev/mixer") == 0) {
+ else if (filename && mixer_cloak_enable() && strcmp(filename, "/dev/mixer") == 0)
r = mixer_open(flags, &_errno);
- } else if (sndstat_cloak_enable() && strcmp(filename, "/dev/sndstat") == 0) {
+ else if (filename && sndstat_cloak_enable() && strcmp(filename, "/dev/sndstat") == 0)
r = sndstat_open(flags, &_errno);
- } else {
+ else {
function_exit();
LOAD_OPEN_FUNC();
return _open(filename, flags, mode);
}
function_exit();
-
+
if (_errno)
errno = _errno;
-
+
return r;
}
+int open(const char *filename, int flags, ...) {
+ va_list args;
+ mode_t mode = 0;
+
+ if (flags & O_CREAT) {
+ va_start(args, flags);
+ if (sizeof(mode_t) < sizeof(int))
+ mode = va_arg(args, int);
+ else
+ mode = va_arg(args, mode_t);
+ va_end(args);
+ }
+
+ return real_open(filename, flags, mode);
+}
+
static int mixer_ioctl(fd_info *i, unsigned long request, void*argp, int *_errno) {
int ret = -1;
-
+
switch (request) {
case SOUND_MIXER_READ_DEVMASK :
debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_MIXER_READ_DEVMASK\n");
@@ -1423,7 +1498,7 @@ static int mixer_ioctl(fd_info *i, unsigned long request, void*argp, int *_errno
*(int*) argp = SOUND_MASK_IGAIN;
break;
-
+
case SOUND_MIXER_READ_STEREODEVS:
debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_MIXER_READ_STEREODEVS\n");
@@ -1434,7 +1509,7 @@ static int mixer_ioctl(fd_info *i, unsigned long request, void*argp, int *_errno
if (i->source_volume.channels > 1)
*(int*) argp |= SOUND_MASK_IGAIN;
pa_threaded_mainloop_unlock(i->mainloop);
-
+
break;
case SOUND_MIXER_READ_RECSRC:
@@ -1452,7 +1527,7 @@ static int mixer_ioctl(fd_info *i, unsigned long request, void*argp, int *_errno
*(int*) argp = 0;
break;
-
+
case SOUND_MIXER_READ_PCM:
case SOUND_MIXER_READ_IGAIN: {
pa_cvolume *v;
@@ -1482,14 +1557,14 @@ static int mixer_ioctl(fd_info *i, unsigned long request, void*argp, int *_errno
case SOUND_MIXER_WRITE_IGAIN: {
pa_cvolume v, *pv;
- if (request == SOUND_MIXER_READ_PCM)
+ if (request == SOUND_MIXER_WRITE_PCM)
debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_MIXER_WRITE_PCM\n");
else
debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_MIXER_WRITE_IGAIN\n");
pa_threaded_mainloop_lock(i->mainloop);
- if (request == SOUND_MIXER_READ_PCM) {
+ if (request == SOUND_MIXER_WRITE_PCM) {
v = i->sink_volume;
pv = &i->sink_volume;
} else {
@@ -1503,7 +1578,7 @@ static int mixer_ioctl(fd_info *i, unsigned long request, void*argp, int *_errno
if (!pa_cvolume_equal(pv, &v)) {
pa_operation *o;
- if (request == SOUND_MIXER_READ_PCM)
+ if (request == SOUND_MIXER_WRITE_PCM)
o = pa_context_set_sink_volume_by_index(i->context, i->sink_index, pv, context_success_cb, i);
else
o = pa_context_set_source_volume_by_index(i->context, i->source_index, pv, context_success_cb, i);
@@ -1515,23 +1590,23 @@ static int mixer_ioctl(fd_info *i, unsigned long request, void*argp, int *_errno
i->operation_success = 0;
while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
CONTEXT_CHECK_DEAD_GOTO(i, exit_loop);
-
+
pa_threaded_mainloop_wait(i->mainloop);
}
exit_loop:
-
+
if (!i->operation_success)
debug(DEBUG_LEVEL_NORMAL, __FILE__": Failed to set volume: %s\n", pa_strerror(pa_context_errno(i->context)));
pa_operation_unref(o);
}
-
+
/* We don't wait for completion here */
i->volume_modify_count++;
}
-
+
pa_threaded_mainloop_unlock(i->mainloop);
-
+
break;
}
@@ -1548,7 +1623,7 @@ static int mixer_ioctl(fd_info *i, unsigned long request, void*argp, int *_errno
pa_threaded_mainloop_unlock(i->mainloop);
break;
}
-
+
default:
debug(DEBUG_LEVEL_NORMAL, __FILE__": unknown ioctl 0x%08lx\n", request);
@@ -1557,44 +1632,44 @@ static int mixer_ioctl(fd_info *i, unsigned long request, void*argp, int *_errno
}
ret = 0;
-
+
fail:
-
+
return ret;
}
static int map_format(int *fmt, pa_sample_spec *ss) {
-
+
switch (*fmt) {
case AFMT_MU_LAW:
ss->format = PA_SAMPLE_ULAW;
break;
-
+
case AFMT_A_LAW:
ss->format = PA_SAMPLE_ALAW;
break;
-
+
case AFMT_S8:
*fmt = AFMT_U8;
/* fall through */
case AFMT_U8:
ss->format = PA_SAMPLE_U8;
break;
-
+
case AFMT_U16_BE:
*fmt = AFMT_S16_BE;
/* fall through */
case AFMT_S16_BE:
ss->format = PA_SAMPLE_S16BE;
break;
-
+
case AFMT_U16_LE:
*fmt = AFMT_S16_LE;
/* fall through */
case AFMT_S16_LE:
ss->format = PA_SAMPLE_S16LE;
break;
-
+
default:
ss->format = PA_SAMPLE_S16NE;
*fmt = AFMT_S16_NE;
@@ -1666,14 +1741,14 @@ static int dsp_flush_socket(fd_info *i) {
static int dsp_empty_socket(fd_info *i) {
#ifdef SIOCINQ
int ret = -1;
-
+
/* Empty the socket */
for (;;) {
int l;
-
+
if (i->thread_fd < 0)
break;
-
+
if (ioctl(i->thread_fd, SIOCINQ, &l) < 0) {
debug(DEBUG_LEVEL_NORMAL, __FILE__": SIOCINQ: %s\n", strerror(errno));
break;
@@ -1683,7 +1758,7 @@ static int dsp_empty_socket(fd_info *i) {
ret = 0;
break;
}
-
+
pa_threaded_mainloop_wait(i->mainloop);
}
@@ -1700,19 +1775,19 @@ static int dsp_drain(fd_info *i) {
if (!i->mainloop)
return 0;
-
+
debug(DEBUG_LEVEL_NORMAL, __FILE__": Draining.\n");
pa_threaded_mainloop_lock(i->mainloop);
if (dsp_empty_socket(i) < 0)
goto fail;
-
+
if (!i->play_stream)
goto fail;
debug(DEBUG_LEVEL_NORMAL, __FILE__": Really draining.\n");
-
+
if (!(o = pa_stream_drain(i->play_stream, stream_success_cb, i))) {
debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_drain(): %s\n", pa_strerror(pa_context_errno(i->context)));
goto fail;
@@ -1721,7 +1796,7 @@ static int dsp_drain(fd_info *i) {
i->operation_success = 0;
while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
PLAYBACK_STREAM_CHECK_DEAD_GOTO(i, fail);
-
+
pa_threaded_mainloop_wait(i->mainloop);
}
@@ -1731,9 +1806,9 @@ static int dsp_drain(fd_info *i) {
}
r = 0;
-
+
fail:
-
+
if (o)
pa_operation_unref(o);
@@ -1755,7 +1830,7 @@ static int dsp_trigger(fd_info *i) {
goto fail;
debug(DEBUG_LEVEL_NORMAL, __FILE__": Triggering.\n");
-
+
if (!(o = pa_stream_trigger(i->play_stream, stream_success_cb, i))) {
debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_trigger(): %s\n", pa_strerror(pa_context_errno(i->context)));
goto fail;
@@ -1764,7 +1839,7 @@ static int dsp_trigger(fd_info *i) {
i->operation_success = 0;
while (!pa_operation_get_state(o) != PA_OPERATION_DONE) {
PLAYBACK_STREAM_CHECK_DEAD_GOTO(i, fail);
-
+
pa_threaded_mainloop_wait(i->mainloop);
}
@@ -1774,9 +1849,47 @@ static int dsp_trigger(fd_info *i) {
}
r = 0;
-
+
fail:
-
+
+ if (o)
+ pa_operation_unref(o);
+
+ pa_threaded_mainloop_unlock(i->mainloop);
+
+ return 0;
+}
+
+static int dsp_cork(fd_info *i, pa_stream *s, int b) {
+ pa_operation *o = NULL;
+ int r = -1;
+
+ pa_threaded_mainloop_lock(i->mainloop);
+
+ if (!(o = pa_stream_cork(s, b, stream_success_cb, i))) {
+ debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_cork(): %s\n", pa_strerror(pa_context_errno(i->context)));
+ goto fail;
+ }
+
+ i->operation_success = 0;
+ while (!pa_operation_get_state(o) != PA_OPERATION_DONE) {
+ if (s == i->play_stream)
+ PLAYBACK_STREAM_CHECK_DEAD_GOTO(i, fail);
+ else if (s == i->rec_stream)
+ RECORD_STREAM_CHECK_DEAD_GOTO(i, fail);
+
+ pa_threaded_mainloop_wait(i->mainloop);
+ }
+
+ if (!i->operation_success) {
+ debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_cork(): %s\n", pa_strerror(pa_context_errno(i->context)));
+ goto fail;
+ }
+
+ r = 0;
+
+fail:
+
if (o)
pa_operation_unref(o);
@@ -1787,11 +1900,21 @@ fail:
static int dsp_ioctl(fd_info *i, unsigned long request, void*argp, int *_errno) {
int ret = -1;
-
+
+ if (i->thread_fd == -1) {
+ /*
+ * We've encountered some fatal error and are just waiting
+ * for a close.
+ */
+ debug(DEBUG_LEVEL_NORMAL, __FILE__": got ioctl 0x%08lx in fatal error state\n", request);
+ *_errno = EIO;
+ return -1;
+ }
+
switch (request) {
case SNDCTL_DSP_SETFMT: {
debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_SETFMT: %i\n", *(int*) argp);
-
+
pa_threaded_mainloop_lock(i->mainloop);
if (*(int*) argp == AFMT_QUERY)
@@ -1804,12 +1927,12 @@ static int dsp_ioctl(fd_info *i, unsigned long request, void*argp, int *_errno)
pa_threaded_mainloop_unlock(i->mainloop);
break;
}
-
+
case SNDCTL_DSP_SPEED: {
pa_sample_spec ss;
int valid;
char t[256];
-
+
debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_SPEED: %i\n", *(int*) argp);
pa_threaded_mainloop_lock(i->mainloop);
@@ -1821,7 +1944,7 @@ static int dsp_ioctl(fd_info *i, unsigned long request, void*argp, int *_errno)
i->sample_spec = ss;
free_streams(i);
}
-
+
debug(DEBUG_LEVEL_NORMAL, __FILE__": ss: %s\n", pa_sample_spec_snprint(t, sizeof(t), &i->sample_spec));
pa_threaded_mainloop_unlock(i->mainloop);
@@ -1833,24 +1956,24 @@ static int dsp_ioctl(fd_info *i, unsigned long request, void*argp, int *_errno)
break;
}
-
+
case SNDCTL_DSP_STEREO:
debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_STEREO: %i\n", *(int*) argp);
-
+
pa_threaded_mainloop_lock(i->mainloop);
-
+
i->sample_spec.channels = *(int*) argp ? 2 : 1;
free_streams(i);
-
+
pa_threaded_mainloop_unlock(i->mainloop);
return 0;
case SNDCTL_DSP_CHANNELS: {
pa_sample_spec ss;
int valid;
-
+
debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_CHANNELS: %i\n", *(int*) argp);
-
+
pa_threaded_mainloop_lock(i->mainloop);
ss = i->sample_spec;
@@ -1860,7 +1983,7 @@ static int dsp_ioctl(fd_info *i, unsigned long request, void*argp, int *_errno)
i->sample_spec = ss;
free_streams(i);
}
-
+
pa_threaded_mainloop_unlock(i->mainloop);
if (!valid) {
@@ -1878,16 +2001,16 @@ static int dsp_ioctl(fd_info *i, unsigned long request, void*argp, int *_errno)
fix_metrics(i);
*(int*) argp = i->fragment_size;
-
+
pa_threaded_mainloop_unlock(i->mainloop);
-
+
break;
case SNDCTL_DSP_SETFRAGMENT:
debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_SETFRAGMENT: 0x%08x\n", *(int*) argp);
-
+
pa_threaded_mainloop_lock(i->mainloop);
-
+
i->fragment_size = 1 << ((*(int*) argp) & 31);
i->n_fragments = (*(int*) argp) >> 16;
@@ -1896,30 +2019,30 @@ static int dsp_ioctl(fd_info *i, unsigned long request, void*argp, int *_errno)
i->n_fragments = 12;
free_streams(i);
-
+
pa_threaded_mainloop_unlock(i->mainloop);
-
+
break;
-
+
case SNDCTL_DSP_GETCAPS:
debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_CAPS\n");
-
- *(int*) argp = DSP_CAP_DUPLEX
+
+ *(int*) argp = DSP_CAP_DUPLEX | DSP_CAP_TRIGGER
#ifdef DSP_CAP_MULTI
- | DSP_CAP_MULTI
+ | DSP_CAP_MULTI
#endif
- ;
+ ;
break;
case SNDCTL_DSP_GETODELAY: {
int l;
-
+
debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_GETODELAY\n");
-
+
pa_threaded_mainloop_lock(i->mainloop);
*(int*) argp = 0;
-
+
for (;;) {
pa_usec_t usec;
@@ -1937,10 +2060,10 @@ static int dsp_ioctl(fd_info *i, unsigned long request, void*argp, int *_errno)
pa_threaded_mainloop_wait(i->mainloop);
}
-
+
exit_loop:
-#ifdef SIOCINQ
+#ifdef SIOCINQ
if (ioctl(i->thread_fd, SIOCINQ, &l) < 0)
debug(DEBUG_LEVEL_NORMAL, __FILE__": SIOCINQ failed: %s\n", strerror(errno));
else
@@ -1955,39 +2078,76 @@ static int dsp_ioctl(fd_info *i, unsigned long request, void*argp, int *_errno)
break;
}
-
+
case SNDCTL_DSP_RESET: {
debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_RESET\n");
-
+
pa_threaded_mainloop_lock(i->mainloop);
free_streams(i);
dsp_flush_socket(i);
i->optr_n_blocks = 0;
-
+
pa_threaded_mainloop_unlock(i->mainloop);
break;
}
-
+
case SNDCTL_DSP_GETFMTS: {
debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_GETFMTS\n");
-
+
*(int*) argp = AFMT_MU_LAW|AFMT_A_LAW|AFMT_U8|AFMT_S16_LE|AFMT_S16_BE;
break;
}
case SNDCTL_DSP_POST:
debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_POST\n");
-
- if (dsp_trigger(i) < 0)
+
+ if (dsp_trigger(i) < 0)
*_errno = EIO;
break;
- case SNDCTL_DSP_SYNC:
+ case SNDCTL_DSP_GETTRIGGER:
+ debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_GETTRIGGER\n");
+
+ *(int*) argp = 0;
+ if (!i->play_precork)
+ *(int*) argp |= PCM_ENABLE_OUTPUT;
+ if (!i->rec_precork)
+ *(int*) argp |= PCM_ENABLE_INPUT;
+
+ break;
+
+ case SNDCTL_DSP_SETTRIGGER:
+ debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_SETTRIGGER: 0x%08x\n", *(int*) argp);
+
+ if (!i->io_event) {
+ *_errno = EIO;
+ break;
+ }
+
+ i->play_precork = !((*(int*) argp) & PCM_ENABLE_OUTPUT);
+
+ if (i->play_stream) {
+ if (dsp_cork(i, i->play_stream, !((*(int*) argp) & PCM_ENABLE_OUTPUT)) < 0)
+ *_errno = EIO;
+ if (dsp_trigger(i) < 0)
+ *_errno = EIO;
+ }
+
+ i->rec_precork = !((*(int*) argp) & PCM_ENABLE_INPUT);
+
+ if (i->rec_stream) {
+ if (dsp_cork(i, i->rec_stream, !((*(int*) argp) & PCM_ENABLE_INPUT)) < 0)
+ *_errno = EIO;
+ }
+
+ break;
+
+ case SNDCTL_DSP_SYNC:
debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_SYNC\n");
-
- if (dsp_drain(i) < 0)
+
+ if (dsp_drain(i) < 0)
*_errno = EIO;
break;
@@ -2055,36 +2215,36 @@ static int dsp_ioctl(fd_info *i, unsigned long request, void*argp, int *_errno)
case SOUND_PCM_READ_RATE:
debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_PCM_READ_RATE\n");
-
+
pa_threaded_mainloop_lock(i->mainloop);
*(int*) argp = i->sample_spec.rate;
pa_threaded_mainloop_unlock(i->mainloop);
- break;
+ break;
case SOUND_PCM_READ_CHANNELS:
debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_PCM_READ_CHANNELS\n");
-
+
pa_threaded_mainloop_lock(i->mainloop);
*(int*) argp = i->sample_spec.channels;
pa_threaded_mainloop_unlock(i->mainloop);
- break;
+ break;
case SOUND_PCM_READ_BITS:
debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_PCM_READ_BITS\n");
-
+
pa_threaded_mainloop_lock(i->mainloop);
*(int*) argp = pa_sample_size(&i->sample_spec)*8;
pa_threaded_mainloop_unlock(i->mainloop);
- break;
-
+ break;
+
case SNDCTL_DSP_GETOPTR: {
count_info *info;
-
+
debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_GETOPTR\n");
info = (count_info*) argp;
memset(info, 0, sizeof(*info));
-
+
pa_threaded_mainloop_lock(i->mainloop);
for (;;) {
@@ -2095,7 +2255,7 @@ static int dsp_ioctl(fd_info *i, unsigned long request, void*argp, int *_errno)
if (pa_stream_get_time(i->play_stream, &usec) >= 0) {
size_t k = pa_usec_to_bytes(usec, &i->sample_spec);
int m;
-
+
info->bytes = (int) k;
m = k / i->fragment_size;
info->blocks = m - i->optr_n_blocks;
@@ -2111,7 +2271,7 @@ static int dsp_ioctl(fd_info *i, unsigned long request, void*argp, int *_errno)
pa_threaded_mainloop_wait(i->mainloop);
}
-
+
pa_threaded_mainloop_unlock(i->mainloop);
debug(DEBUG_LEVEL_NORMAL, __FILE__": GETOPTR bytes=%i, blocks=%i, ptr=%i\n", info->bytes, info->blocks, info->ptr);
@@ -2122,9 +2282,15 @@ static int dsp_ioctl(fd_info *i, unsigned long request, void*argp, int *_errno)
case SNDCTL_DSP_GETIPTR:
debug(DEBUG_LEVEL_NORMAL, __FILE__": invalid ioctl SNDCTL_DSP_GETIPTR\n");
goto inval;
-
+
+ case SNDCTL_DSP_SETDUPLEX:
+ debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_SETDUPLEX\n");
+ /* this is a no-op */
+ break;
+
default:
- debug(DEBUG_LEVEL_NORMAL, __FILE__": unknown ioctl 0x%08lx\n", request);
+ /* Mixer ioctls are valid on /dev/dsp aswell */
+ return mixer_ioctl(i, request, argp, _errno);
inval:
*_errno = EINVAL;
@@ -2132,13 +2298,17 @@ inval:
}
ret = 0;
-
+
fail:
-
+
return ret;
}
+#ifdef sun
+int ioctl(int fd, int request, ...) {
+#else
int ioctl(int fd, unsigned long request, ...) {
+#endif
fd_info *i;
va_list args;
void *argp;
@@ -2165,14 +2335,14 @@ int ioctl(int fd, unsigned long request, ...) {
r = mixer_ioctl(i, request, argp, &_errno);
else
r = dsp_ioctl(i, request, argp, &_errno);
-
+
fd_info_unref(i);
if (_errno)
errno = _errno;
function_exit();
-
+
return r;
}
@@ -2194,7 +2364,7 @@ int close(int fd) {
fd_info_remove_from_list(i);
fd_info_unref(i);
-
+
function_exit();
return 0;
@@ -2202,18 +2372,13 @@ int close(int fd) {
int access(const char *pathname, int mode) {
- if (!pathname) {
- /* Firefox needs this. See #27 */
- errno = EFAULT;
- return -1;
- }
-
- debug(DEBUG_LEVEL_VERBOSE, __FILE__": access(%s)\n", pathname);
+ debug(DEBUG_LEVEL_VERBOSE, __FILE__": access(%s)\n", pathname?pathname:"NULL");
- if (strcmp(pathname, "/dev/dsp") != 0 &&
- strcmp(pathname, "/dev/adsp") != 0 &&
- strcmp(pathname, "/dev/sndstat") != 0 &&
- strcmp(pathname, "/dev/mixer") != 0) {
+ if (!pathname ||
+ ( strcmp(pathname, "/dev/dsp") != 0 &&
+ strcmp(pathname, "/dev/adsp") != 0 &&
+ strcmp(pathname, "/dev/sndstat") != 0 &&
+ strcmp(pathname, "/dev/mixer") != 0 )) {
LOAD_ACCESS_FUNC();
return _access(pathname, mode);
}
@@ -2229,43 +2394,194 @@ int access(const char *pathname, int mode) {
return 0;
}
+int stat(const char *pathname, struct stat *buf) {
+#ifdef HAVE_OPEN64
+ struct stat64 parent;
+#else
+ struct stat parent;
+#endif
+ int ret;
+
+ if (!pathname ||
+ !buf ||
+ ( strcmp(pathname, "/dev/dsp") != 0 &&
+ strcmp(pathname, "/dev/adsp") != 0 &&
+ strcmp(pathname, "/dev/sndstat") != 0 &&
+ strcmp(pathname, "/dev/mixer") != 0 )) {
+ debug(DEBUG_LEVEL_VERBOSE, __FILE__": stat(%s)\n", pathname?pathname:"NULL");
+ LOAD_STAT_FUNC();
+ return _stat(pathname, buf);
+ }
+
+ debug(DEBUG_LEVEL_NORMAL, __FILE__": stat(%s)\n", pathname);
+
+#ifdef _STAT_VER
+#ifdef HAVE_OPEN64
+ ret = __xstat64(_STAT_VER, "/dev", &parent);
+#else
+ ret = __xstat(_STAT_VER, "/dev", &parent);
+#endif
+#else
+#ifdef HAVE_OPEN64
+ ret = stat64("/dev", &parent);
+#else
+ ret = stat("/dev", &parent);
+#endif
+#endif
+
+ if (ret) {
+ debug(DEBUG_LEVEL_NORMAL, __FILE__": unable to stat \"/dev\"\n");
+ return -1;
+ }
+
+ buf->st_dev = parent.st_dev;
+ buf->st_ino = 0xDEADBEEF; /* FIXME: Can we do this in a safe way? */
+ buf->st_mode = S_IFCHR | S_IRUSR | S_IWUSR;
+ buf->st_nlink = 1;
+ buf->st_uid = getuid();
+ buf->st_gid = getgid();
+ buf->st_rdev = 0x0E03; /* FIXME: Linux specific */
+ buf->st_size = 0;
+ buf->st_atime = 1181557705;
+ buf->st_mtime = 1181557705;
+ buf->st_ctime = 1181557705;
+ buf->st_blksize = 1;
+ buf->st_blocks = 0;
+
+ return 0;
+}
+
#ifdef HAVE_OPEN64
+int stat64(const char *pathname, struct stat64 *buf) {
+ struct stat oldbuf;
+ int ret;
+
+ debug(DEBUG_LEVEL_VERBOSE, __FILE__": stat64(%s)\n", pathname?pathname:"NULL");
+
+ if (!pathname ||
+ !buf ||
+ ( strcmp(pathname, "/dev/dsp") != 0 &&
+ strcmp(pathname, "/dev/adsp") != 0 &&
+ strcmp(pathname, "/dev/sndstat") != 0 &&
+ strcmp(pathname, "/dev/mixer") != 0 )) {
+ LOAD_STAT64_FUNC();
+ return _stat64(pathname, buf);
+ }
+
+ ret = stat(pathname, &oldbuf);
+ if (ret)
+ return ret;
+
+ buf->st_dev = oldbuf.st_dev;
+ buf->st_ino = oldbuf.st_ino;
+ buf->st_mode = oldbuf.st_mode;
+ buf->st_nlink = oldbuf.st_nlink;
+ buf->st_uid = oldbuf.st_uid;
+ buf->st_gid = oldbuf.st_gid;
+ buf->st_rdev = oldbuf.st_rdev;
+ buf->st_size = oldbuf.st_size;
+ buf->st_atime = oldbuf.st_atime;
+ buf->st_mtime = oldbuf.st_mtime;
+ buf->st_ctime = oldbuf.st_ctime;
+ buf->st_blksize = oldbuf.st_blksize;
+ buf->st_blocks = oldbuf.st_blocks;
+
+ return 0;
+}
+
int open64(const char *filename, int flags, ...) {
va_list args;
mode_t mode = 0;
- debug(DEBUG_LEVEL_VERBOSE, __FILE__": open64(%s)\n", filename);
-
- va_start(args, flags);
- if (flags & O_CREAT)
- mode = va_arg(args, mode_t);
- va_end(args);
+ debug(DEBUG_LEVEL_VERBOSE, __FILE__": open64(%s)\n", filename?filename:"NULL");
- if (strcmp(filename, "/dev/dsp") != 0 &&
- strcmp(filename, "/dev/adsp") != 0 &&
- strcmp(filename, "/dev/sndstat") != 0 &&
- strcmp(filename, "/dev/mixer") != 0) {
+ if (flags & O_CREAT) {
+ va_start(args, flags);
+ if (sizeof(mode_t) < sizeof(int))
+ mode = va_arg(args, int);
+ else
+ mode = va_arg(args, mode_t);
+ va_end(args);
+ }
+
+ if (!filename ||
+ ( strcmp(filename, "/dev/dsp") != 0 &&
+ strcmp(filename, "/dev/adsp") != 0 &&
+ strcmp(filename, "/dev/sndstat") != 0 &&
+ strcmp(filename, "/dev/mixer") != 0 )) {
LOAD_OPEN64_FUNC();
return _open64(filename, flags, mode);
}
- return open(filename, flags, mode);
+ return real_open(filename, flags, mode);
+}
+
+#endif
+
+#ifdef _STAT_VER
+
+int __xstat(int ver, const char *pathname, struct stat *buf) {
+ debug(DEBUG_LEVEL_VERBOSE, __FILE__": __xstat(%s)\n", pathname?pathname:"NULL");
+
+ if (!pathname ||
+ !buf ||
+ ( strcmp(pathname, "/dev/dsp") != 0 &&
+ strcmp(pathname, "/dev/adsp") != 0 &&
+ strcmp(pathname, "/dev/sndstat") != 0 &&
+ strcmp(pathname, "/dev/mixer") != 0 )) {
+ LOAD_XSTAT_FUNC();
+ return ___xstat(ver, pathname, buf);
+ }
+
+ if (ver != _STAT_VER) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ return stat(pathname, buf);
}
+#ifdef HAVE_OPEN64
+
+int __xstat64(int ver, const char *pathname, struct stat64 *buf) {
+ debug(DEBUG_LEVEL_VERBOSE, __FILE__": __xstat64(%s)\n", pathname?pathname:"NULL");
+
+ if (!pathname ||
+ !buf ||
+ ( strcmp(pathname, "/dev/dsp") != 0 &&
+ strcmp(pathname, "/dev/adsp") != 0 &&
+ strcmp(pathname, "/dev/sndstat") != 0 &&
+ strcmp(pathname, "/dev/mixer") != 0 )) {
+ LOAD_XSTAT64_FUNC();
+ return ___xstat64(ver, pathname, buf);
+ }
+
+ if (ver != _STAT_VER) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ return stat64(pathname, buf);
+}
+
+#endif
+
#endif
FILE* fopen(const char *filename, const char *mode) {
FILE *f = NULL;
int fd;
mode_t m;
-
- debug(DEBUG_LEVEL_VERBOSE, __FILE__": fopen(%s)\n", filename);
- if (strcmp(filename, "/dev/dsp") != 0 &&
- strcmp(filename, "/dev/adsp") != 0 &&
- strcmp(filename, "/dev/sndstat") != 0 &&
- strcmp(filename, "/dev/mixer") != 0) {
+ debug(DEBUG_LEVEL_VERBOSE, __FILE__": fopen(%s)\n", filename?filename:"NULL");
+
+ if (!filename ||
+ !mode ||
+ ( strcmp(filename, "/dev/dsp") != 0 &&
+ strcmp(filename, "/dev/adsp") != 0 &&
+ strcmp(filename, "/dev/sndstat") != 0 &&
+ strcmp(filename, "/dev/mixer") != 0 )) {
LOAD_FOPEN_FUNC();
return _fopen(filename, mode);
}
@@ -2286,14 +2602,14 @@ FILE* fopen(const char *filename, const char *mode) {
if ((((mode[1] == 'b') || (mode[1] == 't')) && (mode[2] == '+')) || (mode[1] == '+'))
m = O_RDWR;
- if ((fd = open(filename, m)) < 0)
+ if ((fd = real_open(filename, m, 0)) < 0)
return NULL;
if (!(f = fdopen(fd, mode))) {
close(fd);
return NULL;
}
-
+
return f;
}
@@ -2301,12 +2617,14 @@ FILE* fopen(const char *filename, const char *mode) {
FILE *fopen64(const char *filename, const char *mode) {
- debug(DEBUG_LEVEL_VERBOSE, __FILE__": fopen64(%s)\n", filename);
+ debug(DEBUG_LEVEL_VERBOSE, __FILE__": fopen64(%s)\n", filename?filename:"NULL");
- if (strcmp(filename, "/dev/dsp") != 0 &&
- strcmp(filename, "/dev/adsp") != 0 &&
- strcmp(filename, "/dev/sndstat") != 0 &&
- strcmp(filename, "/dev/mixer") != 0) {
+ if (!filename ||
+ !mode ||
+ ( strcmp(filename, "/dev/dsp") != 0 &&
+ strcmp(filename, "/dev/adsp") != 0 &&
+ strcmp(filename, "/dev/sndstat") != 0 &&
+ strcmp(filename, "/dev/mixer") != 0 )) {
LOAD_FOPEN64_FUNC();
return _fopen64(filename, mode);
}
@@ -2337,9 +2655,9 @@ int fclose(FILE *f) {
/* Dirty trick to avoid that the fd is not freed twice, once by us
* and once by the real fclose() */
i->app_fd = -1;
-
+
fd_info_unref(i);
-
+
function_exit();
LOAD_FCLOSE_FUNC();
diff --git a/src/utils/paplay.c b/src/utils/paplay.c
index 0386c9df..1b6228b1 100644
--- a/src/utils/paplay.c
+++ b/src/utils/paplay.c
@@ -1,18 +1,19 @@
-/* $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
@@ -77,14 +78,14 @@ static void stream_drain_complete(pa_stream*s, int success, void *userdata) {
fprintf(stderr, "Failed to drain stream: %s\n", pa_strerror(pa_context_errno(context)));
quit(1);
}
-
- if (verbose)
+
+ if (verbose)
fprintf(stderr, "Playback stream drained.\n");
pa_stream_disconnect(stream);
pa_stream_unref(stream);
stream = NULL;
-
+
if (!(o = pa_context_drain(context, context_drain_complete, NULL)))
pa_context_disconnect(context);
else {
@@ -111,7 +112,7 @@ static void stream_write_callback(pa_stream *s, size_t length, void *userdata) {
if ((bytes = readf_function(sndfile, data, length/k)) > 0)
bytes *= k;
-
+
} else
bytes = sf_read_raw(sndfile, data, length);
@@ -120,7 +121,7 @@ static void stream_write_callback(pa_stream *s, size_t length, void *userdata) {
else
pa_xfree(data);
- if (bytes < length) {
+ if (bytes < (sf_count_t) length) {
sf_close(sndfile);
sndfile = NULL;
pa_operation_unref(pa_stream_drain(s, stream_drain_complete, NULL));
@@ -140,7 +141,7 @@ static void stream_state_callback(pa_stream *s, void *userdata) {
if (verbose)
fprintf(stderr, "Stream successfully created\n");
break;
-
+
case PA_STREAM_FAILED:
default:
fprintf(stderr, "Stream errror: %s\n", pa_strerror(pa_context_errno(pa_stream_get_context(s))));
@@ -157,10 +158,10 @@ static void context_state_callback(pa_context *c, void *userdata) {
case PA_CONTEXT_AUTHORIZING:
case PA_CONTEXT_SETTING_NAME:
break;
-
+
case PA_CONTEXT_READY: {
pa_cvolume cv;
-
+
assert(c && !stream);
if (verbose)
@@ -172,10 +173,10 @@ static void context_state_callback(pa_context *c, void *userdata) {
pa_stream_set_state_callback(stream, stream_state_callback, NULL);
pa_stream_set_write_callback(stream, stream_write_callback, NULL);
pa_stream_connect_playback(stream, device, NULL, 0, pa_cvolume_set(&cv, sample_spec.channels, volume), NULL);
-
+
break;
}
-
+
case PA_CONTEXT_TERMINATED:
quit(0);
break;
@@ -192,7 +193,7 @@ static void exit_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig,
if (verbose)
fprintf(stderr, "Got SIGINT, exiting.\n");
quit(0);
-
+
}
static void help(const char *argv0) {
@@ -200,9 +201,9 @@ static void help(const char *argv0) {
printf("%s [options] [FILE]\n\n"
" -h, --help Show this help\n"
" --version Show version\n\n"
- " -v, --verbose Enable verbose operations\n\n"
+ " -v, --verbose Enable verbose operation\n\n"
" -s, --server=SERVER The name of the server to connect to\n"
- " -d, --device=DEVICE The name of the sink/source to connect to\n"
+ " -d, --device=DEVICE The name of the sink to connect to\n"
" -n, --client-name=NAME How to call this client on the server\n"
" --stream-name=NAME How to call this stream on the server\n"
" --volume=VOLUME Specify the initial (linear) volume in range 0...65536\n"
@@ -251,7 +252,7 @@ int main(int argc, char *argv[]) {
help(bn);
ret = 0;
goto quit;
-
+
case ARG_VERSION:
printf("paplay "PACKAGE_VERSION"\nCompiled with libpulse %s\nLinked with libpulse %s\n", pa_get_headers_version(), pa_get_library_version());
ret = 0;
@@ -302,7 +303,7 @@ int main(int argc, char *argv[]) {
}
filename = optind < argc ? argv[optind] : "STDIN";
-
+
memset(&sfinfo, 0, sizeof(sfinfo));
if (optind < argc)
@@ -317,9 +318,9 @@ int main(int argc, char *argv[]) {
sample_spec.rate = sfinfo.samplerate;
sample_spec.channels = sfinfo.channels;
-
+
readf_function = NULL;
-
+
switch (sfinfo.format & 0xFF) {
case SF_FORMAT_PCM_16:
case SF_FORMAT_PCM_U8:
@@ -327,11 +328,11 @@ int main(int argc, char *argv[]) {
sample_spec.format = PA_SAMPLE_S16NE;
readf_function = (sf_count_t (*)(SNDFILE *_sndfile, void *ptr, sf_count_t frames)) sf_readf_short;
break;
-
+
case SF_FORMAT_ULAW:
sample_spec.format = PA_SAMPLE_ULAW;
break;
-
+
case SF_FORMAT_ALAW:
sample_spec.format = PA_SAMPLE_ALAW;
break;
@@ -369,13 +370,13 @@ int main(int argc, char *argv[]) {
if (!stream_name)
stream_name = pa_utf8_filter(n);
}
-
+
if (verbose) {
char t[PA_SAMPLE_SPEC_SNPRINT_MAX];
pa_sample_spec_snprint(t, sizeof(t), &sample_spec);
fprintf(stderr, "Using sample spec '%s'\n", t);
}
-
+
/* Set up a new main loop */
if (!(m = pa_mainloop_new())) {
fprintf(stderr, "pa_mainloop_new() failed.\n");
@@ -390,7 +391,7 @@ int main(int argc, char *argv[]) {
#ifdef SIGPIPE
signal(SIGPIPE, SIG_IGN);
#endif
-
+
/* Create a new connection context */
if (!(context = pa_context_new(mainloop_api, client_name))) {
fprintf(stderr, "pa_context_new() failed.\n");
@@ -407,7 +408,7 @@ int main(int argc, char *argv[]) {
fprintf(stderr, "pa_mainloop_run() failed.\n");
goto quit;
}
-
+
quit:
if (stream)
pa_stream_unref(stream);
@@ -427,6 +428,6 @@ quit:
if (sndfile)
sf_close(sndfile);
-
+
return ret;
}
diff --git a/src/utils/pasuspender.c b/src/utils/pasuspender.c
new file mode 100644
index 00000000..5b4885db
--- /dev/null
+++ b/src/utils/pasuspender.c
@@ -0,0 +1,316 @@
+/***
+ 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 <sys/types.h>
+#include <sys/wait.h>
+
+#include <signal.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <getopt.h>
+
+#include <sndfile.h>
+
+#ifdef __linux__
+#include <sys/prctl.h>
+#endif
+
+#include <pulse/pulseaudio.h>
+#include <pulsecore/macro.h>
+
+#if PA_API_VERSION < 10
+#error Invalid PulseAudio API version
+#endif
+
+#define BUFSIZE 1024
+
+static pa_context *context = NULL;
+static pa_mainloop_api *mainloop_api = NULL;
+static char **child_argv = NULL;
+static int child_argc = 0;
+static pid_t child_pid = (pid_t) -1;
+static int child_ret = 0;
+static int dead = 1;
+
+static void quit(int ret) {
+ pa_assert(mainloop_api);
+ mainloop_api->quit(mainloop_api, ret);
+}
+
+
+static void context_drain_complete(pa_context *c, void *userdata) {
+ pa_context_disconnect(c);
+}
+
+static void drain(void) {
+ pa_operation *o;
+
+ if (!(o = pa_context_drain(context, context_drain_complete, NULL)))
+ pa_context_disconnect(context);
+ else
+ pa_operation_unref(o);
+}
+
+static void start_child(void) {
+
+ if ((child_pid = fork()) < 0) {
+
+ fprintf(stderr, "fork(): %s\n", strerror(errno));
+ quit(1);
+
+ } else if (child_pid == 0) {
+ /* Child */
+
+#ifdef __linux__
+ prctl(PR_SET_PDEATHSIG, SIGTERM, 0, 0, 0);
+#endif
+
+ if (execvp(child_argv[0], child_argv) < 0)
+ fprintf(stderr, "execvp(): %s\n", strerror(errno));
+
+ _exit(1);
+
+ } else {
+
+ /* parent */
+ dead = 0;
+ }
+}
+
+static void suspend_complete(pa_context *c, int success, void *userdata) {
+ static int n = 0;
+
+ n++;
+
+ if (!success) {
+ fprintf(stderr, "Failure to suspend: %s\n", pa_strerror(pa_context_errno(c)));
+ quit(1);
+ return;
+ }
+
+ if (n >= 2)
+ start_child();
+}
+
+static void resume_complete(pa_context *c, int success, void *userdata) {
+ static int n = 0;
+
+ n++;
+
+ if (!success) {
+ fprintf(stderr, "Failure to resume: %s\n", pa_strerror(pa_context_errno(c)));
+ quit(1);
+ return;
+ }
+
+ if (n >= 2)
+ drain(); /* drain and quit */
+}
+
+static void context_state_callback(pa_context *c, void *userdata) {
+ pa_assert(c);
+
+ switch (pa_context_get_state(c)) {
+ case PA_CONTEXT_CONNECTING:
+ case PA_CONTEXT_AUTHORIZING:
+ case PA_CONTEXT_SETTING_NAME:
+ break;
+
+ case PA_CONTEXT_READY:
+ if (pa_context_is_local(c)) {
+ pa_operation_unref(pa_context_suspend_sink_by_index(c, PA_INVALID_INDEX, 1, suspend_complete, NULL));
+ pa_operation_unref(pa_context_suspend_source_by_index(c, PA_INVALID_INDEX, 1, suspend_complete, NULL));
+ } else {
+ fprintf(stderr, "WARNING: Sound server is not local, not suspending.\n");
+ start_child();
+ }
+
+ break;
+
+ case PA_CONTEXT_TERMINATED:
+ quit(0);
+ break;
+
+ case PA_CONTEXT_FAILED:
+ default:
+ fprintf(stderr, "Connection failure: %s\n", pa_strerror(pa_context_errno(c)));
+
+ pa_context_unref(context);
+ context = NULL;
+
+ if (child_pid == (pid_t) -1)
+ /* not started yet, then we do it now */
+ start_child();
+ else if (dead)
+ /* already started, and dead, so let's quit */
+ quit(1);
+
+ break;
+ }
+}
+
+static void sigint_callback(pa_mainloop_api *m, pa_signal_event *e, int sig, void *userdata) {
+ fprintf(stderr, "Got SIGINT, exiting.\n");
+ quit(0);
+}
+
+static void sigchld_callback(pa_mainloop_api *m, pa_signal_event *e, int sig, void *userdata) {
+ int status = 0;
+ pid_t p;
+
+ p = waitpid(-1, &status, WNOHANG);
+
+ if (p != child_pid)
+ return;
+
+ dead = 1;
+
+ if (WIFEXITED(status))
+ child_ret = WEXITSTATUS(status);
+ else if (WIFSIGNALED(status)) {
+ fprintf(stderr, "WARNING: Child process terminated by signal %u\n", WTERMSIG(status));
+ child_ret = 1;
+ }
+
+ if (context) {
+ if (pa_context_is_local(context)) {
+ /* A context is around, so let's resume */
+ pa_operation_unref(pa_context_suspend_sink_by_index(context, PA_INVALID_INDEX, 0, resume_complete, NULL));
+ pa_operation_unref(pa_context_suspend_source_by_index(context, PA_INVALID_INDEX, 0, resume_complete, NULL));
+ } else
+ drain();
+ } else
+ /* Hmm, no context here, so let's terminate right away */
+ quit(0);
+}
+
+static void help(const char *argv0) {
+
+ printf("%s [options] ... \n\n"
+ " -h, --help Show this help\n"
+ " --version Show version\n"
+ " -s, --server=SERVER The name of the server to connect to\n\n",
+ argv0);
+}
+
+enum {
+ ARG_VERSION = 256
+};
+
+int main(int argc, char *argv[]) {
+ pa_mainloop* m = NULL;
+ int c, ret = 1;
+ char *server = NULL, *bn;
+
+ static const struct option long_options[] = {
+ {"server", 1, NULL, 's'},
+ {"version", 0, NULL, ARG_VERSION},
+ {"help", 0, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ if (!(bn = strrchr(argv[0], '/')))
+ bn = argv[0];
+ else
+ bn++;
+
+ while ((c = getopt_long(argc, argv, "s:h", long_options, NULL)) != -1) {
+ switch (c) {
+ case 'h' :
+ help(bn);
+ ret = 0;
+ goto quit;
+
+ case ARG_VERSION:
+ printf("pasuspender "PACKAGE_VERSION"\nCompiled with libpulse %s\nLinked with libpulse %s\n", pa_get_headers_version(), pa_get_library_version());
+ ret = 0;
+ goto quit;
+
+ case 's':
+ pa_xfree(server);
+ server = pa_xstrdup(optarg);
+ break;
+
+ default:
+ goto quit;
+ }
+ }
+
+ child_argv = argv + optind;
+ child_argc = argc - optind;
+
+ if (child_argc <= 0) {
+ help(bn);
+ ret = 0;
+ goto quit;
+ }
+
+ if (!(m = pa_mainloop_new())) {
+ fprintf(stderr, "pa_mainloop_new() failed.\n");
+ goto quit;
+ }
+
+ pa_assert_se(mainloop_api = pa_mainloop_get_api(m));
+ pa_assert_se(pa_signal_init(mainloop_api) == 0);
+ pa_signal_new(SIGINT, sigint_callback, NULL);
+ pa_signal_new(SIGCHLD, sigchld_callback, NULL);
+#ifdef SIGPIPE
+ signal(SIGPIPE, SIG_IGN);
+#endif
+
+ if (!(context = pa_context_new(mainloop_api, bn))) {
+ fprintf(stderr, "pa_context_new() failed.\n");
+ goto quit;
+ }
+
+ pa_context_set_state_callback(context, context_state_callback, NULL);
+ pa_context_connect(context, server, PA_CONTEXT_NOAUTOSPAWN, NULL);
+
+ if (pa_mainloop_run(m, &ret) < 0) {
+ fprintf(stderr, "pa_mainloop_run() failed.\n");
+ goto quit;
+ }
+
+quit:
+ if (context)
+ pa_context_unref(context);
+
+ if (m) {
+ pa_signal_done();
+ pa_mainloop_free(m);
+ }
+
+ pa_xfree(server);
+
+ if (!dead)
+ kill(child_pid, SIGTERM);
+
+ return ret == 0 ? child_ret : ret;
+}
diff --git a/src/utils/pax11publish.c b/src/utils/pax11publish.c
index 6a3c6dbc..eee7b6a8 100644
--- a/src/utils/pax11publish.c
+++ b/src/utils/pax11publish.c
@@ -1,18 +1,18 @@
-/* $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
@@ -99,7 +99,7 @@ int main(int argc, char *argv[]) {
switch (mode) {
case DUMP: {
char t[1024];
- if (pa_x11_get_prop(d, "PULSE_SERVER", t, sizeof(t)))
+ if (pa_x11_get_prop(d, "PULSE_SERVER", t, sizeof(t)))
printf("Server: %s\n", t);
if (pa_x11_get_prop(d, "PULSE_SOURCE", t, sizeof(t)))
printf("Source: %s\n", t);
@@ -110,10 +110,10 @@ int main(int argc, char *argv[]) {
break;
}
-
+
case IMPORT: {
char t[1024];
- if (pa_x11_get_prop(d, "PULSE_SERVER", t, sizeof(t)))
+ if (pa_x11_get_prop(d, "PULSE_SERVER", t, sizeof(t)))
printf("PULSE_SERVER='%s'\nexport PULSE_SERVER\n", t);
if (pa_x11_get_prop(d, "PULSE_SOURCE", t, sizeof(t)))
printf("PULSE_SOURCE='%s'\nexport PULSE_SOURCE\n", t);
@@ -158,7 +158,7 @@ int main(int argc, char *argv[]) {
pa_x11_del_prop(d, "PULSE_SOURCE");
pa_x11_del_prop(d, "PULSE_ID");
pa_x11_del_prop(d, "PULSE_COOKIE");
-
+
if (server)
pa_x11_set_prop(d, "PULSE_SERVER", server);
else if (conf->default_server)
@@ -169,7 +169,7 @@ int main(int argc, char *argv[]) {
fprintf(stderr, "Failed to get FQDN.\n");
goto finish;
}
-
+
pa_x11_set_prop(d, "PULSE_SERVER", hn);
}
@@ -184,7 +184,7 @@ int main(int argc, char *argv[]) {
pa_x11_set_prop(d, "PULSE_SOURCE", conf->default_source);
pa_client_conf_free(conf);
-
+
if (pa_authkey_load_auto(cookie_file, cookie, sizeof(cookie)) < 0) {
fprintf(stderr, "Failed to load cookie data\n");
goto finish;
@@ -201,20 +201,20 @@ int main(int argc, char *argv[]) {
pa_x11_del_prop(d, "PULSE_ID");
pa_x11_del_prop(d, "PULSE_COOKIE");
break;
-
+
default:
fprintf(stderr, "No yet implemented.\n");
goto finish;
}
ret = 0;
-
+
finish:
if (d) {
XSync(d, False);
XCloseDisplay(d);
}
-
+
return ret;
}