summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRainer Blickle <rblickle@gmx.de>2010-08-13 21:05:19 +0200
committerroot <root@server.(none)>2010-08-13 21:05:19 +0200
commit201670d0adaf587758bbbad0b03f4e5431a70e61 (patch)
tree61683358b6cbb6f3f03f9fc9ea8540d2c9742ed7
downloadvdr-plugin-sndctl-201670d0adaf587758bbbad0b03f4e5431a70e61.tar.gz
vdr-plugin-sndctl-201670d0adaf587758bbbad0b03f4e5431a70e61.tar.bz2
Initial import
-rw-r--r--COPYING340
-rw-r--r--HISTORY33
-rw-r--r--INSTALL33
-rw-r--r--INSTALL.de33
-rw-r--r--Makefile71
-rw-r--r--README213
-rw-r--r--README.de223
-rw-r--r--TODO19
-rw-r--r--alsa.c201
-rw-r--r--alsa.h43
-rw-r--r--defaults.h72
-rw-r--r--i18n.c416
-rw-r--r--i18n.h34
-rw-r--r--mainmenu.c132
-rw-r--r--mainmenu.h33
-rw-r--r--menuitems.c165
-rw-r--r--menuitems.h73
-rw-r--r--mixer.c176
-rw-r--r--mixer.h66
-rw-r--r--setup.c135
-rw-r--r--setup.h36
-rw-r--r--setupmenu.c105
-rw-r--r--setupmenu.h40
-rw-r--r--sndctl.c283
-rw-r--r--sndctl.cbp81
-rw-r--r--sndctl.h58
-rw-r--r--sndctl.layout43
-rw-r--r--soundman.c334
-rw-r--r--soundman.h54
-rw-r--r--soundset.c207
-rw-r--r--soundset.h48
-rw-r--r--soundsetmenu.c71
-rw-r--r--soundsetmenu.h34
-rw-r--r--status.c47
-rw-r--r--status.h34
35 files changed, 3986 insertions, 0 deletions
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..f90922e
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser 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.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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 Street, Fifth Floor, Boston, MA 02110-1301 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.
+
+ <signature of Ty Coon>, 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 Lesser General
+Public License instead of this License.
diff --git a/HISTORY b/HISTORY
new file mode 100644
index 0000000..072f653
--- /dev/null
+++ b/HISTORY
@@ -0,0 +1,33 @@
+This is a "plugin" for the Video Disk Recorder (VDR).
+
+Written by: Thomas Hildebrandt <toxym@web.de>
+Project's homepage: none
+Latest version available at: http://www.box.net/shared/qhu44kgcv4
+
+See the file COPYING for license information.
+
+---------------------------------------------------------------------------
+Sound control plugin - 'sndctl'
+---------------------------------------------------------------------------
+
+ HISTORY
+===============================
+
+2007-05-16: version 0.1.3
+- bug in sound set editing fixed (crash when editing twice)
+
+2007-04-22: version 0.1.2
+- Sound flash implemented
+- bugfixing (in sound set editing)
+
+2007-04-20: version 0.1.1
+- bugfixing in SVDR handling
+
+2007-04-19: version 0.1.0
+- creating, editing and deleting of sound sets from VDR menu implemented
+- Dolby digital auto switch implemented
+- SVDR commands implemented
+- initial volume implemented
+
+2007-03-14: version 0.0.1
+- Initial revision
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..1f82bd1
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,33 @@
+This is a "plugin" for the Video Disk Recorder (VDR).
+
+Written by: Thomas Hildebrandt <toxym@web.de>
+Further development: Rainer Blicke <rblickle@gmx.de>
+Project's homepage: none
+Latest version available at: http://www.box.net/shared/qhu44kgcv4
+
+See the file COPYING for license information.
+
+---------------------------------------------------------------------------
+Sound control plugin - 'sndctl'
+---------------------------------------------------------------------------
+
+ Installation
+-------------------------------
+Handle like any other plugin for VDR. (All following paths may be different
+for your system.)
+
+installation example:
+
+* install the alsa header files
+ for debian use: apt-get install libasound2-dev
+* untar to the VDR plugins directory
+ example: cd /usr/src/vdr/VDR/PLUGINS/src
+ tar -xzf /path/to/vdr-sndctl-0.1.2.tgz
+* make a symlink
+ example: ln -sf sndctl-0.1.2 /usr/src/vdr/VDR/PLUGINS/src/sndctl
+* do a 'make' from the VDR directory
+ example: cd /usr/src/vdr/VDR
+ make plugins
+* copy the library to the appropriate directory (or let it 'make' do)
+ example: make install-plugins
+* be sure, your VDR is called with argument '-P sndctl'
diff --git a/INSTALL.de b/INSTALL.de
new file mode 100644
index 0000000..7efe259
--- /dev/null
+++ b/INSTALL.de
@@ -0,0 +1,33 @@
+This is a "plugin" for the Video Disk Recorder (VDR).
+
+Written by: Thomas Hildebrandt <toxym@web.de>
+Weiterentwicklung: Rainer Blicke <rblickle@gmx.de>
+Project's homepage: none
+Latest version available at: http://www.box.net/shared/qhu44kgcv4
+
+See the file COPYING for license information.
+
+---------------------------------------------------------------------------
+Sound control plugin - 'sndctl'
+---------------------------------------------------------------------------
+
+ Installation
+-------------------------------
+Die Installation erfolgt auf die gleiche Weise wie bei jedem anderen Plugin.
+(Alle folgenden Pfade koennen auf deinem System anders lauten.)
+
+Beispielinstallation:
+
+* installieren der alsa-header-files
+ für debian: apt-get install libasound2-dev
+* auspacken in VDR Plugin-Verzeichnis
+ Beispiel: cd /usr/src/vdr/VDR/PLUGINS/src
+ tar -xzf /path/to/vdr-sndctl-0.1.2tgz
+* symbolischen Link erstellen
+ Beispiel: ln -sf sndctl-0.1.2 /usr/src/vdr/VDR/PLUGINS/src/sndctl
+* 'make' aus dem VDR Verzeichnis ausfuehren
+ Beispiel: cd /usr/src/vdr/VDR
+ make plugins
+* Bibliothek an die richtige Stelle kopieren (oder von 'make' tun lassen)
+ Beispiel: make install-plugins
+* stelle sicher, dass dein VDR mit dem Argument '-P sndctl' aufgrufen wird
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..5b00089
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,71 @@
+#
+# Makefile for Video Disk Recorder plugin 'sndctl'
+#
+# Thomas Hildebrandt <toxym@web.de>
+
+# The official name of this plugin.
+PLUGIN = sndctl
+
+### The version number of this plugin (taken from the main source file):
+VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ print $$6 }' | sed -e 's/[";]//g')
+
+### The C++ compiler and options:
+CXX ?= g++
+CXXFLAGS ?= -fPIC -g -O2 -Wall -Woverloaded-virtual
+#CXXFLAGS ?= -O2 -Wall -Woverloaded-virtual
+
+### The directory environment:
+VDRDIR = ../../..
+LIBDIR = ../../lib
+TMPDIR = /tmp
+
+### Allow user defined options to overwrite defaults:
+-include $(VDRDIR)/Make.config
+
+### The version number of VDR's plugin API (taken from VDR's "config.h"):
+APIVERSION = $(shell sed -ne '/define APIVERSION/s/^.*"\(.*\)".*$$/\1/p' $(VDRDIR)/config.h)
+
+### The name of the distribution archive:
+ARCHIVE = $(PLUGIN)-$(VERSION)
+PACKAGE = vdr-$(ARCHIVE)
+
+### Includes and Defines (add further entries here):
+INCLUDES += -I$(VDRDIR)/include
+DEFINES += -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
+
+### The object files (add further files here):
+OBJS = $(PLUGIN).o alsa.o i18n.o mainmenu.o menuitems.o mixer.o setup.o\
+ setupmenu.o soundman.o soundset.o soundsetmenu.o status.o
+
+### Implicit rules:
+
+%.o: %.c
+ $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $<
+
+# Dependencies:
+
+MAKEDEP = $(CXX) -MM -MG
+DEPFILE = .dependencies
+$(DEPFILE): Makefile
+ @$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@
+
+-include $(DEPFILE)
+
+### Targets:
+
+all: libvdr-$(PLUGIN).so
+
+libvdr-$(PLUGIN).so: $(OBJS)
+ $(CXX) $(CXXFLAGS) -shared $(OBJS) -lasound -o $@
+ @cp --remove-destination $@ $(LIBDIR)/$@.$(APIVERSION)
+
+dist: clean
+ @-rm -rf $(TMPDIR)/$(ARCHIVE)
+ @mkdir $(TMPDIR)/$(ARCHIVE)
+ @cp -a * $(TMPDIR)/$(ARCHIVE)
+ @tar czf $(PACKAGE).tgz -C $(TMPDIR) $(ARCHIVE)
+ @-rm -rf $(TMPDIR)/$(ARCHIVE)
+ @echo Distribution package created as $(PACKAGE).tgz
+
+clean:
+ @-rm -f $(OBJS) $(DEPFILE) *.so *.tgz core* *~
diff --git a/README b/README
new file mode 100644
index 0000000..eb9199f
--- /dev/null
+++ b/README
@@ -0,0 +1,213 @@
+This is a "plugin" for the Video Disk Recorder (VDR).
+
+Written by: Thomas Hildebrandt <toxym@web.de>
+Further development: Rainer Blicke <rblickle@gmx.de>
+Project's homepage: none
+Latest version available at: http://www.box.net/shared/qhu44kgcv4
+
+See the file COPYING for license information.
+
+---------------------------------------------------------------------------
+Sound control plugin - 'sndctl'
+---------------------------------------------------------------------------
+
+ MOTIVATION
+===============================
+This plugin was inspired and reengineered from the AVolCtl-Plugin by Martin
+Prochnow (thanks for it).
+It's used to control the volume levels of different controls of your sound-
+card according to the volume settings of VDR.
+
+So far so good, you can use 'AVolCtl' for this purpose, too. My personal
+needs are to have different ways, how the soundcard controls depends from
+the VDR volume settings. This ways (or profiles) are named 'sound sets'.
+
+A sound set for 'normal' stereo sound is different from a dolby digital
+sound set, means, other soundcard controls have to be moved, when changing
+the VDR volume.
+
+With 'sndctl' you can define as many sound sets as you want and switch
+between it from the VDR menu.
+
+
+ PREREQUISITES
+===============================
+VDR version 1.4.1 or higher
+ALSA 1.0.x
+
+ DEVELOPMENT ENVIRONMENT
+-------------------------------
+Linux 2.6.8.1 (LFS 6.0)
+VDR 1.5.1
+ALSA-Lib 1.0.10
+g++ (GCC) 3.4.1
+
+ COMPILATION AND RUNNING TESTED
+-------------------------------
+VDR 1.4.1
+VDR 1.4.6
+VDR 1.5.0
+VDR 1.5.1
+VDR 1.5.2
+
+
+ HANDLING
+===============================
+The switching between the several sound sets is done in the main menu entry.
+The currently activated sound set is marked. Select a sound set with 'Up' and
+'Down' and confirm with 'Ok' or 'Red' (menu disappears with this selection).
+
+A new sound set can be created with 'Blue', edited with 'Green' and deleted
+with 'Yellow'.
+
+
+ CONFIGURATION
+===============================
+Configuration is divided into two parts. General settings can be found as
+usual in the plugin configuration menu of VDR. The sound set configuration
+takes place in the main menu entry.
+
+ GENERAL SETTINGS
+-------------------------------
+"Hide main menu entry"
+ when set to 'yes', no plugin entry is shown in VDR main menu
+
+"Menu name"
+ The plugin entry in the VDR main menu gets this name
+ (I like to name it for instance 'Sound Manager'.)
+
+"Initial volume"
+ -1: VDR volume is set when starting
+ 0..100: This value is the initial volume instead of the VDR volume. The
+ first VDR volume change synchronizes the plugin volume with the
+ VDR volume.
+
+ Why this?
+ When VDR does an automatic boot up (e.g. for making some
+ recordings) and thats why (like my case) the 5.1 system goes up,
+ too, VDR of course should be muted.
+ For this, we could give VDR an initial volume of 0. But when I
+ decide to switch my VDR on to look TV, I have to give it a 'normal'
+ volume manually.
+ Solution: Give 'sndctl' an initial volume of 0. This causes VDR to start
+ muted, but one volume key hit results in initial VDR volume.
+
+"Default sound set"
+ This sound set gets activated when starting.
+ In addition, this is the sound set to switch to, when the Dolby Digital
+ auto switcher detects a non-DD stream.
+
+"Auto switch for DD"
+ When active, the plugin tries to detect the switching to a Dolby Digital
+ audio channel and activates the appropriate sound set.
+
+"Auto switch sound set",
+ see before ... This sound set is used for the Dolby Digital switching.
+
+"Enable sound flash"
+ Activates the 'sound flash' function.
+ If the volume goes up and down within a short time, the plugin changes the
+ volume to very loud but immediately back to the normal value.
+ Why this?
+ You may use some devices with an automatic standby connected to your
+ soundcard. It may decide to go into sleep when your volume is low. Now you
+ have to go up with your volume to switch on your external equipment and, of
+ course, go back, if you don't want to hear it so loud.
+ Solution: Sound flash -> Change volume up and down fast and with the short
+ volume increasing your external technic is back online. You can't
+ hear the 'sound flash' itself; until the external devices are
+ ready, the volume was changed back to normal.
+
+"Mute on end"
+ Mutes volume, when VDR quits.
+
+
+ SOUND SET SETTINGS
+-------------------------------
+"name"
+ The name of the sound set.
+
+All of the following parameters are names for the soundcard controls.
+(Well, not for all, but for 'senseful' ones.)
+Every control owns a certain value, which causes a certain behaviour.
+
+The value syntax is always the same and is STRICTLY to made as descripted.
+
+ operation
+ |
+ | minimum (the control will not go lesser than this)
+ | |
+ =80;1;90
+ | |
+ | maximum (the control will not go over this value)
+ |
+ value (VDR volume and operation results in control volume)
+
+The operation is mandatory, all other values may not be important.
+See some examples for demonstration:
+"~"
+ does nothing, the control will not be 'touched'
+ (default value for all controls in a new sound set)
+
+"=0"
+ set the control fixed to the value (here 0)
+ (on my system e.g. Bass =40 / Treble =60)
+
+"+10"
+ adds the value to VDR volume
+ (e.g. "+0" follows VDR voluem directly)
+
+"-5"
+ subtracts the value (here 5) from VDR volume
+
+"%10"
+ control value is always 10% of VDR volume
+
+"+10;1"
+ plus 10, but never less than 1
+ Why this?
+ The 'LFE' control of my soundcard (which controls the subwoofer) has only
+ 32 steps. This causes the hardware on little volume values sometimes to
+ go completely to 0, when real volume is 5% (or similar). This minimum value
+ prevents from this.
+
+
+ SVDR
+===============================
+THe following SVDR commands will be provided.
+
+SSET [ name ]
+ set or show the active sound set (by name)
+
+SSID [ id ]
+ set or show the active sound set (by ID)
+
+LIST [ names | all ]
+ without parameter shows a list of the ID's of all sound sets,
+ with 'names' a list with sound set names and with all a list
+ with both, ID and name
+
+ EXAMPLES
+-------------------------------
+svdrpsend.pl plug sndctl list
+svdrpsend.pl plug sndctl list all
+svdrpsend.pl plug sndctl sset
+svdrpsend.pl plug sndctl sset Stereo
+
+
+ HINT
+===============================
+- the plugin calculates volumes from 0 to 100
+- if volume goes to 0, no minimum value will be used, the control goes to 0
+- the following operation modes (first character) are valid:
+ '~' > do nothing (a control with this setting will be removed from the
+ setup values)
+ '=' > set this control always to the same level
+ (nice for 'Bass' and 'Treble' or control muting)
+ '+' > increase the VDR volume with this value
+ '-' > decrease the VDR volume with this value
+ '%' > set volume level to x percent of the VDR volume
+- if VDR on your system doesn't run as root, the VDR user needs permissions
+ to access the soundcard mixer device; maybe you need to put the VDR user
+ to the group 'audio' or 'video'
+
diff --git a/README.de b/README.de
new file mode 100644
index 0000000..e351242
--- /dev/null
+++ b/README.de
@@ -0,0 +1,223 @@
+This is a "plugin" for the Video Disk Recorder (VDR).
+
+Written by: Thomas Hildebrandt <toxym@web.de>
+Weiterentwicklung: Rainer Blicke <rblickle@gmx.de>
+Project's homepage: none
+Latest version available at: http://www.box.net/shared/qhu44kgcv4
+
+See the file COPYING for license information.
+
+---------------------------------------------------------------------------
+Sound control plugin - 'sndctl'
+---------------------------------------------------------------------------
+
+ MOTIVATION
+===============================
+Dieses Plugin wurde inspiriert und neuentwickelt aus dem AVolCtl-Plugin von
+Martin Prochnow (vielen Dank dafuer).
+Es steuert die Lautstaerke verschiedener Regler der Soundkarte in
+Abhaengigkeit von der Lautstaerkeregelung des VDR.
+
+So weit so gut, fuer diesen Zweck kann man 'AVolCtl' auch verwenden. Meine
+persoenlichen Beduerfnisse waren, verschiedene Wege zu haben, wie die
+Regler der Soundkarte von der VDR-Lautstaerke abhaengen. Diese Wege
+(oder Profile) nenne ich 'Soundsets'.
+
+Ein Soundset fuer 'normales' Stereo unterscheidet sich von einem Dolby
+Digital Soundset, sprich, andere Soundkarten-Regler bewegen sich, wenn
+VDR die Lautstaerke aendert.
+
+Mit 'sndctl' lassen sich beliebig viele Soundsets anlegen, zwischen denen
+aus dem VDR-Menue umgeschaltet werden kann.
+
+
+ VORAUSSETZUNGEN
+===============================
+VDR ab Version 1.4.1
+ALSA 1.0.x
+
+ ENTWICKLUNGSUMGEBUNG
+-------------------------------
+Linux 2.6.8.1 (LFS 6.0)
+VDR 1.5.1
+ALSA-Lib 1.0.10
+g++ (GCC) 3.4.1
+
+ KOMPILIERT UND GETESTET
+-------------------------------
+VDR 1.4.1
+VDR 1.4.6
+VDR 1.5.0
+VDR 1.5.1
+VDR 1.5.2
+
+
+ BEDIENUNG
+===============================
+Das Umschalten zwischen den einzelnen Soundsets erledigt man ueber den
+Eintrag im Hauptmenue. Das derzeit aktive Soundset ist gekennzeichnet.
+Mit 'Hoch' und 'Runter' waehlt man das gewuenschte Soundset, 'Ok' oder
+'Rot' aktiviert das Soundset (und beendet das Menue).
+
+Ein neues Soundset wird mit 'Blau' angelegt, ein bestehendes mit 'Gruen'
+veraendert und mit 'Gelb' geloescht.
+
+
+ KONFIGURATION
+===============================
+Die Konfiguration teilt sich in zwei Bereiche. Allgemeine Einstellungen finden
+sich wie ueblich im VDR Einstellungsmenue fuer Plugins. Die Konfiguration
+der Soundsets selbst erreicht man �ber den Hauptmenueeintrag.
+
+ ALLGEMEINE EINSTELLUNGEN
+-------------------------------
+"Eintrag im Hauptmenue verstecken"
+ Wenn auf 'ja' gesetzt, wird das Plugin im VDR-Hauptmenue nicht angezeigt.
+
+"Menue Name"
+ Unter diesem Namen taucht das Plugin im VDR-Hauptmenue auf.
+ (Ich bevorzuge es z.B., den Eintrag 'Sound Manager' zu nennen.)
+
+"Lautstaerke beim Start"
+ -1: Die VDR-Lautstaerke wird beim Start gesetzt
+ 0..100: Statt der VDR-Lautstaerke wird diese Lautstaerke gesetzt. Das erste
+ Betaetigung der VDR-Lautstaerke stellt diese dann ein
+
+ Wozu ist das gut?
+ Wenn der VDR sich allein einschaltet (z.B. wegen anstehender
+ Aufnahme[n]) und (wie bei mir) sich die 5.1-Boxen gleich
+ dazuschalten, soll natuerlich der Ton ausgeschaltet bleiben.
+ Dazu koennte man den VDR auf eine Startlautstaerke von 0 stellen.
+ Wenn man den VDR aber manuell einschaltet und fernsehen moechte,
+ muss man jetzt die Lautstaerke erst bis auf ein 'normales' Mass
+ regeln.
+ Loesung: Ist der Startwert des Plugins auf 0 gesetzt, ergibt sich der
+ Effekt eines stumm startenden VDR, der aber bei der ersten
+ Lautstaerkenbetaetigung sofort auf die VDR-Lautstaerke geht.
+
+"Standard Soundset"
+ Dieses Soundset wird beim Start des Plugins aktiviert.
+ Sollte die Dolby Digital Automatik eingeschaltet sein, ist das auch das
+ Soundset, auf das geschaltet wird, wenn ein Nicht-Dolby-Digital-Audio-Kanal
+ gewaehlt wird.
+
+"DD Automatik"
+ Wenn aktiv, versucht das Plugin zu erkennen, ob der Audio-Kanal auf
+ Dolby Digital geschaltet wird und ein entsprechendes Soundset aktivieren.
+
+"DD Automatik Soundset",
+ siehe vorher ... Dieses Soundset wird im Falle eines erkannten Dolby
+ Digital Kanals benutzt.
+
+"Soundflash aktivieren"
+ Aktiviert die 'Soundflash'-Funktion.
+ Wenn unmittelbar hintereinander die Lautstaerke hoch und wieder herunter
+ geregelt wird, setzt das Plugin die Lautstaerke kurz sehr laut und gleich
+ wieder auf den Normalwert.
+ Wozu ist das gut?
+ Eine evt. an den Ausgaengen der Soundkarte haengende Geraetschaft mag eine
+ automatische Standbyfunktion haben; sie schaltet sich bei allzu leisen
+ Lautstaerken aus. Man muss dann die Lautstaerke soweit erhoehen, dass sich
+ alles wieder einschaltet und wieder heruntersetzen, falls man es so laut gar
+ nicht wollte.
+ Loesung: Soundflash -> Lautstaerke kurz hoch und wieder runter und durch
+ die ebenso kurze Lautstaerkenerhoehung schaltet sich das Equipment
+ wieder ein. Die kurzzeitige hohe Lautstaerke ist nicht zu hoeren;
+ bis die externe Technik soweit ist, ist die Lautstaerke laengst
+ wieder normal.
+
+"Stumm beim Beenden"
+ Schaltet beim Beenden des VDR auf stumm.
+
+
+ SOUNDSET EINSTELLUNGEN
+-------------------------------
+"Name"
+ Der Name des Soundsets.
+
+Alle weiteren Parameter sind die Namen der einzelnen Regler der Soundkarte.
+(Nicht alle, sondern nur 'sinnvolle').
+Jeder Regler hat einen zugeordneten Wert, welcher festlegt, was mit ihm getan
+wird, wenn sich die VDR-Lautstaerke aendert.
+
+Der Aufbau dieses Wertes ist immer gleich und muss STRIKT eingehalten werden.
+
+ Operand (bestimmt, die Rechenoperation)
+ |
+ | Minimum (der Regler geht nicht unter diesen Wert)
+ | |
+ =80;1;90
+ | |
+ | Maximum (der Regler geht nicht ueber diesen Wert)
+ |
+ Wert (VDR-Lautstaerke wird per Operand mit diesem Wert verknuepft)
+
+Bis auf den Operand koennen die anderen Werte u.U. wegfallen.
+Ein paar Beispiele, die das Verfahren demonstrieren:
+"~"
+ tut gar nichts, der Regler wird nicht 'angefasst'
+ (Standardwert fuer alle Regler in einem neuen Soundset)
+
+"=0"
+ setzt den Regler dauerhaft auf den angegebenen Wert (hier 0)
+ (bei mir z.B. Bass =40 / Treble =60)
+
+"+10"
+ addiert den Wert (hier 10) zur VDR-Lautstaerke hinzu
+ (z.B. folgt "+0" der VDR-Lautstaerke direkt)
+
+"-5"
+ subtrahiert den Wert (hier 5) von der VDR-Lautstaerke
+
+"%10"
+ der Reglerwert ist immer 10% von der VDR-Lautstaerke
+
+"+10;1"
+ plus 10, aber nie weniger als 1
+ Wozu dies?
+ Der 'LFE' Regler meiner Soundkarte, der den Subwoofer bedient, ist mit nur
+ 32 Stufen nicht so fein aufgeloest, was zur Folge hat, dass die Hardware bei
+ kleinen Werten den Regler schon mal ganz auf 0 zieht, obwohl vielleicht 5%
+ gemeint waren. Die Minimum-Einstellung verhindert das.
+
+
+ SVDR
+===============================
+Das Plugin stellt die folgenden SVDR Kommandos zur Verfuegung.
+
+SSET [ name ]
+ setzt oder zeigt das aktive Soundset (mittels Name)
+
+SSID [ id ]
+ setzt oder zeigt das aktive Soundset (mittels ID)
+
+LIST [ names | all ]
+ zeigt ohne Parameter eine Liste der ID's aller Soundsets,
+ mit 'names' eine Namensliste und mit 'all' eine Liste mit
+ ID und Name
+
+ BEISPIELE
+-------------------------------
+svdrpsend.pl plug sndctl list
+svdrpsend.pl plug sndctl list all
+svdrpsend.pl plug sndctl sset
+svdrpsend.pl plug sndctl sset Stereo
+
+
+
+ HINWEISE
+===============================
+- das Plugin rechnet stets mit Lautstaerken von 0 bis 100
+- wenn die VDR-Lautstaerke auf 0 geht, gelten keine Minimum-Werte mehr, der
+ Regler wird dann auch auf 0 gezogen
+- die folgenden Operationen (erstes Zeichen) sind moeglich:
+ '~' > gar nichts tun (so ein Regler wird aus den Parametern entfernt)
+ '=' > Regler wird immer auf denselben Wert gesetzt
+ (gut fuer 'Bass' und 'Treble' oder um einen Regler "still"zulegen)
+ '+' > Regler bekommt die VDR Lautstaerke erhoeht um diesen Wert
+ '-' > Regler bekommt die VDR Lautstaerke abzueglich dieses Wertes
+ '%' > Regler bekommt x Prozent vom VDR Lautstaerke-Wert
+- falls dein VDR nicht mit root-Rechten laeuft, benoetigt der VDR user Rechte
+ fuer den Zugriff auf den Mixer der Soundkarte; evt. muss der VDR user
+ Mitglied der Gruppe 'audio' oder 'video' sein
+
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..eeed0f5
--- /dev/null
+++ b/TODO
@@ -0,0 +1,19 @@
+This is a "plugin" for the Video Disk Recorder (VDR).
+
+Written by: Thomas Hildebrandt <toxym@web.de>
+Project's homepage: none
+Latest version available at: http://www.box.net/shared/qhu44kgcv4
+
+See the file COPYING for license information.
+
+---------------------------------------------------------------------------
+Sound control plugin - 'sndctl'
+---------------------------------------------------------------------------
+
+ TODO
+===============================
+
+- service for other plugins to handle sound set switching
+- maybe a rule system for automatic sound set switching
+ (if DD automatic is not smart enough)
+- maybe support for OSS (if this makes sense)
diff --git a/alsa.c b/alsa.c
new file mode 100644
index 0000000..8389538
--- /dev/null
+++ b/alsa.c
@@ -0,0 +1,201 @@
+/*
+ * sndctl - a plugin for the Video Disk Recorder
+ * file: alsa.c
+ * description: handling of the Alsa API
+ *
+ * author: Thomas Hildebrandt <toxym@web.de>
+ *
+ * inspired by and reengineered from 'avolctl'
+ * thanks to Martin Prochnow <nordlichtl@martins-kabuff.de>
+ */
+
+#include "defaults.h"
+#include "alsa.h"
+
+/*********************************************************
+ * member functions for class cAlsa
+ *********************************************************/
+
+/*
+ * constructors
+ */
+cAlsa::cAlsa(){
+ init();
+}
+
+/*
+ * destructor
+ */
+cAlsa::~cAlsa(){
+}
+
+/*
+ * Initialize mapCardNameToHwNumber and mapCardNumberToMixerHandle
+ */
+static void dumpErrorMessage(string msg, int err)
+{
+ msg.append(snd_strerror(err));
+ dsyslog(msg.c_str());
+}
+
+void cAlsa::init( void ) {
+ char hwName[64];
+ snd_ctl_card_info_t *info;
+ snd_ctl_card_info_malloc(&info);
+ snd_ctl_t *ctl;
+ snd_hctl_t *hctl;
+ int err;
+
+ int number = -1;
+ for (;;) {
+ /* open card for getting the mixers of this card */
+ err = snd_card_next(&number);
+ if (err < 0) {
+ dumpErrorMessage(string("sndctl (cAlsa::init): ERROR: Cannot get next card number because "), err);
+ break;
+ }
+ if (number < 0)
+ break;
+ sprintf(hwName, "hw:%d", number);
+ if ((err = snd_ctl_open(&ctl, hwName, 0)) < 0) {
+ dumpErrorMessage(string("sndctl (cAlsa::init): ERROR: Cannot open card number because "), err);
+ break;
+ }
+ if ((err = snd_ctl_card_info(ctl, info)) < 0) {
+ dumpErrorMessage(string("sndctl (cAlsa::init): ERROR: Cannot get sound card info because "), err);
+ break;
+ }
+ dsyslog("sndctl (cAlsa::init): Found sound card:%s\n", snd_ctl_card_info_get_name(info));
+
+ if ((err = snd_hctl_open(&hctl, hwName, 0)) < 0) {
+ dumpErrorMessage(string("sndctl (cAlsa::init): ERROR: Cannot snd_hctl_open because "), err);
+ break;
+ }
+
+ /* now iterate over the mixers */
+ if ((err = snd_hctl_load(hctl)) < 0) {
+ dumpErrorMessage(string("sndctl (cAlsa::init): ERROR: Cannot snd_hctl_load because "), err);
+ }
+
+ snd_hctl_elem_t *elem = NULL;
+ snd_ctl_elem_info_t *elemInfo;
+ snd_ctl_elem_info_alloca(&elemInfo);
+
+ int controlNr = 0;
+ for (elem = snd_hctl_first_elem(hctl); elem; elem = snd_hctl_elem_next(elem)) {
+ if ((err = snd_hctl_elem_info(elem, elemInfo)) < 0) {
+ dumpErrorMessage(string("sndctl (cAlsa::init): ERROR: Cannot snd_hctl_elem_info because "), err);
+ break;
+ }
+ if (snd_ctl_elem_info_is_inactive(elemInfo)) {
+ continue;
+ }
+
+ snd_ctl_elem_id_t *id;
+ snd_ctl_elem_id_alloca(&id);
+ snd_hctl_elem_get_id(elem, id);
+
+ if (snd_ctl_elem_info_get_type(elemInfo) == SND_CTL_ELEM_TYPE_INTEGER) {
+ alsacontrol *control = new alsacontrol();
+ control->min = snd_ctl_elem_info_get_min(elemInfo);
+ control->max = snd_ctl_elem_info_get_max(elemInfo);
+ control->soundCardId = string(snd_ctl_card_info_get_name(info));
+ control->hwName = string(hwName);
+
+ // check for a senseful range
+ if( control->min == 0 && control->max > control->min && control->max > 10 ){
+ // get name
+ control->name = string( snd_ctl_elem_id_get_name(id));
+ control->mixername = string( snd_ctl_elem_id_get_name(id));
+ control->controlNr = controlNr;
+ controlNr++;
+
+ dsyslog( "sndctl (cAlsa::init): '%s' updated with min=%ld and max=%ld", control->name.c_str(), control->min, control->max );
+ vControls.push_back(control);
+ } else {
+ delete control;
+ }
+ } else {
+ dsyslog("sndctl (cAlsa::init): '%s' has no playback volume", snd_ctl_elem_id_get_name(id));
+ }
+ }
+ snd_hctl_close(hctl);
+ snd_ctl_close(ctl);
+ }
+ snd_ctl_card_info_free(info);
+ dsyslog("sndctl (cAlsa::init): found %d controls:", vControls.size());
+}
+
+/*
+ * checks for a mixer with a given name
+ */
+bool cAlsa::IsMixer( string Name ){
+ snd_mixer_elem_t *elem;
+
+ // search in all mixers for 'Name'
+ for( elem = snd_mixer_first_elem(handle); elem; elem = snd_mixer_elem_next( elem )){
+ if(( strcmp(snd_mixer_selem_get_name( elem ), Name.c_str()) == 0 ) &&
+ snd_mixer_selem_is_active( elem ) &&
+ ( snd_mixer_selem_has_playback_volume( elem ) ||
+ snd_mixer_selem_has_playback_switch( elem )))
+ return true;
+ }
+
+ // nothing found
+ return false;
+}
+
+/*
+ * set the volume of all controls
+ */
+void cAlsa::Volumes( void ){
+/* snd_mixer_elem_t *elem;
+ int i;
+ snd_mixer_selem_id_t *sid;
+ snd_mixer_selem_id_malloc( &sid );
+*/
+
+ // loop over all controls
+ for(int i = 0; i < vControls.size(); i++ ){
+ alsacontrol *control = static_cast<alsacontrol*>(vControls[i]);
+ if( control->volume >= 0 ){
+ int err;
+ // get ID from the name
+ dsyslog("sndctl (cAlsa::Volumes): trying to set control '%s' of card '%s'", control->name.c_str(), control->hwName.c_str());
+ snd_hctl_t *hctl;
+ if ((err = snd_hctl_open(&hctl, control->hwName.c_str(), 0)) < 0) {
+ dumpErrorMessage(string("sndctl (cAlsa::Volumes): snd_hctl_open failed because "), err);
+ break;
+ }
+ if ((err = snd_hctl_load(hctl)) < 0) {
+ dumpErrorMessage(string("sndctl (cAlsa::Volumes): snd_hctl_load failed because "), err);
+ break;
+ }
+
+ snd_ctl_elem_id_t *id;
+ snd_ctl_elem_id_alloca(&id);
+ snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER);
+
+ snd_ctl_elem_id_set_name(id, control->mixername.c_str());
+
+ snd_hctl_elem_t *elem = snd_hctl_find_elem(hctl, id);
+
+ snd_ctl_elem_value_t *ctl;
+ snd_ctl_elem_value_alloca(&ctl);
+ snd_ctl_elem_value_set_id(ctl, id);
+
+ /* left */
+ snd_ctl_elem_value_set_integer(ctl, 0, control->volume);
+ /* right */
+ snd_ctl_elem_value_set_integer(ctl, 1, control->volume);
+
+ if ((err = snd_hctl_elem_write(elem, ctl)) < 0) {
+ dumpErrorMessage(string("sndctl (cAlsa::Volumes): snd_hctl_elem_write failed because "), err);
+ break;
+ }
+
+ snd_hctl_close(hctl);
+
+ }
+ }
+}
diff --git a/alsa.h b/alsa.h
new file mode 100644
index 0000000..5cb46cb
--- /dev/null
+++ b/alsa.h
@@ -0,0 +1,43 @@
+/*
+ * sndctl - a plugin for the Video Disk Recorder
+ * file: alsa.h
+ * description: header file for alsa.c
+ *
+ * author: Thomas Hildebrandt <toxym@web.de>
+ *
+ * inspired by and reengineered from 'avolctl'
+ * thanks to Martin Prochnow <nordlichtl@martins-kabuff.de>
+ */
+
+#ifndef SNDCTL_ALSA_H
+#define SNDCTL_ALSA_H
+
+#include <alsa/asoundlib.h>
+#include "mixer.h"
+
+/*
+ * cAlsa
+ * class for handling the Alsa API
+ */
+class cAlsa : public cMixer {
+ private:
+ struct alsacontrol : public control {
+ string hwName;
+ string mixername;
+ };
+ snd_mixer_t *handle;
+ vector<snd_mixer_t *> handles;
+
+ void init();
+
+ protected:
+ virtual void Volumes( void );
+
+ public:
+ cAlsa();
+ virtual ~cAlsa();
+
+ virtual bool IsMixer( string );
+};
+
+#endif //SNDCTL_ALSA_H
diff --git a/defaults.h b/defaults.h
new file mode 100644
index 0000000..3ece480
--- /dev/null
+++ b/defaults.h
@@ -0,0 +1,72 @@
+/*
+ * sndctl - a plugin for the Video Disk Recorder
+ * file: defaults.h
+ * description: default definitions
+ *
+ * author: Thomas Hildebrandt <toxym@web.de>
+ *
+ * inspired by and reengineered from 'avolctl'
+ * thanks to Martin Prochnow <nordlichtl@martins-kabuff.de>
+ */
+
+#ifndef SNDCTL_DEFAULTS_H
+#define SNDCTL_DEFAULTS_H
+
+class cMainMenuSndctl;
+class cMixer;
+class cPluginSndctl;
+class cSetupSndctl;
+class cSetupMenuSndctl;
+class cSoundMan;
+class cSoundSet;
+class cSoundsetMenuSndctl;
+class cStatusSndctl;
+
+#include <map>
+#include <string>
+#include <time.h>
+#include <vector>
+#include <vdr/interface.h>
+#include <vdr/plugin.h>
+#include "i18n.h"
+using namespace std;
+
+#define SNDCTL_MAX_LEN_CONTROL_NAME 128
+#define SNDCTL_MAX_LEN_CONTROL_VAL 12
+
+#define SNDCTL_DEFAULT_MENUNAME "Sound control"
+#define SNDCTL_DEFAULT_SOUNDSET_NAME "New sound set"
+#define SNDCTL_DEFAULT_HIDEMAINMENUENTRY "no"
+#define SNDCTL_DEFAULT_DEFAULT_SSET ""
+#define SNDCTL_DEFAULT_INIT_VOLUME "-1"
+#define SNDCTL_DEFAULT_DD_AUTO_SWITCH "no"
+#define SNDCTL_DEFAULT_DD_AUTO_SSET ""
+#define SNDCTL_DEFAULT_SOUNDFLASH "no"
+#define SNDCTL_DEFAULT_SOUNDFLASH_DELTA "2"
+#define SNDCTL_DEFAULT_SOUNDFLASH_VOL "100"
+#define SNDCTL_DEFAULT_SOUNDFLASH_TIME "1"
+#define SNDCTL_DEFAULT_MUTE_ON_END "yes"
+
+#define SNDCTL_SETUP_MENUNAME "MenuName"
+#define SNDCTL_SETUP_HIDEMAINMENUENTRY "HideMainMenuEntry"
+#define SNDCTL_SETUP_DEFAULT_SSET "DefaultSoundSet"
+#define SNDCTL_SETUP_INIT_VOLUME "InitialVolume"
+#define SNDCTL_SETUP_DD_AUTO_SWITCH "DDAutoSwitch"
+#define SNDCTL_SETUP_DD_AUTO_SSET "DDAutoSoundSet"
+#define SNDCTL_SETUP_SOUNDFLASH "SoundFlash"
+#define SNDCTL_SETUP_SOUNDFLASH_DELTA "SoundFlashDelta"
+#define SNDCTL_SETUP_SOUNDFLASH_VOL "SoundFlashVolume"
+#define SNDCTL_SETUP_SOUNDFLASH_TIME "SoundFlashTime"
+#define SNDCTL_SETUP_MUTE_ON_END "MuteOnEnd"
+
+#define SNDCTL_MIXER_NONE '~'
+#define SNDCTL_MIXER_STATIC '='
+#define SNDCTL_MIXER_PERCENT '%'
+#define SNDCTL_MIXER_PLUS '+'
+#define SNDCTL_MIXER_MINUS '-'
+
+#define SNDCTL_SVDR_SSET "SSET"
+#define SNDCTL_SVDR_SSID "SSID"
+#define SNDCTL_SVDR_LIST "LIST"
+
+#endif //SNDCTL_DEFAULTS_H
diff --git a/i18n.c b/i18n.c
new file mode 100644
index 0000000..ad68c36
--- /dev/null
+++ b/i18n.c
@@ -0,0 +1,416 @@
+/*
+ * sndctl - a plugin for the Video Disk Recorder
+ * file: i18n.c
+ * description: internationalization (string definitions)
+ *
+ * author: Thomas Hildebrandt <toxym@web.de>
+ *
+ * inspired by and reengineered from 'avolctl'
+ * thanks to Martin Prochnow <nordlichtl@martins-kabuff.de>
+ */
+
+#include "defaults.h"
+#include "i18n.h"
+
+const tI18nPhrase Phrases[] = {
+ { "Sound control", // english
+ "Soundcontrol", // german
+ "",// TODO italian
+ "",// TODO dutch
+ "",// TODO portuguese
+ "",// TODO french
+ "",// TODO norwegian
+ "",// TODO finnish
+ "",// TODO polish
+ "",// TODO spanish
+ "",// TODO greek
+ "",// TODO swedish
+ "",// TODO romanian
+ "",// TODO hungarian
+ "",// TODO catalanian
+ "",// TODO russian
+ "",// TODO croatian
+ "",// TODO estonian
+ "",// TODO danish
+ "",// TODO czech
+ },
+ { "ALSA mixer control",
+ "ALSA Mixer Steuerung",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ { SNDCTL_DEFAULT_SOUNDSET_NAME,
+ "Neues Soundset",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ { SNDCTL_TXT_0001,
+ "Soundcontrol - Soundset auswählen",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ { SNDCTL_TXT_0002,
+ "Eintrag im Hauptmenü verstecken",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ { SNDCTL_TXT_0003,
+ "Standard Soundset",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ { SNDCTL_TXT_0004,
+ "Wählen",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ { SNDCTL_TXT_0005,
+ "Ändern",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ { SNDCTL_TXT_0006,
+ "Soundset bearbeiten",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ { SNDCTL_TXT_0007,
+ "Menü Name",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ { SNDCTL_TXT_0008,
+ "Name",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ { SNDCTL_TXT_0009,
+ "Neu",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ { SNDCTL_TXT_0010,
+ "Löschen",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ { SNDCTL_TXT_0011,
+ "Soundset löschen?",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ { SNDCTL_TXT_0012,
+ "DD Automatik",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ { SNDCTL_TXT_0013,
+ "DD Automatik Soundset",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ { SNDCTL_TXT_0014,
+ "Lautstärke beim Start",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ { SNDCTL_TXT_0015,
+ "Soundflash aktivieren",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ { SNDCTL_TXT_0016,
+ "Stumm beim Beenden",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ { NULL }
+};
diff --git a/i18n.h b/i18n.h
new file mode 100644
index 0000000..691e566
--- /dev/null
+++ b/i18n.h
@@ -0,0 +1,34 @@
+/*
+ * sndctl - a plugin for the Video Disk Recorder
+ * file: i18n.h
+ * description: header file for i18n.c
+ *
+ * author: Thomas Hildebrandt <toxym@web.de>
+ *
+ * inspired by and reengineered from 'avolctl'
+ * thanks to Martin Prochnow <nordlichtl@martins-kabuff.de>
+ */
+
+#ifndef SNDCTL_I18N_H
+#define SNDCTL_I18N_H
+
+extern const tI18nPhrase Phrases[];
+
+#define SNDCTL_TXT_0001 "Sound control - select sound set"
+#define SNDCTL_TXT_0002 "Hide main meny entry"
+#define SNDCTL_TXT_0003 "Default sound set"
+#define SNDCTL_TXT_0004 "Select"
+#define SNDCTL_TXT_0005 "Edit"
+#define SNDCTL_TXT_0006 "Edit sound set"
+#define SNDCTL_TXT_0007 "Menu name"
+#define SNDCTL_TXT_0008 "Name"
+#define SNDCTL_TXT_0009 "New"
+#define SNDCTL_TXT_0010 "Delete"
+#define SNDCTL_TXT_0011 "Delete sound set?"
+#define SNDCTL_TXT_0012 "Auto switch for DD"
+#define SNDCTL_TXT_0013 "Auto switch sound set"
+#define SNDCTL_TXT_0014 "Initial volume"
+#define SNDCTL_TXT_0015 "Enable sound flash"
+#define SNDCTL_TXT_0016 "Mute on end"
+
+#endif //SNDCTL_I18N_H
diff --git a/mainmenu.c b/mainmenu.c
new file mode 100644
index 0000000..554b463
--- /dev/null
+++ b/mainmenu.c
@@ -0,0 +1,132 @@
+/*
+ * sndctl - a plugin for the Video Disk Recorder
+ * file: mainmenu.c
+ * description: the main menu OSD
+ *
+ * author: Thomas Hildebrandt <toxym@web.de>
+ *
+ * inspired by and reengineered from 'avolctl'
+ * thanks to Martin Prochnow <nordlichtl@martins-kabuff.de>
+ */
+
+#include "defaults.h"
+#include "mainmenu.h"
+#include "soundset.h"
+#include "soundsetmenu.h"
+#include "menuitems.h"
+
+/*********************************************************
+ * member functions for class cMainMenuSndctl
+ *********************************************************/
+
+/*
+ * constructors
+ */
+cMainMenuSndctl::cMainMenuSndctl( cPluginSndctl *Plugin )
+:cOsdMenu( tr( SNDCTL_TXT_0001 ), 2 ){
+ // save plugin
+ plugin = Plugin;
+
+ // add content
+ Set();
+
+ // add help keys
+ SetHelpKeys();
+
+ // show
+ Display();
+}
+
+/*
+ * displays the menu (after refreshing the content)
+ */
+void cMainMenuSndctl::Display( void ){
+ // refresh items
+ Set();
+
+ // let do cOsdMenu the rest of the job
+ cOsdMenu::Display();
+
+ // update help keys
+ SetHelpKeys();
+}
+
+/*
+ * cares for pressing a key at a sound set entry
+ */
+eOSState cMainMenuSndctl::ProcessKey( eKeys Key ){
+ cSoundSetItem *item;
+
+ // get the current item
+ item = (cSoundSetItem *) Get( Current());
+
+ eOSState state = cOsdMenu::ProcessKey( Key );
+
+ if( state == osUnknown )
+ switch( Key ){
+ case kOk:
+ case kRed:
+ // select sound set
+ plugin->GetSoundManager()->SetSoundSet( item->GetId());
+ state = osEnd;
+ break;
+
+ case kGreen:
+ // edit sound set
+ return AddSubMenu( new cSoundsetMenuSndctl( plugin->GetSoundManager()->GetSoundSet( item->GetId()), plugin->GetSoundManager()->GetCurrentSoundSetID()));
+ break;
+
+ case kYellow:
+ // delete sound set only if there are more than one
+ if(plugin->GetSoundManager()->GetSoundSets()->size() <= 1)
+ break;
+ if( Interface->Confirm( tr( SNDCTL_TXT_0011 ))){
+ plugin->GetSoundManager()->DeleteSoundSet( item->GetId());
+ cOsdMenu::Del( Current());
+ SetHelpKeys();
+ Display();
+ }
+
+ state = osContinue;
+ break;
+
+ case kBlue:
+ // create a new sound set and switch to edit screen
+ return AddSubMenu( new cSoundsetMenuSndctl( plugin->GetSoundManager()->GetSoundSet( plugin->GetSoundManager()->CreateSoundSet()), plugin->GetSoundManager()->GetCurrentSoundSetID()));
+ break;
+
+ default:
+ break;
+ }
+
+ return state;
+}
+
+/*
+ * makes one menu entry for every soundset
+ */
+void cMainMenuSndctl::Set( void ){
+ map<string,cSoundSet*>::iterator it;
+ map<string,cSoundSet*> *soundsets;
+
+ // remove any old item
+ Clear();
+
+ // get defined sound sets
+ soundsets = plugin->GetSoundManager()->GetSoundSets();
+
+ // iterate over all soundsets
+ for( it = (*soundsets).begin(); it != (*soundsets).end(); it++ )
+ Add( new cSoundSetItem( it->first, plugin->GetSoundManager()));
+}
+
+/*
+ * set the colored help keys for this menu
+ */
+void cMainMenuSndctl::SetHelpKeys( void ){
+ if (plugin->GetSoundManager()->GetSoundSets()->size() > 1) {
+ SetHelp( tr( SNDCTL_TXT_0004 ), tr( SNDCTL_TXT_0005 ), tr( SNDCTL_TXT_0010 ), tr( SNDCTL_TXT_0009 ));
+ } else {
+ SetHelp( tr( SNDCTL_TXT_0004 ), tr( SNDCTL_TXT_0005 ), NULL, tr( SNDCTL_TXT_0009 ));
+ }
+}
diff --git a/mainmenu.h b/mainmenu.h
new file mode 100644
index 0000000..bc42268
--- /dev/null
+++ b/mainmenu.h
@@ -0,0 +1,33 @@
+/*
+ * sndctl - a plugin for the Video Disk Recorder
+ * file: mainmenu.h
+ * description: header file for mainmenu.c
+ *
+ * author: Thomas Hildebrandt <toxym@web.de>
+ *
+ * inspired by and reengineered from 'avolctl'
+ * thanks to Martin Prochnow <nordlichtl@martins-kabuff.de>
+ */
+
+#ifndef SNDCTL_MAINMENU_H
+#define SNDCTL_MAINMENU_H
+
+/*
+ * cMainMenuSndctl
+ * the main menu OSD
+ */
+class cMainMenuSndctl:public cOsdMenu {
+ private:
+ cPluginSndctl *plugin;
+
+ void Set( void );
+ void SetHelpKeys( void );
+
+ public:
+ cMainMenuSndctl( cPluginSndctl* );
+
+ virtual void Display( void );
+ virtual eOSState ProcessKey( eKeys );
+};
+
+#endif //SNDCTL_MAINMENU_H
diff --git a/menuitems.c b/menuitems.c
new file mode 100644
index 0000000..13eb8c3
--- /dev/null
+++ b/menuitems.c
@@ -0,0 +1,165 @@
+/*
+ * sndctl - a plugin for the Video Disk Recorder
+ * file: menuitems.c
+ * description: representation of OSD menu entries
+ *
+ * author: Thomas Hildebrandt <toxym@web.de>
+ *
+ * inspired by and reengineered from 'avolctl'
+ * thanks to Martin Prochnow <nordlichtl@martins-kabuff.de>
+ */
+
+#include "defaults.h"
+#include "menuitems.h"
+#include "soundman.h"
+
+/*********************************************************
+ * member functions for class cSoundSetItem
+ *********************************************************/
+
+/*
+ * constructors
+ */
+cSoundSetItem::cSoundSetItem( string Id, cSoundMan *SoundMan )
+:cOsdItem( "" ){
+ string text;
+
+ // save Id
+ id = Id;
+
+ // save sound manager
+ soundman = SoundMan;
+
+ // set text
+ text = string();
+ if( id == soundman->GetCurrentSoundSetID())
+ text.append( ">" );
+
+ text.append( "\t" );
+ text.append( soundman->GetSoundSet( id )->GetName());
+
+ SetText( text.c_str());
+}
+
+/*
+ * returns this items sound set ID
+ */
+string cSoundSetItem::GetId( void ){
+ return id;
+}
+
+/*********************************************************
+ * member functions for class cSoundSetChooserItem
+ *********************************************************/
+
+/*
+ * constructor
+ */
+cSoundSetChooserItem::cSoundSetChooserItem( cSoundMan *SoundMan, string *SoundSet, char *Text )
+:cMenuEditItem( tr( Text )){
+ // save arguments
+ soundman = SoundMan;
+ soundset = SoundSet;
+
+ // set initial value
+ Set();
+}
+
+/*
+ * cares for pressing a key at a sound set entry
+ */
+eOSState cSoundSetChooserItem::ProcessKey( eKeys Key ){
+ map<string,cSoundSet*> *soundsets;
+ map<string,cSoundSet*>::iterator sset;
+
+ eOSState state = cMenuEditItem::ProcessKey( Key );
+
+ // get list of sound sets
+ soundsets = soundman->GetSoundSets();
+ sset = (*soundsets).find( *soundset );
+
+ if( state == osUnknown )
+ switch( Key ){
+ case kLeft:
+ // go to the previous sound set (if possible)
+ if( sset-- != (*soundsets).begin())
+ (*soundset).assign( sset->first );
+ state = osContinue;
+ break;
+
+ case kRight:
+ // go to the next sound set (if possible)
+ if( ++sset != (*soundsets).end())
+ (*soundset).assign( sset->first );
+ state = osContinue;
+ break;
+
+ default:
+ break;
+ }
+
+ // update display
+ Set();
+
+ return state;
+}
+
+/*
+ * set the current value (means: the name of the current soundset)
+ */
+void cSoundSetChooserItem::Set( void ){
+ SetValue( soundman->GetSoundSet( *soundset )->GetName().c_str());
+}
+
+/*********************************************************
+ * member functions for class cSoundSetControlItem
+ *********************************************************/
+
+
+static char* copyMixerControl(string controlString) {
+ char *copied = new char[SNDCTL_MAX_LEN_CONTROL_VAL];
+ strncpy(copied, controlString.c_str(), SNDCTL_MAX_LEN_CONTROL_VAL-1);
+ return copied;
+}
+
+/*
+ * constructor
+ */
+cSoundSetControlItem::cSoundSetControlItem( cSoundSet *SoundSet, cControlId Control, bool IsActive )
+:cMenuEditStrItem( Control.getDisplayName().c_str(), copyMixerControl( SoundSet->Get( Control )), 11, "~=+-%0123456789;" ){
+ // save arguments
+ soundset = SoundSet;
+ control = Control;
+ isActive = IsActive;
+}
+
+/*
+ * cares for pressing a key when editing a control value
+ */
+eOSState cSoundSetControlItem::ProcessKey( eKeys Key ){
+ if( InEditMode()) {
+ /* The ProcessKey call sets the value */
+ eOSState state = cMenuEditStrItem::ProcessKey( Key );
+ switch( Key ){
+ case kOk:
+ dsyslog( "sndctl (cSoundSetControlItem::ProcessKey): Control '%s' set to '%s'.", control.getDisplayName().c_str(), value );
+ // save changed value
+ soundset->Set( control, string( value ));
+ // set volume
+ if( isActive )
+ soundset->Volume();
+ break;
+
+ default:
+ break;
+ }
+ return state;
+ } else {
+ return cMenuEditStrItem::ProcessKey( Key );
+ }
+
+}
+
+cSoundSetControlItem::~cSoundSetControlItem() {
+ delete[] value;
+}
diff --git a/menuitems.h b/menuitems.h
new file mode 100644
index 0000000..453d402
--- /dev/null
+++ b/menuitems.h
@@ -0,0 +1,73 @@
+/*
+ * sndctl - a plugin for the Video Disk Recorder
+ * file: menuitems.h
+ * description: header file for soundsetitem.c
+ *
+ * author: Thomas Hildebrandt <toxym@web.de>
+ *
+ * inspired by and reengineered from 'avolctl'
+ * thanks to Martin Prochnow <nordlichtl@martins-kabuff.de>
+ */
+
+#ifndef SNDCTL_MENUITEMS_H
+#define SNDCTL_MENUITEMS_H
+
+#include "mixer.h"
+
+/*
+ * cursor position when editing a mixer control value
+ */
+enum CursorPos {op, val, limitL};
+
+/*
+ * cSoundSetItem
+ * representation of an OSD menu entry for one sound set
+ */
+class cSoundSetItem:public cOsdItem {
+ private:
+ string id;
+ string soundCardId;
+ cSoundMan *soundman;
+
+ public:
+ cSoundSetItem( string, cSoundMan* );
+
+ string GetId( void );
+};
+
+/*
+ * cSoundSetChooserItem
+ * representation of a list of all soundsets
+ */
+class cSoundSetChooserItem:public cMenuEditItem {
+ private:
+ string *soundset;
+ cSoundMan *soundman;
+
+ protected:
+ virtual void Set( void );
+
+ public:
+ cSoundSetChooserItem( cSoundMan*, string*, char* );
+
+ virtual eOSState ProcessKey( eKeys );
+};
+
+/*
+ * cSoundSetControlItem
+ * representation of one mixer control value
+ */
+class cSoundSetControlItem:public cMenuEditStrItem {
+ private:
+ cControlId control;
+ bool isActive;
+ cSoundSet *soundset;
+
+ public:
+ cSoundSetControlItem( cSoundSet*, cControlId Control, bool );
+ ~cSoundSetControlItem();
+
+ virtual eOSState ProcessKey( eKeys );
+};
+
+#endif //SNDCTL_MENUITEMS_H
diff --git a/mixer.c b/mixer.c
new file mode 100644
index 0000000..bac74a1
--- /dev/null
+++ b/mixer.c
@@ -0,0 +1,176 @@
+/*
+ * sndctl - a plugin for the Video Disk Recorder
+ * file: mixer.c
+ * description: parent class for all types of mixer devices
+ *
+ * author: Thomas Hildebrandt <toxym@web.de>
+ *
+ * inspired by and reengineered from 'avolctl'
+ * thanks to Martin Prochnow <nordlichtl@martins-kabuff.de>
+ */
+
+#include "defaults.h"
+#include "mixer.h"
+#include "soundset.h"
+
+/*********************************************************
+ * member functions for class cMixer
+ *********************************************************/
+
+/*
+ * constructor
+ */
+cMixer::cMixer(){
+}
+
+/*
+ * destructor
+ */
+cMixer::~cMixer(){
+ int countControls = vControls.size();
+ for(int i = 0 ; i < countControls ; i++) {
+ delete vControls[i];
+ }
+}
+
+int cMixer::CountControls() {
+ return vControls.size();
+}
+
+
+/*
+ * returns a vector with all controls names
+ */
+vector<cControlId> cMixer::GetControls( void ){
+ vector<cControlId> ctrls;
+ int i;
+
+ // loop over all controls
+ for( i = 0; i < vControls.size(); i++ ) {
+ ctrls.push_back(cControlId(vControls[i]->soundCardId, vControls[i]->name, vControls[i]->controlNr));
+ }
+
+ return ctrls;
+}
+
+/*
+ * update mixer values
+ */
+void cMixer::Update( cSoundSet *SoundSet ){
+ int i;
+ string::size_type loc;
+ string v;
+
+ dsyslog( "sndctl (cMixer::Update): Updating controls with sound set '%s'.", SoundSet->GetName().c_str());
+
+ // loop over all controls
+ for( i = 0; i < vControls.size(); i++ ){
+ control* actualControl = vControls[i];
+ v = SoundSet->Get(cControlId(actualControl->soundCardId, actualControl->name, actualControl->controlNr ));
+
+ // get operation
+ actualControl->op = v.substr( 0, 1 ).c_str()[0];
+
+ // get value
+ if(actualControl->op == SNDCTL_MIXER_NONE )
+ actualControl->value = 0;
+ else
+ actualControl->value = atoi( v.substr( 1 ).c_str());
+
+ // check and correct limits
+ actualControl->value = actualControl->value < 0 ? 0 : actualControl->value;
+ actualControl->value = actualControl->value > 999 ? 999 : actualControl->value;
+
+ // check and set minimum volume
+ actualControl->limitL = actualControl->min;
+ if(( loc = v.find( ';', 0 )) != string::npos )
+ actualControl->limitL = atoi( v.substr( ++loc ).c_str());
+
+ // check and set maximum volume
+ actualControl->limitU = actualControl->max;
+ if(( loc != string::npos ) && (( loc = v.find( ';', loc )) != string::npos ))
+ vControls[i]->limitU = atoi( v.substr( ++loc ).c_str());
+
+ dsyslog( "sndctl (cMixer::Update): '%s' updated with %c%d;%ld;%ld", actualControl->name.c_str(), actualControl->op, actualControl->value, actualControl->limitL, actualControl->limitU );
+ }
+}
+
+/*
+ * sets the volume of this sound set
+ */
+void cMixer::Volume( int Vol ){
+ int i;
+ long vol;
+
+ dsyslog( "sndctl (cMixer::Volume): Volume goes to %d%%.", Vol );
+
+ // loop over all controls
+ for( i = 0; i < vControls.size(); i++ ){
+ control* actualControl = vControls[i];
+ // initial value for 'volume'
+ actualControl->volume = -1;
+
+ // calculate volume according to operation mode
+ switch( actualControl->op ){
+ case SNDCTL_MIXER_STATIC:
+ vol = actualControl->value;
+ break;
+
+ case SNDCTL_MIXER_PLUS:
+ vol = Vol + actualControl->value;
+ break;
+
+ case SNDCTL_MIXER_MINUS:
+ vol = Vol - actualControl->value;
+ break;
+
+ case SNDCTL_MIXER_PERCENT:
+ vol = Vol * actualControl->value / 100;
+ break;
+
+ case SNDCTL_MIXER_NONE:
+ default:
+ dsyslog( "sndctl (cMixer::Volume): Control '%s' stays unchanged.", actualControl->name.c_str());
+ continue;
+ break;
+ }
+
+ // Vol = 0 means, no matter, what happend before
+ if( Vol == 0 )
+ vol = 0;
+
+ // scale volume to real values
+ vol = actualControl->min + ( actualControl->max - actualControl->min ) * vol / 100;
+
+ // check for hard limits
+ if( Vol > 0 && vol < actualControl->limitL )
+ vol = actualControl->limitL;
+ if( vol > actualControl->limitU )
+ vol = actualControl->limitU;
+
+ dsyslog( "sndctl (cMixer::Volume): Control '%s' goes to '%ld'.", actualControl->name.c_str(), vol );
+
+ // set volume
+ actualControl->volume = vol;
+ }
+
+ // set volumes to real hardware
+ Volumes();
+}
+
+cControlId::cControlId(string SoundCardId, string DisplayName, int ControlNr) {
+ name = DisplayName;
+ soundCardId = SoundCardId;
+ controlNr = ControlNr;
+}
+
+cControlId::cControlId() : name(), soundCardId(), controlNr(0) {
+}
+
+bool cControlId::operator==(cControlId const& b) {
+ return controlNr == b.controlNr && soundCardId.compare(b.soundCardId) == 0;
+}
+
+string cControlId::getDisplayName() { return name; }
+int cControlId::getControlNr() { return controlNr; }
+string cControlId::getSoundCardId() { return soundCardId; }
diff --git a/mixer.h b/mixer.h
new file mode 100644
index 0000000..9cbebf5
--- /dev/null
+++ b/mixer.h
@@ -0,0 +1,66 @@
+/*
+ * sndctl - a plugin for the Video Disk Recorder
+ * file: mixer.h
+ * description: header file for mixer.c
+ *
+ * author: Thomas Hildebrandt <toxym@web.de>
+ *
+ * inspired by and reengineered from 'avolctl'
+ * thanks to Martin Prochnow <nordlichtl@martins-kabuff.de>
+ */
+
+#ifndef SNDCTL_MIXER_H
+#define SNDCTL_MIXER_H
+
+/*
+ * cMixer
+ * parent class for all types of mixer devices
+ */
+
+class cControlId {
+private:
+ string name;
+ int controlNr;
+ string soundCardId;
+public:
+ cControlId(string SoundCardId, string DisplayName, int controlNr);
+ cControlId();
+ bool operator==(cControlId const& b);
+ string getDisplayName();
+ int getControlNr();
+ string getSoundCardId();
+};
+
+class cMixer {
+ protected:
+ struct control{
+ long min;
+ long max;
+ string name;
+ long limitL;
+ long limitU;
+ char op;
+ int value;
+ long volume;
+ string soundCardId;
+ int controlNr;
+ };
+
+ vector<control *> vControls;
+
+ virtual void Volumes( void ) = 0;
+
+ public:
+
+ cMixer();
+ virtual ~cMixer();
+
+ vector<cControlId> GetControls( void );
+ void Update( cSoundSet* );
+ void Volume( int );
+ int CountControls( void );
+
+ virtual bool IsMixer( string ) = 0;
+};
+
+#endif //SNDCTL_MIXER_H
diff --git a/setup.c b/setup.c
new file mode 100644
index 0000000..c69dbc4
--- /dev/null
+++ b/setup.c
@@ -0,0 +1,135 @@
+/*
+ * sndctl - a plugin for the Video Disk Recorder
+ * file: setup.c
+ * description: management of setup configuration
+ *
+ * author: Thomas Hildebrandt <toxym@web.de>
+ *
+ * inspired by and reengineered from 'avolctl'
+ * thanks to Martin Prochnow <nordlichtl@martins-kabuff.de>
+ */
+
+#include "defaults.h"
+#include "setup.h"
+#include "soundset.h"
+
+/*********************************************************
+ * member functions for class cSetupSndctl
+ *********************************************************/
+
+/*
+ * constructor
+ */
+cSetupSndctl::cSetupSndctl(){
+ dsyslog( "sndctl (cSetupSndctl::cSetupSndctl): cSetupSndctl created" );
+
+ // create all possible setup items with default values
+ items[SNDCTL_SETUP_MENUNAME] = tr( SNDCTL_DEFAULT_MENUNAME );
+ items[SNDCTL_SETUP_HIDEMAINMENUENTRY] = SNDCTL_DEFAULT_HIDEMAINMENUENTRY;
+ items[SNDCTL_SETUP_DEFAULT_SSET] = SNDCTL_DEFAULT_DEFAULT_SSET;
+ items[SNDCTL_SETUP_INIT_VOLUME] = SNDCTL_DEFAULT_INIT_VOLUME;
+ items[SNDCTL_SETUP_DD_AUTO_SWITCH] = SNDCTL_DEFAULT_DD_AUTO_SWITCH;
+ items[SNDCTL_SETUP_DD_AUTO_SSET] = SNDCTL_DEFAULT_DD_AUTO_SSET;
+ items[SNDCTL_SETUP_SOUNDFLASH] = SNDCTL_DEFAULT_SOUNDFLASH;
+ items[SNDCTL_SETUP_SOUNDFLASH_DELTA] = SNDCTL_DEFAULT_SOUNDFLASH_DELTA;
+ items[SNDCTL_SETUP_SOUNDFLASH_VOL] = SNDCTL_DEFAULT_SOUNDFLASH_VOL;
+ items[SNDCTL_SETUP_SOUNDFLASH_TIME] = SNDCTL_DEFAULT_SOUNDFLASH_TIME;
+ items[SNDCTL_SETUP_MUTE_ON_END] = SNDCTL_DEFAULT_MUTE_ON_END;
+}
+
+/*
+ * returns a setup value
+ */
+string cSetupSndctl::Get( string Name ){
+ if( items.find( Name ) != items.end())
+ return items[Name];
+
+ // default return value is NULL
+ return NULL;
+}
+
+/*
+ * returns a setup value as bool
+ */
+bool cSetupSndctl::GetBool( string Name ){
+ return ( Get( Name ) == "yes" ) ? true : false;
+}
+
+/*
+ * returns a setup value as integer
+ */
+int cSetupSndctl::GetInt( string Name ){
+ return atoi( Get( Name ).c_str());
+}
+
+/*
+ * set a setup value
+ */
+bool cSetupSndctl::Set( string Name, string Value, cSoundMan *Soundman ){
+ bool result;
+ cSoundSet *sset;
+ unsigned int div;
+
+ dsyslog( "sndctl (cSetupSndctl::Set): Receiving parameter '%s' with value '%s'.", Name.c_str(), Value.c_str() );
+
+ // save this parameter
+ parameters.push_back( Name );
+
+ // lets assume the worst case ;-)
+ result = false;
+
+ // first check for a general option
+ if( result = (items.find( Name ) != items.end())){
+ // save this item
+ items[Name] = Value;
+
+ // take special care for SNDCTL_DEFAULT_DD_AUTO_SSET
+ if( Name == SNDCTL_SETUP_DEFAULT_SSET &&
+ items[SNDCTL_SETUP_DD_AUTO_SSET].empty())
+ items[SNDCTL_SETUP_DD_AUTO_SSET] = Value;
+ }
+
+ // assuming this value belongs to a soundset
+ // we need a '_' in 'Name' to deal with this
+ else if(( div = Name.find_first_of( '_' )) != string::npos ){
+ // get sound set for this ID (it's up to the first '_')
+ sset = Soundman->GetSoundSet( Name.substr( 0, div ));
+
+ // set value
+ result = sset->Set( Name.substr( div + 1 ), Value );
+ }
+
+ // nothing of all, that's bad
+ return result;
+}
+
+/*
+ * store all setup values
+ */
+bool cSetupSndctl::Store( cPluginSndctl *Plugin ){
+ vector<string>::iterator it;
+
+ // first, remove all parameters
+ for( it = parameters.begin(); it != parameters.end(); it++ ){
+ dsyslog( "sndctl (cSetupSndctl::Store): Removing: %s", (*it).c_str());
+ Plugin->Store( *it, string() );
+ }
+
+ // store all general settings
+ Plugin->Store( string( SNDCTL_SETUP_MENUNAME ), Get( SNDCTL_SETUP_MENUNAME ));
+ Plugin->Store( string( SNDCTL_SETUP_HIDEMAINMENUENTRY ), Get( SNDCTL_SETUP_HIDEMAINMENUENTRY ));
+ Plugin->Store( string( SNDCTL_SETUP_DEFAULT_SSET ), Get( SNDCTL_SETUP_DEFAULT_SSET ));
+ Plugin->Store( string( SNDCTL_SETUP_INIT_VOLUME ), Get( SNDCTL_SETUP_INIT_VOLUME ));
+ Plugin->Store( string( SNDCTL_SETUP_DD_AUTO_SWITCH ), Get( SNDCTL_SETUP_DD_AUTO_SWITCH ));
+ Plugin->Store( string( SNDCTL_SETUP_DD_AUTO_SSET ), Get( SNDCTL_SETUP_DD_AUTO_SSET ));
+ Plugin->Store( string( SNDCTL_SETUP_SOUNDFLASH ), Get( SNDCTL_SETUP_SOUNDFLASH ));
+ Plugin->Store( string( SNDCTL_SETUP_SOUNDFLASH_DELTA ), Get( SNDCTL_SETUP_SOUNDFLASH_DELTA ));
+ Plugin->Store( string( SNDCTL_SETUP_SOUNDFLASH_VOL ), Get( SNDCTL_SETUP_SOUNDFLASH_VOL ));
+ Plugin->Store( string( SNDCTL_SETUP_SOUNDFLASH_TIME ), Get( SNDCTL_SETUP_SOUNDFLASH_TIME ));
+ Plugin->Store( string( SNDCTL_SETUP_MUTE_ON_END ), Get( SNDCTL_SETUP_MUTE_ON_END ));
+
+ // store all settings from all soundsets
+ Plugin->GetSoundManager()->Store( Plugin );
+
+ return true;
+}
diff --git a/setup.h b/setup.h
new file mode 100644
index 0000000..b8f0396
--- /dev/null
+++ b/setup.h
@@ -0,0 +1,36 @@
+/*
+ * sndctl - a plugin for the Video Disk Recorder
+ * file: setup.h
+ * description: header file for setup.c
+ *
+ * author: Thomas Hildebrandt <toxym@web.de>
+ *
+ * inspired by and reengineered from 'avolctl'
+ * thanks to Martin Prochnow <nordlichtl@martins-kabuff.de>
+ */
+
+#ifndef SNDCTL_SETUP_H
+#define SNDCTL_SETUP_H
+
+#include "sndctl.h"
+
+/*
+ * cSetupSndctl
+ * setup class definition
+ */
+class cSetupSndctl {
+ private:
+ map<string,string> items;
+ vector<string> parameters;
+
+ public:
+ cSetupSndctl();
+
+ string Get( string );
+ bool GetBool( string );
+ int GetInt( string );
+ bool Set( string, string, cSoundMan* = NULL );
+ bool Store( cPluginSndctl* );
+};
+
+#endif //SNDCTL_SETUP_H
diff --git a/setupmenu.c b/setupmenu.c
new file mode 100644
index 0000000..c1447ec
--- /dev/null
+++ b/setupmenu.c
@@ -0,0 +1,105 @@
+/*
+ * sndctl - a plugin for the Video Disk Recorder
+ * file: setupmenu.c
+ * description: the OSD menu page for this plugin
+ *
+ * author: Thomas Hildebrandt <toxym@web.de>
+ *
+ * inspired by and reengineered from 'avolctl'
+ * thanks to Martin Prochnow <nordlichtl@martins-kabuff.de>
+ */
+
+#include "defaults.h"
+#include "setupmenu.h"
+#include "menuitems.h"
+#include "sndctl.h"
+#include "setup.h"
+
+/*********************************************************
+ * member functions for class cSetupMenuSndctl
+ *********************************************************/
+
+/*
+ * constructors
+ */
+cSetupMenuSndctl::cSetupMenuSndctl( cPluginSndctl *Plugin ){
+ // save plugin
+ plugin = Plugin;
+
+ // get current values
+ menuname = strdup( plugin->GetSetup()->Get( SNDCTL_SETUP_MENUNAME ).c_str());
+ hideMainMenuEntry = plugin->GetSetup()->GetBool( SNDCTL_SETUP_HIDEMAINMENUENTRY ) ? 1 : 0;
+ initVolume = plugin->GetSetup()->GetInt( SNDCTL_SETUP_INIT_VOLUME );
+ defSoundSet = plugin->GetSetup()->Get( SNDCTL_SETUP_DEFAULT_SSET );
+ ddAutoSwitch = plugin->GetSetup()->GetBool( SNDCTL_SETUP_DD_AUTO_SWITCH ) ? 1 : 0;
+ ddAutoSoundSet = plugin->GetSetup()->Get( SNDCTL_SETUP_DD_AUTO_SSET );
+ soundflash = plugin->GetSetup()->GetBool( SNDCTL_SETUP_SOUNDFLASH ) ? 1 : 0;
+ muteOnEnd = plugin->GetSetup()->GetBool( SNDCTL_SETUP_MUTE_ON_END ) ? 1 : 0;
+
+ // add content
+ Set();
+}
+
+/*
+ * makes setup menu entries
+ */
+void cSetupMenuSndctl::Set( void ){
+ // main menu entry
+ Add( new cMenuEditBoolItem( tr( SNDCTL_TXT_0002 ), &hideMainMenuEntry ));
+
+ // main menu name
+ Add( new cMenuEditStrItem( tr( SNDCTL_TXT_0007 ), menuname, 40, tr(FileNameChars)));
+
+ // default soundset
+ Add( new cSoundSetChooserItem( plugin->GetSoundManager(), &defSoundSet, SNDCTL_TXT_0003 ));
+
+ // initial volume
+ Add( new cMenuEditIntItem( tr( SNDCTL_TXT_0014 ), &initVolume, -1, 100 ));
+
+ // DD auto switch
+ Add( new cMenuEditBoolItem( tr( SNDCTL_TXT_0012 ), &ddAutoSwitch ));
+
+ // DD auto sound set
+ Add( new cSoundSetChooserItem( plugin->GetSoundManager(), &ddAutoSoundSet, SNDCTL_TXT_0013 ));
+
+ // sound flash
+ Add( new cMenuEditBoolItem( tr( SNDCTL_TXT_0015 ), &soundflash ));
+
+ // sound flash
+ Add( new cMenuEditBoolItem( tr( SNDCTL_TXT_0016 ), &muteOnEnd ));
+}
+
+/*
+ * stores changed values
+ */
+void cSetupMenuSndctl::Store( void ){
+ char vol[3];
+
+ // main menu entry
+ plugin->GetSetup()->Set( SNDCTL_SETUP_HIDEMAINMENUENTRY, string( hideMainMenuEntry ? "yes" : "no" ));
+
+ // main menu name
+ plugin->GetSetup()->Set( SNDCTL_SETUP_MENUNAME, string( menuname ));
+
+ // default sound set
+ plugin->GetSetup()->Set( SNDCTL_SETUP_DEFAULT_SSET, defSoundSet );
+
+ // initial volume
+ sprintf( vol, "%d", initVolume );
+ plugin->GetSetup()->Set( SNDCTL_SETUP_INIT_VOLUME, string( vol ));
+
+ // DD auto switch
+ plugin->GetSetup()->Set( SNDCTL_SETUP_DD_AUTO_SWITCH, string( ddAutoSwitch ? "yes" : "no" ));
+
+ // DD auto switch sound set
+ plugin->GetSetup()->Set( SNDCTL_SETUP_DD_AUTO_SSET, ddAutoSoundSet );
+
+ // sound flash
+ plugin->GetSetup()->Set( SNDCTL_SETUP_SOUNDFLASH, string( soundflash ? "yes" : "no" ));
+
+ // mute on end
+ plugin->GetSetup()->Set( SNDCTL_SETUP_MUTE_ON_END, string( muteOnEnd ? "yes" : "no" ));
+
+ // do a global store now
+ plugin->GetSetup()->Store( plugin );
+}
diff --git a/setupmenu.h b/setupmenu.h
new file mode 100644
index 0000000..edd2ee8
--- /dev/null
+++ b/setupmenu.h
@@ -0,0 +1,40 @@
+/*
+ * sndctl - a plugin for the Video Disk Recorder
+ * file: setupmenu.h
+ * description: header file for setupmenu.c
+ *
+ * author: Thomas Hildebrandt <toxym@web.de>
+ *
+ * inspired by and reengineered from 'avolctl'
+ * thanks to Martin Prochnow <nordlichtl@martins-kabuff.de>
+ */
+
+#ifndef SNDCTL_SETUPMENU_H
+#define SNDCTL_SETUPMENU_H
+
+/*
+ * cSetupMenuSndctl
+ * the OSD menu page for this plugin
+ */
+class cSetupMenuSndctl:public cMenuSetupPage {
+ private:
+ string defSoundSet;
+ string ddAutoSoundSet;
+ int ddAutoSwitch;
+ int hideMainMenuEntry;
+ int initVolume;
+ int muteOnEnd;
+ int soundflash;
+ char *menuname;
+ cPluginSndctl *plugin;
+
+ void Set();
+
+ protected:
+ virtual void Store( void );
+
+ public:
+ cSetupMenuSndctl( cPluginSndctl* );
+};
+
+#endif //SNDCTL_SETUPMENU_H
diff --git a/sndctl.c b/sndctl.c
new file mode 100644
index 0000000..3e7ccd0
--- /dev/null
+++ b/sndctl.c
@@ -0,0 +1,283 @@
+/*
+ * sndctl - a plugin for the Video Disk Recorder
+ * file: sndctl.c
+ * description: main plugin file
+ *
+ * author: Thomas Hildebrandt <toxym@web.de>
+ *
+ * inspired by and reengineered from 'avolctl'
+ * thanks to Martin Prochnow <nordlichtl@martins-kabuff.de>
+ */
+
+#include "defaults.h"
+#include "sndctl.h"
+#include "mainmenu.h"
+#include "setupmenu.h"
+
+static const char *VERSION = "0.1.5";
+static const char *DESCRIPTION = "ALSA mixer control";
+static const char *SVDRHelpPages[] = {
+ "SSET [ sound set name ]\n"
+ " Set or show the active sound set by name.",
+ "SSID [ sound set ID ]\n"
+ " Set or show the active sound set by ID.",
+ "LIST [ names | all ]\n"
+ " List all available sound sets.\n"
+ " Specify 'names' to see sound set names instead of ID's.\n"
+ " Specify 'all' to see sound set ID's and names.",
+ NULL
+};
+
+/*********************************************************
+ * member functions for class cPluginSndctl
+ *********************************************************/
+
+/*
+ * constructor
+ */
+cPluginSndctl::cPluginSndctl( void ){
+ dsyslog( "sndctl (cPluginSndctl::cPluginSndctl): cPluginSndctl created" );
+
+ // create sound manager
+ soundman = new cSoundMan( this );
+
+ // create setup manager
+ setup = new cSetupSndctl();
+
+ // initialize statusMonitor
+ statusMonitor = 0;
+}
+
+/*
+ * destructor
+ */
+cPluginSndctl::~cPluginSndctl(){
+ // destroy something
+ delete statusMonitor;
+ delete setup;
+ delete soundman;
+ statusMonitor = 0;
+ setup = 0;
+ soundman = 0;
+}
+
+/*
+ * return this plugins command line help
+ */
+const char *cPluginSndctl::CommandLineHelp( void ){
+// FIXME: do something with this
+ return NULL;
+}
+
+/*
+ * return this plugins description
+ */
+const char *cPluginSndctl::Description( void ){
+ return tr( DESCRIPTION );
+}
+
+/*
+ * returns the sound manager
+ */
+cSoundMan *cPluginSndctl::GetSoundManager( void ){
+ return soundman;
+}
+
+/*
+ * returns the setup object
+ */
+cSetupSndctl *cPluginSndctl::GetSetup( void ){
+ return setup;
+}
+
+/*
+ * return this plugins main menu entry
+ */
+const char *cPluginSndctl::MainMenuEntry( void ){
+ if( setup->GetBool( SNDCTL_SETUP_HIDEMAINMENUENTRY ))
+ return NULL;
+
+ return setup->Get( SNDCTL_SETUP_MENUNAME ).c_str();
+}
+
+/*
+ * returns the OSD object for the main menu entry
+ */
+cOsdObject *cPluginSndctl::MainMenuAction( void ){
+ return new cMainMenuSndctl( this );
+}
+
+/*
+ * returns the setup page for this plugin
+ */
+cMenuSetupPage *cPluginSndctl::SetupMenu( void ){
+ return new cSetupMenuSndctl( this );
+}
+
+/*
+ * care for setup parameters
+ */
+bool cPluginSndctl::SetupParse( const char *Name, const char *Value ){
+ return setup->Set( string( Name ), string( Value ), soundman );
+}
+
+/*
+ * start function
+ */
+bool cPluginSndctl::Start( void ){
+ // register i18n phrases
+ RegisterI18n( Phrases );
+
+ // set default sound set
+ setup->Set( string( SNDCTL_SETUP_DEFAULT_SSET ), soundman->SetSoundSet( soundman->DefaultSoundSetID( setup->Get( string( SNDCTL_SETUP_DEFAULT_SSET )))));
+
+ // create status monitor
+ statusMonitor = new cStatusSndctl( soundman );
+
+ return true;
+}
+
+/*
+ * stop function
+ */
+void cPluginSndctl::Stop(void){
+ // mute volume, if requested
+ if( setup->GetBool( SNDCTL_SETUP_MUTE_ON_END )){
+ dsyslog( "sndctl (cPluginSndctl::Stop): Mute on end is enabled." );
+ soundman->SetVolume( 0 );
+ }
+
+ // save setup
+ setup->Store( this );
+}
+
+/*
+ * a wrapper for VDR's SetupStore function
+ */
+void cPluginSndctl::Store( string Name, string Value ){
+ dsyslog( "sndctl (cPluginSndctl::Store): Store '%s' with value '%s'.", Name.c_str(), Value.c_str());
+
+ if( Value.empty())
+ SetupStore( Name.c_str());
+ else
+ SetupStore( Name.c_str(), Value.c_str());
+}
+
+/*
+ * implements the SVDR commands
+ */
+cString cPluginSndctl::SVDRPCommand( const char *Command, const char *Option, int &ReplyCode ){
+ map<string,cSoundSet*>::iterator it;
+ string list;
+ map<string,cSoundSet*> *ssets;
+
+ isyslog( "sndctl (cPluginSndctl::SVDRPCommand): Receiving '%s %s'", Command, Option );
+
+ // check for SSET
+ if( strcasecmp( Command, SNDCTL_SVDR_SSET ) == 0 )
+ if( *Option ){
+ if( soundman->SetSoundSet( string( Option )) == "~" ){
+ ReplyCode = 954;
+ return cString::sprintf( "Sound set not found: \"%s\"", Option );
+ } else {
+ ReplyCode = 950;
+ return cString::sprintf( "Switched to sound set: \"%s\"", Option );
+ }
+ }else {
+ ReplyCode = 950;
+ return cString::sprintf( "%s", soundman->GetSoundSet( soundman->GetCurrentSoundSetID())->GetName().c_str());
+ }
+
+ // check for SSID
+ else if( strcasecmp( Command, SNDCTL_SVDR_SSID ) == 0 )
+ if( *Option ){
+ if( soundman->SetSoundSet( string( Option )) == "~" ){
+ ReplyCode = 954;
+ return cString::sprintf( "Sound set not found: \"%s\"", Option );
+ } else {
+ ReplyCode = 950;
+ return cString::sprintf( "Switched to sound set: \"%s\"", Option );
+ }
+ }else {
+ ReplyCode = 950;
+ return cString::sprintf( "%s", soundman->GetCurrentSoundSetID().c_str());
+ }
+
+ // check for LIST
+ else if( strcasecmp( Command, SNDCTL_SVDR_LIST ) == 0 ){
+ // prepare list
+ list = string();
+ ssets = soundman->GetSoundSets();
+ for( it = ssets->begin(); it != ssets->end(); it++ ){
+ // add a newline, when list is not empty
+ if( !list.empty())
+ list.append( "\n" );
+
+ if( strcasecmp( Option, "names" ) == 0 )
+ list.append( it->second->GetName());
+ else if( strcasecmp( Option, "all" ) == 0 )
+ list.append( it->first + " " + it->second->GetName());
+ else
+ list.append( it->first );
+ }
+
+ ReplyCode = 950;
+ return cString::sprintf("%s", list.c_str());
+ }
+
+ return NULL;
+}
+
+/*
+ * returns the SVDR help page(s)
+ */
+const char **cPluginSndctl::SVDRPHelpPages( void ){
+ return SVDRHelpPages;
+}
+
+/*
+ * return this plugins version
+ */
+const char *cPluginSndctl::Version( void ){
+ return VERSION;
+}
+
+
+/*
+bool cPluginSndctl::ProcessArgs(int argc, char *argv[])
+{
+ // Implement command line argument processing here if applicable.
+ return true;
+}
+
+bool cPluginSndctl::Initialize(void)
+{
+ // Initialize any background activities the plugin shall perform.
+ return true;
+}
+
+void cPluginSndctl::Housekeeping(void)
+{
+ // Perform any cleanup or other regular tasks.
+}
+
+void cPluginSndctl::MainThreadHook(void)
+{
+ // Perform actions in the context of the main program thread.
+ // WARNING: Use with great care - see PLUGINS.html!
+}
+
+cString cPluginSndctl::Active(void)
+{
+ // Return a message string if shutdown should be postponed
+ return NULL;
+}
+
+bool cPluginSndctl::Service(const char *Id, void *Data)
+{
+ // Handle custom service requests from other plugins
+ return false;
+}
+*/
+
+VDRPLUGINCREATOR(cPluginSndctl); // Don't touch this! (oops, not in my dreams...!)
diff --git a/sndctl.cbp b/sndctl.cbp
new file mode 100644
index 0000000..84c6bee
--- /dev/null
+++ b/sndctl.cbp
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+<CodeBlocks_project_file>
+ <FileVersion major="1" minor="6" />
+ <Project>
+ <Option title="sndctl" />
+ <Option makefile="/home/rainer/develop/vdr-1.6.0/PLUGINS/src/sndctl/Makefile" />
+ <Option makefile_is_custom="1" />
+ <Option pch_mode="2" />
+ <Option compiler="gcc" />
+ <Build>
+ <Target title="all">
+ <Option output="/home/rainer/develop/vdr-1.6.0/vdr" prefix_auto="1" extension_auto="1" />
+ <Option working_dir="/home/rainer/develop/vdr-1.6.0" />
+ <Option type="1" />
+ <Option compiler="gcc" />
+ <Option use_console_runner="0" />
+ <Option parameters=' -P sndctl -P &quot;xineliboutput --local=none --primary --remote=127.0.0.1:37890&quot;' />
+ <Option host_application="vdr" />
+ </Target>
+ </Build>
+ <VirtualTargets>
+ <Add alias="run" targets="all;" />
+ </VirtualTargets>
+ <Compiler>
+ <Add option="-Wall" />
+ </Compiler>
+ <Unit filename="alsa.c">
+ <Option compilerVar="CC" />
+ </Unit>
+ <Unit filename="alsa.h" />
+ <Unit filename="defaults.h" />
+ <Unit filename="i18n.c">
+ <Option compilerVar="CC" />
+ </Unit>
+ <Unit filename="i18n.h" />
+ <Unit filename="mainmenu.c">
+ <Option compilerVar="CC" />
+ </Unit>
+ <Unit filename="mainmenu.h" />
+ <Unit filename="menuitems.c">
+ <Option compilerVar="CC" />
+ </Unit>
+ <Unit filename="menuitems.h" />
+ <Unit filename="mixer.c">
+ <Option compilerVar="CC" />
+ </Unit>
+ <Unit filename="mixer.h" />
+ <Unit filename="setup.c">
+ <Option compilerVar="CC" />
+ </Unit>
+ <Unit filename="setup.h" />
+ <Unit filename="setupmenu.c">
+ <Option compilerVar="CC" />
+ </Unit>
+ <Unit filename="setupmenu.h" />
+ <Unit filename="sndctl.c">
+ <Option compilerVar="CC" />
+ </Unit>
+ <Unit filename="sndctl.h" />
+ <Unit filename="soundman.c">
+ <Option compilerVar="CC" />
+ </Unit>
+ <Unit filename="soundman.h" />
+ <Unit filename="soundset.c">
+ <Option compilerVar="CC" />
+ </Unit>
+ <Unit filename="soundset.h" />
+ <Unit filename="soundsetmenu.c">
+ <Option compilerVar="CC" />
+ </Unit>
+ <Unit filename="soundsetmenu.h" />
+ <Unit filename="status.c">
+ <Option compilerVar="CC" />
+ </Unit>
+ <Unit filename="status.h" />
+ <Extensions>
+ <code_completion />
+ <debugger />
+ </Extensions>
+ </Project>
+</CodeBlocks_project_file>
diff --git a/sndctl.h b/sndctl.h
new file mode 100644
index 0000000..3f33f85
--- /dev/null
+++ b/sndctl.h
@@ -0,0 +1,58 @@
+/*
+ * sndctl - a plugin for the Video Disk Recorder
+ * file: sndctl.h
+ * description: header file for sndctl.c
+ *
+ * author: Thomas Hildebrandt <toxym@web.de>
+ *
+ * inspired by and reengineered from 'avolctl'
+ * thanks to Martin Prochnow <nordlichtl@martins-kabuff.de>
+ */
+
+#ifndef SNDCTL_SNDCTL_H
+#define SNDCTL_SNDCTL_H
+
+#include "setup.h"
+#include "soundman.h"
+#include "status.h"
+
+/*
+ * cPluginSndctl
+ * plugin main class definition
+ */
+class cPluginSndctl : public cPlugin {
+ private:
+ cSetupSndctl *setup;
+ cSoundMan *soundman;
+ cStatusSndctl *statusMonitor;
+
+ public:
+ cPluginSndctl( void );
+ virtual ~cPluginSndctl();
+
+ virtual const char *CommandLineHelp( void );
+ virtual const char *Description( void );
+ cSoundMan *GetSoundManager( void );
+ cSetupSndctl *GetSetup( void );
+ virtual const char *MainMenuEntry( void );
+ virtual cOsdObject *MainMenuAction( void );
+ virtual cMenuSetupPage *SetupMenu( void );
+ virtual bool SetupParse( const char*, const char* );
+ virtual bool Start( void );
+ virtual void Stop( void );
+ void Store( string, string );
+ virtual cString SVDRPCommand( const char*, const char*, int& );
+ virtual const char **SVDRPHelpPages( void );
+ virtual const char *Version( void );
+
+/* virtual bool ProcessArgs(int argc, char *argv[]);
+ virtual bool Initialize(void);
+ virtual void Housekeeping(void);
+ virtual void MainThreadHook(void);
+ virtual cString Active(void);
+ virtual bool Service(const char *Id, void *Data = NULL);
+*/
+};
+
+#endif //SNDCTL_SNDCTL_H
+
diff --git a/sndctl.layout b/sndctl.layout
new file mode 100644
index 0000000..e2b6169
--- /dev/null
+++ b/sndctl.layout
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+<CodeBlocks_layout_file>
+ <ActiveTarget name="run" />
+ <File name="alsa.c" open="0" top="0" tabpos="0">
+ <Cursor position="1620" topLine="32" />
+ </File>
+ <File name="defaults.h" open="0" top="0" tabpos="0">
+ <Cursor position="80" topLine="0" />
+ </File>
+ <File name="mainmenu.c" open="0" top="0" tabpos="0">
+ <Cursor position="1728" topLine="68" />
+ </File>
+ <File name="menuitems.c" open="0" top="0" tabpos="0">
+ <Cursor position="2332" topLine="86" />
+ </File>
+ <File name="mixer.c" open="0" top="0" tabpos="0">
+ <Cursor position="647" topLine="15" />
+ </File>
+ <File name="mixer.h" open="0" top="0" tabpos="0">
+ <Cursor position="860" topLine="10" />
+ </File>
+ <File name="setupmenu.c" open="0" top="0" tabpos="0">
+ <Cursor position="3283" topLine="48" />
+ </File>
+ <File name="sndctl.c" open="0" top="0" tabpos="0">
+ <Cursor position="1476" topLine="42" />
+ </File>
+ <File name="sndctl.h" open="0" top="0" tabpos="0">
+ <Cursor position="592" topLine="16" />
+ </File>
+ <File name="soundman.c" open="0" top="0" tabpos="1">
+ <Cursor position="1000" topLine="19" />
+ </File>
+ <File name="soundman.h" open="0" top="0" tabpos="0">
+ <Cursor position="722" topLine="20" />
+ </File>
+ <File name="soundset.c" open="0" top="0" tabpos="0">
+ <Cursor position="1692" topLine="45" />
+ </File>
+ <File name="soundset.h" open="0" top="0" tabpos="0">
+ <Cursor position="697" topLine="11" />
+ </File>
+</CodeBlocks_layout_file>
diff --git a/soundman.c b/soundman.c
new file mode 100644
index 0000000..376218c
--- /dev/null
+++ b/soundman.c
@@ -0,0 +1,334 @@
+/*
+ * sndctl - a plugin for the Video Disk Recorder
+ * file: soundman.c
+ * description: manager for volume change requests
+ *
+ * author: Thomas Hildebrandt <toxym@web.de>
+ *
+ * inspired by and reengineered from 'avolctl'
+ * thanks to Martin Prochnow <nordlichtl@martins-kabuff.de>
+ */
+
+#include "defaults.h"
+#include "soundman.h"
+#include <stdio.h>
+
+/*********************************************************
+ * member functions for class cSoundMan
+ *********************************************************/
+
+/*
+ * constructor
+ */
+cSoundMan::cSoundMan( cPluginSndctl *Plugin ){
+ vector<string> controls;
+ vector<string>::iterator it;
+
+ dsyslog( "sndctl (cSoundMan::cSoundMan): cSoundMan created" );
+
+ plugin = Plugin;
+
+ // create mixer device
+ mixer = (cMixer*) new cAlsa();
+
+ // initialize variables
+ volume = volumeCounter = 0;
+ soundflash = false;
+}
+
+/*
+ * destructor
+ */
+cSoundMan::~cSoundMan(){
+ // remove objects
+ delete mixer;
+ if (!soundsets.empty()) {
+ map<string,cSoundSet*>::iterator it;
+ for( it = soundsets.begin(); it != soundsets.end(); it++ ) {
+ delete it->second;
+ }
+ }
+// free( &activeSoundSetID );
+}
+
+/*
+ * care for sound set switching, when audio track is changed
+ */
+void cSoundMan::AudioTrack( const char* Track ){
+ string sset;
+
+ dsyslog( "sndctl (cSoundMan::AudioTrack): Audio track switched to '%s'.", Track );
+
+ if( plugin->GetSetup()->GetBool( SNDCTL_SETUP_DD_AUTO_SWITCH )){
+ if( strstr( Track, "dolby" ) != NULL ||
+ strstr( Track, "Dolby" ) != NULL ||
+ strstr( Track, "DOLBY" ) != NULL )
+ // switch to DD sound set
+ sset = plugin->GetSetup()->Get( SNDCTL_SETUP_DD_AUTO_SSET );
+ else
+ // switch back to 'normal' sound set
+ sset = plugin->GetSetup()->Get( SNDCTL_SETUP_DEFAULT_SSET );
+
+ dsyslog( "sndctl (cSoundMan::AudioTrack): Doing DD auto switching to '%s'.", sset.c_str());
+ SetSoundSet( sset );
+ }
+}
+
+/*
+ * create a new, empty sound set
+ */
+string cSoundMan::CreateSoundSet(){
+ char rnd[6];
+ string id;
+
+ // initialize randomizer
+ srand( time( NULL ));
+
+ // create a new ID
+ do{
+ // create new random number (in range 1000 ~ 1999)
+ sprintf( rnd, "s%u", (int) ( 1000 + ((double) rand()) / RAND_MAX * 999 ));
+ id = rnd;
+ } while( soundsets.find( id ) != soundsets.end());
+
+ // create a new sound set
+ soundsets[id] = new cSoundSet( mixer, id );
+
+ dsyslog( "sndctl (cSoundMan::CreateSoundSet): New sound set with ID '%s' created.", id.c_str());
+
+ return id;
+}
+
+/*
+ * delete a sound set
+ */
+bool cSoundMan::DeleteSoundSet( string Id ){
+ string defSoundSet;
+ map<string,cSoundSet*>::iterator it;
+
+ if (soundsets.size() <= 1) {
+ esyslog( "sndctl (cSoundMan::DeleteSoundSet): Failed to delete sound set with ID '%s'. Cannot delete last one.", Id.c_str());
+ return false;
+ }
+ // trying to find the requested sound set
+ if(( it = soundsets.find( Id )) != soundsets.end()){
+ // remove sound set
+ free( it->second );
+ soundsets.erase( it );
+
+ isyslog( "sndctl (cSoundMan::DeleteSoundSet): Sound set with ID '%s' deleted.", Id.c_str());
+
+ // do something, when deleted sound set was the active one
+ if( Id == activeSoundSetID ){
+ defSoundSet = plugin->GetSetup()->Get( string( SNDCTL_SETUP_DEFAULT_SSET ));
+ if( Id != defSoundSet )
+ // currently deleted sound set was NOT the default one
+ // -> set active sound set to default
+ SetSoundSet( defSoundSet );
+ else{
+ // currently deleted sound set was the default one
+ // -> we need a new default one
+ SetSoundSet( defSoundSet = soundsets.begin()->first );
+ isyslog( "sndctl (cSoundMan::DeleteSoundSet): Deleted sound set was the default sound set." );
+ plugin->GetSetup()->Set( string( SNDCTL_SETUP_DEFAULT_SSET ), defSoundSet );
+ }
+
+ isyslog( "sndctl (cSoundMan::DeleteSoundSet): Active sound set to '%s'.", activeSoundSetID.c_str());
+ }
+
+ return true;
+ }
+
+ esyslog( "sndctl (cSoundMan::DeleteSoundSet): Failed to delete sound set with ID '%s'. Sound set not found.", Id.c_str());
+ return false;
+}
+
+/*
+ * returns the ID of the current active sound set
+ */
+string cSoundMan::GetCurrentSoundSetID( void ){
+ return activeSoundSetID;
+}
+
+/*
+ * returns a sound set object
+ */
+cSoundSet *cSoundMan::GetSoundSet( string Id, bool CreateNew ){
+ map<string,cSoundSet*>::iterator it;
+
+ // trying to find an already created sound set by its ID
+ if( soundsets.find( Id ) != soundsets.end())
+ return soundsets[Id];
+
+ // trying to find a sound set by its name
+ for( it = soundsets.begin(); it != soundsets.end(); it++ )
+ if( it->second->GetName() == Id )
+ return it->second;
+
+ // no sound set found, create a new one or return nothing
+ return CreateNew ? soundsets[Id] = new cSoundSet( mixer, Id ) : NULL;
+}
+
+/*
+ * returns the sound sets map
+ */
+map<string,cSoundSet*> *cSoundMan::GetSoundSets(){
+ return &soundsets;
+}
+
+/*
+ * checks and correct the default soundset ID
+ * (it's only done once at start)
+ */
+string cSoundMan::DefaultSoundSetID( string RequestedID ){
+ string id;
+
+ dsyslog( "sndctl (cSoundMan::DefaultSoundSetID): Trying to set default sound set to ID '%s'.", RequestedID.c_str());
+
+ if( soundsets.find( RequestedID ) != soundsets.end())
+ // 'RequestedID' found
+ id = RequestedID;
+
+ // if no soundset exists, we have to create a first one
+ else if( soundsets.empty())
+ id = CreateSoundSet();
+
+ // at least, lets take the first one
+ else
+ id = soundsets.begin()->first;
+
+ isyslog( "sndctl (cSoundMan::DefaultSoundSetID): Default sound set is '%s'.", id.c_str());
+
+ // now return the default sound set to store this value
+ return id;
+}
+
+/*
+ * returns the current volume value
+ */
+int cSoundMan::GetVolume( void ){
+ return volume;
+}
+
+/*
+ * sets the active sound set
+ */
+string cSoundMan::SetSoundSet( string Id ){
+ cSoundSet *sset;
+
+ // look for this ID
+ if( soundsets.find( Id ) == soundsets.end()){
+ // look for the name
+ if(( sset = GetSoundSet( Id, false )) == NULL ){
+ esyslog( "sndctl (cSoundMan::SetSoundSet): No sound set with ID or name '%s' found.", Id.c_str());
+ return string( "~" );
+ } else {
+ // 'Id' contains a sound set name, but we need an ID
+ activeSoundSetID = sset->GetID();
+ }
+ } else
+ activeSoundSetID = Id;
+
+ isyslog( "sndctl (cSoundMan::SetSoundSet): Active sound set is now '%s'.", activeSoundSetID.c_str());
+
+ // update mixer with sound set parameters
+ mixer->Update( GetSoundSet( activeSoundSetID ));
+
+ // set volume for new sound set
+ SetVolume();
+
+ return activeSoundSetID;
+}
+
+/*
+ * doing a sound flash
+ */
+void cSoundMan::SoundFlash( void ){
+ int vol;
+
+ isyslog( "sndctl (cSoundMan::SoundFlash): Igniting sound flash." );
+
+ // mark sound flashing in progress
+ soundflash = true;
+
+ // save volume
+ vol = volume;
+
+ // volume up
+ cDevice::PrimaryDevice()->SetVolume( plugin->GetSetup()->GetInt( SNDCTL_SETUP_SOUNDFLASH_VOL ) / 100 * 255, true);
+
+ // wait
+ sleep( plugin->GetSetup()->GetInt( SNDCTL_SETUP_SOUNDFLASH_TIME ));
+
+ // change volume back to former value
+ cDevice::PrimaryDevice()->SetVolume( vol, true);
+
+ // sound flashing is done
+ soundflash = false;
+}
+
+/*
+ * set the current volume level
+ * (and checks the limits of 0..255)
+ */
+int cSoundMan::SetVolume( int Volume ){
+ int initVol;
+
+ // time for a sound flash?
+ if( plugin->GetSetup()->GetBool( SNDCTL_SETUP_SOUNDFLASH ) &&
+ !soundflash &&
+ lastWasUp &&
+ Volume < volume &&
+ ( time( NULL ) - lastSet ) < plugin->GetSetup()->GetInt( SNDCTL_SETUP_SOUNDFLASH_DELTA )){
+ SoundFlash();
+ return volume;
+ }
+
+ // save time and direction of last volume setting
+ time( &lastSet );
+ lastWasUp = Volume > volume;
+
+ // save volume
+ volume = Volume < 0 ? volume : Volume;
+
+ // care for initial volume?
+ if( volumeCounter < 2 && plugin->GetSetup()->GetInt( SNDCTL_SETUP_INIT_VOLUME ) >= 0 ){
+ volumeCounter++;
+ initVol = plugin->GetSetup()->GetInt( SNDCTL_SETUP_INIT_VOLUME );
+ dsyslog( "sndctl (cSoundMan::SetVolume): Using initial volume %d%%", initVol );
+ GetSoundSet( activeSoundSetID )->Volume( initVol );
+ return initVol / 100 * 255;
+ }
+
+ // work with an initial volume?
+ if( plugin->GetSetup()->GetInt( SNDCTL_SETUP_INIT_VOLUME ) < 0 )
+ // no initial volume, save given value
+ volume = Volume < 0 ? volume : Volume;
+ else
+ // initial volume set, count this event
+ volumeCounter++;
+
+ // check limits
+ volume = volume < 0 ? 0 : volume;
+ volume = volume > 255 ? 255 : volume;
+
+ isyslog( "sndctl (cSoundMan::SetVolume): Volume goes to %d (of 255).", volume );
+
+ // set volume to current soundset
+ GetSoundSet( activeSoundSetID )->Volume( 100 * volume / 255 );
+
+ return volume;
+}
+
+/*
+ * store all soundsets
+ */
+bool cSoundMan::Store( cPluginSndctl *Plugin ){
+ map<string,cSoundSet*>::iterator it;
+
+ // iterate over all soundsets
+ for( it = soundsets.begin(); it != soundsets.end(); it++ )
+ it->second->Store( it->first, Plugin );
+
+ return true;
+}
diff --git a/soundman.h b/soundman.h
new file mode 100644
index 0000000..320ac39
--- /dev/null
+++ b/soundman.h
@@ -0,0 +1,54 @@
+/*
+ * sndctl - a plugin for the Video Disk Recorder
+ * file: soundman.h
+ * description: header file for soundman.c
+ *
+ * author: Thomas Hildebrandt <toxym@web.de>
+ *
+ * inspired by and reengineered from 'avolctl'
+ * thanks to Martin Prochnow <nordlichtl@martins-kabuff.de>
+ */
+
+#ifndef SNDCTL_SOUNDMAN_H
+#define SNDCTL_SOUNDMAN_H
+
+#include "alsa.h"
+#include "sndctl.h"
+#include "soundset.h"
+
+/*
+ * cSoundMan
+ * sound manager class definition
+ */
+class cSoundMan {
+ private:
+ string activeSoundSetID;
+ time_t lastSet;
+ bool lastWasUp;
+ cMixer *mixer;
+ cPluginSndctl *plugin;
+ bool soundflash;
+ map<string,cSoundSet*> soundsets;
+ int volume; // this is the VDR volume, 0..255
+ int volumeCounter;
+
+ void SoundFlash( void );
+
+ public:
+ cSoundMan( cPluginSndctl* );
+ ~cSoundMan();
+
+ void AudioTrack( const char* );
+ string CreateSoundSet( void );
+ string DefaultSoundSetID( string );
+ bool DeleteSoundSet( string );
+ string GetCurrentSoundSetID( void );
+ cSoundSet *GetSoundSet( string, bool = true );
+ map<string,cSoundSet*> *GetSoundSets( void );
+ int GetVolume( void );
+ string SetSoundSet( string );
+ int SetVolume( int = -1 );
+ bool Store( cPluginSndctl* );
+};
+
+#endif //SNDCTL_SOUNDMAN_H
diff --git a/soundset.c b/soundset.c
new file mode 100644
index 0000000..640d0ae
--- /dev/null
+++ b/soundset.c
@@ -0,0 +1,207 @@
+/*
+ * sndctl - a plugin for the Video Disk Recorder
+ * file: soundset.c
+ * description: representation of a soundset
+ *
+ * author: Thomas Hildebrandt <toxym@web.de>
+ *
+ * inspired by and reengineered from 'avolctl'
+ * thanks to Martin Prochnow <nordlichtl@martins-kabuff.de>
+ */
+
+#include "defaults.h"
+#include "soundset.h"
+
+/*********************************************************
+ * member functions for class cSoundSet
+ *********************************************************/
+
+/*
+ * constructors
+ */
+cSoundSet::cSoundSet( cMixer *Mixer, string Id ){
+ int i = 0;
+ vector<cControlId> ctrls;
+ vector<cControlId>::iterator it;
+
+ dsyslog( "sndctl (cSoundSet::cSoundSet): New sound set created" );
+
+ // initial value for volume
+ volume = 0;
+
+ // save mixer class
+ mixer = Mixer;
+
+ // save my ID
+ id = Id;
+
+ // set default name
+ name = tr( SNDCTL_DEFAULT_SOUNDSET_NAME );
+
+ // initialize control array
+ if(( nrOfControls = mixer->CountControls()) > 0 )
+ if( controls = new struct control[nrOfControls] ){
+ // fill controls array with values
+ ctrls = GetControls();
+ for( it = ctrls.begin(); it != ctrls.end(); it++ ){
+ (controls + i)->control = (*it);
+ strncpy((controls + i)->value, "~", SNDCTL_MAX_LEN_CONTROL_VAL );
+ i++;
+ }
+ }else
+ esyslog( "sndctl (cSoundSet::cSoundSet): ERROR! Can't allocate memory for '%d' controls.", nrOfControls );
+ else
+ esyslog( "sndctl (cSoundSet::cSoundSet): WARNING! No controls found. Sound set is not initialized!" );
+}
+
+/*
+ * destructor
+ */
+cSoundSet::~cSoundSet(){
+ // remove mixers
+ //delete &mixers;
+ delete [] controls;
+}
+
+/*
+ * returns the value for a control
+ */
+string cSoundSet::Get( cControlId Control){
+ int i;
+ string result;
+
+ result = string( "~" );
+
+ // search for this control
+ for( i = 0; i < nrOfControls; i++ ) {
+ if( (controls + i)->control == Control){
+ result = string((controls + i)->value );
+ break;
+ }
+ }
+
+ dsyslog( "sndctl (cSoundSet::Get): Value for '%s' requested, returning '%s'", Control.getDisplayName().c_str(), result.c_str());
+
+ // nothing found, return default value
+ return result;
+}
+
+/*
+ * returns a vector with all controls
+ */
+vector<cControlId> cSoundSet::GetControls( void ){
+ return mixer->GetControls();
+}
+
+/*
+ * returns the ID of this sound set
+ */
+string cSoundSet::GetID( void ){
+ return id;
+}
+
+/*
+ * returns the name of this sound set
+ */
+string cSoundSet::GetName( void ){
+ return name;
+}
+
+/*
+ * set a parameter for this soundset
+ */
+bool cSoundSet::Set(string Name, string Value ){
+ int i;
+
+ dsyslog( "sndctl (cSoundSet::Set): Trying to set '%s' to '%s'.", Name.c_str(), Value.c_str());
+
+ // name?
+ if( Name == "name" ){
+ name = Value;
+ dsyslog( "sndctl (cSoundSet::Set): Sound set was renamed to '%s'.", name.c_str());
+ return true;
+ }
+
+ // a known control?
+ int pos = 0;
+ if ((pos = Name.find_first_of("@")) != string::npos) {
+ string soundCardId = Name.substr(0, pos);
+ string name = Name.substr(pos+1);
+ int mixerNr = atoi(name.c_str());
+ cControlId id(soundCardId, string(""), mixerNr);
+ for( i = 0; i < nrOfControls; i++ ){
+ if( (controls + i)->control == id){
+ // save value
+ strncpy((controls + i)->value, Value.c_str(), SNDCTL_MAX_LEN_CONTROL_VAL );
+ dsyslog( "sndctl (cSoundSet::Set): Control '%s@%s' in sound set '%s' is now '%s'.",
+ (controls + i)->control.getSoundCardId().c_str(),
+ (controls + i)->control.getDisplayName().c_str(),
+ name.c_str(),
+ Get( id ).c_str());
+
+ mixer->Update( this );
+ return true;
+ }
+ };
+ }
+
+ // nothing of all
+ return false;
+}
+
+bool cSoundSet::Set(cControlId ControlId, string Value ){
+ int i;
+
+ dsyslog( "sndctl (cSoundSet::Set): Trying to set '%s'@'%s' to '%s'.", ControlId.getSoundCardId().c_str(), ControlId.getDisplayName().c_str(), Value.c_str());
+ for( i = 0; i < nrOfControls; i++ ){
+ if( (controls + i)->control == ControlId){
+ // save value
+ strncpy((controls + i)->value, Value.c_str(), SNDCTL_MAX_LEN_CONTROL_VAL );
+ dsyslog( "sndctl (cSoundSet::Set): Control '%s@%s' in sound set '%s' is now '%s'.",
+ (controls + i)->control.getSoundCardId().c_str(),
+ (controls + i)->control.getDisplayName().c_str(),
+ name.c_str(),
+ Get( ControlId ).c_str());
+
+ mixer->Update( this );
+ return true;
+ }
+ }
+
+ // nothing of all
+ return false;
+}
+
+/*
+ * store this sound sets settings
+ */
+bool cSoundSet::Store( string Id, cPluginSndctl *Plugin ){
+ int i;
+ char buf[64];
+
+ // save my name
+ Plugin->Store( Id + "_name", name );
+
+ // save control settings
+ for( i = 0; i < nrOfControls; i++ ) {
+ if( strcmp((controls + i)->value, "~" )) {
+ sprintf(buf, "%d", (controls + i)->control.getControlNr());
+ Plugin->Store( Id + "_" + (controls + i)->control.getSoundCardId()+"@"+buf, (controls + i)->value );
+ }
+ }
+
+ return true;
+}
+
+/*
+ * sets the volume of this sound set
+ * Vol range is here 0..100
+ */
+void cSoundSet::Volume( int Vol ){
+ // save last set volume
+ volume = Vol < 0 ? volume : Vol;
+
+ isyslog( "sndctl (cSoundSet::Volume): Volume for soundset '%s' goes to %d%%.", name.c_str(), volume );
+ mixer->Volume( volume );
+}
+
diff --git a/soundset.h b/soundset.h
new file mode 100644
index 0000000..6f5d4fd
--- /dev/null
+++ b/soundset.h
@@ -0,0 +1,48 @@
+/*
+ * sndctl - a plugin for the Video Disk Recorder
+ * file: soundset.h
+ * description: header file for soundset.c
+ *
+ * author: Thomas Hildebrandt <toxym@web.de>
+ *
+ * inspired by and reengineered from 'avolctl'
+ * thanks to Martin Prochnow <nordlichtl@martins-kabuff.de>
+ */
+
+#ifndef SNDCTL_SOUNDSET_H
+#define SNDCTL_SOUNDSET_H
+
+#include "sndctl.h"
+#include "mixer.h"
+
+/*
+ * cSoundSet
+ * sound set representation class definition
+ */
+class cSoundSet {
+ private:
+ struct control{
+ cControlId control;
+ char value[SNDCTL_MAX_LEN_CONTROL_VAL];
+ } *controls;
+ string id;
+ cMixer *mixer;
+ string name;
+ int nrOfControls;
+ int volume;
+
+ public:
+ cSoundSet( cMixer*, string );
+ ~cSoundSet();
+
+ string Get( cControlId ControlId );
+ string GetID( void );
+ string GetName( void );
+ bool Set( string, string );
+ bool Set(cControlId ControlId, string Value );
+ bool Store( string, cPluginSndctl* );
+ void Volume( int = -1 );
+ vector<cControlId> GetControls( void );
+};
+
+#endif //SNDCTL_SOUNDSET_H
diff --git a/soundsetmenu.c b/soundsetmenu.c
new file mode 100644
index 0000000..fc6ee30
--- /dev/null
+++ b/soundsetmenu.c
@@ -0,0 +1,71 @@
+/*
+ * sndctl - a plugin for the Video Disk Recorder
+ * file: soundsetmenu.c
+ * description: the sound set editor menu
+ *
+ * author: Thomas Hildebrandt <toxym@web.de>
+ *
+ * inspired by and reengineered from 'avolctl'
+ * thanks to Martin Prochnow <nordlichtl@martins-kabuff.de>
+ */
+
+#include "defaults.h"
+#include "soundsetmenu.h"
+#include "menuitems.h"
+#include "soundset.h"
+
+/*********************************************************
+ * member functions for class cSoundsetMenuSndctl
+ *********************************************************/
+
+/*
+ * constructors
+ */
+cSoundsetMenuSndctl::cSoundsetMenuSndctl( cSoundSet *SoundSet, string ActiveSoundSet ){
+ string title;
+
+ // save parameter
+ soundset = SoundSet;
+ isActive = soundset->GetID() == ActiveSoundSet;
+
+ // set titel
+ title = string( tr(SNDCTL_TXT_0006));
+ title.append( " - " );
+ title.append( soundset->GetName());
+ SetTitle( title.c_str());
+
+ // get initial values
+ name = strdup( soundset->GetName().c_str());
+
+ // add content
+ Set();
+}
+
+/*
+ * makes setup menu entries
+ */
+void cSoundsetMenuSndctl::Set( void ){
+ vector<cControlId> controls;
+ vector<cControlId>::iterator it;
+
+ // sound set name
+ Add( new cMenuEditStrItem( tr( SNDCTL_TXT_0008 ), name, 20, tr(FileNameChars)));
+
+ // one entry for every control
+ controls = soundset->GetControls();
+ cControlId *lastId = NULL;
+ for( it = controls.begin(); it != controls.end(); it++ ) {
+ if (lastId == NULL || lastId->getSoundCardId() != it->getSoundCardId())
+ Add (new cOsdItem(it->getSoundCardId().c_str(), osUnknown, false));
+ lastId = it.base();
+ Add( new cSoundSetControlItem( soundset, *it, isActive ));
+ }
+}
+
+/*
+ * stores changed values
+ */
+void cSoundsetMenuSndctl::Store( void ){
+ // name
+ soundset->Set( string( "name" ), string( name ));
+}
diff --git a/soundsetmenu.h b/soundsetmenu.h
new file mode 100644
index 0000000..69c242b
--- /dev/null
+++ b/soundsetmenu.h
@@ -0,0 +1,34 @@
+/*
+ * sndctl - a plugin for the Video Disk Recorder
+ * file: soundsetmenu.h
+ * description: header file for soundsetmenu.c
+ *
+ * author: Thomas Hildebrandt <toxym@web.de>
+ *
+ * inspired by and reengineered from 'avolctl'
+ * thanks to Martin Prochnow <nordlichtl@martins-kabuff.de>
+ */
+
+#ifndef SNDCTL_SOUNDSETMENU_H
+#define SNDCTL_SOUNDSETMENU_H
+
+/*
+ * cSoundsetMenuSndctl
+ * the sound set editor menu
+ */
+class cSoundsetMenuSndctl:public cMenuSetupPage {
+ private:
+ bool isActive;
+ char *name;
+ cSoundSet *soundset;
+
+ void Set();
+
+ protected:
+ virtual void Store( void );
+
+ public:
+ cSoundsetMenuSndctl( cSoundSet*, string );
+};
+
+#endif //SNDCTL_SOUNDSETMENU_H
diff --git a/status.c b/status.c
new file mode 100644
index 0000000..969fdaf
--- /dev/null
+++ b/status.c
@@ -0,0 +1,47 @@
+/*
+ * sndctl - a plugin for the Video Disk Recorder
+ * file: status.c
+ * description: status monitor to get informations from VDR
+ *
+ * author: Thomas Hildebrandt <toxym@web.de>
+ *
+ * inspired by and reengineered from 'avolctl'
+ * thanks to Martin Prochnow <nordlichtl@martins-kabuff.de>
+ */
+
+#include "defaults.h"
+#include "status.h"
+
+/*********************************************************
+ * member functions for class cStatusSndctl
+ *********************************************************/
+
+/*
+ * constructor
+ */
+cStatusSndctl::cStatusSndctl( const cSoundMan *Soundman ){
+ // save sound manager pointer
+ soundman = (cSoundMan*) Soundman;
+}
+
+void cStatusSndctl::SetAudioTrack( int Index, const char * const *Tracks ){
+ isyslog( "sndctl (cStatusSndctl::SetAudioTrack): Audio track switched to '%s'.", Tracks[Index] );
+
+ // inform sound manager
+ soundman->AudioTrack( Tracks[Index] );
+}
+
+/*
+ * the volume has been set to the given value, either
+ * absolutely or relative to the current volume.
+ */
+void cStatusSndctl::SetVolume( int Volume, bool Absolute ){
+
+ isyslog( "sndctl (cStatusSndctl::SetVolume): Received volume from VDR: '%d'.", Volume );
+
+ // set current volume
+ if( Absolute )
+ soundman->SetVolume( Volume );
+ else
+ soundman->SetVolume( soundman->GetVolume() + Volume );
+}
diff --git a/status.h b/status.h
new file mode 100644
index 0000000..3ab7fb0
--- /dev/null
+++ b/status.h
@@ -0,0 +1,34 @@
+/*
+ * sndctl - a plugin for the Video Disk Recorder
+ * file: status.h
+ * description: header file for status.c
+ *
+ * author: Thomas Hildebrandt <toxym@web.de>
+ *
+ * inspired by and reengineered from 'avolctl'
+ * thanks to Martin Prochnow <nordlichtl@martins-kabuff.de>
+ */
+
+#ifndef SNDCTL_STATUS_H
+#define SNDCTL_STATUS_H
+
+#include <vdr/status.h>
+#include "soundman.h"
+
+/*
+ * cStatusSndctl
+ * status monitor class definition
+ */
+class cStatusSndctl : public cStatus {
+ private:
+ cSoundMan *soundman;
+
+ protected:
+ virtual void SetAudioTrack( int, const char* const* );
+ virtual void SetVolume( int, bool );
+
+ public:
+ cStatusSndctl( const cSoundMan *Soundman );
+};
+
+#endif //SNDCTL_STATUS_H