From e06c76688c5f61bbed2977282146595b90371ded Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 30 Oct 2007 17:12:57 +0000 Subject: Tagging release 0.9.3 git-svn-id: file:///home/lennart/svn/public/xmms-pulse/tags/release-0.9.3@56 ef929aba-56e2-0310-84e0-b7573d389508 --- trunk/LICENSE | 340 ++++++++++++++++++++ trunk/Makefile.am | 38 +++ trunk/acinclude.m4 | 199 ++++++++++++ trunk/bootstrap.sh | 63 ++++ trunk/configure.ac | 159 ++++++++++ trunk/doc/Makefile.am | 36 +++ trunk/doc/README.html.in | 130 ++++++++ trunk/doc/style.css | 27 ++ trunk/src/Makefile.am | 45 +++ trunk/src/plugin.c | 801 +++++++++++++++++++++++++++++++++++++++++++++++ 10 files changed, 1838 insertions(+) create mode 100644 trunk/LICENSE create mode 100644 trunk/Makefile.am create mode 100644 trunk/acinclude.m4 create mode 100755 trunk/bootstrap.sh create mode 100644 trunk/configure.ac create mode 100644 trunk/doc/Makefile.am create mode 100644 trunk/doc/README.html.in create mode 100644 trunk/doc/style.css create mode 100644 trunk/src/Makefile.am create mode 100644 trunk/src/plugin.c diff --git a/trunk/LICENSE b/trunk/LICENSE new file mode 100644 index 0000000..d60c31a --- /dev/null +++ b/trunk/LICENSE @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/trunk/Makefile.am b/trunk/Makefile.am new file mode 100644 index 0000000..6963ce7 --- /dev/null +++ b/trunk/Makefile.am @@ -0,0 +1,38 @@ +# $Id$ +# +# This file is part of xmms-pulse. +# +# xmms-pulse 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. +# +# xmms-pulse is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY 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 xmms-pulse; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +# USA. + +EXTRA_DIST = bootstrap.sh README LICENSE +SUBDIRS=src doc + +MAINTAINERCLEANFILES=README +noinst_DATA = README + +README: + rm -f README + $(MAKE) -C doc README + cd $(srcdir) && ln -s doc/README README + +homepage: all dist + test -d $$HOME/homepage/private + mkdir -p $$HOME/homepage/private/projects/xmms-pulse + cp xmms-pulse-@PACKAGE_VERSION@.tar.gz $$HOME/homepage/private/projects/xmms-pulse + cp doc/README.html doc/style.css $$HOME/homepage/private/projects/xmms-pulse + ln -sf README.html $$HOME/homepage/private/projects/xmms-pulse/index.html + +.PHONY: homepage diff --git a/trunk/acinclude.m4 b/trunk/acinclude.m4 new file mode 100644 index 0000000..bedf51c --- /dev/null +++ b/trunk/acinclude.m4 @@ -0,0 +1,199 @@ +dnl Available from the GNU Autoconf Macro Archive at: +dnl http://www.gnu.org/software/ac-archive/htmldoc/acx_pthread.html +dnl +AC_DEFUN([ACX_PTHREAD], [ +AC_REQUIRE([AC_CANONICAL_HOST]) +AC_LANG_SAVE +AC_LANG_C +acx_pthread_ok=no + +# We used to check for pthread.h first, but this fails if pthread.h +# requires special compiler flags (e.g. on True64 or Sequent). +# It gets checked for in the link test anyway. + +# First of all, check if the user has set any of the PTHREAD_LIBS, +# etcetera environment variables, and if threads linking works using +# them: +if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) + AC_TRY_LINK_FUNC(pthread_join, acx_pthread_ok=yes) + AC_MSG_RESULT($acx_pthread_ok) + if test x"$acx_pthread_ok" = xno; then + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" + fi + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" +fi + +# We must check for the threads library under a number of different +# names; the ordering is very important because some systems +# (e.g. DEC) have both -lpthread and -lpthreads, where one of the +# libraries is broken (non-POSIX). + +# Create a list of thread flags to try. Items starting with a "-" are +# C compiler flags, and other items are library names, except for "none" +# which indicates that we try without any flags at all, and "pthread-config" +# which is a program returning the flags for the Pth emulation library. + +acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" + +# The ordering *is* (sometimes) important. Some notes on the +# individual items follow: + +# pthreads: AIX (must check this before -lpthread) +# none: in case threads are in libc; should be tried before -Kthread and +# other compiler flags to prevent continual compiler warnings +# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) +# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) +# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) +# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) +# -pthreads: Solaris/gcc +# -mthreads: Mingw32/gcc, Lynx/gcc +# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it +# doesn't hurt to check since this sometimes defines pthreads too; +# also defines -D_REENTRANT) +# pthread: Linux, etcetera +# --thread-safe: KAI C++ +# pthread-config: use pthread-config program (for GNU Pth library) + +case "${host_cpu}-${host_os}" in + *solaris*) + + # On Solaris (at least, for some versions), libc contains stubbed + # (non-functional) versions of the pthreads routines, so link-based + # tests will erroneously succeed. (We need to link with -pthread or + # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather + # a function called by this macro, so we could check for that, but + # who knows whether they'll stub that too in a future libc.) So, + # we'll just look for -pthreads and -lpthread first: + + acx_pthread_flags="-pthread -pthreads pthread -mt $acx_pthread_flags" + ;; +esac + +if test x"$acx_pthread_ok" = xno; then +for flag in $acx_pthread_flags; do + + case $flag in + none) + AC_MSG_CHECKING([whether pthreads work without any flags]) + ;; + + -*) + AC_MSG_CHECKING([whether pthreads work with $flag]) + PTHREAD_CFLAGS="$flag" + ;; + + pthread-config) + AC_CHECK_PROG(acx_pthread_config, pthread-config, yes, no) + if test x"$acx_pthread_config" = xno; then continue; fi + PTHREAD_CFLAGS="`pthread-config --cflags`" + PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" + ;; + + *) + AC_MSG_CHECKING([for the pthreads library -l$flag]) + PTHREAD_LIBS="-l$flag" + ;; + esac + + save_LIBS="$LIBS" + save_CFLAGS="$CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + + # Check for various functions. We must include pthread.h, + # since some functions may be macros. (On the Sequent, we + # need a special flag -Kthread to make this header compile.) + # We check for pthread_join because it is in -lpthread on IRIX + # while pthread_create is in libc. We check for pthread_attr_init + # due to DEC craziness with -lpthreads. We check for + # pthread_cleanup_push because it is one of the few pthread + # functions on Solaris that doesn't have a non-functional libc stub. + # We try pthread_create on general principles. + AC_TRY_LINK([#include ], + [pthread_t th; pthread_join(th, 0); + pthread_attr_init(0); pthread_cleanup_push(0, 0); + pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], + [acx_pthread_ok=yes]) + + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + + AC_MSG_RESULT($acx_pthread_ok) + if test "x$acx_pthread_ok" = xyes; then + break; + fi + + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" +done +fi + +# Various other checks: +if test "x$acx_pthread_ok" = xyes; then + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + + # Detect AIX lossage: threads are created detached by default + # and the JOINABLE attribute has a nonstandard name (UNDETACHED). + AC_MSG_CHECKING([for joinable pthread attribute]) + AC_TRY_LINK([#include ], + [int attr=PTHREAD_CREATE_JOINABLE;], + ok=PTHREAD_CREATE_JOINABLE, ok=unknown) + if test x"$ok" = xunknown; then + AC_TRY_LINK([#include ], + [int attr=PTHREAD_CREATE_UNDETACHED;], + ok=PTHREAD_CREATE_UNDETACHED, ok=unknown) + fi + if test x"$ok" != xPTHREAD_CREATE_JOINABLE; then + AC_DEFINE(PTHREAD_CREATE_JOINABLE, $ok, + [Define to the necessary symbol if this constant + uses a non-standard name on your system.]) + fi + AC_MSG_RESULT(${ok}) + if test x"$ok" = xunknown; then + AC_MSG_WARN([we do not know how to create joinable pthreads]) + fi + + AC_MSG_CHECKING([if more special flags are required for pthreads]) + flag=no + case "${host_cpu}-${host_os}" in + *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";; + *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";; + esac + AC_MSG_RESULT(${flag}) + if test "x$flag" != xno; then + PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" + fi + + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + + # More AIX lossage: must compile with cc_r + AC_CHECK_PROG(PTHREAD_CC, cc_r, cc_r, ${CC}) +else + PTHREAD_CC="$CC" +fi + +AC_SUBST(PTHREAD_LIBS) +AC_SUBST(PTHREAD_CFLAGS) +AC_SUBST(PTHREAD_CC) + +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test x"$acx_pthread_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1]) + : +else + acx_pthread_ok=no + $2 +fi +AC_LANG_RESTORE +])dnl ACX_PTHREAD diff --git a/trunk/bootstrap.sh b/trunk/bootstrap.sh new file mode 100755 index 0000000..07c6e2b --- /dev/null +++ b/trunk/bootstrap.sh @@ -0,0 +1,63 @@ +#!/bin/bash +# $Id$ + +# This file is part of xmms-pulse. +# +# xmms-pulse 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. +# +# xmms-pulse is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY 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 xmms-pulse; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +VERSION=1.9 + +run_versioned() { + local P + local V + + V=$(echo "$2" | sed -e 's,\.,,g') + + if [ -e "`which $1$V`" ] ; then + P="$1$V" + else + if [ -e "`which $1-$2`" ] ; then + P="$1-$2" + else + P="$1" + fi + fi + + shift 2 + "$P" "$@" +} + +set -ex + +if [ "x$1" = "xam" ] ; then + run_versioned automake "$VERSION" -a -c --foreign + ./config.status +else + rm -rf autom4te.cache + rm -f config.cache + + test "x$LIBTOOLIZE" = "x" && LIBTOOLIZE=libtoolize + + "$LIBTOOLIZE" -c --force + run_versioned aclocal "$VERSION" + run_versioned autoconf 2.59 -Wall + run_versioned autoheader 2.59 + run_versioned automake "$VERSION" -a -c --foreign + + if test "x$NOCONFIGURE" = "x"; then + CFLAGS="-g -O0" ./configure --sysconfdir=/etc "$@" + make clean + fi +fi diff --git a/trunk/configure.ac b/trunk/configure.ac new file mode 100644 index 0000000..948dc90 --- /dev/null +++ b/trunk/configure.ac @@ -0,0 +1,159 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +# $Id$ + +# This file is part of xmms-pulse. +# +# xmms-pulse 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. +# +# xmms-pulse is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY 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 xmms-pulse; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +AC_PREREQ(2.57) +AC_INIT([xmms-pulse],[0.9.3],[mzkzzfchyfr (at) 0pointer (dot) de]) +AC_CONFIG_SRCDIR([src/plugin.c]) +AC_CONFIG_HEADERS([config.h]) +AM_INIT_AUTOMAKE([foreign 1.9 -Wall]) + +AC_SUBST(PACKAGE_URL, [http://0pointer.de/lennart/projects/xmms-pulse/]) + +if type -p stow > /dev/null && test -d /usr/local/stow ; then + AC_MSG_NOTICE([*** Found /usr/local/stow: default install prefix set to /usr/local/stow/${PACKAGE_NAME}-${PACKAGE_VERSION} ***]) + ac_default_prefix="/usr/local/stow/${PACKAGE_NAME}-${PACKAGE_VERSION}" +fi + +# Checks for programs. +AC_PROG_CC +AC_PROG_LN_S +AC_PROG_LIBTOOL + +AC_CHECK_FUNCS([gethostname]) +AC_CHECK_HEADERS([limits.h]) +AC_C_CONST +AC_FUNC_MALLOC +AC_TYPE_SIZE_T + +PKG_PROG_PKG_CONFIG + +if test -d ../pulseaudio ; then + PULSE_CFLAGS='-I$(top_srcdir)/../pulseaudio/src' + PULSE_LIBS='-L$(top_srcdir)/../pulseaudio/src/.libs -lpulse' + echo "*** Found pulseaudio in ../pulseaudio, using that version ***" +else + PKG_CHECK_MODULES(PULSE, [ libpulse >= 0.9.2 ]) +fi + +AC_SUBST(PULSE_LIBS) +AC_SUBST(PULSE_CFLAGS) + + +# XMMS plugin # + +AC_ARG_ENABLE([xmms], + AS_HELP_STRING([--disable-xmms],[Disable optional XMMS pluggin support]), + [ + case "${enableval}" in + yes) xmms=true ;; + no) xmms=false ;; + *) AC_MSG_ERROR(bad value ${enableval} for --disable-xmms) ;; + esac + ], + [xmms=true]) + +if test "x${xmms}" != xfalse ; then + AC_PATH_PROG(XMMS_CONFIG, xmms-config) + if test "x$XMMS_CONFIG" = "x" ; then + AC_MSG_ERROR([*** Please install xmms-config of the xmms-dev package into your \$PATH ***]) + fi + XMMS_CFLAGS=`$XMMS_CONFIG --cflags` + XMMS_LIBS=`$XMMS_CONFIG --libs` + XMMS_OUTPUTPLUGINDIR=`$XMMS_CONFIG --output-plugin-dir` + +else + echo "*** XMMS disabled ***" + XMMS_CFLAGS= + XMMS_LIBS= + XMMS_OUTPUTPLUGINDIR= +fi + +AC_SUBST(XMMS_CFLAGS) +AC_SUBST(XMMS_LIBS) +AC_SUBST(XMMS_OUTPUTPLUGINDIR) + +AM_CONDITIONAL(BUILD_XMMS, test "x$xmms" = xtrue) + + +# end of xmms # + + +# BMP plugin # + +AC_ARG_ENABLE([bmp], + AS_HELP_STRING([--enable-bmp],[Enable optional Beep-Media-Player pluggin support]), + [ + case "${enableval}" in + yes) bmp=true ;; + no) bmp=false ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-bmp) ;; + esac + ], + [bmp=false]) + +if test "x${bmp}" != xfalse ; then + PKG_CHECK_MODULES(BMP, [ bmp >= 0.9.7 ]) + + BMP_CFLAGS="$BMP_CFLAGS -I/usr/include/bmp" + BMP_OUTPUTPLUGINDIR=`pkg-config bmp --variable=output_plugin_dir` + +else + echo "*** BMP disabled ***" + BMP_CFLAGS= + BMP_LIBS= + BMP_OUTPUTPLUGINDIR= +fi + +AC_SUBST(BMP_CFLAGS) +AC_SUBST(BMP_LIBS) +AC_SUBST(BMP_OUTPUTPLUGINDIR) + +AM_CONDITIONAL(BUILD_BMP, test "x$bmp" = xtrue) + +# end of bmp # + + +# If using GCC specifiy some additional parameters +if test "x$GCC" = "xyes" ; then + CFLAGS="$CFLAGS -pipe -Wall -W -Wno-unused-parameter" +fi + +# LYNX documentation generation +AC_ARG_ENABLE(lynx, + AS_HELP_STRING(--disable-lynx,Turn off lynx usage for documentation generation), +[case "${enableval}" in + yes) lynx=yes ;; + no) lynx=no ;; + *) AC_MSG_ERROR(bad value ${enableval} for --disable-lynx) ;; +esac],[lynx=yes]) + +if test x$lynx = xyes ; then + AC_CHECK_PROG(have_lynx, lynx, yes, no) + + if test x$have_lynx = xno ; then + AC_MSG_ERROR([*** Sorry, you have to install lynx or use --disable-lynx ***]) + fi +fi + +AM_CONDITIONAL([USE_LYNX], [test "x$lynx" = xyes]) + +AC_CONFIG_FILES([Makefile src/Makefile doc/Makefile doc/README.html]) +AC_OUTPUT diff --git a/trunk/doc/Makefile.am b/trunk/doc/Makefile.am new file mode 100644 index 0000000..8d427eb --- /dev/null +++ b/trunk/doc/Makefile.am @@ -0,0 +1,36 @@ +# $Id$ + +# This file is part of xmms-pulse. +# +# xmms-pulse 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. +# +# xmms-pulse is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY 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 xmms-pulse; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +noinst_DATA = README.html README +EXTRA_DIST = $(noinst_DATA) style.css README.html.in + +MAINTAINERCLEANFILES = README README.html +CLEANFILES = + +if USE_LYNX +README: README.html + lynx --dump $^ | sed 's,file://localhost/.*/doc/README.html,README,' > $@ + +CLEANFILES += README +endif + +tidy: README.html + tidy -e < README.html + +.PHONY: tidy + diff --git a/trunk/doc/README.html.in b/trunk/doc/README.html.in new file mode 100644 index 0000000..88593b9 --- /dev/null +++ b/trunk/doc/README.html.in @@ -0,0 +1,130 @@ + + + + + +xmms-pulse @PACKAGE_VERSION@ + + + + +

xmms-pulse @PACKAGE_VERSION@

+ +

Copyright 2004-2006 Lennart Poettering <@PACKAGE_BUGREPORT@>

+ + + +

License

+ +

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., 675 Mass Ave, Cambridge, MA 02139, USA.

+ +

News

+ +
Fri Jul 21 2006:

Version 0.9.3 released; fix a memory leak

+ +
Sat Jul 8 2006:

Version 0.9.2 released; updated for PulseAudio 0.9.2

+ +
Sat May 27 2006:

Version 0.9.0 released; upgrade to Polypaudio 0.9.0; fix handling of non-ASCII charsets

+ +
Sun Nov 21 2004:

Version 0.5 released; upgrade to Polypaudio 0.7

+ +
Thu Oct 28 2004:

Version 0.4 released; upgrade to Polypaudio 0.6

+ +
Wed Sep 20 2004:

Version 0.3 released; upgrade to Polypaudio 0.5

+ +
Wed Sep 8 2004:

Version 0.2 released; upgrade to Polypaudio 0.4

+ +
Fri Aug 27 2004:

Version 0.1 released

+ +

Overview

+ +

xmms-pulse is an XMMS output plugin for the PulseAudio +sound server.

+ +

Current Status

+ +

There's currently no configuration dialog. Use $PULSE_SERVER and $PULSE_SINK to change the default output sinks.

+ +

Documentation

+ +

There is not much to say. Just install this software, the driver will then be available in XMMS.

+ +

Requirements

+ +

Currently, xmms-pulse is tested on Linux only.

+ +

xmms-pulse was developed and tested on Debian GNU/Linux +"testing" from July 2004, it should work on most other Linux +distributions (and maybe Unix versions) since it uses GNU autoconf for +source code configuration.

+ +

Obviously xmms-pulse requires an installation of +PulseAudio (version 0.9.x) and xmms (version 1.2.10 +works, earlier versions probably as well).

+ +

Installation

+ +

As this package is made with the GNU autotools you should run +./configure inside the distribution directory for configuring +the source tree. After that you should run make for +compilation and make install (as root) for installation of +xmms-pulse.

+ +

Make sure to have XMMS's development package xmms-dev +installed and the script xmms-config in your $PATH +before configuring the source code.

+ +

Acknowledgements

+ +

Mario Izquierdo for Beep Media Player support.

+ +

Download

+ +

The newest release is always available from @PACKAGE_URL@

+ +

The current release is @PACKAGE_VERSION@

+ +

Get xmms-pulse's development sources from the Subversion repository (viewcvs):

+ +
svn checkout svn://0pointer.de/xmms-pulse/trunk xmms-pulse
+ +

If you want to be notified whenever I release a new version of this software use the subscription feature of Freshmeat.

+ +
+
Lennart Poettering <@PACKAGE_BUGREPORT@>, July 2006
+
$Id$
+ + + diff --git a/trunk/doc/style.css b/trunk/doc/style.css new file mode 100644 index 0000000..2ca9940 --- /dev/null +++ b/trunk/doc/style.css @@ -0,0 +1,27 @@ +/* $Id$ */ + +/*** + * This file is part of xmms-pulse. + * + * xmms-pulse 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. + * + * xmms-pulse is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY 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 xmms-pulse; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + ***/ + +body { color: black; background-color: white; } +a:link, a:visited { color: #900000; } +div.news-date { font-size: 80%; font-style: italic; } +pre { background-color: #f0f0f0; padding: 0.4cm; } +.grey { color: #8f8f8f; font-size: 80%; } +table { margin-left: 1cm; border:1px solid lightgrey; padding: 0.2cm; } +td { padding-left:10px; padding-right:10px; } diff --git a/trunk/src/Makefile.am b/trunk/src/Makefile.am new file mode 100644 index 0000000..6622878 --- /dev/null +++ b/trunk/src/Makefile.am @@ -0,0 +1,45 @@ +# $Id$ +# +# This file is part of xmms-pulse. +# +# xmms-pulse 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. +# +# xmms-pulse is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY 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 xmms-pulse; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +# USA. + +AM_CFLAGS=$(PTHREAD_CFLAGS) +AM_LIBADD=$(PTHREAD_LIBS) + + + +if BUILD_XMMS +xmmsplugindir=$(XMMS_OUTPUTPLUGINDIR) +xmmsplugin_LTLIBRARIES=libxmms-pulse.la +libxmms_pulse_la_SOURCES=plugin.c +libxmms_pulse_la_LDFLAGS=-module -avoid-version +libxmms_pulse_la_LIBADD=$(AM_LIBADD) $(PULSE_LIBS) $(XMMS_LIBS) +libxmms_pulse_la_CFLAGS=$(AM_CFLAGS) $(PULSE_CFLAGS) $(XMMS_CFLAGS) +XMMS_CFLAGS+= -DHAVE_XMMS=1 +endif + + +if BUILD_BMP +bmpplugindir=$(BMP_OUTPUTPLUGINDIR) +bmpplugin_LTLIBRARIES=libbmp-pulse.la +libbmp_pulse_la_SOURCES=plugin.c +libbmp_pulse_la_LDFLAGS=-module -avoid-version +libbmp_pulse_la_LIBADD=$(AM_LIBADD) $(PULSE_LIBS) $(BMP_LIBS) +libbmp_pulse_la_CFLAGS=$(AM_CFLAGS) $(PULSE_CFLAGS) $(BMP_CFLAGS) +BMP_CFLAGS+= -DHAVE_BMP=1 +endif + diff --git a/trunk/src/plugin.c b/trunk/src/plugin.c new file mode 100644 index 0000000..c54163a --- /dev/null +++ b/trunk/src/plugin.c @@ -0,0 +1,801 @@ +/* $Id$ */ + +/*** + This file is part of xmms-pulse. + + xmms-pulse 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. + + xmms-pulse is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY 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 xmms-pulse; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef HAVE_XMMS + #include + #include +#endif + +#ifdef HAVE_BMP + #include + #include +#endif + +#include + +#include + +static pa_context *context = NULL; +static pa_stream *stream = NULL; +static pa_threaded_mainloop *mainloop = NULL; + +static pa_cvolume volume; +static int volume_valid = 0; + +static int do_trigger = 0; +static uint64_t written = 0; +static int time_offset_msec = 0; +static int just_flushed = 0; + +static int connected = 0; + +static pa_time_event *volume_time_event = NULL; + +#define CHECK_DEAD_GOTO(label, warn) do { \ +if (!mainloop || \ + !context || pa_context_get_state(context) != PA_CONTEXT_READY || \ + !stream || pa_stream_get_state(stream) != PA_STREAM_READY) { \ + if (warn) \ + g_warning("Connection died: %s", context ? pa_strerror(pa_context_errno(context)) : "NULL"); \ + goto label; \ + } \ +} while(0); + +#define CHECK_CONNECTED(retval) \ +do { \ + if (!connected) return retval; \ +} while (0); + +/* This function is from xmms' core */ +gint ctrlsocket_get_session_id(void); + +static const char* get_song_name(void) { + static char t[256]; + gint session, pos; + char *str, *u; + + session = ctrlsocket_get_session_id(); + pos = xmms_remote_get_playlist_pos(session); + if (!(str = xmms_remote_get_playlist_title(session, pos))) + return "Playback Stream"; + + snprintf(t, sizeof(t), "%s", u = pa_locale_to_utf8(str)); + pa_xfree(u); + + return t; +} + +static void info_cb(struct pa_context *c, const struct pa_sink_input_info *i, int is_last, void *userdata) { + assert(c); + + if (!i) + return; + + volume = i->volume; + volume_valid = 1; +} + +static void subscribe_cb(struct pa_context *c, enum pa_subscription_event_type t, uint32_t index, void *userdata) { + pa_operation *o; + + assert(c); + + if (!stream || + index != pa_stream_get_index(stream) || + (t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE) && + t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW))) + return; + + if (!(o = pa_context_get_sink_input_info(c, index, info_cb, NULL))) { + g_warning("pa_context_get_sink_input_info() failed: %s", pa_strerror(pa_context_errno(c))); + return; + } + + pa_operation_unref(o); +} + +static void context_state_cb(pa_context *c, void *userdata) { + assert(c); + + switch (pa_context_get_state(c)) { + case PA_CONTEXT_READY: + case PA_CONTEXT_TERMINATED: + case PA_CONTEXT_FAILED: + pa_threaded_mainloop_signal(mainloop, 0); + break; + + case PA_CONTEXT_UNCONNECTED: + case PA_CONTEXT_CONNECTING: + case PA_CONTEXT_AUTHORIZING: + case PA_CONTEXT_SETTING_NAME: + break; + } +} + +static void stream_state_cb(pa_stream *s, void * userdata) { + assert(s); + + switch (pa_stream_get_state(s)) { + + case PA_STREAM_READY: + case PA_STREAM_FAILED: + case PA_STREAM_TERMINATED: + pa_threaded_mainloop_signal(mainloop, 0); + break; + + case PA_STREAM_UNCONNECTED: + case PA_STREAM_CREATING: + break; + } +} + +static void stream_success_cb(pa_stream *s, int success, void *userdata) { + assert(s); + + if (userdata) + *(int*) userdata = success; + + pa_threaded_mainloop_signal(mainloop, 0); +} + +static void context_success_cb(pa_context *c, int success, void *userdata) { + assert(c); + + if (userdata) + *(int*) userdata = success; + + pa_threaded_mainloop_signal(mainloop, 0); +} + +static void stream_request_cb(pa_stream *s, size_t length, void *userdata) { + assert(s); + + pa_threaded_mainloop_signal(mainloop, 0); +} + +static void stream_latency_update_cb(pa_stream *s, void *userdata) { + assert(s); + + pa_threaded_mainloop_signal(mainloop, 0); +} + +static void pulse_get_volume(int *l, int *r) { + pa_cvolume v; + int b = 0; + +/* g_message("get_volume"); */ + + *l = *r = 100; + + if (connected) { + pa_threaded_mainloop_lock(mainloop); + CHECK_DEAD_GOTO(fail, 1); + + v = volume; + b = volume_valid; + + fail: + pa_threaded_mainloop_unlock(mainloop); + } else { + v = volume; + b = volume_valid; + } + + if (b) { + if (v.channels == 2) { + *l = (int) ((v.values[0]*100)/PA_VOLUME_NORM); + *r = (int) ((v.values[1]*100)/PA_VOLUME_NORM); + } else + *l = *r = (int) ((pa_cvolume_avg(&v)*100)/PA_VOLUME_NORM); + } +} + +static void volume_time_cb(pa_mainloop_api *api, pa_time_event *e, const struct timeval *tv, void *userdata) { + pa_operation *o; + + if (!(o = pa_context_set_sink_input_volume(context, pa_stream_get_index(stream), &volume, NULL, NULL))) + g_warning("pa_context_set_sink_input_volume() failed: %s", pa_strerror(pa_context_errno(context))); + else + pa_operation_unref(o); + + /* We don't wait for completion of this command */ + + api->time_free(volume_time_event); + volume_time_event = NULL; +} + +static void pulse_set_volume(int l, int r) { + +/* g_message("set_volume"); */ + + if (connected) { + pa_threaded_mainloop_lock(mainloop); + CHECK_DEAD_GOTO(fail, 1); + } + + if (!volume_valid || volume.channels != 1) { + volume.values[0] = ((pa_volume_t) l * PA_VOLUME_NORM)/100; + volume.values[1] = ((pa_volume_t) r * PA_VOLUME_NORM)/100; + volume.channels = 2; + } else { + volume.values[0] = ((pa_volume_t) l * PA_VOLUME_NORM)/100; + volume.channels = 1; + } + + volume_valid = 1; + + if (connected && !volume_time_event) { + struct timeval tv; + pa_mainloop_api *api = pa_threaded_mainloop_get_api(mainloop); + volume_time_event = api->time_new(api, pa_timeval_add(pa_gettimeofday(&tv), 100000), volume_time_cb, NULL); + } + +fail: + if (connected) + pa_threaded_mainloop_unlock(mainloop); +} + +static void pulse_pause(short b) { + pa_operation *o = NULL; + int success = 0; + +/* g_message("pause"); */ + + CHECK_CONNECTED(); + + pa_threaded_mainloop_lock(mainloop); + CHECK_DEAD_GOTO(fail, 1); + + if (!(o = pa_stream_cork(stream, b, stream_success_cb, &success))) { + g_warning("pa_stream_cork() failed: %s", pa_strerror(pa_context_errno(context))); + goto fail; + } + + while (pa_operation_get_state(o) != PA_OPERATION_DONE) { + CHECK_DEAD_GOTO(fail, 1); + pa_threaded_mainloop_wait(mainloop); + } + + if (!success) + g_warning("pa_stream_cork() failed: %s", pa_strerror(pa_context_errno(context))); + +fail: + + if (o) + pa_operation_unref(o); + + pa_threaded_mainloop_unlock(mainloop); +} + +static int pulse_free(void) { + size_t l = 0; + pa_operation *o = NULL; + +/* g_message("free"); */ + + CHECK_CONNECTED(0); + + pa_threaded_mainloop_lock(mainloop); + CHECK_DEAD_GOTO(fail, 1); + + if ((l = pa_stream_writable_size(stream)) == (size_t) -1) { + g_warning("pa_stream_writable_size() failed: %s", pa_strerror(pa_context_errno(context))); + l = 0; + goto fail; + } + + /* If this function is called twice with no pulse_write() call in + * between this means we should trigger the playback */ + if (do_trigger) { + int success = 0; + + if (!(o = pa_stream_trigger(stream, stream_success_cb, &success))) { + g_warning("pa_stream_trigger() failed: %s", pa_strerror(pa_context_errno(context))); + goto fail; + } + + while (pa_operation_get_state(o) != PA_OPERATION_DONE) { + CHECK_DEAD_GOTO(fail, 1); + pa_threaded_mainloop_wait(mainloop); + } + + if (!success) + g_warning("pa_stream_trigger() failed: %s", pa_strerror(pa_context_errno(context))); + } + +fail: + if (o) + pa_operation_unref(o); + + pa_threaded_mainloop_unlock(mainloop); + + do_trigger = !!l; + return (int) l; +} + +static int pulse_get_written_time(void) { + int r = 0; + +/* g_message("get_written_time"); */ + + CHECK_CONNECTED(0); + + pa_threaded_mainloop_lock(mainloop); + CHECK_DEAD_GOTO(fail, 1); + + r = (int) (((double) written*1000) / pa_bytes_per_second(pa_stream_get_sample_spec(stream))); + +/* g_message("written_time = %i", r); */ + +fail: + pa_threaded_mainloop_unlock(mainloop); + + return r; +} + +static int pulse_get_output_time(void) { + int r = 0; + pa_usec_t t; + +/* g_message("get_output_time"); */ + + CHECK_CONNECTED(0); + + pa_threaded_mainloop_lock(mainloop); + + for (;;) { + CHECK_DEAD_GOTO(fail, 1); + + if (pa_stream_get_time(stream, &t) >= 0) + break; + + if (pa_context_errno(context) != PA_ERR_NODATA) { + g_warning("pa_stream_get_time() failed: %s", pa_strerror(pa_context_errno(context))); + goto fail; + } + + pa_threaded_mainloop_wait(mainloop); + } + + r = (int) (t / 1000); + + if (just_flushed) { + time_offset_msec -= r; + just_flushed = 0; + } + + r += time_offset_msec; + +/* g_message("output_time = %i", r); */ + +fail: + pa_threaded_mainloop_unlock(mainloop); + + return r; +} + +static int pulse_playing(void) { + int r = 0; + const pa_timing_info *i; + + CHECK_CONNECTED(0); + +/* g_message("playing"); */ + + pa_threaded_mainloop_lock(mainloop); + + for (;;) { + CHECK_DEAD_GOTO(fail, 1); + + if ((i = pa_stream_get_timing_info(stream))) + break; + + if (pa_context_errno(context) != PA_ERR_NODATA) { + g_warning("pa_stream_get_timing_info() failed: %s", pa_strerror(pa_context_errno(context))); + goto fail; + } + + pa_threaded_mainloop_wait(mainloop); + } + + r = i->playing; + +fail: + pa_threaded_mainloop_unlock(mainloop); + + return r; +} + +static void pulse_flush(int time) { + pa_operation *o = NULL; + int success = 0; + +/* g_message("flush"); */ + + CHECK_CONNECTED(); + + pa_threaded_mainloop_lock(mainloop); + CHECK_DEAD_GOTO(fail, 1); + + if (!(o = pa_stream_flush(stream, stream_success_cb, &success))) { + g_warning("pa_stream_flush() failed: %s", pa_strerror(pa_context_errno(context))); + goto fail; + } + + while (pa_operation_get_state(o) != PA_OPERATION_DONE) { + CHECK_DEAD_GOTO(fail, 1); + pa_threaded_mainloop_wait(mainloop); + } + + if (!success) + g_warning("pa_stream_flush() failed: %s", pa_strerror(pa_context_errno(context))); + + written = (uint64_t) (((double) time * pa_bytes_per_second(pa_stream_get_sample_spec(stream))) / 1000); + just_flushed = 1; + time_offset_msec = time; + +fail: + if (o) + pa_operation_unref(o); + + pa_threaded_mainloop_unlock(mainloop); +} + +static void pulse_write(void* ptr, int length) { + +/* g_message("write"); */ + + CHECK_CONNECTED(); + + pa_threaded_mainloop_lock(mainloop); + CHECK_DEAD_GOTO(fail, 1); + + if (pa_stream_write(stream, ptr, length, NULL, PA_SEEK_RELATIVE, 0) < 0) { + g_warning("pa_stream_write() failed: %s", pa_strerror(pa_context_errno(context))); + goto fail; + } + + do_trigger = 0; + written += length; + +fail: + + pa_threaded_mainloop_unlock(mainloop); +} + +static void drain(void) { + pa_operation *o = NULL; + int success = 0; + + CHECK_CONNECTED(); + + pa_threaded_mainloop_lock(mainloop); + CHECK_DEAD_GOTO(fail, 0); + + if (!(o = pa_stream_drain(stream, stream_success_cb, &success))) { + g_warning("pa_stream_drain() failed: %s", pa_strerror(pa_context_errno(context))); + goto fail; + } + + while (pa_operation_get_state(o) != PA_OPERATION_DONE) { + CHECK_DEAD_GOTO(fail, 1); + pa_threaded_mainloop_wait(mainloop); + } + + if (!success) + g_warning("pa_stream_drain() failed: %s", pa_strerror(pa_context_errno(context))); + +fail: + if (o) + pa_operation_unref(o); + + pa_threaded_mainloop_unlock(mainloop); +} + +static void pulse_close(void) { + +/* g_message("close"); */ + + drain(); + + connected = 0; + + if (mainloop) + pa_threaded_mainloop_stop(mainloop); + + if (stream) { + pa_stream_disconnect(stream); + pa_stream_unref(stream); + stream = NULL; + } + + if (context) { + pa_context_disconnect(context); + pa_context_unref(context); + context = NULL; + } + + if (mainloop) { + pa_threaded_mainloop_free(mainloop); + mainloop = NULL; + } + + volume_time_event = NULL; +} + +static int pulse_open(AFormat fmt, int rate, int nch) { + pa_sample_spec ss; + pa_operation *o = NULL; + int success; + +/* g_message("open"); */ + + g_assert(!mainloop); + g_assert(!context); + g_assert(!stream); + g_assert(!connected); + + if (fmt == FMT_U8) + ss.format = PA_SAMPLE_U8; + else if (fmt == FMT_S16_LE) + ss.format = PA_SAMPLE_S16LE; + else if (fmt == FMT_S16_BE) + ss.format = PA_SAMPLE_S16BE; + else if (fmt == FMT_S16_NE) + ss.format = PA_SAMPLE_S16NE; + else + return FALSE; + + ss.rate = rate; + ss.channels = nch; + + if (!pa_sample_spec_valid(&ss)) + return FALSE; + + if (!volume_valid) { + pa_cvolume_reset(&volume, ss.channels); + volume_valid = 1; + } else if (volume.channels != ss.channels) + pa_cvolume_set(&volume, ss.channels, pa_cvolume_avg(&volume)); + + if (!(mainloop = pa_threaded_mainloop_new())) { + g_warning("Failed to allocate main loop"); + goto fail; + } + + pa_threaded_mainloop_lock(mainloop); + +#ifdef HAVE_XMMS + if (!(context = pa_context_new(pa_threaded_mainloop_get_api(mainloop), "XMMS"))) { +#endif + +#if HAVE_BMP + if (!(context = pa_context_new(pa_threaded_mainloop_get_api(mainloop), "Beep-Media-Player"))) { +#endif + g_warning("Failed to allocate context"); + goto unlock_and_fail; + } + + pa_context_set_state_callback(context, context_state_cb, NULL); + pa_context_set_subscribe_callback(context, subscribe_cb, NULL); + + if (pa_context_connect(context, NULL, 0, NULL) < 0) { + g_warning("Failed to connect to server: %s", pa_strerror(pa_context_errno(context))); + goto unlock_and_fail; + } + + if (pa_threaded_mainloop_start(mainloop) < 0) { + g_warning("Failed to start main loop"); + goto unlock_and_fail; + } + + /* Wait until the context is ready */ + pa_threaded_mainloop_wait(mainloop); + + if (pa_context_get_state(context) != PA_CONTEXT_READY) { + g_warning("Failed to connect to server: %s", pa_strerror(pa_context_errno(context))); + goto unlock_and_fail; + } + + if (!(stream = pa_stream_new(context, get_song_name(), &ss, NULL))) { + g_warning("Failed to create stream: %s", pa_strerror(pa_context_errno(context))); + goto unlock_and_fail; + } + + pa_stream_set_state_callback(stream, stream_state_cb, NULL); + pa_stream_set_write_callback(stream, stream_request_cb, NULL); + pa_stream_set_latency_update_callback(stream, stream_latency_update_cb, NULL); + + if (pa_stream_connect_playback(stream, NULL, NULL, PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE, &volume, NULL) < 0) { + g_warning("Failed to connect stream: %s", pa_strerror(pa_context_errno(context))); + goto unlock_and_fail; + } + + /* Wait until the stream is ready */ + pa_threaded_mainloop_wait(mainloop); + + if (pa_stream_get_state(stream) != PA_STREAM_READY) { + g_warning("Failed to connect stream: %s", pa_strerror(pa_context_errno(context))); + goto unlock_and_fail; + } + + /* Now subscribe to events */ + if (!(o = pa_context_subscribe(context, PA_SUBSCRIPTION_MASK_SINK_INPUT, context_success_cb, &success))) { + g_warning("pa_context_subscribe() failed: %s", pa_strerror(pa_context_errno(context))); + goto unlock_and_fail; + } + + success = 0; + while (pa_operation_get_state(o) != PA_OPERATION_DONE) { + CHECK_DEAD_GOTO(fail, 1); + pa_threaded_mainloop_wait(mainloop); + } + + if (!success) { + g_warning("pa_context_subscribe() failed: %s", pa_strerror(pa_context_errno(context))); + goto unlock_and_fail; + } + + pa_operation_unref(o); + + /* Now request the initial stream info */ + if (!(o = pa_context_get_sink_input_info(context, pa_stream_get_index(stream), info_cb, NULL))) { + g_warning("pa_context_get_sink_input_info() failed: %s", pa_strerror(pa_context_errno(context))); + goto unlock_and_fail; + } + + while (pa_operation_get_state(o) != PA_OPERATION_DONE) { + CHECK_DEAD_GOTO(fail, 1); + pa_threaded_mainloop_wait(mainloop); + } + + if (!volume_valid) { + g_warning("pa_context_get_sink_input_info() failed: %s", pa_strerror(pa_context_errno(context))); + goto unlock_and_fail; + } + + do_trigger = 0; + written = 0; + time_offset_msec = 0; + just_flushed = 0; + connected = 1; + volume_time_event = NULL; + + pa_threaded_mainloop_unlock(mainloop); + + return TRUE; + +unlock_and_fail: + + if (o) + pa_operation_unref(o); + + pa_threaded_mainloop_unlock(mainloop); + +fail: + + pulse_close(); + + return FALSE; +} + +static void pulse_init(void) { +} + +static void pulse_about(void) { + static GtkWidget *dialog; + + if (dialog != NULL) + return; + +#ifdef HAVE_XMMS + dialog = xmms_show_message( + "About XMMS PulseAudio Output Plugin", + "XMMS PulseAudio Output Plugin\n\n " + "This program is free software; you can redistribute it and/or modify\n" + "it under the terms of the GNU General Public License as published by\n" + "the Free Software Foundation; either version 2 of the License, or\n" + "(at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,\n" + "USA.", + "OK", + FALSE, + NULL, + NULL); +#endif + +#ifdef HAVE_BMP + dialog = xmms_show_message( + "About Beep_media-Player PulseAudio Output Plugin", + "Beep-Media-Player PulseAudio Output Plugin\n\n " + "This program is free software; you can redistribute it and/or modify\n" + "it under the terms of the GNU General Public License as published by\n" + "the Free Software Foundation; either version 2 of the License, or\n" + "(at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,\n" + "USA.", + "OK", + FALSE, + NULL, + NULL); +#endif + + gtk_signal_connect( + GTK_OBJECT(dialog), + "destroy", + GTK_SIGNAL_FUNC(gtk_widget_destroyed), + &dialog); +} + + +OutputPlugin *get_oplugin_info(void) { + static OutputPlugin pulse_plugin = { + NULL, + NULL, + "PulseAudio Output Plugin", + pulse_init, + pulse_about, + NULL, /* pulse_configure, */ + pulse_get_volume, + pulse_set_volume, + pulse_open, + pulse_write, + pulse_close, + pulse_flush, + pulse_pause, + pulse_free, + pulse_playing, + pulse_get_output_time, + pulse_get_written_time, + }; + + return &pulse_plugin; +} -- cgit