From 201670d0adaf587758bbbad0b03f4e5431a70e61 Mon Sep 17 00:00:00 2001 From: Rainer Blickle Date: Fri, 13 Aug 2010 21:05:19 +0200 Subject: Initial import --- COPYING | 340 ++++++++++++++++++++++++++++++++++++++++++++++ HISTORY | 33 +++++ INSTALL | 33 +++++ INSTALL.de | 33 +++++ Makefile | 71 ++++++++++ README | 213 +++++++++++++++++++++++++++++ README.de | 223 +++++++++++++++++++++++++++++++ TODO | 19 +++ alsa.c | 201 ++++++++++++++++++++++++++++ alsa.h | 43 ++++++ defaults.h | 72 ++++++++++ i18n.c | 416 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ i18n.h | 34 +++++ mainmenu.c | 132 ++++++++++++++++++ mainmenu.h | 33 +++++ menuitems.c | 165 +++++++++++++++++++++++ menuitems.h | 73 ++++++++++ mixer.c | 176 ++++++++++++++++++++++++ mixer.h | 66 +++++++++ setup.c | 135 +++++++++++++++++++ setup.h | 36 +++++ setupmenu.c | 105 +++++++++++++++ setupmenu.h | 40 ++++++ sndctl.c | 283 +++++++++++++++++++++++++++++++++++++++ sndctl.cbp | 81 +++++++++++ sndctl.h | 58 ++++++++ sndctl.layout | 43 ++++++ soundman.c | 334 +++++++++++++++++++++++++++++++++++++++++++++ soundman.h | 54 ++++++++ soundset.c | 207 ++++++++++++++++++++++++++++ soundset.h | 48 +++++++ soundsetmenu.c | 71 ++++++++++ soundsetmenu.h | 34 +++++ status.c | 47 +++++++ status.h | 34 +++++ 35 files changed, 3986 insertions(+) create mode 100644 COPYING create mode 100644 HISTORY create mode 100644 INSTALL create mode 100644 INSTALL.de create mode 100644 Makefile create mode 100644 README create mode 100644 README.de create mode 100644 TODO create mode 100644 alsa.c create mode 100644 alsa.h create mode 100644 defaults.h create mode 100644 i18n.c create mode 100644 i18n.h create mode 100644 mainmenu.c create mode 100644 mainmenu.h create mode 100644 menuitems.c create mode 100644 menuitems.h create mode 100644 mixer.c create mode 100644 mixer.h create mode 100644 setup.c create mode 100644 setup.h create mode 100644 setupmenu.c create mode 100644 setupmenu.h create mode 100644 sndctl.c create mode 100644 sndctl.cbp create mode 100644 sndctl.h create mode 100644 sndctl.layout create mode 100644 soundman.c create mode 100644 soundman.h create mode 100644 soundset.c create mode 100644 soundset.h create mode 100644 soundsetmenu.c create mode 100644 soundsetmenu.h create mode 100644 status.c create mode 100644 status.h 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. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 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. + + , 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 +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 +Further development: Rainer Blicke +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 +Weiterentwicklung: Rainer Blicke +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 + +# 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 +Further development: Rainer Blicke +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 +Weiterentwicklung: Rainer Blicke +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 +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 + * + * inspired by and reengineered from 'avolctl' + * thanks to Martin Prochnow + */ + +#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(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 + * + * inspired by and reengineered from 'avolctl' + * thanks to Martin Prochnow + */ + +#ifndef SNDCTL_ALSA_H +#define SNDCTL_ALSA_H + +#include +#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 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 + * + * inspired by and reengineered from 'avolctl' + * thanks to Martin Prochnow + */ + +#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 +#include +#include +#include +#include +#include +#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 + * + * inspired by and reengineered from 'avolctl' + * thanks to Martin Prochnow + */ + +#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 + * + * inspired by and reengineered from 'avolctl' + * thanks to Martin Prochnow + */ + +#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 + * + * inspired by and reengineered from 'avolctl' + * thanks to Martin Prochnow + */ + +#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::iterator it; + map *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 + * + * inspired by and reengineered from 'avolctl' + * thanks to Martin Prochnow + */ + +#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 + * + * inspired by and reengineered from 'avolctl' + * thanks to Martin Prochnow + */ + +#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 *soundsets; + map::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 + * + * inspired by and reengineered from 'avolctl' + * thanks to Martin Prochnow + */ + +#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 + * + * inspired by and reengineered from 'avolctl' + * thanks to Martin Prochnow + */ + +#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 cMixer::GetControls( void ){ + vector 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 + * + * inspired by and reengineered from 'avolctl' + * thanks to Martin Prochnow + */ + +#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 vControls; + + virtual void Volumes( void ) = 0; + + public: + + cMixer(); + virtual ~cMixer(); + + vector 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 + * + * inspired by and reengineered from 'avolctl' + * thanks to Martin Prochnow + */ + +#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::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 + * + * inspired by and reengineered from 'avolctl' + * thanks to Martin Prochnow + */ + +#ifndef SNDCTL_SETUP_H +#define SNDCTL_SETUP_H + +#include "sndctl.h" + +/* + * cSetupSndctl + * setup class definition + */ +class cSetupSndctl { + private: + map items; + vector 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 + * + * inspired by and reengineered from 'avolctl' + * thanks to Martin Prochnow + */ + +#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 + * + * inspired by and reengineered from 'avolctl' + * thanks to Martin Prochnow + */ + +#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 + * + * inspired by and reengineered from 'avolctl' + * thanks to Martin Prochnow + */ + +#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::iterator it; + string list; + map *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 @@ + + + + + + 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 + * + * inspired by and reengineered from 'avolctl' + * thanks to Martin Prochnow + */ + +#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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + * + * inspired by and reengineered from 'avolctl' + * thanks to Martin Prochnow + */ + +#include "defaults.h" +#include "soundman.h" +#include + +/********************************************************* + * member functions for class cSoundMan + *********************************************************/ + +/* + * constructor + */ +cSoundMan::cSoundMan( cPluginSndctl *Plugin ){ + vector controls; + vector::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::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::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::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 *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::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 + * + * inspired by and reengineered from 'avolctl' + * thanks to Martin Prochnow + */ + +#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 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 *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 + * + * inspired by and reengineered from 'avolctl' + * thanks to Martin Prochnow + */ + +#include "defaults.h" +#include "soundset.h" + +/********************************************************* + * member functions for class cSoundSet + *********************************************************/ + +/* + * constructors + */ +cSoundSet::cSoundSet( cMixer *Mixer, string Id ){ + int i = 0; + vector ctrls; + vector::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 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 + * + * inspired by and reengineered from 'avolctl' + * thanks to Martin Prochnow + */ + +#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 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 + * + * inspired by and reengineered from 'avolctl' + * thanks to Martin Prochnow + */ + +#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 controls; + vector::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 + * + * inspired by and reengineered from 'avolctl' + * thanks to Martin Prochnow + */ + +#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 + * + * inspired by and reengineered from 'avolctl' + * thanks to Martin Prochnow + */ + +#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 + * + * inspired by and reengineered from 'avolctl' + * thanks to Martin Prochnow + */ + +#ifndef SNDCTL_STATUS_H +#define SNDCTL_STATUS_H + +#include +#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 -- cgit v1.2.3