summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorworo <woro@e10066b5-e1e2-0310-b819-94efdf66514b>2008-04-11 20:11:00 +0000
committerworo <woro@e10066b5-e1e2-0310-b819-94efdf66514b>2008-04-11 20:11:00 +0000
commit57d7ee812c8be1bd118471512682cb3393ce595f (patch)
tree2e0d792fc74eda1661b439853c004e2ed5c06820
parentb2550195d6b61f9be3470679082b8d6b5159aa83 (diff)
downloadvdr-plugin-muggle-57d7ee812c8be1bd118471512682cb3393ce595f.tar.gz
vdr-plugin-muggle-57d7ee812c8be1bd118471512682cb3393ce595f.tar.bz2
merge mp3ng branch into trunk
git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@1173 e10066b5-e1e2-0310-b819-94efdf66514b
-rw-r--r--Doxyfile899
-rw-r--r--HISTORY106
-rw-r--r--Makefile38
-rw-r--r--Makefile.music240
-rw-r--r--README58
-rw-r--r--README.postgresql5
-rw-r--r--bitmap.c184
-rw-r--r--bitmap.h101
-rw-r--r--common.h64
-rw-r--r--config.h60
-rw-r--r--imagecache.c5
-rw-r--r--imagecache.h102
-rw-r--r--lyrics.c143
-rw-r--r--lyrics.h28
-rw-r--r--menu-async.h44
-rw-r--r--mg_db.c1080
-rw-r--r--mg_db.h255
-rw-r--r--mg_db_gd_mysql.c846
-rw-r--r--mg_db_gd_mysql.h77
-rw-r--r--mg_db_gd_pg.c404
-rw-r--r--mg_db_gd_pg.h66
-rw-r--r--mg_db_gd_sqlite.c416
-rw-r--r--mg_db_gd_sqlite.h62
-rw-r--r--mg_image_provider.c820
-rw-r--r--mg_image_provider.h133
-rw-r--r--mg_incremental_search.c103
-rw-r--r--mg_incremental_search.h21
-rw-r--r--mg_item.c95
-rw-r--r--mg_item.h87
-rw-r--r--mg_item_gd.c321
-rw-r--r--mg_item_gd.h99
-rw-r--r--mg_keytypes.h9
-rw-r--r--mg_listitem.c26
-rw-r--r--mg_listitem.h2
-rw-r--r--mg_menu.c502
-rw-r--r--mg_menu.h234
-rw-r--r--mg_playcommands.c54
-rw-r--r--mg_playcommands.h37
-rw-r--r--mg_sel_gd.c226
-rw-r--r--mg_sel_gd.h52
-rw-r--r--mg_selection.c1115
-rw-r--r--mg_selection.h763
-rw-r--r--mg_setup.c400
-rw-r--r--mg_setup.h76
-rw-r--r--mg_skin.c245
-rw-r--r--mg_skin.h77
-rw-r--r--mg_tables.h783
-rw-r--r--mg_thread_sync.c75
-rw-r--r--mg_thread_sync.h29
-rw-r--r--mg_tools.c233
-rw-r--r--mg_tools.h27
-rw-r--r--mg_valmap.c29
-rw-r--r--mg_valmap.h21
-rw-r--r--muggle.c214
-rw-r--r--muggle.h30
-rw-r--r--mugglei.c114
-rw-r--r--pcmplayer.c743
-rw-r--r--pcmplayer.h235
-rw-r--r--po/de.po213
-rw-r--r--po/fr.po239
-rw-r--r--po/muggle.pot181
-rw-r--r--quantize.c387
-rw-r--r--quantize.h70
-rw-r--r--scripts/COPYRIGHT4
-rwxr-xr-xscripts/googlyrics409
-rw-r--r--scripts/googlyrics.diff49
-rwxr-xr-xscripts/muggle-image-convert42
-rwxr-xr-xscripts/muggle_getlyrics35
-rw-r--r--service.h42
-rw-r--r--setup.h26
-rw-r--r--symbols/copy.xpm29
-rw-r--r--symbols/delstar.xpm20
-rw-r--r--symbols/fwd.xpm29
-rw-r--r--symbols/insert.xpm29
-rw-r--r--symbols/loop.xpm29
-rw-r--r--symbols/loopall.xpm29
-rw-r--r--symbols/pause.xpm29
-rw-r--r--symbols/play.xpm29
-rw-r--r--symbols/rate00.xpm20
-rw-r--r--symbols/rate05.xpm20
-rw-r--r--symbols/rate10.xpm21
-rw-r--r--symbols/rate15.xpm21
-rw-r--r--symbols/rate20.xpm21
-rw-r--r--symbols/rate25.xpm21
-rw-r--r--symbols/rate30.xpm21
-rw-r--r--symbols/rate35.xpm21
-rw-r--r--symbols/rate40.xpm21
-rw-r--r--symbols/rate45.xpm21
-rw-r--r--symbols/rate50.xpm21
-rw-r--r--symbols/rec.xpm29
-rw-r--r--symbols/rew.xpm29
-rw-r--r--symbols/shuffle.xpm29
-rw-r--r--symbols/stop.xpm29
-rw-r--r--vdr_actions.c1464
-rw-r--r--vdr_actions.h216
-rw-r--r--vdr_config.h3
-rw-r--r--vdr_decoder.c236
-rw-r--r--vdr_decoder.h167
-rw-r--r--vdr_decoder_flac.c572
-rw-r--r--vdr_decoder_flac.h77
-rw-r--r--vdr_decoder_mp3.c654
-rw-r--r--vdr_decoder_mp3.h108
-rw-r--r--vdr_decoder_ogg.c638
-rw-r--r--vdr_decoder_ogg.h40
-rw-r--r--vdr_decoder_sndfile.c518
-rw-r--r--vdr_decoder_sndfile.h93
-rw-r--r--vdr_menu.c1562
-rw-r--r--vdr_menu.h563
-rw-r--r--vdr_network.h53
-rw-r--r--vdr_player.c2872
-rw-r--r--vdr_player.h335
-rw-r--r--vdr_setup.c160
-rw-r--r--vdr_setup.h9
-rw-r--r--vdr_sound.c1054
-rw-r--r--vdr_stream.c518
-rw-r--r--vdr_stream.h61
-rw-r--r--version.h27
117 files changed, 16135 insertions, 11443 deletions
diff --git a/Doxyfile b/Doxyfile
new file mode 100644
index 0000000..cdd7ec8
--- /dev/null
+++ b/Doxyfile
@@ -0,0 +1,899 @@
+# Doxyfile 1.2.13.1
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# General configuration options
+#---------------------------------------------------------------------------
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = Music
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER = <will be set by the Makefile>
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = srcdoc
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Brazilian, Chinese, Croatian, Czech, Danish, Dutch, Finnish, French,
+# German, Greek, Hungarian, Italian, Japanese, Korean, Norwegian, Polish,
+# Portuguese, Romanian, Russian, Slovak, Slovene, Spanish and Swedish.
+
+OUTPUT_LANGUAGE = German
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = YES
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these class will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = YES
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited
+# members of a class in the documentation of that class as if those members were
+# ordinary class members. Constructors, destructors and assignment operators of
+# the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. It is allowed to use relative paths in the argument list.
+
+STRIP_FROM_PATH =
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower case letters. If set to YES upper case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# users are adviced to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like the Qt-style comments (thus requiring an
+# explict @brief command for a brief description.
+
+JAVADOC_AUTOBRIEF = YES
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# reimplements.
+
+INHERIT_DOCS = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 8
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consist of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C.
+# For instance some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+##kls
+OPTIMIZE_OUTPUT_FOR_C = NO
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text.
+
+WARN_FORMAT =
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = .
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp
+# *.h++ *.idl
+
+FILE_PATTERNS = *.c *.h
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE =
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+
+EXCLUDE_PATTERNS = */RCS/* */OLD/*
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+
+INPUT_FILTER =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse.
+
+FILTER_SOURCE_FILES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+
+SOURCE_BROWSER = YES
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES (the default)
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES (the default)
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = YES
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX = c
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT =
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet
+
+HTML_STYLESHEET =
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compressed HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the Html help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
+# generated containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript and frames is required (for instance Mozilla, Netscape 4.0+,
+# or Internet explorer 4.0+). Note that for large projects the tree generation
+# can take a very long time. In such cases it is better to disable this feature.
+# Windows users are probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = YES
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT =
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimised for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT =
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assigments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT =
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION =
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_XML = NO
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_PREDEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_PREDEF_ONLY tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line and do not end with a semicolon. Such function macros are typically
+# used for boiler-plate code, and will confuse the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::addtions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tagfiles.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in Html, RTF and LaTeX) for classes with base or
+# super classes. Setting the tag to NO turns the diagrams off. Note that this
+# option is superceded by the HAVE_DOT option below. This is only a fallback. It is
+# recommended to install and use dot, since it yield more powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = YES
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = NO
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = YES
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found on the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than
+# this value, doxygen will try to truncate the graph, so that it fits within
+# the specified constraint. Beware that most browsers cannot cope with very
+# large images.
+
+MAX_DOT_GRAPH_WIDTH = 1024
+
+# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than
+# this value, doxygen will try to truncate the graph, so that it fits within
+# the specified constraint. Beware that most browsers cannot cope with very
+# large images.
+
+MAX_DOT_GRAPH_HEIGHT = 1024
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermedate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
+
+#---------------------------------------------------------------------------
+# Configuration::addtions related to the search engine
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE = YES
+
+# The CGI_NAME tag should be the name of the CGI script that
+# starts the search engine (doxysearch) with the correct parameters.
+# A script with this name will be generated by doxygen.
+
+CGI_NAME =
+
+# The CGI_URL tag should be the absolute URL to the directory where the
+# cgi binaries are located. See the documentation of your http daemon for
+# details.
+
+CGI_URL =
+
+# The DOC_URL tag should be the absolute URL to the directory where the
+# documentation is located. If left blank the absolute path to the
+# documentation, with file:// prepended to it, will be used.
+
+DOC_URL =
+
+# The DOC_ABSPATH tag should be the absolute path to the directory where the
+# documentation is located. If left blank the directory on the local machine
+# will be used.
+
+DOC_ABSPATH =
+
+# The BIN_ABSPATH tag must point to the directory where the doxysearch binary
+# is installed.
+
+BIN_ABSPATH =
+
+# The EXT_DOC_PATHS tag can be used to specify one or more paths to
+# documentation generated for other projects. This allows doxysearch to search
+# the documentation for these projects as well.
+
+EXT_DOC_PATHS =
diff --git a/HISTORY b/HISTORY
index 31216a9..33ef516 100644
--- a/HISTORY
+++ b/HISTORY
@@ -8,8 +8,8 @@ VDR Plugin 'muggle' Revision History
- Added an Ogg Vorbis decoder
XXXXXXXXXX: Version 0.0.5-ALPHA
-- Support für g++ 2.95.4
-- Support für Sockets (statt TCP)
+- Support für g++ 2.95.4
+- Support für Sockets (statt TCP)
- Kleinere Fehlerchen beseitigt
XXXXXXXXXX: Version 0.0.7-ALPHA
@@ -18,31 +18,31 @@ XXXXXXXXXX: Version 0.0.7-ALPHA
aus den Tags upgedated.
- Compilerwarnings beseitigt
- Anzeige auf dem gLCD
-- Menüanzeige bei aktiviertem Progressdisplay funktioniert
+- Menüanzeige bei aktiviertem Progressdisplay funktioniert
- Verbessertes Progress Display beim Abspielen (Track/Playlist,
- Progress/Detail), nur für 1.3.12
+ Progress/Detail), nur für 1.3.12
- Import von genre-Tags
- Instant play funktioniert
- Starten einer Playlist von irgendwo mittels Ok
XXXXXXXXXX: Version 0.0.8-ALPHA
-- Beim import werden bei bereits vorhandene Files nur die DB-Einträge erneuert,
+- Beim import werden bei bereits vorhandene Files nur die DB-Einträge erneuert,
keine Duplikate mehr.
-- mugglei mit der Option -z löscht Datenbankeinträge, bei denen die verwiesene
+- mugglei mit der Option -z löscht Datenbankeinträge, bei denen die verwiesene
Datei nicht existiert.
- Bug in mugglei entfernt, der verhinderte, dass neue Dateien korrekt
eingetragen werden.
- Ein Bug beim Skippen der Tracks am Ende der Playliste entfernt, der letzte
- Track wurde außerdem für immer wiederholt.
-- Französische Übersetzung. Merci a Patrice!
+ Track wurde außerdem für immer wiederholt.
+- Französische Ubersetzung. Merci a Patrice!
2005-01-07: Version 0.1.0-BETA
- der Begriff Playlist ist weggefallen. Neu gibt es Sammlungen.
Wichtig ist vor allem die Sammlung "spielen". Was man an diese
- Sammlung anhängt, wird eines nach dem anderen gespielt.
-- Wenn man etwas an eine Sammlung anhängt, muss man vorher sagen,
+ Sammlung anhängt, wird eines nach dem anderen gespielt.
+- Wenn man etwas an eine Sammlung anhängt, muss man vorher sagen,
welche das sein soll. Beim ersten Start von muggle ist das "spielen".
- Man kann die "Zielsammlung" ändern, indem man in der Sammlungsliste
+ Man kann die "Zielsammlung" ändern, indem man in der Sammlungsliste
die richtige auswahlt, dann den blauen Knopf "Befehle" nimmt und
dann "Ziel auf Sammlung .... setzen" macht.
- man kann direkt in der Sammlungsliste auch neue anlegen.
@@ -55,25 +55,25 @@ XXXXXXXXXX: Version 0.0.8-ALPHA
wird dort weitergemacht. Ein zweiter Druck auf Taste Stop beendet
auch das Abspielen von "spielen".
- Der Befehl "sofort spielen" macht dasselbe wie OK auf einem Track,
- aber für alle Tracks, die hinter dem gewähltenListeneintrag stecken,
+ aber für alle Tracks, die hinter dem gewähltenListeneintrag stecken,
z.B. alles von Abba.
- nachdem man Musik gestartet hat, bleibt das muggle - Menu stehen.
Damit und mit dem OK - Anspielen kann man sehr schnell alle Tracks
kurz anspielen.
-- auch während das muggle - Menu sichtbar ist, funktionieren nun die
+- auch während das muggle - Menu sichtbar ist, funktionieren nun die
Tasten Stop, Play, Pause.
-- wenn man muggle verlässt (am besten mit der Menu - Taste), wird der
+- wenn man muggle verlässt (am besten mit der Menu - Taste), wird der
Status gespeichert. Wenn man muggle wieder aufruft, ist man am gleichen Ort.
- beim Start von muggle kommt man direkt in das aktuelle Suchschema.
- Dieses kann man neu ändern, indem man Taste "Befehle" nimmt, dann
+ Dieses kann man neu ändern, indem man Taste "Befehle" nimmt, dann
Menu "Suchschema".
- Wenn man irgendwo mitten im Suchbaum ist und das Suchschema wechselt,
- verwendet muggle die schon bekannten Schlüsselfelder, um wieder
- möglichst weit in den Suchbaum hineinzugehen.
+ verwendet muggle die schon bekannten Schlüsselfelder, um wieder
+ möglichst weit in den Suchbaum hineinzugehen.
- Filter gibt es erstmal nicht mehr, da wird aber sicher wieder etwas kommen.
-- Man kann die Tasten rot, grün, gelb frei belegen, indem man unter
- "Befehle" auf einen Befehl geht und dann die gewünschte Farbtaste drückt.
- Das funktioniert auch für extern definierte Befehle.
+- Man kann die Tasten rot, grün, gelb frei belegen, indem man unter
+ "Befehle" auf einen Befehl geht und dann die gewünschte Farbtaste drückt.
+ Das funktioniert auch für extern definierte Befehle.
- Datenbankabfragen sind z.T. deutlich schneller
2005-01-23: Version 0.1.1-BETA
@@ -84,55 +84,55 @@ XXXXXXXXXX: Version 0.0.8-ALPHA
- GD compatibility added
- Many bugfixes and usability improvements
-- Die Organisation der Dateien kann nun vom Benutzer verändert werden.
- Zudem können neue Bäume erstellt werden (zB mag ich
+- Die Organisation der Dateien kann nun vom Benutzer verändert werden.
+ Zudem können neue Bäume erstellt werden (zB mag ich
Decade > Genre > Track sehr gern).
- m3u - Dateien werden nun immer in /tmp mit relativen Dateinamen erstellt.
Externe Befehle werden im top level directory der tracks aufgerufen
(vorangehendes chdir).
-- m3u - Dateien enthalten zusätzlich eine Kennung #MUGGLE:XXX
- wobei XXX die tracks.id des Stücks ist. Somit können Kommandos auch
- Befehle auf der Datenbank durchführen (zB Löschen eines Tracks).
- Muggle stellt die OSD-Ansicht danach neu dar, um Änderungen anzuzeigen.
-- Blättern in Genre-Hierarchien ist neu. Das Feld Genre nutzt wie
+- m3u - Dateien enthalten zusätzlich eine Kennung #MUGGLE:XXX
+ wobei XXX die tracks.id des Stücks ist. Somit können Kommandos auch
+ Befehle auf der Datenbank durchführen (zB Löschen eines Tracks).
+ Muggle stellt die OSD-Ansicht danach neu dar, um Änderungen anzuzeigen.
+- Blättern in Genre-Hierarchien ist neu. Das Feld Genre nutzt wie
bisher eine flache Genre-Liste. Die neuen Felder Genre1, Genre2, Genre3
definieren Ebenen im Suchbaum aus der Genre-Hierarchie.
-- Die Sprache wird aus id3v2-Tags importiert (für mp3 und flac)
-- Musikstücke können nach Sprache gebrowsed werden.
+- Die Sprache wird aus id3v2-Tags importiert (für mp3 und flac)
+- Musikstücke können nach Sprache gebrowsed werden.
- Hat ein Track 2 Genres (in den Feldern genre1 und genre2), so
- erscheint es in Kategorien für beide Genres. Allerdings wird das zweite
- Genre derzeit beim Import nicht berücksichtigt.
+ erscheint es in Kategorien für beide Genres. Allerdings wird das zweite
+ Genre derzeit beim Import nicht berücksichtigt.
- Wichtige Meldungen erscheinen nun auch im OSD (nicht mehr nur im Syslog)
-- Läuft mit allen Versionen inkl. 1.3.20
-- Player schaltet nach Ende der Playlist stumm (kein TV-Gedröhne mehr)
-- Decoder für Ogg und FLAC können nach Defines in make.config gebaut werden
+- Läuft mit allen Versionen inkl. 1.3.20
+- Player schaltet nach Ende der Playlist stumm (kein TV-Gedröhne mehr)
+- Decoder für Ogg und FLAC können nach Defines in make.config gebaut werden
- Bugfixes und sonstige Verbesserungen
2005-02-20: Version 0.1.3-BETA
-- das deutsche VDR - Wiki enthält zu muggle einen Abschnitt "Bedienung".
+- das deutsche VDR - Wiki enthält zu muggle einen Abschnitt "Bedienung".
Vielleicht findet sich ja jemand, der da etwas zu schreibt?
- Ich stehe gerne bei Fragen zur Verfügung.
+ Ich stehe gerne bei Fragen zur Verfügung.
- Man kann nun nach Ordnern/Verzeichnissen sortieren. Bis zu 4 Stufen
- sind möglich. Man muss alle Tracks mit mugglei neu importieren, damit
+ sind möglich. Man muss alle Tracks mit mugglei neu importieren, damit
das geht. Wenn mugglei nicht die Berechtigung hat, neue Felder in der
Tabelle tracks anzulegen, bleibt alles wie bisher. In diesem Fall
- müsste man entweder für die nötigen Rechte sorgen oder mit den Scripts
+ müsste man entweder für die nötigen Rechte sorgen oder mit den Scripts
die ganze Datenbank neu anlegen.
-- Hinter den Listeneinträgen steht nun, wieviele Tracks das jeweilen sind.
- Dank geht an jarny für seine Hilfe zu SQL.
+- Hinter den Listeneinträgen steht nun, wieviele Tracks das jeweilen sind.
+ Dank geht an jarny für seine Hilfe zu SQL.
- Die Sprachcodes werden nun vom Standard ISO 639-2/B (bibliographic)
genommen, wie in den id3v2 Tags. Das betrifft nur den Import, die
- Kompatibilität zu GiantDisc bleibt.
+ Kompatibilität zu GiantDisc bleibt.
- Die Setup - Einstellungen loop mode und shuffle mode werden nun
- berücksichtigt.
-- mugglei erklärt jetzt besser, warum er etwas nicht importieren kann.
+ berücksichtigt.
+- mugglei erklärt jetzt besser, warum er etwas nicht importieren kann.
- wenn die Datei muggle.state nicht schreibbar ist, warnt muggle einmal.
(Die Datei muggle.state speichert den Status (z.B. Sortierungen,
Position, Farbknopfbelegung)
- Einige Fehler korrigiert, vor allem Memory leaks (die meisten mit
valgrind gefunden). Sollte nun auch (wieder) mit g++ 2.95 compilieren.
-- Wer eine ältere Version von mysql benutzt, z.B. 3.23, wird mugglei nicht
- kompilieren können. Die Fixes sollten einfach sein, evtl reicht es,
+- Wer eine ältere Version von mysql benutzt, z.B. 3.23, wird mugglei nicht
+ kompilieren können. Die Fixes sollten einfach sein, evtl reicht es,
die Aufrufe mysql_server_init/end zu entfernen. Das README hat schon
immer mindestens 4.0.18 empfohlen.
@@ -267,8 +267,8 @@ XXXXXXXXXX: Version 0.0.8-ALPHA
(thanks to user sundin in vdrportal.de)
- Setup is now more verbose. NOTE: the enumeration for background mode may
have changed!
-- Several minor fixes (thanks to Ville Skyttä)
-- Improved and more thorough translations (thanks to Ville Skyttä)
+- Several minor fixes (thanks to Ville Skyttä)
+- Improved and more thorough translations (thanks to Ville Skyttä)
2006-04-28: Version 0.1.10-BETA
- SQLite: If the database directory (default $HOME/.muggle) does not exist,
@@ -299,7 +299,11 @@ XXXXXXXXXX: Version 0.0.8-ALPHA
- Rename image_convert.sh to muggle-image-convert, install it in
"make install", make it a bit more robust.
-2008--x-xx: Version 0.1.13-BETA
+2008-4-11: Version 0.2.0-BETA
+- supports UTF-8, see README.i18n
+- rewrote the player OSD, using code from the music plugin.
+- can automatically and asynchronously get lyrics using the
+ amarok script googlyrics, see README
- MP3Decoder: Suppress out of sync error message for first frame
- Player: If called for just one file which does not exist,
it no more starts.
@@ -309,7 +313,6 @@ XXXXXXXXXX: Version 0.0.8-ALPHA
- mugglei: fix postgresql help text
- postgresql: Default for user now is the login name
- mugglei -c now also deletes and rebuilds existing data bases
-- support UTF-8, see README.i18n
- mugglei: after importing, always delete albums without corresponding tracks
- mugglei: New german and french translations using the new mechanisms
- while playing, some text was not yet translatable
@@ -317,3 +320,8 @@ XXXXXXXXXX: Version 0.0.8-ALPHA
Maybe somebody will do that?
- more complete german and french translations.
- gcc 4.3 build fixes
+- rename image cache directory to cache directory and use it for lyrics too.
+ Cached files are permanently cached. If the last modification of the original image
+ indicates a change, the cached file is regenerated.
+- import: for flac files, prefer vorbis comments over id3 tags as recommended
+ by the flac people \ No newline at end of file
diff --git a/Makefile b/Makefile
index 33f8c08..66adfdb 100644
--- a/Makefile
+++ b/Makefile
@@ -9,6 +9,9 @@
#
PLUGIN = muggle
+all: libvdr-$(PLUGIN).so mugglei i18n
+
+
#no HAVE_* flags should ever be changed in this Makefile. Instead
#edit $VDRDIR/Make.config
@@ -18,6 +21,8 @@ PLUGIN = muggle
#HAVE_FLAC=1
#HAVE_SNDFILE=1
+HAVE_FFARD=1
+
#if you do not want to compile in code for embedded mysql,
#define this:
#HAVE_ONLY_SERVER=1
@@ -85,26 +90,21 @@ DEFINES += -DMUSICDIR='"$(MUSICDIR)"'
### The object files (add further files here):
OBJS = $(PLUGIN).o mg_valmap.o mg_thread_sync.o \
- mg_item.o mg_item_gd.o mg_listitem.o mg_selection.o mg_sel_gd.o vdr_actions.o vdr_menu.o mg_tools.o \
+ mg_item.o mg_item_gd.o mg_listitem.o mg_selection.o mg_sel_gd.o vdr_actions.o mg_menu.o vdr_menu.o mg_tools.o \
vdr_decoder_mp3.o vdr_stream.o vdr_decoder.o vdr_player.o \
- vdr_setup.o mg_setup.o mg_incremental_search.o mg_image_provider.o
-
-#ifdef HAVE_MP3NG_OSD
-MOBJS = i18n.o data.o menu.o \
- vars.o bitmap.o imagecache.o quantize.o \
- commands.o options.o lyrics.o cover.o skin.o visual.o \
- search.o mp3id3.o mp3id3tag.o rating.o menubrowse.o mp3control.o \
- data-mp3.o setup-mp3.o player-mp3.o stream.o network.o \
- decoder.o decoder-mp3.o decoder-mp3-stream.o decoder-snd.o \
- decoder-ogg.o
-
-$(MOBJS):
- make -f Makefile.music all
-#endif
-
-PLAYLIBS = -lmad $(shell taglib-config --libs)
+ vdr_setup.o mg_setup.o mg_incremental_search.o mg_image_provider.o \
+ mg_skin.o quantize.o mg_playcommands.o pcmplayer.o \
+ lyrics.o
+
+PLAYLIBS = -lmad $(shell taglib-config --libs)
MILIBS = $(shell taglib-config --libs)
+ifdef USE_BITMAP
+DEFINES += -DUSE_BITMAP
+OBJS += bitmap.o imagecache.o
+PLAYLIBS += -lImlib2
+endif
+
ifdef HAVE_SQLITE
DB_OBJ = mg_db_gd_sqlite.o
DB_CFLAGS = $(shell pkg-config --cflags sqlite3)
@@ -156,8 +156,6 @@ OBJS += $(DB_OBJ)
### Targets:
-all: libvdr-$(PLUGIN).so mugglei i18n
-
# Dependencies:
MAKEDEP = $(CXX) -MM -MG
@@ -207,7 +205,7 @@ i18n: $(I18Nmsgs) $(I18Npot)
### Targets:
libvdr-$(PLUGIN).so: $(OBJS) $(MOBJS)
- $(CXX) $(CXXFLAGS) -shared $(OBJS) $(MOBJS) $(PLAYLIBS) $(SQLLIBS) -o $@
+ $(CXX) $(CXXFLAGS) -shared $(OBJS) $(MOBJS) $(PLAYLIBS) $(MLIBS) $(SQLLIBS) -o $@
@cp --remove-destination $@ $(LIBDIR)/$@.$(APIVERSION)
mugglei: mg_tools.o mugglei.o $(DB_OBJ) mg_listitem.o mg_item.o mg_item_gd.o mg_valmap.o mg_setup.o
diff --git a/Makefile.music b/Makefile.music
deleted file mode 100644
index 03fcd95..0000000
--- a/Makefile.music
+++ /dev/null
@@ -1,240 +0,0 @@
-#
-# MP3/MPlayer plugin to VDR
-#
-# (C) 2001-2005 Stefan Huelswitt <s.huelswitt@gmx.de>
-#
-# This code 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 code is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-# Or, point your browser to http://www.gnu.org/copyleft/gpl.html
-
-# You can change the compile options here or create a Make.config
-# in the VDR directory an set them there.
-
-# Uncomment if you own a FF-Card , otherwise scrollmode is disabled .
-# If you have problems with scrolling oder hang-ups , dont use it.
-HAVE_FFCARD=1
-
-# Uncomment if you own enough ram for OSD (e.g modded FF-Card to 4MB ram or softdecoder)
-#HAVE_OSDMOD=1
-
-# Default: Imlib2, otherwise uncomment
-#HAVE_MAGICK=1
-
-# Uncomment, if you patched VDR with Wareagle-iconpatch
-#HAVE_ICONPATCH=1
-
-# Uncomment one of these lines, if you don't want one of the plugins
-#WITHOUT_MP3=1
-WITHOUT_MPLAYER=1
-
-# Uncomment the following line, if you don't have libsndfile installed
-#WITHOUT_LIBSNDFILE=1
-
-# Uncomment the following line, if you don't have libvorbisfile installed
-#WITHOUT_LIBVORBISFILE=1
-
-# Uncomment the following line, if you want OSS sound output
-#WITH_OSS_OUTPUT=1
-
-# Uncomment the following line, if you want to include debug symbols
-#DBG=1
-
-# INTERNAL USE , DONT UNCOMMENT OR PLUGIN WILL NOT COMPILE
-#IM_A_MORON=1
-#HAVE_TUNED_GTFT=1
-
-### The C++ compiler and options:
-CXX ?= g++
-CXXFLAGS ?= -g -O2 -fPIC -Wall -Woverloaded-virtual
-##CXXFLAGS ?= -ggdb -fPIC -Wall -Woverloaded-virtual
-
-###############################################
-###############################################
-#
-# no user configurable options below this point
-#
-###############################################
-###############################################
-
-### The directory environment:
-
-VDRDIR = ../../..
-LIBDIR = ../../lib
-TMPDIR = /tmp
-
-### Allow user defined options to overwrite defaults:
-
--include $(VDRDIR)/Make.config
-
-# The official name of this plugin.
-# This name will be used in the '-P...' option of VDR to load the plugin.
-# By default the main source file also carries this name.
-#
-PLUGIN = music
-PLUGIN2 = mplayer
-
-ifndef WITHOUT_MP3
- ALL += libvdr-$(PLUGIN).so
-endif
-ifndef WITHOUT_MPLAYER
- ALL += libvdr-$(PLUGIN2).so
-endif
-
-DOXYGEN = /usr/bin/doxygen
-DOXYFILE = Doxyfile
-
-### The version number of this plugin (taken from the main source file):
-
-VERSION = $(shell grep 'define PLUGIN_VERSION' version.h | awk '{ print $$3 }' | sed -e 's/[";]//g')
-
-### The version number of VDR (taken from VDR's "config.h"):
-
-VDRVERSION = $(shell sed -ne '/define VDRVERSION/ s/^.*"\(.*\)".*$$/\1/p' $(VDRDIR)/config.h)
-APIVERSION = $(shell sed -ne '/define APIVERSION/ s/^.*"\(.*\)".*$$/\1/p' $(VDRDIR)/config.h)
-ifeq ($(strip $(APIVERSION)),)
- APIVERSION = $(VDRVERSION)
-endif
-VDRVERSNUM = $(shell sed -ne '/define VDRVERSNUM/ s/^.[a-zA-Z ]*\([0-9]*\) .*$$/\1/p' $(VDRDIR)/config.h)
-APIVERSNUM = $(shell sed -ne '/define APIVERSNUM/ s/^.[a-zA-Z ]*\([0-9]*\) .*$$/\1/p' $(VDRDIR)/config.h)
-ifeq ($(strip $(APIVERSNUM)),)
- APIVERSNUM = $(VDRVERSNUM)
-endif
-
-### 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)"' -DAPIVERSNUM=$(APIVERSNUM)
-
-### The object files (add further files here):
-
-COM_OBJS = i18n.o data.o menu.o
-
-OBJS = vars.o bitmap.o imagecache.o quantize.o \
- commands.o options.o lyrics.o cover.o skin.o visual.o \
- search.o mp3id3.o mp3id3tag.o rating.o menubrowse.o mp3control.o \
- $(PLUGIN).o $(COM_OBJS)\
- data-mp3.o setup-mp3.o player-mp3.o stream.o network.o \
- decoder.o decoder-mp3.o decoder-mp3-stream.o decoder-snd.o \
- decoder-ogg.o
-LIBS = -lmad -lid3tag
-
-
-ifdef HAVE_FFCARD
- DEFINES += -DHAVE_FFCARD
-endif
-
-ifdef HAVE_MAGICK
- DEFINES += -DHAVE_MAGICK
- LIBS += -lMagick -lMagick++
-else
- DEFINES += -DHAVE_IMLIB2
- LIBS += -lImlib2
-endif
-
-ifdef HAVE_ICONPATCH
- DEFINES += -DHAVE_ICONPATCH
-endif
-
-ifdef HAVE_OSDMOD
- DEFINES += -DHAVE_OSDMOD
-endif
-
-ifdef IM_A_MORON
- DEFINES += -DIM_A_MORON
-endif
-
-ifdef HAVE_TUNED_GTFT
- DEFINES += -DHAVE_TUNED_GTFT
-endif
-
-ifndef WITHOUT_LIBSNDFILE
- LIBS += -lsndfile
- DEFINES += -DHAVE_SNDFILE
-endif
-
-ifndef WITHOUT_LIBVORBISFILE
- LIBS += -lvorbisfile -lvorbis
- DEFINES += -DHAVE_VORBISFILE
-endif
-
-ifdef WITH_OSS_OUTPUT
- DEFINES += -DWITH_OSS
-endif
-
-ifdef BROKEN_PCM
- DEFINES += -DBROKEN_PCM
-endif
-
-OBJS2 = $(PLUGIN2).o $(COM_OBJS)\
- setup-mplayer.o player-mplayer.o
-LIBS2 =
-
-ifeq ($(shell test -f $(VDRDIR)/fontsym.h ; echo $$?),0)
- DEFINES += -DHAVE_BEAUTYPATCH
-endif
-
-ifdef DBG
- CXXFLAGS += -g
-endif
-
-### Implicit rules:
-
-%.o: %.c
- $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $<
-
-# Dependencies:
-
-MAKEDEP = g++ -MM -MG
-DEPFILE = .dependencies
-$(DEPFILE): Makefile
- @$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) $(OBJS2:%.o=%.c) > $@
-
--include $(DEPFILE)
-
-### Targets:
-
-all: $(ALL)
-
-libvdr-$(PLUGIN).so: $(OBJS)
- $(CXX) $(CXXFLAGS) -shared $(OBJS) $(LIBS) -o $@
- @cp $@ $(LIBDIR)/$@.$(APIVERSION)
-
-libvdr-$(PLUGIN2).so: $(OBJS2)
- $(CXX) $(CXXFLAGS) -shared $(OBJS2) $(LIBS2) -o $@
- @cp $@ $(LIBDIR)/$@.$(APIVERSION)
-
-i18ntest: i18ntest.c i18n.c i18n.h
- $(CXX) $(CXXFLAGS) $(INCLUDES) $< -o $@
-
-dist: clean
- @-rm -rf $(TMPDIR)/$(ARCHIVE)
- @mkdir $(TMPDIR)/$(ARCHIVE)
- @cp -a * $(TMPDIR)/$(ARCHIVE)
- @tar czf $(PACKAGE).tar.gz -C $(TMPDIR) $(ARCHIVE)
- @-rm -rf $(TMPDIR)/$(ARCHIVE)
- @echo Distribution package created as $(PACKAGE).tar.gz
-
-srcdoc:
- @cp $(DOXYFILE) $(DOXYFILE).tmp
- @echo PROJECT_NUMBER = $(VERSION) >> $(DOXYFILE).tmp
- $(DOXYGEN) $(DOXYFILE).tmp
- @rm $(DOXYFILE).tmp
-
-clean:
- @-rm -f $(OBJS) $(OBJS2) $(DEPFILE) i18ntest libvdr-*.so $(PACKAGE).tar.gz core* *~
diff --git a/README b/README
index 906f96e..083c890 100644
--- a/README
+++ b/README
@@ -57,6 +57,7 @@ required:
http://www.xiph.org/ogg/vorbis/)
- optionally libFLAC++ to replay FLAC files
(Debian package libflac++-dev or sources from flac.sourceforge.net)
+ - recode if you want to download lyrics
The developer versions are needed because their headers are required
for compilation. The server need not be on the same machine as the
@@ -252,8 +253,12 @@ If a track has no ID3 tags, the following defaults will be applied:
\section covers COVERS
Muggle can display cover images (and others). Set the setup entry
-"Background mode" to "Image" to enable this feature. Please note,
-that the mechanism to find images has changed in release 0.1.9
+"Background mode" to "Cover small" or "Cover big" to enable this
+feature. "Cover small" puts it into the lower right corner which
+is unused by the main player screen, "Cover big" shows it on
+the full screen as background image.
+
+Please note that the mechanism to find images has changed in release 0.1.9
(and may be changed again in future versions). The images are now
found like this.
@@ -285,18 +290,55 @@ music playlist replayed side by side! In order to enable this
behavior, apply the patch (vdr-image-0.2.3.diff) to the image plugin
sources and recompile.
+The images need to be converted to Mpeg. Starting with muggle 0.2.0,
+the converted images are stored in a cache directory (define it in
+the setup menu). So if you change between Cover small and Cover big,
+you will need to remove all cached images. If the original image file
+changes, the image will also be regenerated.
+
+
+\section lyrics LYRICS
+
+Muggle can display lyrics from local files or from the internet.
+The lyrics are stored in the cache directory, with
+the file name extension .lyrics or .lyrics.tmp. .lyrics is for
+the saved version, .lyrics.tmp is downloaded from the internet but
+not saved. Both files can coexist, however if .lyrics.tmp exists,
+this one will be displayed with the option to save it. Saving of
+course removes .lyrics.tmp
+
+If a track starts playing and lyrics are wanted, muggle first tries the local file.
+If it does not exist, muggle will automatically get it from the
+internet. If you do not like the text, you can edit the lyrics file
+(outside of muggle) or request another load from the internet -
+maybe somebody fixed it meanwhile. If characters in the lyrics are
+not shown correcly, you can try to adjust the muggle_getlyrics script.
+
+Muggle starts the retrieval script in the background and displays
+the lyrics as soon as it is available. If you change track before
+the retrieval finished, two retrieval scripts might be running at
+the same time - no problem.
+
+Muggle calls the script muggle_getlyrics which in turn calls
+googlyrics - this is slightly adapted to muggle. The original is
+here: http://www.kde-apps.org/content/show.php/GoogLyrics?content=73850
+googlyrics is a plugin to the amarok music player, it can currently ask
+27 different sources.
+
+
+
\section quickuse QUICK INTRO
Quick version: select Muggle on the OSD, browse titles (using up/down
and Ok), add them using the red button. Music will start instantly
while you can continue to browse and add tracks.
-During playback, Up/Down jumps forth and back in the current
-playlist. Yellow toggles play/pause mode and Ok toggles a display of
-the replay process. Using Green, the display can be switched between
-playlist and single display mode, Red toggles info and progress
-view. For VDR 1.3.6- the progress display is "quite simple",
-unfortunately.
+Starting with 0.2.0, muggle has a new OSD for playback mode. It is
+similar to the OSD of the Music plugin but with less possibilities.
+Move the cursor in the track list as usual and press enter to play
+the currently selected track. Most other thing should be self
+explaining.
+
\section use DETAILED USER'S GUIDE
diff --git a/README.postgresql b/README.postgresql
index 08f95b0..bcd6cc6 100644
--- a/README.postgresql
+++ b/README.postgresql
@@ -29,6 +29,11 @@ Please execute these steps before starting muggle the first time:
3. createuser XXX
4. createdb GiantDisc
+under ubuntu:
+1. sudo -u postgres createuser XXX
+2. sudo -u postgres createdb GiantDisc
+
+
where XXX is the user name vdr is running under, normally vdr.
You may want to define the database encoding as UTF8, see
http://www.postgresql.org/docs/8.3/interactive/multibyte.html.
diff --git a/bitmap.c b/bitmap.c
new file mode 100644
index 0000000..57f2a0e
--- /dev/null
+++ b/bitmap.c
@@ -0,0 +1,184 @@
+/*
+ * borrowed from vdr-text2skin
+ */
+
+#include "bitmap.h"
+#include "quantize.h"
+#include <vdr/tools.h>
+
+#define X_DISPLAY_MISSING
+
+#include <Imlib2.h>
+cBitmapCache cMP3Bitmap::mCache(1);
+
+#include <glob.h>
+
+void cBitmapCache::DeleteObject(const tBitmapSpec &Key, cMP3Bitmap *&Data) {
+ delete Data;
+}
+
+void cBitmapCache::ResetObject(cMP3Bitmap *&Data) {
+ Data->Reset();
+}
+
+cMP3Bitmap *cMP3Bitmap::Load(const std::string &Filename, int Alpha, int height, int width, int colors, bool Quiet) {
+ tBitmapSpec spec(Filename, Alpha, height, width, colors);
+ // d(printf("checking image with spec %s_%d_%d_%d_%d..", Filename.c_str(),Alpha,height,width,colors));
+ std::string fname = Filename;
+
+ cMP3Bitmap *res = NULL;
+ if (mCache.Contains(spec)) {
+ res = mCache[spec];
+ // d(printf("..cache ok\n"));
+ }
+ else {
+ int pos;
+ if ((pos = fname.find('*')) != -1) {
+ glob_t gbuf;
+ if (glob(fname.c_str(), 0, NULL, &gbuf) == 0) {
+ // d(printf("GLOB: FOUND %s\n", gbuf.gl_pathv[0]));
+ fname = gbuf.gl_pathv[0];
+ }
+ else {
+ if (!Quiet) d(printf("music: bitmap: ERROR: No match for wildcard filename %s", Filename.c_str()));
+ fname = "";
+ }
+ globfree(&gbuf);
+ }
+
+ res = new cMP3Bitmap;
+ bool result = false;
+
+ result = res->LoadImlib(fname.c_str(),height,width,colors, Quiet);
+
+ if (result) {
+ res->SetAlpha(Alpha);
+ }
+ else {
+ d(printf("music: bitmap: ERROR: filename %s too short to identify format", fname.c_str()));
+ DELETENULL(res);
+ }
+ mCache[spec] = res;
+ }
+
+ return res;
+}
+
+bool cMP3Bitmap::Available(const std::string &Filename, int Alpha, int height, int width, int colors) {
+ if ((int)Filename.find('*') != -1) {
+ bool result = false;
+ glob_t gbuf;
+ if (glob(Filename.c_str(), 0, NULL, &gbuf) == 0)
+ result = true;
+ globfree(&gbuf);
+ return result;
+ }
+ else
+ return access(Filename.c_str(), F_OK) == 0;
+}
+
+cMP3Bitmap::cMP3Bitmap(void) {
+ mCurrent = 0;
+ mLastGet = 0;
+}
+
+cMP3Bitmap::~cMP3Bitmap() {
+ for (int i = 0; i < (int)mBitmaps.size(); ++i) delete mBitmaps[i];
+ mBitmaps.clear();
+}
+
+cBitmap &cMP3Bitmap::Get(void) {
+ if (mBitmaps.size() == 1) return *mBitmaps[0];
+
+ /* time_t upd;
+ int diff;
+ if (mLastGet == 0) {
+ mLastGet = Now;
+ upd = mDelay;
+ } else if ((diff = Now - mLastGet) >= mDelay) {
+ mCurrent = (mCurrent + 1) % mBitmaps.size();
+ mLastGet = Now;
+ upd = mDelay - diff > 1 ? mDelay - diff : 1;
+ } else {
+ upd = mDelay - diff;
+ }
+
+ if (UpdateIn == 0 || UpdateIn > (uint)upd)
+ UpdateIn = upd;
+ */
+ mCurrent = (mCurrent + 1) % mBitmaps.size();
+ return *mBitmaps[mCurrent];
+}
+
+void cMP3Bitmap::SetAlpha(int Alpha) {
+ if (Alpha > 0) {
+ std::vector<cBitmap*>::iterator it = mBitmaps.begin();
+ for (; it != mBitmaps.end(); ++it) {
+ int count;
+ if ((*it)->Colors(count)) {
+ for (int i = 0; i < count; ++i) {
+ int alpha = (((*it)->Color(i) & 0xFF000000) >> 24) * Alpha / 255;
+ (*it)->SetColor(i, ((*it)->Color(i) & 0x00FFFFFF) | (alpha << 24));
+ }
+ }
+ }
+ }
+}
+
+bool cMP3Bitmap::LoadImlib(const char *Filename, int height, int width, int colors, bool Quiet) {
+ Imlib_Image buffer;
+ int h,w;
+ unsigned char * outputImage = NULL;
+ unsigned int * outputPalette = NULL;
+ cQuantizeWu* quantizer = new cQuantizeWu();
+ cBitmap *bmp = NULL;
+
+ buffer = imlib_load_image(Filename);
+
+ imlib_context_set_image(buffer);
+ w= imlib_image_get_width();
+ h= imlib_image_get_height();
+ imlib_context_set_image(buffer);
+
+ if (!buffer)
+ return false;
+
+ Imlib_Image image;
+ image = imlib_create_cropped_scaled_image(0, 0, w, h ,width , height);
+
+ imlib_context_set_image(buffer);
+ imlib_free_image_and_decache();
+
+ imlib_context_set_image(image);
+ w= imlib_image_get_width();
+ h= imlib_image_get_height();
+ imlib_context_set_image(image);
+
+ bmp = new cBitmap(w, h, 8);
+ uint8_t *data = (uint8_t*)imlib_image_get_data_for_reading_only();
+
+ if ( colors != 0 ) {
+ quantizer->Quantize(data, w* h, colors);
+ outputImage = quantizer->OutputImage();
+ outputPalette = quantizer->OutputPalette();
+ }
+ imlib_free_image();
+
+ int pos = 0;
+ for (int y = 0; y < bmp->Height(); ++y) {
+ for (int x = 0; x < bmp->Width(); ++x) {
+ if ( colors != 0 ) {
+ bmp->DrawPixel(x, y , outputPalette[outputImage[y * bmp->Width() + x]] | 0xFF000000 );
+ }
+ else {
+ tColor col = (data[pos + 3] << 24) | (data[pos + 2] << 16) | (data[pos + 1] << 8) | data[pos + 0];
+ bmp->DrawPixel(x, y, col);
+ pos += 4;
+ }
+ }
+ }
+
+ mBitmaps.push_back(bmp);
+ delete(quantizer);
+ return true;
+}
diff --git a/bitmap.h b/bitmap.h
new file mode 100644
index 0000000..186eaa7
--- /dev/null
+++ b/bitmap.h
@@ -0,0 +1,101 @@
+/*
+ * Borrowed from vdr-text2skin
+ */
+
+#ifdef USE_BITMAP
+#ifndef VDR_MP3BITMAP_H
+#define VDR_MP3BITMAP_H
+
+#include "common.h"
+#include "imagecache.h"
+#include <vdr/osd.h>
+
+struct tBitmapSpec
+{
+ std::string Filename;
+ int Alpha;
+ int Width;
+ int Height;
+ int Colors;
+
+ tBitmapSpec(const std::string &filename, int alpha, int width, int height, int colors):
+ Filename(filename), Alpha(alpha), Width(width), Height(height), Colors(colors) {}
+
+ bool operator<(const tBitmapSpec &Src) const;
+ bool operator==(const tBitmapSpec &Src) const;
+};
+
+inline bool tBitmapSpec::operator<(const tBitmapSpec &Src) const
+{
+ if (Filename == Src.Filename) {
+ if (Alpha == Src.Alpha) {
+ if (Width == Src.Width) {
+ if (Height == Src.Height)
+ return Colors < Src.Colors;
+ return Height < Src.Height;
+ }
+ return Width < Src.Width;
+ }
+ return Alpha < Src.Alpha;
+ }
+ return Filename < Src.Filename;
+}
+
+inline bool tBitmapSpec::operator==(const tBitmapSpec &Src) const
+{
+ return Filename == Src.Filename
+ && Alpha == Src.Alpha
+ && Width == Src.Width
+ && Height == Src.Height
+ && Colors == Src.Colors;
+}
+
+class cMP3Bitmap;
+
+class cBitmapCache: public cxCache<tBitmapSpec,cMP3Bitmap*>
+{
+ protected:
+ virtual void DeleteObject(const tBitmapSpec &Key, cMP3Bitmap *&Data);
+ virtual void ResetObject(cMP3Bitmap *&Data);
+
+ public:
+ cBitmapCache(uint MaxItems): cxCache<tBitmapSpec,cMP3Bitmap*>(MaxItems) {}
+ virtual ~cBitmapCache() { Flush(); }
+};
+
+class cMP3Bitmap
+{
+ private:
+ static cBitmapCache mCache;
+
+ std::vector<cBitmap*> mBitmaps;
+ int mCurrent;
+ time_t mDelay;
+ time_t mLastGet;
+
+ // disallow direct construction
+ cMP3Bitmap(void);
+
+ public:
+ static cMP3Bitmap *Load(const std::string &Filename, int Alpha = 0, int height = 0,
+ int width = 0, int colors = 0, bool Quiet = false);
+ static bool Available(const std::string &Filename, int Alpha = 0, int height = 0,
+ int width = 0, int colors = 0);
+ static void ResetCache(void) { mCache.Reset(); }
+ static void FlushCache(void) { mCache.Flush(); }
+
+ virtual ~cMP3Bitmap();
+
+ void Reset(void) { mCurrent = 0; mLastGet = 0; }
+ cBitmap &Get(void);
+ void SetColor(int Index, tColor Color);
+ void SetAlpha(int Alpha);
+
+ bool LoadImlib(const char *Filename,int height, int width, int colors, bool Quiet);
+};
+
+inline void cMP3Bitmap::SetColor(int Index, tColor Color) {
+ mBitmaps[mCurrent]->SetColor(Index, Color);
+}
+#endif // VDR_MP3BITMAP_H
+#endif
diff --git a/common.h b/common.h
new file mode 100644
index 0000000..657ce0b
--- /dev/null
+++ b/common.h
@@ -0,0 +1,64 @@
+/*
+ * MP3/MPlayer plugin to VDR (C++)
+ *
+ * (C) 2001-2005 Stefan Huelswitt <s.huelswitt@gmx.de>
+ *
+ * This code 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 code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef ___COMMON_H
+#define ___COMMON_H
+
+#ifndef APIVERSNUM
+#include <vdr/config.h>
+#endif
+#include "config.h"
+
+#if APIVERSNUM >= 10313
+#define SLEEP(x) cCondWait::SleepMs(x)
+#else
+#define SLEEP(x) usleep((x)*1000)
+#endif
+
+#if APIVERSNUM >= 10318
+#include <vdr/tools.h>
+#include "bitmap.h"
+static cTimeMs __time;
+#define time_ms() ((int)__time.Elapsed())
+#endif
+
+#if APIVERSNUM >= 10338
+#define BUTTON "Button$"
+#else
+#define BUTTON
+#endif
+
+#if !defined(NO_DEBUG) && defined(DEBUG)
+#define d(x) { (x); }
+#else
+#define d(x) ;
+#endif
+
+extern char Songname[256];
+extern bool scrollmode;
+extern char coverpicture[256];
+extern char urlname[256];
+extern bool isWebstream;
+extern bool recordstream;
+extern char BaseSource[256];
+extern bool deletetracks;
+extern bool deletetrack;
+#endif //___COMMON_H
diff --git a/config.h b/config.h
new file mode 100644
index 0000000..518717e
--- /dev/null
+++ b/config.h
@@ -0,0 +1,60 @@
+/*
+ * MP3/MPlayer plugin to VDR (C++)
+ *
+ * (C) 2001-2005 Stefan Huelswitt <s.huelswitt@gmx.de>
+ *
+ * This code 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 code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+// This files contains some option switches/values for compile time
+// configuration of the MP3/MPlayer plugin. You should only alter
+// this file, if you have understand the source code parts which deal
+// with the changed value.
+
+// After changing this file you should do a "make plugins-clean ; make plugins"
+// to recompile vdr.
+
+#include <string>
+#include <vdr/config.h>
+
+#ifndef ___CONFIG_H
+#define ___CONFIG_H
+
+// Uncomment to enable generic debugging messages to the console. This may slow
+// down operation in some cases.
+//#define DEBUG
+#define NO_DEBUG
+
+// Defines the filename extention to use for playlist files.
+#define PLAYLISTEXT ".m3u"
+
+// Defines the text to identify WinAmp-Style playlists.
+#define WINAMPEXT "#EXTM3U"
+
+// Defines the timeout in seconds for functions which use a single key
+// (e.g. openning the playlist window). If the key is repressed during
+// the timeout, the secondary function is activated.
+#define MULTI_TIMEOUT 3
+
+// Defines the timeout in ms for entering the single digits in direct song
+// selection.
+#define SELECT_TIMEOUT 1000
+
+// If the progress display is closed on direct song selection, the display
+// is opend temporarily. This defines the time in seconds after the display
+// is closed again.
+#define SELECTHIDE_TIMEOUT 3
+#endif //___CONFIG_H
diff --git a/imagecache.c b/imagecache.c
new file mode 100644
index 0000000..96bf20c
--- /dev/null
+++ b/imagecache.c
@@ -0,0 +1,5 @@
+/*
+ * borrowed from vdr-text2skin
+ */
+
+#include "imagecache.h"
diff --git a/imagecache.h b/imagecache.h
new file mode 100644
index 0000000..baeb3d0
--- /dev/null
+++ b/imagecache.h
@@ -0,0 +1,102 @@
+/*
+ * borrowed from vdr-text2skin
+ */
+
+#ifndef VDR_IMAGECACHE_HPP
+#define VDR_IMAGECACHE_HPP
+
+#include <map>
+#include <vector>
+#include <string>
+
+template<class key_type, class data_type>
+class cxCache {
+private:
+ typedef std::vector<key_type> usage_list;
+ typedef typename usage_list::iterator usage_iterator;
+ typedef std::map<key_type,data_type> item_map;
+ typedef typename item_map::iterator item_iterator;
+
+ item_map mItems;
+ usage_list mUsage;
+ uint mMaxItems;
+
+protected:
+ virtual void DeleteObject(const key_type &Key, data_type &Data) = 0;
+ virtual void ResetObject(data_type &Data) = 0;
+
+public:
+ cxCache(uint MaxItems);
+ virtual ~cxCache();
+
+ void Reset(void);
+ void Flush(void);
+ bool Contains(const key_type &Key);
+ data_type &operator[](const key_type &Key);
+ uint Count(void) { return mUsage.size(); }
+};
+
+template<class key_type, class data_type>
+inline bool cxCache<key_type, data_type>::Contains(const key_type &Key)
+{
+ return mItems.find(Key) != mItems.end();
+}
+
+template<class key_type, class data_type>
+cxCache<key_type, data_type>::cxCache(uint MaxItems)
+{
+ mMaxItems = MaxItems;
+}
+
+template<class key_type, class data_type>
+cxCache<key_type, data_type>::~cxCache()
+{
+}
+
+template<class key_type, class data_type>
+void cxCache<key_type, data_type>::Flush(void)
+{
+ item_iterator it = mItems.begin();
+ for (; it != mItems.end(); ++it)
+ DeleteObject(it->first, it->second);
+
+ mUsage.clear();
+ mItems.clear();
+}
+
+template<class key_type, class data_type>
+void cxCache<key_type, data_type>::Reset(void)
+{
+ item_iterator it = mItems.begin();
+ for (; it != mItems.end(); ++it)
+ ResetObject(it->second);
+}
+
+template<class key_type, class data_type>
+data_type &cxCache<key_type, data_type>::operator[](const key_type &Key)
+{
+ item_iterator it = mItems.find(Key);
+ if (it != mItems.end()) {
+ usage_iterator ut = mUsage.begin();
+ for (; ut != mUsage.end(); ++ut) {
+ if (*ut == Key) {
+ mUsage.erase(ut);
+ break;
+ }
+ }
+ mUsage.push_back(Key);
+ return it->second;
+ }
+
+ if (mUsage.size() == mMaxItems) {
+ item_iterator it = mItems.find(*mUsage.begin());
+ DeleteObject(it->first, it->second);
+ mUsage.erase(mUsage.begin());
+ mItems.erase(it);
+ }
+
+ it = mItems.insert(typename item_map::value_type(Key, data_type())).first;
+ return it->second;
+}
+
+#endif // VDR_IMAGECACHE_HPP
diff --git a/lyrics.c b/lyrics.c
new file mode 100644
index 0000000..0cb8e6d
--- /dev/null
+++ b/lyrics.c
@@ -0,0 +1,143 @@
+#include <string.h>
+#include <fstream>
+
+#include <vdr/interface.h>
+#include <vdr/menu.h>
+#include <vdr/plugin.h>
+
+#include "lyrics.h"
+#include "mg_item_gd.h"
+#include "vdr_actions.h"
+#include "mg_setup.h"
+#include "vdr_player.h"
+
+int mgLyrics::RunCommand(const string cmd) {
+ int res=-1;
+ if (access(cmd.c_str(),R_OK|X_OK)) return res;
+ char *tmp;
+ msprintf(&tmp,"%s \"%s\" \"%s\" \"%s\"",cmd.c_str(),
+ playItem->getArtist().c_str(),
+ playItem->getTitle().c_str(),
+ playItem->getCachedFilename("lyrics.tmp").c_str());
+ mgDebug(1,"muggle[%d]: lyrics: executing '%s'\n",getpid (), tmp);
+ res=SystemExec(tmp,true); // run detached
+ free(tmp);
+ return res;
+}
+
+void
+mgLyrics::LoadExternal() {
+mgLog("LoadExternal");
+ osd()->Message1(tr("Loading lyrics from internet..."));
+ string script=the_setup.ConfigDirectory + "/scripts/muggle_getlyrics";
+ if (RunCommand(script)==0) {
+ state=lyricsLoading;
+// BlueAction=actNone;
+// SetHelpKeys();
+ playItem->setCheckedForTmpLyrics(time(0));
+ }
+}
+
+void
+mgLyrics::SaveExternal() {
+ const mgItemGd *item = PlayingItem();
+ if (!item) return;
+ string local=item->getCachedFilename("lyrics");
+ string tmp=item->getCachedFilename("lyrics.tmp");
+ PlayerControl()->CurrentItem()->resetHasLyricsFile();
+ char *cmd;
+ msprintf(&cmd, "mv \"%s\" \"%s\"", tmp.c_str(), local.c_str());
+ mgDebug(1,"muggle[%d]: lyrics: Executing %s\n",getpid(), cmd);
+ if (!SystemExec(cmd)) {
+ BlueAction=actLoadExternalLyrics;
+ SetHelpKeys();
+ }
+ free(cmd);
+}
+
+eOSState
+mgLyrics::Process(eKeys key) {
+ playItem=mutPlayingItem();
+ long cl=playItem->getCheckedForTmpLyrics();
+ LyricsState prevstate=state;
+ if (displayItem!=playItem || cl>0 && cl<time(0)) {
+ if (!access(playItem->getCachedFilename("lyrics.tmp.loading").c_str(),R_OK)) {
+ state=lyricsLoading;
+ playItem->setCheckedForTmpLyrics(time(0));
+ } else if (!access(playItem->getCachedFilename("lyrics.tmp").c_str(),R_OK)) {
+ state=lyricsLoaded;
+ playItem->setCheckedForTmpLyrics(0);
+ } else if (displayItem!=playItem) {
+ if (!access(playItem->getCachedFilename("lyrics").c_str(),R_OK)) {
+ state=lyricsSaved;
+ playItem->setCheckedForTmpLyrics(0);
+ } else {
+ LoadExternal();
+ }
+ } else {
+ state=lyricsSaved;
+ playItem->setCheckedForTmpLyrics(0);
+ }
+ }
+ if (displayItem!=playItem || state!=prevstate) {
+ BuildOsd();
+ osd()->Display();
+ }
+ eOSState result = osContinue;
+ switch (key) {
+ case kBlue:
+ if (BlueAction==actSaveExternalLyrics)
+ SaveExternal();
+ else if (BlueAction==actLoadExternalLyrics) {
+ LoadExternal();
+ BuildOsd();
+ osd()->Display();
+ }
+ break;
+ default:
+ result = mgMenu::Process(key);
+ break;
+ }
+ return result;
+}
+
+mgLyrics::mgLyrics() {
+ playItem=0;
+ displayItem=0;
+ state=lyricsSaved;
+}
+
+string
+mgLyrics::Title() const
+{
+ const mgItemGd *item = PlayingItem();
+ if (!item) return "";
+ return item->getArtist() + ":" + item->getTitle();
+}
+
+void
+mgLyrics::BuildOsd () {
+ playItem = mutPlayingItem();
+ if (!playItem) return;
+ RedAction=actBack2;
+ GreenAction=actPrevTrack;
+ YellowAction=actNextTrack;
+ string loadfile="";
+ switch (state) {
+ case lyricsLoading:
+ BlueAction=actNone;
+ break;
+ case lyricsLoaded:
+ BlueAction=actSaveExternalLyrics;
+ loadfile=playItem->getCachedFilename("lyrics.tmp");
+ break;
+ default:
+ BlueAction=actLoadExternalLyrics;
+ loadfile=playItem->getCachedFilename("lyrics");
+ break;
+ }
+ InitOsd();
+ if (!access(loadfile.c_str(),R_OK))
+ osd()->AddFile(loadfile);
+ displayItem=playItem;
+}
diff --git a/lyrics.h b/lyrics.h
new file mode 100644
index 0000000..03e8760
--- /dev/null
+++ b/lyrics.h
@@ -0,0 +1,28 @@
+#ifndef __LYRICS_H
+#define __LYRICS_H
+
+#include "mg_menu.h"
+
+enum LyricsState {
+ lyricsNone,
+ lyricsLoading,
+ lyricsLoaded,
+ lyricsSaved
+};
+
+class mgLyrics : public mgMenu
+{
+ private:
+ int RunCommand(string cmd);
+ void LoadExternal();
+ void SaveExternal();
+ const mgItemGd *displayItem;
+ mgItemGd *playItem;
+ LyricsState state;
+ public:
+ mgLyrics(void);
+ void BuildOsd();
+ string Title() const;
+ eOSState Process(eKeys key);
+};
+#endif // __LYRICS_H
diff --git a/menu-async.h b/menu-async.h
new file mode 100644
index 0000000..c8fc38c
--- /dev/null
+++ b/menu-async.h
@@ -0,0 +1,44 @@
+/*
+ * MP3/MPlayer plugin to VDR (C++)
+ *
+ * (C) 2001-2005 Stefan Huelswitt <s.huelswitt@gmx.de>
+ *
+ * This code 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 code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef ___MENU_ASYNC_H
+#define ___MENU_ASYNC_H
+
+#include <vdr/thread.h>
+
+// ----------------------------------------------------------------
+
+class cAsyncStatus : private cMutex
+{
+ private:
+ const char *text;
+ bool changed;
+ public:
+ cAsyncStatus(void);
+ ~cAsyncStatus();
+ void Set(const char *Text);
+ bool Changed(void) { return changed; }
+ const char *Begin(void);
+ void Finish(void);
+};
+
+extern cAsyncStatus asyncStatus;
+#endif //___MENU_ASYNC_H
diff --git a/mg_db.c b/mg_db.c
index 0c54fd0..e0462f0 100644
--- a/mg_db.c
+++ b/mg_db.c
@@ -26,7 +26,6 @@ using namespace std;
#include "mg_db_gd_mysql.h"
#endif
-
#include <sys/stat.h>
#include <stdio.h>
#include <unistd.h>
@@ -35,13 +34,13 @@ using namespace std;
#include <mpegfile.h>
#include <flacfile.h>
+
static map <mgKeyTypes, map<string,string> > map_values;
static map <mgKeyTypes, map<string,string> > map_ids;
mgDbServer* DbServer;
-mgDbServer::mgDbServer()
-{
+mgDbServer::mgDbServer() {
#ifdef HAVE_SQLITE
m_server = new mgDbServerSQLite;
#elif HAVE_PG
@@ -51,24 +50,21 @@ mgDbServer::mgDbServer()
#endif
}
-mgDbServer::~mgDbServer()
-{
+mgDbServer::~mgDbServer() {
delete m_server;
m_server = 0;
}
-mgSQLString::~mgSQLString()
-{
+mgSQLString::~mgSQLString() {
delete m_str;
free(m_original);
}
void
-mgSQLString::Init(const char* s)
-{
+mgSQLString::Init(const char* s) {
// strip trailing spaces
- m_original = strdup(s);
+ m_original = strdup(s?s:"");
char *p=strrchr(m_original,' ');
if (p)
if (p+1 == strchr(m_original,0))
@@ -84,40 +80,35 @@ mgSQLString::Init(const char* s)
#endif
}
-mgSQLString::mgSQLString(const char*s)
-{
+mgSQLString::mgSQLString(const char*s) {
Init(s);
}
-mgSQLString::mgSQLString(const mgSQLString& s)
-{
+mgSQLString::mgSQLString(const mgSQLString& s) {
Init(s.original());
}
-mgSQLString::mgSQLString(string s)
-{
+mgSQLString::mgSQLString(string s) {
Init(s.c_str());
}
-mgSQLString::mgSQLString(TagLib::String s)
-{
+mgSQLString::mgSQLString(TagLib::String s) {
Init(s.toCString(the_setup.utf8));
}
-const char*
+const char*
mgSQLString::original() const
{
return m_str->original();
}
-char*
+char*
mgSQLString::unquoted() const
{
return m_str->unquoted();
}
-
-char*
+char*
mgSQLString::quoted() const
{
return m_str->quoted();
@@ -160,8 +151,7 @@ mgSQLString::operator!=(string b) const
}
void
-mgSQLString::operator=(const char* b)
-{
+mgSQLString::operator=(const char* b) {
delete m_str;
free(m_original);
m_original = 0;
@@ -169,45 +159,38 @@ mgSQLString::operator=(const char* b)
}
void
-mgSQLString::operator=(const mgSQLString& b)
-{
+mgSQLString::operator=(const mgSQLString& b) {
delete m_str;
free(m_original);
m_original = 0;
Init(b.original());
}
-mgSQLStringImp::mgSQLStringImp()
-{
+mgSQLStringImp::mgSQLStringImp() {
m_quoted = 0;
}
-mgSQLStringImp::~mgSQLStringImp()
-{
+mgSQLStringImp::~mgSQLStringImp() {
if (m_quoted)
free(m_quoted);
m_quoted=0;
}
-
-char*
+char*
mgSQLStringImp::quoted() const
{
- if (!m_quoted)
- {
+ if (!m_quoted) {
msprintf(&m_quoted,"'%s'",unquoted());
}
return m_quoted;
}
-mgQuery::mgQuery(void *db,const char *s,mgQueryNoise noise)
-{
+mgQuery::mgQuery(void *db,const char *s,mgQueryNoise noise) {
Init(db,s,noise);
}
void
-mgQuery::Init(void *db,const char *s,mgQueryNoise noise)
-{
+mgQuery::Init(void *db,const char *s,mgQueryNoise noise) {
#ifdef HAVE_SQLITE
m_q = new mgQuerySQLite(db,s,noise);
#elif HAVE_PG
@@ -217,18 +200,15 @@ mgQuery::Init(void *db,const char *s,mgQueryNoise noise)
#endif
}
-mgQuery::mgQuery(void *db,string s,mgQueryNoise noise)
-{
+mgQuery::mgQuery(void *db,string s,mgQueryNoise noise) {
Init(db,s.c_str(),noise);
}
-mgQuery::~mgQuery()
-{
+mgQuery::~mgQuery() {
delete m_q;
}
-mgQueryImp::mgQueryImp(void *db,string sql,mgQueryNoise noise)
-{
+mgQueryImp::mgQueryImp(void *db,string sql,mgQueryNoise noise) {
m_db_handle = db;
m_sql = sql;
m_noise = noise;
@@ -241,24 +221,22 @@ mgQueryImp::mgQueryImp(void *db,string sql,mgQueryNoise noise)
}
void
-mgQueryImp::HandleErrors()
-{
- mgDebug(5,"%X:%d rows: %s",m_db_handle,m_rows,m_optsql);
+mgQueryImp::HandleErrors() {
+ mgDebug(5,"%X:%d rows: %s",m_db_handle,m_rows,m_optsql);
if (m_errormessage && strlen(m_errormessage))
- switch (m_noise) {
- case mgQueryNormal:
- mgError("SQL Error in %s: %d/%s",m_optsql,m_rc,m_errormessage);
- break;
- case mgQueryWarnOnly:
- mgWarning("SQL Error in %s: %d/%s",m_optsql,m_rc,m_errormessage);
- break;
- case mgQuerySilent:
- break;
- }
+ switch (m_noise) {
+ case mgQueryNormal:
+ mgError("SQL Error in %s: %d/%s",m_optsql,m_rc,m_errormessage);
+ break;
+ case mgQueryWarnOnly:
+ mgWarning("SQL Error in %s: %d/%s",m_optsql,m_rc,m_errormessage);
+ break;
+ case mgQuerySilent:
+ break;
+ }
}
-mgDb::mgDb(bool SeparateThread)
-{
+mgDb::mgDb(bool SeparateThread) {
m_database_found=false;
m_hasfolderfields=false;
m_separate_thread=SeparateThread;
@@ -266,8 +244,7 @@ mgDb::mgDb(bool SeparateThread)
m_create_time=0;
}
-mgDb::~mgDb()
-{
+mgDb::~mgDb() {
}
void *
@@ -277,8 +254,7 @@ mgDb::DbHandle() {
}
string
-optimize (string & spar)
-{
+optimize (string & spar) {
string s = spar;
string::size_type tmp = s.find (" WHERE");
if (tmp != string::npos)
@@ -287,75 +263,66 @@ optimize (string & spar)
if (tmp != string::npos)
s.erase (tmp, 9999);
string::size_type frompos = s.find (" FROM ") + 6;
- if (s.substr (frompos).find (",") == string::npos)
- {
+ if (s.substr (frompos).find (",") == string::npos) {
string from = s.substr (frompos, 999) + '.';
string::size_type tbl;
- while ((tbl = spar.find (from)) != string::npos)
- {
- spar.erase (tbl, from.size ());
+ while ((tbl = spar.find (from)) != string::npos) {
+ spar.erase (tbl, from.size ());
}
}
return spar;
}
bool
-mgDb::Connect ()
-{
- if (m_database_found)
- return true;
- if (!ServerConnect())
- return false;
- if (time(0)<m_create_time+10)
- return false;
- m_create_time=time(0);
- m_database_found=ConnectDatabase();
- if (!m_database_found && !Creatable())
- {
- mgWarning("database not found");
- return false;
- }
- bool clearwanted=false;
- if (the_setup.IsMugglei())
- clearwanted=the_setup.CreateMode;
- else
- if (!m_database_found)
- {
- extern bool create_question();
- clearwanted=create_question();
- }
- if (clearwanted)
- {
- if (!m_database_found && Creatable())
- {
- m_database_found = true; // avoid recursion
- mgWarning("Dropping and recreating database %s",the_setup.DbName);
- m_database_found = Create();
- if (m_database_found)
- {
- m_database_found = true; // avoid recursion
- m_database_found = ConnectDatabase();
- }
+mgDb::Connect () {
+ if (m_database_found)
+ return true;
+ if (!ServerConnect())
+ return false;
+ if (time(0)<m_create_time+10)
+ return false;
+ m_create_time=time(0);
+ m_database_found=ConnectDatabase();
+ if (!m_database_found && !Creatable()) {
+ mgWarning("database not found");
+ return false;
+ }
+ bool clearwanted=false;
+ if (the_setup.IsMugglei())
+ clearwanted=the_setup.CreateMode;
+ else
+ if (!m_database_found) {
+ extern bool create_question();
+ clearwanted=create_question();
}
- }
- if (m_database_found)
- {
- if (clearwanted || !FieldExists("tracks","id"))
- {
- if (Clear())
- mgWarning(trdb("empty database created"));
- }
- if (m_database_found && exec_count("SELECT COUNT(1) FROM genre")==0)
- FillTables();
- }
- return m_database_found;
+ if (clearwanted) {
+ if (!m_database_found && Creatable()) {
+ // avoid recursion
+ m_database_found = true;
+ mgWarning("Dropping and recreating database %s",the_setup.DbName);
+ m_database_found = Create();
+ if (m_database_found) {
+ // avoid recursion
+ m_database_found = true;
+ m_database_found = ConnectDatabase();
+ }
+ }
+ }
+ if (m_database_found) {
+ if (clearwanted || !FieldExists("tracks","id")) {
+ if (Clear())
+ mgWarning(trdb("empty database created"));
+ }
+ if (m_database_found && exec_count("SELECT COUNT(1) FROM genre")==0)
+ FillTables();
+ }
+ return m_database_found;
}
bool
-mgDb::SyncStart()
-{
- if (!Connect())
- return false;
+mgDb::SyncStart() {
+ if (!Connect())
+ return false;
// init random number generator
struct timeval tv;
struct timezone tz;
@@ -366,28 +333,24 @@ mgDb::SyncStart()
}
void
-mgDb::Sync(char * const * path_argv)
-{
+mgDb::Sync(const char * const * path_argv) {
if (!SyncStart())
return;
- extern void showimportcount(unsigned int,bool final=false);
+ extern void showimportcount(unsigned int,bool final=false);
LoadMapInto("SELECT id,genre from genre",&m_Genres,0);
LoadMapInto("SELECT genre,id3genre from genre",&m_GenreIds,0);
StartTransaction();
- if (the_setup.DeleteStaleReferences)
- {
+ if (the_setup.DeleteStaleReferences) {
int count=0;
mgParts all;
vector<mgItem*> items;
LoadItemsInto(all,items);
- for (unsigned int idx=0;idx<items.size();idx++)
- {
+ for (unsigned int idx=0;idx<items.size();idx++) {
mgItem* item = items[idx];
string fullpath=item->getSourceFile(true,true);
- if (!item->Valid(true))
- {
+ if (!item->Valid(true)) {
char *b;
msprintf(&b,"DELETE FROM tracks WHERE id=%ld",item->getItemid());
count += Execute(b);
@@ -400,13 +363,10 @@ mgDb::Sync(char * const * path_argv)
unsigned int importcount=0;
FTS *fts;
FTSENT *ftsent;
- fts = fts_open( path_argv, FTS_LOGICAL, 0);
- if (fts)
- {
- while ( (ftsent = fts_read(fts)) != NULL)
- {
- if (ftsent->fts_path[0]=='/' && ftsent->fts_info!=FTS_DP)
- {
+ fts = fts_open((char* const*) path_argv, FTS_LOGICAL, 0);
+ if (fts) {
+ while ( (ftsent = fts_read(fts)) != NULL) {
+ if (ftsent->fts_path[0]=='/' && ftsent->fts_info!=FTS_DP) {
mgWarning("Ignoring absolute path %s",ftsent->fts_path);
fts_set(fts,ftsent,FTS_SKIP);
continue;
@@ -428,8 +388,8 @@ mgDb::Sync(char * const * path_argv)
mgDebug(1,"Ignoring broken symbolic link %s",
ftsent->fts_path);
break;
- case FTS_NSOK: // should never happen because we do not do FTS_NOSTAT
- case FTS_SL: // should never happen because we do FTS_LOGICAL
+ case FTS_NSOK: // should never happen because we do not do FTS_NOSTAT
+ case FTS_SL: // should never happen because we do FTS_LOGICAL
case FTS_ERR:
mgDebug(1,"Ignoring %s: error %d",
ftsent->fts_path,ftsent->fts_errno);
@@ -443,9 +403,8 @@ mgDb::Sync(char * const * path_argv)
mgDebug(1,"internal error: fts_path is 0");
else if (access(ftsent->fts_path,R_OK))
mgDebug(1,"Ignoring unreadable file %s: %s",
- ftsent->fts_path,strerror(errno));
- else
- {
+ ftsent->fts_path,strerror(errno));
+ else {
if (SyncFile(ftsent->fts_path))
importcount++;
if (importcount%1000==0)
@@ -471,7 +430,6 @@ mgDb::Sync(char * const * path_argv)
showimportcount(importcount,true);
}
-
string
mgKeyNormal::id() const
{
@@ -496,17 +454,14 @@ mgKeyNormal::value() const
return "";
}
-
-mgKeyNormal::mgKeyNormal(const mgKeyNormal& k)
-{
+mgKeyNormal::mgKeyNormal(const mgKeyNormal& k) {
m_kt = k.m_kt;
m_table = k.m_table;
m_field = k.m_field;
m_item = k.m_item->Clone();
}
-mgKeyNormal::mgKeyNormal(const mgKeyTypes kt, string table, string field)
-{
+mgKeyNormal::mgKeyNormal(const mgKeyTypes kt, string table, string field) {
m_kt = kt;
m_table = table;
m_field = field;
@@ -514,14 +469,12 @@ mgKeyNormal::mgKeyNormal(const mgKeyTypes kt, string table, string field)
}
void
-mgKeyNormal::set(mgListItem* item)
-{
+mgKeyNormal::set(mgListItem* item) {
m_item=item->Clone();
}
mgListItem*
-mgKeyNormal::get()
-{
+mgKeyNormal::get() {
return m_item;
}
@@ -541,13 +494,12 @@ mgKeyNormal::IdClause(mgDb *db,string what,string::size_type start,string::size_
{
if (len==0)
len=string::npos;
- if (id() == "'NULL'")
+ if (id() == "'NULL'")
return what + " is NULL";
- else if (len==string::npos)
+ else if (len==string::npos)
return what + "=" + mgSQLString(id()).quoted();
- else
- {
- return "substring("+what + ","+ltos(start+1)+","+ltos(len)+")="
+ else {
+ return "substr("+what + ","+ltos(start+1)+","+ltos(len)+")="
+ mgSQLString(id().substr(start,len)).quoted();
}
}
@@ -556,7 +508,7 @@ void
mgKeyNormal::AddIdClause(mgDb *db,mgParts &result,string what) const
{
if (valid())
- result.clauses.push_back(IdClause(db,what));
+ result.clauses.push_back(IdClause(db,what));
}
bool
@@ -570,12 +522,11 @@ mgKey::LoadMap() const
return true;
}
-
mgKeyMaps KeyMaps;
bool
mgKeyMaps::loadvalues (mgKeyTypes kt) const
{
- if (map_ids[kt].size()>0)
+ if (map_ids[kt].size()>0)
return true;
mgKey* k = ktGenerate(kt);
bool result = k->LoadMap();
@@ -590,13 +541,11 @@ mgKeyMaps::value(mgKeyTypes kt, string idstr) const
return idstr;
if (idstr=="NULL")
return idstr;
- if (loadvalues (kt))
- {
+ if (loadvalues (kt)) {
map<string,string>& valmap = map_values[kt];
map<string,string>::iterator it;
it = valmap.find(idstr);
- if (it!=valmap.end())
- {
+ if (it!=valmap.end()) {
string r = it->second;
if (!r.empty())
return r;
@@ -613,54 +562,45 @@ mgKeyMaps::value(mgKeyTypes kt, string idstr) const
string
mgKeyMaps::id(mgKeyTypes kt, string valstr) const
{
- if (loadvalues (kt))
- {
+ if (loadvalues (kt)) {
map<string,string>& idmap = map_ids[kt];
return idmap[valstr];
}
return valstr;
}
-
-class mgRefParts : public mgParts {
+class mgRefParts : public mgParts
+{
public:
mgRefParts(const mgReference* r);
};
-
-strlist& operator+=(strlist&a, strlist b)
-{
+strlist& operator+=(strlist&a, strlist b) {
a.insert(a.end(), b.begin(),b.end());
return a;
}
-strlist operator+(strlist&a, strlist&b)
-{
+strlist operator+(strlist&a, strlist&b) {
strlist result;
result.insert(result.end(),a.begin(),a.end());
result.insert(result.end(),b.begin(),b.end());
return result;
}
-
string
-sql_list (string prefix,strlist v,string sep,string postfix)
-{
+sql_list (string prefix,strlist v,string sep,string postfix) {
string result = "";
for (list < string >::iterator it = v.begin (); it != v.end (); ++it)
addsep (result, sep, *it);
- if (!result.empty())
- {
- result.insert(0,prefix+" ");
- result += postfix;
+ if (!result.empty()) {
+ result.insert(0,prefix+" ");
+ result += postfix;
}
return result;
}
-
mgParts&
-mgParts::operator+=(mgParts a)
-{
+mgParts::operator+=(mgParts a) {
valuefields += a.valuefields;
idfields += a.idfields;
tables += a.tables;
@@ -668,8 +608,7 @@ mgParts::operator+=(mgParts a)
return *this;
}
-mgRefParts::mgRefParts(const mgReference* r)
-{
+mgRefParts::mgRefParts(const mgReference* r) {
tables.push_back(r->t1());
tables.push_back(r->t2());
clauses.push_back(r->t1() + '.' + r->f1() + '=' + r->t2() + '.' + r->f2());
@@ -685,25 +624,21 @@ mgParts::Dump(string where) const
}
void
-mgParts::ConnectAllTables()
-{
+mgParts::ConnectAllTables() {
tables.sort();
tables.unique();
strlist::reverse_iterator rit;
string prevtable = "";
rest.InitReferences();
positives.clear();
- for (rit = tables.rbegin(); rit != tables.rend(); ++rit)
- {
- if (!prevtable.empty())
- {
+ for (rit = tables.rbegin(); rit != tables.rend(); ++rit) {
+ if (!prevtable.empty()) {
rest.InitReferences();
ConnectTables(prevtable,*rit);
}
prevtable = *rit;
}
- for (unsigned int i = 0 ; i < positives.size(); i++)
- {
+ for (unsigned int i = 0 ; i < positives.size(); i++) {
*this += mgRefParts(positives[i]);
}
tables.sort();
@@ -713,8 +648,7 @@ mgParts::ConnectAllTables()
}
void
-mgParts::Prepare()
-{
+mgParts::Prepare() {
ConnectAllTables();
clauses.sort();
clauses.unique();
@@ -725,13 +659,10 @@ mgParts::Prepare()
}
void
-mgParts::push_table_to_front(string table)
-{
+mgParts::push_table_to_front(string table) {
strlist::iterator it;
- for (it = tables.begin(); it != tables.end(); ++it)
- {
- if (*it==table)
- {
+ for (it = tables.begin(); it != tables.end(); ++it) {
+ if (*it==table) {
tables.erase(it);
tables.push_front(table);
break;
@@ -740,22 +671,19 @@ mgParts::push_table_to_front(string table)
}
string
-mgParts::sql_select(bool distinct)
-{
+mgParts::sql_select(bool distinct) {
if (!special_statement.empty())
return special_statement;
Prepare();
string result;
- if (distinct)
- {
+ if (distinct) {
idfields.push_front("1");
idfields.push_front("COUNT(*)");
result = sql_list("SELECT",idfields);
idfields.pop_front();
idfields.pop_front();
}
- else
- {
+ else {
strlist::iterator p = find(tables.begin(),tables.end(),"tracks");
if (p!=tables.end())
idfields.push_front("tracks.id");
@@ -763,12 +691,10 @@ mgParts::sql_select(bool distinct)
result = sql_list("SELECT",idfields+valuefields);
idfields.pop_front();
}
- if (!result.empty())
- {
+ if (!result.empty()) {
result += sql_list(" FROM",tables);
result += sql_list(" WHERE",clauses," AND ");
- if (distinct)
- {
+ if (distinct) {
result += sql_list(" GROUP BY",idfields);
}
}
@@ -776,8 +702,7 @@ mgParts::sql_select(bool distinct)
}
string
-mgParts::sql_selectitems()
-{
+mgParts::sql_selectitems() {
ConnectAllTables();
string result;
result = sql_list("SELECT",idfields);
@@ -787,8 +712,7 @@ mgParts::sql_selectitems()
}
string
-mgParts::sql_count()
-{
+mgParts::sql_count() {
Prepare();
#if defined (HAVE_PG) || defined(HAVE_SQLITE) || MYSQL_VERSION_ID >= 40111
string result = sql_list("SELECT COUNT(*) FROM ( SELECT",idfields,",","");
@@ -809,8 +733,7 @@ mgParts::sql_count()
return result;
}
-mgReference::mgReference(string t1,string f1,string t2,string f2)
-{
+mgReference::mgReference(string t1,string f1,string t2,string f2) {
m_t1 = t1;
m_f1 = f1;
m_t2 = t2;
@@ -818,8 +741,7 @@ mgReference::mgReference(string t1,string f1,string t2,string f2)
}
void
-mgReferences::InitReferences()
-{
+mgReferences::InitReferences() {
// define them such that no circle is possible
for (unsigned int idx = 0 ; idx < size() ; idx ++)
delete operator[](idx);
@@ -834,8 +756,7 @@ unsigned int
mgReferences::CountTable(string table) const
{
unsigned int result = 0;
- for (unsigned int i=0 ; i<size(); i++ )
- {
+ for (unsigned int i=0 ; i<size(); i++ ) {
mgReference* r = operator[](i);
if (table==r->t1() || table==r->t2())
result++;
@@ -847,24 +768,21 @@ bool
mgReference::Equal(string table1, string table2) const
{
return ((t1()==table1) && (t2()==table2))
- || ((t1()==table2) && (t2()==table1));
+ || ((t1()==table2) && (t2()==table1));
}
void
-mgParts::ConnectTables(string table1, string table2)
-{
+mgParts::ConnectTables(string table1, string table2) {
// same table?
if (table1 == table2)
return;
// backend specific:
- if (table1=="genre")
- {
+ if (table1=="genre") {
ConnectTables("tracks",table2);
return;
}
- if (table2=="genre")
- {
+ if (table2=="genre") {
ConnectTables("tracks",table1);
return;
}
@@ -875,70 +793,55 @@ mgParts::ConnectTables(string table1, string table2)
if (table2.find(" AS ")!=string::npos) return;
// now the generic part:
- for (unsigned int i=0 ; i<rest.size(); i++ )
- {
+ for (unsigned int i=0 ; i<rest.size(); i++ ) {
mgReference* r = rest[i];
- if (r->Equal(table1,table2))
- {
+ if (r->Equal(table1,table2)) {
rest.erase(rest.begin()+i);
positives.push_back(r);
return;
}
}
-again:
- for (unsigned int i=0 ; i<rest.size(); i++ )
- {
+ again:
+ for (unsigned int i=0 ; i<rest.size(); i++ ) {
mgReference* r = rest[i];
unsigned int ct1=rest.CountTable(r->t1());
unsigned int ct2=rest.CountTable(r->t2());
- if (ct1==1 || ct2==1)
- {
+ if (ct1==1 || ct2==1) {
rest.erase(rest.begin()+i);
- if (ct1==1 && ct2==1)
- {
- if (r->Equal(table1,table2))
- {
+ if (ct1==1 && ct2==1) {
+ if (r->Equal(table1,table2)) {
positives.push_back(r);
return;
}
- else
- {
+ else {
delete r;
continue;
}
}
- else if (ct1==1)
- {
- if (r->t1()==table1)
- {
+ else if (ct1==1) {
+ if (r->t1()==table1) {
positives.push_back(r);
ConnectTables(r->t2(),table2);
}
- else if (r->t1()==table2)
- {
+ else if (r->t1()==table2) {
positives.push_back(r);
ConnectTables(table1,r->t2());
}
- else
- {
+ else {
delete r;
goto again;
}
}
- else
- {
- if (r->t2()==table1)
- {
+ else {
+ if (r->t2()==table1) {
positives.push_back(r);
ConnectTables(r->t1(),table2);
}
- else if (r->t2()==table2)
- {
+ else if (r->t2()==table2) {
positives.push_back(r);
ConnectTables(table1,r->t1());
}
- else
- {
+ else {
delete r;
goto again;
}
@@ -947,97 +850,91 @@ again:
}
}
-
-mgParts::mgParts()
-{
+mgParts::mgParts() {
special_statement="";
orderByCount = false;
}
-mgParts::~mgParts()
-{
+mgParts::~mgParts() {
}
unsigned long
-mgDb::exec_count( const string sql)
-{
+mgDb::exec_count( const string sql) {
unsigned long result = 0;
if (Connect())
result = atol (get_col0 ( sql).c_str ());
return result;
}
-
-struct genres_t {
- char *id;
+struct genres_t
+{
+ const char *id;
int id3genre;
- char *name;
+ const char *name;
};
-struct lang_t {
- char *id;
- char *name;
+struct lang_t
+{
+ const char *id;
+ const char *name;
};
-struct musictypes_t {
- char *name;
+struct musictypes_t
+{
+ const char *name;
};
-struct sources_t {
- char *name;
+struct sources_t
+{
+ const char *name;
};
-void mgDb::FillTables()
-{
+void mgDb::FillTables() {
#include "mg_tables.h"
- int len = sizeof( genres ) / sizeof( genres_t );
- StartTransaction();
- Execute("INSERT INTO genre (id,genre) VALUES('NULL','No Genre')");
- for( int i=0; i < len; i ++ )
- {
- char b[600];
- char id3genre[5];
- if (genres[i].id3genre>=0)
- sprintf(id3genre,"%d",genres[i].id3genre);
- else
- strcpy(id3genre,"NULL");
- sprintf(b,"INSERT INTO genre (id,id3genre,genre) VALUES ('%s',%s,%s)",
- genres[i].id,id3genre,mgSQLString(genres[i].name).quoted());
- Execute(b);
- }
- len = sizeof( languages ) / sizeof( lang_t );
- Execute("INSERT INTO language (id,language) VALUES('NULL','Instrumental')");
- for( int i=0; i < len; i ++ )
- {
- char b[600];
- char id[4];
- char lang[41];
- strncpy(id,languages[i].id,3);
- id[4]=0;
- strncpy(lang,languages[i].name,40);
- lang[40]=0;
- sprintf(b,"INSERT INTO language (id,language) VALUES('%s',%s)",
- id,
- mgSQLString(lang).quoted());
- Execute(b);
- }
- len = sizeof( musictypes ) / sizeof( musictypes_t );
- for( int i=0; i < len; i ++ )
- {
- char b[600];
- sprintf(b,"INSERT INTO musictype (musictype) VALUES('%s')",
- musictypes[i].name);
- Execute(b);
- }
- len = sizeof( sources ) / sizeof( sources_t );
- for( int i=0; i < len; i ++ )
- {
- char b[600];
- sprintf(b,"INSERT INTO source (source) VALUES('%s')",
- sources[i].name);
- Execute(b);
- }
- Commit();
+ int len = sizeof( genres ) / sizeof( genres_t );
+ StartTransaction();
+ Execute("INSERT INTO genre (id,genre) VALUES('NULL','No Genre')");
+ for( int i=0; i < len; i ++ ) {
+ char b[600];
+ char id3genre[5];
+ if (genres[i].id3genre>=0)
+ sprintf(id3genre,"%d",genres[i].id3genre);
+ else
+ strcpy(id3genre,"NULL");
+ sprintf(b,"INSERT INTO genre (id,id3genre,genre) VALUES ('%s',%s,%s)",
+ genres[i].id,id3genre,mgSQLString(genres[i].name).quoted());
+ Execute(b);
+ }
+ len = sizeof( languages ) / sizeof( lang_t );
+ Execute("INSERT INTO language (id,language) VALUES('NULL','Instrumental')");
+ for( int i=0; i < len; i ++ ) {
+ char b[600];
+ char id[4];
+ char lang[41];
+ strncpy(id,languages[i].id,3);
+ id[4]=0;
+ strncpy(lang,languages[i].name,40);
+ lang[40]=0;
+ sprintf(b,"INSERT INTO language (id,language) VALUES('%s',%s)",
+ id,
+ mgSQLString(lang).quoted());
+ Execute(b);
+ }
+ len = sizeof( musictypes ) / sizeof( musictypes_t );
+ for( int i=0; i < len; i ++ ) {
+ char b[600];
+ sprintf(b,"INSERT INTO musictype (musictype) VALUES('%s')",
+ musictypes[i].name);
+ Execute(b);
+ }
+ len = sizeof( sources ) / sizeof( sources_t );
+ for( int i=0; i < len; i ++ ) {
+ char b[600];
+ sprintf(b,"INSERT INTO source (source) VALUES('%s')",
+ sources[i].name);
+ Execute(b);
+ }
+ Commit();
}
mgSQLString
@@ -1054,15 +951,13 @@ mgDb::Build_cddbid(const mgSQLString& artist) const
mgSQLString
mgDb::getAlbum(const char *filename,const mgSQLString& c_album,
- const mgSQLString& c_artist)
-{
+const mgSQLString& c_artist) {
char *b;
msprintf(&b,"SELECT cddbid FROM album"
- " WHERE title=%s AND artist=%s",c_album.quoted(),c_artist.quoted());
+ " WHERE title=%s AND artist=%s",c_album.quoted(),c_artist.quoted());
mgSQLString result(get_col0(b));
free(b);
- if (result=="NULL")
- {
+ if (result=="NULL") {
char *directory = strdup(filename);
char *slash=strrchr(directory,'/');
if (slash)
@@ -1083,17 +978,15 @@ mgDb::getAlbum(const char *filename,const mgSQLString& c_album,
free(b);
long new_album_artists = q.Rows();
mgSQLString buf("");
- if (new_album_artists==1)
- {
+ if (new_album_artists==1) {
buf=mgSQLString(q.Next()[0]);
if (buf==c_artist)
new_album_artists++;
}
else
buf="";
- if (new_album_artists>1 && strcmp(buf.original(),"Various Artists"))
- // is the album multi artist and not yet marked as such?
- {
+ if (new_album_artists>1 && strcmp(buf.original(),"Various Artists")) {
+ // is the album multi artist and not yet marked as such?
msprintf(&b,"SELECT album.cddbid FROM album, tracks %s",where);
result=mgSQLString(get_col0(b));
free(b);
@@ -1101,16 +994,16 @@ mgDb::getAlbum(const char *filename,const mgSQLString& c_album,
Execute(b);
free(b);
// here we could change all tracks.sourceid to result and delete
- // the other album entries for this album, but that should only
+ // the other album entries for this album, but that should only
// be needed if a pre 0.1.4 import has been done incorrectly, so we
// don't bother
}
- else
- { // no usable album found
+ else {
+ // no usable album found
result=Build_cddbid(c_artist);
char *b;
msprintf(&b,"INSERT INTO album (title,artist,cddbid) "
- "VALUES(%s,%s,%s)",
+ "VALUES(%s,%s,%s)",
c_album.quoted(),c_artist.quoted(),result.quoted());
int rows = Execute(b);
free(b);
@@ -1125,96 +1018,109 @@ mgDb::getAlbum(const char *filename,const mgSQLString& c_album,
TagLib::String
mgDb::getId3v2Tag(TagLib::ID3v2::Tag *id3v2tags,const char *name) const
{
- TagLib::String result;
- TagLib::ID3v2::FrameList l = id3v2tags->frameListMap()[name];
- if (!l.isEmpty())
- result = l.front()->toString();
- return result;
+ TagLib::String result;
+ TagLib::ID3v2::FrameList l = id3v2tags->frameListMap()[name];
+ if (!l.isEmpty())
+ result = l.front()->toString();
+ return result;
+}
+
+TagLib::String
+mgDb::getVorbisComment(const TagLib::Ogg::FieldListMap& vorbiscomments,const char* key) {
+ TagLib::String result;
+ TagLib::StringList sl = vorbiscomments[key];
+ if (sl.size()) {
+ result = sl[0];
+ if (result.size())
+ mgDebug(9,"found vorbiscomment %s %s",key,
+ result.toCString(the_setup.utf8));
+ }
+ return result;
}
void
-mgDb::get_tags(TagLib::ID3v2::Tag *tags)
-{
- if (!tags)
- return;
- m_TLAN = getId3v2Tag(tags,"TLAN");
- m_TCON = getId3v2Tag(tags,"TCON");
+mgDb::get_id3_tags(TagLib::ID3v2::Tag *tags) {
+ if (tags) {
+ if (m_TLAN.size()==0) m_TLAN = getId3v2Tag(tags,"TLAN");
+ if (m_TCON.size()==0) m_TCON = getId3v2Tag(tags,"TCON");
+ }
}
void
-mgDb::get_ID3v2_Tags(const char *filename)
-{
- if (!strcasecmp(extension(filename),"flac"))
- {
- TagLib::FLAC::File f(filename);
- get_tags(f.ID3v2Tag());
- }
- else if (!strcasecmp(extension(filename),"mp3"))
- {
- TagLib::MPEG::File f(filename);
- get_tags(f.ID3v2Tag());
- }
+mgDb::get_vorbis_tags(const TagLib::Ogg::FieldListMap& vorbiscomments) {
+ if (vorbiscomments.size()) {
+ m_TLAN=getVorbisComment(vorbiscomments,"LANGUAGE");
+ m_TCON=getVorbisComment(vorbiscomments,"GENRE");
+ }
}
void
-mgDb::DefineGenre(const string genre)
-{
- mgQuery q1(DbHandle(),"SELECT id FROM genre WHERE id ='z'" );
- if (q1.Rows()==0)
- {
- Execute("INSERT INTO genre (id,genre) VALUES('z','Extra')");
- for (char c='a';c<='z';c++)
- {
- char g[20];
- strcpy(g,"Extra");
- if (c!='a')
- sprintf(strchr(g,0)," %c",c);
- char *b;
- msprintf(&b,"INSERT INTO genre (id,genre) VALUES('z%c','%s')",c,g);
- Execute(b);
- free(b);
+mgDb::get_ID3v2_Tags(const char *filename) {
+ m_TLAN="";
+ m_TCON="";
+ if (!strcasecmp(extension(filename),"flac")) {
+ TagLib::FLAC::File f(filename);
+ TagLib::Ogg::XiphComment* m_tagV2 = f.xiphComment();
+ get_id3_tags(f.ID3v2Tag());
+ get_vorbis_tags(m_tagV2->fieldListMap());
}
- }
- mgQuery q(DbHandle(),"SELECT id FROM genre WHERE id LIKE 'z__'" );
- char **r;
- char *newid=0;
- while ((r = q.Next()))
- {
- newid = r[0];
- }
- if (!newid)
- newid="zaa";
- else
- {
- newid[2]++;
- if (newid[2]>'z')
- {
- newid[1]++;
- if (newid[1]>'z')
- return;
- newid[2]='a';
+ else if (!strcasecmp(extension(filename),"mp3")) {
+ TagLib::MPEG::File f(filename);
+ get_id3_tags(f.ID3v2Tag());
}
- }
- char *b;
- mgSQLString c_genre(genre);
- msprintf(&b,"INSERT INTO genre (id,genre) VALUES('%s',%s)",newid,c_genre.quoted());
- Execute(b);
- free(b);
- m_Genres[genre]=newid;
- mgDebug(1,"Added new genre %s",genre.c_str());
-}
-
-mgSQLString
-mgDb::getGenre1(TagLib::FileRef& f)
-{
+}
+
+void
+mgDb::DefineGenre(const string genre) {
+ mgQuery q1(DbHandle(),"SELECT id FROM genre WHERE id ='z'" );
+ if (q1.Rows()==0) {
+ Execute("INSERT INTO genre (id,genre) VALUES('z','Extra')");
+ for (char c='a';c<='z';c++) {
+ char g[20];
+ strcpy(g,"Extra");
+ if (c!='a')
+ sprintf(strchr(g,0)," %c",c);
+ char *b;
+ msprintf(&b,"INSERT INTO genre (id,genre) VALUES('z%c','%s')",c,g);
+ Execute(b);
+ free(b);
+ }
+ }
+ mgQuery q(DbHandle(),"SELECT id FROM genre WHERE id LIKE 'z__'" );
+ char **r;
+ char newid[4];
+ newid[0]=0;
+ while ((r = q.Next())) {
+ strncpy(newid,r[0],4);
+ }
+ if (!newid[0])
+ strcpy(newid,"zaa");
+ else {
+ newid[2]++;
+ if (newid[2]>'z') {
+ newid[1]++;
+ if (newid[1]>'z')
+ return;
+ newid[2]='a';
+ }
+ }
+ char *b;
+ mgSQLString c_genre(genre);
+ msprintf(&b,"INSERT INTO genre (id,genre) VALUES('%s',%s)",newid,c_genre.quoted());
+ Execute(b);
+ free(b);
+ m_Genres[genre]=newid;
+ mgDebug(1,"Added new genre %s",genre.c_str());
+}
+
+mgSQLString
+mgDb::getGenre1(TagLib::FileRef& f) {
string genre1 = f.tag()->genre().toCString(the_setup.utf8);
- if (genre1.empty())
- {
+ if (genre1.empty()) {
genre1 = m_TCON.toCString(the_setup.utf8);
const char *tcon=genre1.c_str();
char *rparen=strchr(tcon,')');
- if (tcon[0]=='(' && rparen)
- {
+ if (tcon[0]=='(' && rparen) {
*rparen=0;
genre1 = m_GenreIds[tcon+1];
}
@@ -1227,9 +1133,9 @@ mgDb::getGenre1(TagLib::FileRef& f)
}
bool
-mgDb::SyncFile(const char *filename) // returns true if a new file is imported
-{
- char *ext = extension(filename);
+ // returns true if a new file is imported
+mgDb::SyncFile(const char *filename) {
+ char *ext = extension(filename);
if (strcasecmp(ext,"flac")
&& strcasecmp(ext,"wav")
&& strcasecmp(ext,"ogg")
@@ -1237,13 +1143,13 @@ mgDb::SyncFile(const char *filename) // returns true if a new file is imported
return false;
char sql[7000];
- if (!strncmp(filename,"./",2)) // strip leading ./
+ // strip leading ./
+ if (!strncmp(filename,"./",2))
filename += 2;
const char *cfilename=filename;
if (isdigit(filename[0]) && isdigit(filename[1]) && filename[2]=='/' && !strchr(filename+3,'/'))
cfilename=cfilename+3;
- if (strlen(cfilename)>255)
- {
+ if (strlen(cfilename)>255) {
mgWarning("Length of file exceeds database field capacity: %s", filename);
return false;
}
@@ -1268,19 +1174,16 @@ mgDb::SyncFile(const char *filename) // returns true if a new file is imported
int bitrate = 0;
int samplerate = 0;
int channels = 0;
- if (!f.isNull())
- {
+ if (!f.isNull()) {
TagLib::AudioProperties *ap = f.audioProperties();
- if (ap)
- {
+ if (ap) {
length = ap->length();
bitrate = ap->bitrate();
samplerate = ap->sampleRate();
channels = ap->channels();
}
}
- if (!f.isNull() && f.tag())
- {
+ if (!f.isNull() && f.tag()) {
c_artist = f.tag()->artist();
c_album = f.tag()->album();
c_title = f.tag()->title();
@@ -1292,40 +1195,51 @@ mgDb::SyncFile(const char *filename) // returns true if a new file is imported
c_album = "Unassigned";
mgSQLString c_lang(m_TLAN);
mgSQLString c_cddbid(getAlbum(filename,c_album,c_artist));
- mgSQLString c_mp3file(cfilename);
+
+ char cwd[5000];
+ if (!getcwd(cwd,4999)) {
+ std::cout << "Path too long" << std::endl;
+ exit (1);
+ }
+ int tldlen = strlen(the_setup.ToplevelDir);
+ strcat(cwd,"/");
+ int cwdlen = strlen(cwd);
+ const char *relpath=cwd;
+ if (cwdlen>tldlen); relpath += tldlen;
+ char *b;
+ msprintf(&b,"%s%s",relpath,cfilename);
+ mgSQLString c_mp3file(b);
+ free(b);
sprintf(sql,"SELECT id from tracks WHERE mp3file=%s",c_mp3file.quoted());
string id = get_col0(sql);
- if (id!="NULL")
- {
- sprintf(sql,"UPDATE tracks SET artist=%s, title=%s,year=%d,sourceid=%s,"
- "tracknb=%d,length=%d,bitrate=%d,samplerate=%d,"
- "channels=%d,genre1=%s,lang=%s WHERE id=%ld",
- c_artist.quoted(),c_title.quoted(),year,c_cddbid.quoted(),
- track,length,bitrate,samplerate,
- channels,c_genre1.quoted(),c_lang.quoted(),atol(id.c_str()));
- }
- else
- {
- sprintf(sql,"INSERT INTO tracks "
- "(artist,title,year,sourceid,"
- "tracknb,mp3file,length,bitrate,samplerate,"
- "channels,genre1,genre2,lang,folder1,folder2,"
- "folder3,folder4) "
- "VALUES (%s,%s,%u,%s,"
- "%u,%s,%d,%d,%d,"
- "%d,%s,'',%s,%s,%s,%s,%s)",
- c_artist.quoted(),c_title.quoted(),year,c_cddbid.quoted(),
- track,c_mp3file.quoted(),length,bitrate,samplerate,
- channels,c_genre1.quoted(),c_lang.quoted(),
- c_folder1.quoted(),c_folder2.quoted(),
- c_folder3.quoted(),c_folder4.quoted());
- }
+ if (id!="NULL") {
+ sprintf(sql,"UPDATE tracks SET artist=%s, title=%s,year=%d,sourceid=%s,"
+ "tracknb=%d,length=%d,bitrate=%d,samplerate=%d,"
+ "channels=%d,genre1=%s,lang=%s WHERE id=%ld",
+ c_artist.quoted(),c_title.quoted(),year,c_cddbid.quoted(),
+ track,length,bitrate,samplerate,
+ channels,c_genre1.quoted(),c_lang.quoted(),atol(id.c_str()));
+ }
+ else {
+ sprintf(sql,"INSERT INTO tracks "
+ "(artist,title,year,sourceid,"
+ "tracknb,mp3file,length,bitrate,samplerate,"
+ "channels,genre1,genre2,lang,folder1,folder2,"
+ "folder3,folder4) "
+ "VALUES (%s,%s,%u,%s,"
+ "%u,%s,%d,%d,%d,"
+ "%d,%s,'',%s,%s,%s,%s,%s)",
+ c_artist.quoted(),c_title.quoted(),year,c_cddbid.quoted(),
+ track,c_mp3file.quoted(),length,bitrate,samplerate,
+ channels,c_genre1.quoted(),c_lang.quoted(),
+ c_folder1.quoted(),c_folder2.quoted(),
+ c_folder3.quoted(),c_folder4.quoted());
+ }
return Execute(sql) == 1 && id=="NULL";
}
string
-mgDb::get_col0( const string sql)
-{
+mgDb::get_col0( const string sql) {
mgQuery q(DbHandle(),sql);
char **r = q.Next();
if (r)
@@ -1335,19 +1249,17 @@ mgDb::get_col0( const string sql)
}
int
-mgDb::Execute(const string sql)
-{
- if (sql.empty())
- return 0;
- if (!Connect())
- return 0;
- mgQuery q(DbHandle(),sql);
- return q.Rows();
+mgDb::Execute(const string sql) {
+ if (sql.empty())
+ return 0;
+ if (!Connect())
+ return 0;
+ mgQuery q(DbHandle(),sql);
+ return q.Rows();
}
void
-mgDb::LoadMapInto(string sql,map<string,string>*idmap,map<string,string>*valmap)
-{
+mgDb::LoadMapInto(string sql,map<string,string>*idmap,map<string,string>*valmap) {
if (!valmap && !idmap)
return;
if (!Connect())
@@ -1355,17 +1267,15 @@ mgDb::LoadMapInto(string sql,map<string,string>*idmap,map<string,string>*valmap)
mgQuery q(DbHandle(),sql);
char **row;
while ((row = q.Next()))
- if (row[0] && row[1])
- {
- if (valmap) (*valmap)[row[0]] = row[1];
- if (idmap) (*idmap)[row[1]] = row[0];
- }
+ if (row[0] && row[1]) {
+ if (valmap) (*valmap)[row[0]] = row[1];
+ if (idmap) (*idmap)[row[1]] = row[0];
+ }
}
string
-mgDb::LoadItemsInto(mgParts& what,vector<mgItem*>& items)
-{
- if (!Connect())
+mgDb::LoadItemsInto(mgParts& what,vector<mgItem*>& items) {
+ if (!Connect())
return "";
what.idfields.clear();
what.valuefields.clear();
@@ -1387,7 +1297,7 @@ mgDb::LoadItemsInto(mgParts& what,vector<mgItem*>& items)
what.idfields.push_back("album.coverimg");
what.tables.push_back("tracks");
what.tables.push_back("album");
- string result = what.sql_selectitems();
+ string result = what.sql_selectitems();
for (unsigned int idx=0;idx<items.size();idx++)
delete items[idx];
items.clear ();
@@ -1398,20 +1308,17 @@ mgDb::LoadItemsInto(mgParts& what,vector<mgItem*>& items)
return result;
}
-
string
-mgDb::LoadValuesInto(mgParts& what,mgKeyTypes tp,vector<mgListItem*>& listitems,bool distinct)
-{
- if (!Connect())
+mgDb::LoadValuesInto(mgParts& what,mgKeyTypes tp,vector<mgListItem*>& listitems,bool distinct) {
+ if (!Connect())
return "";
string result = what.sql_select(distinct);
- listitems.clear ();
+ listitems.clear ();
mgQuery q(DbHandle(),result);
if (q.Rows())
assert(q.Columns()==4 || q.Columns()==3);
char **row;
- while ((row = q.Next()))
- {
+ while ((row = q.Next())) {
if (!row[0]) continue;
if (!row[1]) continue;
if (!row[2]) continue;
@@ -1429,88 +1336,86 @@ mgDb::LoadValuesInto(mgParts& what,mgKeyTypes tp,vector<mgListItem*>& listitems,
}
int
-mgDb::AddToCollection( const string Name, const vector<mgItem*>&items,mgParts *what)
-{
- if (Name.empty())
- return 0;
- if (!Connect())
- return 0;
- CreateCollection(Name);
- string listid = KeyMaps.id(keyGdCollection,Name);
- if (listid.empty())
- return 0;
- StartTransaction();
- // insert all tracks:
- int result = 0;
- for (unsigned int i = 0; i < items.size(); i++)
- result += Execute("INSERT INTO playlistitem VALUES( " + listid + ","
+mgDb::AddToCollection( const string Name, const vector<mgItem*>&items,mgParts *what) {
+ if (Name.empty())
+ return 0;
+ if (!Connect())
+ return 0;
+ CreateCollection(Name);
+ string listid = KeyMaps.id(keyGdCollection,Name);
+ if (listid.empty())
+ return 0;
+ StartTransaction();
+ // insert all tracks:
+ int result = 0;
+ for (unsigned int i = 0; i < items.size(); i++)
+ result += Execute("INSERT INTO playlistitem VALUES( " + listid + ","
+ ltos (items[i]->getItemid ()) +")");
- Commit();
- return result;
+ Commit();
+ return result;
}
int
-mgDb::RemoveFromCollection (const string Name, const vector<mgItem*>&items,mgParts* what)
-{
- if (Name.empty())
- return 0;
- if (!Connect())
- return 0;
- string listid = KeyMaps.id(keyGdCollection,Name);
- if (listid.empty())
- return 0;
- StartTransaction();
- // remove all tracks:
- int result = 0;
- for (unsigned int i = 0; i < items.size(); i++)
- result += Execute("DELETE FROM playlistitem WHERE playlist="+listid+
+mgDb::RemoveFromCollection (const string Name, const vector<mgItem*>&items,mgParts* what) {
+ if (Name.empty())
+ return 0;
+ if (!Connect())
+ return 0;
+ string listid = KeyMaps.id(keyGdCollection,Name);
+ if (listid.empty())
+ return 0;
+ StartTransaction();
+ // remove all tracks:
+ int result = 0;
+ for (unsigned int i = 0; i < items.size(); i++)
+ result += Execute("DELETE FROM playlistitem WHERE playlist="+listid+
" AND trackid = " + ltos (items[i]->getItemid ()));
- Commit();
- return result;
+ Commit();
+ return result;
}
bool
-mgDb::DeleteCollection (const string Name)
-{
- if (!Connect()) return false;
- ClearCollection(Name);
- return Execute (string("DELETE FROM playlist WHERE title=") + mgSQLString (Name).quoted()) == 1;
+mgDb::DeleteCollection (const string Name) {
+ if (!Connect()) return false;
+ ClearCollection(Name);
+ return Execute (string("DELETE FROM playlist WHERE title=") + mgSQLString (Name).quoted()) == 1;
}
void
-mgDb::ClearCollection (const string Name)
-{
- if (!Connect()) return;
- string listid = KeyMaps.id(keyGdCollection,Name);
- Execute (string("DELETE FROM playlistitem WHERE playlist=")+mgSQLString(listid).quoted());
+mgDb::ClearCollection (const string Name) {
+ if (!Connect()) return;
+ string listid = KeyMaps.id(keyGdCollection,Name);
+ Execute (string("DELETE FROM playlistitem WHERE playlist=")+mgSQLString(listid).quoted());
}
bool
-mgDb::CreateCollection (const string Name)
-{
- if (!Connect()) return false;
- string name = mgSQLString(Name).quoted();
- if (exec_count("SELECT count(title) FROM playlist WHERE title = " + name)>0)
- return false;
- Execute ("INSERT INTO playlist (title,author,created) VALUES(" + name + ",'VDR',"+Now()+")");
- return true;
+mgDb::CreateCollection (const string Name) {
+ if (!Connect()) return false;
+ string name = mgSQLString(Name).quoted();
+ if (exec_count("SELECT count(title) FROM playlist WHERE title = " + name)>0)
+ return false;
+ Execute ("INSERT INTO playlist (title,author,created) VALUES(" + name + ",'VDR',"+Now()+")");
+ return true;
}
-class mgKeyGdUnique : public mgKeyNormal {
+class mgKeyGdUnique : public mgKeyNormal
+{
public:
mgKeyGdUnique() : mgKeyNormal(keyGdTrack,"tracks","id") {};
mgParts Parts(mgDb *db,bool groupby) const;
mgSortBy SortBy() const { return mgSortByIdNum; }
};
-class mgKeyGdTrack : public mgKeyNormal {
+class mgKeyGdTrack : public mgKeyNormal
+{
public:
mgKeyGdTrack() : mgKeyNormal(keyGdTrack,"tracks","tracknb") {};
mgParts Parts(mgDb *db,bool groupby) const;
mgSortBy SortBy() const { return mgSortByIdNum; }
};
-class mgKeyGdAlbum : public mgKeyNormal {
+class mgKeyGdAlbum : public mgKeyNormal
+{
public:
mgKeyGdAlbum() : mgKeyNormal(keyGdAlbum,"album","title") {};
mgParts Parts(mgDb *db,bool groupby) const;
@@ -1524,7 +1429,8 @@ mgKeyGdAlbum::Parts(mgDb *db,bool groupby) const
return result;
}
-class mgKeyGdFolder : public mgKeyNormal {
+class mgKeyGdFolder : public mgKeyNormal
+{
public:
mgKeyGdFolder(mgKeyTypes kt,const char *fname)
: mgKeyNormal(kt,"tracks",fname) { m_enabled=-1;};
@@ -1533,19 +1439,17 @@ class mgKeyGdFolder : public mgKeyNormal {
int m_enabled;
};
-
bool
-mgKeyGdFolder::Enabled(mgDb *db)
-{
- if (m_enabled<0)
- {
- if (!db->Connect()) return false;
- m_enabled = db->FieldExists("tracks", m_field);
- }
- return (m_enabled==1);
+mgKeyGdFolder::Enabled(mgDb *db) {
+ if (m_enabled<0) {
+ if (!db->Connect()) return false;
+ m_enabled = db->FieldExists("tracks", m_field);
+ }
+ return (m_enabled==1);
}
-class mgKeyGdGenres : public mgKeyNormal {
+class mgKeyGdGenres : public mgKeyNormal
+{
public:
mgKeyGdGenres() : mgKeyNormal(keyGdGenres,"tracks","genre1") {};
mgKeyGdGenres(mgKeyTypes kt) : mgKeyNormal(kt,"tracks","genre1") {};
@@ -1594,27 +1498,23 @@ mgKeyGdGenres::GenreClauses(mgDb *db,bool groupby) const
strlist g2;
if (groupby)
- if (genrelevel()==4)
- {
- g1.push_back("tracks.genre1=genre.id");
- g2.push_back("tracks.genre2=genre.id");
- }
- else
- {
- g1.push_back("substring(tracks.genre1,1,"+ltos(genrelevel())+")=genre.id");
- g2.push_back("substring(tracks.genre2,1,"+ltos(genrelevel())+")=genre.id");
- }
+ if (genrelevel()==4) {
+ g1.push_back("tracks.genre1=genre.id");
+ g2.push_back("tracks.genre2=genre.id");
+ }
+ else {
+ g1.push_back("substr(tracks.genre1,1,"+ltos(genrelevel())+")=genre.id");
+ g2.push_back("substr(tracks.genre2,1,"+ltos(genrelevel())+")=genre.id");
+ }
- if (valid())
- {
+ if (valid()) {
unsigned int len=genrelevel();
if (len==4) len=0;
- g1.push_back(IdClause(db,"tracks.genre1",0,genrelevel()));
- g2.push_back(IdClause(db,"tracks.genre2",0,genrelevel()));
+ g1.push_back(IdClause(db,"tracks.genre1",0,genrelevel()));
+ g2.push_back(IdClause(db,"tracks.genre2",0,genrelevel()));
}
- if (db->NeedGenre2())
- {
+ if (db->NeedGenre2()) {
string o1=sql_list("(",g1," AND ",")");
if (o1.empty())
return "";
@@ -1625,27 +1525,25 @@ mgKeyGdGenres::GenreClauses(mgDb *db,bool groupby) const
return sql_list("",g1," AND ");
}
-
mgParts
mgKeyGdGenres::Parts(mgDb *db,bool groupby) const
{
mgParts result;
- result.clauses.push_back(GenreClauses(db,groupby));
+ result.clauses.push_back(GenreClauses(db,groupby));
result.tables.push_back("tracks");
- if (groupby)
- {
+ if (groupby) {
result.valuefields.push_back("genre.genre");
if (genrelevel()==4)
result.idfields.push_back("genre.id");
else
- result.idfields.push_back("substring(genre.id,1,"+ltos(genrelevel())+")");
+ result.idfields.push_back("substr(genre.id,1,"+ltos(genrelevel())+")");
result.tables.push_back("genre");
}
return result;
}
-
-class mgKeyGdLanguage : public mgKeyNormal {
+class mgKeyGdLanguage : public mgKeyNormal
+{
public:
mgKeyGdLanguage() : mgKeyNormal(keyGdLanguage,"tracks","lang") {};
mgParts Parts(mgDb *db,bool groupby) const;
@@ -1653,36 +1551,37 @@ class mgKeyGdLanguage : public mgKeyNormal {
string map_sql() const { return "SELECT id,language FROM language"; }
};
-class mgKeyGdCollection: public mgKeyNormal {
+class mgKeyGdCollection: public mgKeyNormal
+{
public:
- mgKeyGdCollection() : mgKeyNormal(keyGdCollection,"playlist","id") {};
- mgParts Parts(mgDb *db,bool groupby) const;
+ mgKeyGdCollection() : mgKeyNormal(keyGdCollection,"playlist","id") {};
+ mgParts Parts(mgDb *db,bool groupby) const;
protected:
- string map_sql() const { return "SELECT id,title FROM playlist"; }
+ string map_sql() const { return "SELECT id,title FROM playlist"; }
};
-class mgKeyGdCollectionItem : public mgKeyNormal {
+class mgKeyGdCollectionItem : public mgKeyNormal
+{
public:
mgKeyGdCollectionItem() : mgKeyNormal(keyGdCollectionItem,"playlistitem","trackid") {};
mgParts Parts(mgDb *db,bool groupby) const;
mgSortBy SortBy() const { return mgSortNone; }
};
-class mgKeyDecade : public mgKeyNormal {
+class mgKeyDecade : public mgKeyNormal
+{
public:
mgKeyDecade() : mgKeyNormal(keyGdDecade,"tracks","year") {}
string expr(mgDb* db) const { return db->DecadeExpr(); }
};
mgKey*
-ktGenerate(const mgKeyTypes kt)
-{
+ktGenerate(const mgKeyTypes kt) {
mgKey* result = 0;
- switch (kt)
- {
- case keyGdGenres: result = new mgKeyGdGenres;break;
- case keyGdGenre1: result = new mgKeyGdGenre1;break;
- case keyGdGenre2: result = new mgKeyGdGenre2;break;
- case keyGdGenre3: result = new mgKeyGdGenre3;break;
+ switch (kt) {
+ case keyGdGenres: result = new mgKeyGdGenres;break;
+ case keyGdGenre1: result = new mgKeyGdGenre1;break;
+ case keyGdGenre2: result = new mgKeyGdGenre2;break;
+ case keyGdGenre3: result = new mgKeyGdGenre3;break;
case keyGdFolder1:result = new mgKeyGdFolder(keyGdFolder1,"folder1");break;
case keyGdFolder2:result = new mgKeyGdFolder(keyGdFolder2,"folder2");break;
case keyGdFolder3:result = new mgKeyGdFolder(keyGdFolder3,"folder3");break;
@@ -1723,8 +1622,7 @@ mgKeyGdTrack::Parts(mgDb *db,bool groupby) const
mgParts result;
result.tables.push_back("tracks");
AddIdClause(db,result,"tracks.tracknb");
- if (groupby)
- {
+ if (groupby) {
result.valuefields.push_back("tracks.title");
result.idfields.push_back("tracks.tracknb");
}
@@ -1737,8 +1635,7 @@ mgKeyGdLanguage::Parts(mgDb *db,bool groupby) const
mgParts result;
AddIdClause(db,result,"tracks.lang");
result.tables.push_back("tracks");
- if (groupby)
- {
+ if (groupby) {
result.valuefields.push_back("language.language");
result.idfields.push_back("tracks.lang");
result.tables.push_back("language");
@@ -1750,15 +1647,13 @@ mgParts
mgKeyGdCollection::Parts(mgDb *db,bool groupby) const
{
mgParts result;
- if (groupby)
- {
+ if (groupby) {
result.tables.push_back("playlist");
AddIdClause(db,result,"playlist.id");
result.valuefields.push_back("playlist.title");
result.idfields.push_back("playlist.id");
}
- else
- {
+ else {
result.tables.push_back("playlistitem");
AddIdClause(db,result,"playlistitem.playlist");
}
@@ -1770,8 +1665,7 @@ mgKeyGdCollectionItem::Parts(mgDb *db,bool groupby) const
{
mgParts result;
result.tables.push_back("playlistitem");
- if (groupby)
- {
+ if (groupby) {
result.tables.push_back("tracks");
result.valuefields.push_back("tracks.title");
result.idfields.push_back("tracks.id");
diff --git a/mg_db.h b/mg_db.h
index ff35401..d358618 100644
--- a/mg_db.h
+++ b/mg_db.h
@@ -1,4 +1,4 @@
-/*!
+/*!
* \file mg_db.h
* \brief A generic capsule around database access
*
@@ -20,6 +20,7 @@
#include <tag.h>
#include <id3v2tag.h>
#include <fileref.h>
+#include <xiphcomment.h>
#ifndef trdb
#define trdb(a) (a)
@@ -36,10 +37,10 @@ typedef list<string> strlist;
strlist& operator+=(strlist&a, strlist b);
-
string sql_list (string prefix,strlist v,string sep=",",string postfix="");
-class mgSQLStringImp {
+class mgSQLStringImp
+{
public:
mgSQLStringImp();
virtual ~mgSQLStringImp();
@@ -52,7 +53,8 @@ class mgSQLStringImp {
mutable char *m_quoted;
};
-class mgSQLString {
+class mgSQLString
+{
public:
mgSQLString(const char*s);
mgSQLString(string s);
@@ -78,7 +80,8 @@ class mgSQLString {
enum mgQueryNoise {mgQueryNormal, mgQueryWarnOnly, mgQuerySilent};
-class mgQueryImp {
+class mgQueryImp
+{
public:
mgQueryImp(void *db,string sql,mgQueryNoise noise);
virtual ~mgQueryImp() {};
@@ -99,7 +102,8 @@ class mgQueryImp {
void *m_db_handle;
};
-class mgQuery {
+class mgQuery
+{
public:
mgQuery(void *db,const char*s,mgQueryNoise noise = mgQueryNormal);
mgQuery(void *db,string s,mgQueryNoise noise = mgQueryNormal);
@@ -113,13 +117,14 @@ class mgQuery {
mgQueryImp* m_q;
};
-class mgReference {
+class mgReference
+{
public:
mgReference(string t1,string f1,string t2,string f2);
- string t1() const { return m_t1; }
- string t2() const { return m_t2; }
- string f1() const { return m_f1; }
- string f2() const { return m_f2; }
+ string t1() const { return m_t1; }
+ string t2() const { return m_t2; }
+ string f1() const { return m_f1; }
+ string f2() const { return m_f2; }
bool Equal(string table1, string table2) const;
private:
string m_t1;
@@ -128,112 +133,117 @@ class mgReference {
string m_f2;
};
-class mgReferences : public vector<mgReference*> {
-public:
- void InitReferences();
- unsigned int CountTable(string table) const;
+class mgReferences : public vector<mgReference*>
+{
+ public:
+ void InitReferences();
+ unsigned int CountTable(string table) const;
};
-
-class mgParts {
-public:
- mgParts();
- ~mgParts();
- strlist valuefields; // if idfield and valuefield are identical, define idfield only
- strlist idfields;
- strlist tables;
- strlist clauses;
- mgParts& operator+=(mgParts a);
- void Prepare();
- void ConnectAllTables();
- string sql_count();
- string sql_select(bool distinct);
- string sql_selectitems();
- bool empty() const { return tables.size()==0;}
- string special_statement;
- bool orderByCount;
- void Dump(string where) const;
-private:
- mgReferences rest;
- mgReferences positives;
- void ConnectTables(string c1, string c2);
- void push_table_to_front(string table);
+class mgParts
+{
+ public:
+ mgParts();
+ ~mgParts();
+ strlist valuefields; // if idfield and valuefield are identical, define idfield only
+ strlist idfields;
+ strlist tables;
+ strlist clauses;
+ mgParts& operator+=(mgParts a);
+ void Prepare();
+ void ConnectAllTables();
+ string sql_count();
+ string sql_select(bool distinct);
+ string sql_selectitems();
+ bool empty() const { return tables.size()==0;}
+ string special_statement;
+ bool orderByCount;
+ void Dump(string where) const;
+ private:
+ mgReferences rest;
+ mgReferences positives;
+ void ConnectTables(string c1, string c2);
+ void push_table_to_front(string table);
};
/*!
* \brief an abstract database class
- *
+ *
*/
-class mgDb {
- public:
- mgDb (bool SeparateThread=false);
- virtual ~mgDb ();
- /*! \brief executes a query and returns the integer value from
- * the first column in the first row. The query shold be a COUNT query
- * returning only one row.
- * \param query the SQL query to be executed
- */
- unsigned long exec_count(const string sql);
- virtual bool ServerConnect() { return true; }
- bool Connect();
- virtual bool ConnectDatabase() = 0;
- bool HasFolderFields() const { return m_hasfolderfields;}
- virtual int AddToCollection( const string Name,const vector<mgItem*>&items,mgParts* what=0);
- virtual int RemoveFromCollection( const string Name,const vector<mgItem*>&items,mgParts* what=0);
- virtual bool DeleteCollection( const string Name);
- virtual void ClearCollection( const string Name);
- virtual bool CreateCollection( const string Name);
-
- void Sync(char * const * path_argv);
- virtual bool FieldExists(string table, string field)=0;
- void LoadMapInto(string sql,map<string,string>*idmap,map<string,string>*valmap);
- string LoadItemsInto(mgParts& what,vector<mgItem*>& items);
- string LoadValuesInto(mgParts& what,mgKeyTypes tp,vector<mgListItem*>& listitems,bool groupby);
- virtual bool NeedGenre2() = 0;
- virtual bool Threadsafe() { return false; }
- int Execute(const string sql);
- virtual const char* Options() const =0;
- virtual const char* HelpText() const =0;
- void* DbHandle();
- virtual const char* DecadeExpr()=0;
- virtual string Now() const =0;
- virtual string Directory() const =0;
- protected:
- int m_rows;
- int m_cols;
- virtual void SyncEnd() {}
- bool SyncFile(const char *filename);
- bool m_hasfolderfields;
- bool m_separate_thread;
- time_t m_connect_time;
- time_t m_create_time;
- string get_col0(const string sql);
- virtual bool Creatable() { return true; }
- virtual bool Create() = 0;
- virtual bool Clear() = 0;
- virtual void StartTransaction() {};
- virtual void Commit() {};
- virtual bool SyncStart();
- virtual void CreateFolderFields() {};
- virtual void* ImplDbHandle() const = 0;
- private:
- TagLib::String m_TLAN;
- TagLib::String m_TCON;
- TagLib::String getId3v2Tag(TagLib::ID3v2::Tag *id3v2tags,const char *name) const;
- void get_ID3v2_Tags(const char *filename);
- void get_tags(TagLib::ID3v2::Tag *tags);
- void DefineGenre(const string genre);
- mgSQLString getGenre1(TagLib::FileRef& f);
- mgSQLString Build_cddbid(const mgSQLString& artist) const;
- mgSQLString getAlbum(const char *filename,const mgSQLString& c_album,
+class mgDb
+{
+ public:
+ mgDb (bool SeparateThread=false);
+ virtual ~mgDb ();
+ /*! \brief executes a query and returns the integer value from
+ * the first column in the first row. The query shold be a COUNT query
+ * returning only one row.
+ * \param query the SQL query to be executed
+ */
+ unsigned long exec_count(const string sql);
+ virtual bool ServerConnect() { return true; }
+ bool Connect();
+ virtual bool ConnectDatabase() = 0;
+ bool HasFolderFields() const { return m_hasfolderfields;}
+ virtual int AddToCollection( const string Name,const vector<mgItem*>&items,mgParts* what=0);
+ virtual int RemoveFromCollection( const string Name,const vector<mgItem*>&items,mgParts* what=0);
+ virtual bool DeleteCollection( const string Name);
+ virtual void ClearCollection( const string Name);
+ virtual bool CreateCollection( const string Name);
+
+ void Sync(const char * const * path_argv);
+ virtual bool FieldExists(string table, string field)=0;
+ void LoadMapInto(string sql,map<string,string>*idmap,map<string,string>*valmap);
+ string LoadItemsInto(mgParts& what,vector<mgItem*>& items);
+ string LoadValuesInto(mgParts& what,mgKeyTypes tp,vector<mgListItem*>& listitems,bool groupby);
+ virtual bool NeedGenre2() = 0;
+ virtual bool Threadsafe() { return false; }
+ int Execute(const string sql);
+ virtual const char* Options() const =0;
+ virtual const char* HelpText() const =0;
+ void* DbHandle();
+ virtual const char* DecadeExpr()=0;
+ virtual string Now() const =0;
+ virtual string Directory() const =0;
+ protected:
+ int m_rows;
+ int m_cols;
+ virtual void SyncEnd() {}
+ bool SyncFile(const char *filename);
+ bool m_hasfolderfields;
+ bool m_separate_thread;
+ time_t m_connect_time;
+ time_t m_create_time;
+ string get_col0(const string sql);
+ virtual bool Creatable() { return true; }
+ virtual bool Create() = 0;
+ virtual bool Clear() = 0;
+ virtual void StartTransaction() {};
+ virtual void Commit() {};
+ virtual bool SyncStart();
+ virtual void CreateFolderFields() {};
+ virtual void* ImplDbHandle() const = 0;
+ private:
+ TagLib::String m_TLAN;
+ TagLib::String m_TCON;
+ TagLib::String getId3v2Tag(TagLib::ID3v2::Tag *id3v2tags,const char *name) const;
+ void get_ID3v2_Tags(const char *filename);
+ TagLib::String getVorbisComment(const TagLib::Ogg::FieldListMap& vorbiscomments,const char* key);
+ void get_vorbis_tags(const TagLib::Ogg::FieldListMap& vorbiscomments);
+ void get_id3_tags(TagLib::ID3v2::Tag *tags);
+ void DefineGenre(const string genre);
+ mgSQLString getGenre1(TagLib::FileRef& f);
+ mgSQLString Build_cddbid(const mgSQLString& artist) const;
+ mgSQLString getAlbum(const char *filename,const mgSQLString& c_album,
const mgSQLString& c_artist);
- map<string,string> m_Genres;
- map<string,string> m_GenreIds;
- bool m_database_found;
- void FillTables();
+ map<string,string> m_Genres;
+ map<string,string> m_GenreIds;
+ bool m_database_found;
+ void FillTables();
};
-class mgKey {
+class mgKey
+{
public:
virtual ~mgKey() {};
virtual mgParts Parts(mgDb *db,bool groupby) const = 0;
@@ -251,7 +261,8 @@ class mgKey {
virtual string map_sql() const { return ""; }
};
-class mgKeyNormal : public mgKey {
+class mgKeyNormal : public mgKey
+{
public:
mgKeyNormal(const mgKeyNormal& k);
mgKeyNormal(const mgKeyTypes kt, string table, string field);
@@ -274,16 +285,18 @@ class mgKeyNormal : public mgKey {
string m_table;
};
-class mgKeyABC : public mgKeyNormal {
+class mgKeyABC : public mgKeyNormal
+{
public:
mgKeyABC(const mgKeyNormal& k) : mgKeyNormal(k) {}
mgKeyABC(const mgKeyTypes kt, string table, string field) : mgKeyNormal(kt,table,field) {}
- virtual string expr(mgDb*db) const { return "substring("+mgKeyNormal::expr(db)+",1,1)"; }
+ virtual string expr(mgDb*db) const { return "substr("+mgKeyNormal::expr(db)+",1,1)"; }
protected:
//void AddIdClause(mgDb *db,mgParts &result,string what) const;
};
-class mgKeyDate : public mgKeyNormal {
+class mgKeyDate : public mgKeyNormal
+{
public:
mgKeyDate(mgKeyTypes kt,string table, string field) : mgKeyNormal(kt,table,field) {}
};
@@ -293,13 +306,14 @@ ktGenerate(const mgKeyTypes kt);
mgDb* GenerateDB(bool SeparateThread=false);
/*! \brief if the SQL command works on only 1 table, remove all table
-* qualifiers. Example: SELECT X.title FROM X becomes SELECT title
-* FROM X
-* \param spar the sql command. It will be edited in place
-* \return the new sql command is also returned
-*/
+ * qualifiers. Example: SELECT X.title FROM X becomes SELECT title
+ * FROM X
+ * \param spar the sql command. It will be edited in place
+ * \return the new sql command is also returned
+ */
extern string optimize(string& spar);
-class mgKeyMaps {
+class mgKeyMaps
+{
public:
string value(mgKeyTypes kt, string idstr) const;
string id(mgKeyTypes kt, string valstr) const;
@@ -309,7 +323,8 @@ class mgKeyMaps {
extern mgKeyMaps KeyMaps;
-class mgDbServerImp {
+class mgDbServerImp
+{
public:
mgDbServerImp() {m_escape_db = 0;}
virtual ~mgDbServerImp() {delete m_escape_db;}
@@ -318,7 +333,8 @@ class mgDbServerImp {
mgDb* m_escape_db;
};
-class mgDbServer {
+class mgDbServer
+{
private:
mgDbServerImp *m_server;
public:
@@ -328,5 +344,4 @@ class mgDbServer {
};
extern mgDbServer* DbServer;
-
#endif
diff --git a/mg_db_gd_mysql.c b/mg_db_gd_mysql.c
index a2d43f8..6da92ea 100644
--- a/mg_db_gd_mysql.c
+++ b/mg_db_gd_mysql.c
@@ -1,4 +1,4 @@
-/*!
+/*!
* \file mg_db_gd_mysql.c
* \brief A capsule around database access
*
@@ -22,73 +22,65 @@
using namespace std;
-mgSQLStringMySQL::~mgSQLStringMySQL()
-{
+mgSQLStringMySQL::~mgSQLStringMySQL() {
if (m_unquoted)
free(m_unquoted);
}
-mgSQLStringMySQL::mgSQLStringMySQL(const char*s)
-{
+mgSQLStringMySQL::mgSQLStringMySQL(const char*s) {
m_unquoted = 0;
m_original = s;
}
-char*
+char*
mgSQLStringMySQL::unquoted() const
{
- if (!m_unquoted)
- {
+ if (!m_unquoted) {
int buflen=2*strlen(m_original)+3;
- m_unquoted = (char *) malloc( buflen);
+ m_unquoted = (char *) malloc( buflen);
mgDb* esc = DbServer->EscapeDb();
- if (esc && esc->DbHandle())
- mysql_real_escape_string( (MYSQL*)esc->DbHandle(),
+ if (esc && esc->DbHandle())
+ mysql_real_escape_string( (MYSQL*)esc->DbHandle(),
m_unquoted, m_original, strlen(m_original) );
- else
+ else
strcpy(m_unquoted,m_original);
}
return m_unquoted;
}
-
mgQueryMySQL::mgQueryMySQL(void* db,string sql,mgQueryNoise noise)
- : mgQueryImp(db,sql,noise)
-{
+: mgQueryImp(db,sql,noise) {
m_db = (MYSQL*)m_db_handle;
m_table = 0;
if ((m_rc=mysql_query(m_db,m_optsql)))
m_errormessage = mysql_error (m_db);
- else
- {
+ else {
m_table = mysql_store_result (m_db);
- m_rows = mysql_affected_rows(m_db);
+ m_rows = mysql_affected_rows(m_db);
if (m_table)
- m_columns = mysql_num_fields(m_table);
+ m_columns = mysql_num_fields(m_table);
}
HandleErrors();
}
-mgQueryMySQL::~mgQueryMySQL()
-{
+mgQueryMySQL::~mgQueryMySQL() {
mysql_free_result (m_table);
m_table = 0;
}
char **
-mgQueryMySQL::Next()
-{
+mgQueryMySQL::Next() {
if (!m_table)
return 0;
else
- return mysql_fetch_row(m_table);
+ return mysql_fetch_row(m_table);
}
const char*
mgDbGd::Options() const
{
#if !defined(HAVE_ONLY_SERVER) && MYSQL_VERSION_ID < 40111
- return "";
+ return "d";
#else
return "hspuw";
#endif
@@ -98,22 +90,21 @@ const char*
mgDbGd::HelpText() const
{
#if !defined(HAVE_ONLY_SERVER) && MYSQL_VERSION_ID < 40111
- return "";
+ return " -d DIRN, --datadir=DIRN specify directory for embedded sql data (default is \"$HOME/.muggle\")\n";
#else
return
- " -h HHHH, --host=HHHH specify database host (default is embedded or localhost)\n"
- " if the specified host is localhost, sockets will\n"
- " be used if possible.\n"
- " Otherwise the -s parameter will be ignored\n"
- " -s SSSS --socket=PATH specify database socket\n"
- " -p PPPP, --port=PPPP specify port of database server (default is )\n"
- " -u UUUU, --user=UUUU specify database user (default is )\n"
- " -w WWWW, --password=WWWW specify database password (default is empty)\n";
+ " -h HHHH, --host=HHHH specify database host (default is embedded or localhost)\n"
+ " if the specified host is localhost, sockets will\n"
+ " be used if possible.\n"
+ " Otherwise the -s parameter will be ignored\n"
+ " -s SSSS --socket=PATH specify database socket\n"
+ " -p PPPP, --port=PPPP specify port of database server (default is )\n"
+ " -u UUUU, --user=UUUU specify database user (default is )\n"
+ " -w WWWW, --password=WWWW specify database password (default is empty)\n";
#endif
}
-mgDb* GenerateDB(bool SeparateThread)
-{
+mgDb* GenerateDB(bool SeparateThread) {
// \todo should return different backends according to the_setup.Variant
return new mgDbGd(SeparateThread);
}
@@ -123,11 +114,9 @@ static bool needGenre2_set=false;
bool UsingEmbeddedMySQL();
-mgDbGd::mgDbGd(bool SeparateThread)
-{
- m_db = 0;
- if (m_separate_thread)
- {
+mgDbGd::mgDbGd(bool SeparateThread) {
+ m_db = 0;
+ if (m_separate_thread) {
if (Threadsafe())
mysql_thread_init();
else
@@ -135,20 +124,18 @@ mgDbGd::mgDbGd(bool SeparateThread)
}
}
-mgDbGd::~mgDbGd()
-{
- if (m_db)
- mysql_close (m_db);
- m_db = 0;
+mgDbGd::~mgDbGd() {
+ if (m_db)
+ mysql_close (m_db);
+ m_db = 0;
#if MYSQL_VERSION_ID >=400000
- if (m_separate_thread)
- mysql_thread_end();
+ if (m_separate_thread)
+ mysql_thread_end();
#endif
}
bool
-mgDbGd::Threadsafe()
-{
+mgDbGd::Threadsafe() {
#if defined THREAD_SAFE_CLIENT && MYSQL_VERSION_ID >=400000
// 3.23 does define THREAD_SAFE_CLIENT but has no mysql_thread_init.
// So we assume we should better not assume threading to be safe
@@ -159,64 +146,56 @@ mgDbGd::Threadsafe()
}
#ifndef HAVE_ONLY_SERVER
-static char *mysql_embedded_args[] =
-{
- "muggle",
- 0, // placeholder for --datadir=
+static char *mysql_embedded_args[] = {
+ "muggle",
+ 0, // placeholder for --datadir=
"--key_buffer_size=32M"
};
static void
-wrong_embedded_mysql_for_external_server(int version)
-{
+wrong_embedded_mysql_for_external_server(int version) {
mgError("You are using the embedded mysql library. For accessing external servers "
"you need mysql 040111 but you have only %06d", version);
abort();
}
#endif
-mgDbServerMySQL::mgDbServerMySQL()
-{
+mgDbServerMySQL::mgDbServerMySQL() {
#ifndef HAVE_ONLY_SERVER
- static char *mysql_embedded_groups[] =
- {
- "embedded",
- "server",
- "muggle_SERVER",
- 0
- };
+ static const char *mysql_embedded_groups[4];
+ mysql_embedded_groups[0] = "embedded";
+ mysql_embedded_groups[1] = "server";
+ mysql_embedded_groups[2] = "muggle_SERVER";
+ mysql_embedded_groups[3] = 0;
int argv_size;
- if (UsingEmbeddedMySQL())
- {
+ if (UsingEmbeddedMySQL()) {
argv_size = sizeof(mysql_embedded_args) / sizeof(char *);
struct stat stbuf;
if (stat(the_setup.DbDatadir,&stbuf))
mkdir(the_setup.DbDatadir,0755);
- if (stat(the_setup.DbDatadir,&stbuf))
- {
+ if (stat(the_setup.DbDatadir,&stbuf)) {
mgError("Cannot access mysqldata directory %s: errno=%d",
- the_setup.DbDatadir,errno);
+ the_setup.DbDatadir,errno);
abort();
}
msprintf(&mysql_embedded_args[1],"--datadir=%s",the_setup.DbDatadir);
mgDebug(1,"calling mysql_server_init for embedded in %s",the_setup.DbDatadir);
}
- else
- {
+ else {
#if MYSQL_VERSION_ID < 40111
// compile time check
wrong_embedded_mysql_for_external_server(MYSQL_VERSION_ID);
#endif
#if MYSQL_VERSION_ID >= 40101
// runtime check
- if (mysql_get_client_version()<40111) // this function was added for embedded library in MySQL 4.1.1
+ // this function was added for embedded library in MySQL 4.1.1
+ if (mysql_get_client_version()<40111)
wrong_embedded_mysql_for_external_server(mysql_get_client_version());
#endif
mgDebug(1,"calling mysql_server_init for external server");
argv_size = -1;
}
- if (mysql_server_init(argv_size, mysql_embedded_args, mysql_embedded_groups))
- {
+ if (mysql_server_init(argv_size, mysql_embedded_args, (char **)mysql_embedded_groups)) {
mgError("mysql_server_init failed");
abort();
}
@@ -224,310 +203,290 @@ mgDbServerMySQL::mgDbServerMySQL()
m_escape_db = new mgDbGd;
}
-mgDbServerMySQL::~mgDbServerMySQL()
-{
+mgDbServerMySQL::~mgDbServerMySQL() {
#ifndef HAVE_ONLY_SERVER
- mgDebug(3,"calling mysql_server_end");
- mysql_server_end();
+ mgDebug(3,"calling mysql_server_end");
+ mysql_server_end();
#endif
}
-
-
-static char *db_cmds[] =
-{
- "drop table if exists album",
- "CREATE TABLE album ( "
- "artist varchar(255) default NULL, "
- "title varchar(255) default NULL, "
- "composer varchar(255) default NULL, "
- "cddbid varchar(20) NOT NULL default '', "
- "coverimg varchar(255) default NULL, "
- "covertxt mediumtext, "
- "modified date default NULL, "
- "genre varchar(10) default NULL, "
- "PRIMARY KEY (cddbid), "
- "KEY artist (artist(10)), "
- "KEY title (title(10)), "
- "KEY genre (genre), "
- "KEY modified (modified)) "
- "TYPE=MyISAM",
- "drop table if exists genre",
- "CREATE TABLE genre ("
- "id varchar(10) NOT NULL default '', "
- "id3genre smallint(6) default NULL, "
- "genre varchar(255) default NULL, "
- "freq int(11) default NULL, "
- "PRIMARY KEY (id)) "
- "TYPE=MyISAM",
- "drop table if exists language",
- "CREATE TABLE language ("
- "id varchar(4) NOT NULL default '', "
- "language varchar(40) default NULL, "
- "freq int(11) default NULL, "
- "PRIMARY KEY (id)) "
- "TYPE=MyISAM",
- "drop table if exists musictype",
- "CREATE TABLE musictype ("
- "musictype varchar(40) default NULL, "
- "id tinyint(3) unsigned NOT NULL auto_increment, "
- "PRIMARY KEY (id)) "
- "TYPE=MyISAM",
- "drop table if exists player",
- "CREATE TABLE player ( "
- "ipaddr varchar(255) NOT NULL default '', "
- "uichannel varchar(255) NOT NULL default '', "
- "logtarget int(11) default NULL, "
- "cdripper varchar(255) default NULL, "
- "mp3encoder varchar(255) default NULL, "
- "cdromdev varchar(255) default NULL, "
- "cdrwdev varchar(255) default NULL, "
- "id int(11) NOT NULL default '0', "
- "PRIMARY KEY (id)) "
- "TYPE=MyISAM",
- "drop table if exists playerstate",
- "CREATE TABLE playerstate ( "
- "playerid int(11) NOT NULL default '0', "
- "playertype int(11) NOT NULL default '0', "
- "snddevice varchar(255) default NULL, "
- "playerapp varchar(255) default NULL, "
- "playerparams varchar(255) default NULL, "
- "ptlogger varchar(255) default NULL, "
- "currtracknb int(11) default NULL, "
- "state varchar(4) default NULL, "
- "shufflepar varchar(255) default NULL, "
- "shufflestat varchar(255) default NULL, "
- "pauseframe int(11) default NULL, "
- "framesplayed int(11) default NULL, "
- "framestotal int(11) default NULL, "
- "anchortime bigint(20) default NULL, "
- "PRIMARY KEY (playerid,playertype)) "
- "TYPE=HEAP",
- "drop table if exists playlist",
- "CREATE TABLE playlist ( "
- "title varchar(255) default NULL, "
- "author varchar(255) default NULL, "
- "note varchar(255) default NULL, "
- "created timestamp(8) NOT NULL, "
- "id int(10) unsigned NOT NULL auto_increment, "
- "PRIMARY KEY (id)) "
- "TYPE=MyISAM",
- "drop table if exists playlistitem",
- "CREATE TABLE playlistitem ( "
- "playlist int(11) NOT NULL default '0', "
- "tracknumber mediumint(9) NOT NULL default '0', "
- "trackid int(11) default NULL, "
- "PRIMARY KEY (playlist,tracknumber)) "
- "TYPE=MyISAM",
- "drop table if exists playlog",
- "CREATE TABLE playlog ( "
- "trackid int(11) default NULL, "
- "played date default NULL, "
- "id tinyint(3) unsigned NOT NULL auto_increment, "
- "PRIMARY KEY (id)) "
- "TYPE=MyISAM",
- "drop table if exists recordingitem",
- "CREATE TABLE recordingitem ( "
- "trackid int(11) default NULL, "
- "recdate date default NULL, "
- "rectime time default NULL, "
- "reclength int(11) default NULL, "
- "enddate date default NULL, "
- "endtime time default NULL, "
- "repeating varchar(10) default NULL, "
- "initcmd varchar(255) default NULL, "
- "parameters varchar(255) default NULL, "
- "atqjob int(11) default NULL, "
- "id int(11) NOT NULL default '0', "
- "PRIMARY KEY (id)) "
- "TYPE=MyISAM",
- "drop table if exists source",
- "CREATE TABLE source ( "
- "source varchar(40) default NULL, "
- "id tinyint(3) unsigned NOT NULL auto_increment, "
- "PRIMARY KEY (id)) "
- "TYPE=MyISAM",
- "drop table if exists tracklistitem",
- "CREATE TABLE tracklistitem ( "
- "playerid int(11) NOT NULL default '0', "
- "listtype smallint(6) NOT NULL default '0', "
- "tracknb int(11) NOT NULL default '0', "
- "trackid int(11) NOT NULL default '0', "
- "PRIMARY KEY (playerid,listtype,tracknb)) "
- "TYPE=MyISAM",
- "drop table if exists tracks",
- "CREATE TABLE tracks ( "
- "artist varchar(255) default NULL, "
- "title varchar(255) default NULL, "
- "composer varchar(255) default NULL, "
- "genre1 varchar(10) default NULL, "
- "genre2 varchar(10) default NULL, "
- "year smallint(5) unsigned default NULL, "
- "lang varchar(4) default NULL, "
- "type tinyint(3) unsigned default NULL, "
- "rating tinyint(3) unsigned default NULL, "
- "length smallint(5) unsigned default NULL, "
- "source tinyint(3) unsigned default NULL, "
- "sourceid varchar(20) default NULL, "
- "tracknb tinyint(3) unsigned default NULL, "
- "mp3file varchar(255) default NULL, "
- "quality tinyint(3) unsigned default NULL, "
- "voladjust smallint(6) default '0', "
- "lengthfrm mediumint(9) default '0', "
- "startfrm mediumint(9) default '0', "
- "bpm smallint(6) default '0', "
- "lyrics mediumtext, "
- "moreinfo mediumtext, "
- "bitrate varchar(10) default NULL, "
- "created date default NULL, "
- "modified date default NULL, "
- "backup tinyint(3) unsigned default NULL, "
- "samplerate int(7) unsigned default NULL, "
- "channels tinyint(3) unsigned default NULL, "
- "id int(11) NOT NULL auto_increment, "
- "folder1 varchar(255), "
- "folder2 varchar(255), "
- "folder3 varchar(255), "
- "folder4 varchar(255), "
- "PRIMARY KEY (id), "
- "KEY title (title(10)), "
- "KEY mp3file (mp3file(10)), "
- "KEY genre1 (genre1), "
- "KEY genre2 (genre2), "
- "KEY year (year), "
- "KEY lang (lang), "
- "KEY type (type), "
- "KEY rating (rating), "
- "KEY sourceid (sourceid), "
- "KEY artist (artist(10))) "
- "TYPE=MyISAM"
+static const char *db_cmds[] = {
+ "drop table if exists album",
+ "CREATE TABLE album ( "
+ "artist varchar(255) default NULL, "
+ "title varchar(255) default NULL, "
+ "composer varchar(255) default NULL, "
+ "cddbid varchar(20) NOT NULL default '', "
+ "coverimg varchar(255) default NULL, "
+ "covertxt mediumtext, "
+ "modified date default NULL, "
+ "genre varchar(10) default NULL, "
+ "PRIMARY KEY (cddbid), "
+ "KEY artist (artist(10)), "
+ "KEY title (title(10)), "
+ "KEY genre (genre), "
+ "KEY modified (modified)) "
+ "TYPE=MyISAM",
+ "drop table if exists genre",
+ "CREATE TABLE genre ("
+ "id varchar(10) NOT NULL default '', "
+ "id3genre smallint(6) default NULL, "
+ "genre varchar(255) default NULL, "
+ "freq int(11) default NULL, "
+ "PRIMARY KEY (id)) "
+ "TYPE=MyISAM",
+ "drop table if exists language",
+ "CREATE TABLE language ("
+ "id varchar(4) NOT NULL default '', "
+ "language varchar(40) default NULL, "
+ "freq int(11) default NULL, "
+ "PRIMARY KEY (id)) "
+ "TYPE=MyISAM",
+ "drop table if exists musictype",
+ "CREATE TABLE musictype ("
+ "musictype varchar(40) default NULL, "
+ "id tinyint(3) unsigned NOT NULL auto_increment, "
+ "PRIMARY KEY (id)) "
+ "TYPE=MyISAM",
+ "drop table if exists player",
+ "CREATE TABLE player ( "
+ "ipaddr varchar(255) NOT NULL default '', "
+ "uichannel varchar(255) NOT NULL default '', "
+ "logtarget int(11) default NULL, "
+ "cdripper varchar(255) default NULL, "
+ "mp3encoder varchar(255) default NULL, "
+ "cdromdev varchar(255) default NULL, "
+ "cdrwdev varchar(255) default NULL, "
+ "id int(11) NOT NULL default '0', "
+ "PRIMARY KEY (id)) "
+ "TYPE=MyISAM",
+ "drop table if exists playerstate",
+ "CREATE TABLE playerstate ( "
+ "playerid int(11) NOT NULL default '0', "
+ "playertype int(11) NOT NULL default '0', "
+ "snddevice varchar(255) default NULL, "
+ "playerapp varchar(255) default NULL, "
+ "playerparams varchar(255) default NULL, "
+ "ptlogger varchar(255) default NULL, "
+ "currtracknb int(11) default NULL, "
+ "state varchar(4) default NULL, "
+ "shufflepar varchar(255) default NULL, "
+ "shufflestat varchar(255) default NULL, "
+ "pauseframe int(11) default NULL, "
+ "framesplayed int(11) default NULL, "
+ "framestotal int(11) default NULL, "
+ "anchortime bigint(20) default NULL, "
+ "PRIMARY KEY (playerid,playertype)) "
+ "TYPE=HEAP",
+ "drop table if exists playlist",
+ "CREATE TABLE playlist ( "
+ "title varchar(255) default NULL, "
+ "author varchar(255) default NULL, "
+ "note varchar(255) default NULL, "
+ "created timestamp(8) NOT NULL, "
+ "id int(10) unsigned NOT NULL auto_increment, "
+ "PRIMARY KEY (id)) "
+ "TYPE=MyISAM",
+ "drop table if exists playlistitem",
+ "CREATE TABLE playlistitem ( "
+ "playlist int(11) NOT NULL default '0', "
+ "tracknumber mediumint(9) NOT NULL default '0', "
+ "trackid int(11) default NULL, "
+ "PRIMARY KEY (playlist,tracknumber)) "
+ "TYPE=MyISAM",
+ "drop table if exists playlog",
+ "CREATE TABLE playlog ( "
+ "trackid int(11) default NULL, "
+ "played date default NULL, "
+ "id tinyint(3) unsigned NOT NULL auto_increment, "
+ "PRIMARY KEY (id)) "
+ "TYPE=MyISAM",
+ "drop table if exists recordingitem",
+ "CREATE TABLE recordingitem ( "
+ "trackid int(11) default NULL, "
+ "recdate date default NULL, "
+ "rectime time default NULL, "
+ "reclength int(11) default NULL, "
+ "enddate date default NULL, "
+ "endtime time default NULL, "
+ "repeating varchar(10) default NULL, "
+ "initcmd varchar(255) default NULL, "
+ "parameters varchar(255) default NULL, "
+ "atqjob int(11) default NULL, "
+ "id int(11) NOT NULL default '0', "
+ "PRIMARY KEY (id)) "
+ "TYPE=MyISAM",
+ "drop table if exists source",
+ "CREATE TABLE source ( "
+ "source varchar(40) default NULL, "
+ "id tinyint(3) unsigned NOT NULL auto_increment, "
+ "PRIMARY KEY (id)) "
+ "TYPE=MyISAM",
+ "drop table if exists tracklistitem",
+ "CREATE TABLE tracklistitem ( "
+ "playerid int(11) NOT NULL default '0', "
+ "listtype smallint(6) NOT NULL default '0', "
+ "tracknb int(11) NOT NULL default '0', "
+ "trackid int(11) NOT NULL default '0', "
+ "PRIMARY KEY (playerid,listtype,tracknb)) "
+ "TYPE=MyISAM",
+ "drop table if exists tracks",
+ "CREATE TABLE tracks ( "
+ "artist varchar(255) default NULL, "
+ "title varchar(255) default NULL, "
+ "composer varchar(255) default NULL, "
+ "genre1 varchar(10) default NULL, "
+ "genre2 varchar(10) default NULL, "
+ "year smallint(5) unsigned default NULL, "
+ "lang varchar(4) default NULL, "
+ "type tinyint(3) unsigned default NULL, "
+ "rating tinyint(3) unsigned default NULL, "
+ "length smallint(5) unsigned default NULL, "
+ "source tinyint(3) unsigned default NULL, "
+ "sourceid varchar(20) default NULL, "
+ "tracknb tinyint(3) unsigned default NULL, "
+ "mp3file varchar(255) default NULL, "
+ "quality tinyint(3) unsigned default NULL, "
+ "voladjust smallint(6) default '0', "
+ "lengthfrm mediumint(9) default '0', "
+ "startfrm mediumint(9) default '0', "
+ "bpm smallint(6) default '0', "
+ "lyrics mediumtext, "
+ "moreinfo mediumtext, "
+ "bitrate varchar(10) default NULL, "
+ "created date default NULL, "
+ "modified date default NULL, "
+ "backup tinyint(3) unsigned default NULL, "
+ "samplerate int(7) unsigned default NULL, "
+ "channels tinyint(3) unsigned default NULL, "
+ "id int(11) NOT NULL auto_increment, "
+ "folder1 varchar(255), "
+ "folder2 varchar(255), "
+ "folder3 varchar(255), "
+ "folder4 varchar(255), "
+ "PRIMARY KEY (id), "
+ "KEY title (title(10)), "
+ "KEY mp3file (mp3file(10)), "
+ "KEY genre1 (genre1), "
+ "KEY genre2 (genre2), "
+ "KEY year (year), "
+ "KEY lang (lang), "
+ "KEY type (type), "
+ "KEY rating (rating), "
+ "KEY sourceid (sourceid), "
+ "KEY artist (artist(10))) "
+ "TYPE=MyISAM"
};
void
-mgDbGd::StartTransaction()
-{
+mgDbGd::StartTransaction() {
Execute("START TRANSACTION");
}
void
-mgDbGd::Commit()
-{
+mgDbGd::Commit() {
Execute("COMMIT");
}
bool
-mgDbGd::SetCharset()
-{
- const char *cmd;
- if (the_setup.utf8)
+mgDbGd::SetCharset() {
+ const char *cmd;
+ if (the_setup.utf8)
cmd="SET NAMES utf8";
else
cmd="SET NAMES latin1";
- mgQuery q0(m_db,cmd);
- if (!q0.ErrorMessage().empty())
+ mgQuery q0(m_db,cmd);
+ if (!q0.ErrorMessage().empty())
return false;
return true;
}
bool
-mgDbGd::Create()
-{
- // create database and tables
- char buffer[500];
- sprintf(buffer,"DROP DATABASE IF EXISTS %s",the_setup.DbName);
- if (strlen(buffer)>400)
- mgError("name of database too long: %s",the_setup.DbName);
- mgQuery q(m_db,buffer);
- if (!q.ErrorMessage().empty())
- {
- mgWarning(q.ErrorMessage().c_str());
- return false;
- }
- sprintf(buffer,"CREATE DATABASE %s",the_setup.DbName);
- mgQuery q1(m_db,buffer);
- if (!q1.ErrorMessage().empty())
- {
- mgWarning(q.ErrorMessage().c_str());
- return false;
- }
+mgDbGd::Create() {
+ // create database and tables
+ char buffer[500];
+ sprintf(buffer,"DROP DATABASE IF EXISTS %s",the_setup.DbName);
+ if (strlen(buffer)>400)
+ mgError("name of database too long: %s",the_setup.DbName);
+ mgQuery q(m_db,buffer);
+ if (!q.ErrorMessage().empty()) {
+ mgWarning(q.ErrorMessage().c_str());
+ return false;
+ }
+ sprintf(buffer,"CREATE DATABASE %s",the_setup.DbName);
+ mgQuery q1(m_db,buffer);
+ if (!q1.ErrorMessage().empty()) {
+ mgWarning(q.ErrorMessage().c_str());
+ return false;
+ }
- if (!UsingEmbeddedMySQL())
- sprintf(buffer,"grant all privileges on %s.* to vdr@localhost",
+ if (!UsingEmbeddedMySQL())
+ sprintf(buffer,"grant all privileges on %s.* to vdr@localhost",
the_setup.DbName);
- Execute(buffer);
- // ignore error. If we can create the data base, we can do everything
- // with it anyway.
- return true;
+ Execute(buffer);
+ // ignore error. If we can create the data base, we can do everything
+ // with it anyway.
+ return true;
}
bool
-mgDbGd::Clear()
-{
- char buffer[500];
- int len = sizeof( db_cmds ) / sizeof( char* );
- for( int i=0; i < len; i ++ )
- {
- mgQuery q(m_db, db_cmds[i],mgQueryWarnOnly);
- if (!q.ErrorMessage().empty())
- {
- sprintf(buffer,"DROP DATABASE IF EXISTS %s",the_setup.DbName);
- Execute(buffer);
- return false;
+mgDbGd::Clear() {
+ char buffer[500];
+ int len = sizeof( db_cmds ) / sizeof( char* );
+ for( int i=0; i < len; i ++ ) {
+ mgQuery q(m_db, db_cmds[i],mgQueryWarnOnly);
+ if (!q.ErrorMessage().empty()) {
+ sprintf(buffer,"DROP DATABASE IF EXISTS %s",the_setup.DbName);
+ Execute(buffer);
+ return false;
+ }
}
- }
- return true;
+ return true;
}
-
bool
-mgDbGd::ServerConnect ()
-{
- if (m_db)
- return true;
- if (time(0)<m_connect_time+10)
- return false;
- m_connect_time=time(0);
- if (!DbServer)
- DbServer = new mgDbServer;
- m_db = mysql_init (0);
- if (!m_db)
- return false;
- if (UsingEmbeddedMySQL())
- {
+mgDbGd::ServerConnect () {
+ if (m_db)
+ return true;
+ if (time(0)<m_connect_time+10)
+ return false;
+ m_connect_time=time(0);
+ if (!DbServer)
+ DbServer = new mgDbServer;
+ m_db = mysql_init (0);
+ if (!m_db)
+ return false;
+ if (UsingEmbeddedMySQL()) {
#ifndef HAVE_ONLY_SERVER
- if (!mysql_real_connect(m_db, 0, 0, 0, 0, 0, 0, 0))
- mgWarning("Failed to connect to embedded mysql in %s:%s ",
+ if (!mysql_real_connect(m_db, 0, 0, 0, 0, 0, 0, 0))
+ mgWarning("Failed to connect to embedded mysql in %s:%s ",
the_setup.DbDatadir,mysql_error(m_db));
- else
- mgDebug(1,"Connected to embedded mysql in %s",
+ else
+ mgDebug(1,"Connected to embedded mysql in %s",
the_setup.DbDatadir);
#endif
- }
- else
- {
- if (the_setup.NoHost() || !strcmp(the_setup.DbHost,"localhost"))
- mgDebug(1,"Using socket %s for connecting to local system as user %s.",
- the_setup.DbSocket, the_setup.DbUser);
- else
- mgDebug(1,"Using TCP for connecting to server %s as user %s.",
- the_setup.DbHost, the_setup.DbUser);
- if (!mysql_real_connect( m_db,
- the_setup.DbHost, the_setup.DbUser, the_setup.DbPass, 0,
- the_setup.DbPort, the_setup.DbSocket, 0 ) != 0 )
- {
- mgWarning("Failed to connect to server '%s' as User '%s', Password '%s': %s",
- the_setup.DbHost,the_setup.DbUser,the_setup.DbPass,mysql_error(m_db));
- mysql_close (m_db);
- m_db = 0;
- }
- }
- return m_db!=0;
+ }
+ else {
+ if (the_setup.NoHost() || !strcmp(the_setup.DbHost,"localhost"))
+ mgDebug(1,"Using socket %s for connecting to local system as user %s.",
+ the_setup.DbSocket, the_setup.DbUser);
+ else
+ mgDebug(1,"Using TCP for connecting to server %s as user %s.",
+ the_setup.DbHost, the_setup.DbUser);
+ if (!mysql_real_connect( m_db,
+ the_setup.DbHost, the_setup.DbUser, the_setup.DbPass, 0,
+ the_setup.DbPort, the_setup.DbSocket, 0 ) != 0 ) {
+ mgWarning("Failed to connect to server '%s' as User '%s', Password '%s': %s",
+ the_setup.DbHost,the_setup.DbUser,the_setup.DbPass,mysql_error(m_db));
+ mysql_close (m_db);
+ m_db = 0;
+ }
+ }
+ return m_db!=0;
}
-
bool
-UsingEmbeddedMySQL()
-{
+UsingEmbeddedMySQL() {
#ifdef HAVE_ONLY_SERVER
return false;
#else
@@ -536,139 +495,126 @@ UsingEmbeddedMySQL()
}
bool
-mgDbGd::ConnectDatabase ()
-{
- bool res=mysql_select_db(m_db,the_setup.DbName)==0;
- if (res)
- res=SetCharset();
- else
- mgError("mysql_select_db(%s) failed with %s",the_setup.DbName,mysql_error(m_db));
- return res;
+mgDbGd::ConnectDatabase () {
+ bool res=mysql_select_db(m_db,the_setup.DbName)==0;
+ if (res)
+ res=SetCharset();
+ else
+ mgError("mysql_select_db(%s) failed with %s",the_setup.DbName,mysql_error(m_db));
+ return res;
}
-bool
-mgDbGd::NeedGenre2()
-{
- if (!needGenre2_set && Connect())
- {
- needGenre2_set=true;
- needGenre2=exec_count("SELECT COUNT(DISTINCT genre2) FROM tracks")>1;
- }
- return needGenre2;
+bool
+mgDbGd::NeedGenre2() {
+ if (!needGenre2_set && Connect()) {
+ needGenre2_set=true;
+ needGenre2=exec_count("SELECT COUNT(DISTINCT genre2) FROM tracks")>1;
+ }
+ return needGenre2;
}
void
-mgDbGd::CreateFolderFields()
-{
- if (HasFolderFields())
- return;
- mgQuery q(m_db, "DESCRIBE tracks folder1");
- m_hasfolderfields = q.Rows()>0;
- if (!m_hasfolderfields)
- {
- mgQuery q(m_db,
- "alter table tracks add column folder1 varchar(255),"
- "add column folder2 varchar(255),"
- "add column folder3 varchar(255),"
- "add column folder4 varchar(255)");
- m_hasfolderfields = q.ErrorMessage().empty();
-
- }
+mgDbGd::CreateFolderFields() {
+ if (HasFolderFields())
+ return;
+ mgQuery q(m_db, "DESCRIBE tracks folder1");
+ m_hasfolderfields = q.Rows()>0;
+ if (!m_hasfolderfields) {
+ mgQuery q(m_db,
+ "alter table tracks add column folder1 varchar(255),"
+ "add column folder2 varchar(255),"
+ "add column folder3 varchar(255),"
+ "add column folder4 varchar(255)");
+ m_hasfolderfields = q.ErrorMessage().empty();
+
+ }
}
int
-mgDbGd::AddToCollection( const string Name,const vector<mgItem*>&items, mgParts* what)
-{
- if (!Connect()) return 0;
- CreateCollection(Name);
- string listid = mgSQLString (get_col0
- (string("SELECT id FROM playlist WHERE title=")
- + mgSQLString(Name).quoted())).quoted();
- unsigned int tracksize = items.size();
- if (tracksize==0)
- return 0;
-
- // this code is rather complicated but works in a multi user
- // environment:
-
- // insert a unique trackid:
- string trackid = ltos(thread_id()+1000000);
- Execute("INSERT INTO playlistitem SELECT "+listid+","
- "MAX(tracknumber)+"+ltos(tracksize)+","+trackid+
- " FROM playlistitem WHERE playlist="+listid);
-
- // find tracknumber of the trackid we just inserted:
- string sql = string("SELECT tracknumber FROM playlistitem WHERE "
- "playlist=")+listid+" AND trackid="+trackid;
- long first = atol(get_col0(sql).c_str()) - tracksize + 1;
-
- // replace the place holder trackid by the correct value:
- Execute("UPDATE playlistitem SET trackid="+ltos(items[tracksize-1]->getItemid())+
- " WHERE playlist="+listid+" AND trackid="+trackid);
-
- // insert all other tracks:
- const char *sql_prefix = "INSERT INTO playlistitem VALUES ";
- sql = "";
- for (unsigned int i = 0; i < tracksize-1; i++)
- {
- string item = "(" + listid + "," + ltos (first + i) + "," +
- ltos (items[i]->getItemid ()) + ")";
- comma(sql, item);
- if ((i%100)==99)
- {
- Execute (sql_prefix+sql);
- sql = "";
+mgDbGd::AddToCollection( const string Name,const vector<mgItem*>&items, mgParts* what) {
+ if (!Connect()) return 0;
+ CreateCollection(Name);
+ string listid = mgSQLString (get_col0
+ (string("SELECT id FROM playlist WHERE title=")
+ + mgSQLString(Name).quoted())).quoted();
+ unsigned int tracksize = items.size();
+ if (tracksize==0)
+ return 0;
+
+ // this code is rather complicated but works in a multi user
+ // environment:
+
+ // insert a unique trackid:
+ string trackid = ltos(thread_id()+1000000);
+ Execute("INSERT INTO playlistitem SELECT "+listid+","
+ "MAX(tracknumber)+"+ltos(tracksize)+","+trackid+
+ " FROM playlistitem WHERE playlist="+listid);
+
+ // find tracknumber of the trackid we just inserted:
+ string sql = string("SELECT tracknumber FROM playlistitem WHERE "
+ "playlist=")+listid+" AND trackid="+trackid;
+ long first = atol(get_col0(sql).c_str()) - tracksize + 1;
+
+ // replace the place holder trackid by the correct value:
+ Execute("UPDATE playlistitem SET trackid="+ltos(items[tracksize-1]->getItemid())+
+ " WHERE playlist="+listid+" AND trackid="+trackid);
+
+ // insert all other tracks:
+ const char *sql_prefix = "INSERT INTO playlistitem VALUES ";
+ sql = "";
+ for (unsigned int i = 0; i < tracksize-1; i++) {
+ string item = "(" + listid + "," + ltos (first + i) + "," +
+ ltos (items[i]->getItemid ()) + ")";
+ comma(sql, item);
+ if ((i%100)==99) {
+ Execute (sql_prefix+sql);
+ sql = "";
+ }
}
- }
- if (!sql.empty()) Execute (sql_prefix+sql);
- return tracksize;
+ if (!sql.empty()) Execute (sql_prefix+sql);
+ return tracksize;
}
int
-mgDbGd::RemoveFromCollection (const string Name, const vector<mgItem*>&items, mgParts* what)
-{
- if (Name.empty())
- return 0;
- if (!Connect()) return 0;
- string pid = KeyMaps.id(keyGdCollection,Name);
- if (pid.empty())
- return 0;
- what->Prepare();
- what->tables.push_front("playlistitem as del");
- what->clauses.push_back("del.playlist="+pid);
- bool usesTracks = false;
- for (list < string >::iterator it = what->tables.begin (); it != what->tables.end (); ++it)
- if (*it == "tracks")
- {
+mgDbGd::RemoveFromCollection (const string Name, const vector<mgItem*>&items, mgParts* what) {
+ if (Name.empty())
+ return 0;
+ if (!Connect()) return 0;
+ string pid = KeyMaps.id(keyGdCollection,Name);
+ if (pid.empty())
+ return 0;
+ what->Prepare();
+ what->tables.push_front("playlistitem as del");
+ what->clauses.push_back("del.playlist="+pid);
+ bool usesTracks = false;
+ for (list < string >::iterator it = what->tables.begin (); it != what->tables.end (); ++it)
+ if (*it == "tracks") {
usesTracks = true;
break;
}
- if (usesTracks)
- what->clauses.push_back("del.trackid=tracks.id");
- else
- what->clauses.push_back("del.trackid=playlistitem.trackid");
- string sql = "DELETE playlistitem";
- sql += sql_list(" FROM",what->tables);
- sql += sql_list(" WHERE",what->clauses," AND ");
- return Execute (sql);
+ if (usesTracks)
+ what->clauses.push_back("del.trackid=tracks.id");
+ else
+ what->clauses.push_back("del.trackid=playlistitem.trackid");
+ string sql = "DELETE playlistitem";
+ sql += sql_list(" FROM",what->tables);
+ sql += sql_list(" WHERE",what->clauses," AND ");
+ return Execute (sql);
}
bool
-mgDbGd::FieldExists(string table, string field)
-{
- char *b;
- msprintf(&b,"DESCRIBE %s %s",table.c_str(),field.c_str());
- mgQuery q(m_db,b);
- free(b);
- if (q.Next())
+mgDbGd::FieldExists(string table, string field) {
+ char *b;
+ msprintf(&b,"DESCRIBE %s %s",table.c_str(),field.c_str());
+ mgQuery q(m_db,b);
+ free(b);
+ if (q.Next())
return q.Rows() == 1;
else
return false;
}
const char*
-mgDbGd::DecadeExpr()
-{
+mgDbGd::DecadeExpr() {
return "substring(10 * floor(tracks.year/10),3)";
}
-
diff --git a/mg_db_gd_mysql.h b/mg_db_gd_mysql.h
index d44445c..fa1dd53 100644
--- a/mg_db_gd_mysql.h
+++ b/mg_db_gd_mysql.h
@@ -20,47 +20,53 @@ using namespace std;
#include "mg_db.h"
-class mgDbGd : public mgDb {
- public:
- mgDbGd (bool SeparateThread=false);
- ~mgDbGd();
- bool ServerConnect();
- bool ConnectDatabase();
- bool Create();
- bool Clear();
- int AddToCollection( const string Name,const vector<mgItem*>&items,mgParts* what);
- int RemoveFromCollection( const string Name,const vector<mgItem*>&items,mgParts* what);
-
- bool NeedGenre2();
- long thread_id() { return mysql_thread_id(m_db); }
- bool FieldExists(string table, string field);
- void ServerEnd();
- bool Threadsafe();
- const char* HelpText() const;
- const char *Options() const;
- const char *DecadeExpr();
- string Now() const { return "CURRENT_TIMESTAMP"; }
- string Directory() const { return "substring(tracks.mp3file,1,length(tracks.mp3file)"
- "-instr(reverse(tracks.mp3file),'/'))"; }
- protected:
- void StartTransaction();
- void Commit();
- void *ImplDbHandle() const { return (void*)m_db; }
- private:
- MYSQL *m_db;
- void CreateFolderFields();
- MYSQL_RES* Query( const string sql);
- bool sql_query(string sql);
- bool SetCharset();
+class mgDbGd : public mgDb
+{
+ public:
+ mgDbGd (bool SeparateThread=false);
+ ~mgDbGd();
+ bool ServerConnect();
+ bool ConnectDatabase();
+ bool Create();
+ bool Clear();
+ int AddToCollection( const string Name,const vector<mgItem*>&items,mgParts* what);
+ int RemoveFromCollection( const string Name,const vector<mgItem*>&items,mgParts* what);
+
+ bool NeedGenre2();
+ long thread_id() { return mysql_thread_id(m_db); }
+ bool FieldExists(string table, string field);
+ void ServerEnd();
+ bool Threadsafe();
+ const char* HelpText() const;
+ const char *Options() const;
+ const char *DecadeExpr();
+ string Now() const { return "CURRENT_TIMESTAMP"; }
+ string Directory() const
+ {
+ return "substr(tracks.mp3file,1,length(tracks.mp3file)"
+ "-instr(reverse(tracks.mp3file),'/'))";
+ }
+ protected:
+ void StartTransaction();
+ void Commit();
+ void *ImplDbHandle() const { return (void*)m_db; }
+ private:
+ MYSQL *m_db;
+ void CreateFolderFields();
+ MYSQL_RES* Query( const string sql);
+ bool sql_query(string sql);
+ bool SetCharset();
};
-class mgDbServerMySQL : public mgDbServerImp {
+class mgDbServerMySQL : public mgDbServerImp
+{
public:
mgDbServerMySQL();
~mgDbServerMySQL();
};
-class mgSQLStringMySQL : public mgSQLStringImp {
+class mgSQLStringMySQL : public mgSQLStringImp
+{
public:
mgSQLStringMySQL(const char* s);
~mgSQLStringMySQL();
@@ -69,7 +75,8 @@ class mgSQLStringMySQL : public mgSQLStringImp {
mutable char* m_unquoted;
};
-class mgQueryMySQL : public mgQueryImp {
+class mgQueryMySQL : public mgQueryImp
+{
public:
mgQueryMySQL(void* db,string sql,mgQueryNoise noise);
~mgQueryMySQL();
diff --git a/mg_db_gd_pg.c b/mg_db_gd_pg.c
index ea63816..b51b1c8 100644
--- a/mg_db_gd_pg.c
+++ b/mg_db_gd_pg.c
@@ -1,4 +1,4 @@
-/*!
+/*!
* \file mg_db_gd_pg.c
* \brief A capsule around postgresql database access
*
@@ -18,7 +18,6 @@
#include <time.h>
#include <assert.h>
-
#include "mg_setup.h"
#include "mg_item_gd.h"
#include "mg_db_gd_pg.h"
@@ -29,35 +28,32 @@
using namespace std;
mgQueryPG::mgQueryPG(void* db,string sql,mgQueryNoise noise)
- : mgQueryImp(db,sql,noise)
-{
+: mgQueryImp(db,sql,noise) {
m_db = (PGconn*)m_db_handle;
m_cursor = 0;
m_table = PQexec(m_db,m_optsql);
switch ((m_rc=PQresultStatus(m_table))) {
case PGRES_COMMAND_OK:
- m_rows = atol(PQcmdTuples(m_table));
+ m_rows = atol(PQcmdTuples(m_table));
break;;
case PGRES_TUPLES_OK:
- m_rows = PQntuples(m_table);
+ m_rows = PQntuples(m_table);
m_columns = PQnfields(m_table);
break;
- default:
+ default:
m_errormessage = PQresultErrorMessage (m_table);
break;
}
HandleErrors();
}
-mgQueryPG::~mgQueryPG()
-{
+mgQueryPG::~mgQueryPG() {
PQclear(m_table);
}
char **
-mgQueryPG::Next()
-{
- if (m_cursor>=Rows())
+mgQueryPG::Next() {
+ if (m_cursor>=Rows())
return 0;
assert(Columns()<100);
memset(m_rowpointers,0,sizeof(m_rowpointers));
@@ -68,26 +64,23 @@ mgQueryPG::Next()
return m_rowpointers;
}
-mgSQLStringPG::~mgSQLStringPG()
-{
+mgSQLStringPG::~mgSQLStringPG() {
if (m_unquoted)
free(m_unquoted);
}
-mgSQLStringPG::mgSQLStringPG(const char*s)
-{
+mgSQLStringPG::mgSQLStringPG(const char*s) {
m_unquoted = 0;
m_original = s;
}
-char*
+char*
mgSQLStringPG::unquoted() const
{
- if (!m_unquoted)
- {
- int buflen=2*strlen(m_original)+5;
- m_unquoted = (char *) malloc( buflen);
- PQescapeString(m_unquoted,m_original,strlen(m_original));
+ if (!m_unquoted) {
+ int buflen=2*strlen(m_original)+5;
+ m_unquoted = (char *) malloc( buflen);
+ PQescapeString(m_unquoted,m_original,strlen(m_original));
}
return m_unquoted;
}
@@ -101,19 +94,18 @@ mgDbGd::Options() const
const char*
mgDbGd::HelpText() const
{
- return
- " -h HHHH, --host=HHHH specify database host (default is localhost)\n"
- " -s SSSS --socket=PATH specify database socket\n"
- " -p PPPP, --port=PPPP specify port of database server\n"
- " -u UUUU, --user=UUUU specify database user (default is current login name)\n"
- " -w WWWW, --password=WWWW specify database password (default is empty)\n"
- "\n"
- " if the database runs on the same computer you should only need -u and -w if\n"
- " at all, see README.postgresql\n";
+ return
+ " -h HHHH, --host=HHHH specify database host (default is localhost)\n"
+ " -s SSSS --socket=PATH specify database socket\n"
+ " -p PPPP, --port=PPPP specify port of database server\n"
+ " -u UUUU, --user=UUUU specify database user (default is current login name)\n"
+ " -w WWWW, --password=WWWW specify database password (default is empty)\n"
+ "\n"
+ " if the database runs on the same computer you should only need -u and -w if\n"
+ " at all, see README.postgresql\n";
}
-mgDb* GenerateDB(bool SeparateThread)
-{
+mgDb* GenerateDB(bool SeparateThread) {
// \todo should return different backends according to the_setup.Variant
return new mgDbGd(SeparateThread);
}
@@ -121,241 +113,219 @@ mgDb* GenerateDB(bool SeparateThread)
static bool needGenre2;
static bool needGenre2_set=false;
-mgDbGd::mgDbGd(bool SeparateThread)
-{
- m_db = 0;
+mgDbGd::mgDbGd(bool SeparateThread) {
+ m_db = 0;
if (m_separate_thread)
if (!Threadsafe())
mgError("Your database library is not thread safe");
}
-mgDbGd::~mgDbGd()
-{
- PQfinish (m_db);
- m_db = 0;
+mgDbGd::~mgDbGd() {
+ PQfinish (m_db);
+ m_db = 0;
}
bool
-mgDbGd::Threadsafe()
-{
+mgDbGd::Threadsafe() {
return ENABLE_THREAD_SAFETY;
}
-static char *db_cmds[] =
-{
- "DROP TABLE IF EXISTS album",
- "CREATE TABLE album ( "
- "artist varchar(255) default NULL, "
- "title varchar(255) default NULL, "
- "cddbid varchar(20) default '', "
- "coverimg varchar(255) default NULL, "
- "covertxt text, "
- "modified date default NULL, "
- "genre varchar(10) default NULL, "
- "PRIMARY KEY (cddbid))",
- "CREATE INDEX alb_artist ON album (artist)",
- "CREATE INDEX alb_title ON album (title)",
- "DROP TABLE IF EXISTS genre",
- "CREATE TABLE genre ("
- "id varchar(10) NOT NULL default '', "
- "id3genre smallint default NULL, "
- "genre varchar(255) default NULL, "
- "freq int default NULL, "
- "PRIMARY KEY (id))",
- "DROP TABLE IF EXISTS language",
- "CREATE TABLE language ("
- "id varchar(4) NOT NULL default '', "
- "language varchar(40) default NULL, "
- "freq int default NULL, "
- "PRIMARY KEY (id))",
- "DROP TABLE IF EXISTS musictype",
- "CREATE TABLE musictype ("
- "musictype varchar(40) default NULL, "
- "id serial, "
- "PRIMARY KEY (id)) ",
- "DROP TABLE IF EXISTS playlist",
- "CREATE TABLE playlist ( "
- "title varchar(255) default NULL, "
- "author varchar(255) default NULL, "
- "note varchar(255) default NULL, "
- "created timestamp default NULL, "
- "id serial, "
- "PRIMARY KEY (id))",
- "DROP TABLE IF EXISTS playlistitem",
- "CREATE TABLE playlistitem ( "
- "playlist int NOT NULL,"
- "trackid int NOT NULL) WITH OIDS",
- "DROP TABLE IF EXISTS source",
- "CREATE TABLE source ( "
- "source varchar(40) default NULL, "
- "id serial, "
- "PRIMARY KEY (id)) ",
- "DROP TABLE IF EXISTS tracks",
- "CREATE TABLE tracks ( "
- "artist varchar(255) default NULL, "
- "title varchar(255) default NULL, "
- "genre1 varchar(10) default NULL, "
- "genre2 varchar(10) default NULL, "
- "year smallint default NULL, "
- "lang varchar(4) default NULL, "
- "type smallint default NULL, "
- "rating smallint default NULL, "
- "length smallint default NULL, "
- "source smallint default NULL, "
- "sourceid varchar(20) default NULL, "
- "tracknb smallint default NULL, "
- "mp3file varchar(255) default NULL, "
- "quality smallint default NULL, "
- "voladjust smallint default '0', "
- "lengthfrm int default '0', "
- "startfrm int default '0', "
- "bpm smallint default '0', "
- "lyrics text, "
- "bitrate varchar(10) default NULL, "
- "created date default NULL, "
- "modified date default NULL, "
- "backup smallint default NULL, "
- "samplerate int default NULL, "
- "channels smallint default NULL, "
- "id serial, "
- "folder1 varchar(255), "
- "folder2 varchar(255), "
- "folder3 varchar(255), "
- "folder4 varchar(255), "
- "PRIMARY KEY (id))",
- "CREATE INDEX tracks_title ON tracks (title)",
- "CREATE INDEX tracks_sourceid ON tracks (sourceid)",
- "CREATE INDEX tracks_mp3file ON tracks (mp3file)",
- "CREATE INDEX tracks_genre1 ON tracks (genre1)",
- "CREATE INDEX tracks_genre2 ON tracks (genre2)",
- "CREATE INDEX tracks_year ON tracks (year)",
- "CREATE INDEX tracks_lang ON tracks (lang)",
- "CREATE INDEX tracks_artist ON tracks (artist)",
- "CREATE INDEX tracks_rating ON tracks (rating)",
- "CREATE INDEX tracks_folder1 ON tracks (folder1)",
- "CREATE INDEX tracks_folder2 ON tracks (folder2)",
- "CREATE INDEX tracks_folder3 ON tracks (folder3)",
- "CREATE INDEX tracks_folder4 ON tracks (folder4)",
+static const char *db_cmds[] = {
+ "DROP TABLE IF EXISTS album",
+ "CREATE TABLE album ( "
+ "artist varchar(255) default NULL, "
+ "title varchar(255) default NULL, "
+ "cddbid varchar(20) default '', "
+ "coverimg varchar(255) default NULL, "
+ "covertxt text, "
+ "modified date default NULL, "
+ "genre varchar(10) default NULL, "
+ "PRIMARY KEY (cddbid))",
+ "CREATE INDEX alb_artist ON album (artist)",
+ "CREATE INDEX alb_title ON album (title)",
+ "DROP TABLE IF EXISTS genre",
+ "CREATE TABLE genre ("
+ "id varchar(10) NOT NULL default '', "
+ "id3genre smallint default NULL, "
+ "genre varchar(255) default NULL, "
+ "freq int default NULL, "
+ "PRIMARY KEY (id))",
+ "DROP TABLE IF EXISTS language",
+ "CREATE TABLE language ("
+ "id varchar(4) NOT NULL default '', "
+ "language varchar(40) default NULL, "
+ "freq int default NULL, "
+ "PRIMARY KEY (id))",
+ "DROP TABLE IF EXISTS musictype",
+ "CREATE TABLE musictype ("
+ "musictype varchar(40) default NULL, "
+ "id serial, "
+ "PRIMARY KEY (id)) ",
+ "DROP TABLE IF EXISTS playlist",
+ "CREATE TABLE playlist ( "
+ "title varchar(255) default NULL, "
+ "author varchar(255) default NULL, "
+ "note varchar(255) default NULL, "
+ "created timestamp default NULL, "
+ "id serial, "
+ "PRIMARY KEY (id))",
+ "DROP TABLE IF EXISTS playlistitem",
+ "CREATE TABLE playlistitem ( "
+ "playlist int NOT NULL,"
+ "trackid int NOT NULL) WITH OIDS",
+ "DROP TABLE IF EXISTS source",
+ "CREATE TABLE source ( "
+ "source varchar(40) default NULL, "
+ "id serial, "
+ "PRIMARY KEY (id)) ",
+ "DROP TABLE IF EXISTS tracks",
+ "CREATE TABLE tracks ( "
+ "artist varchar(255) default NULL, "
+ "title varchar(255) default NULL, "
+ "genre1 varchar(10) default NULL, "
+ "genre2 varchar(10) default NULL, "
+ "year smallint default NULL, "
+ "lang varchar(4) default NULL, "
+ "type smallint default NULL, "
+ "rating smallint default NULL, "
+ "length smallint default NULL, "
+ "source smallint default NULL, "
+ "sourceid varchar(20) default NULL, "
+ "tracknb smallint default NULL, "
+ "mp3file varchar(255) default NULL, "
+ "quality smallint default NULL, "
+ "voladjust smallint default '0', "
+ "lengthfrm int default '0', "
+ "startfrm int default '0', "
+ "bpm smallint default '0', "
+ "lyrics text, "
+ "bitrate varchar(10) default NULL, "
+ "created date default NULL, "
+ "modified date default NULL, "
+ "backup smallint default NULL, "
+ "samplerate int default NULL, "
+ "channels smallint default NULL, "
+ "id serial, "
+ "folder1 varchar(255), "
+ "folder2 varchar(255), "
+ "folder3 varchar(255), "
+ "folder4 varchar(255), "
+ "PRIMARY KEY (id))",
+ "CREATE INDEX tracks_title ON tracks (title)",
+ "CREATE INDEX tracks_sourceid ON tracks (sourceid)",
+ "CREATE INDEX tracks_mp3file ON tracks (mp3file)",
+ "CREATE INDEX tracks_genre1 ON tracks (genre1)",
+ "CREATE INDEX tracks_genre2 ON tracks (genre2)",
+ "CREATE INDEX tracks_year ON tracks (year)",
+ "CREATE INDEX tracks_lang ON tracks (lang)",
+ "CREATE INDEX tracks_artist ON tracks (artist)",
+ "CREATE INDEX tracks_rating ON tracks (rating)",
+ "CREATE INDEX tracks_folder1 ON tracks (folder1)",
+ "CREATE INDEX tracks_folder2 ON tracks (folder2)",
+ "CREATE INDEX tracks_folder3 ON tracks (folder3)",
+ "CREATE INDEX tracks_folder4 ON tracks (folder4)",
};
void
-mgDbGd::StartTransaction()
-{
+mgDbGd::StartTransaction() {
Execute("BEGIN TRANSACTION");
}
void
-mgDbGd::Commit()
-{
+mgDbGd::Commit() {
Execute("COMMIT");
}
-
bool
-mgDbGd::SetCharset()
-{
- const char *cmd;
- if (the_setup.utf8)
+mgDbGd::SetCharset() {
+ const char *cmd;
+ if (the_setup.utf8)
cmd="SET NAMES 'UTF8'";
else
cmd="SET NAMES 'LATIN1'";
- mgQuery q0(m_db,cmd);
- if (!q0.ErrorMessage().empty())
+ mgQuery q0(m_db,cmd);
+ if (!q0.ErrorMessage().empty())
return false;
return true;
}
bool
-mgDbGd::Creatable()
-{
- return false;
+mgDbGd::Creatable() {
+ return false;
}
bool
-mgDbGd::Create()
-{
- return false;
+mgDbGd::Create() {
+ return false;
}
bool
-mgDbGd::Clear()
-{
- // create database and tables
- int len = sizeof( db_cmds ) / sizeof( char* );
- for( int i=0; i < len; i ++ )
- {
- mgQuery q(m_db,db_cmds[i],mgQueryWarnOnly);
- if (!q.ErrorMessage().empty())
- return false;
- }
- return true;
+mgDbGd::Clear() {
+ // create database and tables
+ int len = sizeof( db_cmds ) / sizeof( char* );
+ for( int i=0; i < len; i ++ ) {
+ mgQuery q(m_db,db_cmds[i],mgQueryWarnOnly);
+ if (!q.ErrorMessage().empty())
+ return false;
+ }
+ return true;
}
bool
-mgDbGd::ServerConnect ()
-{
- return true;
+mgDbGd::ServerConnect () {
+ return true;
}
-
bool
-mgDbGd::ConnectDatabase ()
-{
- char conninfo[500];
- char port[20];
- char host[200];
- char *user;
- if (the_setup.DbPort>0)
- sprintf(port," port = %d ",the_setup.DbPort);
- else
- port[0]=0;
- if (notempty(the_setup.DbHost))
- snprintf(host,199," host = %s ",the_setup.DbHost);
- else if (notempty(the_setup.DbSocket))
- snprintf(host,199," host = %s ",the_setup.DbSocket);
- else
- host[0]=0;
- if (the_setup.DbUser==0)
- user=getenv("LOGNAME");
- else
- user=the_setup.DbUser;
- snprintf(conninfo,499,"%s %s dbname = %s user = %s ",
- host,port,the_setup.DbName,user);
- m_db = PQconnectdb(conninfo);
- if (PQstatus(m_db) != CONNECTION_OK)
- {
- mgWarning("Failed to connect to postgres server using %s:%s",conninfo,PQerrorMessage(m_db));
- return false;
- }
- return SetCharset();
+mgDbGd::ConnectDatabase () {
+ char conninfo[500];
+ char port[20];
+ char host[200];
+ char *user;
+ if (the_setup.DbPort>0)
+ sprintf(port," port = %d ",the_setup.DbPort);
+ else
+ port[0]=0;
+ if (notempty(the_setup.DbHost))
+ snprintf(host,199," host = %s ",the_setup.DbHost);
+ else if (notempty(the_setup.DbSocket))
+ snprintf(host,199," host = %s ",the_setup.DbSocket);
+ else
+ host[0]=0;
+ if (the_setup.DbUser==0)
+ user=getenv("LOGNAME");
+ else
+ user=the_setup.DbUser;
+ snprintf(conninfo,499,"%s %s dbname = %s user = %s ",
+ host,port,the_setup.DbName,user);
+ m_db = PQconnectdb(conninfo);
+ if (PQstatus(m_db) != CONNECTION_OK) {
+ mgWarning("Failed to connect to postgres server using %s:%s",conninfo,PQerrorMessage(m_db));
+ return false;
+ }
+ return SetCharset();
}
-bool
-mgDbGd::NeedGenre2()
-{
- if (!needGenre2_set && Connect())
- {
- needGenre2_set=true;
- needGenre2=exec_count("SELECT COUNT(DISTINCT genre2) FROM tracks")>1;
- }
- return needGenre2;
+bool
+mgDbGd::NeedGenre2() {
+ if (!needGenre2_set && Connect()) {
+ needGenre2_set=true;
+ needGenre2=exec_count("SELECT COUNT(DISTINCT genre2) FROM tracks")>1;
+ }
+ return needGenre2;
}
-
bool
-mgDbGd::FieldExists(string table, string field)
-{
- char *b;
- msprintf(&b,"SELECT COUNT(*) FROM information_schema.columns WHERE table_name='%s' AND column_name='%s'",
+mgDbGd::FieldExists(string table, string field) {
+ char *b;
+ msprintf(&b,"SELECT COUNT(*) FROM information_schema.columns WHERE table_name='%s' AND column_name='%s'",
table.c_str(),field.c_str());
- bool result = exec_count(b)==1;
+ bool result = exec_count(b)==1;
free(b);
return result;
}
const char*
-mgDbGd::DecadeExpr()
-{
- return "substring(10 * floor(tracks.year/10),3)";
+mgDbGd::DecadeExpr() {
+ return "substring(cast(10 * floor(tracks.year/10) as char(4)),3)";
}
-
diff --git a/mg_db_gd_pg.h b/mg_db_gd_pg.h
index 5520767..10ba9c5 100644
--- a/mg_db_gd_pg.h
+++ b/mg_db_gd_pg.h
@@ -1,4 +1,4 @@
-/*!
+/*!
* \file mg_db_gd_mysql.h
* \brief A capsule around giantdisc database access
*
@@ -20,10 +20,12 @@ using namespace std;
#include "mg_db.h"
-class mgDbServerPG : public mgDbServerImp {
+class mgDbServerPG : public mgDbServerImp
+{
};
-class mgSQLStringPG : public mgSQLStringImp {
+class mgSQLStringPG : public mgSQLStringImp
+{
public:
mgSQLStringPG(const char* s);
~mgSQLStringPG();
@@ -32,7 +34,8 @@ class mgSQLStringPG : public mgSQLStringImp {
mutable char* m_unquoted;
};
-class mgQueryPG : public mgQueryImp {
+class mgQueryPG : public mgQueryImp
+{
public:
mgQueryPG(void* db,string sql,mgQueryNoise noise);
~mgQueryPG();
@@ -43,33 +46,34 @@ class mgQueryPG : public mgQueryImp {
char *m_rowpointers[100];
};
-class mgDbGd : public mgDb {
- public:
- mgDbGd (bool SeparateThread=false);
- ~mgDbGd();
- bool ServerConnect();
- bool ConnectDatabase();
- bool Creatable();
- bool Create();
- bool Clear();
-
- bool NeedGenre2();
- long thread_id() { return -1; }
- bool FieldExists(string table, string field);
- bool Threadsafe();
- const char* Options() const;
- const char* HelpText() const;
- const char *DecadeExpr();
- string Now() const { return "CURRENT_TIMESTAMP";}
- string Directory() const { return "substring(tracks.mp3file from '.*/(.*)')"; }
- protected:
- void StartTransaction();
- void Commit();
- void *ImplDbHandle() const { return (void*)m_db;}
- private:
- bool myCreate();
- PGconn *m_db;
- bool SetCharset();
+class mgDbGd : public mgDb
+{
+ public:
+ mgDbGd (bool SeparateThread=false);
+ ~mgDbGd();
+ bool ServerConnect();
+ bool ConnectDatabase();
+ bool Creatable();
+ bool Create();
+ bool Clear();
+
+ bool NeedGenre2();
+ long thread_id() { return -1; }
+ bool FieldExists(string table, string field);
+ bool Threadsafe();
+ const char* Options() const;
+ const char* HelpText() const;
+ const char *DecadeExpr();
+ string Now() const { return "CURRENT_TIMESTAMP";}
+ string Directory() const { return "substring(tracks.mp3file from '.*/(.*)')"; }
+ protected:
+ void StartTransaction();
+ void Commit();
+ void *ImplDbHandle() const { return (void*)m_db;}
+ private:
+ bool myCreate();
+ PGconn *m_db;
+ bool SetCharset();
};
#endif
diff --git a/mg_db_gd_sqlite.c b/mg_db_gd_sqlite.c
index a37da7c..a7a6198 100644
--- a/mg_db_gd_sqlite.c
+++ b/mg_db_gd_sqlite.c
@@ -1,4 +1,4 @@
-/*!
+/*!
* \file mg_db_gd_sqlite.c
* \brief A capsule around database access
*
@@ -19,7 +19,6 @@
#include <time.h>
#include <assert.h>
-
#include "mg_setup.h"
#include "mg_item_gd.h"
#include "mg_db_gd_sqlite.h"
@@ -27,15 +26,13 @@
using namespace std;
mgQuerySQLite::mgQuerySQLite(void* db,string sql,mgQueryNoise noise)
- : mgQueryImp(db,sql,noise)
-{
+: mgQueryImp(db,sql,noise) {
sqlite3* m_db = (sqlite3*)m_db_handle;
m_table = 0;
char *p;
if (!strncmp(m_optsql,"SELECT ",7))
m_rc = sqlite3_get_table(m_db,m_optsql,&m_table,&m_rows,&m_columns,&p);
- else
- {
+ else {
m_rc = sqlite3_exec(m_db,m_optsql,0,0,&p);
if (m_rc==SQLITE_OK)
m_rows = sqlite3_changes(m_db);
@@ -44,33 +41,30 @@ mgQuerySQLite::mgQuerySQLite(void* db,string sql,mgQueryNoise noise)
HandleErrors();
}
-mgQuerySQLite::~mgQuerySQLite()
-{
+mgQuerySQLite::~mgQuerySQLite() {
sqlite3_free_table(m_table);
}
char **
-mgQuerySQLite::Next()
-{
+mgQuerySQLite::Next() {
if (m_cursor>=m_rows)
return 0;
m_cursor++;
- return &m_table[m_columns*m_cursor]; // skip header row
+ // skip header row
+ return &m_table[m_columns*m_cursor];
}
-mgSQLStringSQLite::~mgSQLStringSQLite()
-{
+mgSQLStringSQLite::~mgSQLStringSQLite() {
if (m_unquoted)
sqlite3_free(m_unquoted);
}
-mgSQLStringSQLite::mgSQLStringSQLite(const char*s)
-{
+mgSQLStringSQLite::mgSQLStringSQLite(const char*s) {
m_unquoted = 0;
m_original = s;
}
-char*
+char*
mgSQLStringSQLite::unquoted() const
{
if (!m_unquoted)
@@ -81,187 +75,189 @@ mgSQLStringSQLite::unquoted() const
const char*
mgDbGd::Options() const
{
- return "";
+ return "d";
}
const char*
mgDbGd::HelpText() const
{
- return "";
+ return " -d DIRN, --datadir=DIRN specify directory for embedded sql data (default is \"$HOME/.muggle\")\n";
}
-mgDb* GenerateDB(bool SeparateThread)
-{
+mgDb* GenerateDB(bool SeparateThread) {
// \todo should return different backends according to the_setup.Variant
return new mgDbGd(SeparateThread);
}
-
-mgDbGd::mgDbGd(bool SeparateThread)
-{
- m_db = 0;
+mgDbGd::mgDbGd(bool SeparateThread) {
+ m_db = 0;
if (m_separate_thread)
if (!Threadsafe())
mgError("Your database library is not thread safe");
}
-mgDbGd::~mgDbGd()
-{
- sqlite3_close (m_db);
- m_db = 0;
+mgDbGd::~mgDbGd() {
+ sqlite3_close (m_db);
+ m_db = 0;
}
bool
-mgDbGd::Threadsafe()
-{
+mgDbGd::Threadsafe() {
return false;
}
-static char *db_cmds[] =
-{
- "drop table album",
- "CREATE TABLE album ( "
- "artist varchar(255) default NULL, "
- "title varchar(255) default NULL, "
- "cddbid varchar(20) NOT NULL default '', "
- "coverimg varchar(255) default NULL, "
- "covertxt mediumtext, "
- "modified date default NULL, "
- "genre varchar(10) default NULL, "
- "PRIMARY KEY (cddbid))",
- "CREATE INDEX idx_album_artist ON album (artist)",
- "CREATE INDEX idx_album_title ON album (title)",
- "drop table genre",
- "CREATE TABLE genre ("
- "id varchar(10) NOT NULL default '', "
- "id3genre smallint(6) default NULL, "
- "genre varchar(255) default NULL, "
- "freq int(11) default NULL, "
- "PRIMARY KEY (id))",
- "drop table language",
- "CREATE TABLE language ("
- "id varchar(4) NOT NULL default '', "
- "language varchar(40) default NULL, "
- "freq int(11) default NULL, "
- "PRIMARY KEY (id))",
- "drop table musictype;",
- "CREATE TABLE musictype ("
- "musictype varchar(40) default NULL, "
- "id integer PRIMARY KEY autoincrement)",
- "drop table playlist",
- "CREATE TABLE playlist ( "
- "title varchar(255) default NULL, "
- "author varchar(255) default NULL, "
- "note varchar(255) default NULL, "
- "created timestamp(8) NOT NULL, "
- "id integer PRIMARY KEY autoincrement)",
-"drop table playlistitem",
- "CREATE TABLE playlistitem ( "
- "playlist integer NOT NULL, "
- "trackid int(11) NOT NULL)",
- "CREATE INDEX playlistitem_1 on playlistitem (playlist)",
- "drop table tracklistitem",
- "CREATE TABLE tracklistitem ( "
- "playerid int(11) NOT NULL default '0', "
- "listtype smallint(6) NOT NULL default '0', "
- "tracknb int(11) NOT NULL default '0', "
- "trackid int(11) NOT NULL default '0', "
- "PRIMARY KEY (playerid,listtype,tracknb))",
- "drop table source",
- "CREATE TABLE source ( "
- "source varchar(40) default NULL, "
- "id integer PRIMARY KEY autoincrement)",
- "drop table tracks",
- "CREATE TABLE tracks ( "
- "id integer PRIMARY KEY autoincrement, "
- "artist varchar(255) default NULL, "
- "title varchar(255) default NULL, "
- "genre1 varchar(10) default NULL, "
- "genre2 varchar(10) default NULL, "
- "year smallint(5) default NULL, "
- "lang varchar(4) default NULL, "
- "type tinyint(3) default NULL, "
- "rating tinyint(3) default NULL, "
- "length smallint(5) default NULL, "
- "source tinyint(3) default NULL, "
- "sourceid varchar(20) default NULL, "
- "tracknb tinyint(3) default NULL, "
- "mp3file varchar(255) default NULL, "
- "quality tinyint(3) default NULL, "
- "voladjust smallint(6) default '0', "
- "lengthfrm mediumint(9) default '0', "
- "startfrm mediumint(9) default '0', "
- "bpm smallint(6) default '0', "
- "lyrics mediumtext, "
- "bitrate varchar(10) default NULL, "
- "created date default NULL, "
- "modified date default NULL, "
- "backup tinyint(3) default NULL, "
- "samplerate int(7) default NULL, "
- "channels tinyint(3) default NULL, "
- "folder1 varchar(255), "
- "folder2 varchar(255), "
- "folder3 varchar(255), "
- "folder4 varchar(255)); ",
- "CREATE INDEX tracks_title ON tracks (title)",
- "CREATE INDEX tracks_sourceid ON tracks (sourceid)",
- "CREATE INDEX tracks_mp3file ON tracks (mp3file)",
- "CREATE INDEX tracks_genre1 ON tracks (genre1)",
- "CREATE INDEX tracks_year ON tracks (year)",
- "CREATE INDEX tracks_lang ON tracks (lang)",
- "CREATE INDEX tracks_artist ON tracks (artist)",
- "CREATE INDEX tracks_rating ON tracks (rating)",
- "CREATE INDEX tracks_folder1 ON tracks (folder1)",
- "CREATE INDEX tracks_folder2 ON tracks (folder2)",
- "CREATE INDEX tracks_folder3 ON tracks (folder3)",
- "CREATE INDEX tracks_folder4 ON tracks (folder4)",
+static const char *db_cmds[] = {
+ "drop table album",
+ "CREATE TABLE album ( "
+ "artist varchar(255) default NULL, "
+ "title varchar(255) default NULL, "
+ "cddbid varchar(20) NOT NULL default '', "
+ "coverimg varchar(255) default NULL, "
+ "covertxt mediumtext, "
+ "modified date default NULL, "
+ "genre varchar(10) default NULL, "
+ "PRIMARY KEY (cddbid))",
+ "CREATE INDEX idx_album_artist ON album (artist)",
+ "CREATE INDEX idx_album_title ON album (title)",
+ "drop table genre",
+ "CREATE TABLE genre ("
+ "id varchar(10) NOT NULL default '', "
+ "id3genre smallint(6) default NULL, "
+ "genre varchar(255) default NULL, "
+ "freq int(11) default NULL, "
+ "PRIMARY KEY (id))",
+ "drop table language",
+ "CREATE TABLE language ("
+ "id varchar(4) NOT NULL default '', "
+ "language varchar(40) default NULL, "
+ "freq int(11) default NULL, "
+ "PRIMARY KEY (id))",
+ "drop table musictype;",
+ "CREATE TABLE musictype ("
+ "musictype varchar(40) default NULL, "
+ "id integer PRIMARY KEY autoincrement)",
+ "drop table playlist",
+ "CREATE TABLE playlist ( "
+ "title varchar(255) default NULL, "
+ "author varchar(255) default NULL, "
+ "note varchar(255) default NULL, "
+ "created timestamp(8) NOT NULL, "
+ "id integer PRIMARY KEY autoincrement)",
+ "drop table playlistitem",
+ "CREATE TABLE playlistitem ( "
+ "playlist integer NOT NULL, "
+ "trackid int(11) NOT NULL)",
+ "CREATE INDEX playlistitem_1 on playlistitem (playlist)",
+ "drop table tracklistitem",
+ "CREATE TABLE tracklistitem ( "
+ "playerid int(11) NOT NULL default '0', "
+ "listtype smallint(6) NOT NULL default '0', "
+ "tracknb int(11) NOT NULL default '0', "
+ "trackid int(11) NOT NULL default '0', "
+ "PRIMARY KEY (playerid,listtype,tracknb))",
+ "drop table source",
+ "CREATE TABLE source ( "
+ "source varchar(40) default NULL, "
+ "id integer PRIMARY KEY autoincrement)",
+ "drop table tracks",
+ "CREATE TABLE tracks ( "
+ "id integer PRIMARY KEY autoincrement, "
+ "artist varchar(255) default NULL, "
+ "title varchar(255) default NULL, "
+ "genre1 varchar(10) default NULL, "
+ "genre2 varchar(10) default NULL, "
+ "year smallint(5) default NULL, "
+ "lang varchar(4) default NULL, "
+ "type tinyint(3) default NULL, "
+ "rating tinyint(3) default NULL, "
+ "length smallint(5) default NULL, "
+ "source tinyint(3) default NULL, "
+ "sourceid varchar(20) default NULL, "
+ "tracknb tinyint(3) default NULL, "
+ "mp3file varchar(255) default NULL, "
+ "quality tinyint(3) default NULL, "
+ "voladjust smallint(6) default '0', "
+ "lengthfrm mediumint(9) default '0', "
+ "startfrm mediumint(9) default '0', "
+ "bpm smallint(6) default '0', "
+ "lyrics mediumtext, "
+ "bitrate varchar(10) default NULL, "
+ "created date default NULL, "
+ "modified date default NULL, "
+ "backup tinyint(3) default NULL, "
+ "samplerate int(7) default NULL, "
+ "channels tinyint(3) default NULL, "
+ "folder1 varchar(255), "
+ "folder2 varchar(255), "
+ "folder3 varchar(255), "
+ "folder4 varchar(255)); ",
+ "CREATE INDEX tracks_title ON tracks (title)",
+ "CREATE INDEX tracks_sourceid ON tracks (sourceid)",
+ "CREATE INDEX tracks_mp3file ON tracks (mp3file)",
+ "CREATE INDEX tracks_genre1 ON tracks (genre1)",
+ "CREATE INDEX tracks_year ON tracks (year)",
+ "CREATE INDEX tracks_lang ON tracks (lang)",
+ "CREATE INDEX tracks_artist ON tracks (artist)",
+ "CREATE INDEX tracks_rating ON tracks (rating)",
+ "CREATE INDEX tracks_folder1 ON tracks (folder1)",
+ "CREATE INDEX tracks_folder2 ON tracks (folder2)",
+ "CREATE INDEX tracks_folder3 ON tracks (folder3)",
+ "CREATE INDEX tracks_folder4 ON tracks (folder4)",
};
void
-mgDbGd::StartTransaction()
-{
+mgDbGd::StartTransaction() {
Execute("BEGIN TRANSACTION");
}
void
-mgDbGd::Commit()
-{
+mgDbGd::Commit() {
Execute("COMMIT");
}
bool
-mgDbGd::Creatable()
-{
- return true;
+mgDbGd::SyncStart() {
+ return mgDb::SyncStart();
+#if 0
+// TODO the ubuntu package for sqlite3 does not yet contain
+// sqlite3_file_control
+ int res=sqlite3_file_control(m_db,0,SQLITE_LOCK_EXCLUSIVE,0);
+ if (res) {
+ char *s=sqlite3_mprintf("%s/%s.sqlite",the_setup.DbDatadir,the_setup.DbName);
+ mgWarning(tr("Cannot get exclusive lock on SQLite database %s:%d/%s"),
+ s,res,sqlite3_errmsg(m_db));
+ }
+ return res==0;
+#endif
}
bool
-mgDbGd::Create()
-{
- return true;
+mgDbGd::Creatable() {
+ return true;
}
bool
-mgDbGd::Clear()
-{
- // create database and tables
- int len = sizeof( db_cmds ) / sizeof( char* );
- for( int i=0; i < len; i ++ )
- {
- mgQuery q(m_db,db_cmds[i],mgQuerySilent);
- if (!q.ErrorMessage().empty())
- if (strncmp(db_cmds[i],"drop ",5))
- {
- mgWarning("%20s: %s",db_cmds[i],q.ErrorMessage().c_str());
- return false;
+mgDbGd::Create() {
+ return true;
+}
+
+bool
+mgDbGd::Clear() {
+ // create database and tables
+ int len = sizeof( db_cmds ) / sizeof( char* );
+ for( int i=0; i < len; i ++ ) {
+ mgQuery q(m_db,db_cmds[i],mgQuerySilent);
+ if (!q.ErrorMessage().empty())
+ if (strncmp(db_cmds[i],"drop ",5)) {
+ mgWarning("%20s: %s",db_cmds[i],q.ErrorMessage().c_str());
+ return false;
+ }
}
- }
- return true;
+ return true;
}
void
-mgDirectory(sqlite3_context *context, int argc, sqlite3_value **argv)
-{
+mgDirectory(sqlite3_context *context, int argc, sqlite3_value **argv) {
assert(argc==1);
char *buf=strdup((char*)sqlite3_value_text(argv[0]));
char *slash=strrchr(buf,'/');
@@ -272,8 +268,7 @@ mgDirectory(sqlite3_context *context, int argc, sqlite3_value **argv)
}
void
-mgDecade(sqlite3_context *context, int argc, sqlite3_value **argv)
-{
+mgDecade(sqlite3_context *context, int argc, sqlite3_value **argv) {
assert(argc==1);
unsigned int year=sqlite3_value_int(argv[0]);
char *buf;
@@ -281,88 +276,55 @@ mgDecade(sqlite3_context *context, int argc, sqlite3_value **argv)
sqlite3_result_text(context,buf,2,free);
}
-void
-mgSubstring(sqlite3_context *context, int argc, sqlite3_value **argv)
-{
- assert(argc==3);
- char*full=(char*)sqlite3_value_text(argv[0]);
- unsigned int opos=sqlite3_value_int(argv[1]);
- unsigned int pos=opos;
- unsigned int olen=sqlite3_value_int(argv[2]);
- unsigned int len=olen;
- if (pos>strlen(full))
- pos=strlen(full);
- if (len==0)
- len=99999;
- if (len>strlen(full+pos-1))
- len=strlen(full+pos-1);
- char *buf=strndup(full+pos-1,len);
- sqlite3_result_text(context,buf,len,free);
+bool
+mgDbGd::ConnectDatabase () {
+ struct stat stbuf;
+ if (stat(the_setup.DbDatadir,&stbuf))
+ mkdir(the_setup.DbDatadir,0755);
+ char *s=sqlite3_mprintf("%s/%s.sqlite",the_setup.DbDatadir,the_setup.DbName);
+ mgDebug(1,"%X opening data base %s",m_db,s);
+ // rework this when sqlite3.5 is available, use sqlite3_open_v2
+ // and do not try to create db here
+ int rc = sqlite3_open(s,&m_db);
+ if (rc!=SQLITE_OK)
+ mgWarning("Cannot open/create SQLite database %s:%d/%s",
+ s,rc,sqlite3_errmsg(m_db));
+ sqlite3_free(s);
+ if (rc!=SQLITE_OK)
+ return false;
+ rc = sqlite3_create_function(m_db,"mgDirectory",1,SQLITE_UTF8,
+ 0,&mgDirectory,0,0);
+ if (rc!=SQLITE_OK) {
+ mgWarning("Cannot define mgDirectory:%d/%s",rc,sqlite3_errmsg);
+ return false;
+ }
+ rc = sqlite3_create_function(m_db,"decade",1,SQLITE_UTF8,
+ 0,&mgDecade,0,0);
+ if (rc!=SQLITE_OK) {
+ mgWarning("Cannot define decade:%d/%s",rc,sqlite3_errmsg);
+ return false;
+ }
+ return true;
}
bool
-mgDbGd::ConnectDatabase ()
-{
- struct stat stbuf;
- if (stat(the_setup.DbDatadir,&stbuf))
- mkdir(the_setup.DbDatadir,0755);
- char *s=sqlite3_mprintf("%s/%s.sqlite",the_setup.DbDatadir,the_setup.DbName);
- mgDebug(1,"%X opening data base %s",m_db,s);
-// rework this when sqlite3.5 is available, use sqlite3_open_v2
-// and do not try to create db here
- int rc = sqlite3_open(s,&m_db);
- if (rc!=SQLITE_OK)
- mgWarning("Cannot open/create SQLite database %s:%d/%s",
- s,rc,sqlite3_errmsg(m_db));
- sqlite3_free(s);
- if (rc!=SQLITE_OK)
- return false;
- rc = sqlite3_create_function(m_db,"mgDirectory",1,SQLITE_UTF8,
- 0,&mgDirectory,0,0);
- if (rc!=SQLITE_OK)
- {
- mgWarning("Cannot define mgDirectory:%d/%s",rc,sqlite3_errmsg);
- return false;
- }
- rc = sqlite3_create_function(m_db,"substring",3,SQLITE_UTF8,
- 0,&mgSubstring,0,0);
- if (rc!=SQLITE_OK)
- {
- mgWarning("Cannot define mgSubstring:%d/%s",rc,sqlite3_errmsg);
+mgDbGd::NeedGenre2() {
+ // we do not support genre2 because queries like
+ // SELECT title FROM tracks WHERE (genre1='m' or genre2='m')
+ // are very slow with sqlite
return false;
- }
- rc = sqlite3_create_function(m_db,"decade",1,SQLITE_UTF8,
- 0,&mgDecade,0,0);
- if (rc!=SQLITE_OK)
- {
- mgWarning("Cannot define decade:%d/%s",rc,sqlite3_errmsg);
- return false;
- }
- return true;
-}
-
-bool
-mgDbGd::NeedGenre2()
-{
-// we do not support genre2 because queries like
-// SELECT title FROM tracks WHERE (genre1='m' or genre2='m')
-// are very slow with sqlite
- return false;
}
bool
-mgDbGd::FieldExists(string table, string field)
-{
- char *b;
- msprintf(&b,"SELECT %s FROM %s LIMIT 1",field.c_str(),table.c_str());
+mgDbGd::FieldExists(string table, string field) {
+ char *b;
+ msprintf(&b,"SELECT %s FROM %s LIMIT 1",field.c_str(),table.c_str());
mgQuery q(m_db,b,mgQuerySilent);
- free(b);
+ free(b);
return q.ErrorMessage().empty();
}
const char*
-mgDbGd::DecadeExpr()
-{
+mgDbGd::DecadeExpr() {
return "Decade(tracks.year)";
}
-
diff --git a/mg_db_gd_sqlite.h b/mg_db_gd_sqlite.h
index 3bea6e0..15ca066 100644
--- a/mg_db_gd_sqlite.h
+++ b/mg_db_gd_sqlite.h
@@ -1,4 +1,4 @@
-/*!
+/*!
* \file mg_db_gd_sqlite.h
* \brief A capsule around giantdisc database access
*
@@ -20,10 +20,12 @@ using namespace std;
#include "mg_db.h"
-class mgDbServerSQLite : public mgDbServerImp {
+class mgDbServerSQLite : public mgDbServerImp
+{
};
-class mgSQLStringSQLite : public mgSQLStringImp {
+class mgSQLStringSQLite : public mgSQLStringImp
+{
public:
mgSQLStringSQLite(const char* s);
~mgSQLStringSQLite();
@@ -32,7 +34,8 @@ class mgSQLStringSQLite : public mgSQLStringImp {
mutable char* m_unquoted;
};
-class mgQuerySQLite : public mgQueryImp {
+class mgQuerySQLite : public mgQueryImp
+{
public:
mgQuerySQLite(void *db,string sql,mgQueryNoise noise);
~mgQuerySQLite();
@@ -42,31 +45,30 @@ class mgQuerySQLite : public mgQueryImp {
int m_rc;
};
-class mgDbGd : public mgDb {
- public:
- mgDbGd (bool SeparateThread=false);
- ~mgDbGd();
- bool ConnectDatabase();
- bool Creatable();
- bool Create();
- bool Clear();
-
- bool NeedGenre2();
- long thread_id() { return -1; }
- bool FieldExists(string table, string field);
- bool Threadsafe();
- const char* Options() const;
- const char* HelpText() const;
- void *ImplDbHandle() const { return m_db; }
- const char *DecadeExpr();
- string Now() const { return "strftime('%s','now')"; }
- string Directory() const { return "mgDirectory(mp3file)"; }
- protected:
- void StartTransaction();
- void Commit();
- private:
- sqlite3 *m_db;
-};
-
+class mgDbGd : public mgDb
+{
+ public:
+ mgDbGd (bool SeparateThread=false);
+ ~mgDbGd();
+ bool ConnectDatabase();
+ bool Creatable();
+ bool Create();
+ bool Clear();
+ bool NeedGenre2();
+ long thread_id() { return -1; }
+ bool FieldExists(string table, string field);
+ bool Threadsafe();
+ const char* Options() const;
+ const char* HelpText() const;
+ void *ImplDbHandle() const { return m_db; }
+ const char *DecadeExpr();
+ string Now() const { return "strftime('%s','now')"; }
+ string Directory() const { return "mgDirectory(mp3file)"; }
+ protected:
+ void StartTransaction();
+ void Commit();
+ bool SyncStart();
+ sqlite3 *m_db;
+};
#endif
diff --git a/mg_image_provider.c b/mg_image_provider.c
index 1d127c4..5ef7449 100644
--- a/mg_image_provider.c
+++ b/mg_image_provider.c
@@ -1,10 +1,12 @@
-
+//
#include "mg_image_provider.h"
#include "mg_item_gd.h"
#include "mg_tools.h"
#include "mg_setup.h"
+#include <vdr/plugin.h>
+
#include <id3v2header.h>
#include <flacfile.h>
#include <vorbisfile.h>
@@ -31,460 +33,450 @@ extern int alphasort();
const int FALSE = 0;
const int TRUE = 1;
-int picture_select( struct dirent const *entry )
-{
- if( (strcmp(entry->d_name, ".") == 0) ||
- (strcmp(entry->d_name, "..") == 0) )
- {
- return (FALSE);
- }
- else
- {
- char *ext = rindex( entry->d_name, '.' );
-
- if( ext )
- {
- if( !strcmp( ext, ".jpg" ) || !strcmp( ext, ".png" ) || !strcmp( ext, ".JPG" ) || !strcmp( ext, ".PNG" ) )
- {
- return (TRUE);
- }
- else
- {
- return ( FALSE );
- }
+int picture_select( struct dirent const *entry ) {
+ if( (strcmp(entry->d_name, ".") == 0) ||
+ (strcmp(entry->d_name, "..") == 0) ) {
+ return (FALSE);
}
- else
- {
- return (FALSE);
+ else {
+ char *ext = rindex( entry->d_name, '.' );
+
+ if( ext ) {
+ if( !strcmp( ext, ".jpg" ) || !strcmp( ext, ".png" ) || !strcmp( ext, ".JPG" ) || !strcmp( ext, ".PNG" ) ) {
+ return (TRUE);
+ }
+ else {
+ return ( FALSE );
+ }
+ }
+ else {
+ return (FALSE);
+ }
}
- }
}
-string mgImageProvider::getImagePath( string &source )
-{
- // returns converted image name, writes source image name into source argument reference
-
- string fname="";
- source = "";
-
- // check, how many images are converted at all
- Lock();
- if( m_image_index < m_converted_images.size() )
- {
- fname = m_converted_images[ m_image_index ];
- }
-
- // here we could mangle the original name to add an extension such as -s200
- source = m_image_list[ m_image_index ];
-
- // wrap to beginning of list when all images are displayed
- m_image_index += 1;
- if( m_image_index >= m_converted_images.size() )
- {
- m_image_index = 0;
- }
- Unlock();
-
- return fname;
+#if 0
+mgImage::mgImage() {
+ bmp=0;
}
-mgImageProvider::mgImageProvider( string dir )
-{
- // iterate all files in dir and put them into m_image_list
- m_mode = IM_PLAYLIST;
- m_image_index = 0;
- m_delete_imgs_from_tag = false;
-
- fillImageList( dir );
- Start();
+mgImage::~mgImage() {
+ delete bmp;bmp=0;
}
+#endif
+
+string mgImageProvider::getImagePath( string &source ) {
+ // returns converted image name, writes source image name into source argument reference
+ string fname="";
+ source = "";
-mgImageProvider::mgImageProvider( )
-{
- m_mode = IM_ITEM_DIR;
- m_image_index = 0;
- m_delete_imgs_from_tag = false;
+ if (m_image_index>=m_image_list.size())
+ m_image_index=0;
+ if( m_image_index < m_image_list.size() ) {
+ source = m_image_list[ m_image_index ];
+ }
+ m_image_index += 1;
+
+ return fname;
}
-mgImageProvider::~mgImageProvider()
-{
- deleteTemporaryImages();
+mgMpgImageProvider::mgMpgImageProvider(tArea area)
+ : mgImageProvider(area) {
+}
+
+
+mgImageProvider::mgImageProvider( string dir ) {
+ // iterate all files in dir and put them into m_image_list
+ currItem=0;
+ m_mode = IM_PLAYLIST;
+ m_image_index = 0;
+ m_delete_imgs_from_tag = false;
+
+ fillImageList( dir );
}
-void mgImageProvider::deleteTemporaryImages()
-{
- if( m_delete_imgs_from_tag )
- {
- for( vector<string>::iterator iter = m_image_list.begin(); iter != m_image_list.end(); iter ++ )
- {
- // remove( (*iter).c_str() );
- // cout << "Removing " << *iter << endl;
- }
- m_delete_imgs_from_tag = false;
- }
- m_image_list.clear();
-
- for( vector<string>::iterator iter = m_converted_images.begin(); iter != m_converted_images.end(); iter ++ )
- {
- // remove( (*iter).c_str() );
- // cout << "Removing " << *iter << endl;
- }
- m_converted_images.clear();
+mgMpgImageProvider::mgMpgImageProvider( string dir )
+ : mgImageProvider(dir) {
+ Start();
}
-mgImageProvider* mgImageProvider::Create( string dir )
-{
- return new mgImageProvider( dir );
+mgImageProvider::mgImageProvider(tArea area ) {
+ currItem=0;
+ m_mode = IM_ITEM_DIR;
+ m_image_index = 0;
+ m_delete_imgs_from_tag = false;
+ coverarea=area;
}
-mgImageProvider* mgImageProvider::Create( )
-{
- return new mgImageProvider();
+mgMpgImageProvider::~mgMpgImageProvider() {
+ m_converted_images.clear();
}
-bool mgImageProvider::extractImagesFromDatabase( mgItemGd *item )
-{
- /* GD entry looks like: img0x00004810-00.jpg
- and may reside in $ToplevelDir/[0-9]{2}/
-
- Then, all img0x00004810-??.jpg are possible matches, but not
- files such as img0x00004810-00-s200.jpg which are optimized
- for a different resoluition.
-
- Thanks to viking (from vdrportal.de) for help with this
- */
- string file = item->getImagePath();
- if( file == "" )
- {
- return false;
- }
-
- // cut -00.jpg part from file
- int pos = file.find('-');
- string file_rex_s = file.substr( 0, pos+1 );
- file_rex_s += "[0-9]*.jpg";
- // now file_rex contains a regular expression we can test for to obtain valid images
-
- bool result = false;
-
- char *dir[2];
- dir[0] = the_setup.ToplevelDir;
- dir[1] = NULL;
-
- FTS *fts = fts_open( dir, FTS_LOGICAL, 0);
- if( fts )
- {
- FTSENT *ftsent;
- regex_t path_rex, file_rex;
-
- if( regcomp( &path_rex, "[0-9][0-9]", REG_NOSUB ) )
- {
- mgDebug( 1, "mgImageProvider::extractImagesFromDatabase: Error compiling dir regex. Not using GD images." );
- return false;
- }
+bool mgImageProvider::extractImagesFromDatabase() {
+ /* GD entry looks like: img0x00004810-00.jpg
+ and may reside in $ToplevelDir/[0-9]{2}/
+
+ Then, all img0x00004810-??.jpg are possible matches, but not
+ files such as img0x00004810-00-s200.jpg which are optimized
+ for a different resoluition.
- if( regcomp( &file_rex, file_rex_s.c_str(), REG_NOSUB ) )
- {
- mgDebug( 1, "mgImageProvider::extractImagesFromDatabase: Error compiling file regex. Not using GD images." );
- return false;
+ Thanks to viking (from vdrportal.de) for help with this
+ */
+ string file = currItem->getImagePath();
+ if( file == "" ) {
+ return false;
}
-
- while( (ftsent = fts_read(fts)) != NULL )
- {
- switch( ftsent->fts_info )
- {
- case FTS_DC:
- {
- mgDebug( 1, "Image import: Ignoring directory %s, would cycle already seen %s",
- ftsent->fts_path,ftsent->fts_cycle->fts_path );
- } break;
- case FTS_DNR:
- {
- mgDebug( 1, "Ignoring unreadable directory %s: error %d",
- ftsent->fts_path,ftsent->fts_errno);
- } break;
- case FTS_DOT:
- {
- mgDebug( 1, "Ignoring dot file %s:",
- ftsent->fts_path );
- } break;
- case FTS_SLNONE:
- {
- mgDebug(1,"Ignoring broken symbolic link %s",
- ftsent->fts_path);
- } break;
- case FTS_NSOK: // should never happen because we do not do FTS_NOSTAT
- case FTS_SL: // should never happen because we do FTS_LOGICAL
- case FTS_ERR:
- {
- mgDebug( 1, "Ignoring %s: error %d",
- ftsent->fts_path,ftsent->fts_errno );
- } break;
- case FTS_D:
- {
- if( !regexec( &path_rex, ftsent->fts_name, 0, NULL, 0 ) )
- {
- fts_set( fts, ftsent, FTS_SKIP );
- mgDebug( 1, "Skipping directory %s", ftsent->fts_path );
- }
- } break;
- case FTS_DP:
- break;
- case FTS_F:
- {
- if( !ftsent->fts_path )
- {
- mgDebug( 1, "internal error: fts_path is 0" );
- }
- else if( access( ftsent->fts_path, R_OK ) )
- {
- mgDebug( 1, "Ignoring unreadable file %s: %s",
- ftsent->fts_path, strerror( errno ) );
- }
- else
- {
- if( regexec( &file_rex, ftsent->fts_name, 0, NULL, 0 ) )
- { // an image matching the GD database entry
- string img = string( ftsent->fts_path );
- m_image_list.push_back( img );
- mgDebug( 1, "Found image %s", ftsent->fts_path );
- }
- }
- } break;
- case FTS_NS:
- {
- mgDebug( 1, "Ignoring unstatable file %s: error %d",
- ftsent->fts_path,ftsent->fts_errno );
- } break;
- default:
- {
- mgDebug( 1, "Ignoring %s: unknown fts_info value %d",
- ftsent->fts_path,ftsent->fts_info );
- }
- }
+
+ // cut -00.jpg part from file
+ int pos = file.find('-');
+ string file_rex_s = file.substr( 0, pos+1 );
+ file_rex_s += "[0-9]*.jpg";
+ // now file_rex contains a regular expression we can test for to obtain valid images
+
+ bool result = false;
+
+ char *dir[2];
+ dir[0] = the_setup.ToplevelDir;
+ dir[1] = NULL;
+
+ FTS *fts = fts_open( dir, FTS_LOGICAL, 0);
+ if( fts ) {
+ FTSENT *ftsent;
+ regex_t path_rex, file_rex;
+
+ if( regcomp( &path_rex, "[0-9][0-9]", REG_NOSUB ) ) {
+ mgDebug( 1, "mgImageProvider::extractImagesFromDatabase: Error compiling dir regex. Not using GD images." );
+ return false;
+ }
+
+ if( regcomp( &file_rex, file_rex_s.c_str(), REG_NOSUB ) ) {
+ mgDebug( 1, "mgImageProvider::extractImagesFromDatabase: Error compiling file regex. Not using GD images." );
+ return false;
+ }
+
+ while( (ftsent = fts_read(fts)) != NULL ) {
+ switch( ftsent->fts_info ) {
+ case FTS_DC:
+ {
+ mgDebug( 1, "Image import: Ignoring directory %s, would cycle already seen %s",
+ ftsent->fts_path,ftsent->fts_cycle->fts_path );
+ } break;
+ case FTS_DNR:
+ {
+ mgDebug( 1, "Ignoring unreadable directory %s: error %d",
+ ftsent->fts_path,ftsent->fts_errno);
+ } break;
+ case FTS_DOT:
+ {
+ mgDebug( 1, "Ignoring dot file %s:",
+ ftsent->fts_path );
+ } break;
+ case FTS_SLNONE:
+ {
+ mgDebug(1,"Ignoring broken symbolic link %s",
+ ftsent->fts_path);
+ } break;
+ case FTS_NSOK: // should never happen because we do not do FTS_NOSTAT
+ case FTS_SL: // should never happen because we do FTS_LOGICAL
+ case FTS_ERR:
+ {
+ mgDebug( 1, "Ignoring %s: error %d",
+ ftsent->fts_path,ftsent->fts_errno );
+ } break;
+ case FTS_D:
+ {
+ if( !regexec( &path_rex, ftsent->fts_name, 0, NULL, 0 ) ) {
+ fts_set( fts, ftsent, FTS_SKIP );
+ mgDebug( 1, "Skipping directory %s", ftsent->fts_path );
+ }
+ } break;
+ case FTS_DP:
+ break;
+ case FTS_F:
+ {
+ if( !ftsent->fts_path ) {
+ mgDebug( 1, "internal error: fts_path is 0" );
+ }
+ else if( access( ftsent->fts_path, R_OK ) ) {
+ mgDebug( 1, "Ignoring unreadable file %s: %s",
+ ftsent->fts_path, strerror( errno ) );
+ }
+ else {
+ if( regexec( &file_rex, ftsent->fts_name, 0, NULL, 0 ) ) {
+ // an image matching the GD database entry
+ string img = string( ftsent->fts_path );
+ m_image_list.push_back( img );
+ mgDebug( 1, "Found image %s", ftsent->fts_path );
+ }
+ }
+ } break;
+ case FTS_NS:
+ {
+ mgDebug( 1, "Ignoring unstatable file %s: error %d",
+ ftsent->fts_path,ftsent->fts_errno );
+ } break;
+ default:
+ {
+ mgDebug( 1, "Ignoring %s: unknown fts_info value %d",
+ ftsent->fts_path,ftsent->fts_info );
+ }
+ }
+ }
+ fts_close(fts);
+ regfree( &path_rex );
}
- fts_close(fts);
- regfree( &path_rex );
- }
- return result;
+ return result;
}
-void mgImageProvider::updateFromItemDirectory( mgItemGd *item )
-{
- // no images in tags, find images in the directory of the file itself
- string dir = dirname( (char *) (item->getSourceFile().c_str()) );
-
- // do something, when there are no images here, either:
- // simply go up one step in the directory hierarchy, until we reach top level directory
- bool toplevel_reached = false;
- while( !m_image_list.size() && !toplevel_reached )
- {
- if( samedir( dir.c_str(), the_setup.ToplevelDir ) )
- {
- toplevel_reached = true;
- }
-
- fillImageList( dir );
-
- if( !m_image_list.size() )
- {
- // nothing found, turn up one directory level
- dir = dirname( (char *)dir.c_str() );
+void mgImageProvider::updateFromItemDirectory() {
+ // no images in tags, find images in the directory of the file itself
+ string dir = dirname( (char *) (currItem->getSourceFile().c_str()) );
+
+ // do something, when there are no images here, either:
+ // simply go up one step in the directory hierarchy, until we reach top level directory
+ bool toplevel_reached = false;
+ while( !m_image_list.size() && !toplevel_reached ) {
+ if( samedir( dir.c_str(), the_setup.ToplevelDir ) ) {
+ toplevel_reached = true;
+ }
+
+ fillImageList( dir );
+
+ if( !m_image_list.size() ) {
+ // nothing found, turn up one directory level
+ dir = dirname( (char *)dir.c_str() );
+ }
}
- }
}
-bool mgImageProvider::updateItem( mgItemGd *item )
-{
- // clean up stuff from previous item ?
-
- string filename = item->getSourceFile();
-
- if( m_mode == IM_ITEM_DIR )
- { // do not try to acquire new images when we are playing back a separate directory
- deleteTemporaryImages();
-
- // check whether the item has cover images defined in the database ?
- bool has_db_images = extractImagesFromDatabase( item );
-
- if( !has_db_images )
- {
- // no. check whether the item has cover images defined in tags ?
- string dir = extractImagesFromTag( filename );
-
- if( dir == "" )
- {
- // no. use anything from the directory
- updateFromItemDirectory( item );
- }
- else
- {
- fillImageList( dir );
- }
- }
-
- // start a thread to convert all images in 'dir' into .mpg format in the background
- Start();
-
- m_image_index = 0;
- }
- // else: nothing todo when changing the item currently being played
-
- Lock();
- bool result = m_image_list.size() > 0;
- Unlock();
-
- return result;
+bool mgImageProvider::updateItem( mgItemGd *newitem ) {
+ // clean up stuff from previous item ?
+
+ if (newitem==currItem)
+ return false;
+ currItem=newitem;
+ return CollectImages();
}
-void mgImageProvider::Action()
-{
- // convert all images
- Lock();
- vector<string> images( m_image_list );
- Unlock();
-
- for( unsigned i=0; i < images.size(); i++ )
- {
- FILE *fp;
- string filename = images[i];
-
- if( (fp = fopen( filename.c_str(), "rb" )) )
- {
- // filename can be opened
- fclose (fp);
-
- // determine the filename of a (to be) cached .mpg file
- string bname = basename( (char *)filename.c_str() );
- unsigned dotpos = bname.rfind( ".", filename.length() );
-
- // assemble path from relative paths
- string tmpFile = string( the_setup.ImageCacheDir ) + string( "/" ) + bname.substr( 0, dotpos ) + string( ".mpg" );
-
-
- char *tmp;
- msprintf( &tmp, "muggle-image-convert \"%s\" \"%s\"", filename.c_str(), tmpFile.c_str() );
- system( (const char*) tmp );
- free(tmp);
-
- // if file can be read by process, add to the list of converted images
- if( !access( tmpFile.c_str(), R_OK ) )
- {
- Lock();
- m_converted_images.push_back( tmpFile );
- Unlock();
- }
+string
+mgImageProvider::getCachedMPGFile(mgItemGd *item,string f) {
+ // determine the filename of a (to be) cached .mpg file
+ string bname = basename( (char *)f.c_str() );
+ unsigned dotpos = bname.rfind('.');
+ if (dotpos != string::npos)
+ bname.replace(dotpos,9999,".mpg");
+ else
+ bname = bname + ".mpg";
+ string result=item->getCachedFilename("covers/",false) + bname;
+ mkdir_p(result.c_str());
+ return result;
+}
+
+bool mgImageProvider::CollectImages() {
+ string filename = currItem->getSourceFile();
+ m_image_list.clear();
+
+
+ if( m_mode == IM_ITEM_DIR ) {
+ // do not try to acquire new images when we are playing back a separate directory
+ // check whether the item has cover images defined in the database ?
+ bool has_db_images = extractImagesFromDatabase();
+
+ if( !has_db_images ) {
+ // no. check whether the item has cover images defined in tags ?
+ string dir = extractImagesFromTag( filename );
+
+ if( dir == "" ) {
+ // no. use anything from the directory
+ updateFromItemDirectory();
+ }
+ else {
+ fillImageList( dir );
+ }
+ }
+
+ m_image_index = 0;
+ }
+ bool result = m_image_list.size() > 0;
+ return result;
+}
+
+bool mgMpgImageProvider::updateItem( mgItemGd *newitem ) {
+ // clean up stuff from previous item ?
+ if (newitem==currItem)
+ return false;
+ Lock();
+ currItem=newitem;
+ m_converted_images.clear();
+ m_need_conversion.clear();
+ Unlock();
+ CollectImages();
+ for( unsigned i=0; i < m_image_list.size(); i++ ) {
+ FILE *fp;
+ string filename = m_image_list[i];
+
+ if( (fp = fopen( filename.c_str(), "rb" )) ) {
+ // filename can be opened
+ fclose (fp);
+ string tmpFile = getCachedMPGFile(currItem,filename);
+ struct stat stsource;
+ struct stat sttmp;
+ if (stat(filename.c_str(),&stsource))
+ stsource.st_mtime=0L;
+ if (stat(tmpFile.c_str(),&sttmp))
+ sttmp.st_mtime=0L;
+ Lock();
+ if (sttmp.st_mtime==0L || sttmp.st_mtime<stsource.st_mtime)
+ m_need_conversion.push_back(filename);
+ else if( !access( tmpFile.c_str(), R_OK ))
+ m_converted_images.push_back( tmpFile );
+ Unlock();
+ }
}
+ if( m_mode == IM_ITEM_DIR )
+ Start();
+ Lock();
+ bool result = m_converted_images.size() > 0;
+ Unlock();
- // Check whether we need to continue this!? Next song may be playing already...
- }
- // cout << "Image conversion thread ending." << endl << flush;
+ return result;
}
-void mgImageProvider::fillImageList( string dir )
-{
- // obtain all .png, .jpg in dir and gather them in m_image_list
+void mgMpgImageProvider::Action() {
+ // convert all imges
+ Lock();
+ vector<string> images( m_need_conversion );
+ mgItemGd *cnvItem = currItem;
+ Unlock();
+ for( unsigned i=0; i < images.size(); i++ ) {
+ string tmpFile = getCachedMPGFile(cnvItem,images[i]);
+ char *tmp;
+ msprintf( &tmp, "%s/scripts/muggle-image-convert \"%s\" \"%s\" %d %d %d %d", the_setup.ConfigDirectory.c_str(),
+ images[i].c_str(), tmpFile.c_str(),
+ coverarea.x1,coverarea.y1,coverarea.Width(),coverarea.Height() );
+ mgDebug(1,"%d %s",system( (const char*) tmp ),tmp);
+ free(tmp);
+ // if file can be read by process, add to the list of converted images
+ if( !access( tmpFile.c_str(), R_OK ) ) {
+ Lock();
+ m_converted_images.push_back( tmpFile );
+ Unlock();
+ }
+ // Check whether we need to continue this!? Next song may be playing already...
+ }
+ // cout << "Image conversion thread ending." << endl << flush;
+}
- struct dirent **files;
- int count = scandir( dir.c_str(), &files, picture_select, alphasort );
+void mgImageProvider::fillImageList( string dir ) {
+ // obtain all .png, .jpg in dir and gather them in m_image_list
- if( count )
- {
- for ( int i=0; i < count; i++ )
- {
- string fname = dir + "/" + string( files[i]->d_name );
- m_image_list.push_back( fname );
- free( files[i] );
+ struct dirent **files;
+ int count = scandir( dir.c_str(), &files, picture_select, alphasort );
+
+ if( count ) {
+ for ( int i=0; i < count; i++ ) {
+ string fname = dir + "/" + string( files[i]->d_name );
+ m_image_list.push_back( fname );
+ free( files[i] );
+ }
+ free( files );
}
- free( files );
- }
}
-void mgImageProvider::writeImage( TagLib::ByteVector &image, int num, string &image_cache )
-{
- char* image_data = image.data();
- int len = image.size();
-
- // save image_data to temporary file
- char *buf;
- msprintf( &buf, "%s/image-%d.jpg", image_cache.c_str(), num );
-
- FILE *f = fopen( buf, "w+" );
- fwrite( image_data, sizeof(char), len, f );
- fclose( f );
- free( buf );
+void mgImageProvider::writeImage( TagLib::ByteVector &image, int num, string &image_cache ) {
+ char* image_data = image.data();
+ int len = image.size();
+
+ // save image_data to temporary file
+ char *buf;
+ msprintf( &buf, "%s/image-%d.jpg", image_cache.c_str(), num );
+
+ FILE *f = fopen( buf, "w+" );
+ fwrite( image_data, sizeof(char), len, f );
+ fclose( f );
+ free( buf );
}
-string mgImageProvider::treatFrameList( TagLib::ID3v2::FrameList &l, string &image_cache )
-{
- string result;
-
- if( !l.isEmpty() )
- {
- m_delete_imgs_from_tag = true;
- TagLib::ID3v2::FrameList::ConstIterator it = l.begin();
-
- int num = 0;
- while( !(it == l.end()) )
- {
- TagLib::ID3v2::AttachedPictureFrame *picframe = (TagLib::ID3v2::AttachedPictureFrame*) (*it);
-
- TagLib::ByteVector image = picframe->picture();
- writeImage( image, num, image_cache );
-
- it++;
- num++;
+string mgImageProvider::treatFrameList( TagLib::ID3v2::FrameList &l, string &image_cache ) {
+ string result;
+
+ if( !l.isEmpty() ) {
+ m_delete_imgs_from_tag = true;
+ TagLib::ID3v2::FrameList::ConstIterator it = l.begin();
+
+ int num = 0;
+ while( !(it == l.end()) ) {
+ TagLib::ID3v2::AttachedPictureFrame *picframe = (TagLib::ID3v2::AttachedPictureFrame*) (*it);
+
+ TagLib::ByteVector image = picframe->picture();
+ writeImage( image, num, image_cache );
+
+ it++;
+ num++;
+ }
+ result = image_cache;
}
- result = image_cache;
- }
- else
- {
- result = "";
- }
-
- return result;
+ else {
+ result = "";
+ }
+
+ return result;
}
-string mgImageProvider::extractImagesFromTag( string f )
-{
- TagLib::ID3v2::FrameList l;
- const char *filename = f.c_str();
- string image_cache = string( the_setup.ImageCacheDir );
- string dir = "";
-
- if( !strcasecmp(extension(filename), "flac") )
- {
- TagLib::FLAC::File f(filename);
- if (f.ID3v2Tag())
- {
- l = f.ID3v2Tag()->frameListMap()["APIC"];
- dir = treatFrameList( l, image_cache );
- }
- }
- else if( !strcasecmp(extension(filename), "mp3") )
- {
- TagLib::MPEG::File f(filename);
- if (f.ID3v2Tag())
- {
- l = f.ID3v2Tag()->frameListMap()["APIC"];
- dir = treatFrameList( l, image_cache );
- }
- }
- else if( !strcasecmp(extension(filename), "ogg") )
- {
- // what to do here?
-
- TagLib::Vorbis::File f(filename);
- /*
- if (f.ID3v2Tag())
- {
- l = f.ID3v2Tag()->frameListMap()["APIC"];
- dir = treatFrameList( l, image_cache );
- }
- */
- }
-
- // returns empty if no images were found in tags
- return dir;
+string mgImageProvider::extractImagesFromTag( string f ) {
+ TagLib::ID3v2::FrameList l;
+ const char *filename = f.c_str();
+ string image_cache = string( the_setup.CacheDir );
+ string dir = "";
+
+ if( !strcasecmp(extension(filename), "flac") ) {
+ TagLib::FLAC::File f(filename);
+ if (f.ID3v2Tag()) {
+ l = f.ID3v2Tag()->frameListMap()["APIC"];
+ dir = treatFrameList( l, image_cache );
+ }
+ }
+ else if( !strcasecmp(extension(filename), "mp3") ) {
+ TagLib::MPEG::File f(filename);
+ if (f.ID3v2Tag()) {
+ l = f.ID3v2Tag()->frameListMap()["APIC"];
+ dir = treatFrameList( l, image_cache );
+ }
+ }
+ else if( !strcasecmp(extension(filename), "ogg") ) {
+ // what to do here?
+
+ TagLib::Vorbis::File f(filename);
+ /*
+ if (f.ID3v2Tag())
+ {
+ l = f.ID3v2Tag()->frameListMap()["APIC"];
+ dir = treatFrameList( l, image_cache );
+ }
+ */
+ }
+
+ // returns empty if no images were found in tags
+ return dir;
}
+string mgMpgImageProvider::getImagePath( string &source ) {
+ // returns converted image name, writes source image name into source argument reference
+
+ string fname="";
+ source = "";
+
+ // check, how many images are converted at all
+ Lock();
+ if (m_image_index>=m_converted_images.size())
+ m_image_index=0;
+ if( m_image_index < m_converted_images.size() ) {
+ fname = m_converted_images[ m_image_index ];
+ // here we could mangle the original name to add an extension such as -s200
+ source = m_image_list[ m_image_index ];
+ }
+ m_image_index += 1;
+ Unlock();
+
+ return fname;
+}
diff --git a/mg_image_provider.h b/mg_image_provider.h
index 3c7433c..4ee7dbb 100644
--- a/mg_image_provider.h
+++ b/mg_image_provider.h
@@ -1,5 +1,4 @@
-
#ifndef MG_IMAGE_PROVIDER
#define MG_IMAGE_PROVIDER
@@ -10,97 +9,101 @@ class mgItemGd;
#include <string>
#include <vector>
+using namespace std;
+
#include <id3v2frame.h>
#include <tbytevector.h>
#include <id3v2tag.h>
-class mgImageProvider : public cThread
+#include <vdr/osd.h>
+
+class mgImageProvider
{
- public:
+ public:
- /*! \brief obtain next image in list
- */
- std::string getImagePath( std::string &source );
+ /*! \brief obtain next image in list
+ */
+ virtual string getImagePath( std::string &source );
- /*! \brief tell the image provider that we are replaying a different item now. Return, whether images were found.
- */
- bool updateItem( mgItemGd *item );
+ /*! \brief tell the image provider that we are replaying a different item now. Return, whether images were found.
+ */
+ virtual bool updateItem( mgItemGd *newitem );
- /*! \brief Initialize image provider with all files in the given directory
- */
- static mgImageProvider* Create( std::string dir );
+ mgImageProvider(string dir);
- /*! \brief Initialize image provider for use with a Giantdisc item
- */
- static mgImageProvider* Create();
+ mgImageProvider(tArea area);
- /*! \brief destroy instance and delete remaining temporary files
- */
- ~mgImageProvider();
+ ~mgImageProvider() {};
- protected:
+ protected: // TODO was davon ist private?
- /*! \brief Executes image conversion (.jpg to .mpg) in a separate thread
- */
- virtual void Action();
+ /*! \brief Obtain all images in dir in alphabetic order
+ */
+ void fillImageList( std::string dir );
- private:
+ /*! \brief update images according to GD scheme from database entry
+ */
+ bool extractImagesFromDatabase();
- mgImageProvider( std::string dir );
+ /*! \brief find images for an item
+ */
+ void updateFromItemDirectory();
- mgImageProvider( );
-
- /*! \brief Obtain all images in dir in alphabetic order
- */
- void fillImageList( std::string dir );
+ /*! \brief write image from an id3v2 frame to a file
+ */
+ void writeImage( TagLib::ByteVector &image, int num, std::string &image_cache );
- /*! \brief update images according to GD scheme from database entry
- */
- bool extractImagesFromDatabase( mgItemGd *item );
+ /*! \brief convert all images found in the APIC frame list
+ */
+ std::string treatFrameList( TagLib::ID3v2::FrameList &l, std::string &image_cache );
- /*! \brief find images for an item
- */
- void updateFromItemDirectory( mgItemGd *item );
+ /*! \brief save images from APIC tag and save to file. returns directory where images were saved or empty string if no images were found in the APIC tag
+ */
+ std::string extractImagesFromTag( std::string filename );
- /*! \brief write image from an id3v2 frame to a file
- */
- void writeImage( TagLib::ByteVector &image, int num, std::string &image_cache );
+ //! \brief define various modes how images can be obtained
+ enum ImageMode
+ {
+ IM_ITEM_DIR, //< Use images from the directory in which the mgItem resides
+ IM_PLAYLIST //< Use a separate playlist passed from an external plugin
+ };
- /*! \brief convert all images found in the APIC frame list
- */
- std::string treatFrameList( TagLib::ID3v2::FrameList &l, std::string &image_cache );
+ ImageMode m_mode;
+ mgItemGd *currItem;
- /*! \brief save images from APIC tag and save to file. returns directory where images were saved or empty string if no images were found in the APIC tag
- */
- std::string extractImagesFromTag( std::string filename );
+ //! \brief index of last image delivered
+ unsigned m_image_index;
- /*! \brief delete temporary images
- */
- void deleteTemporaryImages();
+ //! \brief list of all image filenames when in modes ITEM_DIR or PLAYLIST
+ std::vector<std::string> m_image_list;
- //! \brief define various modes how images can be obtained
- enum ImageMode
- {
- IM_ITEM_DIR, //< Use images from the directory in which the mgItem resides
- IM_PLAYLIST //< Use a separate playlist passed from an external plugin
- };
+ std::vector<std::string> m_need_conversion;
- //! \brief the current image delivery mode
- ImageMode m_mode;
+ //! \brief list of all converted image filenames (.mpg) when in modes ITEM_DIR or PLAYLIST
+ std::vector<std::string> m_converted_images;
+
+ std::string m_source_dir;
- //! \brief index of last image delivered
- unsigned m_image_index;
+ bool m_delete_imgs_from_tag;
- //! \brief list of all image filenames when in modes ITEM_DIR or PLAYLIST
- std::vector<std::string> m_image_list;
+ tArea coverarea;
+ bool CollectImages();
+ string getCachedMPGFile(mgItemGd *item,string f);
+};
- //! \brief list of all converted image filenames (.mpg) when in modes ITEM_DIR or PLAYLIST
- std::vector<std::string> m_converted_images;
-
- std::string m_source_dir;
+class mgMpgImageProvider : public mgImageProvider, public cThread
+{
+ public:
+
+ /*! \brief Executes image conversion (.jpg to .mpg) in a separate thread
+ */
+ mgMpgImageProvider(string dir);
+ mgMpgImageProvider(tArea area);
+ ~mgMpgImageProvider();
+ string getImagePath( std::string &source );
+ void Action();
+ bool updateItem( mgItemGd *newitem );
- bool m_delete_imgs_from_tag;
};
#endif
-
diff --git a/mg_incremental_search.c b/mg_incremental_search.c
index b5c5356..bd4a397 100644
--- a/mg_incremental_search.c
+++ b/mg_incremental_search.c
@@ -16,69 +16,66 @@
using namespace std;
-static char* keys[] = { " 0",
- ".-_1",
- "abc2",
- "def3",
- "ghi4",
- "jkl5",
- "mno6",
- "pqrs7",
- "tuv8",
- "wxyz9" };
+static const char* keys[] = {
+ " 0",
+ ".-_1",
+ "abc2",
+ "def3",
+ "ghi4",
+ "jkl5",
+ "mno6",
+ "pqrs7",
+ "tuv8",
+ "wxyz9"
+};
mgIncrementalSearch::mgIncrementalSearch()
- : m_position(-1), m_repeats(0), m_last_key(100), m_last_keypress(0.0)
+: m_position(-1), m_repeats(0), m_last_key(100), m_last_keypress(0.0)
{ }
-string mgIncrementalSearch::KeyStroke( unsigned key )
-{
- struct timeval now ;
- gettimeofday( &now, NULL );
+string mgIncrementalSearch::KeyStroke( unsigned key ) {
+ struct timeval now ;
+ gettimeofday( &now, NULL );
- double current_t = now.tv_sec + (double)now.tv_usec / 1000000.0;
- double delta_t = current_t - m_last_keypress;
- m_last_keypress = current_t;
+ double current_t = now.tv_sec + (double)now.tv_usec / 1000000.0;
+ double delta_t = current_t - m_last_keypress;
+ m_last_keypress = current_t;
- const double IS_TIMEOUT = 1.0; // 1 second
- if( delta_t > IS_TIMEOUT || key != m_last_key )
- {
- m_position ++;
+ // 1 second
+ const double IS_TIMEOUT = 1.0;
+ if( delta_t > IS_TIMEOUT || key != m_last_key ) {
+ m_position ++;
- char tmp[2];
- tmp[0] = (keys[key])[0];
- tmp[1] = '\0';
+ char tmp[2];
+ tmp[0] = (keys[key])[0];
+ tmp[1] = '\0';
- m_buffer += string( tmp );
+ m_buffer += string( tmp );
- m_repeats = 0;
- m_last_key = key;
- }
- else
- {
- // within timeout and have the same key
- // position remains
- m_repeats ++;
-
- if( (unsigned) m_repeats >= strlen( keys[key] ) )
- {
- // wrap around to first char
- m_repeats = 0;
+ m_repeats = 0;
+ m_last_key = key;
}
- m_buffer[m_position] = (keys[key])[m_repeats];
- }
- return m_buffer;
+ else {
+ // within timeout and have the same key
+ // position remains
+ m_repeats ++;
+
+ if( (unsigned) m_repeats >= strlen( keys[key] ) ) {
+ // wrap around to first char
+ m_repeats = 0;
+ }
+ m_buffer[m_position] = (keys[key])[m_repeats];
+ }
+ return m_buffer;
}
-string mgIncrementalSearch::Backspace()
-{
- if( !m_buffer.empty() )
- {
- m_buffer.erase( m_buffer.size()-1, 1 );
- m_position--;
- m_last_key=100;
- m_last_keypress=0.0;
- m_repeats=0;
- }
- return m_buffer;
+string mgIncrementalSearch::Backspace() {
+ if( !m_buffer.empty() ) {
+ m_buffer.erase( m_buffer.size()-1, 1 );
+ m_position--;
+ m_last_key=100;
+ m_last_keypress=0.0;
+ m_repeats=0;
+ }
+ return m_buffer;
}
diff --git a/mg_incremental_search.h b/mg_incremental_search.h
index e125577..19a249f 100644
--- a/mg_incremental_search.h
+++ b/mg_incremental_search.h
@@ -18,19 +18,18 @@
class mgIncrementalSearch
{
- public:
- mgIncrementalSearch();
+ public:
+ mgIncrementalSearch();
- std::string KeyStroke( unsigned key );
+ std::string KeyStroke( unsigned key );
- std::string Backspace();
+ std::string Backspace();
- private:
- std::string m_buffer;
- int m_position;
- unsigned m_repeats, m_last_key;
-
- double m_last_keypress;
-};
+ private:
+ std::string m_buffer;
+ int m_position;
+ unsigned m_repeats, m_last_key;
+ double m_last_keypress;
+};
#endif
diff --git a/mg_item.c b/mg_item.c
index 5e67d39..83f37f1 100644
--- a/mg_item.c
+++ b/mg_item.c
@@ -21,12 +21,12 @@
bool
mgItem::Valid(bool Silent) const
{
- if (!m_validated)
- {
- getSourceFile(true,Silent); // sets m_valid as a side effect
- m_validated=true;
- }
- return m_valid;
+ if (!m_validated) {
+ // sets m_valid as a side effect
+ getSourceFile(true,Silent);
+ m_validated=true;
+ }
+ return m_valid;
}
bool
@@ -43,8 +43,7 @@ mgItem::readable(string filename) const
int i=read(fd,buf,10);
int err = errno;
close(fd);
- if (i!=10)
- {
+ if (i!=10) {
errno = 123456;
return false;
}
@@ -58,58 +57,72 @@ mgItem::readable(string filename) const
void
mgItem::analyze_failure(string filename) const
{
- readable(filename); // sets errno
+ readable(filename); // sets errno
int err = errno;
int nsize = filename.size();
- char *p;
+ const char *p;
if (err==123456)
p="File too short";
else
p = strerror(err);
- extern void showmessage(int duration,const char*,...);
+ extern void showmessage(int duration,const char*,...);
if (nsize<20)
showmessage(0,"%s not readable, errno=%d",filename.c_str(),err);
else
showmessage(0,"%s..%s not readable, errno=%d",
- filename.substr(0,15).c_str(),filename.substr(nsize-15).c_str(),err);
+ filename.substr(0,15).c_str(),filename.substr(nsize-15).c_str(),err);
mgWarning ("cannot read %s: %s", filename.c_str (),p);
}
-mgItem::mgItem()
-{
- m_valid = false;
- m_validated = false;
- m_itemid = -1;
- m_year = 0;
- m_rating = 0;
- m_duration = 0;
+mgItem::mgItem() {
+ m_valid = false;
+ m_validated = false;
+ m_itemid = -1;
+ m_year = 0;
+ m_rating = 0;
+ m_duration = 0;
}
mgItem*
-mgItem::Clone ()
-{
- if (!this)
- return 0;
- mgItem* result = new mgItem();
- result->InitFrom(this);
- return result;
+mgItem::Clone () {
+ if (!this)
+ return 0;
+ mgItem* result = new mgItem();
+ result->InitFrom(this);
+ return result;
}
void
-mgItem::InitFrom(const mgItem* c)
+mgItem::InitFrom(const mgItem* c) {
+ m_sel = c->m_sel;
+ m_valid = c->m_valid;
+ m_validated = c->m_validated;
+ m_itemid = c->m_itemid;
+ m_title = c->m_title;
+ m_realfile = c->m_realfile;
+ m_genre_id = c->m_genre_id;
+ m_genre = c->m_genre;
+ m_language = c->m_language;
+ m_language_id = c->m_language_id;
+ m_year = c->m_year;
+ m_rating = c->m_rating;
+ m_duration = c->m_duration;
+}
+
+
+string
+mgItem::ABC(string s) const
{
- m_sel = c->m_sel;
- m_valid = c->m_valid;
- m_validated = c->m_validated;
- m_itemid = c->m_itemid;
- m_title = c->m_title;
- m_realfile = c->m_realfile;
- m_genre_id = c->m_genre_id;
- m_genre = c->m_genre;
- m_language = c->m_language;
- m_language_id = c->m_language_id;
- m_year = c->m_year;
- m_rating = c->m_rating;
- m_duration = c->m_duration;
+ string result="";
+ if (!s.size()) return " ";
+ result += s[0];
+ if (the_setup.utf8) { // does not recognize illegal utf8 values
+ unsigned char first=(unsigned char) result[0];
+ if (first>0xc0) result += s[1];
+ if (first>0xe0) result += s[2];
+ if (first>0xf0) result += s[3];
+ }
+ mgDebug(1,"ABC(%s):%s - %d",s.c_str(),result.c_str(),result.size());
+ return result;
}
diff --git a/mg_item.h b/mg_item.h
index 4d5bf2d..4067986 100644
--- a/mg_item.h
+++ b/mg_item.h
@@ -20,52 +20,51 @@ using namespace std;
class mgSelection;
-//! \brief represents a content item
+//! \brief represents a content item
class mgItem
{
- public:
-//! \brief copy constructor
-// mgItem (const mgItem* c);
- mgItem();
- virtual mgItem* Clone();
- virtual ~mgItem() {};
- bool Valid(bool Silent=false) const;
- virtual long getItemid() const { return m_itemid; }
- virtual mgListItem* getKeyItem(mgKeyTypes kt) const { return new mgListItem; }
-//! \brief returns filename
- virtual string getSourceFile (bool AbsolutePath=true,bool Silent=false) const { return m_realfile; }
-//! \brief returns title
- string getTitle () const { return m_title; }
-//! \brief returns the name of the language
- string getLanguage () const { return m_language; }
-//! \brief returns year
- int getYear () const { return m_year; }
-//! \brief returns rating
- int getRating () const { return m_rating; }
-//! \brief returns duration
- int getDuration () const { return m_duration; }
- virtual string getImagePath (bool AbsolutePath=true) const { return ""; }
- void setSelection(const mgSelection* sel) { m_sel=sel; }
- const mgSelection* getSelection() const { return m_sel; }
+ public:
+ //! \brief copy constructor
+ // mgItem (const mgItem* c);
+ mgItem();
+ virtual mgItem* Clone();
+ virtual ~mgItem() {};
+ bool Valid(bool Silent=false) const;
+ virtual long getItemid() const { return m_itemid; }
+ virtual mgListItem* getKeyItem(mgKeyTypes kt) const { return new mgListItem; }
+ //! \brief returns filename
+ virtual string getSourceFile (bool AbsolutePath=true,bool Silent=false) const { return m_realfile; }
+ //! \brief returns title
+ string getTitle () const { return m_title; }
+ //! \brief returns the name of the language
+ string getLanguage () const { return m_language; }
+ //! \brief returns year
+ int getYear () const { return m_year; }
+ //! \brief returns rating
+ int getRating () const { return m_rating; }
+ //! \brief returns duration
+ int getDuration () const { return m_duration; }
+ virtual string getImagePath (bool AbsolutePath=true) const { return ""; }
+ void setSelection(const mgSelection* sel) { m_sel=sel; }
+ const mgSelection* getSelection() const { return m_sel; }
+ string ABC(string s) const;
- protected:
- mutable bool m_valid;
- mutable bool m_validated;
- long m_itemid;
- string m_title;
- string m_realfile;
- int m_year;
- int m_rating;
- int m_duration;
- string m_genre_id;
- string m_genre;
- string m_language_id;
- string m_language;
- bool readable(string filename) const;
- void analyze_failure(string filename) const;
- void InitFrom(const mgItem* c);
- const mgSelection* m_sel;
+ protected:
+ mutable bool m_valid;
+ mutable bool m_validated;
+ long m_itemid;
+ string m_title;
+ string m_realfile;
+ int m_year;
+ int m_rating;
+ int m_duration;
+ string m_genre_id;
+ string m_genre;
+ string m_language_id;
+ string m_language;
+ bool readable(string filename) const;
+ void analyze_failure(string filename) const;
+ void InitFrom(const mgItem* c);
+ const mgSelection* m_sel;
};
-
-
#endif
diff --git a/mg_item_gd.c b/mg_item_gd.c
index a684b43..83fe4ee 100644
--- a/mg_item_gd.c
+++ b/mg_item_gd.c
@@ -13,6 +13,7 @@
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
+#include <fstream>
#include "mg_item_gd.h"
#include "mg_setup.h"
#include "mg_tools.h"
@@ -29,45 +30,43 @@ mgItemGd::getKeyItem(mgKeyTypes kt) const
long number=0;
bool val_is_number=false;
bool id_is_number=false;
- if (m_itemid>=0)
- {
+ if (m_itemid>=0) {
switch (kt) {
case keyGdGenres:
case keyGdGenre1:
case keyGdGenre2:
case keyGdGenre3: val = getGenre();id=m_genre_id;break;
case keyGdArtist: val = id = getArtist();break;
- case keyGdArtistABC: val = id = getArtist()[0];break;
+ case keyGdArtistABC: val = id = ABC(getArtist());break;
case keyGdAlbum: val = id = getAlbum();break;
case keyGdYear: val = id = string(ltos(getYear()));break;
case keyGdDecade: val = id = string(ltos(int((getYear() % 100) / 10) * 10));break;
case keyGdTitle: val = id = getTitle();break;
- case keyGdTitleABC: val = id = getTitle()[0];break;
+ case keyGdTitleABC: val = id = ABC(getTitle());break;
case keyGdTrack: val = getTitle();id_is_number=true;number=getTrack();break;
case keyGdLanguage: val = getLanguage();id=m_language_id ; break;
case keyGdRating: id_is_number=val_is_number=true;number=getRating();break;
case keyGdFolder1:
case keyGdFolder2:
case keyGdFolder3:
- case keyGdFolder4:
- {
- char *folders[4];
- char *fbuf=SeparateFolders(m_mp3file.c_str(),folders,4);
- val = id = folders[int(kt)-int(keyGdFolder1)];
- free(fbuf);
- break;
- }
+ case keyGdFolder4:
+ {
+ char *folders[4];
+ char *fbuf=SeparateFolders(m_mp3file.c_str(),folders,4);
+ val = id = folders[int(kt)-int(keyGdFolder1)];
+ free(fbuf);
+ break;
+ }
+ case keyGdUnique: val=id=ltos(getItemid());break;
default: return new mgListItem;
}
}
- if (val_is_number)
- {
+ if (val_is_number) {
char valbuf[30];
sprintf(valbuf,"%.5ld",number);
val=valbuf;
}
- if (id_is_number)
- {
+ if (id_is_number) {
char idbuf[30];
sprintf(idbuf,"%.5ld",number);
id=idbuf;
@@ -75,111 +74,141 @@ mgItemGd::getKeyItem(mgKeyTypes kt) const
return new mgListItem(val,id);
}
-
string mgItemGd::getGenre () const
{
- string result="";
- if (m_genre!="NULL")
- result = m_genre;
- if (m_genre2!="NULL" && m_genre2.size()>0)
- {
- if (!result.empty())
- result += "/";
- result += m_genre2;
- }
- return result;
+ string result="";
+ if (m_genre!="NULL")
+ result = m_genre;
+ if (m_genre2!="NULL" && m_genre2.size()>0) {
+ if (!result.empty())
+ result += "/";
+ result += m_genre2;
+ }
+ return result;
}
-
string mgItemGd::getBitrate () const
{
- return m_bitrate;
+ return m_bitrate;
}
-
string mgItemGd::getArtist () const
{
- return m_artist;
+ return m_artist;
}
-
string mgItemGd::getAlbum () const
{
- return m_albumtitle;
+ return m_albumtitle;
}
-
int mgItemGd::getSampleRate () const
{
- return m_samplerate;
+ return m_samplerate;
}
-
int mgItemGd::getChannels () const
{
- return m_channels;
+ return m_channels;
}
int mgItemGd::getTrack () const
{
- return m_tracknb;
+ return m_tracknb;
}
-mgItemGd::mgItemGd(const mgItemGd* c)
-{
- m_covercount = 0;
- InitFrom(c);
+mgItemGd::mgItemGd(const mgItemGd* c) {
+ InitFrom(c);
}
mgItemGd*
-mgItemGd::Clone ()
-{
- if (!this)
- return 0;
- else
- return new mgItemGd(this);
+mgItemGd::Clone () {
+ if (!this)
+ return 0;
+ else
+ return new mgItemGd(this);
}
void
-mgItemGd::InitFrom(const mgItemGd* c)
-{
- mgItem::InitFrom(c);
- m_mp3file = c->m_mp3file;
- m_artist = c->m_artist;
- m_albumtitle = c->m_albumtitle;
- m_genre2_id = c->m_genre2_id;
- m_genre2 = c->m_genre2;
- m_bitrate = c->m_bitrate;
- m_samplerate = c->m_samplerate;
- m_channels = c->m_channels;
- m_tracknb = c->m_tracknb;
+mgItemGd::InitFrom(const mgItemGd* c) {
+ mgItem::InitFrom(c);
+ m_covercount = c->m_covercount;
+ m_mp3file = c->m_mp3file;
+ m_artist = c->m_artist;
+ m_albumtitle = c->m_albumtitle;
+ m_genre2_id = c->m_genre2_id;
+ m_genre2 = c->m_genre2;
+ m_bitrate = c->m_bitrate;
+ m_samplerate = c->m_samplerate;
+ m_channels = c->m_channels;
+ m_tracknb = c->m_tracknb;
+ m_haslyrics = c->m_haslyrics;
+ m_checkedfortmplyrics = c->m_checkedfortmplyrics;
+ m_loadexternalstart = c->m_loadexternalstart;
}
+bool
+mgItemGd::HasLyrics() {
+ if (m_haslyrics<0) {
+ m_haslyrics=0; // false
+ string lyricsname=getCachedFilename("lyrics");
+ ifstream f(lyricsname.c_str());
+ if (!f) return false;
+ string line;
+ if (!getline(f, line , '\n')) return false;
+ if (line.find("Unusual page layout")!=string::npos) return false;
+ m_haslyrics=1;
+ }
+ return m_haslyrics;
+}
+
+long
+mgItemGd::getCheckedForTmpLyrics() {
+ if (m_checkedfortmplyrics-m_loadexternalstart>60) // abort waiting after 60 seconds
+ setCheckedForTmpLyrics(0);
+ return m_checkedfortmplyrics;
+}
+
+void
+mgItemGd::setCheckedForTmpLyrics(const long t) {
+ if (m_loadexternalstart==0 || t==0) {
+ m_loadexternalstart=t;
+ }
+ m_checkedfortmplyrics=t;
+}
+
+string
+mgItemGd::getCachedFilename(string extension,bool mkdir) const
+{
+ string result=getSourceFile(false);
+ string::size_type dot=result.rfind('.');
+ if (dot != string::npos)
+ result.replace(dot+1,9999,extension);
+ result = string(the_setup.CacheDir)+"/"+result;
+ if (mkdir) mkdir_p(result.c_str());
+ return result;
+}
string
mgItemGd::getSourceFile(bool AbsolutePath,bool Silent) const
{
string result;
string tld = the_setup.ToplevelDir;
- if (AbsolutePath)
- {
+ if (AbsolutePath) {
result = getSourceFile(false,Silent);
if (!result.empty())
result = tld + result;
return result;
}
int access_errno=0;
- result = m_mp3file;
+ result = m_mp3file;
if (m_validated && !m_valid)
return m_mp3file;
- if (!readable(result))
- {
+ if (!readable(result)) {
access_errno=errno;
result="";
- if (!gd_music_dirs_scanned)
- {
- for (unsigned int i =0 ; i < 100 ; i++)
- {
+ if (!gd_music_dirs_scanned) {
+ for (unsigned int i =0 ; i < 100 ; i++) {
struct stat stbuf;
char *dir;
msprintf(&dir,"%s%02d",tld.c_str(),i);
@@ -188,14 +217,12 @@ mgItemGd::getSourceFile(bool AbsolutePath,bool Silent) const
}
gd_music_dirs_scanned=true;
}
- for (unsigned int i =0 ; i < 100 ; i++)
- {
+ for (unsigned int i =0 ; i < 100 ; i++) {
if (!gd_music_dir_exists[i])
continue;
char *file;
msprintf(&file,"%02d/%s",i,m_mp3file.c_str());
- if (readable(file))
- {
+ if (readable(file)) {
m_mp3file = file;
result = m_mp3file;
}
@@ -205,90 +232,90 @@ mgItemGd::getSourceFile(bool AbsolutePath,bool Silent) const
}
}
m_validated=true;
- if (result.empty())
- {
+ if (result.empty()) {
if (!Silent)
analyze_failure(m_mp3file);
m_valid = false;
return m_mp3file;
- }
+ }
return result;
}
string
mgItemGd::getImagePath(bool AbsolutePath) const
{
- return m_coverimg;
+ return m_coverimg;
}
-mgItemGd::mgItemGd (char **row)
-{
- m_valid = true;
- m_validated = false;
- m_covercount = 0;
- m_itemid = atol (row[0]);
- if (row[1])
- m_title = row[1];
- else
- m_title = "NULL";
- if (row[2])
- m_mp3file = row[2];
- else
- m_mp3file = "NULL";
- if (row[3])
- m_artist = row[3];
- else
- m_artist = "NULL";
- if (row[4])
- m_albumtitle = row[4];
- else
- m_albumtitle = "NULL";
- if (row[5] && row[5][0])
- m_genre_id = row[5];
- else
- m_genre_id = "NULL";
- m_genre = KeyMaps.value(keyGdGenres,m_genre_id);
- if (row[6] && row[6][0])
- m_genre2_id = row[6];
- else
- m_genre2_id = "NULL";
- m_genre2 = KeyMaps.value(keyGdGenres,m_genre2_id);
- if (row[7])
- m_bitrate = row[7];
- else
- m_bitrate = "NULL";
- if (row[8])
- m_year = atol (row[8]);
- else
- m_year = 0;
- if (row[9])
- m_rating = atol (row[9]);
- else
- m_rating = 0;
- if (row[10])
- m_duration = atol (row[10]);
- else
- m_duration = 0;
- if (row[11])
- m_samplerate = atol (row[11]);
- else
- m_samplerate = 0;
- if (row[12])
- m_channels = atol (row[12]);
- else
- m_channels = 0;
- if (row[13])
- m_language_id = row[13];
- else
- m_language_id = "NULL";
- if (row[14])
- m_tracknb = atol(row[14]);
- else
- m_tracknb = 0;
- if (row[15])
- m_coverimg = row[14];
- else
- m_coverimg = "";
- m_language = KeyMaps.value(keyGdLanguage,m_language_id);
+mgItemGd::mgItemGd (char **row) {
+ m_haslyrics=-1;
+ m_valid = true;
+ m_validated = false;
+ m_covercount = 0;
+ m_checkedfortmplyrics = 0;
+ m_loadexternalstart = 0;
+ m_itemid = atol (row[0]);
+ if (row[1])
+ m_title = row[1];
+ else
+ m_title = "NULL";
+ if (row[2])
+ m_mp3file = row[2];
+ else
+ m_mp3file = "NULL";
+ if (row[3])
+ m_artist = row[3];
+ else
+ m_artist = "NULL";
+ if (row[4])
+ m_albumtitle = row[4];
+ else
+ m_albumtitle = "NULL";
+ if (row[5] && row[5][0])
+ m_genre_id = row[5];
+ else
+ m_genre_id = "NULL";
+ m_genre = KeyMaps.value(keyGdGenres,m_genre_id);
+ if (row[6] && row[6][0])
+ m_genre2_id = row[6];
+ else
+ m_genre2_id = "NULL";
+ m_genre2 = KeyMaps.value(keyGdGenres,m_genre2_id);
+ if (row[7])
+ m_bitrate = row[7];
+ else
+ m_bitrate = "NULL";
+ if (row[8])
+ m_year = atol (row[8]);
+ else
+ m_year = 0;
+ if (row[9])
+ m_rating = atol (row[9]);
+ else
+ m_rating = 0;
+ if (row[10])
+ m_duration = atol (row[10]);
+ else
+ m_duration = 0;
+ if (row[11])
+ m_samplerate = atol (row[11]);
+ else
+ m_samplerate = 0;
+ if (row[12])
+ m_channels = atol (row[12]);
+ else
+ m_channels = 0;
+ if (row[13])
+ m_language_id = row[13];
+ else
+ m_language_id = "NULL";
+ if (row[14])
+ m_tracknb = atol(row[14]);
+ else
+ m_tracknb = 0;
+ if (row[15])
+ m_coverimg = row[15];
+ else
+ m_coverimg = "";
+ m_language = KeyMaps.value(keyGdLanguage,m_language_id);
};
-
diff --git a/mg_item_gd.h b/mg_item_gd.h
index 4369851..3b5bc0a 100644
--- a/mg_item_gd.h
+++ b/mg_item_gd.h
@@ -19,62 +19,79 @@ using namespace std;
#include "mg_tools.h"
#include "mg_item.h"
-//! \brief represents a GiantDisc content item
+//! \brief represents a GiantDisc content item
class mgItemGd : public mgItem
{
- public:
- mgListItem* getKeyItem(mgKeyTypes kt) const;
+ public:
+ mgListItem* getKeyItem(mgKeyTypes kt) const;
- //! \brief copy constructor
- mgItemGd(const mgItemGd* c);
+ //! \brief copy constructor
+ mgItemGd(const mgItemGd* c);
- //! \brief construct an item from an SQL row
- mgItemGd (char **row);
+ //! \brief construct an item from an SQL row
+ mgItemGd (char **row);
- mgItemGd* Clone();
+ mgItemGd* Clone();
-//! \brief returns filename
- string getSourceFile (bool AbsolutePath=true,bool Silent=false) const;
+ //! \brief returns music filename
+ string getSourceFile (bool AbsolutePath=true,bool Silent=false) const;
-//! \brief returns artist
- string getArtist () const;
+ bool HasLyrics();
-//! \brief returns the name of the album
- string getAlbum () const;
+ //! \brief returns music filename truncated at last dot
+ string getTruncFilename (bool AbsolutePath=true) const;
-//! \brief returns the name of genre
- string getGenre () const;
+ //! \brief returns cached music filename truncated at last dot
+ string getCachedFilename (string extension,bool mkdir=true) const;
-//! \brief returns the bitrate
- string getBitrate () const;
+ //! \brief returns artist
+ string getArtist () const;
-//! \brief returns samplerate
- int getSampleRate () const;
+ //! \brief returns the name of the album
+ string getAlbum () const;
-//! \brief returns # of channels
- int getChannels () const;
+ //! \brief returns the name of genre
+ string getGenre () const;
-//! \brief returns tracknb
- int getTrack () const;
+ //! \brief returns the bitrate
+ string getBitrate () const;
- string getImagePath(bool AbsolutePath=true) const;
+ //! \brief returns samplerate
+ int getSampleRate () const;
- protected:
- void InitFrom(const mgItemGd* c);
- private:
- mutable string m_mp3file;
- string m_artist;
- string m_albumtitle;
- string m_genre2_id;
- string m_genre2;
- string m_bitrate;
- mutable string m_coverimg;
- int m_samplerate;
- int m_channels;
- int m_tracknb;
- mutable int m_covercount;
+ //! \brief returns # of channels
+ int getChannels () const;
-
-};
+ //! \brief returns tracknb
+ int getTrack () const;
+
+ string getImagePath(bool AbsolutePath=true) const;
+
+ // unknown
+ void resetHasLyricsFile() {
+ m_haslyrics=-1;
+ }
+ long getCheckedForTmpLyrics();
+ void setCheckedForTmpLyrics(const long t);
+
+ protected:
+ void InitFrom(const mgItemGd* c);
+ private:
+ mutable string m_mp3file;
+ string m_artist;
+ string m_albumtitle;
+ string m_genre2_id;
+ string m_genre2;
+ string m_bitrate;
+ mutable string m_coverimg;
+ int m_samplerate;
+ int m_channels;
+ int m_tracknb;
+ mutable int m_covercount;
+ int m_haslyrics;
+ long m_checkedfortmplyrics;
+ long m_loadexternalstart;
+
+};
#endif
diff --git a/mg_keytypes.h b/mg_keytypes.h
index 1971cfa..a1d6a1b 100644
--- a/mg_keytypes.h
+++ b/mg_keytypes.h
@@ -12,8 +12,9 @@
#ifndef _MUGGLE_KEYTYPES_H
#define _MUGGLE_KEYTYPES_H
-enum mgKeyTypes {
- keyGdGenre1=1, // the genre types must have exactly this order!
+enum mgKeyTypes
+{
+ keyGdGenre1=1, // the genre types must have exactly this order!
keyGdGenre2,
keyGdGenre3,
keyGdGenres,
@@ -41,11 +42,11 @@ enum mgKeyTypes {
const mgKeyTypes mgGdKeyTypesLow = keyGdGenre1;
const mgKeyTypes mgGdKeyTypesHigh = keyGdUnique;
-enum mgSortBy {
+enum mgSortBy
+{
mgSortNone,
mgSortByValue,
mgSortById,
mgSortByIdNum
};
-
#endif
diff --git a/mg_listitem.c b/mg_listitem.c
index b7cac68..902d958 100644
--- a/mg_listitem.c
+++ b/mg_listitem.c
@@ -12,15 +12,13 @@
#include "mg_listitem.h"
#include <assert.h>
-mgListItem::mgListItem()
-{
+mgListItem::mgListItem() {
m_valid=false;
m_unique_id="0";
m_count=0;
}
-mgListItem::mgListItem(const mgListItem* from)
-{
+mgListItem::mgListItem(const mgListItem* from) {
assert(from);
m_valid=from->m_valid;
m_value=from->m_value;
@@ -29,14 +27,12 @@ mgListItem::mgListItem(const mgListItem* from)
m_count=from->m_count;
}
-mgListItem::mgListItem(string v,string i,unsigned int c)
-{
+mgListItem::mgListItem(string v,string i,unsigned int c) {
set(v,i,c);
}
mgListItem*
-mgListItem::Clone()
-{
+mgListItem::Clone() {
if (!this)
return 0;
else
@@ -44,8 +40,7 @@ mgListItem::Clone()
}
void
-mgListItem::set(string v,string i,unsigned int c)
-{
+mgListItem::set(string v,string i,unsigned int c) {
assert(this);
m_valid=true;
m_value=v;
@@ -54,15 +49,13 @@ mgListItem::set(string v,string i,unsigned int c)
}
void
-mgListItem::set_unique_id(string uid)
-{
+mgListItem::set_unique_id(string uid) {
assert(this);
m_unique_id=uid;
}
-void
-mgListItem::operator=(const mgListItem& from)
-{
+void
+mgListItem::operator=(const mgListItem& from) {
assert(this);
m_valid=from.m_valid;
m_value=from.m_value;
@@ -72,8 +65,7 @@ mgListItem::operator=(const mgListItem& from)
}
void
-mgListItem::operator=(const mgListItem* from)
-{
+mgListItem::operator=(const mgListItem* from) {
assert(this);
m_valid=from->valid();
m_value=from->value();
diff --git a/mg_listitem.h b/mg_listitem.h
index 14bb0f2..007942f 100644
--- a/mg_listitem.h
+++ b/mg_listitem.h
@@ -15,7 +15,6 @@
using namespace std;
-
class mgListItem
{
public:
@@ -40,5 +39,4 @@ class mgListItem
string m_unique_id;
unsigned int m_count;
};
-
#endif
diff --git a/mg_menu.c b/mg_menu.c
new file mode 100644
index 0000000..9abc7e4
--- /dev/null
+++ b/mg_menu.c
@@ -0,0 +1,502 @@
+/*!
+ * \file vdr_menu.c
+ * \brief Implements menu handling for browsing media libraries within VDR
+ *
+ * \version $Revision: 1.27 $ * \date $Date: 2008-03-14 21:12:59 +0100 (Fr, 14 Mär 2008) $
+ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner, Wolfgang Rohdewald
+ * \author Responsible author: $Author: woro $
+ *
+ * $Id: vdr_menu.c 1042 2008-03-14 20:12:59Z woro $
+ */
+
+#include <stdio.h>
+#include <assert.h>
+#include <fstream>
+
+#include <typeinfo>
+#include <string>
+#include <vector>
+
+#include <vdr/menuitems.h>
+#define __STL_CONFIG_H
+#include <vdr/tools.h>
+#include <vdr/config.h>
+#include <vdr/plugin.h>
+#include <vdr/i18n.h>
+
+#if VDRVERSNUM >= 10307
+#include <vdr/interface.h>
+#include <vdr/skins.h>
+#endif
+
+#include "mg_setup.h"
+#include "vdr_menu.h"
+#include "vdr_player.h"
+#include "mg_incremental_search.h"
+#include "mg_thread_sync.h"
+
+#include "mg_tools.h"
+#include "mg_sel_gd.h"
+
+void
+mgStatus::OsdCurrentItem(const char* Text) {
+ cOsdItem* i = main->Get(main->Current());
+ if (!i) return;
+ mgAction * a = dynamic_cast<mgAction *>(i);
+ if (a)
+ a->TryNotify();
+}
+
+mgMenu *
+mgOsd::Parent () {
+ if (Menus.size () < 2)
+ return NULL;
+ return Menus[Menus.size () - 2];
+}
+
+mgAction*
+mgMenu::GenerateAction(const mgActions action,mgActions on) {
+ mgAction *result = actGenerate(action);
+ if (result) {
+ result->SetMenu(this);
+ if (!result->Enabled(on)) {
+ delete result;
+ result=NULL;
+ }
+ }
+ return result;
+}
+
+eOSState
+mgMenu::ExecuteAction(const mgActions action,mgActions on) {
+ mgAction *a = GenerateAction (action,on);
+ if (a) {
+ a->Execute ();
+ delete a;
+ return osContinue;
+ }
+ return osUnknown;
+}
+
+mgMenu::mgMenu () {
+ m_osd = NULL;
+ m_parent_index=-1;
+ mgDebug(1,"ich bin mgMenu::mgMenu %04X ,m_parent_index=%d",this,m_parent_index);
+ RedAction = actNone;
+ GreenAction = actNone;
+ YellowAction = actNone;
+ BlueAction = actNone;
+}
+
+void
+mgOsd::SetHotkeyAction(eKeys key,mgActions action) {
+ if (Parent())
+ Parent()->SetHotkeyAction(key,action);
+}
+
+void
+mgMenu::SetHotkeyAction(eKeys key,mgActions action) {
+ switch (key) {
+ case kRed:
+ RedAction = action;
+ break;
+ case kGreen:
+ GreenAction = action;
+ break;
+ case kYellow:
+ YellowAction = action;
+ default:
+ break;
+ }
+}
+
+// ----------------------- mgOsd ----------------------
+
+mgOsd::mgOsd ():cOsdMenu ("",25) {
+ mgDebug("mgOsd starts");
+ m_Status = new mgStatus(this);
+ m_message = 0;
+ m_root = 0;
+ external_commands = 0;
+ m_save_warned=false;
+
+ // get values from mgValmaps
+ LoadExternalCommands(); // before AddMenu()
+ forcerefresh = false;
+}
+
+void
+mgOsd::LoadExternalCommands() {
+ // Read commands for collections in etc. /video/muggle/playlist_commands.conf
+ external_commands = new cCommands ();
+
+#if VDRVERSNUM >= 10318
+ cString cmd_file = AddDirectory (the_setup.ConfigDirectory.c_str(),
+ "playlist_commands.conf");
+ mgDebug (1, "mgOsd::Start: %d Looking for file %s",VDRVERSNUM, *cmd_file);
+ bool have_cmd_file = external_commands->Load (*cmd_file);
+#else
+ const char *
+ cmd_file = (const char *) AddDirectory (the_setup.ConfigDirectory,
+ "playlist_commands.conf");
+ mgDebug (1, "mgOsd::Start: %d Looking for file %s",VDRVERSNUM, cmd_file);
+ bool have_cmd_file = external_commands->Load ((const char *) cmd_file);
+#endif
+
+ if (!have_cmd_file) {
+ delete external_commands;
+ external_commands = NULL;
+ }
+}
+
+mgOsd::~mgOsd() {
+ mgDebug("mgOsd terminates");
+ delete m_Status;
+ delete m_root;
+ delete external_commands;
+}
+
+void
+mgOsd::InitMapFromSetup (mgValmap& nv) {
+ // values from setup override saved values
+ nv["Directory"] = the_setup.ConfigDirectory;
+}
+
+void
+mgMenu::AddAction (const mgActions action, mgActions on,const bool hotkey) {
+ mgAction *a = GenerateAction(action,on);
+ if (!a) return;
+ const char *mn = a->MenuName();
+ if (strlen(mn)==0)
+ mgError("AddAction(%d):MenuName is empty",int(action));
+ if (hotkey)
+ a->SetText(osd()->hk(mn));
+ else
+ a->SetText(mn);
+ free(const_cast<char*>(mn));
+ osd()->AddItem(a);
+}
+
+void
+mgMenu::AddExternalAction(const mgActions action, const char *title) {
+ mgAction *a = GenerateAction(action,actNone);
+ if (!a) return;
+ a->SetText(osd()->hk(title));
+ osd()->AddItem(a);
+}
+
+const char*
+mgMenu::HKey(const mgActions act,mgActions on) {
+ const char* result = NULL;
+ mgAction *a = GenerateAction(act,on);
+ if (a) {
+ result = a->ButtonName();
+ delete a;
+ }
+ return result;
+}
+
+void
+mgOsd::RefreshTitle() {
+ SetTitle(Menus.back()->Title().c_str());
+ Display ();
+}
+
+void
+mgMenu::InitOsd (const bool hashotkeys) {
+ osd ()->InitOsd (Title(),hashotkeys);
+ SetHelpKeys();
+}
+
+void
+mgMenu::SetHelpKeys(mgActions on) {
+ osd()->SetHelpKeys(
+ HKey(ButtonAction(kRed),on),
+ HKey(ButtonAction(kGreen),on),
+ HKey(ButtonAction(kYellow),on),
+ HKey(ButtonAction(kBlue),on));
+}
+
+void
+mgOsd::InitOsd (string title,const bool hashotkeys) {
+ Clear ();
+ SetTitle (title.c_str());
+ if (hashotkeys) SetHasHotkeys ();
+}
+
+void
+mgOsd::AddItem(mgAction *a) {
+ cOsdItem *c = dynamic_cast<cOsdItem*>(a);
+ if (!c)
+ mgError("AddItem with non cOsdItem");
+ Add(c);
+}
+
+//! \brief used for plain text items like in lyrics
+class mgText : public mgOsdItem
+{
+ public:
+ void Notify() {};
+ bool Enabled(mgActions on) { return true;}
+ const char *MenuName (const unsigned int idx,const mgListItem* item);
+};
+
+const char *
+mgText::MenuName (const unsigned int idx,const mgListItem* item) {
+ return strdup("mgtext::menuname");
+}
+
+void
+mgOsd::AddText(const char* text,bool selectable) {
+ mgText *item = new mgText;
+ item->mgAction::SetText(text,true);
+ AddItem(item);
+ item->SetSelectable(selectable);
+}
+
+void
+mgOsd::AddLongText(const char* text) {
+ char *s=strdup(text);
+ char *p=s;
+ unsigned int brk=57;
+ bool selectable = strlen(s);
+ while (strlen(p)>=brk) {
+ char save=p[brk];
+ p[brk]=0;
+ AddText(p,selectable);
+ p[brk]=save;
+ p+=brk;
+ }
+ if (strlen(p))
+ AddText(p,selectable);
+ free(s);
+}
+
+void
+mgOsd::AddFile(const string filename) {
+ ifstream infile(filename.c_str());
+ string line;
+ if(infile) {
+ while(getline(infile, line , '\n'))
+ AddLongText(line.c_str());
+ }
+ else {
+ char *s;
+ msprintf(&s,tr("File %s not found"),filename.c_str());
+ AddLongText(s);
+ free(s);
+ }
+}
+
+mgActions
+mgOsd::CurrentType() {
+ mgActions result = actNone;
+ cOsdItem* c = Get(Current());
+ if (c) {
+ mgAction *a = dynamic_cast<mgAction*>(c);
+ if (a)
+ result = a->Type();
+ }
+ return result;
+}
+
+mgActions
+mgMenu::ButtonAction(eKeys key) {
+ mgActions result = actNone;
+ switch (key) {
+ case kRed: result = RedAction; break;
+ case kGreen: result = GreenAction; break;
+ case kYellow: result = YellowAction; break;
+ case kBlue: result = BlueAction; break;
+ default: break;
+ }
+ return result;
+}
+
+eOSState
+mgMenu::ExecuteButton(eKeys key) {
+ return ExecuteAction(ButtonAction(key),osd()->CurrentType());
+}
+
+eOSState
+mgMenu::Process (eKeys key) {
+ return ExecuteButton(key);
+}
+
+const char*
+mgOsd::Message1(const char *msg, ...) {
+ if (strlen(msg)==0) return 0;
+ va_list ap;
+ va_start(ap, msg);
+ vmsprintf(&m_message, tr(msg), ap);
+ va_end(ap);
+ return m_message;
+}
+
+const char*
+mgOsd::Message1(const char *msg, const string &arg) {
+ if (strlen(msg)==0) return 0;
+ msprintf(&m_message, tr(msg), arg.c_str());
+ return m_message;
+}
+
+eOSState mgOsd::ProcessKey (eKeys key) {
+ eOSState result = osContinue;
+ if (Menus.size()<1)
+ mgError("mgOsd::ProcessKey: Menus is empty");
+ newmenu = Menus.back(); // Default: Stay in current menu
+ newposition = -1;
+
+ {
+ mgMenu * oldmenu = newmenu;
+
+ // item specific key logic:
+ result = cOsdMenu::ProcessKey (key);
+
+ // mgMenu specific key logic:
+ if (result == osUnknown)
+ result = oldmenu->Process (key);
+ }
+ // catch osBack for empty OSD lists . This should only happen for playlistitems
+ // (because if the list was empty, no mgActions::ProcessKey was ever called)
+ if (result == osBack) {
+ // do as if there was an entry
+ mgAction *a = Menus.back()->GenerateAction(actEntry,actEntry);
+ if (a) {
+ result = a->Back();
+ delete a;
+ }
+ }
+ switch (key) {
+ case kFastRew:
+ case kFastRew|k_Repeat:
+ case kFastFwd:
+ case kFastFwd|k_Repeat:
+ case kPlay:
+ case kPrev:
+ case kPrev|k_Repeat:
+ case kNext:
+ case kNext|k_Repeat:
+ // case kStop: does hide the player osd, but the player is still there in limbo
+ case kPause: {
+ mgPlayerControl *c = PlayerControl();
+ if (c) c->ProcessKey(key);
+ break;
+ }
+ default:
+ break;
+ }
+ // do nothing for unknown keys:
+ if (result == osUnknown)
+ goto pr_exit;
+
+ // change OSD menu as requested:
+ if (newmenu == NULL) {
+ if (Menus.size () > 1) {
+ CloseMenu();
+ forcerefresh = true;
+ }
+ else {
+ result = osBack; // game over
+ goto pr_exit;
+ }
+ }
+ else if (newmenu != Menus.back ())
+ AddMenu (newmenu,newposition);
+
+ forcerefresh |= (newposition>=0);
+
+ if (forcerefresh) {
+ forcerefresh = false;
+ Menus.back ()->Display ();
+ }
+ pr_exit:
+ showMessage();
+ return result;
+}
+
+void
+mgOsd::CloseMenu() {
+ mgMenu* m = Menus.back();
+ if (newposition==-1) newposition = m->getParentIndex();
+ Menus.pop_back ();
+ delete m;
+}
+
+void
+mgOsd::showMessage() {
+ if (m_message) {
+ showmessage(0,m_message);
+ free(m_message);
+ m_message = NULL;
+ }
+}
+
+void
+showmessage(int duration,const char * msg, ...) {
+ va_list ap;
+ va_start(ap,msg);
+ char buffer[200];
+ vsnprintf(buffer,199,tr(msg),ap);
+#if VDRVERSNUM >= 10307
+ if (!duration) duration=2;
+ Skins.Message (mtInfo, buffer,duration);
+ Skins.Flush ();
+#else
+ Interface->Status (buffer);
+ Interface->Flush ();
+#endif
+ va_end(ap);
+}
+
+void
+mgOsd::AddMenu (mgMenu * m,int position) {
+ Menus.push_back (m);
+ m->setosd (this);
+ m->setParentIndex(Current());
+ if (Get(Current()))
+ m->setParentName(Get(Current())->Text());
+ if (position<0) position=0;
+ newposition = position;
+ m->Display ();
+}
+
+void
+mgMenu::setosd(mgOsd *osd) {
+ m_osd = osd;
+}
+
+void
+mgOsd::DisplayGoto () {
+ if (newposition >= 0) {
+ if ((int)newposition>=Count())
+ newposition = Count() -1;
+ SetCurrent (Get (newposition));
+ RefreshCurrent ();
+ }
+ Display ();
+}
+
+void
+mgMenu::Display () {
+ BuildOsd ();
+ osd ()->DisplayGoto ();
+}
+
+mgItemGd *
+mgMenu::mutPlayingItem(void)
+{
+ if (!PlayerControl())
+ return 0;
+ else
+ return PlayerControl()->CurrentItem();
+}
+
+const mgItemGd *
+mgMenu::PlayingItem(void) const
+{
+ if (!PlayerControl())
+ return 0;
+ else
+ return PlayerControl()->CurrentItem();
+}
diff --git a/mg_menu.h b/mg_menu.h
new file mode 100644
index 0000000..b9c2be2
--- /dev/null
+++ b/mg_menu.h
@@ -0,0 +1,234 @@
+/*!
+ * \file vdr_menu.h
+ * \brief Implements menu handling for broswing media libraries within VDR
+ *
+ * \version $Revision: 1.13 $
+ * \date $Date: 2008-03-17 14:58:54 +0100 (Mo, 17 Mär 2008) $
+ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner, Wolfgang Rohdewald
+ * \author Responsible author: $Author: woro $
+ *
+ * $Id: vdr_menu.h 1051 2008-03-17 13:58:54Z woro $
+ */
+
+#ifndef _MG_MENU_H
+#define _MG_MENU_H
+
+#include <string>
+// #include <list>
+#include <vector>
+
+#include <vdr/osd.h>
+#include <vdr/plugin.h>
+#include <vdr/status.h>
+#include "vdr_actions.h"
+
+void showmessage(int duration, const char *msg, ...);
+
+class cCommands;
+
+class mgSelection;
+class mgMenu;
+class mgOsd;
+class mgIncrementalSearch;
+class mgPlayerControl;
+class mgItemGd;
+
+//! \brief callback class, monitors state changes in vdr
+class mgStatus : public cStatus
+{
+ private:
+ //! \brief the mgOsd that wants to be notified
+ mgOsd *main;
+ public:
+ //! \brief default constructor
+ mgStatus(mgOsd* m) { main = m;}
+ protected:
+ //! \brief the event we want to know about
+ virtual void OsdCurrentItem(const char *Text);
+};
+
+/*!
+ * \brief the muggle main OSD
+ */
+
+class mgOsd : public cOsdMenu
+{
+ protected:
+ bool m_save_warned;
+ mgMenu *m_root;
+ char *m_message;
+ void LoadExternalCommands();
+ void showMessage();
+ public:
+
+ mgActions CurrentType();
+
+ virtual void SetHelpKeys(const char *Red,const char *Green, const char *Yellow, const char *Blue) { SetHelp(Red,Green,Yellow,Blue); }
+
+ //! \brief callback object, lets vdr notify us about OSD changes
+ mgStatus *m_Status;
+
+ //! \brief the different menus, the last one is active
+ vector < mgMenu * >Menus;
+
+ //! \brief parent menu if any
+ mgMenu * Parent ();
+
+ //! \brief default constructor
+ mgOsd ();
+
+ //! \brief default destructor
+ virtual ~mgOsd ();
+
+ //! \brief save the entire muggle state
+ virtual void SaveState() =0 ;
+
+ //! \brief adds a new mgMenu to the stack
+ virtual void AddMenu (mgMenu * m,int position=0);
+
+ //! \brief
+
+ //! \brief initializes using values from nv
+ void InitMapFromSetup (mgValmap& nv);
+
+ //! \brief main entry point, called from vdr
+ virtual eOSState ProcessKey (eKeys Key);
+
+ /*! \brief selects a certain line on the OSD and displays the OSD
+ */
+ void DisplayGoto ();
+
+ //! \brief external commands
+ cCommands *external_commands;
+
+ //! \brief Actions can set newmenu which will then be displayed next
+ mgMenu *newmenu;
+
+ //! \brief Actions can set newstate which will then be returned to main vdr
+ eOSState newstate;
+
+ //! \brief Actions can set forcerefresh. This will force a redisplay of the OSD
+ bool forcerefresh;
+
+ //! \brief show a message. Can be called by actions. It will
+ // only be shown at the end of the next mgOsd::ProcessKey
+ // because that might do forcerefresh which overwrites the message
+ void Message (const char *msg) { m_message = strdup(msg); }
+ const char* Message1 (const char *msg, ...)
+ __attribute__ ((format (printf, 2, 3)));
+
+ const char* Message1 (const char *msg, const string &arg);
+
+ //! \brief Actions can request a new position. -1 means none wanted
+ int newposition;
+
+ //! \brief clears the screen, sets a title and the hotkey flag
+ void InitOsd (std::string title,const bool hashotkeys);
+
+#if VDRVERSNUM >= 10307
+ //! \brief expose the protected DisplayMenu() from cOsdMenu
+ cSkinDisplayMenu *DisplayMenu(void) { return cOsdMenu::DisplayMenu(); }
+#endif
+
+ //! \brief expose the protected cOsdMenu::hk()
+ const char *hk (const char *s) { return cOsdMenu::hk (s); }
+
+ void AddItem(mgAction *a);
+
+ void AddText(const char* text,bool selectable=true);
+
+ void AddLongText(const char* text);
+
+ void AddFile(const string filename);
+
+ void CloseMenu();
+
+ void RefreshTitle();
+
+ virtual void SetHotkeyAction(eKeys key,mgActions action);
+};
+
+//! \brief a generic muggle menu
+class mgMenu
+{
+ private:
+ const char *HKey(const mgActions act,mgActions on=mgActions(0));
+ protected:
+ mgOsd* m_osd;
+ unsigned int m_prevpos;
+ virtual mgActions ButtonAction(eKeys key);
+
+ eOSState ExecuteButton(eKeys key);
+ //! \brief adds the wanted action to the OSD menu
+ // \param hotkey if true, add this as a hotkey
+ void AddAction(const mgActions action, mgActions on = mgActions(0),const bool hotkey=true);
+
+ //! \brief add an external action, always with hotkey
+ void AddExternalAction(const mgActions action, const char *title);
+
+ //! \brief the name of the blue button depends of where we are
+ int m_parent_index;
+ string m_parent_name;
+ public:
+ //! sets the correct help keys.
+ void SetHelpKeys(mgActions on = mgActions(0));
+ //! \brief generates an object for the wanted action
+ mgAction* GenerateAction(const mgActions action,mgActions on);
+
+ //! \brief executes the wanted action
+ eOSState ExecuteAction (const mgActions action, const mgActions on);
+
+ //! \brief sets the pointer to the owning mgOsd
+ virtual void setosd (mgOsd* osd);
+
+ void setParentIndex(int idx) { m_parent_index = idx; }
+ int getParentIndex() { return m_parent_index; }
+ void setParentName(std::string name) { m_parent_name = name; }
+ string getParentName() { return m_parent_name; }
+
+ //! \brief the pointer to the owning mgOsd
+ mgOsd* osd () const { return m_osd; }
+
+ mgMenu ();
+
+ virtual ~ mgMenu () { }
+
+ //! \brief computes the title
+ virtual string Title() const = 0;
+
+ //! \brief clears the screen, sets a title and the hotkey flag
+ virtual void InitOsd (const bool hashotkeys=true);
+
+ //! \brief display OSD and go to osd()->newposition
+ void Display ();
+
+ //! \brief BuildOsd() should be abstract but then we cannot compile
+ virtual void BuildOsd () {
+ }
+
+ /*! \brief Process() should be abstract but then we cannot compile.
+ * \return Process may decide that we want another screen to be displayed.
+ * If the mgMenu* returned is not "this", the caller will use the return
+ * value for a new display. If NULL is returned, the caller will display
+ * the previous menu.
+ */
+ virtual eOSState Process (eKeys Key);
+
+ //! \brief the ID of the action defined by the red button.
+ mgActions RedAction;
+
+ //! \brief the ID of the action defined by the green button.
+ mgActions GreenAction;
+
+ //! \brief the action defined by the yellow button.
+ mgActions YellowAction;
+
+ //! \brief the action defined by the blue button.
+ mgActions BlueAction;
+
+ virtual void SetHotkeyAction(eKeys key,mgActions action);
+
+ const mgItemGd * PlayingItem(void ) const;
+ mgItemGd * mutPlayingItem(void );
+};
+#endif
diff --git a/mg_playcommands.c b/mg_playcommands.c
new file mode 100644
index 0000000..ce6bcdd
--- /dev/null
+++ b/mg_playcommands.c
@@ -0,0 +1,54 @@
+
+#include <string>
+#include <fstream>
+
+#include <vdr/interface.h>
+#include <vdr/menu.h>
+#include <vdr/plugin.h>
+#include <vdr/status.h>
+
+#include "mg_playcommands.h"
+#include "mg_skin.h"
+#include "mg_setup.h"
+
+#define X_DISPLAY_MISSING
+#define MAXLENGTH 256
+#define THEMESEXT "*.theme"
+#define VISEXT "*.visual"
+#define FINDCMD "cd '%s' && find '%s' -iname '%s' -printf '%%p\n' | sort -f"
+
+char urlname[256];
+
+extern char coverpicture[256];
+
+
+// ------------ mgPlayerCommands --------------------------------------------------------------------------------------------------- //
+
+mgPlayerCommands::mgPlayerCommands(void) {
+}
+
+mgPlayerCommands::~mgPlayerCommands() {
+}
+
+void
+mgPlayerCommands::BuildOsd() {
+ RedAction=actBack;
+ InitOsd(true);
+ AddAction(actToggleShuffle);
+ AddAction(actToggleLoop);
+ AddAction(actShowLyrics);
+}
+
+string
+mgPlayerCommands::Title() const
+{
+ return tr("Commands");
+}
+
+void mgPlayerCommands::LoadCommands() {
+ commands.Load(AddDirectory(the_setup.ConfigDirectory.c_str(), "data/musiccmds.dat"), true);
+}
+
+eOSState mgPlayerCommands::Execute(void) {
+ return osContinue;
+}
diff --git a/mg_playcommands.h b/mg_playcommands.h
new file mode 100644
index 0000000..2b9f2d0
--- /dev/null
+++ b/mg_playcommands.h
@@ -0,0 +1,37 @@
+#ifndef __COMMANDS_H
+#define __COMMANDS_H
+
+#include <string>
+#include <vdr/osdbase.h>
+
+#include "config.h"
+
+#include "vdr_menu.h"
+
+//class cMP3Player;
+class cFileSources;
+class cFileSource;
+class cFileObj;
+class cFileObjItem;
+
+class mgPlayOsd : public mgOsd
+{
+ public:
+ void SaveState() {}
+};
+
+class mgPlayerCommands : public mgMenu
+{
+ private:
+ cCommands commands;
+ eOSState Execute(void);
+ protected:
+ void BuildOsd();
+ string Title(void) const;
+ public:
+ mgPlayerCommands(void);
+ virtual ~mgPlayerCommands();
+ void LoadCommands();
+ bool DeleteList(void);
+};
+#endif // __COMMANDS_H
diff --git a/mg_sel_gd.c b/mg_sel_gd.c
index cd19591..82a9f4b 100644
--- a/mg_sel_gd.c
+++ b/mg_sel_gd.c
@@ -15,19 +15,16 @@
#include <stdio.h>
#include <assert.h>
-
#include "mg_sel_gd.h"
# include "mg_db.h"
-#include "i18n.h"
+#include <vdr/i18n.h>
-mgSelectionGd::mgSelectionGd(const mgSelection *s)
-{
+mgSelectionGd::mgSelectionGd(const mgSelection *s) {
InitFrom(s);
}
mgSelectionGd::mgSelectionGd(const bool fall_through)
- : mgSelection(fall_through)
-{
+: mgSelection(fall_through) {
}
void mgSelectionGd::InitSelection() {
@@ -35,25 +32,20 @@ void mgSelectionGd::InitSelection() {
m_db = GenerateDB();
}
-
-static bool iskeyGdGenre(mgKeyTypes kt)
-{
+static bool iskeyGdGenre(mgKeyTypes kt) {
return kt>=keyGdGenre1 && kt <= keyGdGenres;
}
bool
mgSelectionGd::DeduceKeyValue(mgKeyTypes new_kt,const mgSelection *s,
- vector<mgListItem>& items)
-{
- if (!s)
+vector<mgListItem>& items) {
+ if (!s)
return false;
- for (unsigned int i=0;i<s->ordersize();i++)
- {
+ for (unsigned int i=0;i<s->ordersize();i++) {
mgKeyTypes old_kt = s->getKeyType(i);
if (old_kt>new_kt
- && iskeyGdGenre(old_kt)
- && iskeyGdGenre(new_kt))
- {
+ && iskeyGdGenre(old_kt)
+ && iskeyGdGenre(new_kt)) {
string selid=KeyMaps.id(new_kt,
KeyMaps.value(new_kt,s->getKeyItem(i)->id()));
items.push_back(mgListItem(
@@ -65,22 +57,19 @@ mgSelectionGd::DeduceKeyValue(mgKeyTypes new_kt,const mgSelection *s,
}
void
-mgSelectionGd::clean()
-{
+mgSelectionGd::clean() {
mgSelection::clean();
keyvector::iterator i;
keyvector::iterator j;
- bool collection_found = false;
- bool collitem_found = false;
- bool album_found = false;
- bool tracknb_found = false;
- bool title_found = false;
+ bool collection_found = false;
+ bool collitem_found = false;
+ bool album_found = false;
+ bool tracknb_found = false;
+ bool title_found = false;
bool is_sufficiently_unique = false;
- for (i = Keys.begin () ; i != Keys.end (); ++i)
- {
+ for (i = Keys.begin () ; i != Keys.end (); ++i) {
mgKey* k = *i;
- if (k->Type()==keyGdUnique)
- {
+ if (k->Type()==keyGdUnique) {
truncate(i-Keys.begin());
break;
}
@@ -91,92 +80,77 @@ mgSelectionGd::clean()
title_found |= (k->Type()==keyGdTitle);
is_sufficiently_unique = tracknb_found || (album_found && title_found)
|| (collection_found && collitem_found);
- if (is_sufficiently_unique)
- {
+ if (is_sufficiently_unique) {
truncate (i+1-Keys.begin());
break;
}
- if (k->Type()==keyGdYear)
- {
+ if (k->Type()==keyGdYear) {
for (j = i+1 ; j != Keys.end(); ++j)
- if ((*j)->Type() == keyGdDecade)
- {
- delete *j;
- Keys.erase(j);
- break;
- }
+ if ((*j)->Type() == keyGdDecade) {
+ delete *j;
+ Keys.erase(j);
+ break;
+ }
}
}
- if (!is_sufficiently_unique)
- {
+ if (!is_sufficiently_unique) {
if (!album_found)
Keys.push_back(ktGenerate(keyGdAlbum));
if (!title_found)
Keys.push_back(ktGenerate(keyGdTitle));
}
- Keys.push_back(ktGenerate(keyGdUnique)); // make sure we ALWAYS have a unique key
+ // make sure we ALWAYS have a unique key
+ Keys.push_back(ktGenerate(keyGdUnique));
}
-
vector <const char *>
mgSelectionGd::Choices(unsigned int level, unsigned int *current) const
{
vector<const char*> result;
- if (level>ordersize())
- {
+ if (level>ordersize()) {
*current = 0;
return result;
}
- for (unsigned int ki=(unsigned int)(ktLow());ki<=(unsigned int)(ktHigh());ki++)
- {
+ for (unsigned int ki=(unsigned int)(ktLow());ki<=(unsigned int)(ktHigh());ki++) {
mgKeyTypes kt = mgKeyTypes(ki);
if (kt == keyGdUnique)
continue;
- if (kt==getKeyType(level))
- {
+ if (kt==getKeyType(level)) {
*current = result.size();
result.push_back(ktName(kt));
continue;
}
if (UsedBefore(kt,level))
continue;
- if (kt==keyGdDecade)
- {
+ if (kt==keyGdDecade) {
if (UsedBefore(keyGdYear,level)) continue;
}
- if (kt==keyGdGenre1)
- {
+ if (kt==keyGdGenre1) {
if (UsedBefore(keyGdGenre2,level)) continue;
if (UsedBefore(keyGdGenre3,level)) continue;
if (UsedBefore(keyGdGenres,level)) continue;
}
- if (kt==keyGdGenre2)
- {
+ if (kt==keyGdGenre2) {
if (UsedBefore(keyGdGenre3,level)) continue;
if (UsedBefore(keyGdGenres,level)) continue;
}
- if (kt==keyGdGenre3)
- {
+ if (kt==keyGdGenre3) {
if (UsedBefore(keyGdGenres,level)) continue;
}
- if (kt==keyGdFolder1)
- {
- if (UsedBefore(keyGdFolder2,level)) continue;
- if (UsedBefore(keyGdFolder3,level)) continue;
- if (UsedBefore(keyGdFolder4,level)) continue;
+ if (kt==keyGdFolder1) {
+ if (UsedBefore(keyGdFolder2,level)) continue;
+ if (UsedBefore(keyGdFolder3,level)) continue;
+ if (UsedBefore(keyGdFolder4,level)) continue;
}
- if (kt==keyGdFolder2)
- {
- if (UsedBefore(keyGdFolder3,level)) continue;
- if (UsedBefore(keyGdFolder4,level)) continue;
+ if (kt==keyGdFolder2) {
+ if (UsedBefore(keyGdFolder3,level)) continue;
+ if (UsedBefore(keyGdFolder4,level)) continue;
}
- if (kt==keyGdFolder3)
- {
- if (UsedBefore(keyGdFolder4,level)) continue;
+ if (kt==keyGdFolder3) {
+ if (UsedBefore(keyGdFolder4,level)) continue;
}
- if (kt==keyGdCollectionItem)
- {
- if (!UsedBefore(keyGdCollection,level)) continue;
+ if (kt==keyGdCollectionItem) {
+ if (!UsedBefore(keyGdCollection,level)) continue;
}
if (kt==keyGdCollection)
result.push_back(ktName(kt));
@@ -192,11 +166,9 @@ mgSelectionGd::NeedKey(unsigned int i) const
assert(m_level<ordersize());
mgKey *k = Keys[i];
mgKeyTypes kt = k->Type();
- for (unsigned int j=i+1;j<=m_level;j++)
- {
+ for (unsigned int j=i+1;j<=m_level;j++) {
mgKey *kn = Keys[j];
- if (kn && !kn->id().empty())
- {
+ if (kn && !kn->id().empty()) {
mgKeyTypes knt = kn->Type();
if (knt==keyGdUnique)
return false;
@@ -212,17 +184,16 @@ mgSelectionGd::NeedKey(unsigned int i) const
mgParts
mgSelectionGd::SelParts(bool distinct, bool deepsort) const
{
- if (distinct && !deepsort && m_level==0 && isCollectionOrder())
- {
+ if (distinct && !deepsort && m_level==0 && isCollectionOrder()) {
mgParts result;
result.orderByCount = m_orderByCount;
// sql command contributed by jarny
result.special_statement = string("SELECT COUNT(*) * "
- "CASE WHEN playlistitem.playlist IS NULL THEN 0 ELSE 1 END, "
- "1,playlist.id,playlist.title "
- " FROM playlist "
- "LEFT JOIN playlistitem ON playlist.id = playlistitem.playlist "
- "GROUP BY playlist.title,playlistitem.playlist,playlist.id");
+ "CASE WHEN playlistitem.playlist IS NULL THEN 0 ELSE 1 END, "
+ "1,playlist.id,playlist.title "
+ " FROM playlist "
+ "LEFT JOIN playlistitem ON playlist.id = playlistitem.playlist "
+ "GROUP BY playlist.title,playlistitem.playlist,playlist.id");
return result;
}
else
@@ -245,39 +216,37 @@ const char * const
mgSelectionGd::ktName(const mgKeyTypes kt) const
{
const char * result = "";
- switch (kt)
- {
- case keyGdGenres: result = "Genre";break;
- case keyGdGenre1: result = "Genre1";break;
- case keyGdGenre2: result = "Genre2";break;
- case keyGdGenre3: result = "Genre3";break;
- case keyGdFolder1: result = "Folder1";break;
- case keyGdFolder2: result = "Folder2";break;
- case keyGdFolder3: result = "Folder3";break;
- case keyGdFolder4: result = "Folder4";break;
- case keyGdArtist: result = "Artist";break;
- case keyGdArtistABC: result = "ArtistABC";break;
- case keyGdTitle: result = "Title";break;
- case keyGdTitleABC: result = "TitleABC";break;
- case keyGdTrack: result = "Track";break;
- case keyGdDecade: result = "Decade";break;
- case keyGdAlbum: result = "Album";break;
- case keyGdCreated: result = "Created";break;
- case keyGdModified: result = "Modified";break;
- case keyGdCollection: result = "Collection";break;
- case keyGdCollectionItem: result = "Collection item";break;
- case keyGdLanguage: result = "Language";break;
- case keyGdRating: result = "Rating";break;
- case keyGdYear: result = "Year";break;
- case keyGdUnique: result = "ID";break;
- default: result="not implemented";break;
+ switch (kt) {
+ case keyGdGenres: result = tr("Genre");break;
+ case keyGdGenre1: result = tr("Genre1");break;
+ case keyGdGenre2: result = tr("Genre2");break;
+ case keyGdGenre3: result = tr("Genre3");break;
+ case keyGdFolder1: result = tr("Folder1");break;
+ case keyGdFolder2: result = tr("Folder2");break;
+ case keyGdFolder3: result = tr("Folder3");break;
+ case keyGdFolder4: result = tr("Folder4");break;
+ case keyGdArtist: result = tr("Artist");break;
+ case keyGdArtistABC: result = tr("ArtistABC");break;
+ case keyGdTitle: result = tr("Title");break;
+ case keyGdTitleABC: result = tr("TitleABC");break;
+ case keyGdTrack: result = tr("Track");break;
+ case keyGdDecade: result = tr("Decade");break;
+ case keyGdAlbum: result = tr("Album");break;
+ case keyGdCreated: result = tr("Created");break;
+ case keyGdModified: result = tr("Modified");break;
+ case keyGdCollection: result = tr("Collection");break;
+ case keyGdCollectionItem: result = tr("Collection item");break;
+ case keyGdLanguage: result = tr("Language");break;
+ case keyGdRating: result = tr("Rating");break;
+ case keyGdYear: result = tr("Year");break;
+ case keyGdUnique: result = tr("ID");break;
+ default: result=tr("not implemented");break;
}
- return tr(result);
+ return result;
}
void
-mgSelectionGd::MakeCollection()
-{
+mgSelectionGd::MakeCollection() {
clear();
setKey(keyGdCollection);
setKey(keyGdCollectionItem);
@@ -295,38 +264,35 @@ mgSelectionGd::isCollectionOrder() const
bool
mgSelectionGd::isLanguagelist() const
{
- return (getKeyType(0) == keyGdLanguage);
+ return (getKeyType(0) == keyGdLanguage);
}
bool
mgSelectionGd::isCollectionlist () const
{
- if (ordersize()==0) return false;
- return (getKeyType(0) == keyGdCollection && orderlevel() == 0);
+ if (ordersize()==0) return false;
+ return (getKeyType(0) == keyGdCollection && orderlevel() == 0);
}
bool
mgSelectionGd::inCollection(const string Name) const
{
- bool result = false;
- for (unsigned int idx = 0 ; idx <= orderlevel(); idx++)
- {
- if (idx==ordersize()) break;
- if (getKeyType(idx) == keyGdCollection)
- if (!Name.empty() && getKeyItem(idx)->value() != Name)
- break;
- if (getKeyType(idx) == keyGdCollectionItem)
- {
- result = true;
- break;
- }
- }
- return result;
+ bool result = false;
+ for (unsigned int idx = 0 ; idx <= orderlevel(); idx++) {
+ if (idx==ordersize()) break;
+ if (getKeyType(idx) == keyGdCollection)
+ if (!Name.empty() && getKeyItem(idx)->value() != Name)
+ break;
+ if (getKeyType(idx) == keyGdCollectionItem) {
+ result = true;
+ break;
+ }
+ }
+ return result;
}
bool
-mgSelectionGd::InitDefaultOrder(unsigned int i)
-{
+mgSelectionGd::InitDefaultOrder(unsigned int i) {
clear();
switch (i) {
case 1:
diff --git a/mg_sel_gd.h b/mg_sel_gd.h
index f625b1f..7cf9fc0 100644
--- a/mg_sel_gd.h
+++ b/mg_sel_gd.h
@@ -18,31 +18,29 @@ using namespace std;
class mgSelectionGd : public mgSelection
{
- public:
- mgSelectionGd(const mgSelection *s);
- mgSelectionGd(const bool fall_through = false);
- void MakeCollection();
- vector <const char*> Choices(unsigned int level, unsigned int *current) const;
- bool NeedKey(unsigned int i) const;
- mgParts SelParts(bool distinct, bool deepsort) const;
- bool inCollection(const string Name="") const;
- bool isLanguagelist() const;
- bool isCollectionlist() const;
- bool InitDefaultOrder(unsigned int i=0);
- bool keyIsUnique(mgKeyTypes kt) const { return kt==keyGdUnique;}
-
-
- protected:
- bool DeduceKeyValue(mgKeyTypes new_kt,const mgSelection *s,
- vector<mgListItem>& items);
- void InitSelection ();
- const char * const ktName(const mgKeyTypes kt) const;
- mgKeyTypes ktLow() const;
- mgKeyTypes ktHigh() const;
- bool isCollectionOrder() const;
-
- private:
- void clean();
+ public:
+ mgSelectionGd(const mgSelection *s);
+ mgSelectionGd(const bool fall_through = false);
+ void MakeCollection();
+ vector <const char*> Choices(unsigned int level, unsigned int *current) const;
+ bool NeedKey(unsigned int i) const;
+ mgParts SelParts(bool distinct, bool deepsort) const;
+ bool inCollection(const string Name="") const;
+ bool isLanguagelist() const;
+ bool isCollectionlist() const;
+ bool InitDefaultOrder(unsigned int i=0);
+ bool keyIsUnique(mgKeyTypes kt) const { return kt==keyGdUnique;}
+
+ protected:
+ bool DeduceKeyValue(mgKeyTypes new_kt,const mgSelection *s,
+ vector<mgListItem>& items);
+ void InitSelection ();
+ const char * const ktName(const mgKeyTypes kt) const;
+ mgKeyTypes ktLow() const;
+ mgKeyTypes ktHigh() const;
+ bool isCollectionOrder() const;
+
+ private:
+ void clean();
};
-
-#endif
+#endif
diff --git a/mg_selection.c b/mg_selection.c
index 935a73a..ad8a123 100644
--- a/mg_selection.c
+++ b/mg_selection.c
@@ -21,51 +21,45 @@
#include "mg_tools.h"
#include "mg_thread_sync.h"
+#include "mg_item_gd.h"
+
#if VDRVERSNUM >= 10307
#include <vdr/interface.h>
#include <vdr/skins.h>
#endif
-
/*! \brief returns a random integer within some range
*/
unsigned int
-randrange (const unsigned int high)
-{
- unsigned int result=0;
- result = random () % high;
- return result;
+randrange (const unsigned int high) {
+ unsigned int result=0;
+ result = random () % high;
+ return result;
}
-bool compvalue (const mgListItem* x, const mgListItem* y)
-{
+bool compvalue (const mgListItem* x, const mgListItem* y) {
return x->value()<y->value();
}
-bool compid (const mgListItem* x, const mgListItem* y)
-{
+bool compid (const mgListItem* x, const mgListItem* y) {
return x->id()<y->id();
}
-bool compidnum (const mgListItem* x, const mgListItem* y)
-{
+bool compidnum (const mgListItem* x, const mgListItem* y) {
return atol(x->id().c_str())<atol(y->id().c_str());
}
-bool compcount (const mgListItem* x, const mgListItem* y)
-{
+bool compcount (const mgListItem* x, const mgListItem* y) {
return x->count()>y->count();
}
-bool compitem (const mgItem* x, const mgItem* y)
-{
+bool compitem (const mgItem* x, const mgItem* y) {
const mgSelection *s = x->getSelection();
string xval="";
string yval="";
int xnum;
int ynum;
- for (unsigned int idx=s->orderlevel();idx<s->ordersize();idx++)
- {
+ for (unsigned int idx=s->orderlevel();idx<s->ordersize();idx++) {
mgSortBy sb = s->getKeySortBy(idx);
mgListItem *xitem = x->getKeyItem(s->getKeyType (idx));
mgListItem *yitem = y->getKeyItem(s->getKeyType (idx));
@@ -84,10 +78,12 @@ bool compitem (const mgItem* x, const mgItem* y)
if (xnum<ynum) {
xval="0";
yval="1";
- } else if (xnum>ynum) {
+ }
+ else if (xnum>ynum) {
xval="1";
yval="0";
- } else {
+ }
+ else {
xval="0";
yval="0";
}
@@ -103,33 +99,26 @@ bool compitem (const mgItem* x, const mgItem* y)
}
void
-mgSelection::mgListItems::sort(bool bycount,mgSortBy SortBy)
-{
- if (SortBy==mgSortNone)
- {
+mgSelection::mgListItems::sort(bool bycount,mgSortBy SortBy) {
+ if (SortBy==mgSortNone) {
return;
}
- if (bycount)
- {
+ if (bycount) {
std::sort(m_items.begin(),m_items.end(),compcount);
}
- else if (SortBy==mgSortById)
- {
+ else if (SortBy==mgSortById) {
std::sort(m_items.begin(),m_items.end(),compid);
}
- else if (SortBy==mgSortByIdNum)
- {
+ else if (SortBy==mgSortByIdNum) {
std::sort(m_items.begin(),m_items.end(),compidnum);
}
- else
- {
+ else {
std::sort(m_items.begin(),m_items.end(),compvalue);
}
}
void
-mgSelection::mgListItems::clear()
-{
+mgSelection::mgListItems::clear() {
for (unsigned int i=0;i<size();i++)
delete m_items[i];
m_items.clear();
@@ -146,10 +135,9 @@ mgSelection::mgListItems::operator==(const mgListItems&x) const
}
void
-mgSelection::mgListItems::refresh()
-{
+mgSelection::mgListItems::refresh() {
if (!m_sel)
- mgError("mgListItems: m_sel is 0");
+ mgError("mgListItems::refresh: m_sel is 0");
m_sel->refreshValues();
}
@@ -157,42 +145,39 @@ size_t
mgSelection::mgListItems::size() const
{
if (!m_sel)
- mgError("mgListItems: m_sel is 0");
+ mgError("mgListItems::size: m_sel is 0");
return m_items.size();
}
mgListItem*
-mgSelection::mgListItems::operator[](unsigned int idx)
-{
+mgSelection::mgListItems::operator[](unsigned int idx) {
if (!m_sel)
- mgError("mgListItems: m_sel is 0");
+ mgError("mgListItems::operator[] m_sel is 0");
m_sel->refreshValues();
assert(idx<size());
return m_items[idx];
}
void
-mgSelection::mgListItems::setOwner(mgSelection* sel)
-{
+mgSelection::mgListItems::setOwner(mgSelection* sel) {
m_sel = sel;
}
int
mgSelection::mgListItems::search (const string v) const
{
- if (!m_sel)
- mgError("mgListItems::index(%s): m_sel is 0",v.c_str());
- unsigned int itemsize = size();
- const char *cstr = v.c_str();
- unsigned int clen = strlen(cstr);
- int result = -1;
- for (unsigned int idx = 0 ; idx < itemsize; idx++)
- if( strncasecmp( m_items[idx]->value().c_str(), cstr, clen )>=0)
- {
+ if (!m_sel)
+ mgError("mgListItems::index(%s): m_sel is 0",v.c_str());
+ unsigned int itemsize = size();
+ const char *cstr = v.c_str();
+ unsigned int clen = strlen(cstr);
+ int result = -1;
+ for (unsigned int idx = 0 ; idx < itemsize; idx++)
+ if( strncasecmp( m_items[idx]->value().c_str(), cstr, clen )>=0) {
result = idx;
break;
}
- return result;
+ return result;
}
unsigned int
@@ -210,39 +195,35 @@ mgSelection::mgListItems::idindex (const string i) const
unsigned int
mgSelection::mgListItems::index (const string s,bool val,bool second_try) const
{
- if (!m_sel)
- mgError("mgListItems::index(%s): m_sel is 0",s.c_str());
- m_sel->refreshValues();
- for (unsigned int i = 0; i < size (); i++)
- {
- if (val)
- {
- if (m_items[i]->value() == s)
- return i;
+ if (!m_sel)
+ mgError("mgListItems::index(%s): m_sel is 0",s.c_str());
+ m_sel->refreshValues();
+ for (unsigned int i = 0; i < size (); i++) {
+ if (val) {
+ if (m_items[i]->value() == s)
+ return i;
+ }
+ else {
+ if (m_items[i]->id() == s)
+ return i;
+ }
}
- else
- {
- if (m_items[i]->id() == s)
- return i;
+ // nochmal mit neuen Werten:
+ if (second_try) {
+ mgDebug(5,"index: Gibt es nicht:%s",s.c_str());
+ mgDebug(5,"index: wir haben z.B.");
+ if (size()>0) mgDebug(5,"%s/%s",m_items[0]->value().c_str(),m_items[0]->id().c_str());
+ if (size()>1) mgDebug(5,"%s/%s",m_items[1]->value().c_str(),m_items[1]->id().c_str());
+ if (size()>2) mgDebug(5,"%s/%s",m_items[2]->value().c_str(),m_items[2]->id().c_str());
+ if (size()>3) mgDebug(5,"%s/%s",m_items[3]->value().c_str(),m_items[3]->id().c_str());
+ if (size()>4) mgDebug(5,"%s/%s",m_items[4]->value().c_str(),m_items[4]->id().c_str());
+ if (size()>5) mgDebug(5,"%s/%s",m_items[5]->value().c_str(),m_items[5]->id().c_str());
+ return 0;
+ }
+ else {
+ m_sel->clearCache();
+ return index(s,val,true);
}
- }
- // nochmal mit neuen Werten:
- if (second_try) {
- mgDebug(5,"index: Gibt es nicht:%s",s.c_str());
- mgDebug(5,"index: wir haben z.B.");
- if (size()>0) mgDebug(5,"%s/%s",m_items[0]->value().c_str(),m_items[0]->id().c_str());
- if (size()>1) mgDebug(5,"%s/%s",m_items[1]->value().c_str(),m_items[1]->id().c_str());
- if (size()>2) mgDebug(5,"%s/%s",m_items[2]->value().c_str(),m_items[2]->id().c_str());
- if (size()>3) mgDebug(5,"%s/%s",m_items[3]->value().c_str(),m_items[3]->id().c_str());
- if (size()>4) mgDebug(5,"%s/%s",m_items[4]->value().c_str(),m_items[4]->id().c_str());
- if (size()>5) mgDebug(5,"%s/%s",m_items[5]->value().c_str(),m_items[5]->id().c_str());
- return 0;
- }
- else
- {
- m_sel->clearCache();
- return index(s,val,true);
- }
}
bool
@@ -258,21 +239,19 @@ mgSelection::inItems() const
}
void
-mgSelection::setOrderByCount(bool orderbycount)
-{
+mgSelection::setOrderByCount(bool orderbycount) {
m_orderByCount = orderbycount;
}
void
mgSelection::clearCache() const
{
- m_current_values = "";
- m_current_tracks = "";
+ m_current_values = "";
+ m_current_tracks = "";
}
string
-mgSelection::getCurrentValue()
-{
+mgSelection::getCurrentValue() {
gotoPosition();
return getValue(m_position);
}
@@ -290,202 +269,177 @@ mgSelection::getValue(unsigned int idx) const
mgListItem*
mgSelection::getKeyItem(const unsigned int level) const
{
- assert(level<Keys.size());
- return Keys[level]->get();
+ assert(level<Keys.size());
+ return Keys[level]->get();
}
-
mgKeyTypes
mgSelection::getKeyType (const unsigned int level) const
{
- assert(level<Keys.size());
- return Keys[level]->Type();
+ assert(level<Keys.size());
+ return Keys[level]->Type();
}
mgSortBy
mgSelection::getKeySortBy (const unsigned int level) const
{
- assert(level<Keys.size());
- return Keys[level]->SortBy();
+ assert(level<Keys.size());
+ return Keys[level]->SortBy();
}
mgItem *
-mgSelection::getItem (unsigned int position)
-{
- if (position >= items().size())
- return 0;
- return m_items[position];
+mgSelection::getItem (unsigned int position) {
+ if (position >= items().size())
+ return 0;
+ return m_items[position];
}
-
-mgSelection::ShuffleMode mgSelection::toggleShuffleMode ()
-{
- setShuffleMode((m_shuffle_mode == SM_PARTY) ? SM_NONE : ShuffleMode (m_shuffle_mode + 1));
- Shuffle();
- return getShuffleMode();
+mgSelection::ShuffleMode mgSelection::toggleShuffleMode () {
+ setShuffleMode((m_shuffle_mode >= SM_NORMAL) ? SM_NONE : ShuffleMode (m_shuffle_mode + 1));
+ Shuffle();
+ return getShuffleMode();
}
void
-mgSelection::setShuffleMode (mgSelection::ShuffleMode mode)
-{
- m_shuffle_mode = mode;
+mgSelection::setShuffleMode (mgSelection::ShuffleMode mode) {
+ m_shuffle_mode = mode;
}
void
mgSelection::Shuffle() const
{
- unsigned int numitems = items().size();
- if (numitems==0) return;
- switch (m_shuffle_mode)
- {
- case SM_NONE:
- {
- long id = m_items[getItemPosition()]->getItemid ();
- m_current_tracks = ""; // force a reload
- numitems = items().size(); // also reloads
- for (unsigned int i = 0; i < numitems; i++)
- if (m_items[i]->getItemid () == id)
- {
- m_items_position = i;
- break;
- }
- }
- break;
- case SM_PARTY:
- case SM_NORMAL:
- {
- // play all, beginning with current item:
- mgItem* tmp = m_items[getItemPosition()];
- m_items[getItemPosition()]=m_items[0];
- m_items[0]=tmp;
- m_items_position = 0;
- // randomize all other items
- for (unsigned int i = 1; i < numitems; i++)
- {
- unsigned int j = 1+randrange (numitems-1);
- tmp = m_items[i];
- m_items[i] = m_items[j];
- m_items[j] = tmp;
- }
- } break;
-/*
-
- - Party mode (see iTunes)
- - initialization
- - find 15 titles according to the scheme below
- - playing
- - before entering next title perform item selection
- - item selection
- - generate a random uid
- - if file exists:
- - determine maximum playcount of all items
-- generate a random number n
-- if n < playcount / max. playcount
-- add the file to the end of the list
-*/
- }
-}
-
-
-mgSelection::LoopMode mgSelection::toggleLoopMode ()
-{
- m_loop_mode = (m_loop_mode == LM_FULL) ? LM_NONE : LoopMode (m_loop_mode + 1);
- return m_loop_mode;
+ unsigned int numitems = items().size();
+ if (numitems==0) return;
+ switch (m_shuffle_mode) {
+ case SM_NONE:
+ {
+ long id = m_items[getItemPosition()]->getItemid ();
+ // force a reload
+ m_current_tracks = "";
+ // also reloads
+ numitems = items().size();
+ for (unsigned int i = 0; i < numitems; i++)
+ if (m_items[i]->getItemid () == id) {
+ m_items_position = i;
+ break;
+ }
+ }
+ break;
+ case SM_PARTY:
+ case SM_NORMAL:
+ {
+ // play all, beginning with current item:
+ mgItem* tmp = m_items[getItemPosition()];
+ m_items[getItemPosition()]=m_items[0];
+ m_items[0]=tmp;
+ m_items_position = 0;
+ // randomize all other items
+ for (unsigned int i = 1; i < numitems; i++) {
+ unsigned int j = 1+randrange (numitems-1);
+ tmp = m_items[i];
+ m_items[i] = m_items[j];
+ m_items[j] = tmp;
+ }
+ } break;
+ /*
+
+ - Party mode (see iTunes)
+ - initialization
+ - find 15 titles according to the scheme below
+ - playing
+ - before entering next title perform item selection
+ - item selection
+ - generate a random uid
+ - if file exists:
+ - determine maximum playcount of all items
+ - generate a random number n
+ - if n < playcount / max. playcount
+ - add the file to the end of the list
+ */
+ }
}
+mgSelection::LoopMode mgSelection::toggleLoopMode () {
+ m_loop_mode = (m_loop_mode == LM_FULL) ? LM_NONE : LoopMode (m_loop_mode + 1);
+ return m_loop_mode;
+}
unsigned int
-mgSelection::AddToCollection (const string Name)
-{
- int result = m_db->AddToCollection(Name,items(),0);
- if (result>0)
- if (inCollection(Name)) clearCache ();
- return result;
+mgSelection::AddToCollection (const string Name) {
+ int result = m_db->AddToCollection(Name,items(),0);
+ if (result>0)
+ if (inCollection(Name)) clearCache ();
+ return result;
}
-
unsigned int
-mgSelection::RemoveFromCollection (const string Name)
-{
- mgParts p = SelParts(false,false);
- unsigned int result = m_db->RemoveFromCollection(Name,items(),&p);
- if (result>0)
- if (inCollection(Name)) clearCache ();
- return result;
+mgSelection::RemoveFromCollection (const string Name) {
+ mgParts p = SelParts(false,false);
+ unsigned int result = m_db->RemoveFromCollection(Name,items(),&p);
+ if (result>0)
+ if (inCollection(Name)) clearCache ();
+ return result;
}
-
-bool mgSelection::DeleteCollection (const string Name)
-{
- bool result = m_db->DeleteCollection (Name);
- if (result)
- if (isCollectionlist()) clearCache ();
- return result;
+bool mgSelection::DeleteCollection (const string Name) {
+ bool result = m_db->DeleteCollection (Name);
+ if (result)
+ if (isCollectionlist()) clearCache ();
+ return result;
}
-
-void mgSelection::ClearCollection (const string Name)
-{
- m_db->ClearCollection (Name);
- if (inCollection(Name)) clearCache ();
+void mgSelection::ClearCollection (const string Name) {
+ m_db->ClearCollection (Name);
+ if (inCollection(Name)) clearCache ();
}
-
-bool mgSelection::CreateCollection(const string Name)
-{
- bool result = m_db->CreateCollection (Name);
- if (result)
- if (isCollectionlist()) clearCache ();
- return result;
+bool mgSelection::CreateCollection(const string Name) {
+ bool result = m_db->CreateCollection (Name);
+ if (result)
+ if (isCollectionlist()) clearCache ();
+ return result;
}
-
-string mgSelection::exportM3U ()
-{
- enter();
-// open a file for writing
- string fn = "/tmp/" + ListFilename () + ".m3u";
- FILE * listfile = fopen (fn.c_str (), "w");
- if (!listfile)
- return "";
- fprintf (listfile, "#EXTM3U\n");
- unsigned int numitems = items().size();
- for (unsigned i = 0; i < numitems; i++)
- {
- mgItem* t = m_items[i];
- fprintf (listfile, "#EXTINF:%d,%s\n", t->getDuration (),
- t->getTitle ().c_str ());
- fprintf (listfile, "#MUGGLE:%ld\n", t->getItemid());
- fprintf (listfile, "%s\n", t->getSourceFile (false).c_str ());
- }
- fclose (listfile);
- leave();
- return fn;
+string mgSelection::exportM3U () {
+ enter();
+ // open a file for writing
+ string fn = "/tmp/" + ListFilename () + ".m3u";
+ FILE * listfile = fopen (fn.c_str (), "w");
+ if (!listfile)
+ return "";
+ fprintf (listfile, "#EXTM3U\n");
+ unsigned int numitems = items().size();
+ for (unsigned i = 0; i < numitems; i++) {
+ mgItem* t = m_items[i];
+ fprintf (listfile, "#EXTINF:%d,%s\n", t->getDuration (),
+ t->getTitle ().c_str ());
+ fprintf (listfile, "#MUGGLE:%ld\n", t->getItemid());
+ fprintf (listfile, "%s\n", t->getSourceFile (false).c_str ());
+ }
+ fclose (listfile);
+ leave();
+ return fn;
}
bool
-mgSelection::empty()
-{
- listitems.refresh();
- return ( listitems.size () == 0);
+mgSelection::empty() {
+ listitems.refresh();
+ return ( listitems.size () == 0);
}
void
-mgSelection::setPosition (unsigned int position)
-{
- assert(m_level<ordersize());
- m_position = position;
+mgSelection::setPosition (unsigned int position) {
+ assert(m_level<ordersize());
+ m_position = position;
}
void
-mgSelection::setPosition(string value)
-{
+mgSelection::setPosition(string value) {
setPosition (listitems.valindex (value));
}
unsigned int
-mgSelection::searchPosition(string search)
-{
+mgSelection::searchPosition(string search) {
int res = listitems.search (search);
if (res>=0)
setPosition (res);
@@ -495,163 +449,151 @@ mgSelection::searchPosition(string search)
void
mgSelection::GotoItemPosition (unsigned int position) const
{
- m_items_position = position;
- skipItems(0);
+ m_items_position = position;
+ skipItems(0);
}
unsigned int
mgSelection::getPosition () const
{
- assert(m_level<ordersize());
- return m_position;
+ assert(m_level<ordersize());
+ return m_position;
}
unsigned int
-mgSelection::gotoPosition ()
-{
- assert(m_level<ordersize());
- listitems.refresh();
- unsigned int itemsize = listitems.size();
- if (itemsize==0)
- m_position = 0;
- else if (m_position >= itemsize)
- m_position = itemsize -1;
- if (itemsize==0)
- Key(m_level)->set (0);
- else
- Key(m_level)->set (listitems[m_position]);
- return m_position;
+mgSelection::gotoPosition () {
+ assert(m_level<ordersize());
+ listitems.refresh();
+ unsigned int itemsize = listitems.size();
+ if (itemsize==0)
+ m_position = 0;
+ else if (m_position >= itemsize)
+ m_position = itemsize -1;
+ if (itemsize==0)
+ Key(m_level)->set (0);
+ else
+ Key(m_level)->set (listitems[m_position]);
+ return m_position;
}
-
unsigned int
mgSelection::getItemPosition() const
{
- if (m_items_position>=m_items.size())
- if (m_items.size()==0)
- m_items_position=0;
+ if (m_items_position>=m_items.size())
+ if (m_items.size()==0)
+ m_items_position=0;
else
m_items_position = m_items.size()-1;
- return m_items_position;
+ return m_items_position;
}
unsigned int
-mgSelection::gotoItemPosition()
-{
- unsigned int numitems = items().size();
- if (numitems == 0)
- {
- m_items_position = 0;
- return 0;
- }
- if (m_items_position >= numitems)
- m_items_position = numitems -1;
- return m_items_position;
+mgSelection::gotoItemPosition() {
+ unsigned int numitems = items().size();
+ if (numitems == 0) {
+ m_items_position = 0;
+ return 0;
+ }
+ if (m_items_position >= numitems)
+ m_items_position = numitems -1;
+ return m_items_position;
}
bool mgSelection::skipItems (int steps) const
{
- unsigned int numitems = items().size();
- if (numitems == 0)
- {
- m_items_position=0;
- return false;
- }
- int old_pos = m_items_position;
- unsigned int new_pos;
- if (m_loop_mode == LM_SINGLE)
- steps = 0;
- if (old_pos + steps < 0)
- {
- if (m_loop_mode == LM_NONE)
- return false;
- new_pos = numitems - 1;
- }
- else
- new_pos = old_pos + steps;
- if (new_pos >= numitems)
- {
- if (m_loop_mode == LM_NONE)
- return false;
- new_pos = 0;
- }
- m_items_position = new_pos;
- while (true)
- {
- if (m_items[m_items_position]->Valid())
- break;
- delete m_items[m_items_position];
- m_items.erase(m_items.begin()+m_items_position);
- if (m_items.size()==0)
- {
- m_items_position = 0;
+ unsigned int numitems = items().size();
+ if (numitems == 0) {
+ m_items_position=0;
return false;
}
- if (steps<0 && m_items.size()>0 && m_items_position>0)
- m_items_position--;
- if (m_items_position==m_items.size())
- m_items_position--;
- }
- return true;
+ int old_pos = m_items_position;
+ unsigned int new_pos;
+ if (m_loop_mode == LM_SINGLE)
+ steps = 0;
+ if (old_pos + steps < 0) {
+ if (m_loop_mode == LM_NONE)
+ return false;
+ new_pos = numitems - 1;
+ }
+ else
+ new_pos = old_pos + steps;
+ if (new_pos >= numitems) {
+ if (m_loop_mode == LM_NONE)
+ return false;
+ new_pos = 0;
+ }
+ m_items_position = new_pos;
+ while (true) {
+ if (m_items[m_items_position]->Valid())
+ break;
+ delete m_items[m_items_position];
+ m_items.erase(m_items.begin()+m_items_position);
+ if (m_items.size()==0) {
+ m_items_position = 0;
+ return false;
+ }
+ if (steps<0 && m_items.size()>0 && m_items_position>0)
+ m_items_position--;
+ if (m_items_position==m_items.size())
+ m_items_position--;
+ }
+ return true;
}
unsigned long
-mgSelection::getLength ()
-{
- unsigned long result = 0;
- unsigned int numitems = items().size();
- for (unsigned int i = 0; i < numitems; i++)
- result += m_items[i]->getDuration ();
- return result;
+mgSelection::getLength () {
+ unsigned long result = 0;
+ unsigned int numitems = items().size();
+ for (unsigned int i = 0; i < numitems; i++)
+ result += m_items[i]->getDuration ();
+ return result;
}
-
unsigned long
mgSelection::getCompletedLength () const
{
- unsigned long result = 0;
- items (); // make sure they are loaded
- for (unsigned int i = 0; i < getItemPosition(); i++)
- result += m_items[i]->getDuration ();
- return result;
+ unsigned long result = 0;
+ items (); // make sure they are loaded
+ for (unsigned int i = 0; i < getItemPosition(); i++)
+ result += m_items[i]->getDuration ();
+ return result;
}
-
-
string mgSelection::getListname () const
{
- list<string> st;
- for (unsigned int i = 0; i < m_level; i++)
- st.push_back(getKeyItem(i)->value());
- st.unique();
- string result="";
- for (list < string >::iterator it = st.begin (); it != st.end (); ++it)
- addsep (result, ":", *it);
- if (result.empty ())
- if (ordersize()>0)
- result = string(ktName(getKeyType(0)));
- return result;
+ list<string> st;
+ for (unsigned int i = 0; i < m_level; i++) {
+ string val=getKeyItem(i)->value();
+ if (Keys[i]->Type()==keyGdLanguage)
+ val=dgettext("iso_639",val.c_str());
+ st.push_back(val);
+ }
+ st.unique();
+ string result="";
+ for (list < string >::iterator it = st.begin (); it != st.end (); ++it)
+ addsep (result, ":", *it);
+ if (result.empty ())
+ if (ordersize()>0)
+ result = string(ktName(getKeyType(0)));
+ return result;
}
-string mgSelection::ListFilename ()
-{
- string res = getListname ();
- // convert char set ?
- string::iterator it;
- for (it=res.begin();it!=res.end();it++)
- {
- char& c = *it;
- switch (c)
- {
- case '\'':
- case '/':
- case '\\':
- case ' ':
- case ')':
- case '(': c = '_';break;
- }
- }
- return res;
+string mgSelection::ListFilename () {
+ string res = getListname ();
+ // convert char set ?
+ string::iterator it;
+ for (it=res.begin();it!=res.end();it++) {
+ char& c = *it;
+ switch (c) {
+ case '\'':
+ case '/':
+ case '\\':
+ case ' ':
+ case ')':
+ case '(': c = '_';break;
+ }
+ }
+ return res;
}
mgParts
@@ -659,29 +601,25 @@ mgSelection::SelParts(bool distinct, bool deepsort) const
{
assert(m_level<ordersize());
mgParts result;
- if (inItem())
- {
+ if (inItem()) {
// only use tracks.id
result += Keys[ordersize()-1]->Parts(m_db,distinct);
return result;
}
mgKey *high = Keys[m_level];
mgListItem* highitem = 0;
- if (high->Type()!=keyGdUnique)
- {
+ if (high->Type()!=keyGdUnique) {
highitem = high->get();
high->set(0);
}
result.orderByCount = m_orderByCount;
- for (unsigned int i=0;i<ordersize();i++)
- {
+ for (unsigned int i=0;i<ordersize();i++) {
if (!deepsort && i>m_level)
break;
if (NeedKey(i))
result += Keys[i]->Parts(m_db,distinct&&i==m_level);
}
- if (highitem)
- {
+ if (highitem) {
high->set(highitem);
delete highitem;
}
@@ -691,68 +629,58 @@ mgSelection::SelParts(bool distinct, bool deepsort) const
const vector < mgItem* > &
mgSelection::items () const
{
- if (m_current_tracks.empty())
- {
- mgParts p = SelParts(false,true);
- m_current_tracks = m_db->LoadItemsInto(p,m_items);
- if (m_shuffle_mode==SM_NONE)
- {
- if (!inCollection(""))
- {
- for (unsigned int i=0;i<m_items.size();i++)
- m_items[i]->setSelection(this);
- std::sort(m_items.begin(),m_items.end(),compitem);
+ if (m_current_tracks.empty()) {
+ mgParts p = SelParts(false,true);
+ m_current_tracks = m_db->LoadItemsInto(p,m_items);
+ if (m_shuffle_mode==SM_NONE) {
+ if (!inCollection("")) {
+ for (unsigned int i=0;i<m_items.size();i++)
+ m_items[i]->setSelection(this);
+ std::sort(m_items.begin(),m_items.end(),compitem);
+ }
}
+ else
+ Shuffle();
}
- else
- Shuffle();
- }
- return m_items;
+ return m_items;
}
-
void mgSelection::InitSelection() {
m_active = false;
- m_position = 0;
- m_items_position = 0;
- if (the_setup.InitShuffleMode)
- m_shuffle_mode = SM_NORMAL;
+ m_position = 0;
+ m_items_position = 0;
+ if (the_setup.InitShuffleMode)
+ m_shuffle_mode = SM_NORMAL;
else
- m_shuffle_mode = SM_NONE;
+ m_shuffle_mode = SM_NONE;
if (the_setup.InitLoopMode)
m_loop_mode = LM_FULL;
else
- m_loop_mode = LM_NONE;
- clearCache();
+ m_loop_mode = LM_NONE;
+ clearCache();
listitems.setOwner(this);
- clear();
+ clear();
m_orderByCount = false;
}
-
-mgSelection::mgSelection (const bool fall_through)
-{
- InitSelection ();
- m_fall_through = fall_through;
+mgSelection::mgSelection (const bool fall_through) {
+ InitSelection ();
+ m_fall_through = fall_through;
}
-mgSelection::mgSelection (const mgSelection &s)
-{
- InitFrom(&s);
+mgSelection::mgSelection (const mgSelection &s) {
+ InitFrom(&s);
}
-mgSelection::mgSelection (const mgSelection* s)
-{
- InitFrom(s);
+mgSelection::mgSelection (const mgSelection* s) {
+ InitFrom(s);
}
-
void mgSelection::DumpState(mgValmap& nv, const char *prefix) const
{
nv.put(m_fall_through,"%s.FallThrough",prefix);
nv.put(m_orderByCount,"%s.OrderByCount",prefix);
- for (unsigned int i=0;i<ordersize();i++)
- {
+ for (unsigned int i=0;i<ordersize();i++) {
nv.put(int(Key(i)->Type()),"%s.Keys.%d.Type",prefix,i);
if (i<=m_level)
nv.put(getKeyItem(i)->value(),
@@ -760,50 +688,43 @@ void mgSelection::DumpState(mgValmap& nv, const char *prefix) const
}
}
-
-void mgSelection::ShowState(char *w) const
+void mgSelection::ShowState(const char *w) const
{
mgDebug(1,"ShowState:%s,m_level=%d",w,m_level);
- for (unsigned int i=0;i<ordersize();i++)
- {
+ for (unsigned int i=0;i<ordersize();i++) {
mgDebug(1," %d:Type=%s,val=%s,id=%s",
- i,ktName(Key(i)->Type()),
- getKeyItem(i)->value().c_str(),
- getKeyItem(i)->id().c_str());
+ i,ktName(Key(i)->Type()),
+ getKeyItem(i)->value().c_str(),
+ getKeyItem(i)->id().c_str());
}
}
void
-mgSelection::InitFrom(const char *prefix,mgValmap& nv)
-{
+mgSelection::InitFrom(const char *prefix,mgValmap& nv) {
InitSelection();
clear();
m_fall_through = true;
setOrderByCount(nv.getbool("%s.OrderByCount",prefix));
- for (unsigned int idx = 0 ; idx < 999 ; idx++)
- {
+ for (unsigned int idx = 0 ; idx < 999 ; idx++) {
unsigned int type = nv.getuint("%s.Keys.%u.Type",prefix,idx);
if (type==0) break;
setKey(mgKeyTypes(type));
}
vector<mgListItem> items;
- for (unsigned int idx = 0 ; idx < ordersize() ; idx++)
- {
- char b[100];
+ for (unsigned int idx = 0 ; idx < ordersize() ; idx++) {
+ char b[100];
sprintf(b,"%s.Keys.%u.Position",prefix,idx);
if (!nv.count(b)) break;
string newval = nv.getstr(b);
items.push_back(mgListItem(newval,KeyMaps.id(Keys[idx]->Type(),newval),0));
}
if (ordersize() && Keys[ordersize()-1]->Type()!=keyGdUnique)
- setKey(keyGdUnique);
+ setKey(keyGdUnique);
InitOrder(items);
}
-
-void
-mgSelection::CopyKeyValues(mgSelection* s)
-{
+void
+mgSelection::CopyKeyValues(mgSelection* s) {
if (!s)
mgError("mgSelection::CopyKeyValues(0)");
if (s==this)
@@ -814,21 +735,17 @@ mgSelection::CopyKeyValues(mgSelection* s)
o = s->getItem(0)->Clone();
SetLevel(0);
vector<mgListItem> items;
- for (unsigned int idx = 0; idx < ordersize(); idx++)
- {
+ for (unsigned int idx = 0; idx < ordersize(); idx++) {
bool found = false;
mgKeyTypes new_kt = getKeyType(idx);
- if (o && o->getItemid()>=0)
- {
+ if (o && o->getItemid()>=0) {
items.push_back(o->getKeyItem(new_kt));
found = true;
continue;
}
- if (s) for (unsigned int i=0;i<s->ordersize();i++)
- {
+ if (s) for (unsigned int i=0;i<s->ordersize();i++) {
mgKeyTypes old_kt = s->getKeyType(i);
- if (old_kt==new_kt && s->getKeyItem(i))
- {
+ if (old_kt==new_kt && s->getKeyItem(i)) {
items.push_back(s->getKeyItem(i));
found = true;
break;
@@ -845,32 +762,32 @@ mgSelection::CopyKeyValues(mgSelection* s)
}
void
-mgSelection::InitOrder(vector<mgListItem>& items)
-{
+mgSelection::InitOrder(vector<mgListItem>& items) {
mgDebug(5,"InitOrder:");
for (unsigned int idx = 0; idx < items.size(); idx++)
mgDebug(5,"%d:%s/%s",idx,items[idx].value().c_str(),items[idx].id().c_str());
if (ordersize()==0)
return;
for (unsigned int idx = 0; idx < ordersize(); idx++)
- Key(idx)->set(0);
+ Key(idx)->set(0);
for (unsigned int idx = 0; idx < items.size(); idx++)
Key(idx)->set (&items[idx]);
m_active = false;
}
-void
-mgSelection::Activate()
-{
- assert(ordersize());
- if (m_level)
- assert(m_level<ordersize());
+void
+mgSelection::Activate() {
+ if (ordersize()==0) {
+ mgError("muggle: Activate: ordersize is 0");
+ InitDefaultOrder();
+ }
+ if (m_level)
+ assert(m_level<ordersize());
if (m_active)
return;
m_active = true;
m_level = 0;
- for (unsigned int lev = 0; lev < ordersize(); lev++)
- {
+ for (unsigned int lev = 0; lev < ordersize()-1; lev++) {
if (!getKeyItem(lev))
break;
m_level = lev;
@@ -884,250 +801,224 @@ mgSelection::Activate()
DecLevel();
}
-mgSelection::~mgSelection ()
-{
- m_level=0;
- truncate(0);
- delete m_db;
+mgSelection::~mgSelection () {
+ m_level=0;
+ truncate(0);
+ delete m_db;
}
-void mgSelection::InitFrom(const mgSelection* s)
-{
- InitSelection();
- if (!s)
- return;
- for (unsigned int i = 0; i < s->ordersize();i++)
- {
- mgKey *k = ktGenerate(s->getKeyType(i));
- k->set(s->getKeyItem(i));
- Keys.push_back(k);
- }
- m_active = s->m_active;
- SetLevel(s->m_level);
- if (m_level)
- assert(m_level<ordersize());
- m_fall_through = s->m_fall_through;
- m_orderByCount = s->m_orderByCount;
- m_position = s->m_position;
- m_items_position = s->m_items_position;
- setShuffleMode (s->getShuffleMode ());
- setLoopMode (s->getLoopMode ());
+void mgSelection::InitFrom(const mgSelection* s) {
+ InitSelection();
+ if (!s)
+ return;
+ for (unsigned int i = 0; i < s->ordersize();i++) {
+ mgKey *k = ktGenerate(s->getKeyType(i));
+ k->set(s->getKeyItem(i));
+ Keys.push_back(k);
+ }
+ m_active = s->m_active;
+ SetLevel(s->m_level);
+ if (m_level)
+ assert(m_level<ordersize());
+ m_fall_through = s->m_fall_through;
+ m_orderByCount = s->m_orderByCount;
+ m_position = s->m_position;
+ m_items_position = s->m_items_position;
+ setShuffleMode (s->getShuffleMode ());
+ setLoopMode (s->getLoopMode ());
}
-
void
mgSelection::refreshValues () const
{
- assert(this);
- assert(m_db);
- if (!m_current_values.empty())
- return;
- mgParts p = SelParts(true,false);
- m_current_values = m_db->LoadValuesInto(
- p,getKeyType(m_level),listitems.items(),m_level<ordersize()-2);
- if (!inCollection(""))
- listitems.sort(m_orderByCount,Keys[m_level]->SortBy());
+ assert(this);
+ assert(m_db);
+ if (!m_current_values.empty())
+ return;
+ mgParts p = SelParts(true,false);
+ m_current_values = m_db->LoadValuesInto(
+ p,getKeyType(m_level),listitems.items(),m_level<ordersize()-2);
+ if (!inCollection(""))
+ listitems.sort(m_orderByCount,Keys[m_level]->SortBy());
}
-
void
-mgSelection::DecLevel()
-{
+mgSelection::DecLevel() {
m_level--;
clearCache();
}
void
-mgSelection::IncLevel()
-{
+mgSelection::IncLevel() {
m_level++;
clearCache();
}
void
-mgSelection::SetLevel(unsigned int level)
-{
+mgSelection::SetLevel(unsigned int level) {
m_level=level;
clearCache();
}
-bool mgSelection::enter (unsigned int position)
-{
- assert(!Keys.empty());
- if (inItem())
- return false;
- if (empty())
- refreshValues();
- if (empty())
- return false;
- mgDebug(5,"%X:level %d:enter(%d)",this,m_level,position);
- if (inCollection())
- {
- mgListItem *item=Key(m_level)->get();
- IncLevel();
- Key(m_level)->set(item);
- setPosition(0);
- gotoPosition();
- return true;
- }
- mgListItems prev;
- listitems.refresh();
- if (m_level<ordersize()-2 && m_fall_through && listitems.size()<100)
- prev=listitems;
- while (1)
- {
- setPosition(position);
- position = gotoPosition(); // reload adjusted position
- if (inItems())
- {
+bool mgSelection::enter (unsigned int position) {
+ assert(!Keys.empty());
+ if (inItem())
+ return false;
+ if (empty())
+ refreshValues();
+ if (empty())
+ return false;
+ mgDebug(5,"%X:level %d:enter(%d)",this,m_level,position);
+ if (inCollection()) {
mgListItem *item=Key(m_level)->get();
IncLevel();
Key(m_level)->set(item);
- mgListItem *i2=Key(m_level)->get();
- i2->set(item->value(),item->unique_id(),1);
+ setPosition(0);
+ gotoPosition();
+ return true;
}
- else
- IncLevel();
- position = 0;
- if (empty())
- break;
- if (!m_fall_through)
- break;
- if (m_level>=ordersize()-2)
- break;
+ mgListItems prev;
+ prev.setOwner(this);
listitems.refresh();
- if (listitems.size () > 1 && !(prev==listitems))
- break;
- }
- setPosition(position);
- position = gotoPosition();
- mgDebug(5,"enter exits:level=%d,set to %s",m_level,getCurrentValue().c_str());
- return true;
+ if (m_level<ordersize()-2 && m_fall_through && listitems.size()<100)
+ prev=listitems;
+ while (1) {
+ setPosition(position);
+ // reload adjusted position
+ position = gotoPosition();
+ if (inItems()) {
+ mgListItem *item=Key(m_level)->get();
+ IncLevel();
+ Key(m_level)->set(item);
+ mgListItem *i2=Key(m_level)->get();
+ i2->set(item->value(),item->unique_id(),1);
+ }
+ else
+ IncLevel();
+ position = 0;
+ if (empty())
+ break;
+ if (!m_fall_through)
+ break;
+ if (m_level>=ordersize()-2)
+ break;
+ listitems.refresh();
+ if (listitems.size () > 1 && !(prev==listitems))
+ break;
+ }
+ setPosition(position);
+ position = gotoPosition();
+ mgDebug(5,"enter exits:level=%d,set to %s",m_level,getCurrentValue().c_str());
+ return true;
}
bool
-mgSelection::leave ()
-{
- unsigned int position=m_position;
- assert(!Keys.empty());
- mgListItems prev;
- listitems.refresh();
- if (m_level>1 && m_fall_through && listitems.size()<100)
- prev=listitems;
- while (1)
- {
- setPosition(position);
- position = gotoPosition(); // reload adjusted position
- Key(m_level)->set(0);
- if (m_level==0)
- return false;
- DecLevel();
- refreshValues();
- position = listitems.valindex (getKeyItem(m_level)->value());
- if (!m_fall_through)
- break;
- if (m_level==0)
- break;
+mgSelection::leave () {
+ unsigned int position=m_position;
+ assert(!Keys.empty());
+ mgListItems prev;
listitems.refresh();
- if (listitems.size () > 1 && !(prev==listitems))
- break;
- }
- setPosition(position);
- return true;
+ if (m_level>1 && m_fall_through && listitems.size()<100)
+ prev=listitems;
+ while (1) {
+ setPosition(position);
+ // reload adjusted position
+ position = gotoPosition();
+ Key(m_level)->set(0);
+ if (m_level==0)
+ return false;
+ DecLevel();
+ refreshValues();
+ position = listitems.valindex (getKeyItem(m_level)->value());
+ if (!m_fall_through)
+ break;
+ if (m_level==0)
+ break;
+ listitems.refresh();
+ if (listitems.size () > 1 && !(prev==listitems))
+ break;
+ }
+ setPosition(position);
+ return true;
}
void
-mgSelection::leave_all ()
-{
+mgSelection::leave_all () {
SetLevel(0);
for (unsigned int i=0;i<ordersize();i++)
Key(i)->set (0);
}
-
void
-mgSelection::truncate(unsigned int i)
-{
- while (ordersize()>i)
- {
+mgSelection::truncate(unsigned int i) {
+ while (ordersize()>i) {
delete Keys.back();
Keys.pop_back();
}
}
void
-mgSelection::setKey (const mgKeyTypes kt)
-{
- mgKey *newkey = ktGenerate(kt);
- if (newkey)
- Keys.push_back(newkey);
+mgSelection::setKey (const mgKeyTypes kt) {
+ mgKey *newkey = ktGenerate(kt);
+ if (newkey)
+ Keys.push_back(newkey);
}
void
-mgSelection::setKeys(vector<const char *>& kt)
-{
+mgSelection::setKeys(vector<const char *>& kt) {
clear();
- for (unsigned int i=0;i<kt.size();i++)
- {
+ for (unsigned int i=0;i<kt.size();i++) {
setKey(ktValue(kt[i]));
}
- clean();
+ clean();
}
void
-mgSelection::clear()
-{
+mgSelection::clear() {
m_level=0;
clearCache();
truncate(0);
}
void
-mgSelection::clean()
-{
+mgSelection::clean() {
// remove double entries:
keyvector::iterator a;
keyvector::iterator b;
- for (a = Keys.begin () ; a != Keys.end (); ++a)
- {
-cleanagain:
+ for (a = Keys.begin () ; a != Keys.end (); ++a) {
+ cleanagain:
for (b = a+1 ; b != Keys.end(); ++b)
- if ((*a)->Type() == (*b)->Type())
- {
- delete *b;
- Keys.erase(b);
- goto cleanagain;
- }
+ if ((*a)->Type() == (*b)->Type()) {
+ delete *b;
+ Keys.erase(b);
+ goto cleanagain;
+ }
}
}
string
-mgSelection::Name()
-{
+mgSelection::Name() {
string result="";
if (ordersize()>0)
- for (unsigned int idx=0;idx<ordersize()-1;idx++)
- {
- if (!result.empty()) result += ":";
- result += ktName(Keys[idx]->Type());
- }
+ for (unsigned int idx=0;idx<ordersize()-1;idx++) {
+ if (!result.empty()) result += ":";
+ result += ktName(Keys[idx]->Type());
+ }
return result;
}
bool
-mgSelection::SameOrder(const mgSelection* other)
-{
- bool result = ordersize()==other->ordersize() && m_orderByCount == other->m_orderByCount;
- if (result)
- for (unsigned int i=0; i<ordersize();i++)
- {
- result &= Key(i)->Type()==other->Key(i)->Type();
+mgSelection::SameOrder(const mgSelection* other) {
+ bool result = ordersize()==other->ordersize() && m_orderByCount == other->m_orderByCount;
+ if (result)
+ for (unsigned int i=0; i<ordersize();i++) {
+ result &= Key(i)->Type()==other->Key(i)->Type();
if (!result) break;
- }
- return result;
+ }
+ return result;
}
-mgKey*
+mgKey*
mgSelection::Key(unsigned int idx) const
{
assert(idx<ordersize());
@@ -1148,16 +1039,13 @@ static vector<int> keycounts;
unsigned int
mgSelection::keycount(mgKeyTypes kt) const
{
- if (keycounts.size()==0)
- {
- for (unsigned int ki=(unsigned int)(ktLow());ki<=(unsigned int)(ktHigh());ki++)
- {
+ if (keycounts.size()==0) {
+ for (unsigned int ki=(unsigned int)(ktLow());ki<=(unsigned int)(ktHigh());ki++) {
keycounts.push_back(-1);
}
}
int& kcount = keycounts[int(kt-ktLow())];
- if (kcount==-1)
- {
+ if (kcount==-1) {
mgKey* k = ktGenerate(kt);
if (k->Enabled(m_db))
kcount = m_db->exec_count(k->Parts(m_db,true).sql_count());
@@ -1173,8 +1061,17 @@ mgSelection::ktValue(const char * name) const
{
for (int kt=int(ktLow());kt<=int(ktHigh());kt++)
if (!strcmp(name,ktName(mgKeyTypes(kt))))
- return mgKeyTypes(kt);
+ return mgKeyTypes(kt);
mgError("ktValue(%s): unknown name",name);
return mgKeyTypes(0);
}
+string
+mgSelection::OneArtist(void) const
+{
+ if (m_items.size()==0) return "";
+ string firstartist=dynamic_cast<mgItemGd*>(m_items[0])->getArtist();
+ for (unsigned int i=0;i<m_items.size();i++)
+ if (dynamic_cast<mgItemGd*>(m_items[i])->getArtist()!=firstartist) return "";
+ return firstartist;
+}
diff --git a/mg_selection.h b/mg_selection.h
index d56d0e6..cd48e83 100644
--- a/mg_selection.h
+++ b/mg_selection.h
@@ -31,391 +31,388 @@ typedef vector<mgKey*> keyvector;
* Some member functions are declared const although they can modify the inner state of mgSelection.
* But they only modify variables used for caching. With const, we want to express
* the logical constness. E.g. the selected items can change without breaking constness:
- * The selection never defines concrete items but only how to choose them.
+ * The selection never defines concrete items but only how to choose them.
*/
class mgSelection
{
- public:
- void CopyKeyValues(mgSelection* s);
-/*! \brief define various ways to play music in random order
- * \todo Party mode is not implemented, does same as SM_NORMAL
- */
- enum ShuffleMode
- {
- SM_NONE, //!< \brief play normal sequence
- SM_NORMAL, //!< \brief a shuffle with a fair distribution
- SM_PARTY //!< \brief select the next few songs randomly, continue forever
- };
-
-//! \brief define various ways to play music in a neverending loop
- enum LoopMode
- {
- LM_NONE, //!< \brief do not loop
- LM_SINGLE, //!< \brief loop a single item
- LM_FULL //!< \brief loop the whole item list
- };
-
- class mgListItems
- {
- public:
- mgListItems() { m_sel=0; }
- void setOwner(mgSelection* sel);
- mgListItem* operator[](unsigned int idx);
- string& id(unsigned int);
- unsigned int count(unsigned int);
- bool operator==(const mgListItems&x) const;
- void refresh();
- size_t size() const;
- int search (const string v) const;
- unsigned int valindex (const string v) const;
- unsigned int idindex (const string i) const;
- void clear();
- void push_back(mgListItem* item) { m_items.push_back(item); }
- vector<mgListItem*>& items() { return m_items; } //! \brief use only for loading!
- void sort(bool bycount,mgSortBy SortBy);
- private:
- unsigned int index (const string s,bool val,bool second_try=false) const;
- vector<mgListItem*> m_items;
- mgSelection* m_sel;
- };
-
-/*! \brief the main constructor
- * \param fall_through if TRUE: If enter() returns a choice
- * containing only one item, that item is automatically entered.
- * The analog happens with leave()
- */
- mgSelection ( const bool fall_through = false);
-
-/*! \brief a copy constructor. Does a deep copy.
- * Some of the data base content will only be retrieved by the
- * new mgSelection as needed, so some data base
- * overhead is involved
- */
- mgSelection (const mgSelection& s);
-/*! \brief a copy constructor. Does a deep copy.
- * Some of the data base content will only be retrieved by the
- * new mgSelection as needed, so some data base
- * overhead is involved
- */
- mgSelection (const mgSelection* s);
-
- virtual void MakeCollection() =0;
-
-//! \brief initializes from a map.
- void InitFrom(const char *prefix,mgValmap& nv);
-
-//! \brief the normal destructor
- virtual ~mgSelection ();
-
-/*! \brief represents all items for the current level. The result
- * is cached, subsequent accesses to values only incur a
- * small overhead for building the SQL WHERE command. The items will
- * be reloaded when the SQL command changes
- */
- mutable mgListItems listitems;
-
-/*! \brief returns the name of a key
- */
- mgKeyTypes getKeyType (const unsigned int level) const;
-
- mgSortBy getKeySortBy (const unsigned int level) const;
-
-//! \brief return the current value of this key
- mgListItem* getKeyItem (const unsigned int level) const;
-
-/*! \brief returns an item value
- */
- string getValue(unsigned int idx) const;
-
-/*! \brief returns the current item from the value() list
- */
- string getCurrentValue();
-
-//! \brief the current position
- unsigned int getPosition ()const;
-
- //! \brief go to the current position. If it does not exist,
- // go to the nearest.
- unsigned int gotoPosition ();
-
-//! \brief the current position in the item list
- unsigned int getItemPosition () const;
-
- //! \brief go to the current item position. If it does not exist,
- // go to the nearest.
- unsigned int gotoItemPosition ();
-
-/*! \brief enter the the next higher level, go one up in the tree.
- * If fall_through (see constructor) is set to true, and the
- * level entered by enter() contains only one item, automatically
- * goes down further until a level with more than one item is reached.
- * \param position is the position in the current level that is to be expanded
- * \return returns false if there is no further level
- */
- bool enter (unsigned int position);
-
-/*! \brief enter the next higher level, expanding the current position.
- * See also enter(unsigned int position)
- */
- bool enter ()
- {
- return enter (gotoPosition ());
- }
-/*! \brief enter the next higher level, expanding the position holding a certain value
- * \param value the position holding value will be expanded.
- */
- bool enter (const string value)
- {
- return enter (listitems.valindex (value));
- }
-/*! \brief leave the current level, go one up in the tree.
- * If fall_through (see constructor) is set to true, and the
- * level entered by leave() contains only one item, automatically
- * goes up further until a level with more than one item is reached.
- * \return returns false if there is no further upper level
- */
- bool leave ();
-
-/*! \brief leave the current level, go up in the tree until
- * target level is reached.
- * If fall_through (see constructor) is set to true, and the
- * level entered by leave() contains only one item, automatically
- * goes up further until a level with more than one item is reached.
- * \return returns false if there is no further upper level
- */
- void leave_all ();
-
- unsigned int ordersize() const
- {
- return Keys.size();
- }
-/*! \brief the orderlevel is 0 for the top level. After initializing
- * an mgSelection from file or from another mgSelection, it is 0.
- * It will only be correct after Activate() has been called. This
- * is so because setting it correctly needs to access the database.
- * We do not want to do that before we really need to.
- */
- unsigned int orderlevel() const
- {
- return m_level;
- }
-
- //! \brief true if the selection holds no items
- bool empty();
-
-/*! \brief returns detailed info about all selected items.
- * The ordering is done only by the keyfield of the current level.
- * This might have to be changed - suborder by keyfields of detail
- * levels. This list is cached so several consequent calls mean no
- * loss of performance. See value(), the same warning applies.
- * \todo call this more seldom. See getNumItems()
- */
- const vector < mgItem* > &items () const;
-
-/*! \brief returns an item from the items() list
- * \param position the position in the items() list
- * \return returns NULL if position is out of range
- */
- mgItem* getItem (unsigned int position);
-
-/*! \brief returns the current item from the items() list
- */
- mgItem* getCurrentItem ()
- {
- return getItem (gotoItemPosition());
- }
-
-/*! \brief toggles the shuffle mode thru all possible values.
- * When a shuffle modus SM_NORMAL or SM_PARTY is selected, the
- * order of the items in the item list will be randomly changed.
- */
- ShuffleMode toggleShuffleMode ();
-
-//! \brief toggles the loop mode thru all possible values
- LoopMode toggleLoopMode ();
-
-//! \brief returns the current shuffle mode
- ShuffleMode getShuffleMode () const
- {
- return m_shuffle_mode;
- }
-
-//! \brief sets the current shuffle mode
- void setShuffleMode (const ShuffleMode shuffle_mode);
-
-//! \brief returns the current loop mode
- LoopMode getLoopMode () const
- {
- return m_loop_mode;
- }
-
-//! \brief sets the current loop mode
- void setLoopMode (const LoopMode loop_mode)
- {
- m_loop_mode = loop_mode;
- }
-
-/*! \brief adds the whole current item list to a collection
- * \param Name the name of the collection. If it does not yet exist,
- * it will be created.
- */
- unsigned int AddToCollection (const string Name);
-
-/*! \brief removes the whole current item from a the collection
- * Remember - this selection can be configured to hold exactly
- * one list, so this command can be used to clear a selected list.
- * \param Name the name of the collection
- */
- unsigned int RemoveFromCollection (const string Name);
-//! \brief delete a collection
- bool DeleteCollection (const string Name);
-/*! \brief create a collection only if it does not yet exist.
- * \return true only if it has been created. false if it already existed.
- */
- bool CreateCollection(const string Name);
-
-//! \brief remove all items from the collection
- void ClearCollection (const string Name);
-
-/*! generates an m3u file containing all items. The directory
- * can be indicated by SetDirectory().
- * The file name will be built from the list name, slashes
- * and spaces converted
- */
- string exportM3U ();
-
-
-/*! \brief go to a position in the current level. If we are at the
- * most detailled level this also sets the item position since
- * they are identical.
- * \param position the wanted position. If it is too big, go to the
- * last existing position
- * \return only if no position exists, false will be returned
- */
- void setPosition (unsigned int position);
-
-/*! \brief go to the position with value in the current level
- * \param value the value of the wanted position
- */
- void setPosition (string value);
-
-/*! \brief search the first position starting with search in the current level
- * \param search the search string
- * \return the new position
- */
- unsigned int searchPosition (string search);
-
-/*! \brief go to a position in the item list
- * \param position the wanted position. If it is too big, go to the
- * last existing position. If the position is not valid, find the
- * next valid one.
- * \return only if no position exists, false will be returned
- */
- void GotoItemPosition (unsigned int position) const;
-
-/*! \brief skip some items in the item list
- * \return false if new position does not exist
- */
- bool skipItems (int step=1) const;
-
-//! \brief returns the sum of the durations of all items
- unsigned long getLength ();
-
-/*! \brief returns the sum of the durations of completed items
- * those are items before the current item position
- */
- unsigned long getCompletedLength () const;
-
-
-/*! returns the name of the current play list. If no play list is active,
- * the name is built from the name of the key fields.
- */
- string getListname () const;
-
- /*! \brief dumps the entire state of this selection into a map,
- * \param nv the values will be entered into this map
- */
- void DumpState(mgValmap& nv,const char *prefix) const;
- void ShowState(char *w) const;
-
- //! \brief clear the cache, next access will reload from data base
- void clearCache() const;
-
- void refreshValues() const;
-
- //! \brief true if values and items need to be reloaded
- bool cacheIsEmpty() const
- {
- return (m_current_values=="" && m_current_tracks=="");
- }
-
- void setKeys(vector<const char*>& kt);
- string Name();
- bool SameOrder(const mgSelection* other);
- mgKey* Key(unsigned int idx) const;
- virtual vector <const char*> Choices(unsigned int level, unsigned int *current) const = 0;
- void setOrderByCount(bool groupbycount);
- bool getOrderByCount() const { return m_orderByCount; }
- virtual bool NeedKey(unsigned int i) const = 0;
- virtual mgParts SelParts(bool distinct,bool deepsort) const;
- virtual bool inCollection(const string Name="") const =0;
- virtual bool isLanguagelist() const =0;
- virtual bool isCollectionlist() const =0;
-/*! \brief sets a default order. Every backend can define any number of
- * default orders. \param i references the wanted order
- * \return If i is higher than the highest default order, we return false
- */
- virtual bool InitDefaultOrder(unsigned int i=0) =0;
-/*! \brief prepare for use: initialize m_level and go to the
- * correct position. This will execute only once after creation
- * of the mgSelection, so we can call it too often
- */
- void Activate();
-
- virtual bool keyIsUnique(mgKeyTypes kt) const = 0;
- bool inItem() const;
- bool inItems() const;
-
- protected:
- void InitFrom(const mgSelection* s);
- virtual bool DeduceKeyValue(mgKeyTypes new_kt,const mgSelection *s,
- vector<mgListItem>& items) {return false;}
- virtual void clean();
- virtual void InitSelection ();
- virtual bool isCollectionOrder() const=0;
- keyvector Keys;
- unsigned int m_level;
- mutable mgDb* m_db;
- bool m_orderByCount;
- bool UsedBefore(const mgKeyTypes kt,unsigned int level) const;
- unsigned int keycount(mgKeyTypes kt) const;
- virtual mgKeyTypes ktLow() const =0;
- virtual mgKeyTypes ktHigh() const =0;
- virtual const char * const ktName(const mgKeyTypes kt) const=0;
- mgKeyTypes ktValue(const char * name) const;
- void truncate(unsigned int i);
- void clear();
- void setKey ( const mgKeyTypes kt);
-
- private:
- bool m_active;
- mutable string m_current_values;
- mutable string m_current_tracks;
-//! \brief be careful when accessing this, see mgSelection::items()
- mutable vector < mgItem* > m_items;
- bool m_fall_through;
- unsigned int m_position;
- mutable unsigned int m_items_position;
- ShuffleMode m_shuffle_mode;
- void Shuffle() const;
- LoopMode m_loop_mode;
-
- string ListFilename ();
-
- void InitOrder(vector<mgListItem>& items);
- void SetLevel(unsigned int level);
- void IncLevel();
- void DecLevel();
+ public:
+ void CopyKeyValues(mgSelection* s);
+ /*! \brief define various ways to play music in random order
+ * \todo Party mode is not implemented, does same as SM_NORMAL
+ */
+ enum ShuffleMode
+ {
+ SM_NONE, //!< \brief play normal sequence
+ SM_NORMAL, //!< \brief a shuffle with a fair distribution
+ SM_PARTY //!< \brief select the next few songs randomly, continue forever
+ };
+
+ //! \brief define various ways to play music in a neverending loop
+ enum LoopMode
+ {
+ LM_NONE, //!< \brief do not loop
+ LM_SINGLE, //!< \brief loop a single item
+ LM_FULL //!< \brief loop the whole item list
+ };
+
+ class mgListItems
+ {
+ public:
+ mgListItems() { m_sel=0; }
+ void setOwner(mgSelection* sel);
+ mgListItem* operator[](unsigned int idx);
+ string& id(unsigned int);
+ unsigned int count(unsigned int);
+ bool operator==(const mgListItems&x) const;
+ void refresh();
+ size_t size() const;
+ int search (const string v) const;
+ unsigned int valindex (const string v) const;
+ unsigned int idindex (const string i) const;
+ void clear();
+ void push_back(mgListItem* item) { m_items.push_back(item); }
+ //! \brief use only for loading!
+ vector<mgListItem*>& items() {
+ return m_items;
+ }
+ void sort(bool bycount,mgSortBy SortBy);
+ private:
+ unsigned int index (const string s,bool val,bool second_try=false) const;
+ vector<mgListItem*> m_items;
+ mgSelection* m_sel;
+ };
+
+ /*! \brief the main constructor
+ * \param fall_through if TRUE: If enter() returns a choice
+ * containing only one item, that item is automatically entered.
+ * The analog happens with leave()
+ */
+ mgSelection ( const bool fall_through = false);
+
+ /*! \brief a copy constructor. Does a deep copy.
+ * Some of the data base content will only be retrieved by the
+ * new mgSelection as needed, so some data base
+ * overhead is involved
+ */
+ mgSelection (const mgSelection& s);
+ /*! \brief a copy constructor. Does a deep copy.
+ * Some of the data base content will only be retrieved by the
+ * new mgSelection as needed, so some data base
+ * overhead is involved
+ */
+ mgSelection (const mgSelection* s);
+
+ virtual void MakeCollection() =0;
+
+ //! \brief initializes from a map.
+ void InitFrom(const char *prefix,mgValmap& nv);
+
+ //! \brief the normal destructor
+ virtual ~mgSelection ();
+
+ /*! \brief represents all items for the current level. The result
+ * is cached, subsequent accesses to values only incur a
+ * small overhead for building the SQL WHERE command. The items will
+ * be reloaded when the SQL command changes
+ */
+ mutable mgListItems listitems;
+
+ /*! \brief returns the name of a key
+ */
+ mgKeyTypes getKeyType (const unsigned int level) const;
+
+ mgSortBy getKeySortBy (const unsigned int level) const;
+
+ //! \brief return the current value of this key
+ mgListItem* getKeyItem (const unsigned int level) const;
+
+ /*! \brief returns an item value
+ */
+ string getValue(unsigned int idx) const;
+
+ /*! \brief returns the current item from the value() list
+ */
+ string getCurrentValue();
+
+ //! \brief the current position
+ unsigned int getPosition ()const;
+
+ //! \brief go to the current position. If it does not exist,
+ // go to the nearest.
+ unsigned int gotoPosition ();
+
+ //! \brief the current position in the item list
+ unsigned int getItemPosition () const;
+
+ //! \brief go to the current item position. If it does not exist,
+ // go to the nearest.
+ unsigned int gotoItemPosition ();
+
+ /*! \brief enter the the next higher level, go one up in the tree.
+ * If fall_through (see constructor) is set to true, and the
+ * level entered by enter() contains only one item, automatically
+ * goes down further until a level with more than one item is reached.
+ * \param position is the position in the current level that is to be expanded
+ * \return returns false if there is no further level
+ */
+ bool enter (unsigned int position);
+
+ /*! \brief enter the next higher level, expanding the current position.
+ * See also enter(unsigned int position)
+ */
+ bool enter () {
+ return enter (gotoPosition ());
+ }
+ /*! \brief enter the next higher level, expanding the position holding a certain value
+ * \param value the position holding value will be expanded.
+ */
+ bool enter (const string value) {
+ return enter (listitems.valindex (value));
+ }
+ /*! \brief leave the current level, go one up in the tree.
+ * If fall_through (see constructor) is set to true, and the
+ * level entered by leave() contains only one item, automatically
+ * goes up further until a level with more than one item is reached.
+ * \return returns false if there is no further upper level
+ */
+ bool leave ();
+
+ /*! \brief leave the current level, go up in the tree until
+ * target level is reached.
+ * If fall_through (see constructor) is set to true, and the
+ * level entered by leave() contains only one item, automatically
+ * goes up further until a level with more than one item is reached.
+ * \return returns false if there is no further upper level
+ */
+ void leave_all ();
+
+ unsigned int ordersize() const
+ {
+ return Keys.size();
+ }
+ /*! \brief the orderlevel is 0 for the top level. After initializing
+ * an mgSelection from file or from another mgSelection, it is 0.
+ * It will only be correct after Activate() has been called. This
+ * is so because setting it correctly needs to access the database.
+ * We do not want to do that before we really need to.
+ */
+ unsigned int orderlevel() const
+ {
+ return m_level;
+ }
+
+ //! \brief true if the selection holds no items
+ bool empty();
+
+ /*! \brief returns detailed info about all selected items.
+ * The ordering is done only by the keyfield of the current level.
+ * This might have to be changed - suborder by keyfields of detail
+ * levels. This list is cached so several consequent calls mean no
+ * loss of performance. See value(), the same warning applies.
+ * \todo call this more seldom. See getNumItems()
+ */
+ const vector < mgItem* > &items () const;
+
+ /*! \brief returns an item from the items() list
+ * \param position the position in the items() list
+ * \return returns NULL if position is out of range
+ */
+ mgItem* getItem (unsigned int position);
+
+ /*! \brief returns the current item from the items() list
+ */
+ mgItem* getCurrentItem () {
+ return getItem (gotoItemPosition());
+ }
+
+ /*! \brief toggles the shuffle mode thru all possible values.
+ * When a shuffle modus SM_NORMAL or SM_PARTY is selected, the
+ * order of the items in the item list will be randomly changed.
+ */
+ ShuffleMode toggleShuffleMode ();
+
+ //! \brief toggles the loop mode thru all possible values
+ LoopMode toggleLoopMode ();
+
+ //! \brief returns the current shuffle mode
+ ShuffleMode getShuffleMode () const
+ {
+ return m_shuffle_mode;
+ }
+
+ //! \brief sets the current shuffle mode
+ void setShuffleMode (const ShuffleMode shuffle_mode);
+
+ //! \brief returns the current loop mode
+ LoopMode getLoopMode () const
+ {
+ return m_loop_mode;
+ }
+
+ //! \brief sets the current loop mode
+ void setLoopMode (const LoopMode loop_mode) {
+ m_loop_mode = loop_mode;
+ }
+
+ /*! \brief adds the whole current item list to a collection
+ * \param Name the name of the collection. If it does not yet exist,
+ * it will be created.
+ */
+ unsigned int AddToCollection (const string Name);
+
+ /*! \brief removes the whole current item from a the collection
+ * Remember - this selection can be configured to hold exactly
+ * one list, so this command can be used to clear a selected list.
+ * \param Name the name of the collection
+ */
+ unsigned int RemoveFromCollection (const string Name);
+ //! \brief delete a collection
+ bool DeleteCollection (const string Name);
+ /*! \brief create a collection only if it does not yet exist.
+ * \return true only if it has been created. false if it already existed.
+ */
+ bool CreateCollection(const string Name);
+
+ //! \brief remove all items from the collection
+ void ClearCollection (const string Name);
+
+ /*! generates an m3u file containing all items. The directory
+ * can be indicated by SetDirectory().
+ * The file name will be built from the list name, slashes
+ * and spaces converted
+ */
+ string exportM3U ();
+
+ /*! \brief go to a position in the current level. If we are at the
+ * most detailled level this also sets the item position since
+ * they are identical.
+ * \param position the wanted position. If it is too big, go to the
+ * last existing position
+ * \return only if no position exists, false will be returned
+ */
+ void setPosition (unsigned int position);
+
+ /*! \brief go to the position with value in the current level
+ * \param value the value of the wanted position
+ */
+ void setPosition (string value);
+
+ /*! \brief search the first position starting with search in the current level
+ * \param search the search string
+ * \return the new position
+ */
+ unsigned int searchPosition (string search);
+
+ /*! \brief go to a position in the item list
+ * \param position the wanted position. If it is too big, go to the
+ * last existing position. If the position is not valid, find the
+ * next valid one.
+ * \return only if no position exists, false will be returned
+ */
+ void GotoItemPosition (unsigned int position) const;
+
+ /*! \brief skip some items in the item list
+ * \return false if new position does not exist
+ */
+ bool skipItems (int step=1) const;
+
+ //! \brief returns the sum of the durations of all items
+ unsigned long getLength ();
+
+ /*! \brief returns the sum of the durations of completed items
+ * those are items before the current item position
+ */
+ unsigned long getCompletedLength () const;
+
+ /*! returns the name of the current play list. If no play list is active,
+ * the name is built from the name of the key fields.
+ */
+ string getListname () const;
+
+ /*! \brief dumps the entire state of this selection into a map,
+ * \param nv the values will be entered into this map
+ */
+ void DumpState(mgValmap& nv,const char *prefix) const;
+ void ShowState(const char *w) const;
+
+ //! \brief clear the cache, next access will reload from data base
+ void clearCache() const;
+
+ void refreshValues() const;
+
+ //! \brief true if values and items need to be reloaded
+ bool cacheIsEmpty() const
+ {
+ return (m_current_values=="" && m_current_tracks=="");
+ }
+
+ void setKeys(vector<const char*>& kt);
+ string Name();
+ bool SameOrder(const mgSelection* other);
+ mgKey* Key(unsigned int idx) const;
+ virtual vector <const char*> Choices(unsigned int level, unsigned int *current) const = 0;
+ void setOrderByCount(bool groupbycount);
+ bool getOrderByCount() const { return m_orderByCount; }
+ virtual bool NeedKey(unsigned int i) const = 0;
+ virtual mgParts SelParts(bool distinct,bool deepsort) const;
+ virtual bool inCollection(const string Name="") const =0;
+ virtual bool isLanguagelist() const =0;
+ virtual bool isCollectionlist() const =0;
+ /*! \brief sets a default order. Every backend can define any number of
+ * default orders. \param i references the wanted order
+ * \return If i is higher than the highest default order, we return false
+ */
+ virtual bool InitDefaultOrder(unsigned int i=0) =0;
+ /*! \brief prepare for use: initialize m_level and go to the
+ * correct position. This will execute only once after creation
+ * of the mgSelection, so we can call it too often
+ */
+ void Activate();
+
+ virtual bool keyIsUnique(mgKeyTypes kt) const = 0;
+ bool inItem() const;
+ bool inItems() const;
+ string OneArtist(void) const;
+
+ protected:
+ void InitFrom(const mgSelection* s);
+ virtual bool DeduceKeyValue(mgKeyTypes new_kt,const mgSelection *s,
+ vector<mgListItem>& items) {return false;}
+ virtual void clean();
+ virtual void InitSelection ();
+ virtual bool isCollectionOrder() const=0;
+ keyvector Keys;
+ unsigned int m_level;
+ mutable mgDb* m_db;
+ bool m_orderByCount;
+ bool UsedBefore(const mgKeyTypes kt,unsigned int level) const;
+ unsigned int keycount(mgKeyTypes kt) const;
+ virtual mgKeyTypes ktLow() const =0;
+ virtual mgKeyTypes ktHigh() const =0;
+ virtual const char * const ktName(const mgKeyTypes kt) const=0;
+ mgKeyTypes ktValue(const char * name) const;
+ void truncate(unsigned int i);
+ void clear();
+ void setKey ( const mgKeyTypes kt);
+
+ private:
+ bool m_active;
+ mutable string m_current_values;
+ mutable string m_current_tracks;
+ //! \brief be careful when accessing this, see mgSelection::items()
+ mutable vector < mgItem* > m_items;
+ bool m_fall_through;
+ unsigned int m_position;
+ mutable unsigned int m_items_position;
+ ShuffleMode m_shuffle_mode;
+ void Shuffle() const;
+ LoopMode m_loop_mode;
+
+ string ListFilename ();
+
+ void InitOrder(vector<mgListItem>& items);
+ void SetLevel(unsigned int level);
+ void IncLevel();
+ void DecLevel();
};
-
#endif
diff --git a/mg_setup.c b/mg_setup.c
index 3100f2c..a3f0b7e 100644
--- a/mg_setup.c
+++ b/mg_setup.c
@@ -14,7 +14,6 @@
* (C) 2001,2002 Stefan Huelswitt <huels@iname.com>
*/
-
#include "mg_setup.h"
#include "mg_tools.h"
#include "mg_db.h"
@@ -23,48 +22,53 @@
#include <cstring>
#include <string>
#include <getopt.h>
+#include <vdr/plugin.h>
mgSetup the_setup;
-
-mgSetup::mgSetup ()
-{
- m_mugglei=false;
- InitLoopMode = 0;
- InitShuffleMode = 0;
- AudioMode = 1;
- DisplayMode = 3;
- BackgrMode = 2;
- TargetLevel = DEFAULT_TARGET_LEVEL;
- LimiterLevel = DEFAULT_LIMITER_LEVEL;
- Only48kHz = 0;
-
- DbHost = 0;
- DbSocket = 0;
- DbPort = 0;
- DbName = strdup ("GiantDisc");
- DbUser = 0;
- DbPass = 0;
- msprintf(&DbDatadir,"%s/.muggle",getenv("HOME"));
- ToplevelDir = strdup(MUSICDIR "/");
- CreateMode = false;
- utf8 = false;
- const char *lang = getenv("LANG");
- if (lang)
- {
- const char *dot = strchr(lang, '.');
- if (dot)
- utf8 = strcmp(dot+1,"UTF-8")==0;
- }
- if (utf8)
- mgWarning("muggle running in UTF-8 mode");
-
- DeleteStaleReferences = false;
-
- // stuff related to cover image display
- ImageCacheDir = strdup( "/tmp" );
- UseDeviceStillPicture = 1;
- ImageShowDuration = 10;
+mgSetup::mgSetup () {
+ m_mugglei=false;
+ InitLoopMode = 0;
+ InitShuffleMode = 0;
+ AudioMode = 1;
+ DisplayMode = 3;
+ BackgrMode = 1;
+ TargetLevel = DEFAULT_TARGET_LEVEL;
+ LimiterLevel = DEFAULT_LIMITER_LEVEL;
+ Only48kHz = 0;
+
+ DbHost = 0;
+ DbSocket = 0;
+ DbPort = 0;
+ DbName = strdup ("GiantDisc");
+ DbUser = 0;
+ DbPass = 0;
+ msprintf(&DbDatadir,"%s/.muggle",getenv("HOME"));
+ ToplevelDir = strdup(MUSICDIR "/");
+ CreateMode = false;
+ utf8 = false;
+ const char *lang = getenv("LANG");
+ if (lang) {
+ const char *dot = strchr(lang, '.');
+ if (dot)
+ utf8 = strcmp(dot+1,"UTF-8")==0;
+ }
+ if (utf8)
+ mgWarning("muggle running in UTF-8 mode");
+
+ DeleteStaleReferences = false;
+
+ //Player:
+ ArtistFirst = 0;
+#ifdef USE_BITMAP
+ ImgAlpha = 255;
+#endif
+ Jumptime = 30;
+
+ // stuff related to cover image display
+ msprintf(&CacheDir,"%s/.muggle/cache",getenv("HOME"));
+ UseDeviceStillPicture = 1;
+ ImageShowDuration = 10;
}
bool
@@ -74,181 +78,175 @@ mgSetup::IsMugglei() const
}
void
-mgSetup::SetMugglei()
-{
+mgSetup::SetMugglei() {
m_mugglei = true;
}
-mgSetup::~mgSetup ()
-{
- free(DbHost);
- free(DbSocket);
- free(DbName);
- free(DbUser);
- free(DbPass);
- free(DbDatadir);
- free(ToplevelDir);
- free(ImageCacheDir);
+mgSetup::~mgSetup () {
+ free(DbHost);
+ free(DbSocket);
+ free(DbName);
+ free(DbUser);
+ free(DbPass);
+ free(DbDatadir);
+ free(ToplevelDir);
+ free(CacheDir);
}
bool
mgSetup::NoHost() const
{
- return !DbHost || strlen(DbHost)==0;
+ return !DbHost || strlen(DbHost)==0;
}
-bool mgSetup::ProcessArguments (int argc, char *argv[])
-{
- mgSetDebugLevel (1);
- char ArgsMessage[1000];
- sprintf(ArgsMessage,"mgSetup::ProcessArgs ");
- for (int i=1;i<argc;i++)
- {
- if (strlen(ArgsMessage)+strlen(argv[i]+2)>1000) break;;
- strcat(ArgsMessage," ");
- strcat(ArgsMessage,argv[i]);
- }
+bool mgSetup::ProcessArguments (int argc, char *argv[]) {
+ mgSetDebugLevel (1);
+ char ArgsMessage[1000];
+ sprintf(ArgsMessage,"mgSetup::ProcessArgs ");
+ for (int i=1;i<argc;i++) {
+ if (strlen(ArgsMessage)+strlen(argv[i]+2)>1000) break;;
+ strcat(ArgsMessage," ");
+ strcat(ArgsMessage,argv[i]);
+ }
- struct option long_options[50];
- char short_options[100];
- memset(short_options,0,sizeof(short_options));
- memset(long_options,0,sizeof(long_options));
- static struct option all_options[] =
- {
- {"host", required_argument, 0, 'h'},
- {"socket", required_argument, 0, 's'},
- {"port", required_argument, 0, 'p'},
- {"user", required_argument, 0, 'u'},
- {"password", required_argument, 0, 'w'},
- {"name", required_argument, 0, 'n'},
- {"datadir", required_argument, 0, 'd'},
- {"toplevel", required_argument, 0, 't'},
- {"verbose", required_argument, 0, 'v'},
- {"create", no_argument, 0, 'c'},
- {"delete", no_argument, 0, 'z'},
- {0,0,0}
- };
- char wanted_opts[50];
- strcpy(wanted_opts,"ndtv");
- if (IsMugglei())
- strcat(wanted_opts,"cz");
- mgDb* db = GenerateDB();
- strcat(wanted_opts,db->Options());
- delete db;
- char *p = wanted_opts;
- char *s = short_options;
- int li = 0;
- while (*p)
- {
- for (unsigned int idx = 0; all_options[idx].name;idx++)
- {
- if (*p==all_options[idx].val)
- {
- *s++ = *p;
- if (all_options[idx].has_arg==required_argument)
- *s++ = ':';
- long_options[li++]=all_options[idx];
+ struct option long_options[50];
+ char short_options[100];
+ memset(short_options,0,sizeof(short_options));
+ memset(long_options,0,sizeof(long_options));
+ static struct option all_options[] = {
+ {"host", required_argument, 0, 'h'},
+ {"socket", required_argument, 0, 's'},
+ {"port", required_argument, 0, 'p'},
+ {"user", required_argument, 0, 'u'},
+ {"password", required_argument, 0, 'w'},
+ {"name", required_argument, 0, 'n'},
+ {"datadir", required_argument, 0, 'd'},
+ {"toplevel", required_argument, 0, 't'},
+ {"verbose", required_argument, 0, 'v'},
+ {"create", no_argument, 0, 'c'},
+ {"delete", no_argument, 0, 'z'},
+ {0,0,0}
+ };
+ char wanted_opts[50];
+ strcpy(wanted_opts,"ntv");
+ if (IsMugglei())
+ strcat(wanted_opts,"cz");
+ mgDb* db = GenerateDB();
+ strcat(wanted_opts,db->Options());
+ delete db;
+ char *p = wanted_opts;
+ char *s = short_options;
+ int li = 0;
+ while (*p) {
+ for (unsigned int idx = 0; all_options[idx].name;idx++) {
+ if (*p==all_options[idx].val) {
+ *s++ = *p;
+ if (all_options[idx].has_arg==required_argument)
+ *s++ = ':';
+ long_options[li++]=all_options[idx];
+ break;
+ }
+ }
+ p++;
+ }
+ int c, option_index = 0;
+ while ((c = getopt_long(argc, argv, short_options,long_options,&option_index)) != -1) {
+ switch (c) {
+ case 'h':
+ {
+ free(DbHost);
+ DbHost = strdup (optarg);
+ }
break;
- }
- }
- p++;
- }
- int c, option_index = 0;
- while ((c = getopt_long(argc, argv, short_options,long_options,&option_index)) != -1)
- {
- switch (c)
- {
- case 'h':
- {
- free(DbHost);
- DbHost = strdup (optarg);
- }
- break;
- case 's':
- {
- free(DbSocket);
- DbSocket = strdup(optarg);
- }
- break;
- case 'p':
- {
- DbPort = atoi (optarg);
- }
- break;
- case 'u':
- {
- free(DbUser);
- DbUser = strdup(optarg);
- }
- break;
- case 'w':
- {
- free(DbPass);
- DbPass = strdup(optarg);
- }
- break;
- case 'n':
- {
- free(DbName);
- DbName = strdup(optarg);
- }
- break;
- case 'd':
- {
- free(DbDatadir);
- DbDatadir = strdup(optarg);
- }
- break;
- case 'v':
- {
- mgSetDebugLevel (atol(optarg));
- }
- break;
- case 't':
- {
- free(ToplevelDir);
- if (optarg[strlen (optarg) - 1] != '/')
- {
- std::string res = std::string (optarg) + "/";
- ToplevelDir = strdup (res.c_str ());
- }
- else
- ToplevelDir = strdup(optarg);
- }
- break;
- case 'z':
- {
- DeleteStaleReferences = true;
- }
- break;
- case 'c':
- {
- CreateMode = true;
- }
- break;
- case '?':
- return false;
+ case 's':
+ {
+ free(DbSocket);
+ DbSocket = strdup(optarg);
+ }
+ break;
+ case 'p':
+ {
+ DbPort = atoi (optarg);
+ }
+ break;
+ case 'u':
+ {
+ free(DbUser);
+ DbUser = strdup(optarg);
+ }
+ break;
+ case 'w':
+ {
+ free(DbPass);
+ DbPass = strdup(optarg);
+ }
+ break;
+ case 'n':
+ {
+ free(DbName);
+ DbName = strdup(optarg);
+ }
+ break;
+ case 'd':
+ {
+ free(DbDatadir);
+ DbDatadir = strdup(optarg);
+ }
+ break;
+ case 'v':
+ {
+ mgSetDebugLevel (atol(optarg));
+ }
+ break;
+ case 't':
+ {
+ free(ToplevelDir);
+ ToplevelDir = strdup(optarg);
+ }
+ break;
+ case 'z':
+ {
+ DeleteStaleReferences = true;
+ }
+ break;
+ case 'c':
+ {
+ CreateMode = true;
+ }
+ break;
+ case '?':
+ return false;
+ }
}
- }
- mgDebug(1,ArgsMessage);
- return true;
+// we want to get the absolute path with symlinks resolved
+ char prev[1000];
+ if (!getcwd(prev,1000))
+ mgError("current path too long");
+ if (chdir(the_setup.ToplevelDir))
+ mgError("cannnot change to directory %s",the_setup.ToplevelDir);
+ char ndir[1000];
+ if (!getcwd(ndir,1000))
+ mgError("new path too long");
+ free(ToplevelDir);
+ msprintf(&the_setup.ToplevelDir,"%s/",ndir);
+ chdir(prev);
+ mgDebug(1,ArgsMessage);
+ return true;
}
const char*
-mgSetup::HelpText()
-{
- static char buf[2000];
- strcpy(buf,
- " -n NNNN, --name=NNNN specify database name (default is GiantDisc)\n"
- " -t TTTT, --toplevel=TTTT specify toplevel directory for music (default is " MUSICDIR ")\n"
- " -d DIRN, --datadir=DIRN specify directory for embedded sql data (default is \"$HOME/.muggle\")\n"
- " -v, --verbose specify debug level. The higher the more. Default is 1\n");
- if (IsMugglei())
- strcat(buf,
- " -z --delete scan all data base entries and delete entries if their file is not found\n"
- " -c --create delete the entire data base and create a new one\n");
- mgDb* db = GenerateDB();
- strcat(buf,db->HelpText());
- delete db;
- return buf;
+mgSetup::HelpText() {
+ static char buf[2000];
+ strcpy(buf,
+ " -n NNNN, --name=NNNN specify database name (default is GiantDisc)\n"
+ " -t TTTT, --toplevel=TTTT specify toplevel directory for music (default is " MUSICDIR ")\n"
+ " -v, --verbose specify debug level. The higher the more. Default is 1\n");
+ if (IsMugglei())
+ strcat(buf,
+ " -z --delete scan all data base entries and delete entries if their file is not found\n"
+ " -c --create delete the entire data base and create a new one\n");
+ mgDb* db = GenerateDB();
+ strcat(buf,db->HelpText());
+ delete db;
+ return buf;
}
diff --git a/mg_setup.h b/mg_setup.h
index 1053128..7e9d080 100644
--- a/mg_setup.h
+++ b/mg_setup.h
@@ -17,6 +17,10 @@
#ifndef ___SETUP_MG_H
#define ___SETUP_MG_H
+#include <string>
+
+using namespace std;
+
#define MAX_STRING_LEN 128
#define DEFAULT_TARGET_LEVEL 25
@@ -29,45 +33,51 @@
*/
class mgSetup
{
- public:
- mgSetup ();
- ~mgSetup (void);
- const char *HelpText();
- bool ProcessArguments(int argc, char *argv[]);
- bool NoHost() const;
- int InitLoopMode;
- int InitShuffleMode;
- int AudioMode;
- int DisplayMode;
- int BackgrMode;
- int TargetLevel;
- int LimiterLevel;
- int Only48kHz;
+ public:
+ mgSetup ();
+ ~mgSetup (void);
+ string ConfigDirectory;
+ const char *HelpText();
+ bool ProcessArguments(int argc, char *argv[]);
+ bool NoHost() const;
+ int InitLoopMode;
+ int InitShuffleMode;
+ int AudioMode;
+ int DisplayMode;
+ int BackgrMode;
+ int TargetLevel;
+ int LimiterLevel;
+ int Only48kHz;
- char *DbHost;
- char *DbSocket;
- char *DbName;
- char *DbUser;
- char *DbPass;
- char *DbDatadir;
+ char *DbHost;
+ char *DbSocket;
+ char *DbName;
+ char *DbUser;
+ char *DbPass;
+ char *DbDatadir;
- int DbPort;
- char *ToplevelDir;
+ int DbPort;
+ char *ToplevelDir;
- int ImageShowDuration;
- char *ImageCacheDir;
- int UseDeviceStillPicture;
+ int ImageShowDuration;
+ char *CacheDir;
+ int UseDeviceStillPicture;
- int DeleteStaleReferences;
- bool CreateMode;
- bool IsMugglei() const;
- void SetMugglei();
- bool utf8;
- private:
- bool m_mugglei;
+ // Player:
+ int ArtistFirst;
+#ifdef USE_BITMAP
+ int ImgAlpha;
+#endif
+ int Jumptime;
+ int DeleteStaleReferences;
+ bool CreateMode;
+ bool IsMugglei() const;
+ void SetMugglei();
+ bool utf8;
+ private:
+ bool m_mugglei;
};
extern mgSetup the_setup;
-
#endif
diff --git a/mg_skin.c b/mg_skin.c
new file mode 100644
index 0000000..ebdd53d
--- /dev/null
+++ b/mg_skin.c
@@ -0,0 +1,245 @@
+/*
+ * Music plugin to VDR (C++)
+ *
+ * (C) 2006 Morone
+ *
+ * This code 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 code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+//#include <string>
+#include <string.h>
+#include <stdlib.h>
+#include <fstream>
+#include <vdr/plugin.h>
+
+#include <vdr/i18n.h>
+#include "mg_skin.h"
+#include "mg_setup.h"
+
+cmgSkin mgSkin;
+
+// --- cmgSkin ---------------------------------------------------------------
+
+cmgSkin::cmgSkin(void) {
+
+ // TOP: 3 colors (one is used for transparence)
+ clrTopBG1 = 0xCFF2A00C;
+ clrTopTextFG1 = 0xFFBABBC0;
+
+ // BETWEEN TOP AND LIST: 4 different colors
+ clrTopBG2 = 0xEF2D435A;
+ clrTopTextFG2 = 0xFFBABBC0;
+ clrTopItemBG1 = 0xEF2D435A;
+ clrTopItemInactiveFG = 0xDF303F52;
+ clrTopItemActiveFG = 0xCFF2A00C;
+ // TRACKLIST: 4 colors
+ clrListBG1 = 0xEF2D435A;
+ clrListBG2 = 0xDF303F52;
+ clrListTextFG = 0xFFBABBC0;
+ clrListTextActiveFG = 0xFFF2A00C;
+ clrListTextActiveBG = 0xDF303F52;
+ clrListRating = 0xFFCC0C0C;
+ // INFO: 4 colors
+ clrInfoBG1 = 0xEF2D435A;
+ clrInfoBG2 = 0xDF303F52;
+ clrInfoTextFG1 = 0xFFBABBC0;
+ clrInfoTitleFG1 = 0xFFBABBC0;
+ clrInfoTextFG2 = 0xFFBABBC0;
+ // PROGRESS: 4 colors
+ clrProgressBG1 = 0xEF2D435A;
+ clrProgressBG2 = 0xDF303F52;
+ clrProgressbarFG = 0xEFA00404;
+ clrProgressbarBG = 0xDF000000;
+ // STATUS: 16 colors , but take care about overall 16 color OSD
+ clrStatusBG = 0xCFF2A00C;
+ clrStatusRed = 0xFFC00000;
+ clrStatusGreen = 0xFF00FF00;
+ clrStatusYellow = 0xFFE0E222;
+ clrStatusBlue = 0xFF3B96FD;
+ clrStatusTextFG = 0xFF000000;
+ // COMMONCOLORS
+ clrMesgBG1 = 0xEF2D435A;
+ clrMesgBG2 = 0xDF303F52;
+ clrMesgFG1 = 0xFFBABBC0;
+
+ // FOR MPEGBACKGROUNDCOVER
+ rows = 7;
+ mpgdif = 0;
+ localbackground = "";
+ streambackground = "";
+ localcover = "/music-default-cover.png";
+ streamcover = "/music-default-stream.png";
+ reloadmpeg = false;
+};
+
+cmgSkin::~cmgSkin() {
+}
+
+int cmgSkin::ParseSkin(const char *SkinName, bool ReloadMpeg) {
+
+ using namespace std;
+ ifstream filestr;
+ std::string line;
+ std::string Value;
+ std::string datei;
+ std::string skinname;
+
+ bool result=false;
+
+ reloadmpeg = ReloadMpeg;
+
+ skinname = SkinName;
+
+ localbackground = "";
+ streambackground = "";
+
+ datei = the_setup.ConfigDirectory;
+ datei = datei + "/themes/";
+ datei = datei + skinname;
+
+ dsyslog("music: Load themefile '%s'\n", datei.c_str());
+
+ filestr.open (datei.c_str());
+ if(filestr) {
+ while (getline(filestr, line, '\n')) {
+ int len = line.length();
+ string::size_type pos = line.find ("<value>",0);
+
+ if(pos != string::npos) {
+ Value = line.substr(len -10, len);
+
+ if (strstr(line.c_str(),"clrTopBG1")) clrTopBG1 = strtoul(Value.c_str(), NULL,16);
+ else if (strstr(line.c_str(),"clrTopTextFG1")) clrTopTextFG1 = strtoul(Value.c_str(), NULL,16);
+
+ else if (strstr(line.c_str(),"clrTopBG2")) clrTopBG2 = strtoul(Value.c_str(), NULL,16);
+ else if (strstr(line.c_str(),"clrTopTextFG2")) clrTopTextFG2 = strtoul(Value.c_str(), NULL,16);
+ else if (strstr(line.c_str(),"clrTopItemBG1")) clrTopItemBG1 = strtoul(Value.c_str(), NULL,16);
+ else if (strstr(line.c_str(),"clrTopItemInactiveFG")) clrTopItemInactiveFG = strtoul(Value.c_str(), NULL,16);
+ else if (strstr(line.c_str(),"clrTopItemActiveFG")) clrTopItemActiveFG = strtoul(Value.c_str(), NULL,16);
+
+ else if (strstr(line.c_str(),"clrListBG1")) clrListBG1 = strtoul(Value.c_str(), NULL,16);
+ else if (strstr(line.c_str(),"clrListBG2")) clrListBG2 = strtoul(Value.c_str(), NULL,16);
+ else if (strstr(line.c_str(),"clrListTextFG")) clrListTextFG = strtoul(Value.c_str(), NULL,16);
+ else if (strstr(line.c_str(),"clrListTextActiveFG")) clrListTextActiveFG = strtoul(Value.c_str(), NULL,16);
+ else if (strstr(line.c_str(),"clrListTextActiveBG")) clrListTextActiveBG = strtoul(Value.c_str(), NULL,16);
+ else if (strstr(line.c_str(),"clrListRating")) clrListRating = strtoul(Value.c_str(), NULL,16);
+
+ else if (strstr(line.c_str(),"clrInfoBG1")) clrInfoBG1 = strtoul(Value.c_str(), NULL,16);
+ else if (strstr(line.c_str(),"clrInfoBG2")) clrInfoBG2 = strtoul(Value.c_str(), NULL,16);
+ else if (strstr(line.c_str(),"clrInfoTextFG1")) clrInfoTextFG1 = strtoul(Value.c_str(), NULL,16);
+ else if (strstr(line.c_str(),"clrInfoTitleFG1")) clrInfoTitleFG1 = strtoul(Value.c_str(), NULL,16);
+ else if (strstr(line.c_str(),"clrInfoTextFG2")) clrInfoTextFG2 = strtoul(Value.c_str(), NULL,16);
+ else if (strstr(line.c_str(),"clrProgressBG1")) clrProgressBG1 = strtoul(Value.c_str(), NULL,16);
+ else if (strstr(line.c_str(),"clrProgressBG2")) clrProgressBG2 = strtoul(Value.c_str(), NULL,16);
+ else if (strstr(line.c_str(),"clrProgressbarFG")) clrProgressbarFG = strtoul(Value.c_str(), NULL,16);
+ else if (strstr(line.c_str(),"clrProgressbarBG")) clrProgressbarBG = strtoul(Value.c_str(), NULL,16);
+
+ else if (strstr(line.c_str(),"clrStatusBG")) clrStatusBG = strtoul(Value.c_str(), NULL,16);
+ else if (strstr(line.c_str(),"clrStatusRed")) clrStatusRed = strtoul(Value.c_str(), NULL,16);
+ else if (strstr(line.c_str(),"clrStatusGreen")) clrStatusGreen = strtoul(Value.c_str(), NULL,16);
+ else if (strstr(line.c_str(),"clrStatusYellow")) clrStatusYellow = strtoul(Value.c_str(), NULL,16);
+ else if (strstr(line.c_str(),"clrStatusBlue")) clrStatusBlue = strtoul(Value.c_str(), NULL,16);
+ else if (strstr(line.c_str(),"clrStatusTextFG")) clrStatusTextFG = strtoul(Value.c_str(), NULL,16);
+
+ else if (strstr(line.c_str(),"clrMesgBG1")) clrMesgBG1 = strtoul(Value.c_str(), NULL,16);
+ else if (strstr(line.c_str(),"clrMesgBG2")) clrMesgBG2 = strtoul(Value.c_str(), NULL,16);
+ else if (strstr(line.c_str(),"clrMesgFG1")) clrMesgFG1 = strtoul(Value.c_str(), NULL,16);
+
+ else if (strstr(line.c_str(),"localcover")) {
+ pos = line.rfind ("=",len);
+ if(pos != string::npos) {
+ Value = line.substr(pos +1,len);
+ localcover = Value.c_str();
+ }
+ }
+ else if (strstr(line.c_str(),"streamcover")) {
+ pos = line.rfind ("=",len);
+ if(pos != string::npos) {
+ Value = line.substr(pos +1,len);
+ streamcover = Value.c_str();
+ }
+ }
+ else if (strstr(line.c_str(),"localbackground")) {
+ pos = line.rfind ("=",len);
+ if(pos != string::npos) {
+ Value = line.substr(pos +1,len);
+ localbackground = Value.c_str();
+ }
+ }
+ else if (strstr(line.c_str(),"streambackground")) {
+ pos = line.rfind ("=",len);
+ if(pos != string::npos) {
+ Value = line.substr(pos +1,len);
+ streambackground = Value.c_str();
+ }
+ }
+ else if (strstr(line.c_str(),"rows")) {
+ pos = line.rfind ("=",len);
+ if(pos != string::npos) {
+ Value = line.substr(pos +1,len);
+ rows = atoi(Value.c_str());
+ }
+ }
+ else if (strstr(line.c_str(),"mpgdif")) {
+ pos = line.rfind ("=",len);
+ if(pos != string::npos) {
+ Value = line.substr(pos +1,len);
+ mpgdif = atoi(Value.c_str());
+ }
+ }
+ }
+ }
+
+ filestr.close();
+ result = true;
+ }
+
+ return result;
+}
+
+int cmgSkin::StoreSkin(const char *ThemeName) {
+ using namespace std;
+ ifstream filestr;
+ std::string line;
+ std::string datei;
+ std::string dateiout;
+ std::string themename;
+
+ bool res=false;
+
+ themename = ThemeName;
+
+ datei = the_setup.ConfigDirectory + "/themes/" + ThemeName;
+
+ dateiout = the_setup.ConfigDirectory + "/themes/current.colors";
+
+ if( FILE *f = fopen(dateiout.c_str(), "w")) {
+ filestr.open (datei.c_str());
+ if(filestr) {
+ while (getline(filestr, line, '\n')) {
+ line = line + "\n";
+ fprintf(f, line.c_str());
+ }
+ filestr.close();
+ }
+ res = true;
+ fclose(f);
+ }
+ else
+ res = false;
+
+ return res;
+}
diff --git a/mg_skin.h b/mg_skin.h
new file mode 100644
index 0000000..71ec331
--- /dev/null
+++ b/mg_skin.h
@@ -0,0 +1,77 @@
+/*
+ * Music plugin to VDR (C++)
+ *
+ * (C) 2006 Morone
+ *
+ * This code 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 code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef ___SKIN_H
+#define ___SKIN_H
+
+#include <string>
+
+class cmgSkin
+{
+ private:
+ // char *localbackground, *streambackground;
+ public:
+ cmgSkin(void);
+ virtual ~cmgSkin();
+ int ParseSkin(const char *SkinName, bool ReloadMpeg);
+ int StoreSkin(const char *ThemeName);
+ int clrTopBG1;
+ int clrTopTextFG1;
+ int clrTopBG2;
+ int clrTopTextFG2;
+ int clrTopItemBG1;
+ int clrTopItemInactiveFG;
+ int clrTopItemActiveFG;
+ int clrListBG1;
+ int clrListBG2;
+ int clrListTextFG;
+ int clrListTextActiveFG;
+ int clrListTextActiveBG;
+ int clrListRating;
+ int clrInfoBG1;
+ int clrInfoBG2;
+ int clrInfoTextFG1;
+ int clrInfoTitleFG1;
+ int clrInfoTextFG2;
+ int clrProgressBG1;
+ int clrProgressBG2;
+ int clrProgressbarFG;
+ int clrProgressbarBG;
+ int clrStatusBG;
+ int clrStatusRed;
+ int clrStatusGreen;
+ int clrStatusYellow;
+ int clrStatusBlue;
+ int clrStatusTextFG;
+ int clrMesgBG1;
+ int clrMesgBG2;
+ int clrMesgFG1;
+ int rows;
+ int mpgdif;
+ std::string localbackground;
+ std::string streambackground;
+ std::string localcover;
+ std::string streamcover;
+ bool reloadmpeg;
+};
+
+extern cmgSkin mgSkin;
+#endif //___SKIN_H
diff --git a/mg_tables.h b/mg_tables.h
new file mode 100644
index 0000000..c8a2595
--- /dev/null
+++ b/mg_tables.h
@@ -0,0 +1,783 @@
+
+//autogenerated by /home/wr/vdr-1.5.13/PLUGINS/src/muggle/scripts/gentables
+
+genres_t genres[] = {
+ { "b", 20, "Alternative" },
+ { "ba", 40, "Alt. Rock" },
+ { "bb", -1, "Art Rock" },
+ { "bc", 90, "Avantgarde" },
+ { "be", -1, "Experimental" },
+ { "bh", 6, "Grunge" },
+ { "bi", 131, "Indie" },
+ { "bm", 12, "Other" },
+ { "bn", 139, "Crossover" },
+ { "c", -1, "Books & Spoken" },
+ { "ca", -1, "Short Stories" },
+ { "cb", 57, "Comedy" },
+ { "cc", 77, "Musical" },
+ { "cd", -1, "Poetry" },
+ { "ce", 65, "Cabaret" },
+ { "cf", -1, "Religion" },
+ { "cg", 101, "Speech" },
+ { "ch", -1, "Stories/Fairytales" },
+ { "ci", -1, "Radio Play" },
+ { "cia", -1, "Literary Radio Play" },
+ { "cib", -1, "Thriller" },
+ { "e", 32, "Classical" },
+ { "ea", 104, "Chamber Music" },
+ { "eaa", 105, "Sonata" },
+ { "eb", -1, "Classical General" },
+ { "ec", -1, "Contemporary" },
+ { "eca", -1, "Contemp. Crossover" },
+ { "ecb", -1, "Electronic Classical" },
+ { "ecc", -1, "Experimental Classical" },
+ { "ecd", -1, "Minimal Music" },
+ { "ed", 24, "Soundtrack" },
+ { "ee", 33, "Instrumental" },
+ { "ef", -1, "Period Music" },
+ { "efa", -1, "Baroque" },
+ { "efb", -1, "Medieval" },
+ { "efc", -1, "Renaissance" },
+ { "efd", -1, "Romantic" },
+ { "efda", -1, "19th Century" },
+ { "eg", -1, "Solo Instruments" },
+ { "ega", -1, "Guitar" },
+ { "egb", -1, "Percussion" },
+ { "egc", -1, "Piano" },
+ { "eh", 106, "Symphony" },
+ { "ei", 28, "Vocal" },
+ { "eia", 97, "Chorus" },
+ { "eib", -1, "Ensembles" },
+ { "eic", 103, "Opera" },
+ { "f", 2, "Country" },
+ { "fa", -1, "Alternative Country" },
+ { "fb", 89, "Bluegrass" },
+ { "fd", -1, "Country Blues" },
+ { "fe", -1, "Country General" },
+ { "fg", -1, "Country and Western" },
+ { "fh", 80, "Folk" },
+ { "fha", -1, "Irish Folk" },
+ { "fi", -1, "Rockabilly" },
+ { "g", 98, "Easy Listening" },
+ { "gb", -1, "Lounge" },
+ { "gc", -1, "Love Songs" },
+ { "gca", 116, "Ballad" },
+ { "gd", -1, "Mood Music" },
+ { "ge", 10, "New Age" },
+ { "gf", -1, "Soft Rock" },
+ { "gfa", -1, "Acoustic Rock" },
+ { "gg", -1, "Schlager" },
+ { "gh", -1, "Soft Pop" },
+ { "gi", 45, "Meditative" },
+ { "gj", -1, "Pair Dance" },
+ { "gja", -1, "Walz" },
+ { "gjb", -1, "Tango" },
+ { "h", 102, "Chanson" },
+ { "ha", -1, "Singer-Songwriter" },
+ { "hb", 65, "Children's Music" },
+ { "i", 52, "Electronic" },
+ { "ia", 34, "Acid" },
+ { "ib", 26, "Ambient" },
+ { "ic", -1, "Breakbeat/Breaks" },
+ { "ica", -1, "Breakbeat" },
+ { "icb", -1, "Darkside" },
+ { "icc", 63, "Jungle" },
+ { "icd", -1, "Ragga" },
+ { "ice", 27, "Trip-Hop" },
+ { "id", 3, "Dance" },
+ { "ie", 127, "Drum & Bass" },
+ { "if", -1, "Electronica" },
+ { "ig", -1, "Envir. Soundscapes" },
+ { "ih", -1, "Experimental Elect." },
+ { "iha", -1, "Minimal Experimental" },
+ { "ihb", 39, "Noise" },
+ { "ii", 37, "Sound Clip" },
+ { "ij", 35, "House" },
+ { "ija", -1, "Acid House" },
+ { "ijb", -1, "Funk House" },
+ { "ijc", -1, "Hard House" },
+ { "ijd", -1, "Progressive House" },
+ { "ije", 124, "Euro-House" },
+ { "ijf", 128, "Club-House" },
+ { "ik", 19, "Industrial" },
+ { "il", 18, "Techno" },
+ { "ilb", -1, "Dub" },
+ { "ild", 126, "Goa" },
+ { "ile", -1, "Hardcore Techno" },
+ { "ilf", -1, "Illbient" },
+ { "ilg", -1, "Minimal" },
+ { "ilh", 25, "Euro-Techno" },
+ { "ili", 68, "Rave" },
+ { "ilj", 31, "Trance" },
+ { "im", 44, "Space Music" },
+ { "j", -1, "Hip Hop/Rap" },
+ { "ja", 7, "Hip-Hop" },
+ { "jb", 15, "Rap" },
+ { "jbb", 61, "Christian Rap" },
+ { "jbd", -1, "Hardcore Rap" },
+ { "jbf", 59, "Gangsta" },
+ { "k", -1, "Blues/R&B" },
+ { "ka", 0, "Blues" },
+ { "kaa", -1, "Acoustic Blues" },
+ { "kab", -1, "Blues Rock" },
+ { "kac", -1, "Blues Vocalist" },
+ { "kae", -1, "Electric Blues" },
+ { "kag", -1, "Jazz Blues" },
+ { "kb", 38, "Gospel" },
+ { "kc", -1, "Improvised" },
+ { "kd", 14, "R&B" },
+ { "ke", 42, "Soul" },
+ { "kea", -1, "Sweet Soul" },
+ { "l", 8, "Jazz" },
+ { "la", 73, "Acid Punk" },
+ { "lb", 85, "Bebob" },
+ { "lc", -1, "Dancefloor Jazz" },
+ { "lf", 30, "Jazz Fusion" },
+ { "lh", -1, "Jazz Vocals" },
+ { "lj", -1, "Ragtime" },
+ { "lk", -1, "Smooth Jazz" },
+ { "ll", 83, "Swing" },
+ { "lla", 96, "Big Band" },
+ { "llb", 76, "Retro-Swing" },
+ { "lm", -1, "Cool Jazz" },
+ { "ln", -1, "Ethno Jazz" },
+ { "lna", -1, "African Ethno Jazz" },
+ { "lnb", -1, "Arab Ethno Jazz" },
+ { "lnc", -1, "Cuban Jazz" },
+ { "lnd", -1, "Latin Jazz" },
+ { "lne", -1, "Far East Jazz" },
+ { "lo", -1, "Modern Jazz" },
+ { "lp", -1, "New Orleans Brass" },
+ { "m", -1, "Pop & Rock" },
+ { "ma", -1, "Country Rock" },
+ { "mb", 5, "Funk" },
+ { "mba", -1, "Acid Funk" },
+ { "mc", 9, "Metal" },
+ { "mcc", 22, "Death Metal" },
+ { "mcd", -1, "Doom Metal" },
+ { "mcf", -1, "Hard Core Metal" },
+ { "mcg", -1, "Heavy Metal" },
+ { "mck", -1, "Thrash Metal" },
+ { "md", 13, "Pop" },
+ { "mda", 99, "Acoustic" },
+ { "mdb", -1, "Synthesizer Pop" },
+ { "mdd", -1, "Latin Pop" },
+ { "mdg", 123, "A Capella" },
+ { "mdh", -1, "Neue Deutsche Welle" },
+ { "mdi", 4, "Disco" },
+ { "mdj", -1, "Dance Pop" },
+ { "mdja", -1, "Twist" },
+ { "mdk", -1, "Doo-Wop" },
+ { "me", 43, "Punk" },
+ { "mea", 121, "Hardcore/Punk Rock" },
+ { "meb", 71, "Lo-Fi/Garage" },
+ { "mec", -1, "Old School Punk" },
+ { "med", 21, "Ska" },
+ { "mf", 17, "Rock" },
+ { "mfb", -1, "Acid Rock" },
+ { "mfe", 81, "Folk/Rock" },
+ { "mfg", -1, "Groove Rock" },
+ { "mfh", -1, "Guitar Rock" },
+ { "mfha", 1, "Classic Rock" },
+ { "mfhb", -1, "Improv Rock" },
+ { "mfhc", 47, "Instrum. Rock" },
+ { "mfhf", -1, "Surf Rock" },
+ { "mfj", 66, "New Wave" },
+ { "mfk", 67, "Psychadelic" },
+ { "mfl", 78, "Rock & Roll" },
+ { "mfm", 94, "Symphonic Rock" },
+ { "mg", -1, "Rock En Espanol" },
+ { "n", -1, "World" },
+ { "na", 16, "Reggae" },
+ { "nb", -1, "Steel Drums" },
+ { "nc", -1, "World Popular" },
+ { "nca", -1, "African Pop" },
+ { "ncb", -1, "Oriental Pop" },
+ { "ncc", -1, "Scandinavian Ethnopop" },
+ { "ncd", -1, "Asian Pop" },
+ { "nce", -1, "Arabian Pop" },
+ { "ncf", -1, "European Ethnopop" },
+ { "ncg", -1, "Latin Pop" },
+ { "nch", -1, "Caribbean Pop" },
+ { "nd", 82, "National Folk" },
+ { "nda", -1, "African" },
+ { "ndaa", -1, "Mali Blues" },
+ { "ndb", -1, "Arabic" },
+ { "ndc", -1, "Asian" },
+ { "ndd", -1, "Bossa Nova" },
+ { "nde", -1, "Caribbean" },
+ { "ndf", 88, "Celtic" },
+ { "ndg", 53, "Pop-Folk" },
+ { "ndga", -1, "Jodel" },
+ { "ndh", -1, "France" },
+ { "ndi", -1, "Germany" },
+ { "ndj", -1, "India" },
+ { "ndk", -1, "Ireland" },
+ { "ndl", 86, "Latin" },
+ { "ndlb", -1, "Flamenco" },
+ { "ndlc", -1, "Mambo" },
+ { "ndld", -1, "Mariachi" },
+ { "ndle", 142, "Merengue" },
+ { "ndlg", 143, "Salsa" },
+ { "ndlh", 114, "Samba" },
+ { "ndm", 64, "Native American" },
+ { "ndn", -1, "Quebecois" },
+ { "ndo", -1, "Russian" },
+ { "ndp", -1, "South/Cent. American" },
+ { "ndq", -1, "Spain" },
+ { "ndr", 113, "Tango" },
+ { "y", 11, "Oldies" },
+ { "y2", 49, "Gothic" },
+ { "y6", 23, "Pranks" },
+ { "y9", 29, "Jazz+Funk" },
+ { "y12", 46, "Instrum. Pop" },
+ { "y14", 48, "Ethnic" },
+ { "y15", 50, "Darkwave" },
+ { "y16", 51, "Techno-Indust." },
+ { "y18", 54, "Eurodance" },
+ { "y19", 55, "Dream" },
+ { "y20", 56, "Southern Rock" },
+ { "y21", 58, "Cult" },
+ { "y22", 60, "Top 40" },
+ { "y23", 62, "Pop/Funk" },
+ { "y25", 69, "Showtunes" },
+ { "y26", 70, "Trailer" },
+ { "y27", 72, "Tribal" },
+ { "y29", 75, "Polka" },
+ { "y30", 79, "Hard Rock" },
+ { "y34", 87, "Revival" },
+ { "y36", 91, "Gothic Rock" },
+ { "y37", 92, "Progress. Rock" },
+ { "y38", 93, "Psychadel. Rock" },
+ { "y39", 95, "Slow Rock" },
+ { "y41", 100, "Humour" },
+ { "y42", 107, "Booty Bass" },
+ { "y43", 108, "Primus" },
+ { "y44", 109, "Porn Groove" },
+ { "y45", 111, "Slow Jam" },
+ { "y46", 112, "Club" },
+ { "y47", 115, "Folklore" },
+ { "y48", 117, "Power Ballad" },
+ { "y49", 118, "Rhythmic Soul" },
+ { "y50", 119, "Freestyle" },
+ { "y51", 120, "Duet" },
+ { "y52", 122, "Drum Solo" },
+ { "y55", 125, "Dance Hall" },
+ { "y58", 130, "Terror" },
+ { "y59", 132, "BritPop" },
+ { "y60", 133, "Negerpunk" },
+ { "y61", 134, "Polsk Punk" },
+ { "y62", 135, "Beat" },
+ { "y63", 136, "Christian Gangsta Rap" },
+ { "y64", 138, "Black Metal" },
+ { "y65", 140, "Contemporary Christian" },
+ { "y66", 141, "Christian Rock" },
+ { "y69", 145, "Anime" },
+ { "y70", 146, "Jpop" },
+ { "y71", 147, "Synthpop" },
+};
+
+lang_t languages[] = {
+ { "aar", "Afar" },
+ { "abk", "Abkhazian" },
+ { "ace", "Achinese" },
+ { "ach", "Acoli" },
+ { "ada", "Adangme" },
+ { "ady", "Adyghe; Adygei" },
+ { "afa", "Afro-Asiatic (Other)" },
+ { "afh", "Afrihili" },
+ { "afr", "Afrikaans" },
+ { "ain", "Ainu" },
+ { "aka", "Akan" },
+ { "akk", "Akkadian" },
+ { "ale", "Aleut" },
+ { "alg", "Algonquian languages" },
+ { "alt", "Southern Altai" },
+ { "amh", "Amharic" },
+ { "ang", "English, Old (ca. 450-1100)" },
+ { "anp", "Angika" },
+ { "apa", "Apache languages" },
+ { "ara", "Arabic" },
+ { "arc", "Official Aramaic (700-300 BCE); Imperial Aramaic (700-300 BCE)" },
+ { "arg", "Aragonese" },
+ { "arn", "Mapudungun; Mapuche" },
+ { "arp", "Arapaho" },
+ { "art", "Artificial (Other)" },
+ { "arw", "Arawak" },
+ { "asm", "Assamese" },
+ { "ast", "Asturian; Bable; Leonese; Asturleonese" },
+ { "ath", "Athapascan languages" },
+ { "aus", "Australian languages" },
+ { "ava", "Avaric" },
+ { "ave", "Avestan" },
+ { "awa", "Awadhi" },
+ { "aym", "Aymara" },
+ { "aze", "Azerbaijani" },
+ { "bad", "Banda languages" },
+ { "bai", "Bamileke languages" },
+ { "bak", "Bashkir" },
+ { "bal", "Baluchi" },
+ { "bam", "Bambara" },
+ { "ban", "Balinese" },
+ { "bas", "Basa" },
+ { "bat", "Baltic (Other)" },
+ { "bej", "Beja; Bedawiyet" },
+ { "bel", "Belarusian" },
+ { "bem", "Bemba" },
+ { "ben", "Bengali" },
+ { "ber", "Berber (Other)" },
+ { "bho", "Bhojpuri" },
+ { "bih", "Bihari" },
+ { "bik", "Bikol" },
+ { "bin", "Bini; Edo" },
+ { "bis", "Bislama" },
+ { "bla", "Siksika" },
+ { "bnt", "Bantu (Other)" },
+ { "tib", "Tibetan" },
+ { "bos", "Bosnian" },
+ { "bra", "Braj" },
+ { "bre", "Breton" },
+ { "btk", "Batak languages" },
+ { "bua", "Buriat" },
+ { "bug", "Buginese" },
+ { "bul", "Bulgarian" },
+ { "byn", "Blin; Bilin" },
+ { "cad", "Caddo" },
+ { "cai", "Central American Indian (Other)" },
+ { "car", "Galibi Carib" },
+ { "cat", "Catalan; Valencian" },
+ { "cau", "Caucasian (Other)" },
+ { "ceb", "Cebuano" },
+ { "cel", "Celtic (Other)" },
+ { "cze", "Czech" },
+ { "cha", "Chamorro" },
+ { "chb", "Chibcha" },
+ { "che", "Chechen" },
+ { "chg", "Chagatai" },
+ { "chk", "Chuukese" },
+ { "chm", "Mari" },
+ { "chn", "Chinook jargon" },
+ { "cho", "Choctaw" },
+ { "chp", "Chipewyan; Dene Suline" },
+ { "chr", "Cherokee" },
+ { "chu", "Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic" },
+ { "chv", "Chuvash" },
+ { "chy", "Cheyenne" },
+ { "cmc", "Chamic languages" },
+ { "cop", "Coptic" },
+ { "cor", "Cornish" },
+ { "cos", "Corsican" },
+ { "cpe", "Creoles and pidgins, English based (Other)" },
+ { "cpf", "Creoles and pidgins, French-based (Other)" },
+ { "cpp", "Creoles and pidgins, Portuguese-based (Other)" },
+ { "cre", "Cree" },
+ { "crh", "Crimean Tatar; Crimean Turkish" },
+ { "crp", "Creoles and pidgins (Other)" },
+ { "csb", "Kashubian" },
+ { "cus", "Cushitic (Other)" },
+ { "wel", "Welsh" },
+ { "dak", "Dakota" },
+ { "dan", "Danish" },
+ { "dar", "Dargwa" },
+ { "day", "Land Dayak languages" },
+ { "del", "Delaware" },
+ { "den", "Slave (Athapascan)" },
+ { "ger", "German" },
+ { "dgr", "Dogrib" },
+ { "din", "Dinka" },
+ { "div", "Divehi; Dhivehi; Maldivian" },
+ { "doi", "Dogri" },
+ { "dra", "Dravidian (Other)" },
+ { "dsb", "Lower Sorbian" },
+ { "dua", "Duala" },
+ { "dum", "Dutch, Middle (ca. 1050-1350)" },
+ { "dyu", "Dyula" },
+ { "dzo", "Dzongkha" },
+ { "efi", "Efik" },
+ { "egy", "Egyptian (Ancient)" },
+ { "eka", "Ekajuk" },
+ { "gre", "Greek, Modern (1453-)" },
+ { "elx", "Elamite" },
+ { "eng", "English" },
+ { "enm", "English, Middle (1100-1500)" },
+ { "epo", "Esperanto" },
+ { "est", "Estonian" },
+ { "baq", "Basque" },
+ { "ewe", "Ewe" },
+ { "ewo", "Ewondo" },
+ { "fan", "Fang" },
+ { "fao", "Faroese" },
+ { "per", "Persian" },
+ { "fat", "Fanti" },
+ { "fij", "Fijian" },
+ { "fil", "Filipino; Pilipino" },
+ { "fin", "Finnish" },
+ { "fiu", "Finno-Ugrian (Other)" },
+ { "fon", "Fon" },
+ { "fre", "French" },
+ { "frm", "French, Middle (ca. 1400-1600)" },
+ { "fro", "French, Old (842-ca. 1400)" },
+ { "frr", "Northern Frisian" },
+ { "frs", "Eastern Frisian" },
+ { "fry", "Western Frisian" },
+ { "ful", "Fulah" },
+ { "fur", "Friulian" },
+ { "gaa", "Ga" },
+ { "gay", "Gayo" },
+ { "gba", "Gbaya" },
+ { "gem", "Germanic (Other)" },
+ { "gez", "Geez" },
+ { "gil", "Gilbertese" },
+ { "gla", "Gaelic; Scottish Gaelic" },
+ { "gle", "Irish" },
+ { "glg", "Galician" },
+ { "glv", "Manx" },
+ { "gmh", "German, Middle High (ca. 1050-1500)" },
+ { "goh", "German, Old High (ca. 750-1050)" },
+ { "gon", "Gondi" },
+ { "gor", "Gorontalo" },
+ { "got", "Gothic" },
+ { "grb", "Grebo" },
+ { "grc", "Greek, Ancient (to 1453)" },
+ { "grn", "Guarani" },
+ { "gsw", "Swiss German; Alemannic" },
+ { "guj", "Gujarati" },
+ { "gwi", "Gwich´in" },
+ { "hai", "Haida" },
+ { "hat", "Haitian; Haitian Creole" },
+ { "hau", "Hausa" },
+ { "haw", "Hawaiian" },
+ { "heb", "Hebrew" },
+ { "her", "Herero" },
+ { "hil", "Hiligaynon" },
+ { "him", "Himachali" },
+ { "hin", "Hindi" },
+ { "hit", "Hittite" },
+ { "hmn", "Hmong" },
+ { "hmo", "Hiri Motu" },
+ { "scr", "Croatian" },
+ { "hsb", "Upper Sorbian" },
+ { "hun", "Hungarian" },
+ { "hup", "Hupa" },
+ { "arm", "Armenian" },
+ { "iba", "Iban" },
+ { "ibo", "Igbo" },
+ { "ido", "Ido" },
+ { "iii", "Sichuan Yi; Nuosu" },
+ { "ijo", "Ijo languages" },
+ { "iku", "Inuktitut" },
+ { "ile", "Interlingue; Occidental" },
+ { "ilo", "Iloko" },
+ { "ina", "Interlingua (International Auxiliary Language Association)" },
+ { "inc", "Indic (Other)" },
+ { "ind", "Indonesian" },
+ { "ine", "Indo-European (Other)" },
+ { "inh", "Ingush" },
+ { "ipk", "Inupiaq" },
+ { "ira", "Iranian (Other)" },
+ { "iro", "Iroquoian languages" },
+ { "ice", "Icelandic" },
+ { "ita", "Italian" },
+ { "jav", "Javanese" },
+ { "jbo", "Lojban" },
+ { "jpn", "Japanese" },
+ { "jpr", "Judeo-Persian" },
+ { "jrb", "Judeo-Arabic" },
+ { "kaa", "Kara-Kalpak" },
+ { "kab", "Kabyle" },
+ { "kac", "Kachin; Jingpho" },
+ { "kal", "Kalaallisut; Greenlandic" },
+ { "kam", "Kamba" },
+ { "kan", "Kannada" },
+ { "kar", "Karen languages" },
+ { "kas", "Kashmiri" },
+ { "geo", "Georgian" },
+ { "kau", "Kanuri" },
+ { "kaw", "Kawi" },
+ { "kaz", "Kazakh" },
+ { "kbd", "Kabardian" },
+ { "kha", "Khasi" },
+ { "khi", "Khoisan (Other)" },
+ { "khm", "Central Khmer" },
+ { "kho", "Khotanese" },
+ { "kik", "Kikuyu; Gikuyu" },
+ { "kin", "Kinyarwanda" },
+ { "kir", "Kirghiz; Kyrgyz" },
+ { "kmb", "Kimbundu" },
+ { "kok", "Konkani" },
+ { "kom", "Komi" },
+ { "kon", "Kongo" },
+ { "kor", "Korean" },
+ { "kos", "Kosraean" },
+ { "kpe", "Kpelle" },
+ { "krc", "Karachay-Balkar" },
+ { "krl", "Karelian" },
+ { "kro", "Kru languages" },
+ { "kru", "Kurukh" },
+ { "kua", "Kuanyama; Kwanyama" },
+ { "kum", "Kumyk" },
+ { "kur", "Kurdish" },
+ { "kut", "Kutenai" },
+ { "lad", "Ladino" },
+ { "lah", "Lahnda" },
+ { "lam", "Lamba" },
+ { "lao", "Lao" },
+ { "lat", "Latin" },
+ { "lav", "Latvian" },
+ { "lez", "Lezghian" },
+ { "lim", "Limburgan; Limburger; Limburgish" },
+ { "lin", "Lingala" },
+ { "lit", "Lithuanian" },
+ { "lol", "Mongo" },
+ { "loz", "Lozi" },
+ { "ltz", "Luxembourgish; Letzeburgesch" },
+ { "lua", "Luba-Lulua" },
+ { "lub", "Luba-Katanga" },
+ { "lug", "Ganda" },
+ { "lui", "Luiseno" },
+ { "lun", "Lunda" },
+ { "luo", "Luo (Kenya and Tanzania)" },
+ { "lus", "Lushai" },
+ { "mad", "Madurese" },
+ { "mag", "Magahi" },
+ { "mah", "Marshallese" },
+ { "mai", "Maithili" },
+ { "mak", "Makasar" },
+ { "mal", "Malayalam" },
+ { "man", "Mandingo" },
+ { "map", "Austronesian (Other)" },
+ { "mar", "Marathi" },
+ { "mas", "Masai" },
+ { "mdf", "Moksha" },
+ { "mdr", "Mandar" },
+ { "men", "Mende" },
+ { "mga", "Irish, Middle (900-1200)" },
+ { "mic", "Mi'kmaq; Micmac" },
+ { "min", "Minangkabau" },
+ { "mis", "Uncoded languages" },
+ { "mac", "Macedonian" },
+ { "mkh", "Mon-Khmer (Other)" },
+ { "mlg", "Malagasy" },
+ { "mlt", "Maltese" },
+ { "mnc", "Manchu" },
+ { "mni", "Manipuri" },
+ { "mno", "Manobo languages" },
+ { "moh", "Mohawk" },
+ { "mol", "Moldavian" },
+ { "mon", "Mongolian" },
+ { "mos", "Mossi" },
+ { "mao", "Maori" },
+ { "may", "Malay" },
+ { "mul", "Multiple languages" },
+ { "mun", "Munda languages" },
+ { "mus", "Creek" },
+ { "mwl", "Mirandese" },
+ { "mwr", "Marwari" },
+ { "bur", "Burmese" },
+ { "myn", "Mayan languages" },
+ { "myv", "Erzya" },
+ { "nah", "Nahuatl languages" },
+ { "nai", "North American Indian" },
+ { "nap", "Neapolitan" },
+ { "nau", "Nauru" },
+ { "nav", "Navajo; Navaho" },
+ { "nbl", "Ndebele, South; South Ndebele" },
+ { "nde", "Ndebele, North; North Ndebele" },
+ { "ndo", "Ndonga" },
+ { "nds", "Low German; Low Saxon; German, Low; Saxon, Low" },
+ { "nep", "Nepali" },
+ { "new", "Newari; Nepal Bhasa" },
+ { "nia", "Nias" },
+ { "nic", "Niger-Kordofanian (Other)" },
+ { "niu", "Niuean" },
+ { "dut", "Dutch; Flemish" },
+ { "nno", "Norwegian Nynorsk; Nynorsk, Norwegian" },
+ { "nob", "Norwegian Bokmål; Bokmål, Norwegian" },
+ { "nog", "Nogai" },
+ { "non", "Norse, Old" },
+ { "nor", "Norwegian" },
+ { "nqo", "N'ko" },
+ { "nso", "Northern Sotho, Pedi; Sepedi" },
+ { "nub", "Nubian languages" },
+ { "nwc", "Classical Newari; Old Newari; Classical Nepal Bhasa" },
+ { "nya", "Chichewa; Chewa; Nyanja" },
+ { "nym", "Nyamwezi" },
+ { "nyn", "Nyankole" },
+ { "nyo", "Nyoro" },
+ { "nzi", "Nzima" },
+ { "oci", "Occitan (post 1500); Provençal" },
+ { "oji", "Ojibwa" },
+ { "ori", "Oriya" },
+ { "orm", "Oromo" },
+ { "osa", "Osage" },
+ { "oss", "Ossetian; Ossetic" },
+ { "ota", "Turkish, Ottoman (1500-1928)" },
+ { "oto", "Otomian languages" },
+ { "paa", "Papuan (Other)" },
+ { "pag", "Pangasinan" },
+ { "pal", "Pahlavi" },
+ { "pam", "Pampanga; Kapampangan" },
+ { "pan", "Panjabi; Punjabi" },
+ { "pap", "Papiamento" },
+ { "pau", "Palauan" },
+ { "peo", "Persian, Old (ca. 600-400 B.C.)" },
+ { "phi", "Philippine (Other)" },
+ { "phn", "Phoenician" },
+ { "pli", "Pali" },
+ { "pol", "Polish" },
+ { "pon", "Pohnpeian" },
+ { "por", "Portuguese" },
+ { "pra", "Prakrit languages" },
+ { "pro", "Provençal, Old (to 1500)" },
+ { "pus", "Pushto" },
+ { "qaa-qtz", "Reserved for local use" },
+ { "que", "Quechua" },
+ { "raj", "Rajasthani" },
+ { "rap", "Rapanui" },
+ { "rar", "Rarotongan; Cook Island Maori" },
+ { "roa", "Romance (Other)" },
+ { "roh", "Romansh" },
+ { "rom", "Romany" },
+ { "rum", "Romanian" },
+ { "run", "Rundi" },
+ { "rup", "Aromanian; Arumanian; Macedo-Romanian" },
+ { "rus", "Russian" },
+ { "sad", "Sandawe" },
+ { "sag", "Sango" },
+ { "sah", "Yakut" },
+ { "sai", "South American Indian (Other)" },
+ { "sal", "Salishan languages" },
+ { "sam", "Samaritan Aramaic" },
+ { "san", "Sanskrit" },
+ { "sas", "Sasak" },
+ { "sat", "Santali" },
+ { "scn", "Sicilian" },
+ { "sco", "Scots" },
+ { "sel", "Selkup" },
+ { "sem", "Semitic (Other)" },
+ { "sga", "Irish, Old (to 900)" },
+ { "sgn", "Sign Languages" },
+ { "shn", "Shan" },
+ { "sid", "Sidamo" },
+ { "sin", "Sinhala; Sinhalese" },
+ { "sio", "Siouan languages" },
+ { "sit", "Sino-Tibetan (Other)" },
+ { "sla", "Slavic (Other)" },
+ { "slo", "Slovak" },
+ { "slv", "Slovenian" },
+ { "sma", "Southern Sami" },
+ { "sme", "Northern Sami" },
+ { "smi", "Sami languages (Other)" },
+ { "smj", "Lule Sami" },
+ { "smn", "Inari Sami" },
+ { "smo", "Samoan" },
+ { "sms", "Skolt Sami" },
+ { "sna", "Shona" },
+ { "snd", "Sindhi" },
+ { "snk", "Soninke" },
+ { "sog", "Sogdian" },
+ { "som", "Somali" },
+ { "son", "Songhai languages" },
+ { "sot", "Sotho, Southern" },
+ { "spa", "Spanish; Castilian" },
+ { "alb", "Albanian" },
+ { "srd", "Sardinian" },
+ { "srn", "Sranan Tongo" },
+ { "scc", "Serbian" },
+ { "srr", "Serer" },
+ { "ssa", "Nilo-Saharan (Other)" },
+ { "ssw", "Swati" },
+ { "suk", "Sukuma" },
+ { "sun", "Sundanese" },
+ { "sus", "Susu" },
+ { "sux", "Sumerian" },
+ { "swa", "Swahili" },
+ { "swe", "Swedish" },
+ { "syc", "Classical Syriac" },
+ { "syr", "Syriac" },
+ { "tah", "Tahitian" },
+ { "tai", "Tai (Other)" },
+ { "tam", "Tamil" },
+ { "tat", "Tatar" },
+ { "tel", "Telugu" },
+ { "tem", "Timne" },
+ { "ter", "Tereno" },
+ { "tet", "Tetum" },
+ { "tgk", "Tajik" },
+ { "tgl", "Tagalog" },
+ { "tha", "Thai" },
+ { "tig", "Tigre" },
+ { "tir", "Tigrinya" },
+ { "tiv", "Tiv" },
+ { "tkl", "Tokelau" },
+ { "tlh", "Klingon; tlhIngan-Hol" },
+ { "tli", "Tlingit" },
+ { "tmh", "Tamashek" },
+ { "tog", "Tonga (Nyasa)" },
+ { "ton", "Tonga (Tonga Islands)" },
+ { "tpi", "Tok Pisin" },
+ { "tsi", "Tsimshian" },
+ { "tsn", "Tswana" },
+ { "tso", "Tsonga" },
+ { "tuk", "Turkmen" },
+ { "tum", "Tumbuka" },
+ { "tup", "Tupi languages" },
+ { "tur", "Turkish" },
+ { "tut", "Altaic (Other)" },
+ { "tvl", "Tuvalu" },
+ { "twi", "Twi" },
+ { "tyv", "Tuvinian" },
+ { "udm", "Udmurt" },
+ { "uga", "Ugaritic" },
+ { "uig", "Uighur; Uyghur" },
+ { "ukr", "Ukrainian" },
+ { "umb", "Umbundu" },
+ { "und", "Undetermined" },
+ { "urd", "Urdu" },
+ { "uzb", "Uzbek" },
+ { "vai", "Vai" },
+ { "ven", "Venda" },
+ { "vie", "Vietnamese" },
+ { "vol", "Volapük" },
+ { "vot", "Votic" },
+ { "wak", "Wakashan languages" },
+ { "wal", "Walamo" },
+ { "war", "Waray" },
+ { "was", "Washo" },
+ { "wen", "Sorbian languages" },
+ { "wln", "Walloon" },
+ { "wol", "Wolof" },
+ { "xal", "Kalmyk; Oirat" },
+ { "xho", "Xhosa" },
+ { "yao", "Yao" },
+ { "yap", "Yapese" },
+ { "yid", "Yiddish" },
+ { "yor", "Yoruba" },
+ { "ypk", "Yupik languages" },
+ { "zap", "Zapotec" },
+ { "zbl", "Blissymbols; Blissymbolics; Bliss" },
+ { "zen", "Zenaga" },
+ { "zha", "Zhuang; Chuang" },
+ { "chi", "Chinese" },
+ { "znd", "Zande languages" },
+ { "zul", "Zulu" },
+ { "zun", "Zuni" },
+ { "zxx", "No linguistic content" },
+ { "zza", "Zaza; Dimili; Dimli; Kirdki; Kirmanjki; Zazaki" },
+};
+
+musictypes_t musictypes[] = {
+ { "soft/slow"},
+ { "medium"},
+ { "groovy"},
+ { "hard"},
+};
+
+sources_t sources[] = {
+ { "cd"},
+ { "radio"},
+ { "vinyl"},
+ { "tape"},
+ { "tv"},
+ { "video"},
+};
diff --git a/mg_thread_sync.c b/mg_thread_sync.c
index 33d30ad..fcf4e6e 100644
--- a/mg_thread_sync.c
+++ b/mg_thread_sync.c
@@ -5,59 +5,46 @@
static mgThreadSync* the_instance = NULL;
-mgThreadSync::mgThreadSync()
-{
+mgThreadSync::mgThreadSync() {
m_path = 0;
m_has_args = false;
}
-mgThreadSync* mgThreadSync::get_instance()
-{
- if( !the_instance )
- {
- the_instance = new mgThreadSync();
- }
-
-
- if( the_instance->Active() )
- {
- return NULL;
- }
- else
- {
- return the_instance;
- }
+mgThreadSync* mgThreadSync::get_instance() {
+ if( !the_instance ) {
+ the_instance = new mgThreadSync();
+ }
+
+ if( the_instance->Active() ) {
+ return NULL;
+ }
+ else {
+ return the_instance;
+ }
}
-void mgThreadSync::SetArguments( char * const * path_argv)
-{
- m_path = path_argv;
- m_has_args = true;
+void mgThreadSync::SetArguments(const char * const * path_argv) {
+ m_path = path_argv;
+ m_has_args = true;
}
-bool mgThreadSync::Sync(char * const * path_argv)
-{
- mgThreadSync *s = mgThreadSync::get_instance();
- if( s )
- {
- s->SetArguments( path_argv);
- s->Start();
- return true;
- }
- else
- {
- return false;
- }
+bool mgThreadSync::Sync(const char * const * path_argv) {
+ mgThreadSync *s = mgThreadSync::get_instance();
+ if( s ) {
+ s->SetArguments( path_argv);
+ s->Start();
+ return true;
+ }
+ else {
+ return false;
+ }
}
void
-mgThreadSync::Action()
-{
- if( m_has_args )
- {
- mgDb *s = GenerateDB(true);
- s->Sync( m_path );
- delete s;
- }
+mgThreadSync::Action() {
+ if( m_has_args ) {
+ mgDb *s = GenerateDB(true);
+ s->Sync( m_path );
+ delete s;
+ }
}
-
diff --git a/mg_thread_sync.h b/mg_thread_sync.h
index 1e51c10..e948a5b 100644
--- a/mg_thread_sync.h
+++ b/mg_thread_sync.h
@@ -16,25 +16,24 @@
class mgThreadSync : public cThread
{
- public:
- mgThreadSync();
- static mgThreadSync* get_instance();
+ public:
+ mgThreadSync();
+ static mgThreadSync* get_instance();
- bool Sync(char * const * path_argv=0);
+ bool Sync(const char * const * path_argv=0);
- protected:
- /*! \brief Runs the import routine as a separate thread
- */
- virtual void Action();
+ protected:
+ /*! \brief Runs the import routine as a separate thread
+ */
+ virtual void Action();
- private:
+ private:
- void SetArguments( char * const * path_argv);
-
- char * const *m_path;
- bool m_has_args;
- bool m_delete;
+ void SetArguments(const char * const * path_argv);
-};
+ const char * const *m_path;
+ bool m_has_args;
+ bool m_delete;
+};
#endif
diff --git a/mg_tools.c b/mg_tools.c
index 60fa26e..5d439db 100644
--- a/mg_tools.c
+++ b/mg_tools.c
@@ -25,145 +25,134 @@ static char buffer[MAX_BUFLEN];
static int DEBUG_LEVEL = 3;
-
void
-mgSetDebugLevel (int new_level)
-{
- DEBUG_LEVEL = new_level;
+mgSetDebugLevel (int new_level) {
+ DEBUG_LEVEL = new_level;
}
int
-msprintf(char **strp, const char *fmt, ...)
-{
- va_list ap;
+msprintf(char **strp, const char *fmt, ...) {
+ va_list ap;
int res;
- va_start (ap, fmt);
+ va_start (ap, fmt);
res=vmsprintf(strp,fmt,ap);
- va_end (ap);
+ va_end (ap);
return res;
}
int
-vmsprintf(char **strp, const char *fmt, va_list &ap)
-{
- int res;
- res=vasprintf (strp, fmt, ap);
- if (res<0)
- {
+vmsprintf(char **strp, const char *fmt, va_list &ap) {
+ *strp=0;
+ int res=vasprintf (strp, fmt, ap);
+ if (res<0) {
*strp=strdup("???,see logfile");
mgError("vasprintf() returns %d. This probably means illformed UTF-8 characters."
- " Please convert your file names to UTF-8",fmt,res);
+ " Please convert your file names to UTF-8",fmt,res);
}
return res;
}
void
-mgDebug (int level, const char *fmt, ...)
-{
-
- if (level <= DEBUG_LEVEL)
- {
- va_list ap;
- va_start (ap, fmt);
- vsnprintf (buffer, sizeof(buffer), fmt, ap);
- dsyslog("%s",buffer);
- va_end (ap);
- }
+mgDebug (int level, const char *fmt, ...) {
+
+ if (level <= DEBUG_LEVEL) {
+ va_list ap;
+ va_start (ap, fmt);
+#ifdef HAVE_PG
+ buffer[0]='P';
+#elif HAVE_SQLITE
+ buffer[0]='S';
+#else
+ buffer[0]='M';
+#endif
+ buffer[1]=' ';
+ vsnprintf (buffer+2, sizeof(buffer)-2, fmt, ap);
+ dsyslog("%s",buffer);
+ va_end (ap);
+ }
}
-
void
-mgDebug (const char *fmt, ...)
-{
- va_list ap;
- va_start (ap, fmt);
- mgDebug (1, fmt, ap);
- va_end (ap);
+mgDebug (const char *fmt, ...) {
+ va_list ap;
+ va_start (ap, fmt);
+ mgDebug (1, fmt, ap);
+ va_end (ap);
}
extern void showmessage(int duration,const char*,...);
void
-mgWarning (const char *fmt, ...)
-{
+mgWarning (const char *fmt, ...) {
- va_list ap;
- va_start (ap, fmt);
- vsnprintf (buffer, sizeof(buffer), fmt, ap);
- isyslog("%s",buffer);
- showmessage(0,buffer);
- va_end (ap);
+ va_list ap;
+ va_start (ap, fmt);
+ vsnprintf (buffer, sizeof(buffer), fmt, ap);
+ isyslog("%s",buffer);
+ showmessage(0,buffer);
+ va_end (ap);
}
-
void
-mgError (const char *fmt, ...)
-{
- va_list ap;
- va_start (ap, fmt);
- vsnprintf (buffer, sizeof(buffer), fmt, ap);
- esyslog("%s",buffer);
- showmessage(0,buffer);
- va_end (ap);
+mgError (const char *fmt, ...) {
+ va_list ap;
+ va_start (ap, fmt);
+ vsnprintf (buffer, sizeof(buffer), fmt, ap);
+ esyslog("%s",buffer);
+ showmessage(0,buffer);
+ va_end (ap);
}
-
std::string trim(std::string const& source, char const* delims ) {
- std::string result(source);
- std::string::size_type index = result.find_last_not_of(delims);
- if(index != std::string::npos)
- result.erase(++index);
- index = result.find_first_not_of(delims);
- if(index != std::string::npos)
- result.erase(0, index);
- else
- result.erase();
- return result;
+ std::string result(source);
+ std::string::size_type index = result.find_last_not_of(delims);
+ if(index != std::string::npos)
+ result.erase(++index);
+ index = result.find_first_not_of(delims);
+ if(index != std::string::npos)
+ result.erase(0, index);
+ else
+ result.erase();
+ return result;
}
-
char *
-SeparateFolders(const char *filename, char * folders[], unsigned int fcount)
-{
- for (unsigned int i=0;i<fcount;i++)
- folders[i]="";
- char *fbuf=strdup(filename);
- char *slash=fbuf-1;
- for (unsigned int i=0;i<fcount;i++)
- {
- char *p=slash+1;
- slash=strchr(p,'/');
- if (!slash)
- break;
- folders[i]=p;
- *slash=0;
- }
- return fbuf;
+SeparateFolders(const char *filename, char * folders[], unsigned int fcount) {
+ static char empty[1];
+ empty[0]=0;
+ for (unsigned int i=0;i<fcount;i++)
+ folders[i]=empty;
+ char *fbuf=strdup(filename);
+ char *slash=fbuf-1;
+ for (unsigned int i=0;i<fcount;i++) {
+ char *p=slash+1;
+ slash=strchr(p,'/');
+ if (!slash)
+ break;
+ folders[i]=p;
+ *slash=0;
+ }
+ return fbuf;
}
string&
-addsep (string & s, string sep, string n)
-{
- if (!n.empty ())
- {
+addsep (string & s, string sep, string n) {
+ if (!n.empty ()) {
if (!s.empty ())
- s.append (sep);
+ s.append (sep);
s.append (n);
}
return s;
}
-
string
-comma (string & s, string n)
-{
- return addsep (s, ",", n);
+comma (string & s, string n) {
+ return addsep (s, ",", n);
}
//! \brief converts long to string
string
-itos (int i)
-{
+itos (int i) {
std::stringstream s;
s << i;
return s.str ();
@@ -171,16 +160,14 @@ itos (int i)
//! \brief convert long to string
string
-ltos (long l)
-{
+ltos (long l) {
std::stringstream s;
s << l;
return s.str ();
}
char *
-extension(const char *filename)
-{
+extension(const char *filename) {
char *dot = strrchr(filename,'.');
if (!dot)
dot = strrchr(filename,0)-1;
@@ -188,32 +175,54 @@ extension(const char *filename)
}
bool
-notempty(const char *s)
-{
+notempty(const char *s) {
if (!s)
return false;
else
return strlen(s);
}
-bool samedir( const char *d1, const char *d2 )
-{
- int path_max;
-
+bool samedir( const char *d1, const char *d2 ) {
+ int path_max;
+
#ifdef PATH_MAX
- path_max = PATH_MAX;
+ path_max = PATH_MAX;
#else
- path_max = pathconf ( "/", _PC_PATH_MAX );
- if (path_max <= 0)
- {
- path_max = 4096;
- }
+ path_max = pathconf ( "/", _PC_PATH_MAX );
+ if (path_max <= 0) {
+ path_max = 4096;
+ }
#endif
-
- char rp1[path_max], rp2[path_max];
- realpath(d1, rp1);
- realpath(d2, rp2);
-
- return (!strcmp( rp1, rp2 ) );
+ char rp1[path_max], rp2[path_max];
+
+ realpath(d1, rp1);
+ realpath(d2, rp2);
+
+ return (!strcmp( rp1, rp2 ) );
+}
+
+bool mkdir_p(const char *s) {
+ char *slash=strrchr(s,'/');
+ if (!slash) return false;
+ char *sc = strdup(s);
+ *strrchr(sc,'/')=0; // cut off the filename
+ char *p = sc;
+ int mode;
+ while (*p) {
+ slash=strchr(p+1,'/');
+ if (slash) *slash=0;
+ mode=R_OK|X_OK|W_OK;
+ if (slash) if (strchr(slash+1,'/'))
+ mode=R_OK|X_OK;
+ if (access(sc,mode)) {
+ mkdir(sc,0755);
+ mgDebug(5,"Made directory %s",sc);
+ }
+ if (!slash) break;
+ *slash='/';
+ p=slash+1;
+ }
+ free(sc);
+ return true;
}
diff --git a/mg_tools.h b/mg_tools.h
index f6dd7b0..1b11944 100644
--- a/mg_tools.h
+++ b/mg_tools.h
@@ -30,7 +30,7 @@ void mgSetDebugLevel (int new_level);
void mgDebug (int level, const char *fmt, ...);
void mgDebug (const char *fmt, ...);
void mgWarning (const char *fmt, ...);
-void mgError (const char *fmt, ...);
+void mgError (const char *fmt, ...);
int msprintf(char **strp, const char *fmt, ...);
int vmsprintf(char **strp, const char *fmt, va_list &ap);
//@}
@@ -50,20 +50,18 @@ int vmsprintf(char **strp, const char *fmt, va_list &ap);
*/
class mgLog
{
- public:
- mgLog (string methodname):m_methodname (methodname)
- {
- mgDebug("%s entered",m_methodname.c_str());
- };
+ public:
+ mgLog (string methodname):m_methodname (methodname) {
+ mgDebug(1,"%s entered",m_methodname.c_str());
+ };
- ~mgLog ()
- {
- mgDebug("%s terminated",m_methodname.c_str());
- }
+ ~mgLog () {
+ mgDebug(1,"%s terminated",m_methodname.c_str());
+ }
- private:
+ private:
- string m_methodname;
+ string m_methodname;
};
@@ -90,5 +88,8 @@ bool notempty(const char *s);
//! \brief check whether two directory names point to the identical directory
bool samedir(const char *s1, const char *s2);
+#endif /* _MUGGLE_TOOLS_H */
-#endif /* _MUGGLE_TOOLS_H */
+//! \brief recursive mkdir, like shell "mkdir -p"
+// \param s this must be a filename
+bool mkdir_p(const char *s);
diff --git a/mg_valmap.c b/mg_valmap.c
index 6e745d5..6e31e88 100644
--- a/mg_valmap.c
+++ b/mg_valmap.c
@@ -18,7 +18,7 @@ void mgValmap::Read(FILE *f) {
while (fgets(line,1000,f)) {
if (strncmp(line,prefix,strlen(prefix))) continue;
if (line[strlen(line)-1]=='\n')
- line[strlen(line)-1]=0;
+ line[strlen(line)-1]=0;
char *name = line + strlen(prefix);
char *eq = strchr(name,'=');
if (!eq) continue;
@@ -79,8 +79,7 @@ void mgValmap::put(const bool value,const char* name, ...) {
va_end(ap);
}
-void mgValmap::put(const char* value, const char* name, ...)
-{
+void mgValmap::put(const char* value, const char* name, ...) {
if (!value) return;
va_list ap;
va_start(ap, name);
@@ -89,8 +88,7 @@ void mgValmap::put(const char* value, const char* name, ...)
}
string
-mgValmap::getstr(const char* name, ...)
-{
+mgValmap::getstr(const char* name, ...) {
va_list ap;
va_start(ap, name);
string result = my_get(name, ap);
@@ -99,45 +97,40 @@ mgValmap::getstr(const char* name, ...)
}
bool
-mgValmap::getbool(const char* name, ...)
-{
+mgValmap::getbool(const char* name, ...) {
va_list ap;
va_start(ap, name);
bool result = my_get(name, ap)=="true";
va_end(ap);
return result;
}
-
+
long
-mgValmap::getlong(const char* name, ...)
-{
+mgValmap::getlong(const char* name, ...) {
va_list ap;
va_start(ap, name);
long result = atol(my_get(name, ap).c_str());
va_end(ap);
return result;
}
-
+
unsigned int
-mgValmap::getuint(const char* name, ...)
-{
+mgValmap::getuint(const char* name, ...) {
va_list ap;
va_start(ap, name);
unsigned int result = atol(my_get(name, ap).c_str());
va_end(ap);
return result;
}
-
-void mgValmap::my_put(const string value, const char* name, va_list& ap)
-{
+
+void mgValmap::my_put(const string value, const char* name, va_list& ap) {
char buffer[600];
vsnprintf(buffer, 599, name, ap);
(*this)[string(buffer)] = value;
}
string
-mgValmap::my_get(const char *name, va_list& ap)
-{
+mgValmap::my_get(const char *name, va_list& ap) {
char buffer[600];
vsnprintf(buffer, 599, name, ap);
return (*this)[buffer];
diff --git a/mg_valmap.h b/mg_valmap.h
index d2c70d6..8a279f8 100644
--- a/mg_valmap.h
+++ b/mg_valmap.h
@@ -8,8 +8,9 @@
using namespace std;
-//! \brief a map for reading / writing configuration data.
-class mgValmap : public map<string,string> {
+//! \brief a map for reading / writing configuration data.
+class mgValmap : public map<string,string>
+{
private:
const char *m_key;
public:
@@ -33,14 +34,14 @@ class mgValmap : public map<string,string> {
void put(unsigned int value, const char*name, ...);
//! \brief enter a bool value
void put(bool value, const char*name, ...);
- //! \brief return a string
- string getstr(const char* name, ...);
- //! \brief return a bool
- bool getbool(const char* name, ...);
- //! \brief return a long
- long getlong(const char* name, ...);
- //! \brief return an unsigned int
- unsigned int getuint(const char* name, ...);
+ //! \brief return a string
+ string getstr(const char* name, ...);
+ //! \brief return a bool
+ bool getbool(const char* name, ...);
+ //! \brief return a long
+ long getlong(const char* name, ...);
+ //! \brief return an unsigned int
+ unsigned int getuint(const char* name, ...);
private:
void my_put(const string value, const char *name, va_list& ap);
string my_get(const char *name, va_list& ap);
diff --git a/muggle.c b/muggle.c
index be65d3a..7034147 100644
--- a/muggle.c
+++ b/muggle.c
@@ -14,167 +14,149 @@
#include "vdr_menu.h"
#include "vdr_setup.h"
+#include "vdr_player.h"
#include "mg_tools.h"
-#include "i18n.h"
+#include <vdr/i18n.h>
#define __STL_CONFIG_H
#include <vdr/tools.h>
#include <vdr/config.h>
-static const char VERSION[] = "0.1.12";
+static const char VERSION[] = "0.2.0";
static const char DESCRIPTION[] = "Media juggle plugin for VDR";
static const char MAINMENUENTRY[] = "Muggle";
const char *
-mgMuggle::Version (void)
-{
- return VERSION;
+mgMuggle::Version (void) {
+ return VERSION;
}
-
const char *
-mgMuggle::Description (void)
-{
- return tr(DESCRIPTION);
+mgMuggle::Description (void) {
+ return tr(DESCRIPTION);
}
-
const char *
-mgMuggle::MainMenuEntry (void)
-{
- return tr(MAINMENUENTRY);
+mgMuggle::MainMenuEntry (void) {
+ return tr(MAINMENUENTRY);
}
-
-mgMuggle::mgMuggle (void)
-{
+mgMuggle::mgMuggle (void) {
}
-
void
-mgMuggle::Stop (void)
-{
+mgMuggle::Stop (void) {
delete DbServer;
DbServer = 0;
}
-
const char *
-mgMuggle::CommandLineHelp (void)
-{
- return the_setup.HelpText();
+mgMuggle::CommandLineHelp (void) {
+ return the_setup.HelpText();
}
-
-bool mgMuggle::ProcessArgs (int argc, char *argv[])
-{
- return the_setup.ProcessArguments(argc,argv);
+bool mgMuggle::ProcessArgs (int argc, char *argv[]) {
+ return the_setup.ProcessArguments(argc,argv);
}
-bool mgMuggle::Initialize (void)
-{
-// Initialize any background activities the plugin shall perform.
- return true;
+bool mgMuggle::Initialize (void) {
+ // Initialize any background activities the plugin shall perform.
+ return true;
}
-
-bool mgMuggle::Start (void)
-{
-// Start any background activities the plugin shall perform.
- return true;
+bool mgMuggle::Start (void) {
+ // Start any background activities the plugin shall perform.
+ return true;
}
-
void
-mgMuggle::Housekeeping (void)
-{
-// Perform any cleanup or other regular tasks.
+mgMuggle::Housekeeping (void) {
+ // Perform any cleanup or other regular tasks.
}
-
cOsdObject *
-mgMuggle::MainMenuAction (void)
-{
-// Perform the action when selected from the main VDR menu.
- return new mgMainMenu ();
+mgMuggle::MainMenuAction (void) {
+ mgDebug("ich bin mgMuggle::MainMenuAction");
+ the_setup.ConfigDirectory=strdup(ConfigDirectory("muggle"));
+ // Perform the action when selected from the main VDR menu.
+ return new mgSelOsd ();
}
-
cMenuSetupPage *
-mgMuggle::SetupMenu (void)
-{
- return new mgMenuSetup ();
+mgMuggle::SetupMenu (void) {
+ return new mgMenuSetup ();
}
-
-bool mgMuggle::SetupParse (const char *Name, const char *Value)
-{
- if (!strcasecmp (Name, "InitLoopMode"))
- the_setup.InitLoopMode = atoi (Value);
- else if (!strcasecmp (Name, "InitShuffleMode"))
- the_setup.InitShuffleMode = atoi (Value);
- else if (!strcasecmp (Name, "AudioMode"))
- the_setup.AudioMode = atoi (Value);
- else if (!strcasecmp (Name, "DisplayMode"))
- the_setup.DisplayMode = atoi (Value);
- else if (!strcasecmp (Name, "BackgrMode"))
- the_setup.BackgrMode = atoi (Value);
- else if (!strcasecmp (Name, "TargetLevel"))
- the_setup.TargetLevel = atoi (Value);
- else if (!strcasecmp (Name, "LimiterLevel"))
- the_setup.LimiterLevel = atoi (Value);
- else if (!strcasecmp (Name, "Only48kHz"))
- the_setup.Only48kHz = atoi (Value);
- else if (!strcasecmp (Name, "DeleteStaleReferences"))
- the_setup.DeleteStaleReferences = atoi (Value);
- else if (!strcasecmp (Name, "ImageShowDuration"))
- the_setup.ImageShowDuration = atoi (Value);
- else if (!strcasecmp (Name, "ImageCacheDir"))
- strn0cpy (the_setup.ImageCacheDir, Value, MAX_PATH);
- else if (!strcasecmp (Name, "UseStillPicture"))
- the_setup.UseDeviceStillPicture = atoi (Value);
- else
- return false;
-
- return true;
+bool mgMuggle::SetupParse (const char *Name, const char *Value) {
+ if (!strcasecmp (Name, "InitLoopMode"))
+ the_setup.InitLoopMode = atoi (Value);
+ else if (!strcasecmp (Name, "InitShuffleMode"))
+ the_setup.InitShuffleMode = atoi (Value);
+ else if (!strcasecmp (Name, "AudioMode"))
+ the_setup.AudioMode = atoi (Value);
+ else if (!strcasecmp (Name, "DisplayMode"))
+ the_setup.DisplayMode = atoi (Value);
+ else if (!strcasecmp (Name, "BackgrMode"))
+ the_setup.BackgrMode = atoi (Value);
+ else if (!strcasecmp (Name, "TargetLevel"))
+ the_setup.TargetLevel = atoi (Value);
+ else if (!strcasecmp (Name, "LimiterLevel"))
+ the_setup.LimiterLevel = atoi (Value);
+ else if (!strcasecmp (Name, "Only48kHz"))
+ the_setup.Only48kHz = atoi (Value);
+ else if (!strcasecmp (Name, "DeleteStaleReferences"))
+ the_setup.DeleteStaleReferences = atoi (Value);
+ else if (!strcasecmp (Name, "ImageShowDuration"))
+ the_setup.ImageShowDuration = atoi (Value);
+ else if (!strcasecmp (Name, "ImageCacheDir")) // old version
+ strn0cpy (the_setup.CacheDir, Value, MAX_PATH);
+ else if (!strcasecmp (Name, "CacheDir"))
+ strn0cpy (the_setup.CacheDir, Value, MAX_PATH);
+ else if (!strcasecmp (Name, "UseStillPicture"))
+ the_setup.UseDeviceStillPicture = atoi (Value);
+ else if (!strcasecmp(Name, "Jumptime"))
+ the_setup.Jumptime = atoi(Value);
+ else if (!strcasecmp(Name, "ArtistFirst"))
+ the_setup.ArtistFirst = atoi(Value);
+#ifdef USE_BITMAP
+ else if (!strcasecmp(Name, "ImgAlpha"))
+ the_setup.ImgAlpha = atoi(Value);
+#endif
+ else
+ return false;
+
+ return true;
}
-bool mgMuggle::Service( const char *Id, void *Data )
-{
- bool result = false;
-
- if( !strcmp( Id, "ReplayDirectoryImages" ) )
- {
- if( Data )
- {
- // check whether there is a current player and signal the new image playlist to it
- mgPlayerControl *c = PlayerControl();
- if( c )
- {
- std::ostringstream os;
- os << "Found running muggle player to display image playlist in " << (char *) Data;
- isyslog("%s",os.str().c_str());
-
- c->NewImagePlaylist( (char*) Data );
- isyslog("New image playlist signaled.");
-
- result = true;
- }
- // return false otherwise (never start an extra player for this)
- else
- {
- result = false;
- }
+bool mgMuggle::Service( const char *Id, void *Data ) {
+ bool result = false;
+
+ if( !strcmp( Id, "ReplayDirectoryImages" ) ) {
+ if( Data ) {
+ // check whether there is a current player and signal the new image playlist to it
+ mgPlayerControl *c = PlayerControl();
+ if( c ) {
+ std::ostringstream os;
+ os << "Found running muggle player to display image playlist in " << (char *) Data;
+ isyslog("%s",os.str().c_str());
+
+ c->NewImagePlaylist( (char*) Data );
+ isyslog("New image playlist signaled.");
+
+ result = true;
+ }
+ // return false otherwise (never start an extra player for this)
+ else {
+ result = false;
+ }
+ }
+ else {
+ // generally we can handle this
+ result = true;
+ }
}
- else
- {
- // generally we can handle this
- result = true;
- }
- }
-
- return result;
-}
+ return result;
+}
-VDRPLUGINCREATOR (mgMuggle); // Don't touch this!
+VDRPLUGINCREATOR (mgMuggle); // Don't touch this!
diff --git a/muggle.h b/muggle.h
index 2d29f00..9138b15 100644
--- a/muggle.h
+++ b/muggle.h
@@ -41,35 +41,35 @@ class mgMainMenu;
class mgMuggle:public cPlugin
{
- public:
+ public:
- mgMuggle (void);
+ mgMuggle (void);
- virtual const char *Version (void);
+ virtual const char *Version (void);
- virtual const char *Description (void);
+ virtual const char *Description (void);
- virtual const char *CommandLineHelp (void);
+ virtual const char *CommandLineHelp (void);
- virtual bool ProcessArgs (int argc, char *argv[]);
+ virtual bool ProcessArgs (int argc, char *argv[]);
- virtual bool Initialize (void);
+ virtual bool Initialize (void);
- virtual bool Start (void);
+ virtual bool Start (void);
- virtual void Stop (void);
+ virtual void Stop (void);
- virtual void Housekeeping (void);
+ virtual void Housekeeping (void);
- virtual const char *MainMenuEntry (void);
+ virtual const char *MainMenuEntry (void);
- virtual cOsdObject *MainMenuAction (void);
+ virtual cOsdObject *MainMenuAction (void);
- virtual cMenuSetupPage *SetupMenu (void);
+ virtual cMenuSetupPage *SetupMenu (void);
- virtual bool SetupParse (const char *Name, const char *Value);
+ virtual bool SetupParse (const char *Name, const char *Value);
- virtual bool Service(const char *Id, void *Data = NULL);
+ virtual bool Service(const char *Id, void *Data = NULL);
};
#endif
diff --git a/mugglei.c b/mugglei.c
index d1b93c1..6aded67 100644
--- a/mugglei.c
+++ b/mugglei.c
@@ -29,13 +29,11 @@
#include "mg_setup.h"
#include "mg_db.h"
-
using namespace std;
-int SysLogLevel = 1;
+int SysLogLevel = 9;
-void showmessage(int duration,const char *msg,...)
-{
+void showmessage(int duration,const char *msg,...) {
va_list ap;
va_start(ap,msg);
vfprintf(stderr,msg,ap);
@@ -43,95 +41,79 @@ void showmessage(int duration,const char *msg,...)
va_end(ap);
}
-void syslog_with_tid(int priority, const char *format, ...)
-{
- va_list ap;
- va_start(ap, format);
- vsyslog(priority, format, ap);
- va_end(ap);
+void syslog_with_tid(int priority, const char *format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ vsyslog(priority, format, ap);
+ va_end(ap);
}
-void showimportcount(unsigned int importcount,bool final=false)
-{
+void showimportcount(unsigned int importcount,bool final=false) {
if (final)
mgDebug(1,"Imported %d tracks",importcount);
}
bool
-create_question()
-{
- return the_setup.CreateMode;
+create_question() {
+ return the_setup.CreateMode;
}
void
-import()
-{
-}
-
-const char *I18nTranslate(const char *s,const char *Plugin)
-{
- return s;
+import() {
}
bool
-path_within_tld()
-{
+path_within_tld() {
char path[5000];
- if (!getcwd(path,4999))
- {
+ if (!getcwd(path,4999)) {
std::cout << "Path too long" << std::endl;
exit (1);
}
- int tldlen = strlen(the_setup.ToplevelDir);
+ int tldlen = strlen(the_setup.ToplevelDir);
strcat(path,"/");
int pathlen = strlen(path);
if (pathlen<tldlen)
return false;
- return !strncmp(path,the_setup.ToplevelDir,tldlen);
+ return !strncmp(path,the_setup.ToplevelDir,tldlen);
}
-
static void
-usage()
-{
- std::cout << "mugglei -- import helper for Muggle VDR plugin" << std::endl;
- std::cout << "(C) Lars von Wedel, Wolfgang Rohdewald" << std::endl;
- std::cout << "This is free software; see the source for copying conditions." << std::endl;
- std::cout << "" << std::endl;
- std::cout << "Usage: mugglei [OPTION]... [FILE]..." << std::endl;
- std::cout << "" << std::endl;
- std::cout << " all FILE arguments will be imported. If they are directories, their content is imported"<< std::endl;
- std::cout << "" << std::endl;
- std::cout << "Only files ending in .flac, .mp3, .ogg (ignoring case) will be imported" << std::endl;
- std::cout << "" << std::endl;
- std::cout << "Options:" << std::endl;
- std::cout << the_setup.HelpText();
-
- exit( 2 );
+usage() {
+ std::cout << "mugglei -- import helper for Muggle VDR plugin" << std::endl;
+ std::cout << "(C) Lars von Wedel, Wolfgang Rohdewald" << std::endl;
+ std::cout << "This is free software; see the source for copying conditions." << std::endl;
+ std::cout << "" << std::endl;
+ std::cout << "Usage: mugglei [OPTION]... [FILE]..." << std::endl;
+ std::cout << "" << std::endl;
+ std::cout << " all FILE arguments will be imported. If they are directories, their content is imported"<< std::endl;
+ std::cout << "" << std::endl;
+ std::cout << "Only files ending in .flac, .mp3, .ogg (ignoring case) will be imported" << std::endl;
+ std::cout << "" << std::endl;
+ std::cout << "Options:" << std::endl;
+ std::cout << the_setup.HelpText();
+
+ exit( 2 );
}
-int main( int argc, char *argv[] )
-{
+int main( int argc, char *argv[] ) {
setlocale(LC_ALL,"");
the_setup.SetMugglei();
mgSetDebugLevel(1);
-
- if( argc < 2 )
- usage();
-
- if (!the_setup.ProcessArguments(argc,argv))
- usage();
-
- if (!path_within_tld())
- {
- std::cout << "you should be in " << the_setup.ToplevelDir
- << " or below" << std::endl;
- exit( 2 );
- }
- if (optind==argc)
- usage();
- mgDb *sync = GenerateDB();
- sync->Sync(argv+optind);
- delete sync;
-}
+ if( argc < 2 )
+ usage();
+
+ if (!the_setup.ProcessArguments(argc,argv))
+ usage();
+
+ if (!path_within_tld()) {
+ std::cout << "you should be in " << the_setup.ToplevelDir
+ << " or below" << std::endl;
+ exit( 2 );
+ }
+ if (optind==argc)
+ usage();
+ mgDb *sync = GenerateDB();
+ sync->Sync(argv+optind);
+ delete sync;
+}
diff --git a/pcmplayer.c b/pcmplayer.c
new file mode 100644
index 0000000..2142fc9
--- /dev/null
+++ b/pcmplayer.c
@@ -0,0 +1,743 @@
+/*!
+ * \file pcmplayer.c
+ * \brief A generic PCM player for a VDR media plugin (muggle)
+ *
+ * \version $Revision: 1.7 $
+ * \date $Date: 2008-03-17 02:44:14 +0100 (Mo, 17 Mär 2008) $
+ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner, Wolfgang Rohdewald
+ * \author Responsible author: $Author: woro $
+ *
+ * $Id: vdr_player.c 1046 2008-03-17 01:44:14Z woro $
+ *
+ * Adapted from
+ * MP3/MPlayer plugin to VDR (C++)
+ * (C) 2001,2002 Stefan Huelswitt <huels@iname.com>
+ */
+
+#include "pcmplayer.h"
+
+#include "vdr_sound.c"
+
+mgPCMPlayer::mgPCMPlayer (mgSelection * plist)
+: cPlayer(the_setup.BackgrMode==3? pmAudioOnly:pmAudioOnlyBlack ) {
+ m_playlist = plist;
+
+ m_active = true;
+ m_started = false;
+
+ m_ringbuffer = new cRingBufferFrame (MP3BUFSIZE);
+
+ m_rframe = 0;
+ m_pframe = 0;
+ m_decoder = 0;
+
+ m_playmode = pmStartup;
+ m_state = msStop;
+
+ m_index = 0;
+ m_playing = false;
+ m_current = 0;
+
+}
+
+mgPCMPlayer::~mgPCMPlayer () {
+ Detach ();
+ delete m_playlist;
+ delete m_ringbuffer;
+
+}
+
+void
+mgPCMPlayer::PlayTrack() {
+ mgItemGd * newcurr = dynamic_cast<mgItemGd*>(m_playlist->getCurrentItem ());
+ if (newcurr) {
+ delete m_current;
+ m_current = new mgItemGd(newcurr);
+ }
+ Play ();
+}
+
+void
+mgPCMPlayer::Activate (bool on) {
+ if (on) {
+ if (m_playlist && !m_started) {
+ m_playmode = pmStartup;
+ Start ();
+
+ m_started = true;
+ m_current = 0;
+
+ m_playmode_mutex.Lock ();
+ // wait for the decoder to become ready
+ WaitPlayMode (pmStartup, true);
+ m_playmode_mutex.Unlock ();
+
+ Lock ();
+ PlayTrack();
+ Unlock ();
+ }
+ }
+ else if (m_started && m_active) {
+ Lock ();
+ StopPlay ();
+ Unlock ();
+
+ m_active = false;
+ SetPlayMode (pmStartup);
+
+ Cancel (2);
+ }
+}
+
+void
+mgPCMPlayer::ReloadPlaylist() {
+ Lock ();
+ m_playlist->clearCache();
+ if (!m_playing) {
+ SkipFile();
+ Play ();
+ }
+ Unlock ();
+}
+
+void
+mgPCMPlayer::NewPlaylist (mgSelection * plist) {
+ MGLOG ("mgPCMPlayer::NewPlaylist");
+ plist->ShowState("mgPCMPlayer::NewPlaylist got:");
+
+ Lock ();
+ StopPlay ();
+
+ delete m_current;
+ m_current = 0;
+ delete m_playlist;
+ m_playlist = plist;
+ PlayTrack();
+ Unlock ();
+}
+
+void
+mgPCMPlayer::SetPlayMode (emgPlayMode mode) {
+ m_playmode_mutex.Lock ();
+ if (mode != m_playmode) {
+ m_playmode = mode;
+ m_playmode_cond.Broadcast ();
+ }
+ m_playmode_mutex.Unlock ();
+}
+
+void
+mgPCMPlayer::WaitPlayMode(emgPlayMode mode, bool inv) {
+ // must be called with m_playmode_mutex LOCKED !!!
+
+ while (m_active
+ && ((!inv && mode != m_playmode) || (inv && mode == m_playmode))) {
+ m_playmode_cond.Wait (m_playmode_mutex);
+ }
+}
+
+void
+mgPCMPlayer::Action (void) {
+
+ struct mgDecode *ds = 0;
+ struct mad_pcm *pcm = 0;
+ mgResample resample[2];
+ unsigned int nsamples[2];
+ const mad_fixed_t *data[2];
+ mgScale scale;
+ mgLevel level;
+ mgNormalize norm;
+ bool haslevel = false;
+ struct LPCMFrame lpcmFrame;
+ const unsigned char *p = 0;
+ int pc = 0, only48khz = the_setup.Only48kHz;
+ cPoller poll;
+#ifdef DEBUG
+ // int beat = 0;
+#endif
+
+#ifdef DEBUGPES
+ FILE *peslog = fopen( "pes.dump", "w" );
+#endif
+
+ dsyslog ("muggle: player thread started (pid=%d)", getpid ());
+
+ memset (&lpcmFrame, 0, sizeof (lpcmFrame));
+ lpcmFrame.PES[2] = 0x01;
+ lpcmFrame.PES[3] = 0xbd;
+ lpcmFrame.PES[6] = 0x87;
+ lpcmFrame.LPCM[0] = 0xa0; // substream ID
+ lpcmFrame.LPCM[1] = 0xff;
+ lpcmFrame.LPCM[2] = 0;
+ lpcmFrame.LPCM[3] = 4;
+ lpcmFrame.LPCM[5] = 0x01;
+ lpcmFrame.LPCM[6] = 0x80;
+
+ dvbSampleRate = 48000;
+ m_state = msStop;
+ SetPlayMode (pmStopped);
+
+#if VDRVERSNUM >= 10318
+ cDevice::PrimaryDevice()->SetCurrentAudioTrack(ttAudio);
+#endif
+ while (m_active) {
+#ifdef DEBUG
+ /*
+ if (time (0) >= beat + 30)
+ {
+ std::
+ cout << "mgPCMPlayer::Action: heartbeat buffer=" << m_ringbuffer->
+ Available () << std::endl << std::flush;
+ scale.Stats ();
+ if (haslevel)
+ norm.Stats ();
+ beat = time (0);
+ }
+ */
+#endif
+
+ Lock ();
+
+ if (!m_rframe && m_playmode == pmPlay) {
+ switch (m_state) {
+ case msStart:
+ {
+ m_index = 0;
+
+ m_playing = true;
+
+ if ( m_current ) {
+ string filename = m_current->getSourceFile ();
+ if ((m_decoder = mgDecoders::findDecoder (m_current))
+ && m_decoder->start ()) {
+ levelgood = true;
+ haslevel = false;
+
+ level.Init ();
+
+ m_state = msDecode;
+ break;
+ }
+ else {
+ mgWarning("found no decoder for %s",filename.c_str());
+ // if loop mode is on and no decoder
+ m_state=msStop;
+ // for any track is found, we would
+ // otherwise get into an endless loop
+ // not stoppable with the remote.
+ break;
+ }
+ }
+ m_state = msEof;
+ }
+ break;
+ case msDecode:
+ {
+ if (imagefile.size()) internShowMPGFile();
+ ds = m_decoder->decode ();
+ switch (ds->status) {
+ case dsPlay:
+ {
+ pcm = ds->pcm;
+ m_index = ds->index / 1000;
+ m_state = msNormalize;
+ }
+ break;
+ case dsSkip:
+ case dsSoftError:
+ {
+ // skipping, state unchanged, next decode
+ }
+ break;
+ case dsEof:
+ {
+ m_state = msEof;
+ }
+ break;
+ case dsOK:
+ case dsError:
+ {
+ m_state = msError;
+ }
+ break;
+ }
+ }
+ break;
+ case msNormalize:
+ {
+ if (!haslevel) {
+ if (levelgood) {
+ level.GetPower (pcm);
+ }
+ }
+ else {
+ norm.AddGain (pcm);
+ }
+ m_state = msResample;
+ }
+ break;
+ case msResample:
+ {
+#ifdef DEBUG
+ {
+ static unsigned int oldrate = 0;
+ if (oldrate != pcm->samplerate) {
+ mgDebug ("mgPCMPlayer::Action: new input sample rate %d",pcm->samplerate);
+ oldrate = pcm->samplerate;
+ }
+ }
+#endif
+ nsamples[0] = nsamples[1] = pcm->length;
+ data[0] = pcm->samples[0];
+ data[1] = pcm->channels > 1 ? pcm->samples[1] : 0;
+
+ lpcmFrame.LPCM[5] &= 0xcf;
+ dvbSampleRate = 48000;
+ if (!only48khz) {
+ switch (pcm->samplerate) {
+ // If one of the supported frequencies, do it without resampling.
+ case 96000:
+ { // Select a "even" upsampling frequency if possible, too.
+ lpcmFrame.LPCM[5] |= 1 << 4;
+ dvbSampleRate = 96000;
+ }
+ break;
+
+ //case 48000: // this is already the default ...
+ // lpcmFrame.LPCM[5]|=0<<4;
+ // dvbSampleRate=48000;
+ // break;
+ case 11025:
+ case 22050:
+ case 44100:
+ {
+ // lpcmFrame.LPCM[5] |= 2 << 4;
+ lpcmFrame.LPCM[5] |= 0x20;
+ dvbSampleRate = 44100;
+ }
+ break;
+ case 8000:
+ case 16000:
+ case 32000:
+ {
+ // lpcmFrame.LPCM[5] |= 3 << 4;
+ lpcmFrame.LPCM[5] |= 0x30;
+ dvbSampleRate = 32000;
+ }
+ break;
+ }
+ }
+
+ if (dvbSampleRate != pcm->samplerate) {
+ if (resample[0].
+ SetInputRate (pcm->samplerate, dvbSampleRate)) {
+ nsamples[0] =
+ resample[0].ResampleBlock (nsamples[0], data[0]);
+ data[0] = resample[0].Resampled ();
+ }
+ if (data[1]
+ && resample[1].SetInputRate (pcm->samplerate,
+ dvbSampleRate)) {
+ nsamples[1] =
+ resample[1].ResampleBlock (nsamples[1], data[1]);
+ data[1] = resample[1].Resampled ();
+ }
+ }
+ m_state = msOutput;
+ }
+ break;
+ case msOutput:
+ {
+ if (nsamples[0] > 0) {
+ unsigned int outlen =
+ scale.ScaleBlock ( lpcmFrame.Data,
+ sizeof (lpcmFrame.Data),
+ nsamples[0],
+ data[0], data[1],
+ the_setup.AudioMode ?
+ amDither : amRound );
+ if (outlen) {
+ outlen += sizeof (lpcmFrame.LPCM) + LEN_CORR;
+ lpcmFrame.PES[4] = outlen >> 8;
+ lpcmFrame.PES[5] = outlen;
+
+ // lPCM has 600 fps which is 80 samples at 48kHz per channel
+ // Frame size = (sample_rate * quantization * channels)/4800
+ size_t frame_size = 2 * (dvbSampleRate*16)/4800;
+
+ lpcmFrame.LPCM[1] = (outlen - LPCM_SIZE)/frame_size;
+ m_rframe = new cFrame ((unsigned char *) &lpcmFrame,
+ outlen +
+ sizeof (lpcmFrame.PES) -
+ LEN_CORR);
+ }
+ }
+ else {
+ m_state = msDecode;
+ }
+ }
+ break;
+ case msError:
+ case msEof:
+ {
+ if (SkipFile ()) {
+ m_state = msStart;
+ }
+ else {
+ m_state = msWait;
+ }
+ } // fall through
+ case msStop:
+ {
+ m_playing = false;
+
+ if (m_decoder) {
+ // who deletes decoder?
+ m_decoder->stop ();
+ delete m_decoder;
+ m_decoder = 0;
+ }
+
+ levelgood = false;
+
+ scale.Stats ();
+ if (haslevel) {
+ norm.Stats ();
+ }
+ if (m_state == msStop) {
+ // might be unequal in case of fall through from eof/error
+ SetPlayMode (pmStopped);
+ }
+ }
+ break;
+ case msWait:
+ {
+ if (m_ringbuffer->Available () == 0) {
+ // m_active = false;
+ SetPlayMode (pmStopped);
+ }
+ }
+ break;
+ }
+ }
+
+ if (m_rframe && m_ringbuffer->Put (m_rframe)) {
+ m_rframe = 0;
+ }
+
+ if (!m_pframe && m_playmode == pmPlay) {
+ m_pframe = m_ringbuffer->Get ();
+ if (m_pframe) {
+ p = m_pframe->Data ();
+ pc = m_pframe->Count ();
+ }
+ }
+
+ if (m_pframe) {
+#ifdef DEBUGPES
+ fwrite( (void *)p, pc, sizeof( char ), peslog );
+#endif
+
+#if VDRVERSNUM >= 10318
+ int w = PlayPes (p, pc);
+#else
+ int w = PlayVideo (p, pc);
+#endif
+ if (w > 0) {
+ p += w;
+ pc -= w;
+
+ if (pc <= 0) {
+ m_ringbuffer->Drop (m_pframe);
+ m_pframe = 0;
+ }
+ }
+ else if (w < 0 && FATALERRNO) {
+ LOG_ERROR;
+ break;
+ }
+ }
+ // avoid helgrind warning
+ eState curr_m_state = m_state;
+
+ Unlock ();
+
+ if ((m_rframe || curr_m_state == msWait) && m_pframe) {
+ // Wait for output to become ready
+ DevicePoll (poll, 500);
+ }
+ else {
+ if (m_playmode != pmPlay) {
+ m_playmode_mutex.Lock ();
+
+ if (m_playmode != pmPlay) {
+ // Wait on playMode change
+ WaitPlayMode (m_playmode, true);
+ }
+ m_playmode_mutex.Unlock ();
+ }
+ }
+ }
+
+ Lock ();
+
+ if (m_rframe) {
+ delete m_rframe;
+ m_rframe = 0;
+ }
+
+ if (m_decoder) {
+ // who deletes decoder?
+ m_decoder->stop ();
+ delete m_decoder;
+ m_decoder = 0;
+ }
+
+ m_playing = false;
+
+ SetPlayMode (pmStopped);
+
+#ifdef DEBUGPES
+ fclose( peslog );
+#endif
+
+ Unlock ();
+
+ m_active = false;
+
+ dsyslog ("muggle: player thread ended (pid=%d)", getpid ());
+}
+
+void
+mgPCMPlayer::Empty (void) {
+
+ Lock ();
+
+ m_ringbuffer->Clear ();
+ DeviceClear ();
+
+ delete m_rframe;
+ m_rframe = 0;
+ m_pframe = 0;
+
+ Unlock ();
+}
+
+void
+mgPCMPlayer::StopPlay () {
+ // StopPlay() must be called in locked state!!!
+ if (m_playmode != pmStopped) {
+ Empty ();
+ m_state = msStop;
+ SetPlayMode (pmPlay);
+ Unlock (); // let the decode thread process the stop signal
+
+ m_playmode_mutex.Lock ();
+ WaitPlayMode (pmStopped, false);
+ m_playmode_mutex.Unlock ();
+
+ Lock ();
+ }
+}
+
+bool mgPCMPlayer::SkipFile (bool skipforward) {
+ mgItemGd * newcurr = NULL;
+ int skip_direction=1;
+ if (!skipforward)
+ skip_direction=-1;
+ if (m_playlist->skipItems (skip_direction)) {
+ newcurr = dynamic_cast<mgItemGd*>(m_playlist->getCurrentItem ());
+ if (newcurr) {
+ delete m_current;
+ m_current = new mgItemGd(newcurr);
+ }
+ }
+ return (newcurr != NULL);
+}
+
+void
+mgPCMPlayer::ToggleShuffle () {
+ m_playlist->toggleShuffleMode ();
+}
+
+void
+mgPCMPlayer::ToggleLoop (void) {
+ m_playlist->toggleLoopMode ();
+}
+
+void
+mgPCMPlayer::Pause (void) {
+ if (m_playmode == pmPaused) {
+ Play ();
+ }
+ else {
+ if (m_playmode == pmPlay) {
+ // DeviceFreeze();
+ SetPlayMode (pmPaused);
+ }
+ }
+}
+
+void
+mgPCMPlayer::Play (void) {
+
+ if (m_playmode != pmPlay && m_current) {
+ Lock ();
+
+ if (m_playmode == pmStopped) {
+ m_state = msStart;
+ }
+ // DevicePlay(); // TODO? Commented out in original code, too
+ SetPlayMode (pmPlay);
+ Unlock ();
+ }
+}
+
+void
+mgPCMPlayer::Forward () {
+ Lock ();
+ if (SkipFile ()) {
+ StopPlay ();
+ Play ();
+ }
+ Unlock ();
+}
+
+void
+mgPCMPlayer::Backward (void) {
+ Lock ();
+ if (SkipFile (false)) {
+ StopPlay ();
+ Play ();
+ }
+ Unlock ();
+}
+
+void
+mgPCMPlayer::Goto (int index) {
+ m_playlist->GotoItemPosition (index );
+ mgItemGd *next = dynamic_cast<mgItemGd*>(m_playlist->getCurrentItem ());
+
+ if (next) {
+ Lock ();
+ StopPlay ();
+ delete m_current;
+ m_current = new mgItemGd(next);
+ Play ();
+ Unlock ();
+ }
+}
+
+void
+mgPCMPlayer::SkipSeconds (int secs) {
+ if (m_playmode != pmStopped) {
+ Lock ();
+
+ if (m_playmode == pmPaused) {
+ SetPlayMode (pmPlay);
+ }
+ if ( m_decoder
+ && m_decoder->skip (secs, m_ringbuffer->Available(),
+ dvbSampleRate) ) {
+ levelgood = false;
+ }
+
+ Empty ();
+ Unlock ();
+ }
+}
+
+bool mgPCMPlayer::GetIndex (int &current, int &total, bool snaptoiframe) {
+ if (m_current) {
+ current = SecondsToFrames (m_index);
+ total = SecondsToFrames (m_current->getDuration ());
+ return total>=0;
+ }
+ return false;
+}
+
+void mgPCMPlayer::internShowMPGFile() {
+ if (!imagefile.size())
+ return;
+ uchar *buffer;
+ int fd;
+ struct stat st;
+ struct video_still_picture sp;
+
+ if ( (fd = open( imagefile.c_str(), O_RDONLY ) ) >= 0 ) {
+ fstat (fd, &st);
+ sp.iFrame = (char *) malloc (st.st_size);
+ if ( sp.iFrame ) {
+ sp.size = st.st_size;
+ if ( read (fd, sp.iFrame, sp.size) > 0 ) {
+ buffer = (uchar *) sp.iFrame;
+
+ if ( the_setup.UseDeviceStillPicture ) {
+ cCondWait::SleepMs(80);
+ DeviceStillPicture( buffer, sp.size );
+ }
+ else {
+ for (int i = 1; i <= 25; i++) {
+ send_pes_packet (buffer, sp.size, i);
+ }
+ }
+ }
+ free (sp.iFrame);
+ }
+ else {
+ esyslog ("muggle[%d]: cannot allocate memory (%d bytes) for still image",
+ getpid(), (int) st.st_size);
+ }
+ close (fd);
+ }
+ else {
+ esyslog ("muggle[%d]: cannot open image file '%s'",
+ getpid(), imagefile.c_str() );
+ }
+ imagefile.clear();
+}
+
+void mgPCMPlayer::send_pes_packet(unsigned char *data, int len, int timestamp) {
+#define PES_MAX_SIZE 2048
+ int ptslen = timestamp ? 5 : 1;
+ static unsigned char pes_header[PES_MAX_SIZE];
+
+ pes_header[0] = pes_header[1] = 0;
+ pes_header[2] = 1;
+ pes_header[3] = 0xe0;
+
+ while(len > 0) {
+ int payload_size = len;
+ if (6 + ptslen + payload_size > PES_MAX_SIZE)
+ payload_size = PES_MAX_SIZE - (6 + ptslen);
+
+ pes_header[4] = (ptslen + payload_size) >> 8;
+ pes_header[5] = (ptslen + payload_size) & 255;
+
+ if (ptslen == 5) {
+ int x;
+ x = (0x02 << 4) | (((timestamp >> 30) & 0x07) << 1) | 1;
+ pes_header[8] = x;
+ x = ((((timestamp >> 15) & 0x7fff) << 1) | 1);
+ pes_header[7] = x >> 8;
+ pes_header[8] = x & 255;
+ x = ((((timestamp) & 0x7fff) < 1) | 1);
+ pes_header[9] = x >> 8;
+ pes_header[10] = x & 255;
+ }
+ else {
+ pes_header[6] = 0x0f;
+ }
+
+ memcpy(&pes_header[6 + ptslen], data, payload_size);
+#if VDRVERSNUM >= 10318
+ PlayPes(pes_header, 6 + ptslen + payload_size);
+#else
+ PlayVideo(pes_header, 6 + ptslen + payload_size);
+#endif
+
+ len -= payload_size;
+ data += payload_size;
+ ptslen = 1;
+ }
+}
diff --git a/pcmplayer.h b/pcmplayer.h
new file mode 100644
index 0000000..fd029bd
--- /dev/null
+++ b/pcmplayer.h
@@ -0,0 +1,235 @@
+/*!
+ * \file pcmplayer.c
+ * \brief A generic PCM player for a VDR media plugin (muggle)
+ *
+ * \version $Revision: 1.7 $
+ * \date $Date: 2008-03-17 02:44:14 +0100 (Mo, 17 Mär 2008) $
+ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner, Wolfgang Rohdewald
+ * \author Responsible author: $Author: woro $
+ *
+ * $Id: vdr_player.c 1046 2008-03-17 01:44:14Z woro $
+ *
+ * Adapted from
+ * MP3/MPlayer plugin to VDR (C++)
+ * (C) 2001,2002 Stefan Huelswitt <huels@iname.com>
+ */
+
+// TODO: wenn es nur ein Bild gibt, nicht loopen
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <math.h>
+
+#include <string>
+#include <iostream>
+#include <fstream>
+
+#include <mad.h>
+
+#include <linux/types.h> // this should really be included by linux/dvb/video.h
+#include <linux/dvb/video.h>
+#include <vdr/player.h>
+#include <vdr/device.h>
+#include <vdr/remote.h>
+#include <vdr/thread.h>
+#include <vdr/ringbuffer.h>
+#define __STL_CONFIG_H
+#include <vdr/tools.h>
+#include <vdr/recording.h>
+#include <vdr/status.h>
+#include <vdr/plugin.h>
+
+#include "vdr_decoder.h"
+#include "vdr_config.h"
+#include "mg_setup.h"
+#include "mg_image_provider.h"
+#include "bitmap.h"
+
+#include "mg_tools.h"
+
+enum emgPlayMode
+{
+ pmPlay,
+ pmStopped,
+ pmPaused,
+ pmStartup
+};
+
+// ----------------------------------------------------------------
+
+// #define DEBUGPES
+
+// mgResample
+#define MAX_NSAMPLES (1152*7) // max. buffer for resampled frame
+
+// mgNormalize
+#define MAX_GAIN 3.0 // max. allowed gain
+#define LIM_ACC 12 // bit, accuracy for lookup table
+
+ // max. value covered by lookup table
+#define F_LIM_MAX (mad_fixed_t)((1<<(MAD_F_FRACBITS+2))-1)
+ // shift value for table lookup
+#define LIM_SHIFT (MAD_F_FRACBITS-LIM_ACC)
+ // lookup table jump between values
+#define F_LIM_JMP (mad_fixed_t)(1<<LIM_SHIFT)
+
+// mgLevel
+#define POW_WIN 100 // window width for smoothing power values
+#define EPSILON 0.00000000001 // anything less than EPSILON is considered zero
+
+// cMP3Player
+#define MAX_FRAMESIZE 2048 // max. frame size allowed for DVB driver
+#define HDR_SIZE 9
+#define LPCM_SIZE 7
+#define LEN_CORR 3
+ // play time to fill buffers before speed check starts (ms)
+#define SPEEDCHECKSTART ((MP3BUFSIZE*1000/32000/2/2)+1000)
+#define SPEEDCHECKTIME 3000 // play time for speed check (ms)
+
+/*
+struct LPCMHeader { int id:8; // id
+ int frame_count:8; // number of frames
+ int access_ptr:16; // first acces unit pointer, i.e. start of audio frame
+ bool emphasis:1; // audio emphasis on-off
+ bool mute:1; // audio mute on-off
+ bool reserved:1; // reserved
+ int frame_number:5; // audio frame number
+ int quant_wlen:2; // quantization word length
+ int sample_freq:2; // audio sampling frequency (48khz=0, 96khz=1, 44,1khz=2, 32khz=3)
+ bool reserved2:1; // reserved
+int chan_count:3; // number of audio channels - 1 (e.g. stereo = 1)
+int dyn_range_ctrl:8; // dynamic range control (0x80 if off)
+};
+*/
+
+struct LPCMFrame
+{
+ unsigned char PES[HDR_SIZE];
+ unsigned char LPCM[LPCM_SIZE];
+ unsigned char Data[MAX_FRAMESIZE - HDR_SIZE - LPCM_SIZE];
+};
+
+/*!
+ * \brief a generic PCM player class
+ *
+ * this class implements a state machine that obtains decoded data from a generic data
+ * and moves it to the DVB device. It inherits from cPlayer in order to be hooked into
+ * VDR as a player and inherits from cThread in order to implement a separate thread
+ * for the decoding process.
+ */
+class mgPCMPlayer : public cPlayer, cThread
+{
+ private:
+
+ //! \brief indicates whether the player is currently active
+ bool m_active;
+
+ //! \brief indicates whether the player has been started
+ bool m_started;
+
+ //! \brief indicates whether the player is currently playing
+ bool m_playing;
+
+ //! \brief a buffer for decoded sound
+ cRingBufferFrame *m_ringbuffer;
+
+ //! \brief a mutex for the playmode
+ cMutex m_playmode_mutex;
+
+ //! \brief a condition to signal playmode changes
+ cCondVar m_playmode_cond;
+
+ //! \brief the current playlist
+ mgSelection *m_playlist;
+
+ //! \brief the currently played or to be played item
+ mgItemGd *m_current;
+
+ //! \brief the decoder responsible for the currently playing item
+ mgDecoder *m_decoder;
+
+ cFrame *m_rframe, *m_pframe;
+
+ emgPlayMode m_playmode;
+
+ enum eState
+ {
+ msStart, msStop,
+ msDecode, msNormalize,
+ msResample, msOutput,
+ msError, msEof, msWait
+ };
+ eState m_state;
+
+ bool levelgood;
+ unsigned int dvbSampleRate;
+
+ //
+ int m_index;
+
+ string imagefile;
+
+ void Empty ();
+ bool SkipFile (bool skipforward=true);
+ void PlayTrack();
+ void StopPlay ();
+
+ void SetPlayMode (emgPlayMode mode);
+ void WaitPlayMode (emgPlayMode mode, bool inv);
+ void internShowMPGFile();
+ void send_pes_packet(unsigned char *data, int len, int timestamp);
+
+ protected:
+ virtual void Activate (bool On);
+ virtual void Action (void);
+
+ public:
+ mgPCMPlayer (mgSelection * plist);
+ virtual ~ mgPCMPlayer ();
+
+ bool Active () {
+ return m_active;
+ }
+
+ bool Playing () {
+ return m_playing || m_active;
+ }
+
+ emgPlayMode PlayMode () {
+ return m_playmode;
+ }
+
+ unsigned int Position() {
+ return m_playlist->getItemPosition();
+ }
+
+ void Pause ();
+ void Play ();
+ void Forward ();
+ void Backward ();
+
+ void Goto (int Index);
+ void SkipSeconds (int secs);
+ void ToggleShuffle (void);
+ void ToggleLoop (void);
+ void ShowMPGFile(const string f ) { imagefile=f; }
+
+ virtual bool GetIndex (int &Current, int &Total, bool SnapToIFrame = false);
+ // bool GetPlayInfo(cMP3PlayInfo *rm); // LVW
+
+ void ReloadPlaylist();
+ void NewPlaylist (mgSelection * plist);
+
+ mgItemGd *getCurrent () {
+ return m_current;
+ }
+
+ mgSelection *getPlaylist () {
+ return m_playlist;
+ }
+
+};
diff --git a/po/de.po b/po/de.po
index 3ae3e02..999f731 100644
--- a/po/de.po
+++ b/po/de.po
@@ -10,8 +10,8 @@ msgid ""
msgstr ""
"Project-Id-Version: de\n"
"Report-Msgid-Bugs-To: <vdr-muggle-develop@sourceforge.net>\n"
-"POT-Creation-Date: 2008-02-19 00:28+0100\n"
-"PO-Revision-Date: 2008-02-19 00:31+0100\n"
+"POT-Creation-Date: 2008-04-11 19:35+0200\n"
+"PO-Revision-Date: 2008-04-11 19:38+0200\n"
"Last-Translator: Wolfgang Rohdewald <wolfgang@rohdewald.de>\n"
"Language-Team: deutsch <vdr-muggle-i18n@sourceforge.net>\n"
"MIME-Version: 1.0\n"
@@ -20,9 +20,119 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: KBabel 1.11.4\n"
+msgid "Loading lyrics from internet..."
+msgstr "Lade Text aus dem Internet..."
+
msgid "empty database created"
msgstr "leere Datenbank angelegt"
+#, c-format
+msgid "Cannot get exclusive lock on SQLite database %s:%d/%s"
+msgstr "Kann kein exklusives Lock bekommen auf SQLite Datenbank %s: %d/%s"
+
+#, c-format
+msgid "File %s not found"
+msgstr "Datei %s nicht gefunden"
+
+msgid "Commands"
+msgstr "Befehle"
+
+msgid "Genre"
+msgstr "Stil"
+
+msgid "Genre1"
+msgstr "Stil1"
+
+msgid "Genre2"
+msgstr "Stil2"
+
+msgid "Genre3"
+msgstr "Stil3"
+
+msgid "Folder1"
+msgstr "Ordner1"
+
+msgid "Folder2"
+msgstr "Ordner2"
+
+msgid "Folder3"
+msgstr "Ordner3"
+
+msgid "Folder4"
+msgstr "Ordner4"
+
+msgid "Artist"
+msgstr "Künstler"
+
+msgid "ArtistABC"
+msgstr "KünstlerABC"
+
+msgid "Title"
+msgstr "Titel"
+
+msgid "TitleABC"
+msgstr "TitelABC"
+
+msgid "Track"
+msgstr "Stück"
+
+msgid "Decade"
+msgstr "Dekade"
+
+msgid "Album"
+msgstr "Album"
+
+msgid "Created"
+msgstr "Erzeugt"
+
+msgid "Modified"
+msgstr "Geändert"
+
+msgid "Collection"
+msgstr "Sammlung"
+
+msgid "Collection item"
+msgstr "Sammlungsstück"
+
+msgid "Language"
+msgstr "Sprache"
+
+msgid "Rating"
+msgstr "Rating"
+
+msgid "Year"
+msgstr "Jahr"
+
+msgid "ID"
+msgstr "ID"
+
+msgid "not implemented"
+msgstr "Nicht implementiert"
+
+msgid "Parent"
+msgstr "Zurück"
+
+msgid "Track-"
+msgstr "Track-"
+
+msgid "Track+"
+msgstr "Track+"
+
+msgid "Change shuffle mode"
+msgstr "Ändere Zufallsmodus"
+
+msgid "Lyrics"
+msgstr "Text"
+
+msgid "Load Lyrics"
+msgstr "Lade Text"
+
+msgid "Save Lyrics"
+msgstr "Speichere Text"
+
+msgid "Change loop mode"
+msgstr "Ändere Schleifenmodus"
+
msgid "Order"
msgstr "Sortierung"
@@ -41,9 +151,6 @@ msgstr "Löschen"
msgid "List"
msgstr "Liste"
-msgid "Commands"
-msgstr "Befehle"
-
msgid "Browse"
msgstr "Navigieren"
@@ -152,69 +259,45 @@ msgstr "Titel importieren?"
msgid "Cannot access directory %s:%d"
msgstr "Kein Zugriff auf Verzeichnis %s:%d"
-msgid "Track info view"
-msgstr "Details"
-
#, c-format
-msgid "Title:\t%s"
-msgstr "Titel:\t%s"
+msgid "%d tracks : %s"
+msgstr "%d Stücke : %s"
-#, c-format
-msgid "Artist:\t%s"
-msgstr "Künstler:\t%s"
+msgid "Lyrics: Yes"
+msgstr "Text: Ja"
-#, c-format
-msgid "Album:\t%s"
-msgstr "Album:\t%s"
+msgid "Lyrics: No"
+msgstr "Text: Nein"
-#, c-format
-msgid "Genre:\t%s"
-msgstr "Stil:\t%s"
+msgid "Now playing :"
+msgstr "Jetzt läuft :"
-#, c-format
-msgid "Year:\t%d"
-msgstr "Jahr:\t%d"
-
-#, c-format
-msgid "Length:\t%s"
-msgstr "Länge:\t%s"
-
-#, c-format
-msgid "Bit rate:\t%s"
-msgstr "Bitrate:\t%s"
+msgid "Volume"
+msgstr "Lautstärke"
-#, c-format
-msgid "Sampling rate:\t%d"
-msgstr "Sampling Rate:\t%d"
+msgid "of"
+msgstr "von"
-#, c-format
-msgid "File name:\t%d"
-msgstr "Dateiname:\t%d"
+msgid "More.."
+msgstr "Mehr..."
-#, c-format
-msgid "File name:\t%s"
-msgstr "Dateiname:\t%s"
+msgid "Jump"
+msgstr "Springe"
-msgid "Now playing"
-msgstr "Jetzt läuft"
-
-msgid "Loop mode off"
-msgstr "Endlosmodus aus"
-
-msgid "Loop mode single"
-msgstr "Endlosmodus Einzeltitel"
+msgid "Delete"
+msgstr "Löschen"
-msgid "Loop mode full"
-msgstr "Endlosmodus alle"
+msgid "Copy"
+msgstr "Kopieren"
-msgid "Shuffle mode off"
-msgstr "Zufallsmodus aus"
+msgid "Min/Sec"
+msgstr "Min/Sec"
-msgid "Shuffle mode normal"
-msgstr "Zufallsmodus normal"
+msgid "Time"
+msgstr "Zeit"
-msgid "Shuffle mode party"
-msgstr "Zufallsmodus Party"
+msgid "Jump: "
+msgstr "Sprung:"
msgid "Muggle"
msgstr "Muggle"
@@ -258,8 +341,11 @@ msgstr "Begrenzer"
msgid "Black"
msgstr "Aus (schwarz)"
-msgid "Image"
-msgstr "Bild"
+msgid "Cover small"
+msgstr "Kleines Cover"
+
+msgid "Cover big"
+msgstr "Großes Cover"
msgid "Live"
msgstr "TV-Bild"
@@ -270,12 +356,21 @@ msgstr "Hintergrundmodus"
msgid "Image show duration (secs)"
msgstr "Anzeigedauer für Bilder"
-msgid "Image cache directory"
-msgstr "Verzeichnis für konvertierte Bilder"
+msgid "Cache directory"
+msgstr "Verzeichnis für Zwischenspeicher"
msgid "Use DVB still picture"
msgstr "DVB still picture benutzen"
+msgid "Show artist first"
+msgstr "Künstler vorne anzeigen"
+
+msgid "Fastforward jump (secs)"
+msgstr "Schnell vorwärts (sec)"
+
+msgid "Setup.muggle$Transparency for cover"
+msgstr "Setup.muggle$Transparenz für Cover"
+
msgid "Delete stale references"
msgstr "Einträge löschen, wenn Datei fehlt"
diff --git a/po/fr.po b/po/fr.po
index 1fa36a4..ba6e5e7 100644
--- a/po/fr.po
+++ b/po/fr.po
@@ -9,8 +9,8 @@ msgid ""
msgstr ""
"Project-Id-Version: fr\n"
"Report-Msgid-Bugs-To: <vdr-muggle-develop@sourceforge.net>\n"
-"POT-Creation-Date: 2008-02-19 00:28+0100\n"
-"PO-Revision-Date: 2008-02-19 00:36+0100\n"
+"POT-Creation-Date: 2008-04-11 19:35+0200\n"
+"PO-Revision-Date: 2008-04-11 20:27+0200\n"
"Last-Translator: Wolfgang Rohdewald <wolfgang@rohdewald.de>\n"
"Language-Team: deutsch <vdr-muggle-i18n@sourceforge.net>\n"
"MIME-Version: 1.0\n"
@@ -19,9 +19,119 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"X-Generator: KBabel 1.11.4\n"
+msgid "Loading lyrics from internet..."
+msgstr "Cherchant le texte dans le web"
+
msgid "empty database created"
msgstr "créé base des données vide"
+#, c-format
+msgid "Cannot get exclusive lock on SQLite database %s:%d/%s"
+msgstr "Pas d'accès exclusif à la base des données SQLite %s:%d/%s"
+
+#, c-format
+msgid "File %s not found"
+msgstr "Fichier %s pas trouvé"
+
+msgid "Commands"
+msgstr "Commandes"
+
+msgid "Genre"
+msgstr "Genre"
+
+msgid "Genre1"
+msgstr "Genre1"
+
+msgid "Genre2"
+msgstr "Genre2"
+
+msgid "Genre3"
+msgstr "Genre3"
+
+msgid "Folder1"
+msgstr "Dossier1"
+
+msgid "Folder2"
+msgstr "Dossier2"
+
+msgid "Folder3"
+msgstr "Dossier3"
+
+msgid "Folder4"
+msgstr "Dossier4"
+
+msgid "Artist"
+msgstr "Artiste"
+
+msgid "ArtistABC"
+msgstr "Artiste ABC"
+
+msgid "Title"
+msgstr "Titre"
+
+msgid "TitleABC"
+msgstr "Titre ABC"
+
+msgid "Track"
+msgstr "pièce"
+
+msgid "Decade"
+msgstr "Décade"
+
+msgid "Album"
+msgstr "Album"
+
+msgid "Created"
+msgstr "Nouveau"
+
+msgid "Modified"
+msgstr "Modifié"
+
+msgid "Collection"
+msgstr "Collection"
+
+msgid "Collection item"
+msgstr "Pièce de collection"
+
+msgid "Language"
+msgstr "Langue"
+
+msgid "Rating"
+msgstr "Estimation"
+
+msgid "Year"
+msgstr "Année"
+
+msgid "ID"
+msgstr "ID"
+
+msgid "not implemented"
+msgstr "pas programmé"
+
+msgid "Parent"
+msgstr "retour"
+
+msgid "Track-"
+msgstr "Pièce-"
+
+msgid "Track+"
+msgstr "Pièce+"
+
+msgid "Change shuffle mode"
+msgstr "changer mode alléatoire"
+
+msgid "Lyrics"
+msgstr "Texte"
+
+msgid "Load Lyrics"
+msgstr "Chercher Texte"
+
+msgid "Save Lyrics"
+msgstr "Sauvegarde Texte"
+
+msgid "Change loop mode"
+msgstr "changer mode répétition"
+
msgid "Order"
msgstr "Ordre"
@@ -40,9 +150,6 @@ msgstr "Effacer"
msgid "List"
msgstr "Liste"
-msgid "Commands"
-msgstr "Commandes"
-
msgid "Browse"
msgstr "Naviguer"
@@ -133,9 +240,9 @@ msgstr "jouer"
msgid "Commands:%s"
msgstr "Commandes:%s"
-#, fuzzy, c-format
+#, c-format
msgid "Key %d"
-msgstr "Key %d"
+msgstr "clef %d"
msgid "Sort by count"
msgstr "Ordre par quantité"
@@ -151,69 +258,45 @@ msgstr "Importer les titres?"
msgid "Cannot access directory %s:%d"
msgstr "Ne peux pas lire %s:%d"
-msgid "Track info view"
-msgstr "Les détails"
-
-#, c-format
-msgid "Title:\t%s"
-msgstr "Titre:\t%s"
-
-#, c-format
-msgid "Artist:\t%s"
-msgstr "Artiste:\t%s"
-
-#, c-format
-msgid "Album:\t%s"
-msgstr "Album:\t%s"
-
#, c-format
-msgid "Genre:\t%s"
-msgstr "Genre:\t%s"
+msgid "%d tracks : %s"
+msgstr "%d pièces : %s"
-#, c-format
-msgid "Year:\t%d"
-msgstr "Année:\t%d"
+msgid "Lyrics: Yes"
+msgstr "Texte: Oui"
-#, c-format
-msgid "Length:\t%s"
-msgstr "Longeur:\t%s"
-
-#, c-format
-msgid "Bit rate:\t%s"
-msgstr ""
+msgid "Lyrics: No"
+msgstr "Texte: Non"
-#, c-format
-msgid "Sampling rate:\t%d"
-msgstr ""
+msgid "Now playing :"
+msgstr "En jouant"
-#, c-format
-msgid "File name:\t%d"
-msgstr "Nom du fichier:\t%d"
+msgid "Volume"
+msgstr "Volume"
-#, c-format
-msgid "File name:\t%s"
-msgstr "Nom du fichier:\t%s"
+msgid "of"
+msgstr "de"
-msgid "Now playing"
-msgstr "En jouant"
+msgid "More.."
+msgstr "plus..."
-msgid "Loop mode off"
-msgstr "Déclancher le mode répétition"
+msgid "Jump"
+msgstr "Saut"
-msgid "Loop mode single"
-msgstr "Mode répétition titre seul"
+msgid "Delete"
+msgstr "Effacer"
-msgid "Loop mode full"
-msgstr "jjMode répétition tout"
+msgid "Copy"
+msgstr "Copier"
-msgid "Shuffle mode off"
-msgstr "mode alléatoire déclenché"
+msgid "Min/Sec"
+msgstr "Min/Sec"
-msgid "Shuffle mode normal"
-msgstr "Mode alléatoire normal"
+msgid "Time"
+msgstr "Heure"
-msgid "Shuffle mode party"
-msgstr "Mode alléatoire fêtes"
+msgid "Jump: "
+msgstr "Saut:"
msgid "Muggle"
msgstr "Muggle"
@@ -222,10 +305,10 @@ msgid "Initial loop mode"
msgstr "mode répétition initial"
msgid "off"
-msgstr ""
+msgstr "non"
msgid "on"
-msgstr ""
+msgstr "actif"
msgid "Initial shuffle mode"
msgstr "mode alléatoire initial"
@@ -234,10 +317,10 @@ msgid "Audio mode"
msgstr "Mode audio"
msgid "Round"
-msgstr ""
+msgstr "Round"
msgid "Dither"
-msgstr ""
+msgstr "Dither"
msgid "Use 48kHz mode only"
msgstr "Seulement 48kHz mode"
@@ -249,32 +332,44 @@ msgid "yes"
msgstr "oui"
msgid "Normalizer level"
-msgstr ""
+msgstr "Normalizer level"
msgid "Limiter level"
-msgstr ""
+msgstr "Limiter level"
msgid "Black"
msgstr "Noir"
-msgid "Image"
-msgstr "Image"
+msgid "Cover small"
+msgstr "Image petit"
+
+msgid "Cover big"
+msgstr "Image grand"
msgid "Live"
-msgstr ""
+msgstr "Live"
msgid "Background mode"
-msgstr ""
+msgstr "Background mode"
msgid "Image show duration (secs)"
-msgstr ""
+msgstr "Duration par image (sec)"
-msgid "Image cache directory"
-msgstr ""
+msgid "Cache directory"
+msgstr "Répertoire temporaire"
msgid "Use DVB still picture"
-msgstr ""
+msgstr "Utiliser DVB still picture"
+
+msgid "Show artist first"
+msgstr "montrer l'artiste en avant"
+
+msgid "Fastforward jump (secs)"
+msgstr "Avance rapide (secs)"
+
+msgid "Setup.muggle$Transparency for cover"
+msgstr "Setup.muggle$Transparancy de l'image"
msgid "Delete stale references"
-msgstr ""
+msgstr "Effacer références éventées"
diff --git a/po/muggle.pot b/po/muggle.pot
index 88fa4e6..7eab8d2 100644
--- a/po/muggle.pot
+++ b/po/muggle.pot
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: <vdr-muggle-develop@sourceforge.net>\n"
-"POT-Creation-Date: 2008-02-19 00:28+0100\n"
+"POT-Creation-Date: 2008-04-11 22:05+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -16,9 +16,119 @@ msgstr ""
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
+msgid "Loading lyrics from internet..."
+msgstr ""
+
msgid "empty database created"
msgstr ""
+#, c-format
+msgid "Cannot get exclusive lock on SQLite database %s:%d/%s"
+msgstr ""
+
+#, c-format
+msgid "File %s not found"
+msgstr ""
+
+msgid "Commands"
+msgstr ""
+
+msgid "Genre"
+msgstr ""
+
+msgid "Genre1"
+msgstr ""
+
+msgid "Genre2"
+msgstr ""
+
+msgid "Genre3"
+msgstr ""
+
+msgid "Folder1"
+msgstr ""
+
+msgid "Folder2"
+msgstr ""
+
+msgid "Folder3"
+msgstr ""
+
+msgid "Folder4"
+msgstr ""
+
+msgid "Artist"
+msgstr ""
+
+msgid "ArtistABC"
+msgstr ""
+
+msgid "Title"
+msgstr ""
+
+msgid "TitleABC"
+msgstr ""
+
+msgid "Track"
+msgstr ""
+
+msgid "Decade"
+msgstr ""
+
+msgid "Album"
+msgstr ""
+
+msgid "Created"
+msgstr ""
+
+msgid "Modified"
+msgstr ""
+
+msgid "Collection"
+msgstr ""
+
+msgid "Collection item"
+msgstr ""
+
+msgid "Language"
+msgstr ""
+
+msgid "Rating"
+msgstr ""
+
+msgid "Year"
+msgstr ""
+
+msgid "ID"
+msgstr ""
+
+msgid "not implemented"
+msgstr ""
+
+msgid "Parent"
+msgstr ""
+
+msgid "Track-"
+msgstr ""
+
+msgid "Track+"
+msgstr ""
+
+msgid "Change shuffle mode"
+msgstr ""
+
+msgid "Lyrics"
+msgstr ""
+
+msgid "Load Lyrics"
+msgstr ""
+
+msgid "Save Lyrics"
+msgstr ""
+
+msgid "Change loop mode"
+msgstr ""
+
msgid "Order"
msgstr ""
@@ -37,9 +147,6 @@ msgstr ""
msgid "List"
msgstr ""
-msgid "Commands"
-msgstr ""
-
msgid "Browse"
msgstr ""
@@ -148,68 +255,44 @@ msgstr ""
msgid "Cannot access directory %s:%d"
msgstr ""
-msgid "Track info view"
-msgstr ""
-
-#, c-format
-msgid "Title:\t%s"
-msgstr ""
-
-#, c-format
-msgid "Artist:\t%s"
-msgstr ""
-
-#, c-format
-msgid "Album:\t%s"
-msgstr ""
-
-#, c-format
-msgid "Genre:\t%s"
-msgstr ""
-
#, c-format
-msgid "Year:\t%d"
+msgid "%d tracks : %s"
msgstr ""
-#, c-format
-msgid "Length:\t%s"
+msgid "Lyrics: Yes"
msgstr ""
-#, c-format
-msgid "Bit rate:\t%s"
+msgid "Lyrics: No"
msgstr ""
-#, c-format
-msgid "Sampling rate:\t%d"
+msgid "Now playing :"
msgstr ""
-#, c-format
-msgid "File name:\t%d"
+msgid "Volume"
msgstr ""
-#, c-format
-msgid "File name:\t%s"
+msgid "of"
msgstr ""
-msgid "Now playing"
+msgid "More.."
msgstr ""
-msgid "Loop mode off"
+msgid "Jump"
msgstr ""
-msgid "Loop mode single"
+msgid "Delete"
msgstr ""
-msgid "Loop mode full"
+msgid "Copy"
msgstr ""
-msgid "Shuffle mode off"
+msgid "Min/Sec"
msgstr ""
-msgid "Shuffle mode normal"
+msgid "Time"
msgstr ""
-msgid "Shuffle mode party"
+msgid "Jump: "
msgstr ""
msgid "Muggle"
@@ -254,7 +337,10 @@ msgstr ""
msgid "Black"
msgstr ""
-msgid "Image"
+msgid "Cover small"
+msgstr ""
+
+msgid "Cover big"
msgstr ""
msgid "Live"
@@ -266,11 +352,20 @@ msgstr ""
msgid "Image show duration (secs)"
msgstr ""
-msgid "Image cache directory"
+msgid "Cache directory"
msgstr ""
msgid "Use DVB still picture"
msgstr ""
+msgid "Show artist first"
+msgstr ""
+
+msgid "Fastforward jump (secs)"
+msgstr ""
+
+msgid "Setup.muggle$Transparency for cover"
+msgstr ""
+
msgid "Delete stale references"
msgstr ""
diff --git a/quantize.c b/quantize.c
new file mode 100644
index 0000000..6d23823
--- /dev/null
+++ b/quantize.c
@@ -0,0 +1,387 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "quantize.h"
+#include <vdr/tools.h>
+
+cQuantizeWu::cQuantizeWu() {
+}
+
+cQuantizeWu::~cQuantizeWu() {
+}
+
+#define WEIG //>>2
+int cQuantizeWu::Quantize(unsigned char * input, int size, int colors) {
+ if (colors>MAXCOLOR) {
+ esyslog("ERROR: Quantize can only handle %d colors, not %d",MAXCOLOR,colors);
+ colors=MAXCOLOR;
+ }
+ struct box cube[MAXCOLOR];
+ int next;
+ int i;
+ int j;
+ int k;
+ int l;
+ float vv[MAXCOLOR];
+ float temp;
+ unsigned char * tag;
+ long weight;
+
+ imageInput = input;
+ imageSize = size;
+ palSize = colors;
+
+ for (j = 0; j < BOX; j++)
+ for (k = 0; k < BOX; k++)
+ for (l = 0; l < BOX; l++) {
+ wt[j][k][l] = 0;
+ mr[j][k][l] = 0;
+ mg[j][k][l] = 0;
+ mb[j][k][l] = 0;
+ m2[j][k][l] = 0.0;
+ }
+
+ Qadd = new unsigned short[imageSize * sizeof(unsigned short)];
+ Hist3d((long *)&wt, (long *)&mr, (long *)&mg, (long *)&mb, (float *)&m2);
+ Momt3d((long *)&wt, (long *)&mr, (long *)&mg, (long *)&mb, (float *)&m2);
+
+ cube[0].r0 = cube[0].g0 = cube[0].b0 = 0;
+ cube[0].r1 = cube[0].g1 = cube[0].b1 = 32;
+
+ next = 0;
+ for (i = 1; i < palSize; i++) {
+ if (Cut(&cube[next], &cube[i])) {
+ vv[next] = (cube[next].vol > 1) ? Var(&cube[next]) : 0.0;
+ vv[i] = (cube[i].vol > 1) ? Var(&cube[i]) : 0.0;
+ }
+ else {
+ vv[next] = 0.0;
+ i--;
+ }
+ next = 0;
+ temp = vv[0];
+ for (k = 1; k <= i; k++)
+ if (vv[k] > temp) {
+ temp = vv[k];
+ next = k;
+ }
+ if (temp <= 0.0) {
+ palSize = i + 1;
+ break;
+ }
+ }
+
+ tag = new unsigned char[BOX * BOX * BOX];
+
+ unsigned char * palette = (unsigned char *) paletteOutput;
+ for (k = 0; k < palSize; ++k) {
+ Mark(&cube[k], k, tag);
+ weight = Vol(&cube[k], wt);
+ if (weight) {
+#ifdef NOINVERT
+ palette[k * 4 + 2] = (Vol(&cube[k], mb) / weight) WEIG;
+ palette[k * 4 + 0] = (Vol(&cube[k], mr) / weight) WEIG;
+#else
+ palette[k * 4 + 0] = (Vol(&cube[k], mb) / weight) WEIG;
+ palette[k * 4 + 2] = (Vol(&cube[k], mr) / weight) WEIG;
+#endif
+ palette[k * 4 + 1] = (Vol(&cube[k], mg) / weight) WEIG;
+ }
+ else {
+ palette[k * 4 + 0] = 0;
+ palette[k * 4 + 1] = 0;
+ palette[k * 4 + 2] = 0;
+ }
+ }
+
+ for (i = 0; i < imageSize; i++) {
+ imageOutput[i] = tag[Qadd[i]];
+ }
+
+ delete[] tag;
+ delete[] Qadd;
+
+ return 0;
+}
+
+// build 3-D color histogram of counts, r/g/b, c^2
+void cQuantizeWu::Hist3d(long *vwt, long *vmr, long *vmg, long *vmb, float *m_2) {
+ int ind;
+ int r;
+ int g;
+ int b;
+ int inr;
+ int ing;
+ int inb;
+ int table[256];
+ int i;
+
+ for (i = 0; i < 256; i++)
+ table[i] = i * i;
+
+ for (i = 0; i < imageSize; i++) {
+ b = imageInput[i*4];
+ g = imageInput[i*4+1];
+ r = imageInput[i*4+2];
+
+ inr = (r >> 3) + 1;
+ ing = (g >> 3) + 1;
+ inb = (b >> 3) + 1;
+ Qadd[i] = ind = (inr << 10) + (inr << 6) + inr + (ing << 5) + ing + inb;
+
+ vwt[ind]++;
+ vmr[ind] += r;
+ vmg[ind] += g;
+ vmb[ind] += b;
+ m_2[ind] += (float) (table[r] + table[g] + table[b]);
+ }
+}
+
+// We now convert histogram into moments so that we can rapidly calculate
+// the sums of the above quantities over any desired box.
+void cQuantizeWu::Momt3d(long *vwt, long *vmr, long *vmg, long *vmb, float *m_2) {
+ unsigned short ind1;
+ unsigned short ind2;
+ unsigned char i;
+ unsigned char r;
+ unsigned char g;
+ unsigned char b;
+ long line;
+ long line_r;
+ long line_g;
+ long line_b;
+ long area[BOX];
+ long area_r[BOX];
+ long area_g[BOX];
+ long area_b[BOX];
+ float line2;
+ float area2[BOX];
+
+ for (r = 1; r <= 32; ++r) {
+ for (i = 0; i <= 32; ++i)
+ area2[i] = area[i] = area_r[i] = area_g[i] = area_b[i] = 0;
+ for (g = 1; g <= 32; ++g) {
+ line2 = line = line_r = line_g = line_b = 0;
+ for (b = 1; b <= 32; ++b) {
+ ind1 = (r << 10) + (r << 6) + r + (g << 5) + g + b;
+ line += vwt[ind1];
+ line_r += vmr[ind1];
+ line_g += vmg[ind1];
+ line_b += vmb[ind1];
+ line2 += m_2[ind1];
+ area[b] += line;
+ area_r[b] += line_r;
+ area_g[b] += line_g;
+ area_b[b] += line_b;
+ area2[b] += line2;
+ /* [r-1][g][b] */
+ ind2 = ind1 - 1089;
+ vwt[ind1] = vwt[ind2] + area[b];
+ vmr[ind1] = vmr[ind2] + area_r[b];
+ vmg[ind1] = vmg[ind2] + area_g[b];
+ vmb[ind1] = vmb[ind2] + area_b[b];
+ m_2[ind1] = m_2[ind2] + area2[b];
+ }
+ }
+ }
+}
+
+int cQuantizeWu::Cut(struct box * set1, struct box * set2) {
+ unsigned char dir;
+ int cutr;
+ int cutg;
+ int cutb;
+ float maxr;
+ float maxg;
+ float maxb;
+ long whole_r;
+ long whole_g;
+ long whole_b;
+ long whole_w;
+
+ whole_r = Vol(set1, mr);
+ whole_g = Vol(set1, mg);
+ whole_b = Vol(set1, mb);
+ whole_w = Vol(set1, wt);
+
+ maxr = Maximize(set1, RED, set1->r0 + 1, set1->r1, &cutr,
+ whole_r, whole_g, whole_b, whole_w);
+ maxg = Maximize(set1, GREEN, set1->g0 + 1, set1->g1, &cutg,
+ whole_r, whole_g, whole_b, whole_w);
+ maxb = Maximize(set1, BLUE, set1->b0 + 1, set1->b1, &cutb,
+ whole_r, whole_g, whole_b, whole_w);
+
+ if ((maxr >= maxg) && (maxr >= maxb)) {
+ dir = RED;
+ if (cutr < 0)
+ return 0; /* can't split the box */
+ }
+ else if ((maxg >= maxr) && (maxg >= maxb))
+ dir = GREEN;
+ else
+ dir = BLUE;
+
+ set2->r1 = set1->r1;
+ set2->g1 = set1->g1;
+ set2->b1 = set1->b1;
+
+ switch (dir) {
+ case RED:
+ set2->r0 = set1->r1 = cutr;
+ set2->g0 = set1->g0;
+ set2->b0 = set1->b0;
+ break;
+ case GREEN:
+ set2->g0 = set1->g1 = cutg;
+ set2->r0 = set1->r0;
+ set2->b0 = set1->b0;
+ break;
+ case BLUE:
+ set2->b0 = set1->b1 = cutb;
+ set2->r0 = set1->r0;
+ set2->g0 = set1->g0;
+ break;
+ }
+ set1->vol = (set1->r1 - set1->r0) * (set1->g1 - set1->g0) * (set1->b1 - set1->b0);
+ set2->vol = (set2->r1 - set2->r0) * (set2->g1 - set2->g0) * (set2->b1 - set2->b0);
+ return 1;
+}
+
+long cQuantizeWu::Vol(struct box * cube, long mmt[BOX][BOX][BOX]) {
+ return (mmt[cube->r1][cube->g1][cube->b1]
+ - mmt[cube->r1][cube->g1][cube->b0]
+ - mmt[cube->r1][cube->g0][cube->b1]
+ + mmt[cube->r1][cube->g0][cube->b0]
+ - mmt[cube->r0][cube->g1][cube->b1]
+ + mmt[cube->r0][cube->g1][cube->b0]
+ + mmt[cube->r0][cube->g0][cube->b1]
+ - mmt[cube->r0][cube->g0][cube->b0]);
+}
+
+float cQuantizeWu::Maximize(struct box * cube, unsigned char dir, int first, int last, int * cut, long whole_r, long whole_g, long whole_b, long whole_w) {
+ long half_r;
+ long half_g;
+ long half_b;
+ long half_w;
+ long base_r;
+ long base_g;
+ long base_b;
+ long base_w;
+ int i;
+ float temp;
+ float max;
+
+ base_r = Bottom(cube, dir, mr);
+ base_g = Bottom(cube, dir, mg);
+ base_b = Bottom(cube, dir, mb);
+ base_w = Bottom(cube, dir, wt);
+ max = 0.0;
+ *cut = -1;
+ for (i = first; i < last; i++) {
+ half_r = base_r + Top(cube, dir, i, mr);
+ half_g = base_g + Top(cube, dir, i, mg);
+ half_b = base_b + Top(cube, dir, i, mb);
+ half_w = base_w + Top(cube, dir, i, wt);
+ // now half_x is sum over lower half of box, if split at i
+ if (half_w == 0) {
+ // subbox could be empty of pixels!
+ continue; // never split into an empty box
+ }
+ else
+ temp = ((float) half_r * half_r + (float) half_g * half_g + (float) half_b * half_b) / half_w;
+
+ half_r = whole_r - half_r;
+ half_g = whole_g - half_g;
+ half_b = whole_b - half_b;
+ half_w = whole_w - half_w;
+ if (half_w == 0) {
+ // subbox could be empty of pixels!
+ continue; // never split into an empty box
+ }
+ else
+ temp += ((float) half_r * half_r + (float) half_g * half_g + (float) half_b * half_b) / half_w;
+
+ if (temp > max) {
+ max = temp;
+ *cut = i;
+ }
+ }
+ return (max);
+}
+
+float cQuantizeWu::Var(struct box * cube) {
+ float dr;
+ float dg;
+ float db;
+ float xx;
+
+ dr = Vol(cube, mr);
+ dg = Vol(cube, mg);
+ db = Vol(cube, mb);
+ xx = m2[cube->r1][cube->g1][cube->b1]
+ - m2[cube->r1][cube->g1][cube->b0]
+ - m2[cube->r1][cube->g0][cube->b1]
+ + m2[cube->r1][cube->g0][cube->b0]
+ - m2[cube->r0][cube->g1][cube->b1]
+ + m2[cube->r0][cube->g1][cube->b0]
+ + m2[cube->r0][cube->g0][cube->b1]
+ - m2[cube->r0][cube->g0][cube->b0];
+
+ return (xx - (dr * dr + dg * dg + db * db) / (float) Vol(cube, wt));
+}
+
+long cQuantizeWu::Bottom(struct box * cube, unsigned char dir, long mmt[BOX][BOX][BOX]) {
+ switch (dir) {
+ case RED:
+ return (-mmt[cube->r0][cube->g1][cube->b1]
+ + mmt[cube->r0][cube->g1][cube->b0]
+ + mmt[cube->r0][cube->g0][cube->b1]
+ - mmt[cube->r0][cube->g0][cube->b0]);
+ case GREEN:
+ return (-mmt[cube->r1][cube->g0][cube->b1]
+ + mmt[cube->r1][cube->g0][cube->b0]
+ + mmt[cube->r0][cube->g0][cube->b1]
+ - mmt[cube->r0][cube->g0][cube->b0]);
+ case BLUE:
+ return (-mmt[cube->r1][cube->g1][cube->b0]
+ + mmt[cube->r1][cube->g0][cube->b0]
+ + mmt[cube->r0][cube->g1][cube->b0]
+ - mmt[cube->r0][cube->g0][cube->b0]);
+ }
+ printf("music: quantize: error in Bottom()");
+ return 0;
+}
+
+long cQuantizeWu::Top(struct box * cube, unsigned char dir, int pos, long mmt[BOX][BOX][BOX]) {
+ switch (dir) {
+ case RED:
+ return (mmt[pos][cube->g1][cube->b1]
+ - mmt[pos][cube->g1][cube->b0]
+ - mmt[pos][cube->g0][cube->b1]
+ + mmt[pos][cube->g0][cube->b0]);
+ case GREEN:
+ return (mmt[cube->r1][pos][cube->b1]
+ - mmt[cube->r1][pos][cube->b0]
+ - mmt[cube->r0][pos][cube->b1]
+ + mmt[cube->r0][pos][cube->b0]);
+ case BLUE:
+ return (mmt[cube->r1][cube->g1][pos]
+ - mmt[cube->r1][cube->g0][pos]
+ - mmt[cube->r0][cube->g1][pos]
+ + mmt[cube->r0][cube->g0][pos]);
+ }
+ printf("error in Top()");
+ return 0;
+}
+
+void cQuantizeWu::Mark(struct box * cube, int label, unsigned char * tag) {
+ int r;
+ int g;
+ int b;
+
+ for (r = cube->r0 + 1; r <= cube->r1; r++)
+ for (g = cube->g0 + 1; g <= cube->g1; g++)
+ for (b = cube->b0 + 1; b <= cube->b1; b++)
+ tag[(r << 10) + (r << 6) + r + (g << 5) + g + b] = label;
+
+}
diff --git a/quantize.h b/quantize.h
new file mode 100644
index 0000000..737d234
--- /dev/null
+++ b/quantize.h
@@ -0,0 +1,70 @@
+#ifndef VDR_GRAPHTFT_QUANTIZE_H
+#define VDR_GRAPHTFT_QUANTIZE_H
+
+#define MAXCOLOR 256
+
+class cQuantize
+{
+ protected:
+ unsigned int paletteOutput[MAXCOLOR];
+ unsigned char imageOutput[1024*1024];
+ public:
+ cQuantize(){};
+ virtual ~cQuantize(){};
+ virtual int Quantize(unsigned char * input, int size, int colors) = 0;
+ unsigned int * OutputPalette() { return paletteOutput; }
+ unsigned char * OutputImage() { return imageOutput; }
+};
+
+#define RED 2
+#define GREEN 1
+#define BLUE 0
+
+//#define NOINVERT // RGB or BGR
+
+struct box
+{
+ int r0;
+ int r1;
+ int g0;
+ int g1;
+ int b0;
+ int b1;
+ int vol;
+};
+
+// Histogram is in elements 1..HISTSIZE along each axis,
+// element 0 is for base or marginal value.
+// NB: these must start out 0!
+#define BOX 33
+
+class cQuantizeWu : public cQuantize
+{
+ private:
+ long wt[BOX][BOX][BOX];
+ long mr[BOX][BOX][BOX];
+ long mg[BOX][BOX][BOX];
+ long mb[BOX][BOX][BOX];
+ float m2[BOX][BOX][BOX];
+
+ unsigned short * Qadd;
+
+ unsigned char * imageInput;
+ int imageSize;
+ int palSize;
+
+ void Hist3d(long *vwt, long *vmr, long *vmg, long *vmb, float *m_2);
+ void Momt3d(long *vwt, long *vmr, long *vmg, long *vmb, float *m_2);
+ int Cut(struct box * set1, struct box * set2);
+ long Vol(struct box * cube, long mmt[BOX][BOX][BOX]);
+ float Maximize(struct box * cube, unsigned char dir, int first, int last, int * cut, long whole_r, long whole_g, long whole_b, long whole_w);
+ float Var(struct box * cube);
+ long Top(struct box * cube, unsigned char dir, int pos, long mmt[BOX][BOX][BOX]);
+ long Bottom(struct box * cube, unsigned char dir, long mmt[BOX][BOX][BOX]);
+ void Mark(struct box * cube, int label, unsigned char * tag);
+ public:
+ cQuantizeWu();
+ virtual ~cQuantizeWu();
+ virtual int Quantize(unsigned char * input, int size, int colors);
+};
+#endif // VDR_GRAPHTFT_QUANTIZE_H
diff --git a/scripts/COPYRIGHT b/scripts/COPYRIGHT
index b0345b9..69933c4 100644
--- a/scripts/COPYRIGHT
+++ b/scripts/COPYRIGHT
@@ -1,3 +1,7 @@
+googlyrics is an adapted copy from
+http://www.kde-apps.org/content/show.php/GoogLyrics?content=73850
+
+
The content of languages.txt is generated from the file
iso_639.xml which contains this copyright:
diff --git a/scripts/googlyrics b/scripts/googlyrics
new file mode 100755
index 0000000..62d6532
--- /dev/null
+++ b/scripts/googlyrics
@@ -0,0 +1,409 @@
+#!/usr/bin/perl
+use strict; # Disabled for release version
+use warnings;
+use WWW::Mechanize;
+use IO::File;
+use HTML::Entities;
+use Text::Iconv;
+
+#Necessary globals
+our $mech = WWW::Mechanize->new();
+$mech->agent_alias( 'Linux Mozilla' );
+
+#Sites used for URL matching
+
+my %metro = (
+ site => "metrolyrics.com",
+ name => "Metrolyrics",
+ regex => qr/Ringtone \*\*\*<\/a>(.*?)<img/msi,
+ disabled => 0,
+ plain => 0,
+);
+
+my %freel = (
+ site => "free-lyrics.net",
+ name => "Free-Lyrics",
+ regex => qr/<td class="style5" style="font-weight:normal;padding-left:5px;">(.*?)<\/td>/msi,
+ disabled => 0,
+ plain => 0,
+);
+
+my %hotly = (
+ site => "hotlyrics.net",
+ name => "Hot Lyrics",
+ regex => qr/<!-- GOOGLE END \/\/-->(.*?)<script type="text\/javascript">/msi,
+ disabled => 0,
+ plain => 0,
+);
+my %leos = (
+ site => "leoslyrics.com",
+ name => "Leo's Lyrics",
+ regex => qr/<font face="Trebuchet MS, Verdana, Arial" size=-1>(.*?)<\/font>/msi,
+ disabled => 0,
+ plain => 0,
+);
+
+my %mma = (
+ site => "themadmusicarchive.com",
+ name => "The Mad Music Archive",
+ regex => qr/<td><span class="Verdana8">(.*?)<\/span>/msi,
+ disabled => 0,
+ plain => 0,
+);
+
+my %lyricspy = (
+ site => "lyricspy.com",
+ name => "Lyricspy",
+ regex => qr/<\/b><br \/>(.*?)<div>/msi,
+ disabled => 0,
+ plain => 0,
+);
+
+my %lyricwiki = (
+ site => "lyricwiki.org",
+ name => "Lyricwiki",
+ regex => qr/<div id="lyric">(.*?)<\/div/msi,
+ disabled => 0,
+ plain => 0,
+);
+
+my %lyriki = (
+ site => "lyriki.org",
+ name => "Lyriki",
+ regex => qr/<\/div>\n<p>(.*?)<\/p>/msi,
+ disabled => 0,
+ plain => 0,
+);
+
+my %lyricsmania = (
+ site => "lyricsmania.com",
+ name => "Lyricsmania",
+ regex => qr/Title: <b>.*?<br><br>(.*?)<script/msi,
+ disabled => 0,
+ plain => 0,
+);
+
+my %letssingit = (
+ site => "letssingit.com",
+ name => "Let's Sing It",
+ regex => qr/<TR class=row2><TD><PRE>(.*)<\/PRE><SPAN class=credits>/msi,
+ disabled => 0,
+ plain => 1,
+);
+
+my %sing365 = (
+ site => "sing365.com",
+ name => "Sing365",
+ #regex => qr/Print the Lyrics(.*?)<hr size=1 color=#cccccc>/msi,
+ regex => qr|Ringtones</u> <<(.*?)<TABLE cellSpacing="0"|msi,
+ disabled => 0,
+ plain => 0,
+);
+
+my %azlyrics = (
+ site => "azlyrics.com",
+ name => "AZLyrics",
+ regex => qr/<FONT size=2>.*?<BR>\s*(.*?)\[ <a href="http:\/\/www.azlyrics.com">www.azlyrics.com<\/a> \]<BR><BR>/msi,
+ disabled => 0,
+ plain => 0,
+);
+
+my %l007 = (
+ site => "lyrics007.com",
+ name => "Lyrics007",
+ #regex => qr/src=\"http:\/\/pagead2\.googlesyndication\.com\/pagead\/show_ads\.js\">\n<\/script>\n<br><br>(.*?)The hottest songs from/msi,
+ regex => qr|Ringtone <<(.*?)<a|msi,
+ disabled => 0,
+ plain => 0,
+);
+
+my %actionext = (
+ site => "actionext.com",
+ name => "Actionext",
+ regex => qr/<h3>performed by .*?<\/h3>(.*)<div class="foundat">/msi,
+ disabled => 0,
+ plain => 0,
+);
+
+my %songmeanings = (
+ site => "songmeanings.net",
+ name => "Song Meanings",
+ regex => qr/<td width="100%" style="text-align:left;">.*<td width="100%" style="text-align:left;">\s*(.*?)\s*<\/td>/msi,
+ disabled => 0,
+ plain => 0,
+);
+
+my %wearethelyrics = (
+ site => "wearethelyrics.com",
+ name => "We Are The Lyrics",
+ regex => qr/<\/h3>\n<p>\s*(.*?)\s*<\/p>/msi,
+ disabled => 0,
+ plain => 0,
+);
+
+my %mp3bg = (
+ site => "mp3-bg.com",
+ name => "mp3-bg.com",
+ regex => qr/<\/h2><p>(.*?)<ul class="admin">/msi,
+ disabled => 0,
+ plain => 0,
+);
+
+my %mldb = (
+ site => "mldb.org",
+ name => "MLDb",
+ regex => qr/<p class=songtext>(.*?)<\/table>/msi,
+ disabled => 0,
+ plain => 0,
+);
+
+my %justsomelyrics = (
+ site => "justsomelyrics.com",
+ name => "JUST SOME LYRICS",
+ regex => qr/<\/h1>(.*?)<a/msi,
+ disabled => 0,
+ plain => 0,
+);
+
+my %mylyricsbox = (
+ site => "mylyricsbox.com",
+ name => "MyLyricsBox",
+ regex => qr/<div class="songLyrics">(.*?)<\/div>/msi,
+ disabled => 0,
+ plain => 0,
+);
+
+my %megalyrics = (
+ site => "megalyrics.ru",
+ name => "MegaLyrics",
+ regex => qr/<\/script>[[:cntrl:]]*?<br><br>(.*?)<br><a href=\"javascript/msi,
+ disabled => 0,
+ plain => 0,
+);
+
+my %lyricsee = (
+ site => "lyrics.ee",
+ name => "Lyrics.ee",
+ regex => qr|</td></tr> -->*?<br>\n(.*?)<p><br>|msi,
+ disabled => 0,
+ plain => 0,
+);
+
+my %lyricseeprint = (
+ site => "lyrics.ee",
+ name => "Lyrics.ee (print page)",
+ regex => qr|<td height="20"></td>(.*?)</td>|msi,
+ disabled => 0,
+ plain => 0,
+);
+
+my %kovach = (
+ site => "kovach.co.yu",
+ name => "Kovach",
+ regex => qr#>Z</a>.*?<td width="100%" valign="top">(.*?)</td></tr></table>#msi,
+ disabled => 0,
+ plain => 0,
+);
+
+my %letras = (
+ site => "letras.terra.com.br",
+ name => "letras.terra.com.br",
+ regex => qr|<p id='cmp'>.*?</p>(.*?)</p><br/>|msi,
+ disabled => 0,
+ plain => 0,
+);
+
+my %lyricstime = (
+ site => "lyricstime.com",
+ name => "Lyrics Time",
+ regex => qr|.*END ADREACTOR ADVANCED CODE BLOCK -->(.*?)<!-- main content end|msi,
+ disabled => 0,
+ plain => 0,
+);
+
+my %lyricsspot = (
+ site => "lyricsspot.com",
+ name => "Lyricsspot",
+ regex => qr/<\/h3><font size="2">(.*?)<\/p><\/font>/msi,
+ disabled => 0,
+ plain => 0,
+);
+
+my %local = (
+ site => "~/lyrics",
+ name => "Local lyrics/cache",
+ disabled => 0,
+ plain => 1,
+);
+
+#put references to all the lyrics sites into the hash
+
+my @sites = (\%metro,\%freel,\%hotly,\%leos, \%mma, \%lyricspy, \%lyricwiki, \%lyriki, \%letssingit, \%sing365, \%azlyrics, \%l007, \%actionext, \%songmeanings, \%wearethelyrics, \%mp3bg, \%mldb, \%justsomelyrics, \%mylyricsbox, \%megalyrics, \%lyricsmania, \%lyricsee, \%lyricseeprint, \%kovach, \%letras, \%lyricstime, \%lyricsspot);
+
+
+sub querylyrics {
+ my $artist = urldecode(shift);
+ my $title = urldecode(shift);
+
+ # This is for local file lyrics
+ my $fh = new IO::File;
+
+ my $file = $title . ".txt";
+ my $file2 = $artist . " - " . $title . ".txt";
+ if (open(FH, "< " . $ENV{"HOME"} . "/lyrics/$file") || open(FH,"< " . $ENV{"HOME"} . "/lyrics/$file2")) {
+ my $text = "";
+ while ($_ = <FH>) {
+ $text .= $_
+ }
+ $fh->close;
+ showlyrics($text, \%local, "http://localhost", $artist, $title);
+ return 1;
+ }
+
+ $artist =~ s/^The //sgi; #Remove the starting word "The" from artist name, it just causes problems
+ $title =~ s/\(.*?\)//sgi;
+ $title =~ s/\[.*?\]//sgi;
+ if ($artist eq "") {
+ $title =~ /(.*) - (.*)/; # try to extract song + artist information.
+ if ($1 ne '' && $2 ne '') {
+ $artist = $1;
+ $title = $2;
+ }
+ }
+ my $attempt = 1;
+ while ($attempt != 5) {
+# print "\n<br>Attempt #" . $attempt . "\n";
+ $mech->get("http://www.google.com/intl/en/");
+ if (!$mech->success()) {
+ return "connectfail";
+ }
+ # Try several search queries.
+ if ($attempt == 1) {
+ $mech->field("q", "lyrics intitle:\"$artist - $title\"", );
+ } elsif ($attempt == 2) {
+ $mech->field("q", "lyrics \"$artist\" intitle:\"$title\"", );
+ } elsif ($attempt == 3) {
+ $mech->field("q", "lyrics \"$artist\" \"$title\"", );
+ } elsif ($attempt == 4) {
+ $mech->field("q", "lyrics $artist $title", );
+ }
+ $mech->submit();
+ foreach ($mech->content() =~ m/<div class=g[\s>].*?<a href=\"(.*?)\"/img) {
+ my $url = $_;
+# print "\n<br>" . $url . "\n";
+ my $o;
+ my $ly;
+ foreach $ly (@sites) {
+ my $urlregex = $ly->{site};
+ if ($url =~ m/$urlregex/si) {
+ if ($o = scrape($url, $ly, $artist, $title)) {
+ return $o;
+ } else {
+ next;
+ }
+ }
+ }
+ }
+ $attempt = $attempt + 1;
+ }
+ return "Fail";
+}
+
+sub scrape {
+ my $loc = shift;
+ my $site = shift;
+ my $artist = shift;
+ my $title = shift;
+ if ($site->{disabled}) {
+ return 0;
+ }
+ $mech->get($loc);
+ if (!$mech->success()) {
+ return 0; #Assume the user _does_ have an internet connection since a previous test has happened on google, let's just say the lyrics site is down.
+ }
+ my @cont_type = $mech->response()->content_type;
+ $cont_type[1]=~ s/charset=(.*)/$1/ig; # Get the charset of the response
+ my $char_converter = Text::Iconv->new($cont_type[1], "UTF-8"); # Convert the response to UTF-8
+ my $current = $mech->content();
+ my $regex = $site->{regex};
+ if ($current =~ $regex) {
+# print "\n<br>Regex success for " . $site->{name} . "\n";
+ showlyrics($char_converter->convert($1), $site, $loc, $artist, $title);
+ return 1;
+ } else {
+# print "\n<br>Regex failed for " . $site->{name} . "\n";
+ return 0;
+ }
+}
+
+#while (1) {
+ my $message = <STDIN>;
+ chomp($message);
+ if ($message =~ /^configure/) {
+ system("dcop", "amarok", "playlist", "popupMessage", "This script does not require any configuration.");
+ } elsif ($message =~ /^fetchLyrics/) {
+ my @tofetch = split(/ /, $message);
+ my $artist = urldecode($tofetch[1]);
+ my $title = urldecode($tofetch[2]);
+ my $out = querylyrics($artist, $title);
+ if ($out eq "Fail") {
+ system("dcop", "amarok", "contextbrowser", "showLyrics", "<?xml version=\"1.0\" encoding=\"UTF-8\" ?> <suggestions page_url=\"http://www.google.org\">Failed to find any lyrics. Press refresh to try again.</suggestions>");
+ } elsif ($out eq "connectfail") {
+ system("dcop", "amarok", "contextbrowser", "showLyrics", ""); #communications errror, "send an empty string"
+ }
+ }
+#}
+
+sub showlyrics {
+ my $out = shift;
+ my $site = shift;
+ my $loc = shift;
+ my $artist = shift;
+ my $title = shift;
+ if ($site->{plain}) {
+ $out = striphtml($out);
+ } else {
+ $out = striphtml(htmllinebreak($out));
+ }
+ $out =~ s/^\s+|\s+$//g; #Kills leading and trailing whitespace.
+ $out =~ s/\[.*? lyrics on http:\/\/www\.metrolyrics\.com\]\n//g; #metrolyrics: we're sick of your bullshit.
+ print $out . "\n";
+ my $doc = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?> <lyrics site=\"" . encode_entities($site->{name}) ."\" site_url=\"" . encode_entities($site->{site}) . "\" page_url=\"" . encode_entities($loc) . "\" artist=\"" . filter($artist) . "\" title=\"" . filter($title) . "\">" . filter($out) . "</lyrics>";
+ my $fh = new IO::File;
+ my $file = $artist . " - " . $title . " - Saved.txt";
+ if ($fh->open("> " . $ENV{"HOME"} . "/lyrics/$file")) {
+ print $fh $out
+ }
+ system("dcop", "amarok", "contextbrowser", "showLyrics", $doc);
+}
+sub htmllinebreak {
+ my $out = shift;
+ $out =~ s/\n//sgi; #Kill normal linebreaks, we're going HTML :)
+ $out =~ s/<br>/\n/sgi;
+ $out =~ s/<br *\/?>/\n/sgi;
+ return $out;
+}
+
+sub filter {
+ my $text = shift;
+ $text =~ s/&/&amp;/go;
+ $text =~ s/</&lt;/go;
+ $text =~ s/>/&gt;/go;
+ $text =~ s/'/&apos;/go;
+ $text =~ s/`/&apos;/go;
+ $text =~ s/’/&apos;/go;
+ $text =~ s/"/&quot;/go;
+ return $text;
+}
+
+sub urldecode {
+ my $str = shift;
+ $str =~ s/%([A-Fa-f0-9]{2})/pack('C', hex($1))/seg;
+ return $str;
+}
+
+sub striphtml {
+ my $str = shift;
+ $str =~ s/\<[^\<]+\>//g;
+ return $str;
+}
diff --git a/scripts/googlyrics.diff b/scripts/googlyrics.diff
new file mode 100644
index 0000000..4e01f62
--- /dev/null
+++ b/scripts/googlyrics.diff
@@ -0,0 +1,49 @@
+--- /usr/share/apps/amarok/scripts/googlyrics/googlyrics 2008-02-13 23:52:25.000000000 +0100
++++ googlyrics 2008-03-27 13:18:20.000000000 +0100
+@@ -264,7 +264,7 @@
+ }
+ my $attempt = 1;
+ while ($attempt != 5) {
+- print "\n<br>Attempt #" . $attempt . "\n";
++# print "\n<br>Attempt #" . $attempt . "\n";
+ $mech->get("http://www.google.com/intl/en/");
+ if (!$mech->success()) {
+ return "connectfail";
+@@ -282,7 +282,7 @@
+ $mech->submit();
+ foreach ($mech->content() =~ m/<div class=g[\s>].*?<a href=\"(.*?)\"/img) {
+ my $url = $_;
+- print "\n<br>" . $url . "\n";
++# print "\n<br>" . $url . "\n";
+ my $o;
+ my $ly;
+ foreach $ly (@sites) {
+@@ -319,16 +319,16 @@
+ my $current = $mech->content();
+ my $regex = $site->{regex};
+ if ($current =~ $regex) {
+- print "\n<br>Regex success for " . $site->{name} . "\n";
++# print "\n<br>Regex success for " . $site->{name} . "\n";
+ showlyrics($char_converter->convert($1), $site, $loc, $artist, $title);
+ return 1;
+ } else {
+- print "\n<br>Regex failed for " . $site->{name} . "\n";
++# print "\n<br>Regex failed for " . $site->{name} . "\n";
+ return 0;
+ }
+ }
+
+-while (1) {
++#while (1) {
+ my $message = <STDIN>;
+ chomp($message);
+ if ($message =~ /^configure/) {
+@@ -344,7 +344,7 @@
+ system("dcop", "amarok", "contextbrowser", "showLyrics", ""); #communications errror, "send an empty string"
+ }
+ }
+-}
++#}
+
+ sub showlyrics {
+ my $out = shift;
diff --git a/scripts/muggle-image-convert b/scripts/muggle-image-convert
index 34ed8d5..3348a69 100755
--- a/scripts/muggle-image-convert
+++ b/scripts/muggle-image-convert
@@ -9,17 +9,25 @@ FORMAT=pal
# target image width/height (taking into account visible screen area)
if [ "$FORMAT" = "ntsc" ]; then
- TW=600
- TH=420
+ TW=704
+ TH=480
else
- TW=632
- TH=512
+ TW=704
+ TH=576
fi
TMP=$(mktemp ${TMPDIR:-/tmp}/image_convert.pnm.XXXXXX) || exit 2
IMG=$1
MPG=$2
+left=$3
+top=$4
+width=$5
+height=$6
+right=`expr $TW - $left - $width`
+bottom=`expr $TH - $top - $height`
+
+
DIR=$(dirname "$MPG")
if [ ! -d "$DIR" ]; then
mkdir -p "$DIR" || exit 2
@@ -60,27 +68,19 @@ case "$FILE_TYPE" in
;;
esac
-#
-# extract the image size & compute scale value
-#
-LANG=C # get the decimal point right
$TO_PNM "$IMG" >"$TMP" 2>/dev/null
-S=$(pnmfile "$TMP" | awk '{ printf "%d %d ",$4,$6 }')
-S=$(echo $S $TW $TH | awk '{ sw=$3/$1; sh=$4/$2; s=(sw<sh)?sw:sh; printf "%.4f\n",(s>1)?1.0:s; }')
-#
-# now run the conversion
-#
if [ "$FORMAT" = "ntsc" ]; then
- pnmscale $S "$TMP" | \
- pnmpad -black -width 704 -height 480 | \
- ppmntsc | \
- ppmtoy4m -v 0 -n 1 -r -S 420mpeg2 -F 30000:1001 | \
+# untested
+ pnmscale -width $width -height $height "$TMP" |
+ pnmpad -black -left $left -top $top -right $right -bottom $bottom |
+ ppmntsc |
+ ppmtoy4m -v 0 -n 1 -r -S 420mpeg2 -F 30000:1001 |
mpeg2enc -f 7 -T 90 -F 4 -nn -a 2 -v 0 -o "$MPG"
else
- pnmscale $S "$TMP" | \
- pnmpad -black -width 704 -height 576 | \
- ppmntsc --pal | \
- ppmtoy4m -v 0 -n 1 -r -S 420mpeg2 -F 25:1 | \
+ pnmscale -width $width -height $height "$TMP" |
+ pnmpad -black -left $left -top $top -right $right -bottom $bottom |
+ ppmntsc --pal |
+ ppmtoy4m -v 0 -n 1 -r -S 420mpeg2 -F 25:1 |
mpeg2enc -f 7 -T 90 -F 3 -np -a 2 -v 0 -o "$MPG"
fi
diff --git a/scripts/muggle_getlyrics b/scripts/muggle_getlyrics
new file mode 100755
index 0000000..80be1cf
--- /dev/null
+++ b/scripts/muggle_getlyrics
@@ -0,0 +1,35 @@
+rm -f "$3"
+txtfound=0
+artist=`echo $1 | sed 's/ /%20/'g`
+title=`echo $2 | sed 's/ /%20/'g`
+echo ich bin $0 mit $1 $2 $3 artist=$artist title=$title>> /tmp/log.wr
+echo fetchLyrics $artist $title | `dirname $0`/googlyrics 2>/dev/null |
+ sed 's/\x0d//g' |
+ sed 's/\xc2\xb4/\x27/g' |
+ sed 's/\xc3\x82\x27/\x27/g' |
+ sed 's/\xc3\x82/\x27/g' |
+ sed 's/\xc3\xb9/\x27/g' |
+ sed 's/\xe2\x80\x99/\x27/g' |
+ grep -ive 'NEW.*ringtones' |
+ recode HTML..utf8 |
+ sed 's/\xc2\x91/\x27/g' | # in unicode, those two are reserved for
+ sed 's/\xc2\x92/\x27/g' | # private use, but still some sites use them...
+while read line
+do
+# strip starting empty lines
+ notempty=0
+ test x"$line" = x || notempty=1;
+ test $notempty -eq 1 && txtfound=1;
+ test $txtfound -eq 1 -o $notempty -eq 1 && echo $line
+done > "$3".loading
+
+# use .loading because the file is already there when googlyrics starts
+# but muggle thinks we are done as soon as $3 exists
+
+if test -s "$3".loading
+then
+ mv "$3".loading "$3"
+else
+ rm -f "$3".loading
+fi
+test -s "$3" # we want the exit code
diff --git a/service.h b/service.h
new file mode 100644
index 0000000..10aac8e
--- /dev/null
+++ b/service.h
@@ -0,0 +1,42 @@
+/*
+ * MP3/MPlayer plugin to VDR (C++)
+ *
+ * (C) 2001-2006 Stefan Huelswitt <s.huelswitt@gmx.de>
+ *
+ * This code 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 code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef ___SERVICE_H
+#define ___SERVICE_H
+
+struct MP3ServiceData
+{
+ int result;
+ union
+ {
+ const char *filename;
+ } data;
+};
+
+struct MPlayerServiceData
+{
+ int result;
+ union
+ {
+ const char *filename;
+ } data;
+};
+#endif //___SERVICE_H
diff --git a/setup.h b/setup.h
new file mode 100644
index 0000000..07a2424
--- /dev/null
+++ b/setup.h
@@ -0,0 +1,26 @@
+/*
+ * MP3/MPlayer plugin to VDR (C++)
+ *
+ * (C) 2001-2005 Stefan Huelswitt <s.huelswitt@gmx.de>
+ *
+ * This code 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 code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef ___SETUP_H
+#define ___SETUP_H
+
+extern const char *mountscript;
+#endif //___SETUP_H
diff --git a/symbols/copy.xpm b/symbols/copy.xpm
new file mode 100644
index 0000000..0b8a094
--- /dev/null
+++ b/symbols/copy.xpm
@@ -0,0 +1,29 @@
+/* XPM */
+static const char * copy_xpm[] = {
+"24 24 2 1",
+". c #000000",
+"+ c #FFFFFF",
+"........................",
+".++++++++++++++++++++++.",
+".++++++++......++++++++.",
+".++++++..........++++++.",
+".+++++............+++++.",
+".++++..............++++.",
+".+++................+++.",
+".++..................++.",
+".++..................++.",
+".+........++++........+.",
+".+.......++++++.......+.",
+".+.......++++++.......+.",
+".+.......++++++.......+.",
+".+.......++++++.......+.",
+".+........++++........+.",
+".++..................++.",
+".++..................++.",
+".+++................+++.",
+".++++..............++++.",
+".+++++............+++++.",
+".++++++..........++++++.",
+".++++++++......++++++++.",
+".++++++++++++++++++++++.",
+"........................"};
diff --git a/symbols/delstar.xpm b/symbols/delstar.xpm
new file mode 100644
index 0000000..7408863
--- /dev/null
+++ b/symbols/delstar.xpm
@@ -0,0 +1,20 @@
+/* XPM */
+static const char * delstar_xpm[] = {
+"56 15 2 1",
+". c #000000",
+"+ c #FFFFFF",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++.......++++++..........+++..++++++++",
+"++++++++++++++++++++........+++++..........+++..++++++++",
+"++++++++++++++++++++..++++...++++..+++++++++++..++++++++",
+"++++++++++++++++++++..+++++...+++..+++++++++++..++++++++",
+"++++++++++++++++++++..++++++..+++..+++++++++++..++++++++",
+"++++++++++++++++++++..++++++..+++......+++++++..++++++++",
+"++++++++++++++++++++..++++++..+++......+++++++..++++++++",
+"++++++++++++++++++++..+++++...+++..+++++++++++..++++++++",
+"++++++++++++++++++++..++++...++++..+++++++++++..++++++++",
+"++++++++++++++++++++........+++++..........+++.........+",
+"++++++++++++++++++++.......++++++..........+++.........+",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++"};
diff --git a/symbols/fwd.xpm b/symbols/fwd.xpm
new file mode 100644
index 0000000..f1d2f6b
--- /dev/null
+++ b/symbols/fwd.xpm
@@ -0,0 +1,29 @@
+/* XPM */
+static const char * fwd_xpm[] = {
+"24 24 2 1",
+". c #FFFFFF",
+"+ c #000000",
+"........................",
+"........................",
+"........................",
+"........................",
+"........................",
+"......+.....+...........",
+"......++....++..........",
+"......+++...+++.........",
+"......++++..++++........",
+"......+++++.+++++.......",
+"......++++++++++++......",
+"......+++++++++++++.....",
+"......+++++++++++++.....",
+"......++++++++++++......",
+"......+++++.+++++.......",
+"......++++..++++........",
+"......+++...+++.........",
+"......++....++..........",
+"......+.....+...........",
+"........................",
+"........................",
+"........................",
+"........................",
+"........................"};
diff --git a/symbols/insert.xpm b/symbols/insert.xpm
new file mode 100644
index 0000000..568a761
--- /dev/null
+++ b/symbols/insert.xpm
@@ -0,0 +1,29 @@
+/* XPM */
+static const char * insert_xpm[] = {
+"24 24 2 1",
+". c #FFFFFF",
+"+ c #000000",
+"........................",
+"........................",
+"........................",
+"........................",
+"........................",
+"...........+.....+......",
+"..........++....++......",
+".........+++...+++......",
+"........++++..++++......",
+".......+++++.+++++......",
+"......+++++++++++++++...",
+".....++++++++++++++++...",
+".....++++++++++++++++...",
+"......+++++++++++++++...",
+".......+++++.+++++......",
+"........++++..++++......",
+".........+++...+++......",
+"..........++....++......",
+"...........+.....+......",
+"........................",
+"........................",
+"........................",
+"........................",
+"........................"};
diff --git a/symbols/loop.xpm b/symbols/loop.xpm
new file mode 100644
index 0000000..3dc5e3d
--- /dev/null
+++ b/symbols/loop.xpm
@@ -0,0 +1,29 @@
+/* XPM */
+static const char * loop_xpm[] = {
+"24 24 2 1",
+". c #000000",
+"+ c #FFFFFF",
+"........................",
+"........................",
+"........................",
+"...++++++++++++++++++...",
+"...+++++++++++.++++++...",
+"...+++++++++++..+++++...",
+"...+...+++++++...++++...",
+"...+..............+++...",
+"...+...............++...",
+"...+................+...",
+"...++++++++++++++++++...",
+"...++++++++++++++++++...",
+"...++++++++++++++++++...",
+"...++++++++++++++++++...",
+"...+................+...",
+"...++...............+...",
+"...+++..............+...",
+"...++++...+++++++...+...",
+"...+++++..+++++++++++...",
+"...++++++.+++++++++++...",
+"...++++++++++++++++++...",
+"........................",
+"........................",
+"........................"};
diff --git a/symbols/loopall.xpm b/symbols/loopall.xpm
new file mode 100644
index 0000000..5b792d6
--- /dev/null
+++ b/symbols/loopall.xpm
@@ -0,0 +1,29 @@
+/* XPM */
+static const char * loopall_xpm[] = {
+"24 24 2 1",
+". c #000000",
+"+ c #FFFFFF",
+"........................",
+"........................",
+"........................",
+"...++++++++++++++++++...",
+"...+++++..++++..+++++...",
+"...++++...++++..+++++...",
+"...+++....++++..+++++...",
+"...++.....++++..+++++...",
+"...+......++++..+++++...",
+"...+++++..++++..+++++...",
+"...+++++..++++..+++++...",
+"...+++++..++++..+++++...",
+"...+++++..++++..+++++...",
+"...+++++..++++..+++++...",
+"...+++++..++++..+++++...",
+"...+++++..++++......+...",
+"...+++++..++++.....++...",
+"...+++++..++++....+++...",
+"...+++++..++++...++++...",
+"...+++++..++++..+++++...",
+"...++++++++++++++++++...",
+"........................",
+"........................",
+"........................"};
diff --git a/symbols/pause.xpm b/symbols/pause.xpm
new file mode 100644
index 0000000..31ec497
--- /dev/null
+++ b/symbols/pause.xpm
@@ -0,0 +1,29 @@
+/* XPM */
+static const char * pause_xpm[] = {
+"24 24 2 1",
+". c #FFFFFF",
+"+ c #000000",
+"........................",
+"........................",
+"........................",
+"........................",
+"........................",
+"........................",
+"......++++....++++......",
+"......++++....++++......",
+"......++++....++++......",
+"......++++....++++......",
+"......++++....++++......",
+"......++++....++++......",
+"......++++....++++......",
+"......++++....++++......",
+"......++++....++++......",
+"......++++....++++......",
+"......++++....++++......",
+"......++++....++++......",
+"........................",
+"........................",
+"........................",
+"........................",
+"........................",
+"........................"};
diff --git a/symbols/play.xpm b/symbols/play.xpm
new file mode 100644
index 0000000..cd66ef3
--- /dev/null
+++ b/symbols/play.xpm
@@ -0,0 +1,29 @@
+/* XPM */
+static const char * play_xpm[] = {
+"24 24 2 1",
+". c #FFFFFF",
+"+ c #000000",
+"........................",
+"........................",
+"........................",
+"........................",
+"........................",
+"........................",
+".......+++..++..........",
+".......+++..+++.........",
+".......+++..++++........",
+".......+++..+++++.......",
+".......+++..++++++......",
+".......+++..+++++++.....",
+".......+++..+++++++.....",
+".......+++..++++++......",
+".......+++..+++++.......",
+".......+++..++++........",
+".......+++..+++.........",
+".......+++..++..........",
+"........................",
+"........................",
+"........................",
+"........................",
+"........................",
+"........................"};
diff --git a/symbols/rate00.xpm b/symbols/rate00.xpm
new file mode 100644
index 0000000..b1f9d6c
--- /dev/null
+++ b/symbols/rate00.xpm
@@ -0,0 +1,20 @@
+/* XPM */
+static const char * rate00_xpm[] = {
+"56 15 2 1",
+". c #000000",
+"+ c #FFFFFF",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++"};
diff --git a/symbols/rate05.xpm b/symbols/rate05.xpm
new file mode 100644
index 0000000..ba5006e
--- /dev/null
+++ b/symbols/rate05.xpm
@@ -0,0 +1,20 @@
+/* XPM */
+static const char * rate05_xpm[] = {
+"56 15 2 1",
+". c #000000",
+"+ c #FFFFFF",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++.+++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++..++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++...+++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++....++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++.....+",
+"++++++++++++++++++++++++++++++++++++++++++++++++++....++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++...+++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++..++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++.+++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++"};
diff --git a/symbols/rate10.xpm b/symbols/rate10.xpm
new file mode 100644
index 0000000..6ea10a0
--- /dev/null
+++ b/symbols/rate10.xpm
@@ -0,0 +1,21 @@
+/* XPM */
+static const char * rate10_xpm[] = {
+"56 15 3 1",
+" c None",
+". c #000000",
+"+ c #FFFFFF",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"+++++++++++++++++++++++++++++++++++++++++++++++++..+++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++....++++",
+"+++++++++++++++++++++++++++++++++++++++++++++++......+++",
+"++++++++++++++++++++++++++++++++++++++++++++++........++",
+"+++++++++++++++++++++++++++++++++++++++++++++..........+",
+"++++++++++++++++++++++++++++++++++++++++++++++........++",
+"+++++++++++++++++++++++++++++++++++++++++++++++......+++",
+"++++++++++++++++++++++++++++++++++++++++++++++++....++++",
+"+++++++++++++++++++++++++++++++++++++++++++++++++..+++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++"};
diff --git a/symbols/rate15.xpm b/symbols/rate15.xpm
new file mode 100644
index 0000000..38243ea
--- /dev/null
+++ b/symbols/rate15.xpm
@@ -0,0 +1,21 @@
+/* XPM */
+static const char * rate15_xpm[] = {
+"56 15 3 1",
+" c None",
+". c #000000",
+"+ c #FFFFFF",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"+++++++++++++++++++++++++++++++++++++++.+++++++++..+++++",
+"+++++++++++++++++++++++++++++++++++++++..+++++++....++++",
+"+++++++++++++++++++++++++++++++++++++++...+++++......+++",
+"+++++++++++++++++++++++++++++++++++++++....+++........++",
+"+++++++++++++++++++++++++++++++++++++++.....+..........+",
+"+++++++++++++++++++++++++++++++++++++++....+++........++",
+"+++++++++++++++++++++++++++++++++++++++...+++++......+++",
+"+++++++++++++++++++++++++++++++++++++++..+++++++....++++",
+"+++++++++++++++++++++++++++++++++++++++.+++++++++..+++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++"};
diff --git a/symbols/rate20.xpm b/symbols/rate20.xpm
new file mode 100644
index 0000000..f309a45
--- /dev/null
+++ b/symbols/rate20.xpm
@@ -0,0 +1,21 @@
+/* XPM */
+static const char * rate20_xpm[] = {
+"56 15 3 1",
+" c None",
+". c #000000",
+"+ c #FFFFFF",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++..+++++++++..+++++",
+"+++++++++++++++++++++++++++++++++++++....+++++++....++++",
+"++++++++++++++++++++++++++++++++++++......+++++......+++",
+"+++++++++++++++++++++++++++++++++++........+++........++",
+"++++++++++++++++++++++++++++++++++..........+..........+",
+"+++++++++++++++++++++++++++++++++++........+++........++",
+"++++++++++++++++++++++++++++++++++++......+++++......+++",
+"+++++++++++++++++++++++++++++++++++++....+++++++....++++",
+"++++++++++++++++++++++++++++++++++++++..+++++++++..+++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++"};
diff --git a/symbols/rate25.xpm b/symbols/rate25.xpm
new file mode 100644
index 0000000..4d91368
--- /dev/null
+++ b/symbols/rate25.xpm
@@ -0,0 +1,21 @@
+/* XPM */
+static const char * rate25_xpm[] = {
+"56 15 3 1",
+" c None",
+". c #000000",
+"+ c #FFFFFF",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++.+++++++++..+++++++++..+++++",
+"++++++++++++++++++++++++++++..+++++++....+++++++....++++",
+"++++++++++++++++++++++++++++...+++++......+++++......+++",
+"++++++++++++++++++++++++++++....+++........+++........++",
+"++++++++++++++++++++++++++++.....+..........+..........+",
+"++++++++++++++++++++++++++++....+++........+++........++",
+"++++++++++++++++++++++++++++...+++++......+++++......+++",
+"++++++++++++++++++++++++++++..+++++++....+++++++....++++",
+"++++++++++++++++++++++++++++.+++++++++..+++++++++..+++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++"};
diff --git a/symbols/rate30.xpm b/symbols/rate30.xpm
new file mode 100644
index 0000000..d15b5ab
--- /dev/null
+++ b/symbols/rate30.xpm
@@ -0,0 +1,21 @@
+/* XPM */
+static const char * rate30_xpm[] = {
+"56 15 3 1",
+" c None",
+". c #000000",
+"+ c #FFFFFF",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"+++++++++++++++++++++++++++..+++++++++..+++++++++..+++++",
+"++++++++++++++++++++++++++....+++++++....+++++++....++++",
+"+++++++++++++++++++++++++......+++++......+++++......+++",
+"++++++++++++++++++++++++........+++........+++........++",
+"+++++++++++++++++++++++..........+..........+..........+",
+"++++++++++++++++++++++++........+++........+++........++",
+"+++++++++++++++++++++++++......+++++......+++++......+++",
+"++++++++++++++++++++++++++....+++++++....+++++++....++++",
+"+++++++++++++++++++++++++++..+++++++++..+++++++++..+++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++"};
diff --git a/symbols/rate35.xpm b/symbols/rate35.xpm
new file mode 100644
index 0000000..d892de7
--- /dev/null
+++ b/symbols/rate35.xpm
@@ -0,0 +1,21 @@
+/* XPM */
+static const char * rate35_xpm[] = {
+"56 15 3 1",
+" c None",
+". c #000000",
+"+ c #FFFFFF",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"+++++++++++++++++.+++++++++..+++++++++..+++++++++..+++++",
+"+++++++++++++++++..+++++++....+++++++....+++++++....++++",
+"+++++++++++++++++...+++++......+++++......+++++......+++",
+"+++++++++++++++++....+++........+++........+++........++",
+"+++++++++++++++++.....+..........+..........+..........+",
+"+++++++++++++++++....+++........+++........+++........++",
+"+++++++++++++++++...+++++......+++++......+++++......+++",
+"+++++++++++++++++..+++++++....+++++++....+++++++....++++",
+"+++++++++++++++++.+++++++++..+++++++++..+++++++++..+++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++"};
diff --git a/symbols/rate40.xpm b/symbols/rate40.xpm
new file mode 100644
index 0000000..2340a34
--- /dev/null
+++ b/symbols/rate40.xpm
@@ -0,0 +1,21 @@
+/* XPM */
+static const char * rate40_xpm[] = {
+"56 15 3 1",
+" c None",
+". c #000000",
+"+ c #FFFFFF",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++..+++++++++..+++++++++..+++++++++..+++++",
+"+++++++++++++++....+++++++....+++++++....+++++++....++++",
+"++++++++++++++......+++++......+++++......+++++......+++",
+"+++++++++++++........+++........+++........+++........++",
+"++++++++++++..........+..........+..........+..........+",
+"+++++++++++++........+++........+++........+++........++",
+"++++++++++++++......+++++......+++++......+++++......+++",
+"+++++++++++++++....+++++++....+++++++....+++++++....++++",
+"++++++++++++++++..+++++++++..+++++++++..+++++++++..+++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++"};
diff --git a/symbols/rate45.xpm b/symbols/rate45.xpm
new file mode 100644
index 0000000..f092a3e
--- /dev/null
+++ b/symbols/rate45.xpm
@@ -0,0 +1,21 @@
+/* XPM */
+static const char * rate45_xpm[] = {
+"56 15 3 1",
+" c None",
+". c #000000",
+"+ c #FFFFFF",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++.+++++++++..+++++++++..+++++++++..+++++++++..+++++",
+"++++++..+++++++....+++++++....+++++++....+++++++....++++",
+"++++++...+++++......+++++......+++++......+++++......+++",
+"++++++....+++........+++........+++........+++........++",
+"++++++.....+..........+..........+..........+..........+",
+"++++++....+++........+++........+++........+++........++",
+"++++++...+++++......+++++......+++++......+++++......+++",
+"++++++..+++++++....+++++++....+++++++....+++++++....++++",
+"++++++.+++++++++..+++++++++..+++++++++..+++++++++..+++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++"};
diff --git a/symbols/rate50.xpm b/symbols/rate50.xpm
new file mode 100644
index 0000000..6374855
--- /dev/null
+++ b/symbols/rate50.xpm
@@ -0,0 +1,21 @@
+/* XPM */
+static const char * rate50_xpm[] = {
+"56 15 3 1",
+" c None",
+". c #000000",
+"+ c #FFFFFF",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"+++++..+++++++++..+++++++++..+++++++++..+++++++++..+++++",
+"++++....+++++++....+++++++....+++++++....+++++++....++++",
+"+++......+++++......+++++......+++++......+++++......+++",
+"++........+++........+++........+++........+++........++",
+"+..........+..........+..........+..........+..........+",
+"++........+++........+++........+++........+++........++",
+"+++......+++++......+++++......+++++......+++++......+++",
+"++++....+++++++....+++++++....+++++++....+++++++....++++",
+"+++++..+++++++++..+++++++++..+++++++++..+++++++++..+++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++"};
diff --git a/symbols/rec.xpm b/symbols/rec.xpm
new file mode 100644
index 0000000..2efb6c5
--- /dev/null
+++ b/symbols/rec.xpm
@@ -0,0 +1,29 @@
+/* XPM */
+static const char * rec_xpm[] = {
+"24 24 2 1",
+". c #FFFFFF",
+"+ c #000000",
+"........................",
+"........................",
+"........................",
+"........................",
+"........................",
+"........................",
+".......+++++++..........",
+".......++++++++.........",
+"........++...+++........",
+"........++....++........",
+"........++....++........",
+"........++...+++........",
+"........+++++++.........",
+"........++++++..........",
+"........++..+++.........",
+"........++...+++........",
+"........++....+++.......",
+".......+++.....+++......",
+".......+++.....+++......",
+"........................",
+"........................",
+"........................",
+"........................",
+"........................"};
diff --git a/symbols/rew.xpm b/symbols/rew.xpm
new file mode 100644
index 0000000..7e7fcd8
--- /dev/null
+++ b/symbols/rew.xpm
@@ -0,0 +1,29 @@
+/* XPM */
+static const char * rew_xpm[] = {
+"24 24 2 1",
+". c #FFFFFF",
+"+ c #000000",
+"........................",
+"........................",
+"........................",
+"........................",
+"........................",
+"...........+.....+......",
+"..........++....++......",
+".........+++...+++......",
+"........++++..++++......",
+".......+++++.+++++......",
+"......++++++++++++......",
+".....+++++++++++++......",
+".....+++++++++++++......",
+"......++++++++++++......",
+".......+++++.+++++......",
+"........++++..++++......",
+".........+++...+++......",
+"..........++....++......",
+"...........+.....+......",
+"........................",
+"........................",
+"........................",
+"........................",
+"........................"};
diff --git a/symbols/shuffle.xpm b/symbols/shuffle.xpm
new file mode 100644
index 0000000..9076f93
--- /dev/null
+++ b/symbols/shuffle.xpm
@@ -0,0 +1,29 @@
+/* XPM */
+static const char * shuffle_xpm[] = {
+"24 24 2 1",
+". c #000000",
+"+ c #FFFFFF",
+"........................",
+"........................",
+"........................",
+"...++++++++++++++++++...",
+"...+........+++++++++...",
+"...+........+++++++++...",
+"...+........+++++++++...",
+"...++++++++++++++++++...",
+"...+.............++++...",
+"...+.............++++...",
+"...+.............++++...",
+"...++++++++++++++++++...",
+"...+.....++++++++++++...",
+"...+.....++++++++++++...",
+"...+.....++++++++++++...",
+"...++++++++++++++++++...",
+"...+................+...",
+"...+................+...",
+"...+................+...",
+"...++++++++++++++++++...",
+"...++++++++++++++++++...",
+"........................",
+"........................",
+"........................"};
diff --git a/symbols/stop.xpm b/symbols/stop.xpm
new file mode 100644
index 0000000..9e1fc55
--- /dev/null
+++ b/symbols/stop.xpm
@@ -0,0 +1,29 @@
+/* XPM */
+static const char * stop_xpm[] = {
+"24 24 2 1",
+". c #FFFFFF",
+"+ c #000000",
+"........................",
+"........................",
+"........................",
+"........................",
+"........................",
+"........................",
+"......++++++++++++......",
+"......++++++++++++......",
+"......++++++++++++......",
+"......++++++++++++......",
+"......++++++++++++......",
+"......++++++++++++......",
+"......++++++++++++......",
+"......++++++++++++......",
+"......++++++++++++......",
+"......++++++++++++......",
+"......++++++++++++......",
+"......++++++++++++......",
+"........................",
+"........................",
+"........................",
+"........................",
+"........................",
+"........................"};
diff --git a/vdr_actions.c b/vdr_actions.c
index 315d390..fffa668 100644
--- a/vdr_actions.c
+++ b/vdr_actions.c
@@ -2,7 +2,7 @@
* \file vdr_actions.c
* \brief Implements all actions for browsing media libraries within VDR
*
- * \version $Revision: 1.27 $
+ * \version $Revision: 1.27 $
* \date $Date: 2004-12-25 16:52:35 +0100 (Sat, 25 Dec 2004) $
* \author Wolfgang Rohdewald
* \author Responsible author: $Author: wr61 $
@@ -25,56 +25,58 @@
#include "mg_setup.h"
#include "vdr_actions.h"
-#include "vdr_menu.h"
-#include "i18n.h"
+#include "vdr_player.h"
+#include "lyrics.h"
#include <vdr/interface.h>
-#define DEBUG
#include "mg_tools.h"
#include "mg_thread_sync.h"
static bool
-IsEntry(mgActions i)
-{
+IsEntry(mgActions i) {
return i == actEntry;
}
-class mgOsdItem : public mgAction, public cOsdItem
-{
- public:
- eOSState ProcessKey(eKeys key) { return mgAction::ProcessKey(key); }
-};
-
-
void
-mgAction::setHandle(unsigned int handle)
-{
+mgAction::setHandle(unsigned int handle) {
m_handle = handle;
}
eOSState
-mgAction::ProcessKey(eKeys key)
-{
- eOSState result = Process(key);
- if (result != osUnknown)
- IgnoreNextEvent = true;
- return result;
+mgAction::ProcessKey(eKeys key) {
+ eOSState result = Process(key);
+ if (result != osUnknown)
+ IgnoreNextEvent = true;
+ return result;
}
class mgNone: public mgOsdItem
-{ public:
+{
+ public:
void Notify() {};
bool Enabled(mgActions on) { return false; }
eOSState Process(eKeys key) { return osUnknown; }
};
+class mgBack : public mgAction
+{
+ const char* ButtonName() { return tr("Parent"); }
+ bool Execute() { Back(); return true; }
+};
+
+class mgBack2 : public mgAction
+{
+ const char* ButtonName() { return tr("Parent"); }
+ bool Execute() { osd()->CloseMenu();Back(); return true; }
+};
+
//! \brief used for normal data base items
class mgEntry : public mgOsdItem
{
public:
void Notify();
bool Enabled(mgActions on) { return IsEntry(on);}
- const char *MenuName (const unsigned int idx,const mgListItem* item);
+ const char *MenuName (const unsigned int idx,const mgListItem* item);
eOSState Process(eKeys key);
bool Execute();
eOSState Back();
@@ -83,17 +85,17 @@ class mgEntry : public mgOsdItem
class mgKeyItem : public mgAction, public cMenuEditStraItem
{
public:
- mgKeyItem(const char *Name, int *Value, int NumStrings, const char *const *Strings) : cMenuEditStraItem(Name, Value, NumStrings, Strings) {}
- eOSState ProcessKey(eKeys key) { return mgAction::ProcessKey(key); }
- eOSState Process(eKeys key);
+ mgKeyItem(const char *Name, int *Value, int NumStrings, const char *const *Strings) : cMenuEditStraItem(Name, Value, NumStrings, Strings) {}
+ eOSState ProcessKey(eKeys key) { return mgAction::ProcessKey(key); }
+ eOSState Process(eKeys key);
};
class mgBoolItem: public mgAction, public cMenuEditBoolItem
{
public:
- mgBoolItem(const char *Name,int *Value) : cMenuEditBoolItem(Name, Value) {}
- eOSState ProcessKey(eKeys key) { return mgAction::ProcessKey(key); }
- eOSState Process(eKeys key);
+ mgBoolItem(const char *Name,int *Value) : cMenuEditBoolItem(Name, Value) {}
+ eOSState ProcessKey(eKeys key) { return mgAction::ProcessKey(key); }
+ eOSState Process(eKeys key);
};
class mgDoCollEntry : public mgEntry
@@ -115,78 +117,68 @@ class mgRemoveCollEntry : public mgDoCollEntry
};
void
-mgAction::TryNotify()
-{
- if (IgnoreNextEvent)
- IgnoreNextEvent = false;
- else
- Notify();
+mgAction::TryNotify() {
+ if (IgnoreNextEvent)
+ IgnoreNextEvent = false;
+ else
+ Notify();
}
eOSState
-mgDoCollEntry::Process(eKeys key)
-{
- mgMenu *n = osd ()->newmenu;
- osd ()->newmenu = NULL;
- eOSState result = osContinue;
- switch (key)
- {
- case kBack:
- break;
- case kOk:
- Execute ();
- break;
- default:
- osd ()->newmenu = n; // wrong key: stay in submenu
- result = osUnknown;
- break;
- }
- return result;
+mgDoCollEntry::Process(eKeys key) {
+ mgMenu *n = Selosd ()->newmenu;
+ Selosd ()->newmenu = NULL;
+ eOSState result = osContinue;
+ switch (key) {
+ case kBack:
+ break;
+ case kOk:
+ Execute ();
+ break;
+ default:
+ // wrong key: stay in submenu
+ Selosd ()->newmenu = n;
+ result = osUnknown;
+ break;
+ }
+ return result;
}
-
bool
-mgAddCollEntry::Execute()
-{
- string target = selection()->getCurrentValue();
- osd()->default_collection = target;
- if (target == osd()->play_collection)
- if (!PlayerControl())
- collselection()->ClearCollection(target);
-
- osd()->Message1 ("Added %s entries",itos (osd()->moveselection->AddToCollection (target)).c_str());
- osd()->CollectionChanged(target,true);
- return true;
+mgAddCollEntry::Execute() {
+ string target = selection()->getCurrentValue();
+ Selosd()->default_collection = target;
+ if (target == Selosd()->play_collection)
+ if (!PlayerControl())
+ collselection()->ClearCollection(target);
+
+ osd()->Message1 ("Added %s entries",itos (Selosd()->moveselection->AddToCollection (target)).c_str());
+ Selosd()->CollectionChanged(target,true);
+ return true;
}
-
bool
-mgRemoveCollEntry::Execute()
-{
- string target = selection()->getCurrentValue();
- int removed = osd()->moveselection->RemoveFromCollection (target);
- osd()->Message1 ("Removed %s entries",ltos(removed).c_str());
- osd()->CollectionChanged(target,false);
- return true;
+mgRemoveCollEntry::Execute() {
+ string target = selection()->getCurrentValue();
+ int removed = Selosd()->moveselection->RemoveFromCollection (target);
+ osd()->Message1 ("Removed %s entries",ltos(removed).c_str());
+ Selosd()->CollectionChanged(target,false);
+ return true;
}
-
void
-mgAction::Notify()
-{
+mgAction::Notify() {
m->SetHelpKeys(Type());
}
void
-mgAction::SetMenu(mgMenu *menu)
-{
+mgAction::SetMenu(mgMenu *menu) {
m = menu;
m_osd = m->osd();
}
void
-mgAction::SetText(const char *text,bool copy)
-{
+mgAction::SetText(const char *text,bool copy) {
cOsdItem *c = dynamic_cast<cOsdItem*>(this);
if (!c)
mgError("mgAction::SetText() on wrong type");
@@ -194,31 +186,26 @@ mgAction::SetText(const char *text,bool copy)
}
const char *
-mgAction::Text()
-{
+mgAction::Text() {
cOsdItem *c = dynamic_cast<cOsdItem*>(this);
if (!c)
mgError("mgAction::Text() on wrong type");
return c->Text();
}
-
bool
-mgAction::Enabled(mgActions on)
-{
+mgAction::Enabled(mgActions on) {
return true;
}
-mgAction::mgAction()
-{
+mgAction::mgAction() {
m = 0;
m_osd = 0;
m_handle = 0;
IgnoreNextEvent = false;
}
-mgAction::~mgAction()
-{
+mgAction::~mgAction() {
}
class mgCommand : public mgOsdItem
@@ -237,80 +224,176 @@ class mgActOrder : public mgOsdItem
};
const char*
-mgActOrder::MenuName(const unsigned int idx,const mgListItem* item)
-{
+mgActOrder::MenuName(const unsigned int idx,const mgListItem* item) {
return strdup(item->value().c_str());
}
eOSState
-mgActOrder::Process(eKeys key)
+mgActOrder::Process(eKeys key) {
+ mgMenu *n = osd ()->newmenu;
+ osd ()->newmenu = NULL;
+ eOSState result = osContinue;
+ switch (key) {
+ case kBack:
+ break;
+ case kOk:
+ if (Execute ())
+ break;
+ default:
+ osd ()->newmenu = n; // wrong key: stay in submenu
+ result = osUnknown;
+ break;
+ }
+ return result;
+}
+
+bool
+mgActOrder::Execute() {
+ return Selosd()->SwitchSelection();
+}
+
+class mgPrevTrack : public mgCommand
{
- mgMenu *n = osd ()->newmenu;
- osd ()->newmenu = NULL;
- eOSState result = osContinue;
- switch (key)
- {
- case kBack:
- break;
- case kOk:
- if (Execute ())
- break;
- default:
- osd ()->newmenu = n; // wrong key: stay in submenu
- result = osUnknown;
- break;
- }
- return result;
+ public:
+ bool Enabled(mgActions on) { return true; }
+ const char* ButtonName() { return tr("Track-"); }
+ bool Execute();
+};
+
+bool
+mgPrevTrack::Execute() {
+ mgPlayerControl *c = PlayerControl();
+ if (c)
+ c->Backward();
+ return c;
}
+class mgNextTrack : public mgCommand
+{
+ public:
+ bool Enabled(mgActions on) { return true; }
+ const char* ButtonName() { return tr("Track+"); }
+ bool Execute();
+};
+
bool
-mgActOrder::Execute()
+mgNextTrack::Execute() {
+ mgPlayerControl *c = PlayerControl();
+ if (c)
+ c->Forward();
+ return c;
+}
+
+class mgToggleShuffle : public mgCommand
{
- return osd()->SwitchSelection();
+ public:
+ bool Enabled(mgActions on) { return true; }
+ const char* MenuName(const unsigned int idx,const mgListItem* item);
+ bool Execute();
+};
+
+const char*
+mgToggleShuffle::MenuName(const unsigned int idx,const mgListItem* item) {
+ return strdup(tr("Change shuffle mode"));
}
bool
-mgCommand::Enabled(mgActions on)
+mgToggleShuffle::Execute() {
+ mgPlayerControl *c = PlayerControl();
+ if (c)
+ c->ToggleShuffle();
+ return c;
+}
+
+class mgShowLyrics : public mgCommand
+{
+ public:
+ bool Enabled(mgActions on) { return true; }
+ const char* ButtonName() { return tr("Lyrics"); }
+ bool Execute();
+};
+
+bool
+mgShowLyrics::Execute() {
+ osd()->newmenu = new mgLyrics;
+ osd()->newposition = 0;
+ return true;
+}
+
+class mgLoadExternalLyrics : public mgCommand
{
+ public:
+ bool Enabled(mgActions on) { return true; }
+ const char* ButtonName() { return tr("Load Lyrics"); }
+};
+
+class mgSaveExternalLyrics : public mgCommand
+{
+ public:
+ bool Enabled(mgActions on) { return true; }
+ const char* ButtonName() { return tr("Save Lyrics"); }
+};
+
+class mgToggleLoop : public mgCommand
+{
+ public:
+ bool Enabled(mgActions on) { return true; }
+ const char* MenuName(const unsigned int idx,const mgListItem* item);
+ bool Execute();
+};
+
+const char*
+mgToggleLoop::MenuName(const unsigned int idx,const mgListItem* item) {
+ return strdup(tr("Change loop mode"));
+}
+
+bool
+mgToggleLoop::Execute() {
+ mgPlayerControl *c = PlayerControl();
+ if (c)
+ c->ToggleLoop();
+ return c;
+}
+
+bool
+mgCommand::Enabled(mgActions on) {
return IsEntry(on);
}
mgSelection*
-mgAction::playselection ()
-{
- return m->playselection ();
+mgAction::playselection () {
+ return Selosd()->playselection();
}
-mgMainMenu*
-mgAction::osd ()
-{
+
+mgOsd*
+mgAction::osd () {
return m_osd;
}
+mgSelOsd*
+mgAction::Selosd () {
+ return dynamic_cast<mgSelOsd *>(m_osd);
+}
+
eOSState
-mgAction::Back()
-{
+mgAction::Back() {
osd()->newmenu = NULL;
return osContinue;
}
-
void
-mgEntry::Notify()
-{
+mgEntry::Notify() {
selection()->setPosition(m_handle);
selection()->gotoPosition();
- osd()->SaveState();
- mgAction::Notify(); // only after selection is updated
+ Selosd()->SaveState();
+ mgAction::Notify(); // only after selection is updated
}
-
const char *
-mgEntry::MenuName(const unsigned int idx,const mgListItem* item)
-{
+mgEntry::MenuName(const unsigned int idx,const mgListItem* item) {
char ct[20];
unsigned int selcount = item->count();
- if (!selection()->inItems())
- {
+ if (!selection()->inItems()) {
char numct[20];
sprintf(numct,"%u",selcount);
memset(ct,' ',19);
@@ -325,11 +408,10 @@ mgEntry::MenuName(const unsigned int idx,const mgListItem* item)
else
ct[0]=0;
char *result;
- if (selection()->isCollectionlist())
- {
- if (item->value() == osd()->default_collection)
+ if (selection()->isCollectionlist()) {
+ if (item->value() == Selosd()->default_collection)
msprintf(&result,"-> %s%s",ct,item->value().c_str());
- else
+ else
msprintf(&result," %s%s",ct,item->value().c_str());
}
else if (selection()->inCollection())
@@ -342,12 +424,10 @@ mgEntry::MenuName(const unsigned int idx,const mgListItem* item)
}
bool
-mgEntry::Execute()
-{
+mgEntry::Execute() {
if (selection()->inItems())
m->ExecuteAction(actInstantPlay,Type());
- else
- {
+ else {
selection()->enter();
osd()->forcerefresh = true;
}
@@ -355,105 +435,89 @@ mgEntry::Execute()
}
eOSState
-mgEntry::Process(eKeys key)
-{
- eOSState result = osUnknown;
+mgEntry::Process(eKeys key) {
+ eOSState result = osUnknown;
- mgTree *menu = dynamic_cast<mgTree*>(m); // und 0 abfangen
- switch (key)
- {
- case kOk:
- {
- if (menu)
- menu->TerminateIncrementalSearch( true );
- Execute();
-
- result = osContinue;
- } break;
- case k0...k9:
- {
- if (menu)
- {
- menu->UpdateIncrementalSearch( key );
- result = osContinue;
- }
- } break;
- case kBack:
- {
- if( menu && menu->UpdateIncrementalSearch( key ) )
- { // search is continued
- result = osContinue;
- }
- else
- { // search is not active at all
- result = Back();
- }
- } break;
- default:
- {
- if( key != kNone )
- {
- if (menu)
- menu->TerminateIncrementalSearch( true );
- }
- result = osUnknown;
- }
+ // und 0 abfangen
+ mgTree *menu = dynamic_cast<mgTree*>(m);
+ switch (key) {
+ case kOk:
+ {
+ if (menu)
+ menu->TerminateIncrementalSearch( true );
+ Execute();
+
+ result = osContinue;
+ } break;
+ case k0...k9:
+ {
+ if (menu) {
+ menu->UpdateIncrementalSearch( key );
+ result = osContinue;
+ }
+ } break;
+ case kBack:
+ {
+ if( menu && menu->UpdateIncrementalSearch( key ) ) {
+ // search is continued
+ result = osContinue;
+ }
+ else {
+ // search is not active at all
+ result = Back();
+ }
+ } break;
+ default:
+ {
+ if( key != kNone ) {
+ if (menu)
+ menu->TerminateIncrementalSearch( true );
+ }
+ result = osUnknown;
+ }
}
-
- return result;
-}
+ return result;
+}
eOSState
-mgEntry::Back()
-{
+mgEntry::Back() {
osd()->forcerefresh = true;
- if (!selection ()->leave ())
- osd()->newmenu = NULL;
+ if (selection()) {
+ if (!selection ()->leave ())
+ osd()->newmenu = NULL;
+ } else
+ osd()->newmenu=NULL;
return osContinue;
}
eOSState
-mgCommand::Process(eKeys key)
-{
- mgMenu *parent = osd ()->Parent ();
- mgMenu *n = osd ()->newmenu;
- osd ()->newmenu = NULL;
- eOSState result = osContinue;
- switch (key)
- {
- case kRed:
- if (osd()->UsingCollection)
- parent->CollRedAction = Type();
- else
- parent->TreeRedAction = Type();
- break;
- case kGreen:
- if (osd()->UsingCollection)
- parent->CollGreenAction = Type();
- else
- parent->TreeGreenAction = Type();
- break;
- case kYellow:
- if (osd()->UsingCollection)
- parent->CollYellowAction = Type();
- else
- parent->TreeYellowAction = Type();
- break;
- case kBack:
- break;
- case kOk:
- Execute ();
- break;
- default:
- osd ()->newmenu = n; // wrong key: stay in submenu
- result = osUnknown;
- break;
- }
- return result;
+mgCommand::Process(eKeys key) {
+ mgMenu *n = osd ()->newmenu;
+ osd ()->newmenu = NULL;
+ eOSState result = osContinue;
+ switch (key) {
+ case kRed:
+ case kGreen:
+ case kYellow:
+ if (strlen(ButtonName()))
+ osd()->SetHotkeyAction(key,Type());
+ else
+ result=osUnknown;
+ break;
+ case kBack:
+ break;
+ case kOk:
+ Execute ();
+ break;
+ default:
+ osd ()->newmenu = n; // wrong key: stay in submenu
+ result = osUnknown;
+ break;
+ }
+ return result;
}
-
class mgExternal : public mgCommand
{
public:
@@ -464,7 +528,6 @@ class mgExternal : public mgCommand
cCommand * Command();
};
-
class mgExternal0 : public mgExternal { };
class mgExternal1 : public mgExternal { };
class mgExternal2 : public mgExternal { };
@@ -487,99 +550,88 @@ class mgExternal18 : public mgExternal { };
class mgExternal19 : public mgExternal { };
const char*
-mgExternal::ButtonName()
-{
- cCommand *command = Command();
- if (command)
- {
- return command->Title();
- }
- else
- return "";
+mgExternal::ButtonName() {
+ cCommand *command = Command();
+ if (command) {
+ return command->Title();
+ }
+ else
+ return "";
}
cCommand *
-mgExternal::Command()
-{
- cCommand *command = NULL;
- if (osd()->external_commands)
- {
- unsigned int idx = Type() - actExternal0;
- command = osd()->external_commands->Get (idx);
- }
- return command;
+mgExternal::Command() {
+ cCommand *command = NULL;
+ if (osd()->external_commands) {
+ unsigned int idx = Type() - actExternal0;
+ command = osd()->external_commands->Get (idx);
+ }
+ return command;
}
bool
-mgExternal::Execute()
-{
- cCommand *command = Command();
- if (command)
- {
- bool confirmed = true;
- if (command->Confirm ())
- {
- char *buffer;
- msprintf (&buffer, "%s?", command->Title ());
- confirmed = Interface->Confirm (buffer);
- free (buffer);
- }
- if (confirmed)
- {
- osd()->Message1 ("%s...", command->Title ());
- string m3u_file = selection ()->exportM3U ();
- if (!m3u_file.empty ())
- {
- /*char *result = (char *)*/
- string quoted = "'" + m3u_file + "'";
- char prev[1000];
- if (!getcwd(prev,1000))
- mgError("current path too long");
- if (chdir(the_setup.ToplevelDir))
- mgError("cannnot change to directory %s",
- the_setup.ToplevelDir);
- command->Execute (quoted.c_str ());
- chdir(prev);
- selection()->clearCache();
- osd()->forcerefresh = true; // the ext cmd could change the database
-/* What to do? Recode cMenuText (not much)?
- if (result)
- {
- free( result );
- return AddSubMenu( new cMenuText( command->Title(), result ) );
- }
-*/
- }
- }
- }
- return true;
+mgExternal::Execute() {
+ cCommand *command = Command();
+ if (command) {
+ bool confirmed = true;
+ if (command->Confirm ()) {
+ char *buffer;
+ msprintf (&buffer, "%s?", command->Title ());
+ confirmed = Interface->Confirm (buffer);
+ free (buffer);
+ }
+ if (confirmed) {
+ osd()->Message1 ("%s...", command->Title ());
+ string m3u_file = selection ()->exportM3U ();
+ if (!m3u_file.empty ()) {
+ /*char *result = (char *)*/
+ string quoted = "'" + m3u_file + "'";
+ char prev[1000];
+ if (!getcwd(prev,1000))
+ mgError("current path too long");
+ if (chdir(the_setup.ToplevelDir))
+ mgError("cannnot change to directory %s",
+ the_setup.ToplevelDir);
+ command->Execute (quoted.c_str ());
+ chdir(prev);
+ selection()->clearCache();
+ // the ext cmd could change the database
+ osd()->forcerefresh = true;
+ /* What to do? Recode cMenuText (not much)?
+ if (result)
+ {
+ free( result );
+ return AddSubMenu( new cMenuText( command->Title(), result ) );
+ }
+ */
+ }
+ }
+ }
+ return true;
}
//! \brief select search order
class mgChooseOrder : public mgCommand
{
- public:
- bool Enabled(mgActions on=mgActions(0));
- virtual eOSState Process(eKeys key);
- bool Execute ();
- const char *ButtonName() { return tr("Order"); }
- const char *MenuName(const unsigned int idx,const mgListItem* item)
- { return strdup(tr("Select an order")); }
+ public:
+ bool Enabled(mgActions on=mgActions(0));
+ virtual eOSState Process(eKeys key);
+ bool Execute ();
+ const char *ButtonName() { return tr("Order"); }
+ const char *MenuName(const unsigned int idx,const mgListItem* item)
+ { return strdup(tr("Select an order")); }
};
bool
-mgChooseOrder::Enabled(mgActions on)
-{
- bool result = !osd()->UsingCollection;
+mgChooseOrder::Enabled(mgActions on) {
+ bool result = !Selosd()->UsingCollection;
result &= IsEntry(on);
return result;
}
eOSState
-mgChooseOrder::Process(eKeys key)
-{
- if (key == kOk)
- {
+mgChooseOrder::Process(eKeys key) {
+ if (key == kOk) {
osd()->CloseMenu();
Execute();
return osContinue;
@@ -589,28 +641,25 @@ mgChooseOrder::Process(eKeys key)
}
bool
-mgChooseOrder::Execute()
-{
- osd ()->newmenu = new mgMenuOrders;
- osd ()->newposition = osd()->getCurrentSelection();
+mgChooseOrder::Execute() {
+ Selosd ()->newmenu = new mgSelMenuOrders;
+ osd ()->newposition = Selosd()->getCurrentSelection();
return true;
}
class mgEditOrder : public mgCommand
{
- public:
- bool Enabled(mgActions on) { return true; }
- eOSState Process(eKeys key);
- bool Execute () { osd ()->newmenu = new mgMenuOrder; return true; }
- const char *ButtonName() { return tr("Button$Edit"); }
+ public:
+ bool Enabled(mgActions on) { return true; }
+ eOSState Process(eKeys key);
+ bool Execute () { osd ()->newmenu = new mgSelMenuOrder; return true; }
+ const char *ButtonName() { return tr("Button$Edit"); }
};
eOSState
-mgEditOrder::Process(eKeys key)
-{
- if (key == kOk)
- {
+mgEditOrder::Process(eKeys key) {
+ if (key == kOk) {
Execute();
return osContinue;
}
@@ -620,34 +669,32 @@ mgEditOrder::Process(eKeys key)
class mgCreateOrder : public mgCommand
{
- public:
- bool Enabled(mgActions on) { return true; }
- bool Execute ();
- const char *ButtonName() { return tr("Create"); }
+ public:
+ bool Enabled(mgActions on) { return true; }
+ bool Execute ();
+ const char *ButtonName() { return tr("Create"); }
};
-bool
-mgCreateOrder::Execute()
-{
- osd()->AddSelection();
- osd()->SaveState();
+bool
+mgCreateOrder::Execute() {
+ Selosd()->AddSelection();
+ Selosd()->SaveState();
osd()->forcerefresh = true;
return true;
}
class mgDeleteOrder : public mgCommand
{
- public:
- bool Enabled(mgActions on) { return true; }
- bool Execute ();
- const char *ButtonName() { return tr("Button$Delete"); }
+ public:
+ bool Enabled(mgActions on) { return true; }
+ bool Execute ();
+ const char *ButtonName() { return tr("Button$Delete"); }
};
-bool
-mgDeleteOrder::Execute()
-{
- osd()->DeleteSelection();
- osd()->SaveState();
+bool
+mgDeleteOrder::Execute() {
+ Selosd()->DeleteSelection();
+ Selosd()->SaveState();
osd()->forcerefresh = true;
osd()->newposition = osd()->Current();
return true;
@@ -656,54 +703,49 @@ mgDeleteOrder::Execute()
//! \brief show the normal selection list
class mgShowList: public mgOsdItem
{
- public:
- bool Enabled(mgActions) { return true; }
- const char *ButtonName () { return tr("List"); }
- bool Execute() { osd()->newmenu=NULL; return true;}
+ public:
+ bool Enabled(mgActions) { return true; }
+ const char *ButtonName () { return tr("List"); }
+ bool Execute() { osd()->newmenu=NULL; return true;}
};
-
//! \brief show the command submenu
class mgShowCommands: public mgOsdItem
{
- public:
- bool Enabled(mgActions on) { return true; }
- const char *ButtonName () { return tr("Commands"); }
- bool Execute() { osd()->newmenu = new mgSubmenu;return true; }
+ public:
+ bool Enabled(mgActions on) { return true; }
+ const char *ButtonName () { return tr("Commands"); }
+ bool Execute() { osd()->newmenu = new mgSubmenu;return true; }
};
-
//! \brief toggles between the normal and the collection selection
class mgToggleSelection:public mgCommand
{
- public:
- bool Enabled(mgActions on) { return true; }
- bool Execute ();
- const char *ButtonName ();
+ public:
+ bool Enabled(mgActions on) { return true; }
+ bool Execute ();
+ const char *ButtonName ();
};
const char *
-mgToggleSelection::ButtonName ()
-{
- if (osd ()->UsingCollection)
- return tr ("Browse");
- else
- return tr ("Collections");
+mgToggleSelection::ButtonName () {
+ if (Selosd ()->UsingCollection)
+ return tr ("Browse");
+ else
+ return tr ("Collections");
}
bool
-mgToggleSelection::Execute ()
-{
- if (osd ()->UsingCollection)
- osd ()->UseNormalSelection ();
- else
- {
- osd ()->UseCollectionSelection ();
- selection()->clearCache();
- }
- selection()->Activate();
- osd()->newposition = selection ()->gotoPosition ();
- return true;
+mgToggleSelection::Execute () {
+ if (Selosd ()->UsingCollection)
+ Selosd ()->UseNormalSelection ();
+ else {
+ Selosd ()->UseCollectionSelection ();
+ selection()->clearCache();
+ }
+ selection()->Activate();
+ osd()->newposition = selection ()->gotoPosition ();
+ return true;
}
class mgCmdSync : public mgOsdItem
@@ -714,12 +756,9 @@ class mgCmdSync : public mgOsdItem
const char *ButtonName() { return tr("Synchronize database"); }
};
-
eOSState
-mgCmdSync::ProcessKey(eKeys key)
-{
- if (key==kOk)
- {
+mgCmdSync::ProcessKey(eKeys key) {
+ if (key==kOk) {
extern bool import();
import();
return osContinue;
@@ -730,314 +769,280 @@ mgCmdSync::ProcessKey(eKeys key)
//! \brief sets the default collection selection
class mgSetDefaultCollection:public mgCommand
{
- public:
- bool Enabled(mgActions on);
- bool Execute ();
- const char *ButtonName ()
- {
- return tr ("Default");
- }
- const char *MenuName (const unsigned int idx,const mgListItem* item);
+ public:
+ bool Enabled(mgActions on);
+ bool Execute ();
+ const char *ButtonName () {
+ return tr ("Default");
+ }
+ const char *MenuName (const unsigned int idx,const mgListItem* item);
};
-const char * mgSetDefaultCollection::MenuName(const unsigned int idx,const mgListItem* item)
-{
- char *b;
- msprintf (&b, tr("Set default to collection '%s'"),
- selection ()->getCurrentValue().c_str());
- return b;
+const char * mgSetDefaultCollection::MenuName(const unsigned int idx,const mgListItem* item) {
+ char *b;
+ msprintf (&b, tr("Set default to collection '%s'"),
+ selection ()->getCurrentValue().c_str());
+ return b;
}
-
bool
-mgSetDefaultCollection::Enabled(mgActions on)
-{
+mgSetDefaultCollection::Enabled(mgActions on) {
bool result = IsEntry(on);
- result &= (!osd()->DefaultCollectionSelected());
- result &= osd()->UsingCollection;
- result &= (selection ()->orderlevel () == 0);
+ result &= (!Selosd()->DefaultCollectionSelected());
+ result &= Selosd()->UsingCollection;
+ result &= (selection ()->orderlevel () == 0);
return result;
}
bool
-mgSetDefaultCollection::Execute ()
-{
- osd ()->default_collection = selection ()->getCurrentValue();
- osd()->Message1 ("Default collection now is '%s'",
- osd ()->default_collection.c_str());
- return true;
+mgSetDefaultCollection::Execute () {
+ Selosd ()->default_collection = selection ()->getCurrentValue();
+ osd()->Message1 ("Default collection now is '%s'",
+ Selosd ()->default_collection.c_str());
+ return true;
}
-
class mgSetButton : public mgCommand
{
- public:
- bool Enabled(mgActions on) { return true; }
- const char *ButtonName() { return tr("Set"); }
+ public:
+ bool Enabled(mgActions on) { return true; }
+ const char *ButtonName() { return tr("Set"); }
};
-
//! \brief instant play
-class mgInstantPlay : public mgCommand {
- public:
- bool Execute ();
- const char *ButtonName () { return tr ("Instant play"); }
+class mgInstantPlay : public mgCommand
+{
+ public:
+ bool Execute ();
+ const char *ButtonName () { return tr ("Instant play"); }
};
bool
-mgInstantPlay::Execute()
-{
- osd()->PlayInstant(true);
- return true;
+mgInstantPlay::Execute() {
+ Selosd()->PlayInstant(true);
+ return true;
}
//! \brief add selected items to a collection
-class mgAddAllToCollection:public mgCommand {
- public:
- bool Enabled(mgActions on);
- bool Execute ();
- //! \brief adds the whole selection to a collection
- // \param collection the target collection. Default is the default collection
- const char *ButtonName ()
- {
- return tr ("Add");
- }
- const char *MenuName (const unsigned int idx,const mgListItem* item);
- protected:
- void ExecuteMove();
+class mgAddAllToCollection:public mgCommand
+{
+ public:
+ bool Enabled(mgActions on);
+ bool Execute ();
+ //! \brief adds the whole selection to a collection
+ // \param collection the target collection. Default is the default collection
+ const char *ButtonName () {
+ return tr ("Add");
+ }
+ const char *MenuName (const unsigned int idx,const mgListItem* item);
+ protected:
+ void ExecuteMove();
};
const char *
-mgAddAllToCollection::MenuName (const unsigned int idx,const mgListItem* item)
-{
- return strdup(tr("Add all to a collection"));
+mgAddAllToCollection::MenuName (const unsigned int idx,const mgListItem* item) {
+ return strdup(tr("Add all to a collection"));
}
bool
-mgAddAllToCollection::Execute()
-{
-// work on a copy, so we don't have to clear the cache of selection()
-// which would result in an osd()->forcerefresh which could scroll.
- osd()->moveselection = GenerateSelection(selection());
- ExecuteMove();
- return true;
+mgAddAllToCollection::Execute() {
+ // work on a copy, so we don't have to clear the cache of selection()
+ // which would result in an osd()->forcerefresh which could scroll.
+ Selosd()->moveselection = GenerateSelection(selection());
+ ExecuteMove();
+ return true;
}
void
-mgAddAllToCollection::ExecuteMove()
-{
- if (osd() ->Menus.size()>1)
- osd ()->CloseMenu(); // TODO Gebastel...
- char *b;
- msprintf(&b,tr("'%s' to collection"),selection()->getCurrentValue().c_str());
- osd ()->newmenu = new mgTreeAddToCollSelector(string(b));
- osd ()->collselection()->leave_all();
- osd ()->newposition = osd()->collselection()->getPosition();
- free(b);
+mgAddAllToCollection::ExecuteMove() {
+ if (osd() ->Menus.size()>1)
+ osd ()->CloseMenu(); // TODO Gebastel...
+ char *b;
+ msprintf(&b,tr("'%s' to collection"),selection()->getCurrentValue().c_str());
+ osd ()->newmenu = new mgTreeAddToCollSelector(string(b));
+ Selosd ()->collselection()->leave_all();
+ osd ()->newposition = Selosd()->collselection()->getPosition();
+ free(b);
}
-
//! \brief add selected items to default collection
-class mgAddAllToDefaultCollection:public mgCommand {
- public:
- bool Enabled(mgActions on);
- bool Execute ();
- const char *MenuName (const unsigned int idx,const mgListItem* item);
- //! \brief adds the whole selection to the default collection
- // \param collection the default collection.
- void ExecuteSelection (mgSelection *s);
+class mgAddAllToDefaultCollection:public mgCommand
+{
+ public:
+ bool Enabled(mgActions on);
+ bool Execute ();
+ const char *MenuName (const unsigned int idx,const mgListItem* item);
+ //! \brief adds the whole selection to the default collection
+ // \param collection the default collection.
+ void ExecuteSelection (mgSelection *s);
};
const char *
-mgAddAllToDefaultCollection::MenuName (const unsigned int idx,const mgListItem* item)
-{
- char *b;
- msprintf (&b, tr ("Add all to '%s'"),
- osd ()->default_collection.c_str ());
- return b;
+mgAddAllToDefaultCollection::MenuName (const unsigned int idx,const mgListItem* item) {
+ char *b;
+ msprintf (&b, tr ("Add all to '%s'"),
+ Selosd ()->default_collection.c_str ());
+ return b;
}
bool
-mgAddAllToDefaultCollection::Execute()
-{
- mgSelection *sel = GenerateSelection(selection());
- ExecuteSelection(sel);
- delete sel;
- return true;
+mgAddAllToDefaultCollection::Execute() {
+ mgSelection *sel = GenerateSelection(selection());
+ ExecuteSelection(sel);
+ delete sel;
+ return true;
}
-
void
-mgAddAllToDefaultCollection::ExecuteSelection (mgSelection *s)
-{
- string target = osd()->default_collection;
- if (target == osd()->play_collection)
- if (!PlayerControl())
- collselection()->ClearCollection(target);
-
- osd()->Message1 ("Added %s entries",itos (s->AddToCollection (target)).c_str());
-
- if (target == osd()->play_collection)
- {
- playselection()->clearCache();
- mgPlayerControl *c = PlayerControl();
- if (c)
- c->ReloadPlaylist();
- else
- osd()->PlayQueue();
- }
+mgAddAllToDefaultCollection::ExecuteSelection (mgSelection *s) {
+ string target = Selosd()->default_collection;
+ if (target == Selosd()->play_collection)
+ if (!PlayerControl())
+ collselection()->ClearCollection(target);
+
+ osd()->Message1 ("Added %s entries",itos (s->AddToCollection (target)).c_str());
+
+ if (target == Selosd()->play_collection) {
+ playselection()->clearCache();
+ mgPlayerControl *c = PlayerControl();
+ if (c)
+ c->ReloadPlaylist();
+ else
+ Selosd()->PlayQueue();
+ }
}
-
//! \brief add selected items to a collection
class mgAddThisToCollection:public mgAddAllToCollection
{
- public:
- bool Execute ();
- const char *MenuName (const unsigned int idx,const mgListItem* item);
+ public:
+ bool Execute ();
+ const char *MenuName (const unsigned int idx,const mgListItem* item);
};
bool
-mgAddThisToCollection::Execute ()
-{
-// work on a copy, so we don't have to clear the cache of selection()
-// which would result in an osd()->forcerefresh which could scroll.
- osd()->moveselection = GenerateSelection(selection());
- osd()->moveselection->enter();
- ExecuteMove();
- return true;
+mgAddThisToCollection::Execute () {
+ // work on a copy, so we don't have to clear the cache of selection()
+ // which would result in an osd()->forcerefresh which could scroll.
+ Selosd()->moveselection = GenerateSelection(selection());
+ Selosd()->moveselection->enter();
+ ExecuteMove();
+ return true;
}
bool
-mgAddAllToCollection::Enabled(mgActions on)
-{
+mgAddAllToCollection::Enabled(mgActions on) {
return IsEntry(on);
}
const char *
-mgAddThisToCollection::MenuName (const unsigned int idx,const mgListItem* item)
-{
- return strdup(tr("Add to a collection"));
+mgAddThisToCollection::MenuName (const unsigned int idx,const mgListItem* item) {
+ return strdup(tr("Add to a collection"));
}
-
bool
-mgAddAllToDefaultCollection::Enabled(mgActions on)
-{
+mgAddAllToDefaultCollection::Enabled(mgActions on) {
bool result = IsEntry(on);
- result &= (!osd()->DefaultCollectionSelected());
+ result &= (!Selosd()->DefaultCollectionSelected());
return result;
}
//! \brief add selected items to default collection
class mgAddThisToDefaultCollection:public mgAddAllToDefaultCollection
{
- public:
- bool Execute ();
- const char *ButtonName ()
- {
- return tr ("Add");
- }
- const char *MenuName (const unsigned int idx,const mgListItem* item);
+ public:
+ bool Execute ();
+ const char *ButtonName () {
+ return tr ("Add");
+ }
+ const char *MenuName (const unsigned int idx,const mgListItem* item);
};
-
bool
-mgAddThisToDefaultCollection::Execute ()
-{
-// work on a copy, so we don't have to clear the cache of selection()
-// which would result in an osd()->forcerefresh which could scroll.
- mgSelection *sel = GenerateSelection(selection());
- sel->enter ();
- ExecuteSelection(sel);
- delete sel;
- return true;
+mgAddThisToDefaultCollection::Execute () {
+ // work on a copy, so we don't have to clear the cache of selection()
+ // which would result in an osd()->forcerefresh which could scroll.
+ mgSelection *sel = GenerateSelection(selection());
+ sel->enter ();
+ ExecuteSelection(sel);
+ delete sel;
+ return true;
}
const char *
-mgAddThisToDefaultCollection::MenuName (const unsigned int idx,const mgListItem* item)
-{
- char *b;
- msprintf (&b, tr ("Add to '%s'"), osd ()->default_collection.c_str ());
- return b;
+mgAddThisToDefaultCollection::MenuName (const unsigned int idx,const mgListItem* item) {
+ char *b;
+ msprintf (&b, tr ("Add to '%s'"), Selosd ()->default_collection.c_str ());
+ return b;
}
//! \brief remove selected items from default collection
class mgRemoveAllFromCollection:public mgCommand
{
- public:
- bool Enabled(mgActions on);
- bool Execute ();
- const char *MenuName (const unsigned int idx,const mgListItem* item);
- protected:
- void ExecuteRemove();
+ public:
+ bool Enabled(mgActions on);
+ bool Execute ();
+ const char *MenuName (const unsigned int idx,const mgListItem* item);
+ protected:
+ void ExecuteRemove();
};
bool
-mgRemoveAllFromCollection::Enabled(mgActions on)
-{
+mgRemoveAllFromCollection::Enabled(mgActions on) {
return IsEntry(on);
}
bool
-mgRemoveAllFromCollection::Execute ()
-{
- osd()->moveselection = GenerateSelection(selection());
- ExecuteRemove();
- return true;
+mgRemoveAllFromCollection::Execute () {
+ Selosd()->moveselection = GenerateSelection(selection());
+ ExecuteRemove();
+ return true;
}
void
-mgRemoveAllFromCollection::ExecuteRemove ()
-{
- if (osd() ->Menus.size()>1)
- osd ()->CloseMenu(); // TODO Gebastel...
- char *b;
- msprintf(&b,tr("Remove '%s' from collection"),osd()->moveselection->getListname().c_str());
- osd ()->newmenu = new mgTreeRemoveFromCollSelector(string(b));
- osd ()->collselection()->leave_all();
- osd ()->newposition = osd()->collselection()->getPosition();
- free(b);
+mgRemoveAllFromCollection::ExecuteRemove () {
+ if (osd() ->Menus.size()>1)
+ osd ()->CloseMenu(); // TODO Gebastel...
+ char *b;
+ msprintf(&b,tr("Remove '%s' from collection"),Selosd()->moveselection->getListname().c_str());
+ osd ()->newmenu = new mgTreeRemoveFromCollSelector(string(b));
+ Selosd ()->collselection()->leave_all();
+ osd ()->newposition = Selosd()->collselection()->getPosition();
+ free(b);
}
const char *
-mgRemoveAllFromCollection::MenuName (const unsigned int idx,const mgListItem* item)
-{
- return strdup(tr("Remove all from a collection"));
+mgRemoveAllFromCollection::MenuName (const unsigned int idx,const mgListItem* item) {
+ return strdup(tr("Remove all from a collection"));
}
class mgClearCollection : public mgCommand
{
- public:
- bool Enabled(mgActions on);
- bool Execute ();
- const char *ButtonName ()
- {
- return tr ("Clear");
- }
- const char *MenuName (const unsigned int idx,const mgListItem* item);
+ public:
+ bool Enabled(mgActions on);
+ bool Execute ();
+ const char *ButtonName () {
+ return tr ("Clear");
+ }
+ const char *MenuName (const unsigned int idx,const mgListItem* item);
};
const char *
-mgClearCollection::MenuName (const unsigned int idx,const mgListItem* item)
-{
+mgClearCollection::MenuName (const unsigned int idx,const mgListItem* item) {
return strdup(tr("Clear the collection"));
}
bool
-mgClearCollection::Enabled(mgActions on)
-{
+mgClearCollection::Enabled(mgActions on) {
return selection()->isCollectionlist();
}
bool
-mgClearCollection::Execute()
-{
- if (Interface->Confirm(tr("Clear the collection?")))
- {
+mgClearCollection::Execute() {
+ if (Interface->Confirm(tr("Clear the collection?"))) {
string target = selection()->getCurrentValue();
selection()->ClearCollection(target);
- osd()->CollectionChanged(target,false);
+ Selosd()->CollectionChanged(target,false);
}
return true;
}
@@ -1045,32 +1050,27 @@ mgClearCollection::Execute()
//! \brief remove selected items from default collection
class mgRemoveThisFromCollection:public mgRemoveAllFromCollection
{
- public:
- bool Execute ();
- const char *ButtonName ()
- {
- return tr ("Remove");
- }
- const char *MenuName (const unsigned int idx,const mgListItem* item);
+ public:
+ bool Execute ();
+ const char *ButtonName () {
+ return tr ("Remove");
+ }
+ const char *MenuName (const unsigned int idx,const mgListItem* item);
};
-
bool
-mgRemoveThisFromCollection::Execute ()
-{
-// work on a copy, so we don't have to clear the cache of selection()
-// which would result in an osd()->forcerefresh which could scroll.
- osd()->moveselection = GenerateSelection(selection());
- osd()->moveselection->enter();
- ExecuteRemove();
- return true;
+mgRemoveThisFromCollection::Execute () {
+ // work on a copy, so we don't have to clear the cache of selection()
+ // which would result in an osd()->forcerefresh which could scroll.
+ Selosd()->moveselection = GenerateSelection(selection());
+ Selosd()->moveselection->enter();
+ ExecuteRemove();
+ return true;
}
-
const char *
-mgRemoveThisFromCollection::MenuName (const unsigned int idx,const mgListItem* item)
-{
- return strdup(tr("Remove from a collection"));
+mgRemoveThisFromCollection::MenuName (const unsigned int idx,const mgListItem* item) {
+ return strdup(tr("Remove from a collection"));
}
class mgEditAction : public mgAction
@@ -1083,42 +1083,38 @@ class mgEditAction : public mgAction
class mgCreate : public mgEditAction, public cMenuEditStrItem
{
- public:
- mgCreate(const char* mn);
- virtual bool Enabled(mgActions on)=0;
- void Notify();
- eOSState ProcessKey(eKeys key) { return mgAction::ProcessKey(key); }
- eOSState Process(eKeys key);
- protected:
- bool Editing();
+ public:
+ mgCreate(const char* mn);
+ virtual bool Enabled(mgActions on)=0;
+ void Notify();
+ eOSState ProcessKey(eKeys key) { return mgAction::ProcessKey(key); }
+ eOSState Process(eKeys key);
+ protected:
+ bool Editing();
};
-mgCreate::mgCreate(const char *mn) : mgEditAction(), cMenuEditStrItem(mn,m_value,30,tr(FileNameChars))
-{
+mgCreate::mgCreate(const char *mn) : mgEditAction(), cMenuEditStrItem(mn,m_value,30,tr(FileNameChars)) {
}
bool
-mgCreate::Editing()
-{
+mgCreate::Editing() {
return (strchr(cOsdItem::Text(),'[') && strchr(cOsdItem::Text(),']'));
}
void
-mgCreate::Notify()
-{
- if (!Editing())
- m->SetHelpKeys();
+mgCreate::Notify() {
+ if (!Editing())
+ m->SetHelpKeys();
}
eOSState
-mgCreate::Process(eKeys key)
-{
+mgCreate::Process(eKeys key) {
if (key == kOk)
if (Editing())
Execute();
- else
- return cMenuEditStrItem::ProcessKey(kRight);
- if (key != kYellow || Editing())
+ else
+ return cMenuEditStrItem::ProcessKey(kRight);
+ if (key != kYellow || Editing())
return cMenuEditStrItem::ProcessKey(key);
else
return osUnknown;
@@ -1126,134 +1122,113 @@ mgCreate::Process(eKeys key)
class mgCreateCollection : public mgCreate
{
- public:
- mgCreateCollection();
- bool Enabled(mgActions on);
- bool Execute ();
- const char *MenuName (const unsigned int idx=0,const mgListItem* item=0);
+ public:
+ mgCreateCollection();
+ bool Enabled(mgActions on);
+ bool Execute ();
+ const char *MenuName (const unsigned int idx=0,const mgListItem* item=0);
};
-mgCreateCollection::mgCreateCollection() : mgCreate(MenuName())
-{
+mgCreateCollection::mgCreateCollection() : mgCreate(MenuName()) {
}
const char*
-mgCreateCollection::MenuName(const unsigned int idx,const mgListItem* item)
-{
- return strdup(tr ("Create collection"));
+mgCreateCollection::MenuName(const unsigned int idx,const mgListItem* item) {
+ return strdup(tr ("Create collection"));
}
bool
-mgCreateCollection::Execute ()
-{
- string name = trim(m_value);
+mgCreateCollection::Execute () {
+ string name = trim(m_value);
if (name.empty()) return false;
- bool created = selection ()->CreateCollection (name);
- if (created)
- {
- mgDebug(1,"created collection %s",name.c_str());
- osd()->default_collection = name;
- selection ()->clearCache();
- if (selection()->isCollectionlist())
- {
- selection ()->setPosition(name);
- }
- osd()->forcerefresh = true;
- return true;
+ bool created = selection ()->CreateCollection (name);
+ if (created) {
+ mgDebug(1,"created collection %s",name.c_str());
+ Selosd()->default_collection = name;
+ selection ()->clearCache();
+ if (selection()->isCollectionlist()) {
+ selection ()->setPosition(name);
+ }
+ osd()->forcerefresh = true;
+ return true;
}
- else
- {
- osd()->Message1 ("Collection '%s' NOT created", name.c_str());
- return false;
+ else {
+ osd()->Message1 ("Collection '%s' NOT created", name.c_str());
+ return false;
}
}
bool
-mgCreateCollection::Enabled(mgActions on)
-{
+mgCreateCollection::Enabled(mgActions on) {
return selection()->isCollectionlist();
}
-
//! \brief delete collection
class mgDeleteCollection:public mgCommand
{
- public:
- bool Execute ();
- bool Enabled(mgActions on);
- const char *ButtonName ()
- {
- return tr ("Button$Delete");
- }
- const char *MenuName (const unsigned int idx,const mgListItem* item);
+ public:
+ bool Execute ();
+ bool Enabled(mgActions on);
+ const char *ButtonName () {
+ return tr ("Button$Delete");
+ }
+ const char *MenuName (const unsigned int idx,const mgListItem* item);
};
bool
-mgDeleteCollection::Enabled(mgActions on)
-{
+mgDeleteCollection::Enabled(mgActions on) {
bool result = IsEntry(on);
result &= selection()->isCollectionlist();
- if (result)
- {
+ if (result) {
string name = selection ()->getCurrentValue();
- result &= (name != osd()->play_collection);
+ result &= (name != Selosd()->play_collection);
}
return result;
}
-const char* mgDeleteCollection::MenuName(const unsigned int idx,const mgListItem* item)
-{
- return strdup(tr("Delete the collection"));
+const char* mgDeleteCollection::MenuName(const unsigned int idx,const mgListItem* item) {
+ return strdup(tr("Delete the collection"));
}
-
bool
-mgDeleteCollection::Execute ()
-{
+mgDeleteCollection::Execute () {
if (!Interface->Confirm(tr("Delete the collection?"))) return false;
string name = selection ()->getCurrentValue();
- if (selection ()->DeleteCollection (name))
- {
- osd()->Message1 ("Collection '%s' deleted", name.c_str());
- mgDebug(1,"Deleted collection %s",name.c_str());
- selection ()->clearCache();
- osd()->forcerefresh = true;
- return true;
+ if (selection ()->DeleteCollection (name)) {
+ osd()->Message1 ("Collection '%s' deleted", name.c_str());
+ mgDebug(1,"Deleted collection %s",name.c_str());
+ selection ()->clearCache();
+ osd()->forcerefresh = true;
+ return true;
}
- else
- {
- osd()->Message1 ("Collection '%s' NOT deleted", name.c_str());
- return false;
+ else {
+ osd()->Message1 ("Collection '%s' NOT deleted", name.c_str());
+ return false;
}
}
-
//! \brief export item list for all selected items
class mgExportItemlist:public mgCommand
{
- public:
- bool Execute ();
- const char *ButtonName ()
- {
- return tr ("Export");
- }
- const char *MenuName (const unsigned int idx,const mgListItem* item)
- {
- return strdup(tr ("Export track list"));
- }
+ public:
+ bool Execute ();
+ const char *ButtonName () {
+ return tr ("Export");
+ }
+ const char *MenuName (const unsigned int idx,const mgListItem* item) {
+ return strdup(tr ("Export track list"));
+ }
};
bool
-mgExportItemlist::Execute ()
-{
- string m3u_file = selection ()->exportM3U ();
- osd()->Message1 ("written to %s", m3u_file.c_str());
- return true;
+mgExportItemlist::Execute () {
+ string m3u_file = selection ()->exportM3U ();
+ osd()->Message1 ("written to %s", m3u_file.c_str());
+ return true;
}
mgActions
-mgAction::Type()
-{
+mgAction::Type() {
const type_info& t = typeid(*this);
if (t == typeid(mgNone)) return actNone;
if (t == typeid(mgChooseOrder)) return actChooseOrder;
@@ -1277,10 +1252,15 @@ mgAction::Type()
if (t == typeid(mgShowCommands)) return actShowCommands;
if (t == typeid(mgSetDefaultCollection)) return actSetDefaultCollection;
if (t == typeid(mgActOrder)) return actOrder;
+ if (t == typeid(mgToggleShuffle)) return actToggleShuffle;
+ if (t == typeid(mgToggleLoop)) return actToggleLoop;
if (t == typeid(mgCreateOrder)) return actCreateOrder;
if (t == typeid(mgDeleteOrder)) return actDeleteOrder;
if (t == typeid(mgEditOrder)) return actEditOrder;
if (t == typeid(mgCmdSync)) return actSync;
+ if (t == typeid(mgShowLyrics)) return actShowLyrics;
+ if (t == typeid(mgPrevTrack)) return actPrevTrack;
+ if (t == typeid(mgNextTrack)) return actNextTrack;
if (t == typeid(mgExternal0)) return actExternal0;
if (t == typeid(mgExternal1)) return actExternal1;
if (t == typeid(mgExternal2)) return actExternal2;
@@ -1305,50 +1285,55 @@ mgAction::Type()
}
mgAction*
-actGenerateKeyItem(const char *Name, int *Value, int NumStrings, const char * const * Strings)
-{
+actGenerateKeyItem(const char *Name, int *Value, int NumStrings, const char * const * Strings) {
return new mgKeyItem(Name,Value,NumStrings,Strings);
}
mgAction*
-actGenerateBoolItem(const char *Name, int *Value)
-{
+actGenerateBoolItem(const char *Name, int *Value) {
return new mgBoolItem(Name,Value);
}
mgAction*
-actGenerate(const mgActions action)
-{
+actGenerate(const mgActions action) {
mgAction * result = NULL;
- switch (action)
- {
- case actNone: result = new mgNone;break;
- case actChooseOrder: result = new mgChooseOrder;break;
- case actToggleSelection: result = new mgToggleSelection;break;
- case actClearCollection: result = new mgClearCollection;break;
- case actCreateCollection: result = new mgCreateCollection;break;
- case actInstantPlay: result = new mgInstantPlay;break;
- case actAddAllToCollection: result = new mgAddAllToCollection;break;
- case actAddAllToDefaultCollection: result = new mgAddAllToDefaultCollection;break;
+ switch (action) {
+ case actNone: result = new mgNone;break;
+ case actChooseOrder: result = new mgChooseOrder;break;
+ case actToggleSelection: result = new mgToggleSelection;break;
+ case actClearCollection: result = new mgClearCollection;break;
+ case actCreateCollection: result = new mgCreateCollection;break;
+ case actInstantPlay: result = new mgInstantPlay;break;
+ case actAddAllToCollection: result = new mgAddAllToCollection;break;
+ case actAddAllToDefaultCollection: result = new mgAddAllToDefaultCollection;break;
case actRemoveAllFromCollection:result = new mgRemoveAllFromCollection;break;
- case actDeleteCollection: result = new mgDeleteCollection;break;
- case actExportItemlist: result = new mgExportItemlist;break;
- case actAddCollEntry: result = new mgAddCollEntry;break;
- case actRemoveCollEntry: result = new mgRemoveCollEntry;break;
- case actAddThisToCollection: result = new mgAddThisToCollection;break;
- case actAddThisToDefaultCollection: result = new mgAddThisToDefaultCollection;break;
+ case actDeleteCollection: result = new mgDeleteCollection;break;
+ case actExportItemlist: result = new mgExportItemlist;break;
+ case actAddCollEntry: result = new mgAddCollEntry;break;
+ case actRemoveCollEntry: result = new mgRemoveCollEntry;break;
+ case actAddThisToCollection: result = new mgAddThisToCollection;break;
+ case actAddThisToDefaultCollection: result = new mgAddThisToDefaultCollection;break;
case actRemoveThisFromCollection: result = new mgRemoveThisFromCollection;break;
case actEntry: result = new mgEntry;break;
case actSetButton: result = new mgSetButton;break;
- case actShowList: result = new mgShowList;break;
- case actShowCommands: result = new mgShowCommands;break;
- case actSync: result = new mgCmdSync;break;
- case actSetDefaultCollection: result = new mgSetDefaultCollection;break;
+ case actShowList: result = new mgShowList;break;
+ case actShowCommands: result = new mgShowCommands;break;
+ case actSync: result = new mgCmdSync;break;
+ case actSetDefaultCollection: result = new mgSetDefaultCollection;break;
case actOrder: result = new mgActOrder;break;
+ case actToggleShuffle: result = new mgToggleShuffle;break;
+ case actToggleLoop: result = new mgToggleLoop;break;
case actUnused6: break;
case actCreateOrder: result = new mgCreateOrder;break;
case actDeleteOrder: result = new mgDeleteOrder;break;
case actEditOrder: result = new mgEditOrder;break;
+ case actPrevTrack: result = new mgPrevTrack;break;
+ case actNextTrack: result = new mgNextTrack;break;
+ case actShowLyrics: result = new mgShowLyrics;break;
+ case actLoadExternalLyrics: result = new mgLoadExternalLyrics;break;
+ case actSaveExternalLyrics: result = new mgSaveExternalLyrics;break;
+ case actBack: result = new mgBack;break;
+ case actBack2: result = new mgBack2;break;
case actExternal0: result = new mgExternal0;break;
case actExternal1: result = new mgExternal1;break;
case actExternal2: result = new mgExternal2;break;
@@ -1373,38 +1358,34 @@ actGenerate(const mgActions action)
return result;
}
-
mgSelection *
-mgAction::selection()
-{
- return osd()->selection();
+mgAction::selection() {
+ if (Selosd())
+ return Selosd()->selection();
+ else
+ return 0;
}
-
mgSelection *
-mgAction::collselection()
-{
- return osd()->collselection();
+mgAction::collselection() {
+ return Selosd()->collselection();
}
eOSState
-mgKeyItem::Process(eKeys key)
-{
- mgMenuOrder *menu = dynamic_cast<mgMenuOrder*>(m);
- if (key==kOk)
- {
+mgKeyItem::Process(eKeys key) {
+ mgSelMenuOrder *menu = dynamic_cast<mgSelMenuOrder*>(m);
+ if (key==kOk) {
if (menu->ChangeSelection(key))
return osContinue;
- else
- {
+ else {
menu->SaveSelection();
- osd ()->newmenu = NULL;
- return osContinue;
+ osd ()->newmenu = NULL;
+ return osContinue;
}
} else if (key==kBack)
{
- osd ()->newmenu = NULL;
- return osContinue;
+ osd ()->newmenu = NULL;
+ return osContinue;
}
if (key==kUp || key==kDown)
if (menu->ChangeSelection(key))
@@ -1412,29 +1393,24 @@ mgKeyItem::Process(eKeys key)
return cMenuEditStraItem::ProcessKey(key);
}
-
eOSState
-mgBoolItem::Process(eKeys key)
-{
- mgMenuOrder *menu = dynamic_cast<mgMenuOrder*>(m);
- if (key==kOk)
- {
+mgBoolItem::Process(eKeys key) {
+ mgSelMenuOrder *menu = dynamic_cast<mgSelMenuOrder*>(m);
+ if (key==kOk) {
if (menu->ChangeSelection(key))
return osContinue;
- else
- {
+ else {
menu->SaveSelection();
- osd ()->newmenu = NULL;
- return osContinue;
+ osd ()->newmenu = NULL;
+ return osContinue;
}
} else if (key==kBack)
{
- osd ()->newmenu = NULL;
- return osContinue;
+ osd ()->newmenu = NULL;
+ return osContinue;
}
if (key==kUp || key==kDown)
if (menu->ChangeSelection(key))
return osContinue;
return cMenuEditBoolItem::ProcessKey(key);
}
-
diff --git a/vdr_actions.h b/vdr_actions.h
index 8edef19..e97034e 100644
--- a/vdr_actions.h
+++ b/vdr_actions.h
@@ -24,29 +24,31 @@ using namespace std;
class mgSelection;
class mgMenu;
-class mgMainMenu;
+class mgOsd;
+class mgSelOsd;
/*! \brief defines all actions which can appear in command submenus.
* Since these values are saved in muggle.state, new actions should
* always be appended. The order does not matter. actNone should be 0.
*/
-enum mgActions {
+enum mgActions
+{
actNone,
- actChooseOrder, //!< show a menu with all possible orders
- actToggleSelection, //!< toggle between search and collection view
- actClearCollection, //!< clear a collection,
+ actChooseOrder, //!< show a menu with all possible orders
+ actToggleSelection, //!< toggle between search and collection view
+ actClearCollection, //!< clear a collection,
actCreateCollection,
- actInstantPlay, //!< instant play
- actAddAllToCollection, //!< add all items of OSD list to default collection
- actRemoveAllFromCollection,//!< remove from default collection
- actDeleteCollection, //!< delete collection
- actExportItemlist, //!< export track list into a *.m3u file
+ actInstantPlay, //!< instant play
+ actAddAllToCollection, //!< add all items of OSD list to default collection
+ actRemoveAllFromCollection, //!< remove from default collection
+ actDeleteCollection, //!< delete collection
+ actExportItemlist, //!< export track list into a *.m3u file
actAddCollEntry,
actRemoveCollEntry,
- actAddThisToCollection, //!< add selected item to default collection
- actRemoveThisFromCollection, //!< remove selected item from default collection
- actEntry, //!< used for normal data base items
- actSetButton, //!< connect a button with an action
+ actAddThisToCollection, //!< add selected item to default collection
+ actRemoveThisFromCollection, //!< remove selected item from default collection
+ actEntry, //!< used for normal data base items
+ actSetButton, //!< connect a button with an action
actShowList,
actShowCommands,
actCreateOrder,
@@ -56,128 +58,137 @@ enum mgActions {
actAddThisToDefaultCollection,
actSetDefaultCollection,
actOrder,
+ actToggleShuffle,
actUnused6,
actEditOrder,
- actExternal0 = 1000, //!< used for external commands, the number is the entry number in the .conf file starting with line 0
- actExternal1, //!< used for external commands, the number is the entry number in the .conf file
- actExternal2, //!< used for external commands, the number is the entry number in the .conf file
- actExternal3, //!< used for external commands, the number is the entry number in the .conf file
- actExternal4, //!< used for external commands, the number is the entry number in the .conf file
- actExternal5, //!< used for external commands, the number is the entry number in the .conf file
- actExternal6, //!< used for external commands, the number is the entry number in the .conf file
- actExternal7, //!< used for external commands, the number is the entry number in the .conf file
- actExternal8, //!< used for external commands, the number is the entry number in the .conf file
- actExternal9, //!< used for external commands, the number is the entry number in the .conf file
- actExternal10, //!< used for external commands, the number is the entry number in the .conf file
- actExternal11, //!< used for external commands, the number is the entry number in the .conf file
- actExternal12, //!< used for external commands, the number is the entry number in the .conf file
- actExternal13, //!< used for external commands, the number is the entry number in the .conf file
- actExternal14, //!< used for external commands, the number is the entry number in the .conf file
- actExternal15, //!< used for external commands, the number is the entry number in the .conf file
- actExternal16, //!< used for external commands, the number is the entry number in the .conf file
- actExternal17, //!< used for external commands, the number is the entry number in the .conf file
- actExternal18, //!< used for external commands, the number is the entry number in the .conf file
- actExternal19, //!< used for external commands, the number is the entry number in the .conf file
+ actToggleLoop,
+ actPrevTrack,
+ actNextTrack,
+ actShowLyrics,
+ actLoadExternalLyrics,
+ actSaveExternalLyrics,
+ actBack,
+ actBack2,
+ actExternal0 = 1000, //!< used for external commands, the number is the entry number in the .conf file starting with line 0
+ actExternal1, //!< used for external commands, the number is the entry number in the .conf file
+ actExternal2, //!< used for external commands, the number is the entry number in the .conf file
+ actExternal3, //!< used for external commands, the number is the entry number in the .conf file
+ actExternal4, //!< used for external commands, the number is the entry number in the .conf file
+ actExternal5, //!< used for external commands, the number is the entry number in the .conf file
+ actExternal6, //!< used for external commands, the number is the entry number in the .conf file
+ actExternal7, //!< used for external commands, the number is the entry number in the .conf file
+ actExternal8, //!< used for external commands, the number is the entry number in the .conf file
+ actExternal9, //!< used for external commands, the number is the entry number in the .conf file
+ actExternal10, //!< used for external commands, the number is the entry number in the .conf file
+ actExternal11, //!< used for external commands, the number is the entry number in the .conf file
+ actExternal12, //!< used for external commands, the number is the entry number in the .conf file
+ actExternal13, //!< used for external commands, the number is the entry number in the .conf file
+ actExternal14, //!< used for external commands, the number is the entry number in the .conf file
+ actExternal15, //!< used for external commands, the number is the entry number in the .conf file
+ actExternal16, //!< used for external commands, the number is the entry number in the .conf file
+ actExternal17, //!< used for external commands, the number is the entry number in the .conf file
+ actExternal18, //!< used for external commands, the number is the entry number in the .conf file
+ actExternal19, //!< used for external commands, the number is the entry number in the .conf file
};
//! \brief the highest possible actExternal value
const mgActions actExternalHigh = actExternal19;
//! \brief a generic class for the definition of user actions
-class mgAction
+class mgAction
{
- public:
+ public:
- //! \brief if true, can be displayed
- virtual bool Enabled(mgActions on = mgActions(0));
+ //! \brief if true, can be displayed
+ virtual bool Enabled(mgActions on = mgActions(0));
- //! \brief the action to be executed
- virtual bool Execute () {return true;}
+ //! \brief the action to be executed
+ virtual bool Execute () {return true;}
- //! \brief handles the kBack key
- virtual eOSState Back();
+ //! \brief handles the kBack key
+ virtual eOSState Back();
-/*! \brief the name for a button.
- * The returned C string should be static, it will never be freed.
- */
- virtual const char *ButtonName ()
- {
- return "";
- }
-
-/*! \brief the name for a menu entry. If empty, no button will be able
- * to execute this. The returned C string must be freeable at any time.
- * \param value a string that can be used for building the menu name.
- */
- virtual const char *MenuName (const unsigned int idx=0,const mgListItem* item=0)
- {
- return strdup(ButtonName());
- }
+ /*! \brief the name for a button.
+ * The returned C string should be static, it will never be freed.
+ */
+ virtual const char *ButtonName () {
+ return "";
+ }
+
+ /*! \brief the name for a menu entry. If empty, no button will be able
+ * to execute this. The returned C string must be freeable at any time.
+ * \param value a string that can be used for building the menu name.
+ */
+ virtual const char *MenuName (const unsigned int idx=0,const mgListItem* item=0) {
+ return strdup(ButtonName());
+ }
- //! \brief default constructor
- mgAction ();
+ //! \brief default constructor
+ mgAction ();
- //! \brief default destructor
- virtual ~ mgAction ();
+ //! \brief default destructor
+ virtual ~ mgAction ();
- //! \brief assoiates the action with the menu which created it
- void SetMenu (mgMenu * const menu);
+ //! \brief assoiates the action with the menu which created it
+ void SetMenu (mgMenu * const menu);
- //! \brief what to do when mgStatus::OsdCurrentItem is called
- //for this one
- mgActions Type();
- void SetText(const char *text,bool copy=true);
- const char *Text();
-
- void TryNotify();
+ //! \brief what to do when mgStatus::OsdCurrentItem is called
+ //for this one
+ mgActions Type();
+ void SetText(const char *text,bool copy=true);
+ const char *Text();
+
+ void TryNotify();
/*! \brief vdr calls OsdCurrentItem more often than we
- * want. This tells mgStatus to ignore the next call
+ * want. This tells mgStatus to ignore the next call
* for a specific item.
* \todo is this behaviour intended or a bug in vdr
* or in muggle ?
*/
- bool IgnoreNextEvent;
+ bool IgnoreNextEvent;
- /*! \brief defines a reference. Can be used depending on the
- * concrete class type. Currently used only by mgEntry
- */
- void setHandle(unsigned int handle);
+ /*! \brief defines a reference. Can be used depending on the
+ * concrete class type. Currently used only by mgEntry
+ */
+ void setHandle(unsigned int handle);
- protected:
+ protected:
- //! \brief returns the OSD owning the menu owning this item
- mgMainMenu* osd ();
+ //! \brief returns the OSD owning the menu owning this item
+ mgOsd* osd ();
- //! \brief the menu that created this object
- mgMenu * m;
+ //! \brief returns the OSD owning the menu owning this item
+ mgSelOsd* Selosd ();
- //! \brief returns the active selection
- mgSelection* selection ();
+ //! \brief the menu that created this object
+ mgMenu * m;
- //! \brief returns the collection selection
- mgSelection* collselection ();
+ //! \brief returns the active selection
+ mgSelection* selection ();
- //! \brief returns the playselection
- mgSelection* playselection ();
+ //! \brief returns the collection selection
+ mgSelection* collselection ();
- virtual void Notify();
- eOSState ProcessKey(eKeys key);
- virtual eOSState Process(eKeys key) { return osUnknown; }
+ //! \brief returns the playselection
+ mgSelection* playselection ();
- unsigned int m_handle;
- private:
- mgMainMenu *m_osd;
-};
+ virtual void Notify();
+ eOSState ProcessKey(eKeys key);
+ virtual eOSState Process(eKeys key) { return osUnknown; }
+ unsigned int m_handle;
+ private:
+ mgOsd *m_osd;
+};
class mgActionWithIntValue: public mgAction
{
- public:
- mgActionWithIntValue() { intval=0; }
- void setValue(int i) { intval = i; }
- protected:
- int intval;
+ public:
+ mgActionWithIntValue() { intval=0; }
+ void setValue(int i) { intval = i; }
+ protected:
+ int intval;
};
//! \brief generate an mgAction for action
@@ -185,4 +196,9 @@ mgAction* actGenerate(const mgActions action);
mgAction* actGenerateBoolItem(const char *Name, int *Value);
mgAction* actGenerateKeyItem(const char *Name, int *Value, int NumStrings, const char * const * Strings);
+class mgOsdItem : public mgAction, public cOsdItem
+{
+ public:
+ eOSState ProcessKey(eKeys key) { return mgAction::ProcessKey(key); }
+};
#endif
diff --git a/vdr_config.h b/vdr_config.h
index 42410b1..c05e0a6 100644
--- a/vdr_config.h
+++ b/vdr_config.h
@@ -105,7 +105,6 @@
// Uncomment to enable generic debugging messages to the console. This may slow
// down operation in some cases.
-#define DEBUG
// Uncomment to disable audio output to the dvb driver. The audio data is
// simply discarded.
@@ -115,4 +114,4 @@
// "/tmp/limiter". The generated file will be about 3MB in size. This option shouldn't
// be enabled for day-by-day operation.
//#define ACC_DUMP
-#endif //___VDR_CONFIG_H
+#endif //___VDR_CONFIG_H
diff --git a/vdr_decoder.c b/vdr_decoder.c
index 60087cc..8bd52ef 100644
--- a/vdr_decoder.c
+++ b/vdr_decoder.c
@@ -26,7 +26,6 @@
#include <vdr/videodir.h>
#include <vdr/interface.h>
-
#include "vdr_decoder.h"
#include "vdr_decoder_mp3.h"
#include "vdr_decoder_ogg.h"
@@ -43,173 +42,150 @@ extern void showmessage(int duration,const char *,...);
#include "vdr_decoder_flac.h"
#endif
-
// --- mgDecoders ---------------------------------------------------------------
-mgMediaType mgDecoders::getMediaType (std::string s)
-{
- mgMediaType mt = MT_UNKNOWN;
-
- char *
- f = (char *) s.c_str ();
- char *
- p = f + strlen (f) - 1; // point to the end
-
- while (p >= f && *p != '.')
- --p;
-
- if (!strcasecmp (p, ".mp3"))
- {
- mt = MT_MP3;
- }
- else
- {
- if (!strcasecmp (p, ".ogg"))
- {
+mgMediaType mgDecoders::getMediaType (std::string s) {
+ mgMediaType mt = MT_UNKNOWN;
+
+ char *
+ f = (char *) s.c_str ();
+ char *
+ p = f + strlen (f) - 1; // point to the end
+
+ while (p >= f && *p != '.')
+ --p;
+
+ if (!strcasecmp (p, ".mp3")) {
+ mt = MT_MP3;
+ }
+ else {
+ if (!strcasecmp (p, ".ogg")) {
#ifdef HAVE_VORBISFILE
- mt = MT_OGG;
+ mt = MT_OGG;
#else
- mgWarning("Support for vorbis not compiled in, define HAVE_VORBISFILE in Makefile");
+ mgWarning("Support for vorbis not compiled in, define HAVE_VORBISFILE in Makefile");
#endif
- }
- else
- {
- if (!strcasecmp (p, ".flac"))
- {
+ }
+ else {
+ if (!strcasecmp (p, ".flac")) {
#ifdef HAVE_FLAC
- mt = MT_FLAC;
+ mt = MT_FLAC;
#else
- mgWarning("Support for flac not compiled in, define HAVE_FLAC in Makefile");
+ mgWarning("Support for flac not compiled in, define HAVE_FLAC in Makefile");
#endif
- }
- else
- {
- if( !strcasecmp( p, ".wav" ) )
- {
+ }
+ else {
+ if( !strcasecmp( p, ".wav" ) ) {
#ifdef HAVE_SNDFILE
- mt = MT_SND;
+ mt = MT_SND;
#else
- mgWarning("Support for wav files not compiled in, define HAVE_SNDFILE in Makefile" );
+ mgWarning("Support for wav files not compiled in, define HAVE_SNDFILE in Makefile" );
#endif
+ }
+ }
}
- }
}
- }
- return mt;
+ return mt;
}
-
mgDecoder *
-mgDecoders::findDecoder (mgItemGd * item)
-{
- mgDecoder *decoder = 0;
-
- std::string filename = item->getSourceFile ();
-
- switch (getMediaType (filename))
- {
- case MT_MP3:
- {
- decoder = new mgMP3Decoder (item);
- } break;
+mgDecoders::findDecoder (mgItemGd * item) {
+ mgDecoder *decoder = 0;
+
+ std::string filename = item->getSourceFile ();
+
+ switch (getMediaType (filename)) {
+ case MT_MP3:
+ {
+ decoder = new mgMP3Decoder (item);
+ } break;
#ifdef HAVE_VORBISFILE
- case MT_OGG:
- {
- decoder = new mgOggDecoder (item);
- } break;
+ case MT_OGG:
+ {
+ decoder = new mgOggDecoder (item);
+ } break;
#endif
#ifdef HAVE_FLAC
- case MT_FLAC:
- {
- decoder = new mgFlacDecoder( item );
- } break;
+ case MT_FLAC:
+ {
+ decoder = new mgFlacDecoder( item );
+ } break;
#endif
#ifdef HAVE_SNDFILE
- case MT_SND:
- {
- decoder = new mgSndfileDecoder( item );
- } break;
+ case MT_SND:
+ {
+ decoder = new mgSndfileDecoder( item );
+ } break;
#endif
- default:
- {
- esyslog ("ERROR: unknown media type ");
+ default:
+ {
+ esyslog ("ERROR: unknown media type ");
+ }
+ break;
}
- break;
- }
-
- if (decoder && !decoder->valid ())
- {
- // no decoder found or decoder doesn't match
-
- delete decoder; // might be carried out on NULL pointer
- decoder = 0;
-
- esyslog ("ERROR: no valid decoder found for %s", filename.c_str ());
- }
- return decoder;
-}
+ if (decoder && !decoder->valid ()) {
+ // no decoder found or decoder doesn't match
+
+ delete decoder; // might be carried out on NULL pointer
+ decoder = 0;
+
+ esyslog ("ERROR: no valid decoder found for %s", filename.c_str ());
+ }
+ return decoder;
+}
// --- mgDecoder ----------------------------------------------------------------
-mgDecoder::mgDecoder (mgItemGd * item)
-{
- m_item = item;
- m_locked = 0;
- m_urgentLock = false;
- m_playing = false;
+mgDecoder::mgDecoder (mgItemGd * item) {
+ m_item = item;
+ m_locked = 0;
+ m_urgentLock = false;
+ m_playing = false;
}
-mgDecoder::~mgDecoder ()
-{
+mgDecoder::~mgDecoder () {
}
void
-mgDecoder::lock (bool urgent)
-{
- m_locklock.Lock ();
-
- if (urgent && m_locked)
- {
- m_urgentLock = true; // signal other locks to release quickly
- }
- m_locked++;
-
- m_locklock.Unlock (); // don't hold the "locklock" when locking "lock", may cause a deadlock
- m_lock.Lock ();
- m_urgentLock = false;
-}
+mgDecoder::lock (bool urgent) {
+ m_locklock.Lock ();
+ if (urgent && m_locked) {
+ m_urgentLock = true; // signal other locks to release quickly
+ }
+ m_locked++;
+
+ m_locklock.Unlock (); // don't hold the "locklock" when locking "lock", may cause a deadlock
+ m_lock.Lock ();
+ m_urgentLock = false;
+}
void
-mgDecoder::unlock (void)
-{
- m_locklock.Lock ();
+mgDecoder::unlock (void) {
+ m_locklock.Lock ();
- m_locked--;
+ m_locked--;
- m_lock.Unlock ();
- m_locklock.Unlock ();
+ m_lock.Unlock ();
+ m_locklock.Unlock ();
}
+bool mgDecoder::tryLock (void) {
+ bool
+ res = false;
+ m_locklock.Lock ();
+
+ if (!m_locked && !m_playing) {
+ m_locked++;
-bool mgDecoder::tryLock (void)
-{
- bool
- res = false;
- m_locklock.Lock ();
-
- if (!m_locked && !m_playing)
- {
- m_locked++;
-
- m_locklock.Unlock (); // don't hold the "locklock" when locking
- // "lock", may cause a deadlock
- m_lock.Lock ();
- m_urgentLock = false;
- res = true;
- }
- else
- m_locklock.Unlock ();
- return res;
+ m_locklock.Unlock (); // don't hold the "locklock" when locking
+ // "lock", may cause a deadlock
+ m_lock.Lock ();
+ m_urgentLock = false;
+ res = true;
+ }
+ else
+ m_locklock.Unlock ();
+ return res;
}
diff --git a/vdr_decoder.h b/vdr_decoder.h
index 4867971..67f26b5 100644
--- a/vdr_decoder.h
+++ b/vdr_decoder.h
@@ -35,7 +35,7 @@
*/
enum eDecodeStatus
{
- dsOK = 0, dsPlay, dsSkip, dsEof, dsError, dsSoftError
+ dsOK = 0, dsPlay, dsSkip, dsEof, dsError, dsSoftError
};
// ----------------------------------------------------------------
@@ -46,9 +46,9 @@ enum eDecodeStatus
*/
struct mgDecode
{
- eDecodeStatus status;
- int index;
- struct mad_pcm *pcm;
+ eDecodeStatus status;
+ int index;
+ struct mad_pcm *pcm;
};
// ----------------------------------------------------------------
@@ -59,8 +59,8 @@ struct mgDecode
*/
class mgPlayInfo
{
- public:
- int m_index, m_total;
+ public:
+ int m_index, m_total;
};
// ----------------------------------------------------------------
@@ -71,7 +71,7 @@ class mgPlayInfo
*/
enum mgMediaType
{
- MT_MP3, MT_MP3_STREAM, MT_OGG, MT_FLAC, MT_SND, MT_UNKNOWN
+ MT_MP3, MT_MP3_STREAM, MT_OGG, MT_FLAC, MT_SND, MT_UNKNOWN
};
/*!
@@ -80,73 +80,70 @@ enum mgMediaType
*/
class mgDecoder
{
- protected:
-
- /*! \brief database handle to the track being decoded */
- mgItemGd * m_item;
-
- /*! \brief The currently playing file */
- std::string m_filename;
-
- /*! \brief Mutexes to coordinate threads */
- cMutex m_lock, m_locklock;
- int m_locked;
- bool m_urgentLock;
-
- /*! \brief Whether the decoder is currently active */
- bool m_playing;
-
- /*! \brief ??? */
- mgPlayInfo m_playinfo;
-
- /*! \brief Place a lock */
- virtual void lock (bool urgent = false);
-
- /*! \brief Release a lock */
- virtual void unlock (void);
-
- /*! \brief Try to obtain a lock */
- virtual bool tryLock (void);
-
- public:
-
- //@{
- /*! \brief The constructor */
- mgDecoder (mgItemGd * item);
-
- /*! \brief The destructor */
- virtual ~ mgDecoder ();
- //@}
-
- /*! \brief Whether a decoder instance is able to play the given file */
- virtual bool valid () = 0;
-
- /*! \brief Whether a stream (i.e. from the network is being decoded */
- virtual bool isStream ()
- {
- return false;
- }
-
- /*! \brief Start decoding */
- virtual bool start () = 0;
-
- /*! \brief Stop decoding */
- virtual bool stop () = 0;
-
- /*! \brief Skip an amount of time. Impossible by default */
- virtual bool skip (int seconds, int avail, int rate)
- {
- return false;
- }
-
- /*! \brief Return decoded data */
- virtual struct mgDecode *decode () = 0;
-
- /*! \brief Information about the current playback status */
- virtual mgPlayInfo *playInfo ()
- {
- return 0;
- }
+ protected:
+
+ /*! \brief database handle to the track being decoded */
+ mgItemGd * m_item;
+
+ /*! \brief The currently playing file */
+ std::string m_filename;
+
+ /*! \brief Mutexes to coordinate threads */
+ cMutex m_lock, m_locklock;
+ int m_locked;
+ bool m_urgentLock;
+
+ /*! \brief Whether the decoder is currently active */
+ bool m_playing;
+
+ /*! \brief ??? */
+ mgPlayInfo m_playinfo;
+
+ /*! \brief Place a lock */
+ virtual void lock (bool urgent = false);
+
+ /*! \brief Release a lock */
+ virtual void unlock (void);
+
+ /*! \brief Try to obtain a lock */
+ virtual bool tryLock (void);
+
+ public:
+
+ //@{
+ /*! \brief The constructor */
+ mgDecoder (mgItemGd * item);
+
+ /*! \brief The destructor */
+ virtual ~ mgDecoder ();
+ //@}
+
+ /*! \brief Whether a decoder instance is able to play the given file */
+ virtual bool valid () = 0;
+
+ /*! \brief Whether a stream (i.e. from the network is being decoded */
+ virtual bool isStream () {
+ return false;
+ }
+
+ /*! \brief Start decoding */
+ virtual bool start () = 0;
+
+ /*! \brief Stop decoding */
+ virtual bool stop () = 0;
+
+ /*! \brief Skip an amount of time. Impossible by default */
+ virtual bool skip (int seconds, int avail, int rate) {
+ return false;
+ }
+
+ /*! \brief Return decoded data */
+ virtual struct mgDecode *decode () = 0;
+
+ /*! \brief Information about the current playback status */
+ virtual mgPlayInfo *playInfo () {
+ return 0;
+ }
};
// ----------------------------------------------------------------
@@ -156,15 +153,15 @@ class mgDecoder
*/
class mgDecoders
{
- public:
-
- /*! \brief Try to find a valid decoder for a file
- */
- static mgDecoder *findDecoder (mgItemGd * item);
-
- /*! \brief determine the media type for a given source
- */
- static mgMediaType getMediaType (std::string filename);
-
+ public:
+
+ /*! \brief Try to find a valid decoder for a file
+ */
+ static mgDecoder *findDecoder (mgItemGd * item);
+
+ /*! \brief determine the media type for a given source
+ */
+ static mgMediaType getMediaType (std::string filename);
+
};
-#endif //___DECODER_H
+#endif //___DECODER_H
diff --git a/vdr_decoder_flac.c b/vdr_decoder_flac.c
index 6ecc6a5..3aa2f43 100644
--- a/vdr_decoder_flac.c
+++ b/vdr_decoder_flac.c
@@ -1,6 +1,6 @@
/*! \file vdr_decoder_flac.c
* \ingroup vdr
- *
+ *
* The file implements a decoder which is used by the player to decode flac audio files.
*
* Based on code from
@@ -27,358 +27,316 @@
#include <mad.h>
-
using namespace std;
static const unsigned MAX_RES_SIZE = 16384;
// --- mgFlacDecoder -------------------------------------------------------------
-mgFlacDecoder::mgFlacDecoder( mgItemGd *item )
- : mgDecoder( item ), FLAC::Decoder::File()
-{
- mgLog lg( "mgFlacDecoder::mgFlacDecoder" );
+mgFlacDecoder::mgFlacDecoder( mgItemGd *item )
+: mgDecoder( item ), FLAC::Decoder::File() {
- m_filename = item->getSourceFile();
- m_pcm = 0;
- m_reservoir = 0;
+ m_filename = item->getSourceFile();
+ m_pcm = 0;
+ m_reservoir = 0;
- initialize();
+ initialize();
}
-mgFlacDecoder::~mgFlacDecoder()
-{
- mgLog lg( "mgFlacDecoder::~mgFlacDecoder" );
- clean();
+mgFlacDecoder::~mgFlacDecoder() {
+ clean();
}
-bool mgFlacDecoder::valid()
-{
- // how to check whether this is a valid flac file?
- return is_valid();
+bool mgFlacDecoder::valid() {
+ // how to check whether this is a valid flac file?
+ return is_valid();
}
-mgPlayInfo *mgFlacDecoder::playInfo(void)
-{
- return 0;
+mgPlayInfo *mgFlacDecoder::playInfo(void) {
+ return 0;
}
-bool mgFlacDecoder::initialize()
-{
- mgLog lg( "mgFlacDecoder::initialize" );
- bool state = true;
-
- clean();
-
- // set_metadata_ignore_all();
- set_metadata_respond( FLAC__METADATA_TYPE_STREAMINFO );
-
- m_first = true;
- m_reservoir_count = 0;
- m_current_time_ms = 0;
- m_len_decoded = 0;
- m_index = 0;
- m_pcm = new struct mad_pcm;
-
- // init reservoir buffer; this should be according to the maximum
- // frame/sample size that we can probably obtain from metadata
- m_reservoir = new FLAC__int32*[2];
- m_reservoir[0] = new FLAC__int32[MAX_RES_SIZE];
- m_reservoir[1] = new FLAC__int32[MAX_RES_SIZE];
-
- // TODO: check init() return value
+bool mgFlacDecoder::initialize() {
+ bool state = true;
+
+ clean();
+
+ // set_metadata_ignore_all();
+ set_metadata_respond( FLAC__METADATA_TYPE_STREAMINFO );
+
+ m_first = true;
+ m_reservoir_count = 0;
+ m_current_time_ms = 0;
+ m_len_decoded = 0;
+ m_index = 0;
+ m_pcm = new struct mad_pcm;
+
+ // init reservoir buffer; this should be according to the maximum
+ // frame/sample size that we can probably obtain from metadata
+ m_reservoir = new FLAC__int32*[2];
+ m_reservoir[0] = new FLAC__int32[MAX_RES_SIZE];
+ m_reservoir[1] = new FLAC__int32[MAX_RES_SIZE];
+
+ // TODO: check init() return value
#ifdef LEGACY_FLAC
- set_filename( m_filename.c_str() );
- /*FLAC::Decoder::File::State d =*/ init();
+ set_filename( m_filename.c_str() );
+ /*FLAC::Decoder::File::State d =*/ init();
#else
- /*FLAC__StreamDecoderInitStatus d =*/ init( m_filename.c_str() );
+ /*FLAC__StreamDecoderInitStatus d =*/ init( m_filename.c_str() );
#endif
- process_until_end_of_metadata(); // basically just skip metadata
+ // basically just skip metadata
+ process_until_end_of_metadata();
- return state;
+ return state;
}
-bool mgFlacDecoder::clean()
-{
- mgLog lg( "mgFlacDecoder::clean" );
- m_playing = false;
-
- delete m_pcm;
- m_pcm = 0;
-
- if( m_reservoir )
- {
- delete[] m_reservoir[0];
- delete[] m_reservoir[1];
- }
- delete[] m_reservoir;
- m_reservoir = 0;
-
- // why false? true?
- return true;
+bool mgFlacDecoder::clean() {
+ m_playing = false;
+
+ delete m_pcm;
+ m_pcm = 0;
+
+ if( m_reservoir ) {
+ delete[] m_reservoir[0];
+ delete[] m_reservoir[1];
+ }
+ delete[] m_reservoir;
+ m_reservoir = 0;
+
+ // why false? true?
+ return true;
}
-bool mgFlacDecoder::start()
-{
- MGLOG( "mgFlacDecoder::start" );
- bool res = false;
- lock(true);
-
- // can FLAC handle more than 2 channels anyway?
- if( m_item->getChannels() <= 2 )
- {
- m_playing = true;
- res = true;
- }
- else
- {
- mgError( "ERROR: cannot play flac file %s: more than 2 channels", m_filename.c_str() );
- clean();
- }
-
- unlock();
- return res;
+bool mgFlacDecoder::start() {
+ bool res = false;
+ lock(true);
+
+ // can FLAC handle more than 2 channels anyway?
+ if( m_item->getChannels() <= 2 ) {
+ m_playing = true;
+ res = true;
+ }
+ else {
+ mgError( "ERROR: cannot play flac file %s: more than 2 channels", m_filename.c_str() );
+ clean();
+ }
+
+ unlock();
+ return res;
}
-bool mgFlacDecoder::stop(void)
-{
- MGLOG( "mgFlacDecoder::stop" );
- lock();
- finish();
+bool mgFlacDecoder::stop(void) {
+ lock();
+ finish();
- if( m_playing )
- {
- clean();
- }
- unlock();
+ if( m_playing ) {
+ clean();
+ }
+ unlock();
- return true;
+ return true;
}
-struct mgDecode *mgFlacDecoder::done( eDecodeStatus status )
-{
- m_ds.status = status;
- m_ds.index = m_index;
- m_ds.pcm = m_pcm;
+struct mgDecode *mgFlacDecoder::done( eDecodeStatus status ) {
+ m_ds.status = status;
+ m_ds.index = m_index;
+ m_ds.pcm = m_pcm;
- unlock(); // release the lock from Decode() !
+ unlock(); // release the lock from Decode() !
- return &m_ds;
+ return &m_ds;
}
-struct mgDecode *mgFlacDecoder::decode()
-{
- // mgLog lg( "mgFlacDecoder::decode" );
- m_decode_status = dsPlay;
-
- const unsigned SF_SAMPLES = (sizeof(m_pcm->samples[0])/sizeof(mad_fixed_t));
-
- lock(true); // this is released in done()
-
- if( m_playing )
- {
- m_pcm->samplerate = m_item->getSampleRate(); // from database
- m_pcm->channels = m_item->getChannels(); // from database
-
- // if there is enough data in the reservoir, don't start decoding
- // PROBLEM: but we need a first time!
- bool finished;
- if( m_first )
- {
- finished = false;
- m_first = false;
- }
- else
- {
- finished = m_reservoir_count >= SF_SAMPLES;
- }
-
- while( !finished )
- { // decode single frames until m_reservoir_count >= SF_SAMPLES or eof/error
- m_first = false;
-
- // decode a single sample into reservoir_buffer (done by the write callback)
- process_single();
+struct mgDecode *mgFlacDecoder::decode() {
+ m_decode_status = dsPlay;
+
+ const unsigned SF_SAMPLES = (sizeof(m_pcm->samples[0])/sizeof(mad_fixed_t));
+
+ lock(true); // this is released in done()
+
+ if( m_playing ) {
+ // from database
+ m_pcm->samplerate = m_item->getSampleRate();
+ // from database
+ m_pcm->channels = m_item->getChannels();
+
+ // if there is enough data in the reservoir, don't start decoding
+ // PROBLEM: but we need a first time!
+ bool finished;
+ if( m_first ) {
+ finished = false;
+ m_first = false;
+ }
+ else {
+ finished = m_reservoir_count >= SF_SAMPLES;
+ }
+
+ while( !finished ) {
+ // decode single frames until m_reservoir_count >= SF_SAMPLES or eof/error
+ m_first = false;
+
+ // decode a single sample into reservoir_buffer (done by the write callback)
+ process_single();
#ifdef LEGACY_FLAC
- if (get_stream_decoder_state()==FLAC__STREAM_DECODER_END_OF_STREAM)
+ if (get_stream_decoder_state()==FLAC__STREAM_DECODER_END_OF_STREAM)
#else
- if (get_state()==FLAC__STREAM_DECODER_END_OF_STREAM)
+ if (get_state()==FLAC__STREAM_DECODER_END_OF_STREAM)
#endif
- {
- m_decode_status = dsEof;
- finished = true;
- }
-
- // check termination criterion
- finished |= m_reservoir_count >= SF_SAMPLES || m_len_decoded == 0; // or error?
- }
-
- // transfer min( SF_SAMPLES, m_reservoir_count ) to pcm buffer
-
- int n = ( SF_SAMPLES <= m_reservoir_count )? SF_SAMPLES: m_reservoir_count;
-
- m_pcm->length = n;
- m_index = m_current_time_ms;
-
- // fill pcm container from reservoir buffer
- FLAC__int32 *data0 = m_reservoir[0];
- FLAC__int32 *data1 = m_reservoir[1];
-
- mad_fixed_t *sam0 = m_pcm->samples[0];
- mad_fixed_t *sam1 = m_pcm->samples[1];
-
- // determine shift value for mad_fixed conversion
- // TODO -- check for real bitsize and shit accordingly (left/right)
- const int s = MAD_F_FRACBITS + 1 - ( sizeof(short)*8 );
- // const int s = ( sizeof(int)*8 ) - 1 - MAD_F_FRACBITS; // from libsoundfile decoder
-
- if( m_pcm->channels > 1 )
- {
- for( int j=n; j > 0 ; j-- )
- {
- // copy buffer and transform (cf. libsoundfile decoder)
- *sam0++ = (*data0++) << s;
- *sam1++ = (*data1++) << s;
- }
- // "delete" transferred samples from reservoir buffer
- memmove( m_reservoir[0], m_reservoir[0] + n, (m_reservoir_count - n)*sizeof(FLAC__int32) );
- memmove( m_reservoir[1], m_reservoir[1] + n, (m_reservoir_count - n)*sizeof(FLAC__int32) );
- }
- else
- {
- for( int j=n; j > 0 ; j--)
- {
- *sam0++ = (*data0++) << s;
- }
- memmove( m_reservoir[0], m_reservoir[0] + n, (m_reservoir_count - n)*sizeof(FLAC__int32) );
- }
- m_reservoir_count -= n;
-
- // and return, indicating that playing will continue (unless an error has occurred)
- return done( m_decode_status );
- }
-
- return done(dsError);
+ {
+ m_decode_status = dsEof;
+ finished = true;
+ }
+
+ // check termination criterion
+ // or error?
+ finished |= m_reservoir_count >= SF_SAMPLES || m_len_decoded == 0;
+ }
+
+ // transfer min( SF_SAMPLES, m_reservoir_count ) to pcm buffer
+
+ int n = ( SF_SAMPLES <= m_reservoir_count )? SF_SAMPLES: m_reservoir_count;
+
+ m_pcm->length = n;
+ m_index = m_current_time_ms;
+
+ // fill pcm container from reservoir buffer
+ FLAC__int32 *data0 = m_reservoir[0];
+ FLAC__int32 *data1 = m_reservoir[1];
+
+ mad_fixed_t *sam0 = m_pcm->samples[0];
+ mad_fixed_t *sam1 = m_pcm->samples[1];
+
+ // determine shift value for mad_fixed conversion
+ // TODO -- check for real bitsize and shit accordingly (left/right)
+ const int s = MAD_F_FRACBITS + 1 - ( sizeof(short)*8 );
+ // const int s = ( sizeof(int)*8 ) - 1 - MAD_F_FRACBITS; // from libsoundfile decoder
+
+ if( m_pcm->channels > 1 ) {
+ for( int j=n; j > 0 ; j-- ) {
+ // copy buffer and transform (cf. libsoundfile decoder)
+ *sam0++ = (*data0++) << s;
+ *sam1++ = (*data1++) << s;
+ }
+ // "delete" transferred samples from reservoir buffer
+ memmove( m_reservoir[0], m_reservoir[0] + n, (m_reservoir_count - n)*sizeof(FLAC__int32) );
+ memmove( m_reservoir[1], m_reservoir[1] + n, (m_reservoir_count - n)*sizeof(FLAC__int32) );
+ }
+ else {
+ for( int j=n; j > 0 ; j--) {
+ *sam0++ = (*data0++) << s;
+ }
+ memmove( m_reservoir[0], m_reservoir[0] + n, (m_reservoir_count - n)*sizeof(FLAC__int32) );
+ }
+ m_reservoir_count -= n;
+
+ // and return, indicating that playing will continue (unless an error has occurred)
+ return done( m_decode_status );
+ }
+
+ return done(dsError);
}
-::FLAC__StreamDecoderWriteStatus
+::FLAC__StreamDecoderWriteStatus
mgFlacDecoder::write_callback(const ::FLAC__Frame *frame,
- const FLAC__int32 * const buffer[])
-{
- // mgLog lg( "mgFlacDecoder::write_callback" );
-
- // add decoded buffer to reservoir
- m_len_decoded = frame->header.blocksize;
- m_current_samples += m_len_decoded;
- m_current_time_ms += (m_len_decoded*1000) / m_pcm->samplerate; // in milliseconds
-
-
- // append buffer to m_reservoir
- if( m_len_decoded > 0 )
- {
- memmove( m_reservoir[0] + m_reservoir_count, buffer[0], m_len_decoded*sizeof(FLAC__int32) );
-
- if( m_pcm->channels > 1 )
- {
- memmove( m_reservoir[1] + m_reservoir_count, buffer[1], m_len_decoded*sizeof(FLAC__int32) );
- }
-
- m_reservoir_count += m_len_decoded;
- }
- else
- {
- m_decode_status = dsEof;
- }
-
- return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
-}
+const FLAC__int32 * const buffer[]) {
+
+ // add decoded buffer to reservoir
+ m_len_decoded = frame->header.blocksize;
+ m_current_samples += m_len_decoded;
+ // in milliseconds
+ m_current_time_ms += (m_len_decoded*1000) / m_pcm->samplerate;
+
+ // append buffer to m_reservoir
+ if( m_len_decoded > 0 ) {
+ memmove( m_reservoir[0] + m_reservoir_count, buffer[0], m_len_decoded*sizeof(FLAC__int32) );
-void mgFlacDecoder::metadata_callback( const ::FLAC__StreamMetadata *metadata )
-{
- // not needed since metadata is ignored!?
- mgLog lg( "mgFlacDecoder::metadata_callback" );
-
- if(metadata->type == FLAC__METADATA_TYPE_STREAMINFO)
- {
- m_nTotalSamples = (long)(metadata->data.stream_info.total_samples & 0xfffffffful);
- m_nBitsPerSample = metadata->data.stream_info.bits_per_sample;
- m_nCurrentChannels = metadata->data.stream_info.channels;
- m_nCurrentSampleRate = metadata->data.stream_info.sample_rate;
- m_nFrameSize = metadata->data.stream_info.max_framesize;
- m_nBlockSize = metadata->data.stream_info.max_blocksize;
-
- // m_nCurrentSampleRate = (int)get_sample_rate();
- m_nCurrentChannels = (int)get_channels();
- m_nCurrentBitsPerSample = (int)get_bits_per_sample();
- m_nBlockAlign = (m_nBitsPerSample / 8) * m_nCurrentChannels;
- m_nLengthMS = m_nTotalSamples / m_nCurrentSampleRate;
- m_nLengthMS *= 1000;
-
- // m_nAverageBitRate = ((m_pReader->GetLength() * 8) / (m_nLengthMS / 1000) / 1000);
- // m_nCurrentBitrate = m_nAverageBitRate;
- }
+ if( m_pcm->channels > 1 ) {
+ memmove( m_reservoir[1] + m_reservoir_count, buffer[1], m_len_decoded*sizeof(FLAC__int32) );
+ }
+
+ m_reservoir_count += m_len_decoded;
+ }
+ else {
+ m_decode_status = dsEof;
+ }
+
+ return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}
-void mgFlacDecoder::error_callback( ::FLAC__StreamDecoderErrorStatus status )
-{
- mgLog lg( "mgFlacDecoder::error_callback" );
-
- // check status and return
- switch( status )
- {
- case FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC:
- {
- m_error = "An error in the stream caused the decoder to lose synchronization";
- } break;
- case FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER:
- {
- m_error = "The decoder encountered a corrupted frame header.";
- } break;
- case FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH:
- {
- m_error = "The frame's data did not match the CRC in the footer.";
- };
- default:
- {
- m_error = "Unknown error occurred.";
- }
- }
-
- // cout << "Error occured: " << m_error << endl;
- m_decode_status = dsError;
+void mgFlacDecoder::metadata_callback( const ::FLAC__StreamMetadata *metadata ) {
+ // TODO not needed since metadata is ignored!?
+
+ if(metadata->type == FLAC__METADATA_TYPE_STREAMINFO) {
+ m_nTotalSamples = (long)(metadata->data.stream_info.total_samples & 0xfffffffful);
+ m_nBitsPerSample = metadata->data.stream_info.bits_per_sample;
+ m_nCurrentChannels = metadata->data.stream_info.channels;
+ m_nCurrentSampleRate = metadata->data.stream_info.sample_rate;
+ m_nFrameSize = metadata->data.stream_info.max_framesize;
+ m_nBlockSize = metadata->data.stream_info.max_blocksize;
+
+ // m_nCurrentSampleRate = (int)get_sample_rate();
+ m_nCurrentChannels = (int)get_channels();
+ m_nCurrentBitsPerSample = (int)get_bits_per_sample();
+ m_nBlockAlign = (m_nBitsPerSample / 8) * m_nCurrentChannels;
+ m_nLengthMS = m_nTotalSamples / m_nCurrentSampleRate;
+ m_nLengthMS *= 1000;
+
+ // m_nAverageBitRate = ((m_pReader->GetLength() * 8) / (m_nLengthMS / 1000) / 1000);
+ // m_nCurrentBitrate = m_nAverageBitRate;
+ }
}
-bool mgFlacDecoder::skip(int seconds, int avail, int rate)
-{
- lock();
- bool res = false;
-
- if( m_playing )
- {
- float bufsecs = (float) avail / (float) (rate * (16 / 8 * 2));
-
- const long target_time_ms = ( ( seconds - (int)bufsecs ) * 1000) + m_current_time_ms;
- const double distance = target_time_ms / (double)m_nLengthMS;
- const long target_sample = (unsigned)(distance * (double)m_nTotalSamples);
-
- if( target_sample > 0 )
- {
- if( seek_absolute( (FLAC__uint64)target_sample) )
- {
- m_current_time_ms = target_time_ms;
- }
- else
- {
- seek_absolute( 0 );
- m_current_time_ms = 0;
- }
- res = true;
+void mgFlacDecoder::error_callback( ::FLAC__StreamDecoderErrorStatus status ) {
+
+ // check status and return
+ switch( status ) {
+ case FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC:
+ {
+ m_error = "An error in the stream caused the decoder to lose synchronization";
+ } break;
+ case FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER:
+ {
+ m_error = "The decoder encountered a corrupted frame header.";
+ } break;
+ case FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH:
+ {
+ m_error = "The frame's data did not match the CRC in the footer.";
+ };
+ default:
+ {
+ m_error = "Unknown error occurred.";
+ }
}
- }
-
- unlock();
- return res;
+
+ // cout << "Error occured: " << m_error << endl;
+ m_decode_status = dsError;
}
-#endif //HAVE_FLAC
+bool mgFlacDecoder::skip(int seconds, int avail, int rate) {
+ lock();
+ bool res = false;
+
+ if( m_playing ) {
+ float bufsecs = (float) avail / (float) (rate * (16 / 8 * 2));
+
+ const long target_time_ms = ( ( seconds - (int)bufsecs ) * 1000) + m_current_time_ms;
+ const double distance = target_time_ms / (double)m_nLengthMS;
+ const long target_sample = (unsigned)(distance * (double)m_nTotalSamples);
+
+ if( target_sample > 0 ) {
+ if( seek_absolute( (FLAC__uint64)target_sample) ) {
+ m_current_time_ms = target_time_ms;
+ }
+ else {
+ seek_absolute( 0 );
+ m_current_time_ms = 0;
+ }
+ res = true;
+ }
+ }
+
+ unlock();
+ return res;
+}
+#endif //HAVE_FLAC
diff --git a/vdr_decoder_flac.h b/vdr_decoder_flac.h
index b465fa5..58bbe72 100644
--- a/vdr_decoder_flac.h
+++ b/vdr_decoder_flac.h
@@ -1,6 +1,6 @@
/*! \file vdr_decoder_flac.h
* \ingroup vdr
- *
+ *
* The file contains a decoder which is used by the player to decode flac audio files.
*
* Based on code from
@@ -21,60 +21,59 @@
// ----------------------------------------------------------------
-class mgFlacDecoder : public mgDecoder,
- public FLAC::Decoder::File
+class mgFlacDecoder : public mgDecoder,
+public FLAC::Decoder::File
{
- private:
-
- struct mgDecode m_ds;
- struct mad_pcm *m_pcm;
- unsigned long long m_index, m_current_time_ms, m_current_samples;
- unsigned int m_reservoir_count, m_len_decoded, m_samplerate;
+ private:
+
+ struct mgDecode m_ds;
+ struct mad_pcm *m_pcm;
+ unsigned long long m_index, m_current_time_ms, m_current_samples;
+ unsigned int m_reservoir_count, m_len_decoded, m_samplerate;
- long m_nCurrentSampleRate, m_nCurrentChannels, m_nCurrentBitsPerSample;
- long m_nLengthMS, m_nBlockAlign, m_nAverageBitRate, m_nCurrentBitrate;
- long m_nTotalSamples, m_nBitsPerSample, m_nFrameSize, m_nBlockSize;
+ long m_nCurrentSampleRate, m_nCurrentChannels, m_nCurrentBitsPerSample;
+ long m_nLengthMS, m_nBlockAlign, m_nAverageBitRate, m_nCurrentBitrate;
+ long m_nTotalSamples, m_nBitsPerSample, m_nFrameSize, m_nBlockSize;
- bool m_state, m_first, m_continue;
- std::string m_error;
+ bool m_state, m_first, m_continue;
+ std::string m_error;
- FLAC__int32 **m_reservoir;
+ FLAC__int32 **m_reservoir;
- bool initialize();
- bool clean();
+ bool initialize();
+ bool clean();
- struct mgDecode* done(eDecodeStatus status);
- eDecodeStatus m_decode_status;
+ struct mgDecode* done(eDecodeStatus status);
+ eDecodeStatus m_decode_status;
- protected:
+ protected:
- /*! \brief FLAC decoder callback routines */
- //@{
- virtual ::FLAC__StreamDecoderWriteStatus write_callback(const ::FLAC__Frame *frame,
- const FLAC__int32 * const buffer[]);
- virtual void metadata_callback(const ::FLAC__StreamMetadata *metadata);
- virtual void error_callback(::FLAC__StreamDecoderErrorStatus status);
- //@}
+ /*! \brief FLAC decoder callback routines */
+ //@{
+ virtual ::FLAC__StreamDecoderWriteStatus write_callback(const ::FLAC__Frame *frame,
+ const FLAC__int32 * const buffer[]);
+ virtual void metadata_callback(const ::FLAC__StreamMetadata *metadata);
+ virtual void error_callback(::FLAC__StreamDecoderErrorStatus status);
+ //@}
- public:
+ public:
- mgFlacDecoder( mgItemGd *item );
- ~mgFlacDecoder();
+ mgFlacDecoder( mgItemGd *item );
+ ~mgFlacDecoder();
- virtual mgPlayInfo *playInfo();
+ virtual mgPlayInfo *playInfo();
- virtual bool valid();
+ virtual bool valid();
- virtual bool start();
+ virtual bool start();
- virtual struct mgDecode *decode(void);
+ virtual struct mgDecode *decode(void);
- virtual bool skip(int Seconds, int Avail, int Rate);
+ virtual bool skip(int Seconds, int Avail, int Rate);
- virtual bool stop();
+ virtual bool stop();
};
// ----------------------------------------------------------------
-
-#endif //HAVE_FLAC
-#endif //___DECODER_FLAC_H
+#endif //HAVE_FLAC
+#endif //___DECODER_FLAC_H
diff --git a/vdr_decoder_mp3.c b/vdr_decoder_mp3.c
index b3b0f14..9c677a3 100644
--- a/vdr_decoder_mp3.c
+++ b/vdr_decoder_mp3.c
@@ -35,431 +35,367 @@
// ----------------------------------------------------------------
int
-mgMadStream (struct mad_stream *stream, mgStream * str)
-{
- unsigned char *data;
- unsigned long len;
- if (str->stream (data, len, stream->next_frame))
- {
- if (len > 0)
- {
- mad_stream_buffer (stream, data, len);
- }
- return len;
- }
- return -1;
+mgMadStream (struct mad_stream *stream, mgStream * str) {
+ unsigned char *data;
+ unsigned long len;
+ if (str->stream (data, len, stream->next_frame)) {
+ if (len > 0) {
+ mad_stream_buffer (stream, data, len);
+ }
+ return len;
+ }
+ return -1;
}
-
// --- mgMP3Decoder -------------------------------------------------------------
mgMP3Decoder::mgMP3Decoder (mgItemGd * item, bool preinit):mgDecoder
-(item)
-{
- m_stream = 0;
- m_isStream = false;
+(item) {
+ m_stream = 0;
+ m_isStream = false;
- m_filename = item->getSourceFile ();
+ m_filename = item->getSourceFile ();
- if (preinit)
- {
- m_stream = new mgStream (m_filename);
- printf ("m_stream for %s\n", m_filename.c_str ());
+ if (preinit) {
+ m_stream = new mgStream (m_filename);
+ printf ("m_stream for %s\n", m_filename.c_str ());
- }
- m_madframe = 0;
- m_madsynth = 0;
+ }
+ m_madframe = 0;
+ m_madsynth = 0;
- memset (&m_madstream, 0, sizeof (m_madstream));
+ memset (&m_madstream, 0, sizeof (m_madstream));
- init ();
+ init ();
}
-
-mgMP3Decoder::~mgMP3Decoder ()
-{
- clean ();
- delete m_stream;
+mgMP3Decoder::~mgMP3Decoder () {
+ clean ();
+ delete m_stream;
}
-
void
-mgMP3Decoder::init ()
-{
- clean ();
- mad_stream_init (&m_madstream);
+mgMP3Decoder::init () {
+ clean ();
+ mad_stream_init (&m_madstream);
- m_madframe = new mad_frame;
- mad_frame_init (m_madframe);
+ m_madframe = new mad_frame;
+ mad_frame_init (m_madframe);
- m_madsynth = new mad_synth;
- mad_synth_init (m_madsynth);
+ m_madsynth = new mad_synth;
+ mad_synth_init (m_madsynth);
- mad_stream_options (&m_madstream, MAD_OPTION_IGNORECRC);
+ mad_stream_options (&m_madstream, MAD_OPTION_IGNORECRC);
- m_playtime = mad_timer_zero;
- m_skiptime = mad_timer_zero;
- m_framenum = m_framemax = 0;
- m_frameinfo = 0;
- m_mute = m_errcount = 0;
+ m_playtime = mad_timer_zero;
+ m_skiptime = mad_timer_zero;
+ m_framenum = m_framemax = 0;
+ m_frameinfo = 0;
+ m_mute = m_errcount = 0;
}
-
void
-mgMP3Decoder::clean ()
-{
- m_playing = false;
- if (m_madsynth)
- {
- mad_synth_finish (m_madsynth);
- delete m_madsynth;
- m_madsynth = 0;
- }
-
- if (m_madframe)
- {
- mad_frame_finish (m_madframe);
- delete m_madframe;
- m_madframe = 0;
- }
- mad_stream_finish (&m_madstream);
+mgMP3Decoder::clean () {
+ m_playing = false;
+ if (m_madsynth) {
+ mad_synth_finish (m_madsynth);
+ delete m_madsynth;
+ m_madsynth = 0;
+ }
+
+ if (m_madframe) {
+ mad_frame_finish (m_madframe);
+ delete m_madframe;
+ m_madframe = 0;
+ }
+ mad_stream_finish (&m_madstream);
}
-
-bool mgMP3Decoder::valid (void)
-{
- bool
- res = false;
- if (tryLock ())
- {
- if (start ())
- {
- struct mgDecode *
- dd;
- int
- count = 10;
- do
- {
- dd = decode ();
- if (dd->status == dsEof)
- {
- count = 0;
- }
- if (dd->status != dsPlay)
- {
- break;
- }
- }
- while (--count);
- if (!count)
- {
- res = true;
- }
- stop ();
- }
- unlock ();
- }
- return res;
+bool mgMP3Decoder::valid (void) {
+ bool
+ res = false;
+ if (tryLock ()) {
+ if (start ()) {
+ struct mgDecode *
+ dd;
+ int
+ count = 10;
+ do {
+ dd = decode ();
+ if (dd->status == dsEof) {
+ count = 0;
+ }
+ if (dd->status != dsPlay) {
+ break;
+ }
+ }
+ while (--count);
+ if (!count) {
+ res = true;
+ }
+ stop ();
+ }
+ unlock ();
+ }
+ return res;
}
-
mgPlayInfo *
-mgMP3Decoder::playInfo ()
-{
- if (m_playing)
- {
- m_playinfo.m_index = mad_timer_count (m_playtime, MAD_UNITS_SECONDS);
-
- return &m_playinfo;
- }
- return 0;
-}
-
-
-bool mgMP3Decoder::start ()
-{
- lock (true);
- init ();
- m_playing = true;
-
- if (m_stream->open (true))
- {
- if (!m_isStream)
- {
- m_stream->seek ();
+mgMP3Decoder::playInfo () {
+ if (m_playing) {
+ m_playinfo.m_index = mad_timer_count (m_playtime, MAD_UNITS_SECONDS);
- printf
- ("mgMP3Decoder::start: m_framemax not determined, rewinding disabled!!!\n");
-
-/* m_framemax = scan->Frames+20; // TODO
-
- m_frameinfo = new struct FrameInfo[m_framemax];
- if(!m_frameinfo)
- {
- printf( "mgMP3Decoder::start: no memory for frame index, rewinding disabled" );
- }
- */
- }
- unlock ();
-
- printf ("mgMP3Decoder::start: true\n");
- return true;
- }
- m_stream->close ();
- clean ();
- unlock ();
- printf ("mgMP3Decoder::start: false");
- return false;
+ return &m_playinfo;
+ }
+ return 0;
}
-
-bool mgMP3Decoder::stop (void)
-{
- lock ();
-
- if (m_playing)
- {
- m_stream->close ();
- clean ();
- }
- unlock ();
- return true;
+bool mgMP3Decoder::start () {
+ lock (true);
+ init ();
+ m_playing = true;
+
+ if (m_stream->open (true)) {
+ if (!m_isStream) {
+ m_stream->seek ();
+
+ printf
+ ("mgMP3Decoder::start: m_framemax not determined, rewinding disabled!!!\n");
+
+ /* m_framemax = scan->Frames+20; // TODO
+
+ m_frameinfo = new struct FrameInfo[m_framemax];
+ if(!m_frameinfo)
+ {
+ printf( "mgMP3Decoder::start: no memory for frame index, rewinding disabled" );
+ }
+ */
+ }
+ unlock ();
+
+ printf ("mgMP3Decoder::start: true\n");
+ return true;
+ }
+ m_stream->close ();
+ clean ();
+ unlock ();
+ printf ("mgMP3Decoder::start: false");
+ return false;
}
+bool mgMP3Decoder::stop (void) {
+ lock ();
-struct mgDecode *
-mgMP3Decoder::done (eDecodeStatus status)
-{
- m_ds.status = status;
- m_ds.index = mad_timer_count (m_playtime, MAD_UNITS_MILLISECONDS);
- m_ds.pcm = &m_madsynth->pcm;
- unlock (); // release the lock from Decode()
-
- return &m_ds;
+ if (m_playing) {
+ m_stream->close ();
+ clean ();
+ }
+ unlock ();
+ return true;
}
+struct mgDecode *
+mgMP3Decoder::done (eDecodeStatus status) {
+ m_ds.status = status;
+ m_ds.index = mad_timer_count (m_playtime, MAD_UNITS_MILLISECONDS);
+ m_ds.pcm = &m_madsynth->pcm;
+ unlock (); // release the lock from Decode()
-eDecodeStatus mgMP3Decoder::decodeError (bool hdr)
-{
- if (m_madstream.error == MAD_ERROR_BUFLEN
- || m_madstream.error == MAD_ERROR_BUFPTR)
- {
- int
- s = mgMadStream (&m_madstream, m_stream);
- if (s < 0)
- {
- printf ("mgMP3Decoder::decodeError: dsError returned\n");
- return dsError;
- }
- if (s == 0)
- {
- printf ("mgMP3Decoder::decodeError: dsEof returned\n");
- return dsEof;
- }
- }
- else if (!MAD_RECOVERABLE (m_madstream.error))
- {
- printf
- ("mgMP3Decoder::decodeError: mad decode %sfailed, frame=%d: %s. Returning dsError\n",
- hdr ? "hdr " : "", m_framenum, mad_stream_errorstr (&m_madstream));
- return dsError;
- }
- else if (m_madstream.error!=MAD_ERROR_LOSTSYNC || m_framenum>0)
- // sync is always lost for frame 0, ignore this.
- // Do we use mad incorrectly?
- {
- m_errcount += hdr ? 1 : 100;
- printf
- ("mgMP3Decoder::decodeError: mad decode%s error, frame=%d count=%d: %s. Returning dsOK\n",
- hdr ? " hdr" : "", m_framenum, m_errcount,
- mad_stream_errorstr (&m_madstream));
- }
- return dsOK;
+ return &m_ds;
}
+eDecodeStatus mgMP3Decoder::decodeError (bool hdr) {
+ if (m_madstream.error == MAD_ERROR_BUFLEN
+ || m_madstream.error == MAD_ERROR_BUFPTR) {
+ int
+ s = mgMadStream (&m_madstream, m_stream);
+ if (s < 0) {
+ printf ("mgMP3Decoder::decodeError: dsError returned\n");
+ return dsError;
+ }
+ if (s == 0) {
+ printf ("mgMP3Decoder::decodeError: dsEof returned\n");
+ return dsEof;
+ }
+ }
+ else if (!MAD_RECOVERABLE (m_madstream.error)) {
+ printf
+ ("mgMP3Decoder::decodeError: mad decode %sfailed, frame=%d: %s. Returning dsError\n",
+ hdr ? "hdr " : "", m_framenum, mad_stream_errorstr (&m_madstream));
+ return dsError;
+ }
+ else if (m_madstream.error!=MAD_ERROR_LOSTSYNC || m_framenum>0) {
+ // sync is always lost for frame 0, ignore this.
+ // Do we use mad incorrectly?
+ m_errcount += hdr ? 1 : 100;
+ printf
+ ("mgMP3Decoder::decodeError: mad decode%s error, frame=%d count=%d: %s. Returning dsOK\n",
+ hdr ? " hdr" : "", m_framenum, m_errcount,
+ mad_stream_errorstr (&m_madstream));
+ }
+ return dsOK;
+}
struct mgDecode *
-mgMP3Decoder::decode ()
-{
- lock (); // this is released in Done()
- eDecodeStatus r;
-
- while (m_playing)
- {
- if (m_errcount >= MAX_FRAME_ERR * 100)
- {
- printf ("mgMP3Decoder::decode: excessive decoding errors,"
- " aborting file %s\n", m_filename.c_str ());
- return done (dsError);
- }
-
- if (mad_header_decode (&m_madframe->header, &m_madstream) == -1)
- {
- if ((r = decodeError (true)))
- {
- return done (r);
- }
- }
- else
- {
- if (!m_isStream)
- {
+mgMP3Decoder::decode () {
+ lock (); // this is released in Done()
+ eDecodeStatus r;
+
+ while (m_playing) {
+ if (m_errcount >= MAX_FRAME_ERR * 100) {
+ printf ("mgMP3Decoder::decode: excessive decoding errors,"
+ " aborting file %s\n", m_filename.c_str ());
+ return done (dsError);
+ }
+
+ if (mad_header_decode (&m_madframe->header, &m_madstream) == -1) {
+ if ((r = decodeError (true))) {
+ return done (r);
+ }
+ }
+ else {
+ if (!m_isStream) {
#ifdef DEBUG2
- if (m_framenum >= m_framemax)
- {
- printf ("mgMP3Decoder::start: framenum >= framemax!!!!\n");
- }
+ if (m_framenum >= m_framemax) {
+ printf ("mgMP3Decoder::start: framenum >= framemax!!!!\n");
+ }
#endif
- if (m_frameinfo && m_framenum < m_framemax)
- {
- m_frameinfo[m_framenum].Pos =
- m_stream->bufferPos () +
- (m_madstream.this_frame - m_madstream.buffer);
- m_frameinfo[m_framenum].Time = m_playtime;
- }
- }
-
- mad_timer_add (&m_playtime, m_madframe->header.duration);
- m_framenum++;
-
- if (mad_timer_compare (m_playtime, m_skiptime) >= 0)
- {
- m_skiptime = mad_timer_zero;
- }
- else
- {
- return done (dsSkip); // skipping, decode next header
- }
-
- if (mad_frame_decode (m_madframe, &m_madstream) == -1)
- {
- if ((r = decodeError (false)))
- {
- return done (r);
- }
- }
- else
- {
- m_errcount = 0;
-
-// TODO: // m_scan->InfoHook( &frame->header );
-
- mad_synth_frame (m_madsynth, m_madframe);
-
- if (m_mute)
- {
- m_mute--;
- return done (dsSkip);
- }
- return done (dsPlay);
- }
- }
- }
- return done (dsError);
+ if (m_frameinfo && m_framenum < m_framemax) {
+ m_frameinfo[m_framenum].Pos =
+ m_stream->bufferPos () +
+ (m_madstream.this_frame - m_madstream.buffer);
+ m_frameinfo[m_framenum].Time = m_playtime;
+ }
+ }
+
+ mad_timer_add (&m_playtime, m_madframe->header.duration);
+ m_framenum++;
+
+ if (mad_timer_compare (m_playtime, m_skiptime) >= 0) {
+ m_skiptime = mad_timer_zero;
+ }
+ else {
+ // skipping, decode next header
+ return done (dsSkip);
+ }
+
+ if (mad_frame_decode (m_madframe, &m_madstream) == -1) {
+ if ((r = decodeError (false))) {
+ return done (r);
+ }
+ }
+ else {
+ m_errcount = 0;
+
+ // TODO: // m_scan->InfoHook( &frame->header );
+
+ mad_synth_frame (m_madsynth, m_madframe);
+
+ if (m_mute) {
+ m_mute--;
+ return done (dsSkip);
+ }
+ return done (dsPlay);
+ }
+ }
+ }
+ return done (dsError);
}
-
void
mgMP3Decoder::makeSkipTime (mad_timer_t * skiptime,
mad_timer_t playtime,
-int secs, int avail, int dvbrate)
-{
- mad_timer_t time;
+int secs, int avail, int dvbrate) {
+ mad_timer_t time;
- *skiptime = playtime;
+ *skiptime = playtime;
- mad_timer_set (&time, abs (secs), 0, 0);
+ mad_timer_set (&time, abs (secs), 0, 0);
- if (secs < 0)
- {
- mad_timer_negate (&time);
- }
+ if (secs < 0) {
+ mad_timer_negate (&time);
+ }
- mad_timer_add (skiptime, time);
+ mad_timer_add (skiptime, time);
- // Byte/s = samplerate * 16 bit * 2 chan
- float bufsecs = (float) avail / (float) (dvbrate * (16 / 8 * 2));
+ // Byte/s = samplerate * 16 bit * 2 chan
+ float bufsecs = (float) avail / (float) (dvbrate * (16 / 8 * 2));
- printf ("mgMP3Decoder::makeSkipTime: skip: avail=%d bufsecs=%f\n", avail,
- bufsecs);
+ printf ("mgMP3Decoder::makeSkipTime: skip: avail=%d bufsecs=%f\n", avail,
+ bufsecs);
- int full = (int) bufsecs;
- bufsecs -= (float) full;
+ int full = (int) bufsecs;
+ bufsecs -= (float) full;
- mad_timer_set (&time, full, (int) (bufsecs * 1000.0), 1000);
+ mad_timer_set (&time, full, (int) (bufsecs * 1000.0), 1000);
- mad_timer_negate (&time);
+ mad_timer_negate (&time);
- mad_timer_add (skiptime, time);
+ mad_timer_add (skiptime, time);
- printf
- ("mgMP3Decoder::makeSkipTime: skip: playtime=%ld secs=%d full=%d bufsecs=%f skiptime=%ld\n",
- mad_timer_count (playtime, MAD_UNITS_MILLISECONDS), secs, full, bufsecs,
- mad_timer_count (*skiptime, MAD_UNITS_MILLISECONDS));
+ printf
+ ("mgMP3Decoder::makeSkipTime: skip: playtime=%ld secs=%d full=%d bufsecs=%f skiptime=%ld\n",
+ mad_timer_count (playtime, MAD_UNITS_MILLISECONDS), secs, full, bufsecs,
+ mad_timer_count (*skiptime, MAD_UNITS_MILLISECONDS));
}
+bool mgMP3Decoder::skip(int seconds, int avail, int rate) {
+ lock ();
-bool mgMP3Decoder::skip(int seconds, int avail, int rate)
-{
- lock ();
-
- bool
- res = false;
- if (m_playing && !m_isStream)
- {
- if (!mad_timer_compare (m_skiptime, mad_timer_zero))
- { // allow only one skip at any time
- mad_timer_t
- time;
- makeSkipTime (&time, m_playtime, seconds, avail, rate);
+ bool
+ res = false;
+ if (m_playing && !m_isStream) {
+ if (!mad_timer_compare (m_skiptime, mad_timer_zero)) {
+ // allow only one skip at any time
+ mad_timer_t
+ time;
+ makeSkipTime (&time, m_playtime, seconds, avail, rate);
- if (mad_timer_compare (m_playtime, time) <= 0)
- { // forward skip
+ if (mad_timer_compare (m_playtime, time) <= 0) {
+ // forward skip
#ifdef DEBUG
- int
- i = mad_timer_count (time, MAD_UNITS_SECONDS);
- printf ("mgMP3Decoder::skip: forward skipping to %02d:%02d\n",
- i / 60, i % 60);
+ int
+ i = mad_timer_count (time, MAD_UNITS_SECONDS);
+ printf ("mgMP3Decoder::skip: forward skipping to %02d:%02d\n",
+ i / 60, i % 60);
#endif
- m_skiptime = time;
- m_mute = 1;
- res = true;
- }
- else
- { // backward skip
- if (m_frameinfo)
- {
+ m_skiptime = time;
+ m_mute = 1;
+ res = true;
+ }
+ else {
+ // backward skip
+ if (m_frameinfo) {
#ifdef DEBUG
- int
- i = mad_timer_count (time, MAD_UNITS_SECONDS);
- printf ("mgMP3Decoder::skip: rewinding to %02d:%02d\n",
- i / 60, i % 60);
+ int
+ i = mad_timer_count (time, MAD_UNITS_SECONDS);
+ printf ("mgMP3Decoder::skip: rewinding to %02d:%02d\n",
+ i / 60, i % 60);
#endif
- while (m_framenum
- && mad_timer_compare (time,
- m_frameinfo[--m_framenum].
- Time) < 0);
- m_mute = 2;
- if (m_framenum >= 2)
- {
- m_framenum -= 2;
- }
- m_playtime = m_frameinfo[m_framenum].Time;
- m_stream->seek (m_frameinfo[m_framenum].Pos);
- // reset stream buffer
- mad_stream_finish (&m_madstream);
- mad_stream_init (&m_madstream);
+ while (m_framenum
+ && mad_timer_compare (time,
+ m_frameinfo[--m_framenum].
+ Time) < 0);
+ m_mute = 2;
+ if (m_framenum >= 2) {
+ m_framenum -= 2;
+ }
+ m_playtime = m_frameinfo[m_framenum].Time;
+ m_stream->seek (m_frameinfo[m_framenum].Pos);
+ // reset stream buffer
+ mad_stream_finish (&m_madstream);
+ mad_stream_init (&m_madstream);
#ifdef DEBUG
- i = mad_timer_count (m_playtime, MAD_UNITS_MILLISECONDS);
- printf
- ("mgMP3Decoder::skip: new playtime=%d framenum=%d filepos=%lld\n",
- i, m_framenum, m_frameinfo[m_framenum].Pos);
+ i = mad_timer_count (m_playtime, MAD_UNITS_MILLISECONDS);
+ printf
+ ("mgMP3Decoder::skip: new playtime=%d framenum=%d filepos=%lld\n",
+ i, m_framenum, m_frameinfo[m_framenum].Pos);
#endif
- res = true;
- }
- }
- }
- }
- unlock ();
- return res;
+ res = true;
+ }
+ }
+ }
+ }
+ unlock ();
+ return res;
}
diff --git a/vdr_decoder_mp3.h b/vdr_decoder_mp3.h
index 0149031..23e0d4f 100644
--- a/vdr_decoder_mp3.h
+++ b/vdr_decoder_mp3.h
@@ -38,76 +38,76 @@ class mgStream;
*/
class mgMP3Decoder:public mgDecoder
{
- private:
- struct mgDecode m_ds;
+ private:
+ struct mgDecode m_ds;
-//
- struct mad_stream m_madstream;
- struct mad_frame *m_madframe;
- struct mad_synth *m_madsynth;
- mad_timer_t m_playtime, m_skiptime;
+ //
+ struct mad_stream m_madstream;
+ struct mad_frame *m_madframe;
+ struct mad_synth *m_madsynth;
+ mad_timer_t m_playtime, m_skiptime;
-//
- struct FrameInfo
- {
- unsigned long long Pos;
- mad_timer_t Time;
- } *m_frameinfo;
+ //
+ struct FrameInfo
+ {
+ unsigned long long Pos;
+ mad_timer_t Time;
+ } *m_frameinfo;
- int m_framenum, m_framemax, m_errcount, m_mute;
+ int m_framenum, m_framemax, m_errcount, m_mute;
- void init ();
+ void init ();
- void clean ();
+ void clean ();
- struct mgDecode *done (eDecodeStatus status);
+ struct mgDecode *done (eDecodeStatus status);
- virtual mgPlayInfo *playInfo ();
+ virtual mgPlayInfo *playInfo ();
- eDecodeStatus decodeError (bool hdr);
+ eDecodeStatus decodeError (bool hdr);
- void makeSkipTime (mad_timer_t * skiptime, mad_timer_t playtime,
- int secs, int avail, int dvbrate);
+ void makeSkipTime (mad_timer_t * skiptime, mad_timer_t playtime,
+ int secs, int avail, int dvbrate);
- protected:
- mgStream * m_stream;
- bool m_isStream;
+ protected:
+ mgStream * m_stream;
+ bool m_isStream;
- public:
+ public:
-/*!
- * \brief construct a decoder from a filename
- */
- mgMP3Decoder (mgItemGd * item, bool preinit = true);
+ /*!
+ * \brief construct a decoder from a filename
+ */
+ mgMP3Decoder (mgItemGd * item, bool preinit = true);
-/*!
- * \brief the destructor
- */
- virtual ~ mgMP3Decoder ();
+ /*!
+ * \brief the destructor
+ */
+ virtual ~ mgMP3Decoder ();
-/*!
- * \brief check, whether the file contains useable MP3 content
- */
- virtual bool valid ();
+ /*!
+ * \brief check, whether the file contains useable MP3 content
+ */
+ virtual bool valid ();
-/*!
- * \brief start the decoding process
- */
- virtual bool start ();
+ /*!
+ * \brief start the decoding process
+ */
+ virtual bool start ();
-/*!
- * \brief stop the decoding process
- */
- virtual bool stop ();
+ /*!
+ * \brief stop the decoding process
+ */
+ virtual bool stop ();
-/*!
- * \brief skip an amount of seconds
- */
- virtual bool skip (int seconds, int avail, int rate);
+ /*!
+ * \brief skip an amount of seconds
+ */
+ virtual bool skip (int seconds, int avail, int rate);
-/*!
- * \brief the actual decoding function (uses libmad)
- */
- virtual struct mgDecode *decode ();
+ /*!
+ * \brief the actual decoding function (uses libmad)
+ */
+ virtual struct mgDecode *decode ();
};
-#endif //___DECODER_MP3_H
+#endif //___DECODER_MP3_H
diff --git a/vdr_decoder_ogg.c b/vdr_decoder_ogg.c
index 91c9a70..b24c301 100644
--- a/vdr_decoder_ogg.c
+++ b/vdr_decoder_ogg.c
@@ -19,440 +19,370 @@
#include <stdlib.h>
#include <stdio.h>
-
// --- mgOggFile ----------------------------------------------------------------
-class mgOggFile // : public mgFileInfo
+class mgOggFile // : public mgFileInfo
{
- private:
+ private:
- bool m_opened, m_canSeek;
+ bool m_opened, m_canSeek;
- OggVorbis_File vf;
+ OggVorbis_File vf;
- void error (const char *action, const int err);
+ void error (const char *action, const int err);
- std::string m_filename;
+ std::string m_filename;
- public:
+ public:
- mgOggFile (std::string filename);
- ~mgOggFile ();
+ mgOggFile (std::string filename);
+ ~mgOggFile ();
- bool open (bool log = true);
+ bool open (bool log = true);
- void close (void);
+ void close (void);
- long long seek (long long posMs = 0, bool relativ = false);
+ long long seek (long long posMs = 0, bool relativ = false);
- int stream (short *buffer, int samples);
+ int stream (short *buffer, int samples);
- bool canSeek ()
- {
- return m_canSeek;
- }
+ bool canSeek () {
+ return m_canSeek;
+ }
- long long indexMs (void);
+ long long indexMs (void);
};
mgOggFile::mgOggFile (std::string filename):
-m_filename (filename)
-{
- m_canSeek = false;
- m_opened = false;
+m_filename (filename) {
+ m_canSeek = false;
+ m_opened = false;
}
-
-mgOggFile::~mgOggFile ()
-{
- close ();
+mgOggFile::~mgOggFile () {
+ close ();
}
-
-bool mgOggFile::open (bool log)
-{
- if (m_opened)
- {
- if (m_canSeek)
- {
- return (seek () >= 0);
- }
- return true;
- }
-
- FILE *
- f = fopen (m_filename.c_str (), "r");
- if (f)
- {
- int
- r = ov_open (f, &vf, 0, 0);
- if (!r)
- {
- m_canSeek = (ov_seekable (&vf) != 0);
- m_opened = true;
- }
- else
- {
- fclose (f);
- if (log)
- {
- error ("open", r);
- }
- }
- }
- else
- {
- if (log)
- {
-// esyslog("ERROR: failed to open file %s: %s", m_filename.c_str(), strerror(errno) );
- }
- }
- return m_opened;
+bool mgOggFile::open (bool log) {
+ if (m_opened) {
+ if (m_canSeek) {
+ return (seek () >= 0);
+ }
+ return true;
+ }
+
+ FILE *
+ f = fopen (m_filename.c_str (), "r");
+ if (f) {
+ int
+ r = ov_open (f, &vf, 0, 0);
+ if (!r) {
+ m_canSeek = (ov_seekable (&vf) != 0);
+ m_opened = true;
+ }
+ else {
+ fclose (f);
+ if (log) {
+ error ("open", r);
+ }
+ }
+ }
+ else {
+ if (log) {
+ // esyslog("ERROR: failed to open file %s: %s", m_filename.c_str(), strerror(errno) );
+ }
+ }
+ return m_opened;
}
-
void
-mgOggFile::close ()
-{
- if (m_opened)
- {
- ov_clear (&vf);
- m_opened = false;
- }
+mgOggFile::close () {
+ if (m_opened) {
+ ov_clear (&vf);
+ m_opened = false;
+ }
}
-
void
-mgOggFile::error (const char *action, const int err)
-{
- char *errstr;
- switch (err)
- {
- case OV_FALSE:
- errstr = "false/no data available";
- break;
- case OV_EOF:
- errstr = "EOF";
- break;
- case OV_HOLE:
- errstr = "missing or corrupted data";
- break;
- case OV_EREAD:
- errstr = "read error";
- break;
- case OV_EFAULT:
- errstr = "internal error";
- break;
- case OV_EIMPL:
- errstr = "unimplemented feature";
- break;
- case OV_EINVAL:
- errstr = "invalid argument";
- break;
- case OV_ENOTVORBIS:
- errstr = "no Ogg Vorbis stream";
- break;
- case OV_EBADHEADER:
- errstr = "corrupted Ogg Vorbis stream";
- break;
- case OV_EVERSION:
- errstr = "unsupported bitstream version";
- break;
- case OV_ENOTAUDIO:
- errstr = "ENOTAUDIO";
- break;
- case OV_EBADPACKET:
- errstr = "EBADPACKET";
- break;
- case OV_EBADLINK:
- errstr = "corrupted link";
- break;
- case OV_ENOSEEK:
- errstr = "stream not seekable";
- break;
- default:
- errstr = "unspecified error";
- break;
- }
-// esyslog( "ERROR: vorbisfile %s failed on %s: %s", action, m_filename.c_str(), errstr );
+mgOggFile::error (const char *action, const int err) {
+ char *errstr;
+ switch (err) {
+ case OV_FALSE:
+ errstr = "false/no data available";
+ break;
+ case OV_EOF:
+ errstr = "EOF";
+ break;
+ case OV_HOLE:
+ errstr = "missing or corrupted data";
+ break;
+ case OV_EREAD:
+ errstr = "read error";
+ break;
+ case OV_EFAULT:
+ errstr = "internal error";
+ break;
+ case OV_EIMPL:
+ errstr = "unimplemented feature";
+ break;
+ case OV_EINVAL:
+ errstr = "invalid argument";
+ break;
+ case OV_ENOTVORBIS:
+ errstr = "no Ogg Vorbis stream";
+ break;
+ case OV_EBADHEADER:
+ errstr = "corrupted Ogg Vorbis stream";
+ break;
+ case OV_EVERSION:
+ errstr = "unsupported bitstream version";
+ break;
+ case OV_ENOTAUDIO:
+ errstr = "ENOTAUDIO";
+ break;
+ case OV_EBADPACKET:
+ errstr = "EBADPACKET";
+ break;
+ case OV_EBADLINK:
+ errstr = "corrupted link";
+ break;
+ case OV_ENOSEEK:
+ errstr = "stream not seekable";
+ break;
+ default:
+ errstr = "unspecified error";
+ break;
+ }
+ // esyslog( "ERROR: vorbisfile %s failed on %s: %s", action, m_filename.c_str(), errstr );
}
-
long long
-mgOggFile::indexMs (void)
-{
- double p = ov_time_tell (&vf);
- if (p < 0.0)
- {
- p = 0.0;
- }
+mgOggFile::indexMs (void) {
+ double p = ov_time_tell (&vf);
+ if (p < 0.0) {
+ p = 0.0;
+ }
- return (long long) (p * 1000.0);
+ return (long long) (p * 1000.0);
}
-
long long
-mgOggFile::seek (long long posMs, bool relativ)
-{
- if (relativ)
- {
- posMs += indexMs ();
- }
+mgOggFile::seek (long long posMs, bool relativ) {
+ if (relativ) {
+ posMs += indexMs ();
+ }
- int r = ov_time_seek (&vf, (double) posMs / 1000.0);
+ int r = ov_time_seek (&vf, (double) posMs / 1000.0);
- if (r)
- {
- error ("seek", r);
- return -1;
- }
+ if (r) {
+ error ("seek", r);
+ return -1;
+ }
- posMs = indexMs ();
- return posMs;
+ posMs = indexMs ();
+ return posMs;
}
-
int
-mgOggFile::stream (short *buffer, int samples)
-{
- int n;
- do
- {
- int stream;
- n = ov_read (&vf, (char *) buffer, samples * 2, 0, 2, 1, &stream);
- }
- while (n == OV_HOLE);
-
- if (n < 0)
- {
- error ("read", n);
- }
-
- return (n / 2);
+mgOggFile::stream (short *buffer, int samples) {
+ int n;
+ do {
+ int stream;
+ n = ov_read (&vf, (char *) buffer, samples * 2, 0, 2, 1, &stream);
+ }
+ while (n == OV_HOLE);
+
+ if (n < 0) {
+ error ("read", n);
+ }
+
+ return (n / 2);
}
-
// --- mgOggDecoder -------------------------------------------------------------
-mgOggDecoder::mgOggDecoder (mgItemGd * item):mgDecoder (item)
-{
- m_filename = item->getSourceFile ();
- m_file = new mgOggFile (m_filename);
- m_pcm = 0;
- init ();
+mgOggDecoder::mgOggDecoder (mgItemGd * item):mgDecoder (item) {
+ m_filename = item->getSourceFile ();
+ m_file = new mgOggFile (m_filename);
+ m_pcm = 0;
+ init ();
}
-
-mgOggDecoder::~mgOggDecoder ()
-{
- clean ();
- delete m_file;
+mgOggDecoder::~mgOggDecoder () {
+ clean ();
+ delete m_file;
}
-
-bool mgOggDecoder::valid ()
-{
- bool
- res = false;
- if (tryLock ())
- {
- if (m_file->open (false))
- {
- res = true;
- }
- unlock ();
- }
- return res;
+bool mgOggDecoder::valid () {
+ bool
+ res = false;
+ if (tryLock ()) {
+ if (m_file->open (false)) {
+ res = true;
+ }
+ unlock ();
+ }
+ return res;
}
-
mgPlayInfo *
-mgOggDecoder::playInfo (void)
-{
- if (m_playing)
- {
-// m_playinfo.m_index = index/1000;
-// m_playinfo.m_total = info.Total;
+mgOggDecoder::playInfo (void) {
+ if (m_playing) {
+ // m_playinfo.m_index = index/1000;
+ // m_playinfo.m_total = info.Total;
- return &m_playinfo;
- }
+ return &m_playinfo;
+ }
- return 0;
+ return 0;
}
-
void
-mgOggDecoder::init ()
-{
- clean ();
- m_pcm = new struct mad_pcm;
- m_index = 0;
+mgOggDecoder::init () {
+ clean ();
+ m_pcm = new struct mad_pcm;
+ m_index = 0;
}
+bool mgOggDecoder::clean () {
+ m_playing = false;
-bool mgOggDecoder::clean ()
-{
- m_playing = false;
+ delete
+ m_pcm;
+ m_pcm = 0;
- delete
- m_pcm;
- m_pcm = 0;
-
- m_file->close ();
- return false;
+ m_file->close ();
+ return false;
}
-
#define SF_SAMPLES (sizeof(m_pcm->samples[0])/sizeof(mad_fixed_t))
-bool mgOggDecoder::start ()
-{
- lock (true);
- init ();
- m_playing = true;
-
- if (m_file->open () /*&& info.DoScan(true) */ )
- {
-// obtain from database: rate, channels
-/* d(printf("ogg: open rate=%d channels=%d seek=%d\n",
- info.SampleFreq,info.Channels,file.CanSeek()))
- */
- if (m_item->getChannels () <= 2)
- {
- unlock ();
- return true;
- }
- else
- {
-// esyslog( "ERROR: cannot play ogg file %s: more than 2 channels", m_filename.c_str() );
- }
- }
-
- clean ();
- unlock ();
-
- return false;
+bool mgOggDecoder::start () {
+ lock (true);
+ init ();
+ m_playing = true;
+
+ if (m_file->open () /*&& info.DoScan(true) */ ) {
+ // obtain from database: rate, channels
+ /* d(printf("ogg: open rate=%d channels=%d seek=%d\n",
+ info.SampleFreq,info.Channels,file.CanSeek()))
+ */
+ if (m_item->getChannels () <= 2) {
+ unlock ();
+ return true;
+ }
+ else {
+ // esyslog( "ERROR: cannot play ogg file %s: more than 2 channels", m_filename.c_str() );
+ }
+ }
+
+ clean ();
+ unlock ();
+
+ return false;
}
+bool mgOggDecoder::stop (void) {
+ lock ();
-bool mgOggDecoder::stop (void)
-{
- lock ();
+ if (m_playing) {
+ clean ();
+ }
+ unlock ();
- if (m_playing)
- {
- clean ();
- }
- unlock ();
-
- return true;
+ return true;
}
-
struct mgDecode *
-mgOggDecoder::done (eDecodeStatus status)
-{
- m_ds.status = status;
- m_ds.index = m_index;
- m_ds.pcm = m_pcm;
+mgOggDecoder::done (eDecodeStatus status) {
+ m_ds.status = status;
+ m_ds.index = m_index;
+ m_ds.pcm = m_pcm;
- unlock (); // release the lock from decode()
+ unlock (); // release the lock from decode()
- return &m_ds;
+ return &m_ds;
}
-
struct mgDecode *
-mgOggDecoder::decode (void)
-{
- lock (); // this is released in Done()
-
- if (m_playing)
- {
- short framebuff[2 * SF_SAMPLES];
- int n = m_file->stream (framebuff, SF_SAMPLES);
-
- if (n < 0)
- {
- return done (dsError);
- }
-
- if (n == 0)
- {
- return done (dsEof);
- }
-
-// should be done during initialization
- // from database
- m_pcm->samplerate = m_item->getSampleRate ();
- m_pcm->channels = m_item->getChannels (); // from database
-
- n /= m_pcm->channels;
- m_pcm->length = n;
- m_index = m_file->indexMs ();
-
- short *data = framebuff;
- mad_fixed_t *sam0 = m_pcm->samples[0], *sam1 = m_pcm->samples[1];
-
- // shift value for mad_fixed conversion
- const int s = MAD_F_FRACBITS + 1 - (sizeof (short) * 8);
-
- if (m_pcm->channels > 1)
- {
- for (; n > 0; n--)
- {
- *sam0++ = (*data++) << s;
- *sam1++ = (*data++) << s;
- }
- }
- else
- {
- for (; n > 0; n--)
- {
- *sam0++ = (*data++) << s;
- }
- }
- return done (dsPlay);
- }
- return done (dsError);
+mgOggDecoder::decode (void) {
+ lock (); // this is released in Done()
+
+ if (m_playing) {
+ short framebuff[2 * SF_SAMPLES];
+ int n = m_file->stream (framebuff, SF_SAMPLES);
+
+ if (n < 0) {
+ return done (dsError);
+ }
+
+ if (n == 0) {
+ return done (dsEof);
+ }
+
+ // should be done during initialization
+ // from database
+ m_pcm->samplerate = m_item->getSampleRate ();
+ // from database
+ m_pcm->channels = m_item->getChannels ();
+
+ n /= m_pcm->channels;
+ m_pcm->length = n;
+ m_index = m_file->indexMs ();
+
+ short *data = framebuff;
+ mad_fixed_t *sam0 = m_pcm->samples[0], *sam1 = m_pcm->samples[1];
+
+ // shift value for mad_fixed conversion
+ const int s = MAD_F_FRACBITS + 1 - (sizeof (short) * 8);
+
+ if (m_pcm->channels > 1) {
+ for (; n > 0; n--) {
+ *sam0++ = (*data++) << s;
+ *sam1++ = (*data++) << s;
+ }
+ }
+ else {
+ for (; n > 0; n--) {
+ *sam0++ = (*data++) << s;
+ }
+ }
+ return done (dsPlay);
+ }
+ return done (dsError);
}
+bool mgOggDecoder::skip (int Seconds, int Avail, int Rate) {
+ lock ();
+ bool
+ res = false;
-bool mgOggDecoder::skip (int Seconds, int Avail, int Rate)
-{
- lock ();
- bool
- res = false;
-
- if (m_playing && m_file->canSeek ())
- {
- float
- fsecs =
- (float) Seconds - ((float) Avail / (float) (Rate * (16 / 8 * 2)));
-// Byte/s = samplerate * 16 bit * 2 chan
-
- long long
- newpos = m_file->indexMs () + (long long) (fsecs * 1000.0);
-
- if (newpos < 0)
- {
- newpos = 0;
- }
-
- newpos = m_file->seek (newpos, false);
-
- if (newpos >= 0)
- {
- m_index = m_file->indexMs ();
+ if (m_playing && m_file->canSeek ()) {
+ float
+ fsecs =
+ (float) Seconds - ((float) Avail / (float) (Rate * (16 / 8 * 2)));
+ // Byte/s = samplerate * 16 bit * 2 chan
+
+ long long
+ newpos = m_file->indexMs () + (long long) (fsecs * 1000.0);
+
+ if (newpos < 0) {
+ newpos = 0;
+ }
+
+ newpos = m_file->seek (newpos, false);
+
+ if (newpos >= 0) {
+ m_index = m_file->indexMs ();
#ifdef xDEBUG
- int
- i = index / 1000;
- printf ("ogg: skipping to %02d:%02d\n", i / 60, i % 60);
+ int
+ i = index / 1000;
+ printf ("ogg: skipping to %02d:%02d\n", i / 60, i % 60);
#endif
- res = true;
- }
- }
- unlock ();
- return res;
+ res = true;
+ }
+ }
+ unlock ();
+ return res;
}
-#endif //HAVE_VORBISFILE
+#endif //HAVE_VORBISFILE
diff --git a/vdr_decoder_ogg.h b/vdr_decoder_ogg.h
index 6f142f3..9e42d63 100644
--- a/vdr_decoder_ogg.h
+++ b/vdr_decoder_ogg.h
@@ -28,36 +28,36 @@ class mgOggFile;
*/
class mgOggDecoder:public mgDecoder
{
- private:
+ private:
- mgOggFile * m_file;
- struct mgDecode m_ds;
- struct mad_pcm *m_pcm;
- unsigned long long m_index;
+ mgOggFile * m_file;
+ struct mgDecode m_ds;
+ struct mad_pcm *m_pcm;
+ unsigned long long m_index;
-//
- void init (void);
- bool clean (void);
- struct mgDecode *done (eDecodeStatus status);
+ //
+ void init (void);
+ bool clean (void);
+ struct mgDecode *done (eDecodeStatus status);
- public:
+ public:
- mgOggDecoder (mgItemGd * item);
- ~mgOggDecoder ();
+ mgOggDecoder (mgItemGd * item);
+ ~mgOggDecoder ();
- virtual mgPlayInfo *playInfo ();
+ virtual mgPlayInfo *playInfo ();
- virtual bool valid ();
+ virtual bool valid ();
- virtual bool start ();
+ virtual bool start ();
- virtual bool stop ();
+ virtual bool stop ();
- virtual bool skip (int Seconds, int Avail, int Rate);
+ virtual bool skip (int Seconds, int Avail, int Rate);
- virtual struct mgDecode *decode (void);
+ virtual struct mgDecode *decode (void);
};
// ----------------------------------------------------------------
-#endif //HAVE_VORBISFILE
-#endif //___DECODER_OGG_H
+#endif //HAVE_VORBISFILE
+#endif //___DECODER_OGG_H
diff --git a/vdr_decoder_sndfile.c b/vdr_decoder_sndfile.c
index aad74dc..da10eda 100644
--- a/vdr_decoder_sndfile.c
+++ b/vdr_decoder_sndfile.c
@@ -30,10 +30,9 @@
#include <unistd.h>
#include <math.h>
-
#include "mg_setup.h"
#include "vdr_decoder_sndfile.h"
-#include "i18n.h"
+#include <vdr/i18n.h>
#define __STL_CONFIG_H
#include <vdr/tools.h>
@@ -48,13 +47,13 @@
#define CDFS_MARK_TR "%*[^[][ %d - %d"
#define CDFS_TRACK "track-"
-#define CDDB_PROTO 5 // used protocol level
-#define CDDB_TOUT 30*1000 // connection timeout (ms)
+#define CDDB_PROTO 5 // used protocol level
+#define CDDB_TOUT 30*1000 // connection timeout (ms)
#ifdef DEBUG_CDFS
#define dc(x) { (x); }
#else
-#define dc(x) ;
+#define dc(x) ;
#endif
// --- mgSndfileDecoder -------------------------------------------------------------
@@ -62,353 +61,302 @@
#define SF_SAMPLES (sizeof(m_pcm->samples[0])/sizeof(mad_fixed_t))
mgSndfileDecoder::mgSndfileDecoder( mgItemGd *item )
- : mgDecoder( item ), m_file( item )
-{
-
- m_pcm = 0;
- m_framebuff = 0;
- m_playing = false;
- m_ready = false;
+: mgDecoder( item ), m_file( item ) {
+
+ m_pcm = 0;
+ m_framebuff = 0;
+ m_playing = false;
+ m_ready = false;
}
-mgSndfileDecoder::~mgSndfileDecoder()
-{
- clean();
+mgSndfileDecoder::~mgSndfileDecoder() {
+ clean();
}
-bool mgSndfileDecoder::valid(void)
-{
- bool res = false;
+bool mgSndfileDecoder::valid(void) {
+ bool res = false;
- if( tryLock() )
- {
- if( m_file.Open(false) )
- {
- res=true;
+ if( tryLock() ) {
+ if( m_file.Open(false) ) {
+ res=true;
+ }
+ mgDecoder::unlock();
}
- mgDecoder::unlock();
- }
- return res;
+ return res;
}
mgPlayInfo *
-mgSndfileDecoder::playInfo ()
-{
- mgPlayInfo *pi = NULL;
+mgSndfileDecoder::playInfo () {
+ mgPlayInfo *pi = NULL;
- return pi;
+ return pi;
}
-void mgSndfileDecoder::init(void)
-{
- clean();
+void mgSndfileDecoder::init(void) {
+ clean();
- m_pcm = new struct mad_pcm;
- m_framebuff = MALLOC( int, 2*SF_SAMPLES + 8 );
+ m_pcm = new struct mad_pcm;
+ m_framebuff = MALLOC( int, 2*SF_SAMPLES + 8 );
#ifdef GUARD_DEBUG
- for(int i=0; i<8; i++) m_framebuff[i+(SF_SAMPLES*2)-4]=0xdeadbeaf;
+ for(int i=0; i<8; i++) m_framebuff[i+(SF_SAMPLES*2)-4]=0xdeadbeaf;
#endif
- m_index = 0;
+ m_index = 0;
}
-bool mgSndfileDecoder::clean(void)
-{
- m_playing = false;
+bool mgSndfileDecoder::clean(void) {
+ m_playing = false;
- m_buffMutex.Lock();
- m_run = false;
- m_bgCond.Broadcast();
- m_buffMutex.Unlock();
+ m_buffMutex.Lock();
+ m_run = false;
+ m_bgCond.Broadcast();
+ m_buffMutex.Unlock();
- cThread::Cancel(3);
+ cThread::Cancel(3);
- m_buffMutex.Lock();
- if( !m_ready )
- {
- m_deferedN = -1;
- m_ready=true;
- }
- m_fgCond.Broadcast();
- m_buffMutex.Unlock();
+ m_buffMutex.Lock();
+ if( !m_ready ) {
+ m_deferedN = -1;
+ m_ready=true;
+ }
+ m_fgCond.Broadcast();
+ m_buffMutex.Unlock();
- delete m_pcm;
- m_pcm=0;
+ delete m_pcm;
+ m_pcm=0;
#ifdef GUARD_DEBUG
- if(m_framebuff)
- {
- printf("snd: bufferguard");
- for(int i=0; i<8; i++) printf(" %08x",m_framebuff[i+(SF_SAMPLES*2)-4]);
- printf("\n");
- }
+ if(m_framebuff) {
+ printf("snd: bufferguard");
+ for(int i=0; i<8; i++) printf(" %08x",m_framebuff[i+(SF_SAMPLES*2)-4]);
+ printf("\n");
+ }
#endif
- free(m_framebuff);
- m_framebuff=0;
- m_file.Close();
+ free(m_framebuff);
+ m_framebuff=0;
+ m_file.Close();
- return false;
+ return false;
}
-bool mgSndfileDecoder::start(void)
-{
- mgDecoder::lock(true);
- init();
- m_playing=true;
-
- if( m_file.Open() )
- {
- // d(printf("snd: open rate=%d frames=%lld channels=%d format=0x%x seek=%d\n",
- // m_file.SoundfileInfo()->samplerate,m_file.SoundfileInfo()->frames,m_file.SoundfileInfo()->channels,m_file.SoundfileInfo()->format,m_file.SoundfileInfo()->seekable))
-
- if( m_file.SoundfileInfo()->channels <= 2 )
- {
- m_ready = false;
- m_run = true;
- m_softCount=0;
-
- cThread::Start();
- mgDecoder::unlock();
- return true;
- }
- else
- {
- // esyslog("ERROR: cannot play sound file %s: more than 2 channels", m_filename.c_str() );
+bool mgSndfileDecoder::start(void) {
+ mgDecoder::lock(true);
+ init();
+ m_playing=true;
+
+ if( m_file.Open() ) {
+ // d(printf("snd: open rate=%d frames=%lld channels=%d format=0x%x seek=%d\n",
+ // m_file.SoundfileInfo()->samplerate,m_file.SoundfileInfo()->frames,m_file.SoundfileInfo()->channels,m_file.SoundfileInfo()->format,m_file.SoundfileInfo()->seekable))
+
+ if( m_file.SoundfileInfo()->channels <= 2 ) {
+ m_ready = false;
+ m_run = true;
+ m_softCount=0;
+
+ cThread::Start();
+ mgDecoder::unlock();
+ return true;
+ }
+ else {
+ // esyslog("ERROR: cannot play sound file %s: more than 2 channels", m_filename.c_str() );
+ }
}
- }
- mgDecoder::unlock();
- return clean();
+ mgDecoder::unlock();
+ return clean();
}
-bool mgSndfileDecoder::stop(void)
-{
- mgDecoder::lock();
- if( m_playing )
- {
- clean();
- }
- mgDecoder::unlock();
- return true;
+bool mgSndfileDecoder::stop(void) {
+ mgDecoder::lock();
+ if( m_playing ) {
+ clean();
+ }
+ mgDecoder::unlock();
+ return true;
}
-void mgSndfileDecoder::Action(void)
-{
- m_buffMutex.Lock();
- while( m_run )
- {
-
- if( m_ready )
- {
- m_bgCond.Wait(m_buffMutex);
- }
+void mgSndfileDecoder::Action(void) {
+ m_buffMutex.Lock();
+ while( m_run ) {
- if(!m_ready)
- {
- m_buffMutex.Unlock();
- m_deferedN = m_file.Stream( m_framebuff,SF_SAMPLES );
- m_buffMutex.Lock();
- m_ready = true;
- m_fgCond.Broadcast();
+ if( m_ready ) {
+ m_bgCond.Wait(m_buffMutex);
+ }
+
+ if(!m_ready) {
+ m_buffMutex.Unlock();
+ m_deferedN = m_file.Stream( m_framebuff,SF_SAMPLES );
+ m_buffMutex.Lock();
+ m_ready = true;
+ m_fgCond.Broadcast();
+ }
}
- }
- m_buffMutex.Unlock();
+ m_buffMutex.Unlock();
}
-struct mgDecode *mgSndfileDecoder::done(eDecodeStatus status)
-{
- m_ds.status = status;
- m_ds.index = m_index*1000 / m_file.SoundfileInfo()->samplerate;
- m_ds.pcm = m_pcm;
+struct mgDecode *mgSndfileDecoder::done(eDecodeStatus status) {
+ m_ds.status = status;
+ m_ds.index = m_index*1000 / m_file.SoundfileInfo()->samplerate;
+ m_ds.pcm = m_pcm;
- mgDecoder::unlock(); // release the lock from Decode()
-
- return &m_ds;
+ mgDecoder::unlock(); // release the lock from Decode()
+
+ return &m_ds;
}
-struct mgDecode *mgSndfileDecoder::decode()
-{
- mgDecoder::lock(); // this is released in Done()
- if(m_playing)
- {
- cMutexLock lock(&m_buffMutex);
- while(!m_ready)
- {
- if( !m_softCount || !m_fgCond.TimedWait( m_buffMutex, m_softCount*5 ) )
- {
- if( m_softCount < 20 )
- {
- m_softCount++;
+struct mgDecode *mgSndfileDecoder::decode() {
+ mgDecoder::lock(); // this is released in Done()
+ if(m_playing) {
+ cMutexLock lock(&m_buffMutex);
+ while(!m_ready) {
+ if( !m_softCount || !m_fgCond.TimedWait( m_buffMutex, m_softCount*5 ) ) {
+ if( m_softCount < 20 ) {
+ m_softCount++;
+ }
+ return done(dsSoftError);
+ }
}
- return done(dsSoftError);
- }
- }
- m_softCount = 0;
- m_ready = false;
- m_bgCond.Broadcast();
+ m_softCount = 0;
+ m_ready = false;
+ m_bgCond.Broadcast();
+
+ int n = m_deferedN;
+ if( n < 0 ) {
+ return done(dsError);
+ }
+
+ if( n == 0 ) {
+ return done(dsEof);
+ }
+
+ m_pcm->samplerate = m_file.SoundfileInfo()->samplerate;
+ m_pcm->channels = m_file.SoundfileInfo()->channels;
+ m_pcm->length = n;
+ m_index += n;
+
+ int *data = m_framebuff;
+ mad_fixed_t *sam0 = m_pcm->samples[0], *sam1=m_pcm->samples[1];
+
+ // shift value for mad_fixed conversion
+ const int s = (sizeof(int)*8)-1-MAD_F_FRACBITS;
- int n = m_deferedN;
- if( n < 0 )
- {
+ if(m_pcm->channels>1) {
+ for(; n>0 ; n--) {
+ *sam0++=(*data++) >> s;
+ *sam1++=(*data++) >> s;
+ }
+ }
+ else {
+ for(; n>0 ; n--) {
+ *sam0++=(*data++) >> s;
+ }
+ }
+ return done(dsPlay);
+ }
return done(dsError);
- }
-
- if( n == 0 )
- {
- return done(dsEof);
- }
-
- m_pcm->samplerate = m_file.SoundfileInfo()->samplerate;
- m_pcm->channels = m_file.SoundfileInfo()->channels;
- m_pcm->length = n;
- m_index += n;
-
- int *data = m_framebuff;
- mad_fixed_t *sam0 = m_pcm->samples[0], *sam1=m_pcm->samples[1];
-
- const int s = (sizeof(int)*8)-1-MAD_F_FRACBITS; // shift value for mad_fixed conversion
-
- if(m_pcm->channels>1)
- {
- for(; n>0 ; n--)
- {
- *sam0++=(*data++) >> s;
- *sam1++=(*data++) >> s;
- }
- }
- else
- {
- for(; n>0 ; n--)
- {
- *sam0++=(*data++) >> s;
- }
- }
- return done(dsPlay);
- }
- return done(dsError);
}
-bool mgSndfileDecoder::skip( int seconds, int avail, int rate )
-{
- mgDecoder::lock();
- bool res=false;
- float bsecs = (float) avail / (float) ( rate * (16 / 8 * 2) );
-
- if( m_playing && m_file.SoundfileInfo()->seekable )
- {
- float fsecs = (float)seconds - bsecs;
- sf_count_t frames = (sf_count_t)( fsecs*(float)m_file.SoundfileInfo()->samplerate );
- sf_count_t newpos = m_file.Seek( 0, true ) + frames;
-
- if( newpos > m_file.SoundfileInfo()->frames )
- {
- newpos = m_file.SoundfileInfo()->frames-1;
- }
-
- if( newpos < 0 )
- {
- newpos=0;
- }
-
- // d(printf("snd: skip: secs=%d fsecs=%f frames=%lld current=%lld new=%lld\n",Seconds,fsecs,frames,m_file.Seek(0,true),newpos))
-
- m_buffMutex.Lock();
- frames = m_file.Seek( newpos, false );
- m_ready = false;
- m_bgCond.Broadcast();
- m_buffMutex.Unlock();
-
- if( frames >= 0 )
- {
- m_index = frames;
+bool mgSndfileDecoder::skip( int seconds, int avail, int rate ) {
+ mgDecoder::lock();
+ bool res=false;
+ float bsecs = (float) avail / (float) ( rate * (16 / 8 * 2) );
+
+ if( m_playing && m_file.SoundfileInfo()->seekable ) {
+ float fsecs = (float)seconds - bsecs;
+ sf_count_t frames = (sf_count_t)( fsecs*(float)m_file.SoundfileInfo()->samplerate );
+ sf_count_t newpos = m_file.Seek( 0, true ) + frames;
+
+ if( newpos > m_file.SoundfileInfo()->frames ) {
+ newpos = m_file.SoundfileInfo()->frames-1;
+ }
+
+ if( newpos < 0 ) {
+ newpos=0;
+ }
+
+ // d(printf("snd: skip: secs=%d fsecs=%f frames=%lld current=%lld new=%lld\n",Seconds,fsecs,frames,m_file.Seek(0,true),newpos))
+
+ m_buffMutex.Lock();
+ frames = m_file.Seek( newpos, false );
+ m_ready = false;
+ m_bgCond.Broadcast();
+ m_buffMutex.Unlock();
+
+ if( frames >= 0 ) {
+ m_index = frames;
#ifdef DEBUG
- int i = frames/m_file.SoundfileInfo()->samplerate;
- printf("snd: skipping to %02d:%02d (frame %lld)\n",i/60,i%60,frames);
+ int i = frames/m_file.SoundfileInfo()->samplerate;
+ printf("snd: skipping to %02d:%02d (frame %lld)\n",i/60,i%60,frames);
#endif
- res=true;
+ res=true;
+ }
}
- }
- mgDecoder::unlock();
- return res;
+ mgDecoder::unlock();
+ return res;
}
// --- cSndfile ----------------------------------------------------------------
-mgSndfile::mgSndfile( mgItemGd *item )
-{
- m_filename = item->getSourceFile();
- m_sf = 0;
+mgSndfile::mgSndfile( mgItemGd *item ) {
+ m_filename = item->getSourceFile();
+ m_sf = 0;
}
-mgSndfile::~mgSndfile()
-{
- Close();
+mgSndfile::~mgSndfile() {
+ Close();
}
-SF_INFO* mgSndfile::SoundfileInfo()
-{
- return &m_sfi;
+SF_INFO* mgSndfile::SoundfileInfo() {
+ return &m_sfi;
}
-bool mgSndfile::Open(bool log)
-{
- if( m_sf )
- {
- return( Seek() >= 0 );
- }
-
- m_sf = sf_open( m_filename.c_str(), SFM_READ, &m_sfi );
- if( !m_sf && log )
- {
- Error( "open" );
- }
-
- return ( m_sf != 0 );
-}
+bool mgSndfile::Open(bool log) {
+ if( m_sf ) {
+ return( Seek() >= 0 );
+ }
-void mgSndfile::Close(void)
-{
- if( m_sf )
- {
- sf_close( m_sf );
- m_sf = 0;
- }
+ m_sf = sf_open( m_filename.c_str(), SFM_READ, &m_sfi );
+ if( !m_sf && log ) {
+ Error( "open" );
+ }
+
+ return ( m_sf != 0 );
}
-void mgSndfile::Error(const char *action)
-{
- char buff[128];
- sf_error_str( m_sf, buff, sizeof(buff) );
- // esyslog( "ERROR: sndfile %s failed on %s: %s", action, Filename, buff );
+void mgSndfile::Close(void) {
+ if( m_sf ) {
+ sf_close( m_sf );
+ m_sf = 0;
+ }
}
-sf_count_t mgSndfile::Seek( sf_count_t frames, bool relative )
-{
- int dir = SEEK_CUR;
- if( !relative )
- {
- dir = SEEK_SET;
- }
-
- int n = sf_seek( m_sf, frames, dir );
- if( n < 0 )
- {
- Error("seek");
- }
-
- return n;
+void mgSndfile::Error(const char *action) {
+ char buff[128];
+ sf_error_str( m_sf, buff, sizeof(buff) );
+ // esyslog( "ERROR: sndfile %s failed on %s: %s", action, Filename, buff );
}
-sf_count_t mgSndfile::Stream( int *buffer, sf_count_t frames )
-{
- sf_count_t n = sf_readf_int( m_sf, buffer, frames );
- if( n < 0 )
- {
- Error("read");
- }
- return n;
+sf_count_t mgSndfile::Seek( sf_count_t frames, bool relative ) {
+ int dir = SEEK_CUR;
+ if( !relative ) {
+ dir = SEEK_SET;
+ }
+
+ int n = sf_seek( m_sf, frames, dir );
+ if( n < 0 ) {
+ Error("seek");
+ }
+
+ return n;
}
-#endif //HAVE_SNDFILE
+sf_count_t mgSndfile::Stream( int *buffer, sf_count_t frames ) {
+ sf_count_t n = sf_readf_int( m_sf, buffer, frames );
+ if( n < 0 ) {
+ Error("read");
+ }
+ return n;
+}
+#endif //HAVE_SNDFILE
diff --git a/vdr_decoder_sndfile.h b/vdr_decoder_sndfile.h
index a9045d0..fc02076 100644
--- a/vdr_decoder_sndfile.h
+++ b/vdr_decoder_sndfile.h
@@ -38,67 +38,66 @@
#include "vdr_decoder.h"
-#define CDFS_MAGIC 0xCDDA // cdfs filesystem-ID
+#define CDFS_MAGIC 0xCDDA // cdfs filesystem-ID
// ----------------------------------------------------------------
class mgSndfile
{
-private:
- SNDFILE *m_sf;
- std::string m_filename;
+ private:
+ SNDFILE *m_sf;
+ std::string m_filename;
- void Error(const char *action);
+ void Error(const char *action);
- SF_INFO m_sfi;
-public:
+ SF_INFO m_sfi;
+ public:
- mgSndfile( mgItemGd *item );
- ~mgSndfile();
+ mgSndfile( mgItemGd *item );
+ ~mgSndfile();
- SF_INFO* SoundfileInfo();
- bool Open( bool log = true );
- void Close();
- sf_count_t Seek( sf_count_t frames = 0, bool relative = false );
- sf_count_t Stream( int *buffer, sf_count_t frames );
+ SF_INFO* SoundfileInfo();
+ bool Open( bool log = true );
+ void Close();
+ sf_count_t Seek( sf_count_t frames = 0, bool relative = false );
+ sf_count_t Stream( int *buffer, sf_count_t frames );
};
// ----------------------------------------------------------------
-class mgSndfileDecoder : public mgDecoder, public cThread
+class mgSndfileDecoder : public mgDecoder, public cThread
{
-private:
- mgSndfile m_file;
-
- struct mgDecode m_ds;
- struct mad_pcm *m_pcm;
- unsigned long long m_index;
- //
- cMutex m_buffMutex;
- cCondVar m_fgCond, m_bgCond;
- bool m_run, m_ready;
- int *m_framebuff, m_deferedN, m_softCount;
- //
- void init();
- bool clean();
- struct mgDecode *done( eDecodeStatus status );
-
-protected:
- virtual void Action(void);
-
-public:
- mgSndfileDecoder( mgItemGd *item );
- ~mgSndfileDecoder();
-
- virtual bool valid(void);
- virtual bool start(void);
- virtual bool stop(void);
- virtual bool skip(int seconds, int avail, int rate);
- virtual struct mgDecode *decode();
- virtual mgPlayInfo *playInfo();
+ private:
+ mgSndfile m_file;
+
+ struct mgDecode m_ds;
+ struct mad_pcm *m_pcm;
+ unsigned long long m_index;
+ //
+ cMutex m_buffMutex;
+ cCondVar m_fgCond, m_bgCond;
+ bool m_run, m_ready;
+ int *m_framebuff, m_deferedN, m_softCount;
+ //
+ void init();
+ bool clean();
+ struct mgDecode *done( eDecodeStatus status );
+
+ protected:
+ virtual void Action(void);
+
+ public:
+ mgSndfileDecoder( mgItemGd *item );
+ ~mgSndfileDecoder();
+
+ virtual bool valid(void);
+ virtual bool start(void);
+ virtual bool stop(void);
+ virtual bool skip(int seconds, int avail, int rate);
+ virtual struct mgDecode *decode();
+ virtual mgPlayInfo *playInfo();
};
// ----------------------------------------------------------------
-
-#endif //HAVE_SNDFILE
-#endif //___DECODER_SND_H
+#endif //HAVE_SNDFILE
+#endif //___DECODER_SND_H
diff --git a/vdr_menu.c b/vdr_menu.c
index aaa7b9f..be8498e 100644
--- a/vdr_menu.c
+++ b/vdr_menu.c
@@ -21,6 +21,7 @@
#include <vdr/tools.h>
#include <vdr/config.h>
#include <vdr/plugin.h>
+#include <vdr/i18n.h>
#if VDRVERSNUM >= 10307
#include <vdr/interface.h>
@@ -32,32 +33,21 @@
#include "vdr_player.h"
#include "mg_incremental_search.h"
#include "mg_thread_sync.h"
-#include "i18n.h"
#include "mg_tools.h"
#include "mg_sel_gd.h"
-void
-mgStatus::OsdCurrentItem(const char* Text)
-{
- cOsdItem* i = main->Get(main->Current());
- if (!i) return;
- mgAction * a = dynamic_cast<mgAction *>(i);
- if (!a)
- mgError("mgStatus::OsdCurrentItem expected an mgAction*");
- if (a)
- a->TryNotify();
-}
-
-void Play(mgSelection *sel, bool enter)
-{
+void Play(mgSelection *sel, bool enter) {
+ char *b;
+ msprintf(&b,"Play, enter=%d",enter);
+ sel->ShowState(b);
+ free(b);
mgSelection *s = GenerateSelection(sel);
if (s->ordersize()==0)
s->InitDefaultOrder(1);
if (enter)
s->enter();
- if (!s->skipItems(0)) // no valid item exists
- {
+ if (!s->skipItems(0)) { // no valid item exists
delete s;
return;
}
@@ -69,15 +59,13 @@ void Play(mgSelection *sel, bool enter)
}
mgSelection*
-GenerateSelection(const mgSelection* s)
-{
+GenerateSelection(const mgSelection* s) {
return new mgSelectionGd(s);
}
//! \brief queue the selection for playing, abort ongoing instant play
void
-mgMainMenu::PlayQueue()
-{
+mgSelOsd::PlayQueue() {
queue_playing=true;
instant_playing=false;
Play(playselection());
@@ -85,19 +73,16 @@ mgMainMenu::PlayQueue()
//! \brief queue the selection for playing, abort ongoing queue playing
void
-mgMainMenu::PlayInstant(bool enter)
-{
+mgSelOsd::PlayInstant(bool enter) {
instant_playing=true;
Play(selection(),enter);
}
bool
-mgMainMenu::SwitchSelection()
-{
+mgSelOsd::SwitchSelection() {
UseNormalSelection();
mgSelection* newsel = getSelection(Current());
- if (newsel->ordersize()>0)
- {
+ if (newsel->ordersize()>0) {
newsel->CopyKeyValues(selection());
newsel->Activate();
m_current_selection = Current();
@@ -105,146 +90,111 @@ mgMainMenu::SwitchSelection()
SaveState();
return true;
}
- else
- {
+ else {
Message1(tr("Order is undefined"));
return false;
}
}
-void mgMainMenu::setSelection(unsigned int idx,mgSelection *s)
-{
+void mgSelOsd::setSelection(unsigned int idx,mgSelection *s) {
if (idx>=selections.size())
- mgError("mgMainMenu::getSelection(%u): selections.size() is %d",
- idx,selections.size());
+ mgError("mgSelOsd::getSelection(%u): selections.size() is %d",
+ idx,selections.size());
delete selections[idx];
selections[idx] = s;
}
-mgSelection* mgMainMenu::getSelection(unsigned int idx)
-{
+mgSelection* mgSelOsd::getSelection(unsigned int idx) {
if (idx>=selections.size())
- mgError("mgMainMenu::getSelection(%u): selections.size() is %d",
- idx,selections.size());
+ mgError("mgSelOsd::getSelection(%u): selections.size() is %d",
+ idx,selections.size());
return selections[idx];
}
void
-mgMainMenu::CollectionChanged(string name,bool added)
-{
- delete moveselection;
- moveselection = NULL;
- forcerefresh = true; // TODO brauchen wir das?
- if (name == play_collection)
- {
- playselection()->clearCache();
- mgPlayerControl *c = PlayerControl();
- if (c)
- c->ReloadPlaylist();
- else if (added)
- PlayQueue();
- }
- if (CollectionEntered(name) || selection()->isCollectionlist())
- selection()->clearCache();
+mgSelOsd::CollectionChanged(string name,bool added) {
+ delete moveselection;
+ moveselection = NULL;
+ forcerefresh = true; // TODO brauchen wir das?
+ if (name == play_collection) {
+ playselection()->clearCache();
+ mgPlayerControl *c = PlayerControl();
+ if (c)
+ c->ReloadPlaylist();
+ else if (added)
+ PlayQueue();
+ }
+ if (CollectionEntered(name) || selection()->isCollectionlist())
+ selection()->clearCache();
}
bool
-mgMainMenu::ShowingCollections()
-{
- return (UsingCollection && selection ()->orderlevel () == 0);
+mgSelOsd::ShowingCollections() {
+ return (UsingCollection && selection ()->orderlevel () == 0);
}
-
bool
-mgMainMenu::DefaultCollectionSelected()
-{
- string this_sel = trim(selection ()->getCurrentValue());
- return (ShowingCollections () && this_sel == default_collection);
+mgSelOsd::DefaultCollectionSelected() {
+ string this_sel = trim(selection ()->getCurrentValue());
+ return (ShowingCollections () && this_sel == default_collection);
}
bool
-mgMainMenu::CollectionEntered(string name)
-{
- if (!UsingCollection) return false;
- if (selection()->orderlevel()==0) return false;
- return trim(selection ()->getKeyItem(0)->value()) == name;
+mgSelOsd::CollectionEntered(string name) {
+ if (!UsingCollection) return false;
+ if (selection()->orderlevel()==0) return false;
+ return trim(selection ()->getKeyItem(0)->value()) == name;
}
-
-mgMenu *
-mgMainMenu::Parent ()
-{
- if (Menus.size () < 2)
- return NULL;
- return Menus[Menus.size () - 2];
+mgSelMenu::mgSelMenu () {
+ CollRedAction = actNone;
+ CollGreenAction = actNone;
+ CollYellowAction = actNone;
+ CollBlueAction = actNone;
}
-
-mgAction*
-mgMenu::GenerateAction(const mgActions action,mgActions on)
-{
- mgAction *result = actGenerate(action);
- if (result)
- {
- result->SetMenu(this);
- if (!result->Enabled(on))
- {
- delete result;
- result=NULL;
- }
+void
+mgSelMenu::SetHotkeyAction(eKeys key,mgActions action) {
+ switch (key) {
+ case kRed:
+ if (Selosd()->UsingCollection)
+ CollRedAction = action;
+ else
+ RedAction = action;
+ break;
+ case kGreen:
+ if (Selosd()->UsingCollection)
+ CollGreenAction = action;
+ else
+ GreenAction = action;
+ break;
+ case kYellow:
+ if (Selosd()->UsingCollection)
+ CollYellowAction = action;
+ else
+ YellowAction = action;
+ default:
+ break;
}
- return result;
-}
-
-eOSState
-mgMenu::ExecuteAction(const mgActions action,mgActions on)
-{
- mgAction *a = GenerateAction (action,on);
- if (a)
- {
- a->Execute ();
- delete a;
- return osContinue;
- }
- return osUnknown;
}
mgPlayerControl *
-PlayerControl ()
-{
- mgPlayerControl *result = NULL;
- cControl *control = cControl::Control ();
- if (control && typeid (*control) == typeid (mgPlayerControl))
-// is there a running MP3 player?
- result = static_cast < mgPlayerControl * >(control);
- return result;
-}
-
-mgMenu::mgMenu ()
-{
- m_osd = NULL;
- m_parent_index=-1;
- TreeRedAction = actNone;
- TreeGreenAction = actNone;
- TreeYellowAction = actNone;
- TreeBlueAction = actNone;
- CollRedAction = actNone;
- CollGreenAction = actNone;
- CollYellowAction = actNone;
- CollBlueAction = actNone;
+PlayerControl () {
+ mgPlayerControl *result = NULL;
+ cControl *control = cControl::Control ();
+ if (control && typeid (*control) == typeid (mgPlayerControl))
+ // is there a running MP3 player?
+ result = static_cast < mgPlayerControl * >(control);
+ return result;
}
-
-// ----------------------- mgMainMenu ----------------------
-
+// ----------------------- mgSelOsd ----------------------
void
-mgMainMenu::DumpSelections(mgValmap& nv)
-{
- for (unsigned int idx=0;idx<selections.size();idx++)
- {
+mgSelOsd::DumpSelections(mgValmap& nv) {
+ for (unsigned int idx=0;idx<selections.size();idx++) {
mgSelection *s = selections[idx];
- if (!s)
+ if (!s)
mgError("DumpSelections:selection[%u] is 0",idx);
char prefix[20];
sprintf(prefix,"order%u",idx);
@@ -253,756 +203,499 @@ mgMainMenu::DumpSelections(mgValmap& nv)
}
void
-mgMainMenu::SaveState()
-{
- char *oldfile;
- char *newfile;
- char *statefile;
- mgValmap nmain("MainMenu");
- mgValmap nsel("tree");
- mgValmap ncol("collection");
- msprintf(&oldfile,"%s/muggle.state.old",cPlugin::ConfigDirectory ("muggle"));
- msprintf(&newfile,"%s/muggle.state.new",cPlugin::ConfigDirectory ("muggle"));
- msprintf(&statefile,"%s/muggle.state",cPlugin::ConfigDirectory ("muggle"));
- FILE *f = fopen(newfile,"w");
- if (!f)
- {
- if (!m_save_warned)
- mgWarning("Cannot write %s",newfile);
- m_save_warned=true;
- goto err_exit;
- }
- nmain.put(default_collection,"DefaultCollection");
- nmain.put(UsingCollection,"UsingCollection");
- nmain.put(int(Menus.front()->TreeRedAction),"TreeRedAction");
- nmain.put(int(Menus.front()->TreeGreenAction),"TreeGreenAction");
- nmain.put(int(Menus.front()->TreeYellowAction),"TreeYellowAction");
- nmain.put(int(Menus.front()->CollRedAction),"CollRedAction");
- nmain.put(int(Menus.front()->CollGreenAction),"CollGreenAction");
- nmain.put(int(Menus.front()->CollYellowAction),"CollYellowAction");
- nsel.put(m_current_selection,"CurrentSelection");
- DumpSelections(nsel);
- m_collectionsel->DumpState(ncol,"collection");
- nmain.Write(f);
- nsel.Write(f);
- ncol.Write(f);
- fclose(f);
- rename(statefile,oldfile);
- rename(newfile,statefile);
-err_exit:
- free(oldfile);
- free(newfile);
- free(statefile);
-}
-
-mgMainMenu::mgMainMenu ():cOsdMenu ("",25)
-{
- m_Status = new mgStatus(this);
- m_message = 0;
- moveselection = 0;
- m_root = 0;
- external_commands = 0;
- queue_playing=false;
- instant_playing=false;
- m_save_warned=false;
- play_collection = tr("play");
- mgValmap nsel("tree");
- mgValmap ncol("collection");
- mgValmap nmain("MainMenu");
-
- // define defaults for values missing in state file:
- nsel.put(true,"FallThrough");
- nmain.put(play_collection,"DefaultCollection");
- nmain.put(false,"UsingCollection");
- nmain.put(int(actAddThisToCollection),"TreeRedAction");
- nmain.put(int(actInstantPlay),"TreeGreenAction");
- nmain.put(int(actToggleSelection),"TreeYellowAction");
- nmain.put(int(actAddThisToCollection),"CollRedAction");
- nmain.put(int(actInstantPlay),"CollGreenAction");
- nmain.put(int(actToggleSelection),"CollYellowAction");
- nmain.put(0,"CurrentOrder");
-
-
- // load values from state file
- char *b;
- msprintf(&b,"%s/muggle.state",cPlugin::ConfigDirectory ("muggle"));
- FILE *f = fopen(b,"r");
- free(b);
- if (f) {
- nsel.Read(f);
- ncol.Read(f);
- nmain.Read(f);
- fclose(f);
- }
-
- // get values from mgValmaps
- InitMapFromSetup(nsel);
- InitMapFromSetup(ncol);
- LoadSelections(nsel);
- default_collection = nmain.getstr("DefaultCollection");
- UsingCollection = nmain.getbool("UsingCollection");
- selections[m_current_selection]->CreateCollection(default_collection);
- if (default_collection!=play_collection)
- selections[m_current_selection]->CreateCollection(play_collection);
- m_collectionsel = GenerateSelection();
- m_collectionsel->InitFrom ("order0",ncol);
- m_collectionsel->MakeCollection();
- m_playsel = GenerateSelection();
- m_playsel->InitFrom("order0",ncol);
- m_playsel->MakeCollection();
- // initialize
- if (m_playsel->orderlevel()!=1)
- {
- m_playsel->leave_all();
- m_playsel->enter(play_collection);
- }
- mgSelection *s = selections[m_current_selection];
- s->CopyKeyValues(s);
- s->Activate();
- unsigned int posi = selection()->gotoPosition();
- LoadExternalCommands(); // before AddMenu()
- m_root = new mgTree;
- m_root->TreeRedAction = mgActions(nmain.getuint("TreeRedAction"));
- m_root->TreeGreenAction = mgActions(nmain.getuint("TreeGreenAction"));
- m_root->TreeYellowAction = mgActions(nmain.getuint("TreeYellowAction"));
- m_root->CollRedAction = mgActions(nmain.getuint("CollRedAction"));
- m_root->CollGreenAction = mgActions(nmain.getuint("CollGreenAction"));
- m_root->CollYellowAction = mgActions(nmain.getuint("CollYellowAction"));
- AddMenu (m_root,posi);
- forcerefresh = false;
-}
+mgSelOsd::SaveState() {
+ char *oldfile;
+ char *newfile;
+ char *statefile;
+ mgSelMenu *mfront = dynamic_cast<mgSelMenu *>(Menus.front());
+ mgValmap nmain("MainMenu");
+ mgValmap nsel("tree");
+ mgValmap ncol("collection");
+ msprintf(&oldfile,"%s/muggle.state.old",the_setup.ConfigDirectory.c_str());
+ msprintf(&newfile,"%s/muggle.state.new",the_setup.ConfigDirectory.c_str());
+ msprintf(&statefile,"%s/muggle.state",the_setup.ConfigDirectory.c_str());
+ FILE *f = fopen(newfile,"w");
+ if (!f) {
+ if (!m_save_warned)
+ mgWarning("Cannot write %s",newfile);
+ m_save_warned=true;
+ goto err_exit;
+ }
+ nmain.put(default_collection,"DefaultCollection");
+ nmain.put(UsingCollection,"UsingCollection");
+ nmain.put(int(mfront->RedAction),"RedAction");
+ nmain.put(int(mfront->GreenAction),"GreenAction");
+ nmain.put(int(mfront->YellowAction),"YellowAction");
+ nmain.put(int(mfront->CollRedAction),"CollRedAction");
+ nmain.put(int(mfront->CollGreenAction),"CollGreenAction");
+ nmain.put(int(mfront->CollYellowAction),"CollYellowAction");
+ nsel.put(m_current_selection,"CurrentSelection");
+ DumpSelections(nsel);
+ m_collectionsel->DumpState(ncol,"collection");
+ nmain.Write(f);
+ nsel.Write(f);
+ ncol.Write(f);
+ fclose(f);
+ rename(statefile,oldfile);
+ rename(newfile,statefile);
+ err_exit:
+ free(oldfile);
+ free(newfile);
+ free(statefile);
+}
+
+mgSelOsd::mgSelOsd () {
+ mgDebug("mgSelOsd starts");
+ moveselection = 0;
+ queue_playing=false;
+ instant_playing=false;
+ play_collection = tr("play");
+ mgValmap nsel("tree");
+ mgValmap ncol("collection");
+ mgValmap nmain("MainMenu");
+
+ // define defaults for values missing in state file:
+ nsel.put(true,"FallThrough");
+ nmain.put(play_collection,"DefaultCollection");
+ nmain.put(false,"UsingCollection");
+ nmain.put(int(actAddThisToCollection),"RedAction");
+ nmain.put(int(actInstantPlay),"GreenAction");
+ nmain.put(int(actToggleSelection),"YellowAction");
+ nmain.put(int(actAddThisToCollection),"CollRedAction");
+ nmain.put(int(actInstantPlay),"CollGreenAction");
+ nmain.put(int(actToggleSelection),"CollYellowAction");
+ nmain.put(0,"CurrentOrder");
+
+ // load values from state file
+ char *b;
+ msprintf(&b,"%s/muggle.state",the_setup.ConfigDirectory.c_str());
+ FILE *f = fopen(b,"r");
+ free(b);
+ if (f) {
+ nsel.Read(f);
+ ncol.Read(f);
+ nmain.Read(f);
+ fclose(f);
+ }
-void
-mgMainMenu::AddSelection()
-{
+ // get values from mgValmaps
+ InitMapFromSetup(nsel);
+ InitMapFromSetup(ncol);
+ LoadSelections(nsel);
+ default_collection = nmain.getstr("DefaultCollection");
+ UsingCollection = nmain.getbool("UsingCollection");
+ selections[m_current_selection]->CreateCollection(default_collection);
+ if (default_collection!=play_collection)
+ selections[m_current_selection]->CreateCollection(play_collection);
+ m_collectionsel = GenerateSelection();
+ m_collectionsel->InitFrom ("order0",ncol);
+ m_collectionsel->MakeCollection();
+ m_playsel = GenerateSelection();
+ m_playsel->InitFrom("order0",ncol);
+ m_playsel->MakeCollection();
+ // initialize
+ if (m_playsel->orderlevel()!=1) {
+ m_playsel->leave_all();
+ m_playsel->enter(play_collection);
+ }
+ mgSelection *s = selections[m_current_selection];
+ s->Activate();
+ unsigned int posi = selection()->gotoPosition();
+ LoadExternalCommands(); // before AddMenu()
+ mgSelMenu* m_selroot = new mgTree;
+ m_root = m_selroot;
+ m_root->RedAction = mgActions(nmain.getuint("RedAction"));
+ m_root->GreenAction = mgActions(nmain.getuint("GreenAction"));
+ m_root->YellowAction = mgActions(nmain.getuint("YellowAction"));
+ m_selroot->CollRedAction = mgActions(nmain.getuint("CollRedAction"));
+ m_selroot->CollGreenAction = mgActions(nmain.getuint("CollGreenAction"));
+ m_selroot->CollYellowAction = mgActions(nmain.getuint("CollYellowAction"));
+ AddMenu (m_root,posi);
+ forcerefresh = false;
+}
+
+void
+mgSelOsd::AddSelection() {
selections.push_back(GenerateSelection());
newposition = selections.size()-1;
}
void
-mgMainMenu::DeleteSelection()
-{
+mgSelOsd::DeleteSelection() {
mgSelection *o = selections[Current()];
delete o;
selections.erase(selections.begin()+Current());
}
void
-mgMainMenu::LoadSelections(mgValmap& nv)
-{
- for (unsigned int idx=0;idx<1000;idx++)
- {
+mgSelOsd::LoadSelections(mgValmap& nv) {
+ for (unsigned int idx=0;idx<1000;idx++) {
char prefix[10];
sprintf(prefix,"order%u",idx);
mgSelection *s = GenerateSelection();
s->InitFrom(prefix,nv);
if (s->ordersize())
selections.push_back(s);
- else
- {
+ else {
delete s;
break;
}
}
- if (selections.size()==0)
- {
- for (unsigned int i=1; i<100;i++)
- {
+ if (selections.size()==0) {
+ for (unsigned int i=1; i<100;i++) {
mgSelection* s=GenerateSelection();
if (s->InitDefaultOrder(i))
selections.push_back(s);
- else
- {
+ else {
delete s;
break;
}
}
}
- m_current_selection = nv.getuint("CurrentSelection");
+ m_current_selection = nv.getuint("CurrentSelection");
if (m_current_selection >= selections.size())
m_current_selection=0;
}
-void
-mgMainMenu::LoadExternalCommands()
-{
-// Read commands for collections in etc. /video/muggle/playlist_commands.conf
- external_commands = new cCommands ();
-
-#if VDRVERSNUM >= 10318
- cString cmd_file = AddDirectory (cPlugin::ConfigDirectory ("muggle"),
- "playlist_commands.conf");
- mgDebug (1, "mgMuggle::Start: %d Looking for file %s",VDRVERSNUM, *cmd_file);
- bool have_cmd_file = external_commands->Load (*cmd_file);
-#else
- const char *
- cmd_file = (const char *) AddDirectory (cPlugin::ConfigDirectory ("muggle"),
- "playlist_commands.conf");
- mgDebug (1, "mgMuggle::Start: %d Looking for file %s",VDRVERSNUM, cmd_file);
- bool have_cmd_file = external_commands->Load ((const char *) cmd_file);
-#endif
-
- if (!have_cmd_file)
- {
- delete external_commands;
- external_commands = NULL;
- }
-}
-
-mgMainMenu::~mgMainMenu()
-{
+mgSelOsd::~mgSelOsd() {
+ mgDebug("mgSelOsd terminates");
delete m_collectionsel;
delete m_playsel;
- delete m_Status;
delete moveselection;
- delete m_root;
- delete external_commands;
for (unsigned int i=0;i<selections.size();i++)
delete selections[i];
}
void
-mgMainMenu::InitMapFromSetup (mgValmap& nv)
-{
- // values from setup override saved values
- nv["Directory"] = cPlugin::ConfigDirectory ("muggle");
-}
-
-void
-mgMenu::AddAction (const mgActions action, mgActions on,const bool hotkey)
-{
- mgAction *a = GenerateAction(action,on);
- if (!a) return;
- const char *mn = a->MenuName();
- if (strlen(mn)==0)
- mgError("AddAction(%d):MenuName is empty",int(action));
- if (hotkey)
- a->SetText(osd()->hk(mn));
- else
- a->SetText(mn);
- free(const_cast<char*>(mn));
- osd()->AddItem(a);
-}
-
-
-void
-mgMenu::AddExternalAction(const mgActions action, const char *title)
-{
- mgAction *a = GenerateAction(action,actNone);
- if (!a) return;
- a->SetText(osd()->hk(title));
- osd()->AddItem(a);
-}
-
-void
-mgMainMenu::AddOrderActions(mgMenu* m)
-{
- for (unsigned int idx=0;idx<selections.size();idx++)
- {
- mgSelection *o = selections[idx];
- if (!o)
- mgError("AddOrderAction:selections[%u] is 0",idx);
- mgAction *a = m->GenerateAction(actOrder,actNone);
- assert(a);
- string name = o->Name(); // do not combine these 2 lines!
- const char *oname = name.c_str();
- if (strlen(oname)==0)
- oname = tr("Order is undefined");
- a->SetText(hk(oname));
- AddItem(a);
- }
+mgSelOsd::InitMapFromSetup (mgValmap& nv) {
+ // values from setup override saved values
+ nv["Directory"] = the_setup.ConfigDirectory;
}
void
-mgMenu::AddSelectionItems (mgSelection *sel,mgActions act)
-{
- sel->Activate();
- sel->listitems.refresh();
- for (unsigned int i = 0; i < sel->listitems.size (); i++)
- {
- mgAction *a = GenerateAction(act, actEntry);
- if (!a) continue;
- const char *name = a->MenuName(i+1,sel->listitems[i]);
- a->SetText(name,false);
- a->setHandle(i);
- osd()->AddItem(a);
- }
- if (osd()->ShowingCollections ())
- {
- mgAction *a = GenerateAction(actCreateCollection,actNone);
- if (a)
- {
- a->SetText(a->MenuName(),false);
- osd()->AddItem(a);
+mgSelOsd::AddOrderActions(mgSelMenu* m) {
+ for (unsigned int idx=0;idx<selections.size();idx++) {
+ mgSelection *o = selections[idx];
+ if (!o)
+ mgError("AddOrderAction:selections[%u] is 0",idx);
+ mgAction *a = m->GenerateAction(actOrder,actNone);
+ assert(a);
+ string name = o->Name(); // do not combine these 2 lines!
+ const char *oname = name.c_str();
+ if (strlen(oname)==0)
+ oname = tr("Order is undefined");
+ a->SetText(hk(oname));
+ AddItem(a);
}
- }
-}
-
-
-const char*
-mgMenu::HKey(const mgActions act,mgActions on)
-{
- const char* result = NULL;
- mgAction *a = GenerateAction(act,on);
- if (a)
- {
- result = a->ButtonName();
- delete a;
- }
- return result;
-}
-
-void
-mgMenu::SetHelpKeys(mgActions on)
-{
- mgActions r,g,y,b;
- if (osd()->UsingCollection)
- {
- r = CollRedAction;
- g = CollGreenAction;
- y = CollYellowAction;
- b = CollBlueAction;
- }
- else
- {
- r = TreeRedAction;
- g = TreeGreenAction;
- y = TreeYellowAction;
- b = TreeBlueAction;
- }
- osd()->SetHelpKeys(HKey(r,on),
- HKey(g,on),
- HKey(y,on),
- HKey(b,on));
-}
-
-
-void
-mgMainMenu::RefreshTitle()
-{
- SetTitle(Menus.back()->Title().c_str());
- Display ();
}
void
-mgMenu::InitOsd (const bool hashotkeys)
-{
- osd ()->InitOsd (Title(),hashotkeys);
- SetHelpKeys(); // Default, will be overridden by the single items
-}
-
-
-void
-mgMainMenu::InitOsd (string title,const bool hashotkeys)
-{
- Clear ();
- SetTitle (title.c_str());
- if (hashotkeys) SetHasHotkeys ();
-}
-
-void
-mgMainMenu::AddItem(mgAction *a)
-{
- cOsdItem *c = dynamic_cast<cOsdItem*>(a);
- if (!c)
- mgError("AddItem with non cOsdItem");
- Add(c);
+mgSelMenu::AddSelectionItems (mgSelection *sel,mgActions act) {
+ sel->Activate();
+ sel->listitems.refresh();
+ for (unsigned int i = 0; i < sel->listitems.size (); i++) {
+ mgAction *a = GenerateAction(act, actEntry);
+ if (!a) continue;
+ const char *name = a->MenuName(i+1,sel->listitems[i]);
+ a->SetText(name,false);
+ a->setHandle(i);
+ osd()->AddItem(a);
+ }
+ if (Selosd()->ShowingCollections ()) {
+ mgAction *a = GenerateAction(actCreateCollection,actNone);
+ if (a) {
+ a->SetText(a->MenuName(),false);
+ osd()->AddItem(a);
+ }
+ }
}
string
mgSubmenu::Title() const
{
- static char b[100];
- snprintf(b,99,tr("Commands:%s"),trim(osd()->selection()->getCurrentValue()).c_str());
- return b;
+ static char b[100];
+ snprintf(b,99,tr("Commands:%s"),trim(Selosd()->selection()->getCurrentValue()).c_str());
+ return b;
}
void
-mgSubmenu::BuildOsd ()
-{
- mgActions on = osd()->CurrentType();
- InitOsd ();
- if (!osd ()->Parent ())
- return;
- AddAction(actInstantPlay,on);
- AddAction(actAddThisToCollection,on);
- AddAction(actAddThisToDefaultCollection,on);
- AddAction(actSetDefaultCollection,on);
- AddAction(actRemoveThisFromCollection,on);
- AddAction(actToggleSelection,on);
- AddAction(actDeleteCollection,on);
- AddAction(actClearCollection,on);
- AddAction(actChooseOrder,on);
- AddAction(actExportItemlist,on);
- cCommand *command;
- if (osd()->external_commands)
- {
- int idx=0;
- while ((command = osd ()->external_commands->Get (idx)) != NULL)
- {
- if (idx>actExternalHigh-actExternal0)
- {
- mgWarning("Too many external commands");
- break;
+mgSubmenu::BuildOsd () {
+ mgActions on = osd()->CurrentType();
+ InitOsd ();
+ if (!osd ()->Parent ())
+ return;
+ AddAction(actInstantPlay,on);
+ AddAction(actAddThisToCollection,on);
+ AddAction(actAddThisToDefaultCollection,on);
+ AddAction(actSetDefaultCollection,on);
+ AddAction(actRemoveThisFromCollection,on);
+ AddAction(actToggleSelection,on);
+ AddAction(actDeleteCollection,on);
+ AddAction(actClearCollection,on);
+ AddAction(actChooseOrder,on);
+ AddAction(actExportItemlist,on);
+ cCommand *command;
+ if (osd()->external_commands) {
+ int idx=0;
+ while ((command = osd ()->external_commands->Get (idx)) != NULL) {
+ if (idx>actExternalHigh-actExternal0) {
+ mgWarning("Too many external commands");
+ break;
+ }
+ AddExternalAction (mgActions(idx+int(actExternal0)),command->Title());
+ idx++;
}
- AddExternalAction (mgActions(idx+int(actExternal0)),command->Title());
- idx++;
}
- }
- TreeRedAction = actSetButton;
- TreeGreenAction = actSetButton;
- TreeYellowAction = actSetButton;
- CollRedAction = actSetButton;
- CollGreenAction = actSetButton;
- CollYellowAction = actSetButton;
+ RedAction = actSetButton;
+ GreenAction = actSetButton;
+ YellowAction = actSetButton;
+ CollRedAction = actSetButton;
+ CollGreenAction = actSetButton;
+ CollYellowAction = actSetButton;
}
mgActions
-mgMainMenu::CurrentType()
-{
- mgActions result = actNone;
- cOsdItem* c = Get(Current());
- if (c)
- {
- mgAction *a = dynamic_cast<mgAction*>(c);
- if (!a)
- mgError("Found an OSD item which is not mgAction:%s",c->Text());
- result = a->Type();
- }
- return result;
-}
-
-eOSState
-mgMenu::ExecuteButton(eKeys key)
-{
- mgActions on = osd()->CurrentType();
- mgActions action = actNone;
- if (osd()->UsingCollection)
- switch (key)
- {
- case kRed: action = CollRedAction; break;
- case kGreen: action = CollGreenAction; break;
- case kYellow: action = CollYellowAction; break;
- case kBlue: action = CollBlueAction; break;
- default: break;
- }
- else
- switch (key)
- {
- case kRed: action = TreeRedAction; break;
- case kGreen: action = TreeGreenAction; break;
- case kYellow: action = TreeYellowAction; break;
- case kBlue: action = TreeBlueAction; break;
- default: break;
- }
- return ExecuteAction(action,on);
+mgSelMenu::ButtonAction(eKeys key) {
+ mgActions result = actNone;
+ if (Selosd()->UsingCollection)
+ switch (key) {
+ case kRed: result = CollRedAction; break;
+ case kGreen: result = CollGreenAction; break;
+ case kYellow: result = CollYellowAction; break;
+ case kBlue: result = CollBlueAction; break;
+ default: break;
+ }
+ else
+ switch (key) {
+ case kRed: result = RedAction; break;
+ case kGreen: result = GreenAction; break;
+ case kYellow: result = YellowAction; break;
+ case kBlue: result = BlueAction; break;
+ default: break;
+ }
+ return result;
}
-mgTree::mgTree()
-{
- TreeBlueAction = actShowCommands;
- CollBlueAction = actShowCommands;
- m_incsearch = NULL;
- m_start_position = 0;
+mgTree::mgTree() {
+ BlueAction = actShowCommands;
+ CollBlueAction = actShowCommands;
+ m_incsearch = NULL;
+ m_start_position = 0;
}
eOSState
-mgMenu::Process (eKeys key)
-{
- return ExecuteButton(key);
+mgSelMenu::Process (eKeys key) {
+ return ExecuteButton(key);
}
void
-mgTree::UpdateSearchPosition()
-{
- if( !m_incsearch || m_filter.empty() )
- osd()->newposition = m_start_position;
- else
- osd()->newposition = osd()->selection()->searchPosition(m_filter);
+mgTree::UpdateSearchPosition() {
+ if( !m_incsearch || m_filter.empty() )
+ osd()->newposition = m_start_position;
+ else
+ osd()->newposition = Selosd()->selection()->searchPosition(m_filter);
}
bool
-mgTree::UpdateIncrementalSearch( eKeys key )
-{
- bool result; // false if no search active and keystroke was not used
-
- if( !m_incsearch )
- {
- switch( key )
- {
- case k0...k9:
- { // create a new search object as this is the first keystroke
- m_incsearch = new mgIncrementalSearch();
-
- // remember the position where we started to search
- m_start_position = osd()->Current();
-
- // interprete this keystroke
- m_filter = m_incsearch->KeyStroke( key - k0 );
- result = true;
- UpdateSearchPosition();
- } break;
- default:
- {
- result = false;
- }
+mgTree::UpdateIncrementalSearch( eKeys key ) {
+ bool result; // false if no search active and keystroke was not used
+
+ if( !m_incsearch ) {
+ switch( key ) {
+ case k0...k9:
+ { // create a new search object as this is the first keystroke
+ m_incsearch = new mgIncrementalSearch();
+
+ // remember the position where we started to search
+ m_start_position = osd()->Current();
+
+ // interprete this keystroke
+ m_filter = m_incsearch->KeyStroke( key - k0 );
+ result = true;
+ UpdateSearchPosition();
+ } break;
+ default:
+ {
+ result = false;
+ }
+ }
}
- }
- else
- { // an incremental search is already active
- switch( key )
- {
- case kBack:
- {
- m_filter = m_incsearch->Backspace();
-
- if( m_filter.empty() )
- { // search should be terminated, returning to the previous item
- TerminateIncrementalSearch( false );
- }
- else
- { // just find the first item for the current search string
- UpdateSearchPosition();
- }
- result = true;
- } break;
- case k0...k9:
- {
- // evaluate the keystroke
- m_filter = m_incsearch->KeyStroke( key - k0 );
- result = true;
- UpdateSearchPosition();
- } break;
- default:
- {
- result = false;
- }
- }
- }
- return result;
+ else {
+ // an incremental search is already active
+ switch( key ) {
+ case kBack:
+ {
+ m_filter = m_incsearch->Backspace();
+
+ if( m_filter.empty() ) {
+ // search should be terminated, returning to the previous item
+ TerminateIncrementalSearch( false );
+ }
+ else {
+ // just find the first item for the current search string
+ UpdateSearchPosition();
+ }
+ result = true;
+ } break;
+ case k0...k9:
+ {
+ // evaluate the keystroke
+ m_filter = m_incsearch->KeyStroke( key - k0 );
+ result = true;
+ UpdateSearchPosition();
+ } break;
+ default:
+ {
+ result = false;
+ }
+ }
+ }
+ return result;
}
-void mgTree::TerminateIncrementalSearch( bool remain_on_current )
-{
- if( m_incsearch )
- {
- m_filter = "";
- delete m_incsearch;
- m_incsearch = NULL;
+void mgTree::TerminateIncrementalSearch( bool remain_on_current ) {
+ if( m_incsearch ) {
+ m_filter = "";
+ delete m_incsearch;
+ m_incsearch = NULL;
- if( remain_on_current )
- {
- m_start_position = osd()->Current();
- }
+ if( remain_on_current ) {
+ m_start_position = osd()->Current();
+ }
- UpdateSearchPosition();
- }
+ UpdateSearchPosition();
+ }
}
string
mgTree::Title () const
{
- string title = selection ()->getListname ();
+ string title = selection ()->getListname ();
- if( !m_filter.empty() )
- {
- title += " (" + m_filter + ")";
- }
+ if( !m_filter.empty() ) {
+ title += " (" + m_filter + ")";
+ }
- return title;
+ return title;
}
void
-mgTree::BuildOsd ()
-{
- InitOsd (false);
- AddSelectionItems (selection());
+mgTree::BuildOsd () {
+ InitOsd (false);
+ AddSelectionItems (selection());
}
-const char*
-mgMainMenu::Message1(const char *msg, ...)
-{
- if (strlen(msg)==0) return 0;
- va_list ap;
- va_start(ap, msg);
- vmsprintf(&m_message, tr(msg), ap);
- va_end(ap);
- return m_message;
-}
+eOSState mgSelOsd::ProcessKey (eKeys key) {
+ // TODO factor out to mgOsd
+ eOSState result = osContinue;
+ if (Menus.size()<1)
+ mgError("mgSelOsd::ProcessKey: Menus is empty");
-const char*
-mgMainMenu::Message1(const char *msg, const string &arg)
-{
- if (strlen(msg)==0) return 0;
- msprintf(&m_message, tr(msg), arg.c_str());
- return m_message;
-}
-
-eOSState mgMainMenu::ProcessKey (eKeys key)
-{
- eOSState result = osContinue;
- if (Menus.size()<1)
- mgError("mgMainMenu::ProcessKey: Menus is empty");
-
- mgPlayerControl * c = PlayerControl ();
- if (c)
- {
- if (!c->Playing ())
- {
- c->Shutdown();
- if (instant_playing && queue_playing)
- {
- PlayQueue();
- }
- else
- {
- instant_playing = false;
- queue_playing = false;
- }
- }
- else
- {
- switch (key)
- {
- case kPause:
- c->Pause ();
- break;
- case kStop:
- if (instant_playing && queue_playing)
- {
- PlayQueue();
+ mgPlayerControl * c = PlayerControl ();
+ if (c) {
+ if (!c->Playing ()) {
+ c->Shutdown();
+ if (instant_playing && queue_playing) {
+ PlayQueue();
+ }
+ else {
+ instant_playing = false;
+ queue_playing = false;
+ }
}
- else
- {
- queue_playing = false;
- c->Stop ();
+ else {
+ switch (key) {
+ case kPause:
+ c->Pause ();
+ break;
+ case kStop:
+ if (instant_playing && queue_playing) {
+ PlayQueue();
+ }
+ else {
+ queue_playing = false;
+ c->Stop ();
+ }
+ break;
+ case kChanUp:
+ c->Forward ();
+ break;
+ case kChanDn:
+ c->Backward ();
+ break;
+ default:
+ goto otherkeys;
+ }
+ goto pr_exit;
}
- break;
- case kChanUp:
- c->Forward ();
- break;
- case kChanDn:
- c->Backward ();
- break;
- default:
- goto otherkeys;
- }
- goto pr_exit;
- }
- }
- else
- if (key==kPlay)
- {
- PlayQueue();
- goto pr_exit;
}
-otherkeys:
- newmenu = Menus.back(); // Default: Stay in current menu
- newposition = -1;
-
- {
- mgMenu * oldmenu = newmenu;
-
- // item specific key logic:
- result = cOsdMenu::ProcessKey (key);
-
- // mgMenu specific key logic:
- if (result == osUnknown)
- result = oldmenu->Process (key);
- }
- // catch osBack for empty OSD lists . This should only happen for playlistitems
- // (because if the list was empty, no mgActions::ProcessKey was ever called)
- if (result == osBack)
- {
- // do as if there was an entry
- mgAction *a = Menus.back()->GenerateAction(actEntry,actEntry);
- if (a)
- {
- result = a->Back();
- delete a;
+ else
+ if (key==kPlay) {
+ PlayQueue();
+ goto pr_exit;
}
- }
-
-// do nothing for unknown keys:
- if (result == osUnknown)
- goto pr_exit;
-
-// change OSD menu as requested:
- if (newmenu == NULL)
- {
- if (Menus.size () > 1)
- {
- CloseMenu();
- forcerefresh = true;
- }
- else
+ otherkeys:
+ newmenu = Menus.back(); // Default: Stay in current menu
+ newposition = -1;
+
{
- result = osBack; // game over
- goto pr_exit;
+ mgMenu * oldmenu = newmenu;
+
+ // item specific key logic:
+ result = cOsdMenu::ProcessKey (key);
+
+ // mgMenu specific key logic:
+ if (result == osUnknown)
+ result = oldmenu->Process (key);
+ }
+ // catch osBack for empty OSD lists . This should only happen for playlistitems
+ // (because if the list was empty, no mgActions::ProcessKey was ever called)
+ if (result == osBack) {
+ // do as if there was an entry
+ mgAction *a = Menus.back()->GenerateAction(actEntry,actEntry);
+ if (a) {
+ result = a->Back();
+ delete a;
+ }
}
- }
- else if (newmenu != Menus.back ())
- AddMenu (newmenu,newposition);
-
- forcerefresh |= selection()->cacheIsEmpty();
-
- forcerefresh |= (newposition>=0);
-
- if (forcerefresh)
- {
- forcerefresh = false;
- if (newposition<0)
- newposition = selection()->gotoPosition();
- Menus.back ()->Display ();
- }
-pr_exit:
- showMessage();
- return result;
-}
-void
-mgMainMenu::CloseMenu()
-{
- mgMenu* m = Menus.back();
- if (newposition==-1) newposition = m->getParentIndex();
- Menus.pop_back ();
- delete m;
-}
+ // do nothing for unknown keys:
+ if (result == osUnknown)
+ goto pr_exit;
-void
-mgMainMenu::showMessage()
-{
- if (m_message)
- {
- showmessage(0,m_message);
- free(m_message);
- m_message = NULL;
- }
-}
+ // change OSD menu as requested:
+ if (newmenu == NULL) {
+ if (Menus.size () > 1) {
+ CloseMenu();
+ forcerefresh = true;
+ }
+ else {
+ result = osBack; // game over
+ goto pr_exit;
+ }
+ }
+ else if (newmenu != Menus.back ())
+ AddMenu (newmenu,newposition);
-void
-showmessage(int duration,const char * msg, ...)
-{
- va_list ap;
- va_start(ap,msg);
- char buffer[200];
- vsnprintf(buffer,199,tr(msg),ap);
-#if VDRVERSNUM >= 10307
- if (!duration) duration=2;
- Skins.Message (mtInfo, buffer,duration);
- Skins.Flush ();
-#else
- Interface->Status (buffer);
- Interface->Flush ();
-#endif
- va_end(ap);
+ forcerefresh |= selection()->cacheIsEmpty();
+
+ forcerefresh |= (newposition>=0);
+
+ if (forcerefresh) {
+ forcerefresh = false;
+ if (newposition<0)
+ newposition = selection()->gotoPosition();
+ Menus.back ()->Display ();
+ }
+ pr_exit:
+ showMessage();
+ return result;
}
void
-showimportcount(unsigned int impcount,bool final=false)
-{
+showimportcount(unsigned int impcount,bool final=false) {
#if 0
- // we should not write to the OSD since this is not the
+ // we should not write to the OSD since this is not the
// foreground thread. We could go thru port 2001.
if (final)
showmessage(1,"Import done:Imported %d items",impcount);
@@ -1012,234 +705,197 @@ showimportcount(unsigned int impcount,bool final=false)
}
void
-mgMainMenu::AddMenu (mgMenu * m,unsigned int position)
-{
- Menus.push_back (m);
- selection()->Activate();
- m->setosd (this);
- m->setParentIndex(Current());
- if (Get(Current()))
- m->setParentName(Get(Current())->Text());
- newposition = position;
- m->Display ();
+mgSelOsd::AddMenu (mgMenu * m,int position) {
+ // TODO zwischen mgSelOsd und mgOsd auseinanderdividieren
+ Menus.push_back (m);
+ selection()->Activate();
+ m->setosd (this);
+ m->setParentIndex(Current());
+ if (Get(Current()))
+ m->setParentName(Get(Current())->Text());
+ newposition = position;
+ m->Display ();
}
void
-mgMenu::setosd(mgMainMenu *osd)
-{
- m_osd = osd;
- m_prevUsingCollection = osd->UsingCollection;
- m_prevpos=osd->selection()->getPosition();
+mgSelMenu::setosd(mgOsd *osd) {
+ m_osd = osd;
+ // TODO sollte direkt parent fragen
+ m_prevUsingCollection = Selosd()->UsingCollection;
+ // TODO sollte direkt parent fragen
+ m_prevpos=Selosd()->selection()->getPosition();
+ // TODO dann muss dieses setosd auch nicht mehr virtual sein.
}
-mgSubmenu::mgSubmenu()
-{
- TreeBlueAction = actShowList;
- CollBlueAction = actShowList;
+mgSubmenu::mgSubmenu() {
+ BlueAction = actShowList;
+ CollBlueAction = actShowList;
}
string
-mgMenuOrders::Title() const
+mgSelMenuOrders::Title() const
{
return tr("Select an order");
}
void
-mgMenuOrders::BuildOsd ()
-{
- TreeRedAction = actEditOrder;
- TreeGreenAction = actCreateOrder;
- TreeYellowAction = actDeleteOrder;
- InitOsd ();
- osd()->AddOrderActions(this);
+mgSelMenuOrders::BuildOsd () {
+ RedAction = actEditOrder;
+ GreenAction = actCreateOrder;
+ YellowAction = actDeleteOrder;
+ InitOsd ();
+ Selosd()->AddOrderActions(this);
}
-mgMenuOrder::mgMenuOrder()
-{
- m_selection=0;
- m_orgselection = 0;
+mgSelMenuOrder::mgSelMenuOrder() {
+ m_selection=0;
+ m_orgselection = 0;
}
-mgMenuOrder::~mgMenuOrder()
-{
- if (m_selection)
- delete m_selection;
+mgSelMenuOrder::~mgSelMenuOrder() {
+ if (m_selection)
+ delete m_selection;
}
string
-mgMenuOrder::Title() const
+mgSelMenuOrder::Title() const
{
return m_selection->Name();
}
void
-mgMenuOrder::BuildOsd ()
-{
- if (!m_orgselection)
- m_orgselection = osd()->getSelection(getParentIndex());;
- if (!m_selection)
- m_selection = GenerateSelection(m_orgselection);
- if (m_selection->ordersize()==0)
- m_selection->InitDefaultOrder(1);
- InitOsd ();
- m_keytypes.clear();
- m_keynames.clear();
- m_orderbycount = m_selection->getOrderByCount();
- for (unsigned int i=0;i<m_selection->ordersize();i++)
- {
- if (m_selection->getKeyType(i)==keyGdUnique)
- break;
- unsigned int kt;
- m_keynames.push_back(m_selection->Choices(i,&kt));
- m_keytypes.push_back(kt);
- }
- for (unsigned int i=0;i<m_selection->ordersize();i++)
- {
- if (m_selection->getKeyType(i)==keyGdUnique)
- break;
- char buf[20];
- sprintf(buf,tr("Key %d"),i+1);
- mgAction *a = actGenerateKeyItem(buf,(int*)&m_keytypes[i],m_keynames[i].size(),&m_keynames[i][0]);
+mgSelMenuOrder::BuildOsd () {
+ if (!m_orgselection)
+ m_orgselection = Selosd()->getSelection(getParentIndex());;
+ if (!m_selection)
+ m_selection = GenerateSelection(m_orgselection);
+ if (m_selection->ordersize()==0)
+ m_selection->InitDefaultOrder(1);
+ InitOsd ();
+ m_keytypes.clear();
+ m_keynames.clear();
+ m_orderbycount = m_selection->getOrderByCount();
+ for (unsigned int i=0;i<m_selection->ordersize();i++) {
+ if (m_selection->getKeyType(i)==keyGdUnique)
+ break;
+ unsigned int kt;
+ m_keynames.push_back(m_selection->Choices(i,&kt));
+ m_keytypes.push_back(kt);
+ }
+ for (unsigned int i=0;i<m_selection->ordersize();i++) {
+ if (m_selection->getKeyType(i)==keyGdUnique)
+ break;
+ char buf[20];
+ sprintf(buf,tr("Key %d"),i+1);
+ mgAction *a = actGenerateKeyItem(buf,(int*)&m_keytypes[i],m_keynames[i].size(),&m_keynames[i][0]);
+ a->SetMenu(this);
+ osd()->AddItem(a);
+ }
+ mgAction *a = actGenerateBoolItem(tr("Sort by count"),&m_orderbycount);
a->SetMenu(this);
- osd()->AddItem(a);
- }
- mgAction *a = actGenerateBoolItem(tr("Sort by count"),&m_orderbycount);
- a->SetMenu(this);
- osd()->AddItem(a);
+ osd()->AddItem(a);
}
bool
-mgMenuOrder::ChangeSelection(eKeys key)
-{
- vector <const char*> newtypes;
- newtypes.clear();
- for (unsigned int i=0; i<m_keytypes.size();i++)
- newtypes.push_back(m_keynames[i][m_keytypes[i]]);
- mgSelection *newsel = GenerateSelection(m_orgselection);
- newsel->setKeys(newtypes);
- newsel->setOrderByCount(m_orderbycount);
- bool changed = !newsel->SameOrder(m_selection);
- if (changed)
- {
- delete m_selection;
- m_selection = newsel;
- osd()->forcerefresh = true;
- int np = osd()->Current();
- if (key==kUp && np) np--;
- if (key==kDown) np++;
- osd()->newposition = np;
- }
- else
- delete newsel;
- return changed;
+mgSelMenuOrder::ChangeSelection(eKeys key) {
+ vector <const char*> newtypes;
+ newtypes.clear();
+ for (unsigned int i=0; i<m_keytypes.size();i++)
+ newtypes.push_back(m_keynames[i][m_keytypes[i]]);
+ mgSelection *newsel = GenerateSelection(m_orgselection);
+ newsel->setKeys(newtypes);
+ newsel->setOrderByCount(m_orderbycount);
+ bool changed = !newsel->SameOrder(m_selection);
+ if (changed) {
+ delete m_selection;
+ m_selection = newsel;
+ osd()->forcerefresh = true;
+ int np = osd()->Current();
+ if (key==kUp && np) np--;
+ if (key==kDown) np++;
+ osd()->newposition = np;
+ }
+ else
+ delete newsel;
+ return changed;
}
void
-mgMenuOrder::SaveSelection()
-{
- m_selection->CopyKeyValues(osd()->selection());
- m_selection->Activate();
- osd()->setSelection(getParentIndex(),m_selection);
- m_selection = 0;
- m_orgselection = 0;
- osd()->SaveState();
+mgSelMenuOrder::SaveSelection() {
+ m_selection->CopyKeyValues(Selosd()->selection());
+ m_selection->Activate();
+ Selosd()->setSelection(getParentIndex(),m_selection);
+ m_selection = 0;
+ m_orgselection = 0;
+ Selosd()->SaveState();
}
-
-mgTreeCollSelector::mgTreeCollSelector()
-{
- TreeBlueAction = actShowList;
- CollBlueAction = actShowList;
+mgTreeCollSelector::mgTreeCollSelector() {
+ BlueAction = actShowList;
+ CollBlueAction = actShowList;
}
-mgTreeCollSelector::~mgTreeCollSelector()
-{
- osd()->UsingCollection = m_prevUsingCollection;
- osd()->newposition = m_prevpos;
+mgTreeCollSelector::~mgTreeCollSelector() {
+ Selosd()->UsingCollection = m_prevUsingCollection;
+ osd()->newposition = m_prevpos;
}
string
mgTreeCollSelector::Title () const
-{
- return m_title;
-}
-
-void
-mgTreeCollSelector::BuildOsd ()
-{
- osd()->UsingCollection = true;
- mgSelection *coll = osd()->collselection();
- InitOsd ();
- coll->leave_all();
- coll->setPosition(osd()->default_collection);
- AddSelectionItems (coll,coll_action());
- osd()->newposition = coll->gotoPosition();
- cOsdItem *c = osd()->Get(osd()->newposition);
- mgAction *a = dynamic_cast<mgAction *>(c);
- a->IgnoreNextEvent = true;
-}
-
-mgTreeAddToCollSelector::mgTreeAddToCollSelector(string title)
{
- m_title = title;
+ return m_title;
}
-mgTreeRemoveFromCollSelector::mgTreeRemoveFromCollSelector(string title)
-{
- m_title = title;
+void
+mgTreeCollSelector::BuildOsd () {
+ Selosd()->UsingCollection = true;
+ mgSelection *coll = Selosd()->collselection();
+ InitOsd ();
+ coll->leave_all();
+ coll->setPosition(Selosd()->default_collection);
+ AddSelectionItems (coll,coll_action());
+ osd()->newposition = coll->gotoPosition();
+ cOsdItem *c = osd()->Get(osd()->newposition);
+ mgAction *a = dynamic_cast<mgAction *>(c);
+ a->IgnoreNextEvent = true;
}
-void
-mgMainMenu::DisplayGoto ()
-{
- if (newposition >= 0)
- {
- if ((int)newposition>=Count())
- newposition = Count() -1;
- SetCurrent (Get (newposition));
- RefreshCurrent ();
- }
- Display ();
+mgTreeAddToCollSelector::mgTreeAddToCollSelector(string title) {
+ m_title = title;
}
-void
-mgMenu::Display ()
-{
- BuildOsd ();
- osd ()->DisplayGoto ();
+mgTreeRemoveFromCollSelector::mgTreeRemoveFromCollSelector(string title) {
+ m_title = title;
}
bool
-create_question()
-{
- char *b;
- msprintf(&b,tr("Create database %s?"),the_setup.DbName);
- bool result = Interface->Confirm(b);
- free(b);
- return result;
+create_question() {
+ char *b;
+ msprintf(&b,tr("Create database %s?"),the_setup.DbName);
+ bool result = Interface->Confirm(b);
+ free(b);
+ return result;
}
bool
-import()
-{
- if (!Interface->Confirm(tr("Import items?")))
- return false;
- mgDb *db = GenerateDB(false); // make sure in main thread that DB exists
- bool db_exists=db->Connect();
- delete db;
- if (!db_exists)
- return false;
- mgThreadSync *s = mgThreadSync::get_instance();
- if (!s)
- return false;
- static char *tld_arg[] = { ".", 0};
- int res = chdir(the_setup.ToplevelDir);
- if (res)
- {
- showmessage(2,tr("Cannot access directory %s:%d"),
- the_setup.ToplevelDir,errno);
- return false;
- }
- s->Sync(tld_arg);
- return true;
+import() {
+ if (!Interface->Confirm(tr("Import items?")))
+ return false;
+ mgDb *db = GenerateDB(false);// make sure in main thread that DB exists
+ bool db_exists=db->Connect();
+ delete db;
+ if (!db_exists)
+ return false;
+ mgThreadSync *s = mgThreadSync::get_instance();
+ if (!s)
+ return false;
+ static const char *tld_arg[] = { ".", 0};
+ int res = chdir(the_setup.ToplevelDir);
+ if (res) {
+ showmessage(2,tr("Cannot access directory %s:%d"),
+ the_setup.ToplevelDir,errno);
+ return false;
+ }
+ s->Sync(tld_arg);
+ return true;
}
diff --git a/vdr_menu.h b/vdr_menu.h
index 971af4d..ca6349a 100644
--- a/vdr_menu.h
+++ b/vdr_menu.h
@@ -21,8 +21,7 @@
#include <vdr/plugin.h>
#include <vdr/status.h>
#include "vdr_actions.h"
-
-#include "vdr_player.h"
+#include "mg_menu.h"
//! \brief play a selection, aborting what is currently played
//! \param select if true, play only what the current position selects
@@ -34,416 +33,298 @@ void showimportcount(unsigned int count);
class cCommands;
class mgSelection;
-class mgMenu;
-class mgMainMenu;
+class mgSelMenu;
class mgIncrementalSearch;
+class mgPlayerControl;
//! \brief if a player is running, return it
mgPlayerControl * PlayerControl ();
-//! \brief callback class, monitors state changes in vdr
-class mgStatus : public cStatus
-{
- private:
- //! \brief the mgMainMenu that wants to be notified
- mgMainMenu *main;
- public:
- //! \brief default constructor
- mgStatus(mgMainMenu* m) { main = m;}
- protected:
- //! \brief the event we want to know about
- virtual void OsdCurrentItem(const char *Text);
-};
-
-
/*!
* \brief the muggle main OSD
*/
-class mgMainMenu:public cOsdMenu
+class mgSelOsd : public mgOsd
{
- private:
- mgSelection *m_playsel;
- mgSelection *m_collectionsel;
- char *m_message;
- void showMessage();
- void LoadExternalCommands();
- vector<mgSelection*> selections;
- unsigned int m_current_selection;
- void DumpSelections(mgValmap& nv);
- void LoadSelections(mgValmap& nv);
- mgMenu *m_root;
- bool m_save_warned;
- public:
- void AddSelection();
- void DeleteSelection();
- void AddOrderActions(mgMenu *m);
- unsigned int getCurrentSelection() { return m_current_selection; }
- void setSelection(unsigned int idx,mgSelection *s);
- mgSelection* getSelection(unsigned int idx);
- bool SwitchSelection();
-
- mgSelection *moveselection;
- mgActions CurrentType();
-
- //! \brief syntactic sugar: expose the protected cOsdMenu::SetHelp
- void SetHelpKeys(const char *Red,const char *Green, const char *Yellow, const char *Blue) { SetHelp(Red,Green,Yellow,Blue); }
-
- //! \brief callback object, lets vdr notify us about OSD changes
- mgStatus *m_Status;
-
- //! \brief play the play selection, abort ongoing instant play
- void PlayQueue();
-
- //! \brief instant play the selection, abort ongoing queue playing
- void PlayInstant(const bool select=false);
-
- //! \brief true if we are browsing m_collectionsel
- bool UsingCollection;
-
- //! \brief the different menus, the last one is active
- vector < mgMenu * >Menus;
-
- //! \brief true if an item from the "playing" selection is being played
- bool queue_playing;
-
- //! \brief true if an item is being instant played
- bool instant_playing;
-
- //! \brief parent menu if any
- mgMenu * Parent ();
-
- //! \brief default constructor
- mgMainMenu ();
-
- //! \brief default destructor
- ~mgMainMenu ();
-
- //! \brief save the entire muggle state
- void SaveState();
-
- //! \brief adds a new mgMenu to the stack
- void AddMenu (mgMenu * m,unsigned int position=0);
-
- //! \brief initializes using values from nv
- void InitMapFromSetup (mgValmap& nv);
-
- //! \brief main entry point, called from vdr
- eOSState ProcessKey (eKeys Key);
-
- //! \brief from now on use the normal selection
- void UseNormalSelection ()
- {
- UsingCollection= false;
- }
-
- //! \brief from now on use the collection selection
- void UseCollectionSelection ()
- {
- UsingCollection= true;
- }
-
- //! \brief this is the collection things will be added to
- std::string default_collection;
-
-/*! \brief this is the "now playing" collection translated in
- * the current language. When changing the OSD language, this
- * collection will NOT be renamed in the data base, but a new
- * empty collection will be started. The collection for the
- * previous language will stay, so the user can copy from the
- * old one to the new one.
- */
- std::string play_collection;
-
-/*! \brief selects a certain line on the OSD and displays the OSD
- */
- void DisplayGoto ();
+ private:
+ mgSelection *m_playsel;
+ mgSelection *m_collectionsel;
+ vector<mgSelection*> selections;
+ unsigned int m_current_selection;
+ void DumpSelections(mgValmap& nv);
+ void LoadSelections(mgValmap& nv);
+ public:
+ void AddSelection();
+ void DeleteSelection();
+ void AddOrderActions(mgSelMenu *m);
+ unsigned int getCurrentSelection() { return m_current_selection; }
+ void setSelection(unsigned int idx,mgSelection *s);
+ mgSelection* getSelection(unsigned int idx);
+ bool SwitchSelection();
- //! \brief external commands
- cCommands *external_commands;
+ mgSelection *moveselection;
- //! \brief Actions can set newmenu which will then be displayed next
- mgMenu *newmenu;
+ //! \brief play the play selection, abort ongoing instant play
+ void PlayQueue();
- //! \brief Actions can set newstate which will then be returned to main vdr
- eOSState newstate;
+ //! \brief instant play the selection, abort ongoing queue playing
+ void PlayInstant(const bool select=false);
- //! \brief Actions can set forcerefresh. This will force a redisplay of the OSD
- bool forcerefresh;
+ //! \brief true if we are browsing m_collectionsel
+ bool UsingCollection;
- //! \brief show a message. Can be called by actions. It will
- // only be shown at the end of the next mgMainMenu::ProcessKey
- // because that might do forcerefresh which overwrites the message
- void Message (const char *msg) { m_message = strdup(msg); }
- const char* Message1 (const char *msg, ...)
- __attribute__ ((format (printf, 2, 3)));
+ //! \brief true if an item from the "playing" selection is being played
+ bool queue_playing;
- const char* Message1 (const char *msg, const string &arg);
+ //! \brief true if an item is being instant played
+ bool instant_playing;
- //! \brief Actions can request a new position. -1 means none wanted
- int newposition;
+ //! \brief default constructor
+ mgSelOsd ();
- //! \brief clears the screen, sets a title and the hotkey flag
- void InitOsd (std::string title,const bool hashotkeys);
+ //! \brief default destructor
+ ~mgSelOsd ();
-#if VDRVERSNUM >= 10307
- //! \brief expose the protected DisplayMenu() from cOsdMenu
- cSkinDisplayMenu *DisplayMenu(void)
- {
- return cOsdMenu::DisplayMenu();
- }
-#endif
+ //! \brief save the entire muggle state
+ void SaveState();
- //! \brief expose the protected cOsdMenu::hk()
- const char *hk (const char *s)
- {
- return cOsdMenu::hk (s);
- }
+ //! \brief adds a new mgMenu to the stack
+ virtual void AddMenu (mgMenu * m,int position=0);
- //! \brief the current selection
- mgSelection* selection () const
- {
- if (UsingCollection)
- return m_collectionsel;
- else
- return selections[m_current_selection];
- }
+ //! \brief initializes using values from nv
+ void InitMapFromSetup (mgValmap& nv);
- //! \brief the collection selection
- mgSelection* collselection() const
- {
- return m_collectionsel;
- }
+ //! \brief main entry point, called from vdr
+ eOSState ProcessKey (eKeys Key);
-//! \brief the "now playing" selection
- mgSelection* playselection () const
- {
- return m_playsel;
- }
+ //! \brief from now on use the normal selection
+ void UseNormalSelection () {
+ UsingCollection= false;
+ }
-//! \brief true if the cursor is placed in the collection list
- bool ShowingCollections();
+ //! \brief from now on use the collection selection
+ void UseCollectionSelection () {
+ UsingCollection= true;
+ }
-//! \brief true if the cursor is placed on the default collection
- bool DefaultCollectionSelected();
+ //! \brief this is the collection things will be added to
+ string default_collection;
-//! \brief true if the cursor is placed in the default collection
- bool CollectionEntered(std::string name);
+ /*! \brief this is the "now playing" collection translated in
+ * the current language. When changing the OSD language, this
+ * collection will NOT be renamed in the data base, but a new
+ * empty collection will be started. The collection for the
+ * previous language will stay, so the user can copy from the
+ * old one to the new one.
+ */
+ string play_collection;
- void AddItem(mgAction *a);
+ /*! \brief selects a certain line on the OSD and displays the OSD
+ */
+ //! \brief external commands
+ cCommands *external_commands;
- void CollectionChanged(std::string name,bool added);
+ //! \brief clears the screen, sets a title and the hotkey flag
+ void InitOsd (string title,const bool hashotkeys);
- void CloseMenu();
+#if VDRVERSNUM >= 10307
+ //! \brief expose the protected DisplayMenu() from cOsdMenu
+ cSkinDisplayMenu *DisplayMenu(void) {
+ return cOsdMenu::DisplayMenu();
+ }
+#endif
- void RefreshTitle();
+ //! \brief expose the protected cOsdMenu::hk()
+ const char *hk (const char *s) {
+ return cOsdMenu::hk (s);
+ }
+
+ //! \brief the current selection
+ mgSelection* selection () const
+ {
+ if (UsingCollection)
+ return m_collectionsel;
+ else
+ return selections[m_current_selection];
+ }
+
+ //! \brief the collection selection
+ mgSelection* collselection() const
+ {
+ return m_collectionsel;
+ }
+
+ //! \brief the "now playing" selection
+ mgSelection* playselection () const
+ {
+ return m_playsel;
+ }
+
+ //! \brief true if the cursor is placed in the collection list
+ bool ShowingCollections();
+
+ //! \brief true if the cursor is placed on the default collection
+ bool DefaultCollectionSelected();
+
+ //! \brief true if the cursor is placed in the default collection
+ bool CollectionEntered(string name);
+
+ void CollectionChanged(string name,bool added);
};
//! \brief a generic muggle menu
-class mgMenu
+class mgSelMenu : public mgMenu
{
- private:
- mgMainMenu* m_osd;
- const char *HKey(const mgActions act,mgActions on);
- protected:
- unsigned int m_prevpos;
- bool m_prevUsingCollection;
- eOSState ExecuteButton(eKeys key);
-//! \brief adds the wanted action to the OSD menu
-// \param hotkey if true, add this as a hotkey
- void AddAction(const mgActions action, mgActions on = mgActions(0),const bool hotkey=true);
-
- //! \brief add an external action, always with hotkey
- void AddExternalAction(const mgActions action, const char *title);
-
-//! \brief adds entries for all selected data base items to the OSD menu.
-// If this is the list of collections, appends a command for collection
-// creation.
- void AddSelectionItems (mgSelection *sel,mgActions act = actEntry);
- //! \brief the name of the blue button depends of where we are
- int m_parent_index;
- std::string m_parent_name;
- public:
- //! sets the correct help keys.
- void SetHelpKeys(mgActions on = mgActions(0));
-//! \brief generates an object for the wanted action
- mgAction* GenerateAction(const mgActions action,mgActions on);
-
-//! \brief executes the wanted action
- eOSState ExecuteAction (const mgActions action, const mgActions on);
-
-//! \brief sets the pointer to the owning mgMainMenu
- void setosd (mgMainMenu* osd);
-
- void setParentIndex(int idx) { m_parent_index = idx; }
- int getParentIndex() { return m_parent_index; }
- void setParentName(std::string name) { m_parent_name = name; }
- std::string getParentName() { return m_parent_name; }
-
-//! \brief the pointer to the owning mgMainMenu
- mgMainMenu* osd () const
- {
- return m_osd;
- }
-
-//! \brief the currently active selection of the owning mgMainMenu
- mgSelection* selection () const
- {
- return osd ()->selection ();
- }
-//! \brief the playselection of the owning mgMainMenu
- mgSelection* playselection () const
- {
- return osd ()->playselection ();
- }
-
- mgMenu ();
-
- virtual ~ mgMenu ()
- {
- }
-
-//! \brief computes the title
- virtual std::string Title() const = 0;
-
-//! \brief clears the screen, sets a title and the hotkey flag
- void InitOsd (const bool hashotkeys=true);
-
-//! \brief display OSD and go to osd()->newposition
- void Display ();
-
-//! \brief BuildOsd() should be abstract but then we cannot compile
- virtual void BuildOsd ()
- {
- }
-
-/*! \brief Process() should be abstract but then we cannot compile.
- * \return Process may decide that we want another screen to be displayed.
- * If the mgMenu* returned is not "this", the caller will use the return
- * value for a new display. If NULL is returned, the caller will display
- * the previous menu.
- */
- virtual eOSState Process (eKeys Key);
+ protected:
+ bool m_prevUsingCollection;
+
+ //! \brief adds entries for all selected data base items to the OSD menu.
+ // If this is the list of collections, appends a command for collection
+ // creation.
+ void AddSelectionItems (mgSelection *sel,mgActions act = actEntry);
+ //! \brief the name of the blue button depends of where we are
+ mgActions ButtonAction(eKeys key);
+ public:
+ mgSelMenu ();
+
+ void SetHotkeyAction(eKeys key,mgActions action);
-//! \brief the ID of the action defined by the red button.
- mgActions TreeRedAction;
- mgActions CollRedAction;
+ mgSelOsd * Selosd() const { return dynamic_cast<mgSelOsd *>(m_osd); }
-//! \brief the ID of the action defined by the green button.
- mgActions TreeGreenAction;
- mgActions CollGreenAction;
+ void setosd(mgOsd *osd);
-//! \brief the action defined by the yellow button.
- mgActions TreeYellowAction;
- mgActions CollYellowAction;
+ //! \brief the currently active selection of the owning mgSelOsd
+ mgSelection* selection () const
+ {
+ return Selosd ()->selection ();
+ }
+ //! \brief the playselection of the owning mgSelOsd
+ mgSelection* playselection () {
+ return Selosd ()->playselection ();
+ }
-//! \brief the action defined by the blue button.
- mgActions TreeBlueAction;
- mgActions CollBlueAction;
+ /*! \brief Process() should be abstract but then we cannot compile.
+ * \return Process may decide that we want another screen to be displayed.
+ * If the mgSelMenu* returned is not "this", the caller will use the return
+ * value for a new display. If NULL is returned, the caller will display
+ * the previous menu.
+ */
+ virtual eOSState Process (eKeys Key);
+
+ //! \brief the ID of the action defined by the red button.
+ mgActions CollRedAction;
+
+ //! \brief the ID of the action defined by the green button.
+ mgActions CollGreenAction;
+
+ //! \brief the action defined by the yellow button.
+ mgActions CollYellowAction;
+
+ //! \brief the action defined by the blue button.
+ mgActions CollBlueAction;
};
-//! \brief an mgMenu class for navigating through the data base
-class mgTree:public mgMenu
+//! \brief an mgSelMenu class for navigating through the data base
+class mgTree:public mgSelMenu
{
- public:
+ public:
- mgTree();
+ mgTree();
- //! \brief computes the title
- std::string Title() const;
+ //! \brief computes the title
+ string Title() const;
- bool UpdateIncrementalSearch( eKeys key );
+ bool UpdateIncrementalSearch( eKeys key );
- void TerminateIncrementalSearch( bool remain_on_current );
+ void TerminateIncrementalSearch( bool remain_on_current );
- protected:
- void BuildOsd ();
+ protected:
+ void BuildOsd ();
- private:
-
- void UpdateSearchPosition();
+ private:
+
+ void UpdateSearchPosition();
- mgIncrementalSearch *m_incsearch;
+ mgIncrementalSearch *m_incsearch;
- std::string m_filter;
+ string m_filter;
- int m_start_position;
+ int m_start_position;
};
-//! \brief an mgMenu class for submenus
-class mgSubmenu:public mgMenu
+//! \brief an mgSelMenu class for submenus
+class mgSubmenu:public mgSelMenu
{
- public:
- mgSubmenu();
-//! \brief computes the title
- std::string Title() const;
- protected:
- void BuildOsd ();
+ public:
+ mgSubmenu();
+ //! \brief computes the title
+ string Title() const;
+ protected:
+ void BuildOsd ();
};
-//! \brief an mgMenu class for selecting a selection
-class mgMenuOrders:public mgMenu
+//! \brief an mgSelMenu class for selecting a selection
+class mgSelMenuOrders:public mgSelMenu
{
- public:
-//! \brief computes the title
- std::string Title() const;
- protected:
- void BuildOsd ();
+ public:
+ //! \brief computes the title
+ string Title() const;
+ protected:
+ void BuildOsd ();
};
-class mgMenuOrder : public mgMenu
+class mgSelMenuOrder : public mgSelMenu
{
- public:
- mgMenuOrder();
- ~mgMenuOrder();
-//! \brief computes the title
- std::string Title() const;
- bool ChangeSelection(eKeys key);
- void SaveSelection();
- protected:
- void BuildOsd ();
- private:
- void AddKeyActions(mgMenu *m,mgSelection *o);
- mgSelection * m_orgselection;
- mgSelection * m_selection;
- int m_orderbycount;
- vector<int> m_keytypes;
- vector < vector <const char*> > m_keynames;
+ public:
+ mgSelMenuOrder();
+ ~mgSelMenuOrder();
+ //! \brief computes the title
+ string Title() const;
+ bool ChangeSelection(eKeys key);
+ void SaveSelection();
+ protected:
+ void BuildOsd ();
+ private:
+ void AddKeyActions(mgSelMenu *m,mgSelection *o);
+ mgSelection * m_orgselection;
+ mgSelection * m_selection;
+ int m_orderbycount;
+ vector<int> m_keytypes;
+ vector < vector <const char*> > m_keynames;
};
-//! \brief an mgMenu class for selecting a collection
-class mgTreeCollSelector:public mgMenu
+//! \brief an mgSelMenu class for selecting a collection
+class mgTreeCollSelector:public mgSelMenu
{
- public:
- mgTreeCollSelector();
- ~mgTreeCollSelector();
-//! \brief computes the title
- std::string Title() const;
- protected:
- void BuildOsd ();
- virtual mgActions coll_action() = 0;
- std::string m_title;
+ public:
+ mgTreeCollSelector();
+ ~mgTreeCollSelector();
+ //! \brief computes the title
+ string Title() const;
+ protected:
+ void BuildOsd ();
+ virtual mgActions coll_action() = 0;
+ string m_title;
};
class mgTreeAddToCollSelector:public mgTreeCollSelector
{
- public:
-//! \brief computes the title
- mgTreeAddToCollSelector(std::string title);
- protected:
- virtual mgActions coll_action() { return actAddCollEntry; }
+ public:
+ //! \brief computes the title
+ mgTreeAddToCollSelector(string title);
+ protected:
+ virtual mgActions coll_action() { return actAddCollEntry; }
};
-//! \brief an mgMenu class for selecting a collection
+//! \brief an mgSelMenu class for selecting a collection
class mgTreeRemoveFromCollSelector:public mgTreeCollSelector
{
- public:
-//! \brief computes the title
- mgTreeRemoveFromCollSelector(std::string title);
- protected:
- virtual mgActions coll_action() { return actRemoveCollEntry; }
+ public:
+ //! \brief computes the title
+ mgTreeRemoveFromCollSelector(string title);
+ protected:
+ virtual mgActions coll_action() { return actRemoveCollEntry; }
};
mgSelection* GenerateSelection(const mgSelection *s=0);
-
#endif
diff --git a/vdr_network.h b/vdr_network.h
index 66f30f7..d3ccf79 100644
--- a/vdr_network.h
+++ b/vdr_network.h
@@ -17,31 +17,30 @@ class cRingBufferLinear;
class mgNet:public cRingBufferLinear, cThread
{
- private:
- int m_fd;
- bool m_connected, m_netup;
- int m_deferedErrno;
- int m_rwTimeout, m_conTimeout;
- unsigned char m_lineBuff[4096];
- int m_count;
-//
- void close (void);
- int ringRead (unsigned char *dest, int len);
- void copyFromBuff (unsigned char *dest, int n);
- protected:
- virtual void action (void);
- public:
- mgNet (int size, int ConTimeoutMs, int RwTimeoutMs);
- ~mgNet ();
- bool connect (const char *hostname, const int port);
- void disconnect (void);
- bool connected (void)
- {
- return m_connected;
- }
- int gets (char *dest, int len);
- int puts (char *dest);
- int read (unsigned char *dest, int len);
- int write (unsigned char *dest, int len);
+ private:
+ int m_fd;
+ bool m_connected, m_netup;
+ int m_deferedErrno;
+ int m_rwTimeout, m_conTimeout;
+ unsigned char m_lineBuff[4096];
+ int m_count;
+ //
+ void close (void);
+ int ringRead (unsigned char *dest, int len);
+ void copyFromBuff (unsigned char *dest, int n);
+ protected:
+ virtual void action (void);
+ public:
+ mgNet (int size, int ConTimeoutMs, int RwTimeoutMs);
+ ~mgNet ();
+ bool connect (const char *hostname, const int port);
+ void disconnect (void);
+ bool connected (void) {
+ return m_connected;
+ }
+ int gets (char *dest, int len);
+ int puts (char *dest);
+ int read (unsigned char *dest, int len);
+ int write (unsigned char *dest, int len);
};
-#endif //___NETWORK_H
+#endif //___NETWORK_H
diff --git a/vdr_player.c b/vdr_player.c
index 4c4ae80..992c527 100644
--- a/vdr_player.c
+++ b/vdr_player.c
@@ -12,6 +12,7 @@
* Adapted from
* MP3/MPlayer plugin to VDR (C++)
* (C) 2001,2002 Stefan Huelswitt <huels@iname.com>
+ * and from the Music plugin (c) Morone
*/
#include <ctype.h>
@@ -25,13 +26,15 @@
#include <string>
#include <iostream>
+#include <fstream>
#include <mad.h>
-#include <linux/types.h> // this should really be included by linux/dvb/video.h
+#include <linux/types.h> // this should really be included by linux/dvb/video.h
#include <linux/dvb/video.h>
#include <vdr/player.h>
#include <vdr/device.h>
+#include <vdr/remote.h>
#include <vdr/thread.h>
#include <vdr/ringbuffer.h>
#define __STL_CONFIG_H
@@ -41,1840 +44,1365 @@
#include <vdr/plugin.h>
#include "vdr_player.h"
-#include "vdr_decoder.h"
#include "vdr_config.h"
#include "mg_setup.h"
+#include "mg_skin.h"
+#include "common.h"
+#include "bitmap.h"
+#include "pcmplayer.h"
#include "mg_image_provider.h"
-#include "i18n.h"
#include "mg_tools.h"
+char coverpicture[256];
+#define SPAN_PROVIDER_CHECK_ID "Span-ProviderCheck-v1.0"
+#define SPAN_CLIENT_CHECK_ID "Span-ClientCheck-v1.0"
+#define SPAN_SET_PCM_DATA_ID "Span-SetPcmData-v1.1"
+#define SPAN_SET_PLAYINDEX_ID "Span-SetPlayindex-v1.0"
+#define SPAN_GET_BAR_HEIGHTS_ID "Span-GetBarHeights-v1.0"
+
+#include "symbols/shuffle.xpm"
+#include "symbols/loop.xpm"
+#include "symbols/loopall.xpm"
+#include "symbols/stop.xpm"
+#include "symbols/play.xpm"
+#include "symbols/pause.xpm"
+#include "symbols/rew.xpm"
+#include "symbols/fwd.xpm"
+#include "symbols/copy.xpm"
+
+#if 0
+#include "symbols/delstar.xpm"
+#include "symbols/rate00.xpm"
+#include "symbols/rate05.xpm"
+#include "symbols/rate10.xpm"
+#include "symbols/rate15.xpm"
+#include "symbols/rate20.xpm"
+#include "symbols/rate25.xpm"
+#include "symbols/rate30.xpm"
+#include "symbols/rate35.xpm"
+#include "symbols/rate40.xpm"
+#include "symbols/rate45.xpm"
+#include "symbols/rate50.xpm"
+#endif
+
+cBitmap mgPlayerControl::bmShuffle(shuffle_xpm);
+cBitmap mgPlayerControl::bmLoop(loop_xpm);
+cBitmap mgPlayerControl::bmLoopAll(loopall_xpm);
+cBitmap mgPlayerControl::bmStop(stop_xpm);
+cBitmap mgPlayerControl::bmPlay(play_xpm);
+cBitmap mgPlayerControl::bmPause(pause_xpm);
+cBitmap mgPlayerControl::bmRew(rew_xpm);
+cBitmap mgPlayerControl::bmFwd(fwd_xpm);
+cBitmap mgPlayerControl::bmCopy(copy_xpm);
+
// ----------------------------------------------------------------
// #define DEBUGPES
-// TODO: check for use of constants
-#define OUT_BITS 16 // output 16 bit samples to DVB driver
-#define OUT_FACT (OUT_BITS/8*2) // output factor is 16 bit & 2 channels -> 4 bytes
-
-// mgResample
-#define MAX_NSAMPLES (1152*7) // max. buffer for resampled frame
-
-// mgNormalize
-#define MAX_GAIN 3.0 // max. allowed gain
-#define LIM_ACC 12 // bit, accuracy for lookup table
- // max. value covered by lookup table
-#define F_LIM_MAX (mad_fixed_t)((1<<(MAD_F_FRACBITS+2))-1)
-#define LIM_SHIFT (MAD_F_FRACBITS-LIM_ACC) // shift value for table lookup
-#define F_LIM_JMP (mad_fixed_t)(1<<LIM_SHIFT) // lookup table jump between values
-
-// mgLevel
-#define POW_WIN 100 // window width for smoothing power values
-#define EPSILON 0.00000000001 // anything less than EPSILON is considered zero
-
-// cMP3Player
-#define MAX_FRAMESIZE 2048 // max. frame size allowed for DVB driver
-#define HDR_SIZE 9
-#define LPCM_SIZE 7
-#define LEN_CORR 3
- // play time to fill buffers before speed check starts (ms)
-#define SPEEDCHECKSTART ((MP3BUFSIZE*1000/32000/2/2)+1000)
-#define SPEEDCHECKTIME 3000 // play time for speed check (ms)
-
-/*
-struct LPCMHeader { int id:8; // id
- int frame_count:8; // number of frames
- int access_ptr:16; // first acces unit pointer, i.e. start of audio frame
- bool emphasis:1; // audio emphasis on-off
- bool mute:1; // audio mute on-off
- bool reserved:1; // reserved
- int frame_number:5; // audio frame number
- int quant_wlen:2; // quantization word length
- int sample_freq:2; // audio sampling frequency (48khz=0, 96khz=1, 44,1khz=2, 32khz=3)
- bool reserved2:1; // reserved
-int chan_count:3; // number of audio channels - 1 (e.g. stereo = 1)
-int dyn_range_ctrl:8; // dynamic range control (0x80 if off)
-};
-*/
-
-struct LPCMFrame
+class cProgressBar : public cBitmap
{
- unsigned char PES[HDR_SIZE];
- unsigned char LPCM[LPCM_SIZE];
- unsigned char Data[MAX_FRAMESIZE - HDR_SIZE - LPCM_SIZE];
+ public:
+ cProgressBar(int Width, int Height, int Current, int Total, int FgColor, int BgColor );
};
-#include "vdr_sound.c"
+// --- mgPlayerControl -------------------------------------------------------
-// --- mgPCMPlayer ----------------------------------------------------------
+const char
+mgPlayerControl::ShuffleChar() {
+ char result='.';
+ switch (PlayList()->getShuffleMode ()) {
+ default:
+ break;
+ case mgSelection::SM_NORMAL:
+ result = 'S'; // Shuffle mode normal
+ break;
+ case mgSelection::SM_PARTY:
+ result = 'P'; // Shuffle mode party
+ break;
+ }
+ return result;
+}
-/*!
- * \brief a generic PCM player class
- *
- * this class implements a state machine that obtains decoded data from a generic data
- * and moves it to the DVB device. It inherits from cPlayer in order to be hooked into
- * VDR as a player and inherits from cThread in order to implement a separate thread
- * for the decoding process.
- */
-class mgPCMPlayer : public cPlayer, cThread
-{
- private:
+const char
+mgPlayerControl::LoopChar() {
+ char result='.';
+ switch (PlayList()->getLoopMode ()) {
+ default:
+ break;
+ case mgSelection::LM_SINGLE:
+ result = 'S'; // Loop mode single
+ break;
+ case mgSelection::LM_FULL:
+ result = 'F'; // Loop mode fuel
+ break;
+ }
+ return result;
+}
-//! \brief indicates whether the player is currently active
- bool m_active;
+mgPlayerControl::mgPlayerControl (mgSelection * plist):
+cControl (player = new mgPCMPlayer (plist)),
+cmdOsd(NULL) {
+ osd = 0 ;
+ cmdOsd = 0;
+ cmdMenu = 0;
+ m_img_provider = 0;
+
+ // Notify all cStatusMonitor
+ prevItem = 0;
+ prevPos=0;
+ orderchanged=false;
+ layout_initialized=false;
+
+ ScrollPosition=-1;
+ oneartist=PlayList()->OneArtist();
+ jumpactive=jumphide=false;
+ skipfwd=skiprew=shown=selecting=selecthide=statusActive=refresh=false;
+ showbuttons=0;
+ timeoutShow=0;
+ messagetime=0;
+ lastkeytime=number=timecount=0;
+ x1=depth=0;
+ framesPerSecond=SecondsToFrames(1);
+
+#if APIVERSNUM >= 10338
+ cStatus::MsgReplaying(this,"muggle",0,true);
+#else
+ cStatus::MsgReplaying(this,"muggle");
+#endif
+}
-//! \brief indicates whether the player has been started
- bool m_started;
+mgPlayerControl::~mgPlayerControl () {
+ Stop();
+ // Notify cleanup all cStatusMonitor
+#if VDRVERSNUM >= 10338
+ cStatus::MsgReplaying (this, 0, 0, false);
+#else
+ cStatus::MsgReplaying (this, 0);
+#endif
-//! \brief indicates whether the player is currently playing
- bool m_playing;
+ delete m_img_provider;
+ m_img_provider = NULL;
+
+ Hide ();
+ Stop ();
+}
-//! \brief a buffer for decoded sound
- cRingBufferFrame *m_ringbuffer;
+bool mgPlayerControl::Active (void) {
+ return player && player->Active ();
+}
-//! \brief a mutex for the playmode
- cMutex m_playmode_mutex;
-
-//! \brief a condition to signal playmode changes
- cCondVar m_playmode_cond;
-
-//! \brief the current playlist
- mgSelection *m_playlist;
-
-//! \brief the currently played or to be played item
- mgItemGd *m_current;
-
-//! \brief the decoder responsible for the currently playing item
- mgDecoder *m_decoder;
-
-//! \brief the image provider
- mgImageProvider *m_img_provider;
-
- cFrame *m_rframe, *m_pframe;
-
- enum emgPlayMode
- {
- pmPlay,
- pmStopped,
- pmPaused,
- pmStartup
- };
- emgPlayMode m_playmode;
-
- enum eState
- {
- msStart, msStop,
- msDecode, msNormalize,
- msResample, msOutput,
- msError, msEof, msWait
- };
- eState m_state;
-
- bool levelgood;
- unsigned int dvbSampleRate;
-
-//
- int m_index;
-
- void Empty ();
- bool SkipFile (bool skipforward=true);
- void PlayTrack();
- void StopPlay ();
-
- void SetPlayMode (emgPlayMode mode);
- void WaitPlayMode (emgPlayMode mode, bool inv);
-
- protected:
- virtual void Activate (bool On);
- virtual void Action (void);
-
- public:
- mgPCMPlayer (mgSelection * plist);
- virtual ~ mgPCMPlayer ();
-
- bool Active ()
- {
- return m_active;
- }
-
- bool Playing ()
- {
- return m_playing || m_active;
- }
-
- void Pause ();
- void Play ();
- void Forward ();
- void Backward ();
-
- void Goto (int Index, bool Still = false);
- void SkipSeconds (int secs);
- void ToggleShuffle (void);
- void ToggleLoop (void);
-
- virtual bool GetIndex (int &Current, int &Total, bool SnapToIFrame = false);
- // bool GetPlayInfo(cMP3PlayInfo *rm); // LVW
-
- void ReloadPlaylist ();
- void NewPlaylist (mgSelection * plist);
- void NewImagePlaylist (const char *directory);
-
- mgItemGd *getCurrent ()
- {
- return m_current;
- }
-
- mgSelection *getPlaylist ()
- {
- return m_playlist;
- }
-
- // background image handling stuff
- int m_lastshow;
- bool m_hasimages;
- string m_current_image;
- void CheckImage( );
- void ShowImage( );
- void TransferImageTFT( string cover );
- void send_pes_packet(unsigned char *data, int len, int timestamp);
-};
+bool mgPlayerControl::Playing (void) {
+ return player && player->Playing ();
+}
-mgPCMPlayer::mgPCMPlayer (mgSelection * plist)
- : cPlayer(the_setup.BackgrMode==2? pmAudioOnly:pmAudioOnlyBlack )
-{
- m_playlist = plist;
-
- m_active = true;
- m_started = false;
-
- m_ringbuffer = new cRingBufferFrame (MP3BUFSIZE);
-
- m_rframe = 0;
- m_pframe = 0;
- m_decoder = 0;
-
- m_playmode = pmStartup;
- m_state = msStop;
-
- m_index = 0;
- m_playing = false;
- m_current = 0;
-
- if( the_setup.BackgrMode == 1 )
- {
- m_img_provider = mgImageProvider::Create();
- }
- else
- {
- m_img_provider = NULL;
- }
+void
+mgPlayerControl::Stop (void) {
+#if VDRVERSNUM >= 10338
+ cStatus::MsgReplaying( this, 0, 0, false );
+#else
+ cStatus::MsgReplaying( this, 0);
+#endif
+ if (player) {
+ delete player;
+ player = 0;
+ }
}
+void
+mgPlayerControl::Pause (void) {
+ if (player) {
+ player->Pause ();
+ }
+}
-mgPCMPlayer::~mgPCMPlayer ()
-{
- Detach ();
- delete m_playlist;
- delete m_ringbuffer;
+void
+mgPlayerControl::Play (void) {
+ if (player) {
+ player->Play ();
+ }
+ ScrollPosition=-1;
+}
- if( m_img_provider )
- {
- delete m_img_provider;
- m_img_provider = NULL;
- }
+void
+mgPlayerControl::Forward (void) {
+ if (player) {
+ player->Forward ();
+ }
+ ScrollPosition=-1;
}
void
-mgPCMPlayer::PlayTrack()
-{
- mgItemGd * newcurr = dynamic_cast<mgItemGd*>(m_playlist->getCurrentItem ());
- if (newcurr)
- {
- delete m_current;
- m_current = new mgItemGd(newcurr);
- }
- Play ();
+mgPlayerControl::Backward (void) {
+ if (player) {
+ player->Backward ();
+ }
+ ScrollPosition=-1;
}
void
-mgPCMPlayer::Activate (bool on)
-{
- MGLOG ("mgPCMPlayer::Activate");
- if (on)
- {
- if (m_playlist && !m_started)
- {
- m_playmode = pmStartup;
- Start ();
-
- m_started = true;
- m_current = 0;
-
- m_playmode_mutex.Lock ();
- WaitPlayMode (pmStartup, true); // wait for the decoder to become ready
- m_playmode_mutex.Unlock ();
-
- Lock ();
- PlayTrack();
- Unlock ();
- }
- }
- else if (m_started && m_active)
- {
- Lock ();
- StopPlay ();
- Unlock ();
-
- m_active = false;
- SetPlayMode (pmStartup);
-
- Cancel (2);
- }
+mgPlayerControl::SkipSeconds(int Seconds) {
+ if (player) {
+ player->SkipSeconds(Seconds);
+ }
}
void
-mgPCMPlayer::ReloadPlaylist()
-{
- Lock ();
- m_playlist->clearCache();
- if (!m_playing)
- {
- SkipFile();
- Play ();
- }
- Unlock ();
+mgPlayerControl::Goto (int Position) {
+ if (player) {
+ player->Goto (Position);
+ }
+ ScrollPosition=-1;
}
void
-mgPCMPlayer::NewPlaylist (mgSelection * plist)
-{
- MGLOG ("mgPCMPlayer::NewPlaylist");
+mgPlayerControl::ToggleShuffle (void) {
+ if (player) {
+ player->ToggleShuffle ();
+ orderchanged=true;
+ }
+}
- Lock ();
- StopPlay ();
+void
+mgPlayerControl::ToggleLoop (void) {
+ if (player) {
+ player->ToggleLoop ();
+ orderchanged=true;
+ }
+}
- delete m_current;
- m_current = 0;
- delete m_playlist;
- m_playlist = plist;
- PlayTrack();
- Unlock ();
+void
+mgPlayerControl::ReloadPlaylist () {
+ if (player) {
+ player->ReloadPlaylist ();
+ orderchanged=true;
+ }
+ oneartist=PlayList()->OneArtist();
}
void
-mgPCMPlayer::NewImagePlaylist( const char *directory )
-{
- MGLOG ("mgPCMPlayer::NewImagePlaylist");
+mgPlayerControl::NewPlaylist (mgSelection * plist) {
+ if (player) {
+ player->NewPlaylist (plist);
+ }
+ ScrollPosition=-1;
+ oneartist=PlayList()->OneArtist();
+}
- Lock ();
+void
+mgPlayerControl::NewImagePlaylist( const char *directory ) {
+ MGLOG ("mgPlayerControl::NewImagePlaylist");
- if( m_img_provider )
- {
delete m_img_provider;
- m_img_provider = NULL;
- }
- m_img_provider = mgImageProvider::Create( directory );
- m_hasimages = true; // assume we have some images here!
-
- Unlock ();
+ m_img_provider = new mgImageProvider( directory ); // TODO
}
+
+
void
-mgPCMPlayer::SetPlayMode (emgPlayMode mode)
-{
- m_playmode_mutex.Lock ();
- if (mode != m_playmode)
- {
- m_playmode = mode;
- m_playmode_cond.Broadcast ();
- }
- m_playmode_mutex.Unlock ();
+mgPlayerControl::InitLayout(void) {
+ if (layout_initialized) return;
+ layout_initialized=true;
+ fw=6;
+ fh=27;
+
+ artistfirst = the_setup.ArtistFirst;
+ depth = 4;
+ clrTopBG1 = mgSkin.clrTopBG1;
+ clrTopTextFG1 = mgSkin.clrTopTextFG1;
+
+ clrTopBG2 = mgSkin.clrTopBG2;
+ clrTopTextFG2 = mgSkin.clrTopTextFG2;
+ clrTopItemBG1 = mgSkin.clrTopItemBG1;
+ clrTopItemInactiveFG = mgSkin.clrTopItemInactiveFG;
+ clrTopItemActiveFG = mgSkin.clrTopItemActiveFG;
+
+ clrListBG1 = mgSkin.clrListBG1;
+ clrListBG2 = mgSkin.clrListBG2;
+ clrListTextFG = mgSkin.clrListTextFG;
+ clrListTextActiveFG = mgSkin.clrListTextActiveFG;
+ clrListTextActiveBG = mgSkin.clrListTextActiveBG;
+
+ clrInfoBG1 = mgSkin.clrInfoBG1;
+ clrInfoBG2 = mgSkin.clrInfoBG2;
+ clrInfoTextFG1 = mgSkin.clrInfoTextFG1;
+ clrInfoTitleFG1 = mgSkin.clrInfoTitleFG1;
+ clrInfoTextFG2 = mgSkin.clrInfoTextFG2;
+
+ clrProgressBG1 = mgSkin.clrProgressBG1;
+ clrProgressBG2 = mgSkin.clrProgressBG2;
+
+ clrStatusBG = mgSkin.clrStatusBG;
+ clrStatusRed = mgSkin.clrStatusRed;
+ clrStatusGreen = mgSkin.clrStatusGreen;
+ clrStatusYellow = mgSkin.clrStatusYellow;
+ clrStatusBlue = mgSkin.clrStatusBlue;
+ clrStatusTextFG = mgSkin.clrStatusTextFG;
+
+ clrProgressbarFG = clrStatusBG;
+ clrProgressbarBG = clrStatusTextFG;
+
+#ifdef USE_BITMAP
+ imgalpha = the_setup.ImgAlpha;
+#endif
+
+ rows = 7;
+ osdheight = Setup.OSDHeight;
+ osdwidth = Setup.OSDWidth;
+ mpgdif = 0;
+
+ lh = 3*fh +(rows * fh) + fh/2;
+ x1 = osdwidth;
+ TopHeight = fh - 3;
+ BottomTop=osdheight-TopHeight+1;
+ PBHeight=2*fw+10;
+ PBTop=BottomTop-PBHeight-10;
+ PBBottom=PBTop+PBHeight-1;
+ InfoTop=lh;
+ InfoBottom = PBTop - 1;
+ int imagex1,imagey1,imagex2,imagey2;
+ if (the_setup.BackgrMode==1) {
+ CoverWidth = PBBottom-lh;
+ imagex1=Setup.OSDLeft+CoverX-10;
+ imagey1=Setup.OSDTop+InfoTop;
+ imagex2=Setup.OSDLeft+CoverX+CoverWidth+3;
+ imagey2=BottomTop+Setup.OSDTop-1 ;
+ CoverX = osdwidth - CoverWidth -3*fw -2;
+ CoverX /=4;
+ CoverX *=4;
+ InfoWidth = CoverX -27 -3*fw;
+ } else if (the_setup.BackgrMode==2) {
+ imagex1=0;
+ imagey1=0;
+ imagex2=703; // fix PAL
+ imagey2=575;
+ CoverWidth=0;
+ CoverX = osdwidth;
+ CoverX /=4;
+ CoverX *=4;
+ InfoWidth = CoverX -27- 3*fw;
+ }
+ if (!m_img_provider) {
+ tArea coverarea = { imagex1, imagey1, imagex2, imagey2};
+#if USE_BITMAP
+ m_img_provider = new mgImageProvider(coverarea);
+#else
+ m_img_provider = new mgMpgImageProvider(coverarea);
+#endif
+ }
}
+string
+mgPlayerControl::TrackInfo(const mgItemGd* trai) {
+ string result,p2;
+ if (!trai) return "";
+ if (oneartist.size()>0) {
+ result=trai->getTitle();
+ p2="";
+ }
+ else if (artistfirst) {
+ result =trai->getArtist();
+ p2 =trai->getTitle();
+ }
+ else {
+ result =trai->getTitle();
+ p2 =trai->getArtist();
+ }
+ if (result.size()>0 && p2.size()>0) result += " - ";
+ result += p2;
+ if (result.size()==0)
+ result=trai->getSourceFile(false);
+ return result;
+}
-void
-mgPCMPlayer::WaitPlayMode(emgPlayMode mode, bool inv)
-{
-// must be called with m_playmode_mutex LOCKED !!!
+class mgPlayerOsdListItem {
+ private:
+ const cFont *font;
+ int fg;
+ int bg;
+ string number;
+ string text;
+ bool changed;
+ public:
+ mgPlayerOsdListItem() { changed=false;font=0;fg=-1;bg=-1;number="XX";text="fdsfdsafdsaf";}
+ const cFont * Font() { return font; }
+ int Fg() { return fg; }
+ int Bg() { return bg; }
+ string Number() { return number; }
+ string Text() { return text; }
+ void setFont(const cFont *f) { changed|=font!=f;font=f;}
+ void setFg(int f) { changed|=fg!=f;fg=f;}
+ void setBg(int b) { changed|=bg!=b;bg=b;}
+ void setNumber(string n) { changed|=number!=n;number=n; }
+ void setText(string t) { changed|=text!=t;text=t; }
+ bool Changed() { bool result=changed;changed=false;return result; }
+};
- while (m_active
- && ((!inv && mode != m_playmode) || (inv && mode == m_playmode)))
- {
- m_playmode_cond.Wait (m_playmode_mutex);
- }
+void
+mgPlayerControl::ShowList(void) {
+ int listsize=PlayList()->items().size();
+ int middle=ScrollPosition;
+ if (middle<0) middle=currPos;
+ int top=middle - rows/2;
+ if (top+rows>listsize) top=listsize-rows;
+ if (top<0) top=0;
+ if (!prevItem) {
+ for (unsigned int idx=0;idx<pl.size();idx++)
+ delete pl[idx];
+ pl.clear();
+ for (int idx=0;idx<rows;idx++)
+ pl.push_back(new mgPlayerOsdListItem);
+ }
+ if (top!=prevTop || ScrollPosition!=prevScrollPosition||currItem!=prevItem) {
+ num=top;
+ char *buf;
+ msprintf(&buf,tr("%d tracks : %s"),PlayList()->items().size(),oneartist.c_str());
+ osd->DrawText( 8*fw, 0 , buf, clrStatusTextFG, clrStatusBG, OsdFont(), 60*fw, fh-2, taLeft);
+ free(buf);
+ for(int i=0 ; i<rows; i++,num++) {
+ mgItemGd *pi=0;
+ if (num<listsize)
+ pi = dynamic_cast<mgItemGd*>(PlayList()->getItem (num));
+ int fg,bg;
+ const cFont *font;
+ if (ScrollPosition==num) {
+ font=BigFont();
+ bg=clrListTextActiveFG;
+ fg=clrListTextActiveBG;
+ }
+ else if (pi==currItem) {
+ font=BigFont();
+ bg=clrListTextActiveBG;
+ fg=clrListTextActiveFG;
+ }
+ else {
+ font=cFont::GetFont(fontSml);
+ fg=clrListTextFG;
+ bg=clrListBG2;
+ }
+ char number[15];
+ snprintf(number,sizeof(number),"%d ",num+1);
+ string info=TrackInfo(pi);
+ if (info.size()==0) number[0]=0;
+ pl[i]->setFont(font);
+ pl[i]->setFg(fg);
+ pl[i]->setBg(bg);
+ pl[i]->setNumber(number);
+ pl[i]->setText(info);
+ if (pl[i]->Changed()) {
+ osd->DrawEllipse( 5*fw , 2*fh + (fh/2) + (i*fh) , (5*fw)+(fh/2) , 2*fh + (fh/2)+ (i*fh)+fh , bg , 7);
+ osd->DrawText( (5*fw) +(fh/2) +1 , (2*fh) + (fh/2) + (i*fh) , number , fg , bg , font , 10*fw , fh , taRight);
+ osd->DrawText( (5*fw) +(fh/2) +1 +(10*fw), 2*fh + (fh/2) + (i*fh) , info.c_str(), fg , bg , font , x1 - (29*fw) -fh -2, fh , taLeft);
+ osd->DrawRectangle( 5*fw +fh/2+10*fw+(x1-29*fw)-fh -1, 2*fh+(fh/2)+(i*fh), x1 -(5*fw) -fh/2 +1, 2*fh + (fh/2)+ (i*fh) +fh -1, bg);
+ osd->DrawEllipse( x1 -(5*fw) -fh/2 , 2*fh + (fh/2) + (i*fh) , x1 - (5*fw), 2*fh + (fh/2) +(i*fh) + fh, bg , 5);
+ flush=true;
+ }
+ }
+ listtime=time(0);
+ prevTop=top;
+ prevScrollPosition=ScrollPosition;
+ }
}
+bool
+mgPlayerControl::SetAreas(const char *caller,const tArea *Areas, int NumAreas) {
+ eOsdError result = osd->CanHandleAreas(Areas, NumAreas);
+ if (result == oeOk)
+ osd->SetAreas(Areas, NumAreas);
+ else {
+ for (int i=0;i<NumAreas;i++)
+ mgDebug(1,"Area %d: x1=%d y1=%d x2=%d y2=%d width=%d height=%d",
+ i,Areas[i].x1,Areas[i].y1,Areas[i].x2,Areas[i].y2,Areas[i].Width(),Areas[i].Height());
+ const char *errormsg = NULL;
+ switch (result) {
+ case oeTooManyAreas:
+ errormsg = "music: Too many OSD areas"; break;
+ case oeTooManyColors:
+ errormsg = "music: Too many colors"; break;
+ case oeBppNotSupported:
+ errormsg = "music: Depth not supported"; break;
+ case oeAreasOverlap:
+ errormsg = "music: Areas are overlapped"; break;
+ case oeWrongAlignment:
+ errormsg = "music: Areas not correctly aligned"; break;
+ case oeOutOfMemory:
+ errormsg = "music: OSD memory overflow"; break;
+ case oeUnknown:
+ errormsg = "music: Unknown OSD error"; break;
+ default:
+ break;
+ }
+ esyslog("muggle: %s: ERROR! osd open failed! can't handle areas (%d)-%s\n", caller, result, errormsg);
+ if (osd){ delete osd; osd=0;}
+ }
+ return result==oeOk;
+}
void
-mgPCMPlayer::Action (void)
-{
- MGLOG ("mgPCMPlayer::Action");
-
- struct mgDecode *ds = 0;
- struct mad_pcm *pcm = 0;
- mgResample resample[2];
- unsigned int nsamples[2];
- const mad_fixed_t *data[2];
- mgScale scale;
- mgLevel level;
- mgNormalize norm;
- bool haslevel = false;
- struct LPCMFrame lpcmFrame;
- const unsigned char *p = 0;
- int pc = 0, only48khz = the_setup.Only48kHz;
- cPoller poll;
-#ifdef DEBUG
-// int beat = 0;
-#endif
+mgPlayerControl::Display() {
+ ShowProgress();
+}
-#ifdef DEBUGPES
- FILE *peslog = fopen( "pes.dump", "w" );
+void
+mgPlayerControl::ShowProgress (bool open) {
+ CheckImage();
+ InitLayout();
+ int current_frame, total_frames;
+ player->GetIndex (current_frame, total_frames);
+ if (!osd) {
+ osd=cOsdProvider::NewOsd(Setup.OSDLeft, Setup.OSDTop,50);
+ if (!osd) return;
+ tArea Area[] = {
+ { 0, 0, x1 -1, TopHeight, 2 }, // border top
+ { 0, fh -2, x1 -1, 2*fh -1, 2 }, // between top and tracklist
+#ifdef HAVE_OSDMOD
+ { 0, 2*fh, x1 -1, lh -1, 4 }, // tracklist
+ { 0, lh, CoverX-1, PBTop-1 , 4 }, // Info
+#else
+ { 0, 2*fh, x1 -1, lh -1, 2 }, // tracklist
+ { 0, lh, CoverX-1, PBTop-1 , 2 }, // Info
#endif
-
- dsyslog ("muggle: player thread started (pid=%d)", getpid ());
-
- memset (&lpcmFrame, 0, sizeof (lpcmFrame));
- lpcmFrame.PES[2] = 0x01;
- lpcmFrame.PES[3] = 0xbd;
- lpcmFrame.PES[6] = 0x87;
- lpcmFrame.LPCM[0] = 0xa0; // substream ID
- lpcmFrame.LPCM[1] = 0xff;
- lpcmFrame.LPCM[2] = 0;
- lpcmFrame.LPCM[3] = 4;
- lpcmFrame.LPCM[5] = 0x01;
- lpcmFrame.LPCM[6] = 0x80;
-
- dvbSampleRate = 48000;
- m_state = msStop;
- SetPlayMode (pmStopped);
-
-#if VDRVERSNUM >= 10318
- cDevice::PrimaryDevice()->SetCurrentAudioTrack(ttAudio);
+#ifdef USE_BITMAP
+ { CoverX, lh, x1 - 1, BottomTop-1, depth }, // Cover
#endif
- while (m_active)
- {
-#ifdef DEBUG
- /*
- if (time (0) >= beat + 30)
- {
- std::
- cout << "mgPCMPlayer::Action: heartbeat buffer=" << m_ringbuffer->
- Available () << std::endl << std::flush;
- scale.Stats ();
- if (haslevel)
- norm.Stats ();
- beat = time (0);
- }
- */
+ { 0, PBTop , CoverX-1, BottomTop-1 , 2 }, // Progressbar
+ { 0, BottomTop , x1 -1, osdheight-1 , 4 }, // Bottom
+ };
+
+ if (!SetAreas("ShowProgress",Area,sizeof(Area) / sizeof(tArea)))
+ return;
+
+ //top
+
+ osd->DrawRectangle(0, 0, x1 - 1, fh -3, clrTopBG1);
+ osd->DrawEllipse(0, 0, fh/2 , fh/2, clrTransparent, -2);
+ osd->DrawEllipse(x1 -1 - fh/2, 0, x1 -1 , fh/2, clrTransparent, -1);
+
+ osd->DrawRectangle(0, fh -2, x1 -1 , 2*fh -1 ,clrTopBG2);
+
+ //tracklist
+ osd->DrawRectangle(0, 2*fh , x1 -1 , lh -1 ,clrListBG1);
+ osd->DrawRectangle(3*fw, 2*fh , x1 - 3*fw , lh - fh/2 -1 ,clrListBG2);
+ osd->DrawEllipse(3*fw , 2*fh , 3*fw + fh/2 , 2*fh + fh/2, clrListBG1, -2);
+ osd->DrawEllipse(x1 - 3*fw - fh/2, 2*fh, x1 - 3*fw , 2*fh + fh/2, clrListBG1, -1);
+ osd->DrawEllipse(3*fw , lh -fh -1, 3*fw + fh/2 , lh - fh/2 -1, clrListBG1, -3);
+ osd->DrawEllipse(x1 - 3*fw - fh/2 , lh -fh -1, x1 - 3*fw , lh - fh/2 -1, clrListBG1, -4);
+
+ //info
+ osd->DrawRectangle(0 , InfoTop , CoverX-1 , PBBottom ,clrInfoBG1);
+
+ // Cover
+#ifdef USE_BITMAP
+ osd->DrawRectangle(CoverX , InfoTop , x1 -1 , PBBottom ,clrInfoBG1);
#endif
+
+ // Info
+ osd->DrawRectangle(3*fw, lh + 27 + fh/2 , CoverX-2*fw+1, PBBottom , clrInfoBG2);
+ osd->DrawEllipse(3*fw , lh + 27 + fh/2 , 3*fw + fh/2 , lh + 54, clrInfoBG1 , -2);
+ osd->DrawEllipse(CoverX-fw-fh/2+1 , lh + fh + fh/2 , CoverX-2*fw+1 , lh + 2*fh, clrInfoBG1, -1);
- Lock ();
-
-
- if (!m_rframe && m_playmode == pmPlay)
- {
- switch (m_state)
- {
- case msStart:
- {
- if( m_img_provider && the_setup.BackgrMode == 1 )
- {
- m_hasimages = m_img_provider->updateItem( m_current );
- m_lastshow = -1; // never showed a picture during this song replay
- }
-
- m_index = 0;
-
- m_playing = true;
-
- if( m_current )
- {
- string filename = m_current->getSourceFile ();
- if ((m_decoder = mgDecoders::findDecoder (m_current))
- && m_decoder->start ())
- {
- levelgood = true;
- haslevel = false;
-
- level.Init ();
-
- m_state = msDecode;
- break;
- }
- else
- {
- mgWarning("found no decoder for %s",filename.c_str());
- m_state=msStop; // if loop mode is on and no decoder
- // for any track is found, we would
- // otherwise get into an endless loop
- // not stoppable with the remote.
- break;
- }
- }
- m_state = msEof;
- }
- break;
- case msDecode:
- {
- if( the_setup.BackgrMode == 1 )
- {
- CheckImage();
- }
-
- ds = m_decoder->decode ();
- switch (ds->status)
- {
- case dsPlay:
- {
- pcm = ds->pcm;
- m_index = ds->index / 1000;
- m_state = msNormalize;
- }
- break;
- case dsSkip:
- case dsSoftError:
- {
- // skipping, state unchanged, next decode
- }
- break;
- case dsEof:
- {
- m_state = msEof;
- }
- break;
- case dsOK:
- case dsError:
- {
- m_state = msError;
- }
- break;
- }
- }
- break;
- case msNormalize:
- {
- if (!haslevel)
- {
- if (levelgood)
- {
- level.GetPower (pcm);
- }
- }
- else
- {
- norm.AddGain (pcm);
- }
- m_state = msResample;
- }
- break;
- case msResample:
- {
-#ifdef DEBUG
- {
- static unsigned int oldrate = 0;
- if (oldrate != pcm->samplerate)
- {
- mgDebug ("mgPCMPlayer::Action: new input sample rate %d",pcm->samplerate);
- oldrate = pcm->samplerate;
- }
- }
-#endif
- nsamples[0] = nsamples[1] = pcm->length;
- data[0] = pcm->samples[0];
- data[1] = pcm->channels > 1 ? pcm->samples[1] : 0;
-
- lpcmFrame.LPCM[5] &= 0xcf;
- dvbSampleRate = 48000;
- if (!only48khz)
- {
- switch (pcm->samplerate)
- { // If one of the supported frequencies, do it without resampling.
- case 96000:
- { // Select a "even" upsampling frequency if possible, too.
- lpcmFrame.LPCM[5] |= 1 << 4;
- dvbSampleRate = 96000;
- }
- break;
-
-//case 48000: // this is already the default ...
-// lpcmFrame.LPCM[5]|=0<<4;
-// dvbSampleRate=48000;
-// break;
- case 11025:
- case 22050:
- case 44100:
- {
- // lpcmFrame.LPCM[5] |= 2 << 4;
- lpcmFrame.LPCM[5] |= 0x20;
- dvbSampleRate = 44100;
- }
- break;
- case 8000:
- case 16000:
- case 32000:
- {
- // lpcmFrame.LPCM[5] |= 3 << 4;
- lpcmFrame.LPCM[5] |= 0x30;
- dvbSampleRate = 32000;
- }
- break;
- }
- }
-
- if (dvbSampleRate != pcm->samplerate)
- {
- if (resample[0].
- SetInputRate (pcm->samplerate, dvbSampleRate))
- {
- nsamples[0] =
- resample[0].ResampleBlock (nsamples[0], data[0]);
- data[0] = resample[0].Resampled ();
- }
- if (data[1]
- && resample[1].SetInputRate (pcm->samplerate,
- dvbSampleRate))
- {
- nsamples[1] =
- resample[1].ResampleBlock (nsamples[1], data[1]);
- data[1] = resample[1].Resampled ();
- }
- }
- m_state = msOutput;
- }
- break;
- case msOutput:
- {
- if (nsamples[0] > 0)
- {
- unsigned int outlen =
- scale.ScaleBlock ( lpcmFrame.Data,
- sizeof (lpcmFrame.Data),
- nsamples[0],
- data[0], data[1],
- the_setup.AudioMode ?
- amDither : amRound );
- if (outlen)
- {
- outlen += sizeof (lpcmFrame.LPCM) + LEN_CORR;
- lpcmFrame.PES[4] = outlen >> 8;
- lpcmFrame.PES[5] = outlen;
-
- // lPCM has 600 fps which is 80 samples at 48kHz per channel
- // Frame size = (sample_rate * quantization * channels)/4800
- size_t frame_size = 2 * (dvbSampleRate*16)/4800;
-
- lpcmFrame.LPCM[1] = (outlen - LPCM_SIZE)/frame_size;
- m_rframe = new cFrame ((unsigned char *) &lpcmFrame,
- outlen +
- sizeof (lpcmFrame.PES) -
- LEN_CORR);
- }
- }
- else
- {
- m_state = msDecode;
- }
- }
- break;
- case msError:
- case msEof:
- {
- if (SkipFile ())
- {
- m_state = msStart;
- }
- else
- {
- m_state = msWait;
- }
- } // fall through
- case msStop:
- {
- m_playing = false;
-
- if (m_decoder)
- { // who deletes decoder?
- m_decoder->stop ();
- delete m_decoder;
- m_decoder = 0;
- }
-
- levelgood = false;
-
- scale.Stats ();
- if (haslevel)
- {
- norm.Stats ();
- }
- if (m_state == msStop)
- { // might be unequal in case of fall through from eof/error
- SetPlayMode (pmStopped);
- }
- }
- break;
- case msWait:
- {
- if (m_ringbuffer->Available () == 0)
- {
- // m_active = false;
- SetPlayMode (pmStopped);
- }
- }
- break;
- }
- }
-
- if (m_rframe && m_ringbuffer->Put (m_rframe))
- {
- m_rframe = 0;
- }
-
- if (!m_pframe && m_playmode == pmPlay)
- {
- m_pframe = m_ringbuffer->Get ();
- if (m_pframe)
- {
- p = m_pframe->Data ();
- pc = m_pframe->Count ();
- }
- }
-
- if (m_pframe)
- {
-#ifdef DEBUGPES
- fwrite( (void *)p, pc, sizeof( char ), peslog );
-#endif
+ //progressbar
+
+ osd->DrawRectangle(0, PBTop, CoverX - 1 , BottomTop-1 ,clrInfoBG1);
+
+ //bottom
+ // border top
+ osd->DrawRectangle(0, BottomTop , x1 - 1 , osdheight-1 , clrStatusBG);
+ osd->DrawEllipse(0, BottomTop , fh/2 , osdheight -1, clrTransparent, -3);
+ osd->DrawEllipse(x1 -1 - fh/2, BottomTop, x1 -1 , osdheight -1, clrTransparent, -4);
+
+ ShowHelpButtons(showbuttons);
+ LoadCover();
+
+ osd->Flush();
-#if VDRVERSNUM >= 10318
- int w = PlayPes (p, pc);
+ ShowStatus(true);
+
+#if VDRVERSNUM >= 10500
+ SetNeedsFastResponse(osd!=0);
#else
- int w = PlayVideo (p, pc);
-#endif
- if (w > 0)
- {
- p += w;
- pc -= w;
-
- if (pc <= 0)
- {
- m_ringbuffer->Drop (m_pframe);
- m_pframe = 0;
- }
- }
- else if (w < 0 && FATALERRNO)
- {
- LOG_ERROR;
- break;
- }
- }
- eState curr_m_state = m_state; // avoid helgrind warning
-
- Unlock ();
-
- if ((m_rframe || curr_m_state == msWait) && m_pframe)
- {
-// Wait for output to become ready
- DevicePoll (poll, 500);
- }
- else
- {
- if (m_playmode != pmPlay)
- {
- m_playmode_mutex.Lock ();
-
- if (m_playmode != pmPlay)
- {
- // Wait on playMode change
- WaitPlayMode (m_playmode, true);
- }
- m_playmode_mutex.Unlock ();
- }
- }
- }
-
- Lock ();
-
- if (m_rframe)
- {
- delete m_rframe;
- m_rframe = 0;
- }
-
- if (m_decoder)
- { // who deletes decoder?
- m_decoder->stop ();
- delete m_decoder;
- m_decoder = 0;
- }
-
- m_playing = false;
-
- SetPlayMode (pmStopped);
-
-#ifdef DEBUGPES
- fclose( peslog );
+ needsFastResponse=true;
#endif
+ fliptime=listtime=0; flipint=0; flip=-1; prevTop=-1; lastIndex=lastTotal=-1;
+ prevScrollPosition=-1;
+ prevItem=0;
- Unlock ();
+ }
- m_active = false;
+ currItem=CurrentItem();
+ currPos=player->Position();
+ bool changed=(prevItem != currItem);
+ char buf[256];
+
+ if (currItem) { // send progress to status monitor
+ if (changed||orderchanged) {
+ snprintf(buf,sizeof(buf),currItem->getArtist().size()?"[%c%c] (%d/%d) %s - %s":"[%c%c] (%d/%d) %s",
+ LoopChar(),ShuffleChar(),currPos,PlayList()->items().size(),currItem->getTitle().c_str(),currItem->getArtist().c_str());
+#if APIVERSNUM >= 10338
+ cStatus::MsgReplaying(this,buf,currItem->getSourceFile().size()?currItem->getSourceFile().c_str():0,true);
+#else
+ cStatus::MsgReplaying(this,buf);
+#endif
- dsyslog ("muggle: player thread ended (pid=%d)", getpid ());
-}
+ }
+ }
+ flush=false;
-void
-mgPCMPlayer::Empty (void)
-{
- MGLOG ("mgPCMPlayer::Empty");
+ if (CoverChanged()) {
+#ifdef USE_BITMAP
+ osd->DrawRectangle(CoverX, lh, x1 -1, PBBottom, clrInfoBG1);
+#endif
+ LoadCover();
+ flush = true;
+ }
- Lock ();
+ const char *lyrics= (CurrentItem() && CurrentItem()->HasLyrics()) ? tr("Lyrics: Yes") : tr("Lyrics: No");
+ osd->DrawText( 30, lh + 3*fh + fh/2, lyrics, clrInfoTextFG2, clrInfoBG2, cFont::GetFont(fontSml), 44 * fw, fh, taLeft);
+
+ if (!selecting && changed && !statusActive || refresh) {
+ if (ScrollPosition==-1) {
+ snprintf(buf,sizeof(buf),"%.1f kHz, %s kbps", currItem->getSampleRate()/1000.0,currItem->getBitrate().c_str());
+ osd->DrawText( 30, lh + 2*fh + fh/2 +4, buf, clrInfoTextFG2, clrInfoBG2, SmallFont(), InfoWidth, fh, taLeft);
+ flush=true;
+ }
+ else {
+ snprintf(buf,sizeof(buf),"%.1f kHz, %s kbps", currItem->getSampleRate()/1000.0,currItem->getBitrate().c_str());
+ osd->DrawText( 30, lh + 2*fh + fh/2 +4, buf, clrInfoTextFG2, clrInfoBG2, cFont::GetFont(fontSml), InfoWidth, fh, taLeft);
+ snprintf(buf,sizeof(buf),currItem->getArtist().size()?"%s - %s":"%s%s",currItem->getTitle().c_str(),currItem->getArtist().c_str());
+ if (buf[0]) {
+ if (!statusActive) {
+ DisplayInfo(buf);
+ flush=true;
+ }
+ }
+ }
+ }
- m_ringbuffer->Clear ();
- DeviceClear ();
+ if (!prevItem || orderchanged) {
+ mgSelection::LoopMode lm=PlayList()->getLoopMode();
+ if (lm==mgSelection::LM_SINGLE)
+ osd->DrawBitmap(x1 - 3*fw - 246, fh , bmLoop, clrTopItemActiveFG, clrTopItemBG1 );
+ else if (lm==mgSelection::LM_FULL)
+ osd->DrawBitmap(x1 - 3*fw - 246, fh , bmLoopAll, clrTopItemActiveFG, clrTopItemBG1 );
+ else
+ osd->DrawBitmap(x1 - 3*fw - 246, fh , bmLoop, clrTopBG2, clrTopBG2);
+ flush=true;
+ }
- delete m_rframe;
- m_rframe = 0;
- m_pframe = 0;
+ if (!prevItem || orderchanged) {
+ if (PlayList()->getShuffleMode()!=mgSelection::SM_NONE)
+ osd->DrawBitmap(x1 - 3*fw - 216, fh , bmShuffle, clrTopItemActiveFG, clrTopItemBG1);
+ else
+ osd->DrawBitmap(x1 - 3*fw - 216, fh , bmShuffle, clrTopBG2, clrTopBG2);
+ flush=true;
+ }
- Unlock ();
-}
+ switch(player->PlayMode()) {
+ case pmStopped:
+ osd->DrawBitmap(osdwidth - 3*fw - 160, fh , bmStop, clrTopItemActiveFG, clrTopItemBG1);
+ osd->DrawBitmap(osdwidth - 3*fw - 130, fh , bmPlay, clrTopItemInactiveFG, clrTopItemBG1);
+ osd->DrawBitmap(osdwidth - 3*fw - 100, fh , bmPause, clrTopItemInactiveFG, clrTopItemBG1);
+ break;
+ case pmPlay:
+ osd->DrawBitmap(osdwidth - 3*fw - 160, fh , bmStop, clrTopItemInactiveFG, clrTopItemBG1);
+ osd->DrawBitmap(osdwidth - 3*fw - 130, fh , bmPlay, clrTopItemActiveFG, clrTopItemBG1);
+ osd->DrawBitmap(osdwidth - 3*fw - 100, fh , bmPause, clrTopItemInactiveFG, clrTopItemBG1);
+ break;
+ case pmPaused:
+ osd->DrawBitmap(osdwidth - 3*fw - 160, fh , bmStop, clrTopItemInactiveFG, clrTopItemBG1);
+ osd->DrawBitmap(osdwidth - 3*fw - 130, fh , bmPlay, clrTopItemInactiveFG, clrTopItemBG1);
+ osd->DrawBitmap(osdwidth - 3*fw - 100, fh , bmPause, clrTopItemActiveFG, clrTopItemBG1);
+ case pmStartup:
+ break;
+ }
+ if (skiprew)
+ osd->DrawBitmap(osdwidth - 3*fw - 70, fh , bmRew, clrTopItemActiveFG, clrTopItemBG1);
+ else
+ osd->DrawBitmap(osdwidth - 3*fw - 70, fh , bmRew, clrTopItemInactiveFG, clrTopItemBG1);
-void
-mgPCMPlayer::StopPlay ()
-{ // StopPlay() must be called in locked state!!!
- MGLOG ("mgPCMPlayer::StopPlay");
- if (m_playmode != pmStopped)
- {
- Empty ();
- m_state = msStop;
- SetPlayMode (pmPlay);
- Unlock (); // let the decode thread process the stop signal
-
- m_playmode_mutex.Lock ();
- WaitPlayMode (pmStopped, false);
- m_playmode_mutex.Unlock ();
-
- Lock ();
- }
-}
+ if (skipfwd) osd->DrawBitmap(osdwidth - 3*fw - 40, fh , bmFwd, clrTopItemActiveFG, clrTopItemBG1);
+ else
+ osd->DrawBitmap(osdwidth - 3*fw - 40, fh , bmFwd, clrTopItemInactiveFG, clrTopItemBG1);
+
+ osd->DrawText( 8*fw, lh + fh/2 -fw, tr("Now playing :"), clrInfoTextFG1, clrInfoBG1, cFont::GetFont(fontOsd), (46*fw), fh, taLeft);
+ flush=true;
+
+ //Volume
+ snprintf(buf,sizeof(buf),"%s: %d", tr("Volume"), CurrentVolume());
+ osd->DrawText( 30, lh + 4*fh + fh/2 -4, buf , clrInfoTextFG2, clrInfoBG2, cFont::GetFont(fontSml), 0, fh, taLeft);
+
+ current_frame/=SecondsToFrames(1); total_frames/=SecondsToFrames(1);
+ if (current_frame!=lastIndex || total_frames!=lastTotal) {
+ if (total_frames>0) {
+ int my_left=3*fw;
+ int my_right=CoverX - 11; //2*fw+1;
+ int my_top=InfoBottom+1;
+ int pb_left=my_left+fh/2;
+ int pb_bottom=PBTop+2*fw+9;
+ int pb_right=my_right-fh/2;
+ int pb_width=pb_right-pb_left+1;
+ int pb_height=pb_bottom-PBTop+1;
+ // osd->DrawRectangle( 7*fw, my_top, my_right - 7*fw, my_top + fh, clrInfoBG2);
+ osd->DrawEllipse(my_left, my_top, pb_left, pb_bottom, clrProgressbarFG, 3);
+ cProgressBar ProgressBar(pb_width, pb_height, current_frame, total_frames,
+ clrProgressbarFG , clrProgressbarBG);
+ osd->DrawBitmap(pb_left, my_top, ProgressBar);
+ osd->DrawEllipse(pb_right+1, my_top, my_right, pb_bottom, clrProgressbarFG,4);
+ }
+ flush=true;
+ }
+ if (!jumpactive) {
+ //TODO if (!scrollmode) {
+ if (true) {
+ bool doflip=false;
+ if (!prevItem || orderchanged)
+ doflip=true;
+ if (!currItem || changed) {
+ fliptime=time(0); flip=0;
+ doflip=true;
+ }
+ else if (time(0)>fliptime+flipint) {
+ fliptime=time(0);
+ flip++; if (flip>=the_setup.DisplayMode) flip=0;
+ doflip=true;
+ }
+ if (doflip) {
+ buf[0]=0;
+
+ switch(flip) {
+ default:
+ flip=0;
+ case 0:
+ snprintf(buf,sizeof(buf),"%s",currItem->getTitle().c_str());
+ flipint=6;
+ break;
+ case 1:
+ if (currItem->getArtist().size()) {
+ snprintf(buf,sizeof(buf),"%s: %s",tr("Artist"),currItem->getArtist().c_str());
+ flipint=4;
+ }
+ else fliptime = 0;
+ break;
+ case 2:
+ {
+ const char *a=currItem->getAlbum().c_str();
+ if (a[0] && strcmp(a,"Unassigned")) {
+ snprintf(buf,sizeof(buf),"Album: %s",a);
+ flipint=4;
+ }
+ else fliptime=0;
+ }
+ break;
+ case 3:
+ if (currItem->getGenre().size()) {
+ snprintf(buf,sizeof(buf),"%s: %s",tr("Genre"), currItem->getGenre().c_str());
+ flipint=3;
+ }
+ else fliptime=0;
+ break;
+ case 4:
+ if (currItem->getYear() > 2) {
+ snprintf(buf,sizeof(buf),"%s: %d",tr("Year"), currItem->getYear());
+ flipint=3;
+ }
+ else fliptime=0;
+ break;
+ }
+ if (buf[0]) {
+ if (!statusActive) {
+ DisplayInfo(buf);
+ flush=true;
+ }
+ else { mgDebug(5,"music: ctrl: displayinfo skipped due to status active!\n"); }
+ }
+ }
+ }
+ }
-bool mgPCMPlayer::SkipFile (bool skipforward)
-{
- MGLOG("mgPCMPlayer::SkipFile");
- mgItemGd * newcurr = NULL;
- int skip_direction=1;
- if (!skipforward)
- skip_direction=-1;
- if (m_playlist->skipItems (skip_direction))
- {
- newcurr = dynamic_cast<mgItemGd*>(m_playlist->getCurrentItem ());
- if (newcurr)
- {
- delete m_current;
- m_current = new mgItemGd(newcurr);
- }
- }
- return (newcurr != NULL);
+ ShowList();
+ if (flush) Flush();
+ skiprew=0;
+ skipfwd=0;
+ lastIndex=current_frame; lastTotal=total_frames;
+ prevPos=currPos;
+ prevItem=currItem;
+ refresh=false;
+ orderchanged=false;
}
void
-mgPCMPlayer::ToggleShuffle ()
-{
- m_playlist->toggleShuffleMode ();
-}
-
+mgPlayerControl::Hide () {
+ HideStatus();
+#if 0
+ if (cmdMenu) {
+ delete cmdMenu;
+ cmdMenu=0;
+ }
+#endif
+ if (cmdOsd) {
+ delete cmdOsd;
+ cmdOsd=0;
+ }
-void
-mgPCMPlayer::ToggleLoop (void)
-{
- m_playlist->toggleLoopMode ();
+#if 0
+ if (rateMenu) {
+ delete rateMenu;
+ rateMenu=0;
+ }
+#endif
+ HidePlayOsd();
}
-
void
-mgPCMPlayer::Pause (void)
-{
- if (m_playmode == pmPaused)
- {
- Play ();
- }
- else
- {
- if (m_playmode == pmPlay)
- {
-// DeviceFreeze();
- SetPlayMode (pmPaused);
- }
- }
+mgPlayerControl::HidePlayOsd() {
+ delete osd; osd=0;
}
-
void
-mgPCMPlayer::Play (void)
-{
- MGLOG ("mgPCMPlayer::Play");
-
- if (m_playmode != pmPlay && m_current)
- {
- Lock ();
-
- if (m_playmode == pmStopped)
- {
- m_state = msStart;
- }
- // DevicePlay(); // TODO? Commented out in original code, too
- SetPlayMode (pmPlay);
- Unlock ();
- }
+mgPlayerControl::Scroll(int by) {
+ int listsize=PlayList()->items().size();
+
+ if (ScrollPosition<0)
+ ScrollPosition=player->Position();
+ ScrollPosition += by;
+ if (ScrollPosition<0) ScrollPosition=0;
+ if (ScrollPosition>listsize-1)
+ ScrollPosition=listsize-1;
}
-
void
-mgPCMPlayer::Forward ()
-{
- MGLOG ("mgPCMPlayer::Forward");
-
- Lock ();
- if (SkipFile ())
- {
- StopPlay ();
- Play ();
- }
- Unlock ();
+mgPlayerControl::ShowCommandMenu() {
+ Hide();
+ cmdOsd = new mgPlayOsd;
+ cmdMenu = new mgPlayerCommands;
+ cmdOsd->AddMenu(cmdMenu);
+ cmdOsd->Display();
}
+eOSState mgPlayerControl::ProcessKey(eKeys Key) {
+ if (m_img_provider) {
+ if (m_img_provider->updateItem(CurrentItem())) {
+ // if the images are cached, we want them ASAP
+ m_imageshowtime = 0;
+ }
+ CheckImage();
+ }
+ switch(Key) {
+ case kFastRew:
+ case kFastRew|k_Repeat:
+ player->SkipSeconds(-the_setup.Jumptime);
+ skiprew=1;
+ return osContinue;
+ case kFastFwd:
+ case kFastFwd|k_Repeat:
+ skipfwd=1;
+ player->SkipSeconds(the_setup.Jumptime);
+ return osContinue;
+ case kPlay:
+ player->Play();
+ return osContinue;
+ case kPrev:
+ case kPrev|k_Repeat:
+ Backward();
+ return osContinue;
+ case kNext:
+ case kNext|k_Repeat:
+ Forward();
+ return osContinue;
+ case kPause:
+ Pause();
+ return osContinue;
+ case kStop:
+ Hide();
+ Stop();
+ return osEnd;
+ default:
+ break;
+ }
+
+ if (cmdOsd) {
+ eOSState st=cmdOsd->ProcessKey(Key);
+ if (st==osBack) {
+ delete cmdOsd; cmdOsd=0;
+ return osContinue;
+ } else if (st==osContinue)
+ return osContinue;
+ }
-void
-mgPCMPlayer::Backward (void)
-{
- MGLOG ("mgPCMPlayer::Backward");
- Lock ();
- if (SkipFile (false))
- {
- StopPlay ();
- Play ();
- }
- Unlock ();
-}
-
+ if (!player->Active()) return osEnd;
-void
-mgPCMPlayer::Goto (int index, bool still)
-{
- m_playlist->GotoItemPosition (index - 1);
- mgItemGd *next = dynamic_cast<mgItemGd*>(m_playlist->getCurrentItem ());
-
- if (next)
- {
- Lock ();
- StopPlay ();
- delete m_current;
- m_current = new mgItemGd(next);
- Play ();
- Unlock ();
- }
-}
+ if (timecount>0) timecount--;
+ if (messagetime) {
+ if (time(0)-messagetime<Setup.OSDMessageTime && Key==kNone) {
+ return osContinue;
+ }
+ else {
+ HidePlayOsd();
+ messagetime=0;
+ }
+ }
+ else if (timecount == 0) {
+ Display();
+ }
-void
-mgPCMPlayer::SkipSeconds (int secs)
-{
- if (m_playmode != pmStopped)
- {
- Lock ();
-
- if (m_playmode == pmPaused)
- {
- SetPlayMode (pmPlay);
- }
- if ( m_decoder
- && m_decoder->skip (secs, m_ringbuffer->Available(),
- dvbSampleRate) )
- {
- levelgood = false;
- }
-
- Empty ();
- Unlock ();
- }
-}
+ ShowStatus(Key==kNone);
+ if (jumpactive && Key!=kNone) { JumpProcess(Key); return osContinue; }
-bool mgPCMPlayer::GetIndex (int &current, int &total, bool snaptoiframe)
-{
- if (m_current)
- {
- current = SecondsToFrames (m_index);
- total = SecondsToFrames (m_current->getDuration ());
- return true;
- }
- return false;
-}
+ if (cmdOsd) {
+ eOSState eOSRet = cmdOsd->ProcessKey(Key);
+ switch(eOSRet) {
+ case kRed:
+ case osBack:
+ delete cmdOsd;
+ cmdOsd = NULL;
+ Display();
-void mgPCMPlayer::CheckImage()
-{
- if( m_hasimages && m_img_provider )
- {
- if( ( m_index % the_setup.ImageShowDuration == 0 && m_index > m_lastshow) || m_lastshow < 0 )
- { // all n decoding steps
- string source;
- m_current_image = m_img_provider->getImagePath( source );
-
- // check for TFT display of image
- if( !source.empty() )
- {
- TransferImageTFT( source );
- }
-
- // check for background display of image
- if( !m_current_image.empty() )
- {
- if( the_setup.BackgrMode == 1 )
- {
- ShowImage();
+ return osContinue;
+ default: return eOSRet;
}
- m_lastshow = m_index;
- }
}
- }
-}
-
-void mgPCMPlayer::ShowImage( )
-{
- // show image .mpg referred in m_current_image
-
- uchar *buffer;
- int fd;
- struct stat st;
- struct video_still_picture sp;
-
- if( (fd = open( m_current_image.c_str(), O_RDONLY ) ) >= 0 )
- {
- fstat (fd, &st);
- sp.iFrame = (char *) malloc (st.st_size);
- if( sp.iFrame )
- {
- sp.size = st.st_size;
- if( read (fd, sp.iFrame, sp.size) > 0 )
- {
- buffer = (uchar *) sp.iFrame;
-
- if( the_setup.UseDeviceStillPicture )
- {
- cCondWait::SleepMs(80);
- DeviceStillPicture( buffer, sp.size );
- }
- else
- {
- for (int i = 1; i <= 25; i++)
- {
- send_pes_packet (buffer, sp.size, i);
- }
+#if 0
+ else if (false) { // war rateMenu
+ eOSState eOSRet = rateMenu->ProcessKey(Key);
+ switch(eOSRet) {
+ case kRed:
+ case osBack:
+ delete rateMenu;
+ rateMenu = NULL;
+ Display();
+
+ return osContinue;
+ default: return eOSRet;
}
- }
- free (sp.iFrame);
}
- else
+ else
+#endif
{
- esyslog ("muggle[%d]: cannot allocate memory (%d bytes) for still image",
- getpid(), (int) st.st_size);
- }
- close (fd);
- }
- else
- {
- esyslog ("muggle[%d]: cannot open image file '%s'",
- getpid(), m_current_image.c_str() );
- }
-}
-void mgPCMPlayer::send_pes_packet(unsigned char *data, int len, int timestamp)
-{
-#define PES_MAX_SIZE 2048
- int ptslen = timestamp ? 5 : 1;
- static unsigned char pes_header[PES_MAX_SIZE];
-
- pes_header[0] = pes_header[1] = 0;
- pes_header[2] = 1;
- pes_header[3] = 0xe0;
-
- while(len > 0)
- {
- int payload_size = len;
- if(6 + ptslen + payload_size > PES_MAX_SIZE)
- payload_size = PES_MAX_SIZE - (6 + ptslen);
-
- pes_header[4] = (ptslen + payload_size) >> 8;
- pes_header[5] = (ptslen + payload_size) & 255;
-
- if(ptslen == 5)
- {
- int x;
- x = (0x02 << 4) | (((timestamp >> 30) & 0x07) << 1) | 1;
- pes_header[8] = x;
- x = ((((timestamp >> 15) & 0x7fff) << 1) | 1);
- pes_header[7] = x >> 8;
- pes_header[8] = x & 255;
- x = ((((timestamp) & 0x7fff) < 1) | 1);
- pes_header[9] = x >> 8;
- pes_header[10] = x & 255;
- }
- else
- {
- pes_header[6] = 0x0f;
- }
-
- memcpy(&pes_header[6 + ptslen], data, payload_size);
-#if VDRVERSNUM >= 10318
- PlayPes(pes_header, 6 + ptslen + payload_size);
+ switch(Key) {
+
+ case kMenu:
+ case kUp:
+ case kUp|k_Repeat:
+ Scroll(-1);
+ break;
+ case kDown:
+ case kDown|k_Repeat:
+ Scroll(1);
+ break;
+
+ case kLeft:
+ case kLeft|k_Repeat:
+ Scroll(-rows);
+ break;
+
+ case kRight:
+ case kRight|k_Repeat:
+ Scroll(rows);
+ break;
+
+ case kRed:
+
+ // MENUE OK
+ if (showbuttons==0) {
+ ShowCommandMenu();
+ }
+ else
+ // BACK OK
+ if (showbuttons==1) {
+ ShowHelpButtons(0);
+ ShowCommandMenu();
+ ShowHelpButtons(0);
+ }
+ break;
+
+ case kGreen:
+ case kGreen|k_Repeat:
+ // TRACK- OK
+ if (showbuttons==0) {
+ Backward();
+ }
+ break;
+
+ case kYellow:
+ case kYellow|k_Repeat:
+ if (showbuttons==0) {
+ Forward();
+ }
+ else
+ // JUMP
+ if (showbuttons==1) {
+ ShowHelpButtons(3);
+ Jump();
+ }
+ else
+ // COPY OK
+ if (showbuttons==2) {
+ ShowHelpButtons(0);
+ }
+ break;
+
+ case kBlue: //OK
+ if (showbuttons==0) {
+ ShowHelpButtons(1);
+ }
+ else
+ if (showbuttons==1) {
+ ShowHelpButtons(2);
+ }
+ else
+ if (showbuttons==2) {
+ ShowHelpButtons(0);
+ }
+ break;
+
+ case kOk:
+ refresh = true;
+ if (ScrollPosition>=0) Goto(ScrollPosition);
+ Display();
+ break;
+
+ case k0 ... k9:
+ {
+ int listsize=PlayList()->items().size();
+ number=number*10+Key-k0;
+ if (prevItem && number>0 && number<listsize) {
+ if (!osd) { ShowTimed(); selecthide=true; }
+ selecting=true; lastkeytime=time_ms();
+ char buf[32];
+ snprintf(buf,sizeof(buf),"%s %d- %s %d","No.",number,tr("of"),listsize);
+ osd->DrawText( 5*fw, lh + 3*fh + fh/2, buf, clrInfoTextFG2, clrInfoBG2, SmallFont(), 40 * fw , fh, taLeft);
+ Flush();
+ break;
+ }
+ number=0; lastkeytime=0;
+ }
+ case kNone:
+
+ if (selecting && time_ms()-lastkeytime>SELECT_TIMEOUT) {
+ if (number>0) Goto(number);
+ if (selecthide) timeoutShow=time(0)+SELECTHIDE_TIMEOUT;
+ number=0; selecting=selecthide=false;
+ }
+
+ break;
+
+ case kBack:
+ if (ScrollPosition>=0) {
+ ScrollPosition=player->Position();
+ Display();
+
+ }
+ else {
+ Hide();
+#if APIVERSNUM >= 10332
+ cRemote::CallPlugin("muggle");
+ return osBack;
#else
- PlayVideo(pes_header, 6 + ptslen + payload_size);
+ return osEnd;
#endif
-
- len -= payload_size;
- data += payload_size;
- ptslen = 1;
- }
-}
-
-void mgPCMPlayer::TransferImageTFT( string cover )
-{
- cPlugin * graphtft = cPluginManager::GetPlugin("graphtft");
-
- if( graphtft )
- {
- graphtft->SetupParse( "CoverImage", cover.c_str() );
- }
-}
-
-// --- mgPlayerControl -------------------------------------------------------
-
-mgPlayerControl::mgPlayerControl (mgSelection * plist):cControl (player =
-new
-mgPCMPlayer (plist))
-{
-#if VDRVERSNUM >= 10307
- m_display = NULL;
- m_menu = NULL;
-#endif
- m_visible = false;
- m_has_osd = false;
- m_track_view = true;
- m_progress_view = true;
-
- m_szLastShowStatusMsg = NULL;
+ }
+ default:
+ return osUnknown;
+ }
- // Notify all cStatusMonitor
- StatusMsgReplaying ();
+ return osContinue;
+ }
}
-
-mgPlayerControl::~mgPlayerControl ()
-{
-// Stop();
-// Notify cleanup all cStatusMonitor
-#if VDRVERSNUM >= 10338
- cStatus::MsgReplaying (this, 0, 0, false);
-#else
- cStatus::MsgReplaying (this, 0);
-#endif
- if (m_szLastShowStatusMsg)
- {
- free (m_szLastShowStatusMsg);
- m_szLastShowStatusMsg = NULL;
- }
-
- InternalHide ();
- Stop ();
+void mgPlayerControl::Flush(void) {
+ if (osd) osd->Flush();
}
-
-bool mgPlayerControl::Active (void)
-{
- return player && player->Active ();
+int mgPlayerControl::CurrentVolume(void) {
+ return cDevice::PrimaryDevice()->CurrentVolume();
}
-
-bool mgPlayerControl::Playing (void)
-{
- return player && player->Playing ();
+const cFont *
+mgPlayerControl::OsdFont(void) {
+ return cFont::GetFont(fontOsd);
}
-
-void
-mgPlayerControl::Stop (void)
-{
-#if VDRVERSNUM >= 10338
- cStatus::MsgReplaying( this, 0, 0, false );
+const cFont *
+mgPlayerControl::BigFont(void) {
+#ifdef IM_A_MORON
+ return cFont::GetFont(fontBig);
#else
- cStatus::MsgReplaying( this, 0);
+ return cFont::GetFont(fontOsd);
#endif
- if (player)
- {
- delete player;
- player = 0;
- }
}
-
-void
-mgPlayerControl::Pause (void)
-{
- if (player)
- {
- player->Pause ();
- }
+const cFont *
+mgPlayerControl::SmallFont(void) {
+ return cFont::GetFont(fontSml);
}
-
-void
-mgPlayerControl::Play (void)
-{
- if (player)
- {
- player->Play ();
- }
+void mgPlayerControl::DisplayInfo(const char *s) {
+ if (osd)
+ osd->DrawText( 30, lh + fh +fh/2 +fw, s, s?clrInfoTitleFG1:clrInfoBG2, clrInfoBG2, BigFont(), InfoWidth, fh, taLeft);
}
+// --- cAsyncStatus ------------------------------------------------------------
+#include "menu-async.h"
+cAsyncStatus asyncStatus;
-void
-mgPlayerControl::Forward (void)
-{
- if (player)
- {
- player->Forward ();
- }
+cAsyncStatus::cAsyncStatus(void) {
+ text=0;
+ changed=false;
}
-
-void
-mgPlayerControl::Backward (void)
-{
- if (player)
- {
- player->Backward ();
- }
+cAsyncStatus::~cAsyncStatus() {
+ free((void *)text);
}
-
-void
-mgPlayerControl::SkipSeconds(int Seconds)
-{
- if (player)
- {
- player->SkipSeconds(Seconds);
- }
+void cAsyncStatus::Set(const char *Text) {
+ Lock();
+ free((void *)text);
+ text=Text ? strdup(Text) : 0;
+ changed=true;
+ Unlock();
}
-
-void
-mgPlayerControl::Goto (int Position, bool Still)
-{
- if (player)
- {
- player->Goto (Position, Still);
- }
+const char *cAsyncStatus::Begin(void) {
+ Lock();
+ return text;
}
-
-void
-mgPlayerControl::ToggleShuffle (void)
-{
- if (player)
- {
- player->ToggleShuffle ();
- }
+void cAsyncStatus::Finish(void) {
+ changed=false;
+ Unlock();
}
+void mgPlayerControl::ShowStatus(bool force) {
+
+ //TODO if (cmdOsd) return;
+ InitLayout();
+ if ((asyncStatus.Changed() || (force && !statusActive))) {
+ const char *text=asyncStatus.Begin();
+ if (text) {
+ if (!statusActive) { osd->SaveRegion( 30, lh +fh +fh/2 +fw, InfoWidth, lh + 3*fh +fw); }
+ osd->DrawText(30, lh + fh +fh/2 +fw, text, clrInfoTitleFG1, clrInfoBG2, OsdFont(), InfoWidth, fh, taCenter);
+ osd->Flush();
+ statusActive=true;
+ }
+ else
+ HideStatus();
+ asyncStatus.Finish();
+ }
-void
-mgPlayerControl::ToggleLoop (void)
-{
- if (player)
- {
- player->ToggleLoop ();
- }
-}
-
-void
-mgPlayerControl::ReloadPlaylist ()
-{
- if (player)
- {
- player->ReloadPlaylist ();
- }
}
-void
-mgPlayerControl::NewPlaylist (mgSelection * plist)
-{
- if (player)
- {
- player->NewPlaylist (plist);
- }
+void mgPlayerControl::HideStatus(void) {
+ if (!osd)
+ return;
+ if (statusActive) {
+ osd->RestoreRegion();
+ osd->Flush();
+ }
+ statusActive=false;
}
-void
-mgPlayerControl::NewImagePlaylist( const char *directory )
-{
- if (player)
- {
- // cout << "Signaling new image playlist to player: " << directory << endl << flush;
- player->NewImagePlaylist (directory);
- }
+bool mgPlayerControl::CoverChanged(void) {
+ return strcmp(coverpicture,m_current_imagesource.c_str());
}
-void
-mgPlayerControl::ShowContents ()
-{
-#if VDRVERSNUM >= 10307
- if (!m_menu)
- {
- m_menu = Skins.Current ()->DisplayMenu ();
- }
-
- if (player && m_menu)
- {
- int num_items = m_menu->MaxItems ();
-
- if (m_track_view)
- {
- m_menu->Clear ();
- m_menu->SetTitle (tr("Track info view"));
-
- m_menu->SetTabs (15);
-
- char *buf;
- if (num_items > 0)
- {
- msprintf (&buf, tr("Title:\t%s"),
- player->getCurrent ()->getTitle ().c_str ());
- m_menu->SetItem (buf, 0, false, false);
- free (buf);
- }
- if (num_items > 1)
- {
- msprintf (&buf, tr("Artist:\t%s"),
- player->getCurrent ()->getArtist ().c_str ());
- m_menu->SetItem (buf, 1, false, false);
- free (buf);
- }
- if (num_items > 2)
- {
- msprintf (&buf, tr("Album:\t%s"),
- player->getCurrent ()->getAlbum ().c_str ());
- m_menu->SetItem (buf, 2, false, false);
- free (buf);
- }
- if (num_items > 3)
- {
- msprintf (&buf, tr("Genre:\t%s"),
- player->getCurrent ()->getGenre ().c_str ());
- m_menu->SetItem (buf, 3, false, false);
- free (buf);
- }
- if( num_items > 4 )
- {
- msprintf (&buf, tr("Year:\t%d"),
- player->getCurrent ()->getYear () );
- m_menu->SetItem (buf, 4, false, false);
- free (buf);
- }
- if (num_items > 5)
- {
- int len = player->getCurrent ()->getDuration ();
- msprintf (&buf, tr("Length:\t%s"),
-#if VDRVERSNUM >= 10318
- *IndexToHMSF (SecondsToFrames (len)));
-#else
- IndexToHMSF (SecondsToFrames (len)));
+void mgPlayerControl::LoadCover(void) {
+ if (!player) return;
+ if (!m_current_imagesource.size()) return;
+ strcpy(coverpicture,m_current_imagesource.c_str());
+
+ fw=6;
+ fh=27;
+
+#if USE_BITMAP
+ int bmpcolors = 15; // TODO xine can handle 256
+ int w1 = CoverWidth;
+ int h1 = CoverWidth;
+ cMP3Bitmap *bmp;
+ if ((bmp = cMP3Bitmap::Load(coverpicture, imgalpha, h1, w1, bmpcolors)) !=NULL) {
+ osd->DrawRectangle(CoverX, lh, osdwidth -1, PBBottom, clrInfoBG1);
+ osd->DrawBitmap(CoverX , PBBottom -CoverWidth, bmp->Get(), clrTransparent, clrTransparent, true);
+ }
#endif
- m_menu->SetItem (buf, 5, false, false);
- free (buf);
- }
- if (num_items > 6)
- {
- msprintf (&buf, tr("Bit rate:\t%s"),
- player->getCurrent ()->getBitrate ().c_str ());
- m_menu->SetItem (buf, 6, false, false);
- free (buf);
- }
- if (num_items > 7)
- {
- int sr = player->getCurrent ()->getSampleRate ();
-
- msprintf (&buf, tr("Sampling rate:\t%d"), sr);
- m_menu->SetItem (buf, 7, false, false);
- free (buf);
- }
- if (num_items > 8)
- {
- int t = player->getCurrent ()->getTrack();
- msprintf (&buf, tr("File name:\t%d"), t);
- m_menu->SetItem (buf, 8, false, false);
- free (buf);
- }
- if (num_items > 9)
- {
- string sf = player->getCurrent ()->getSourceFile ();
- char *p = strrchr(sf.c_str(),'/');
- msprintf (&buf, tr("File name:\t%s"), p+1);
- m_menu->SetItem (buf, 9, false, false);
- free (buf);
- }
- }
- else
- {
- mgSelection *list = player->getPlaylist ();
- if (list)
- {
-// use items for playlist tag display
- m_menu->Clear ();
- m_menu->SetTitle (tr("Now playing"));
- m_menu->SetTabs (25);
-
- int cur = list->getItemPosition ();
- for (int i = 0; i < num_items; i++)
- {
- mgItemGd *item = dynamic_cast<mgItemGd*>(list->getItem (cur + i));
- if (item)
- {
- char *buf;
- msprintf (&buf, "%s\t%s", item->getTitle ().c_str (),
- item->getArtist ().c_str ());
- m_menu->SetItem (buf, i, i == 0, i >= 0);
- free (buf);
- }
- }
- }
- }
- }
+
+#ifdef HAVE_TUNED_GTFT
+ cPlugin *graphtft=cPluginManager::GetPlugin("graphtft");
+ if (graphtft) cStatus::MsgImageFile(coverpicture);
+#else
+ cPlugin *graphtft=cPluginManager::GetPlugin("graphtft");
+ if (graphtft) graphtft->SetupParse("CoverImage", coverpicture);
#endif
-}
+}
-void
-mgPlayerControl::ShowProgress ()
-{
- if (player)
- {
- char *buf;
- bool play = true, forward = true;
- int speed = -1;
-
- int current_frame, total_frames;
- player->GetIndex (current_frame, total_frames);
-
- if (!m_track_view)
- { // playlist stuff
- mgSelection *list = player->getPlaylist ();
- if (list)
- {
- total_frames = SecondsToFrames (list->getLength ());
- current_frame += SecondsToFrames (list->getCompletedLength ());
- msprintf (&buf, "(%d/%zd) %s:%s",
- list->getItemPosition () + 1, list->items().size(),
- player->getCurrent ()->getArtist ().c_str (),
- player->getCurrent ()->getTitle ().c_str ());
- }
- }
- else
- { // track view
- msprintf (&buf, "%s: %s",
- player->getCurrent ()->getArtist ().c_str (),
- player->getCurrent ()->getTitle ().c_str ());
- }
-
-#if VDRVERSNUM >= 10307
- if (!m_display)
- {
- m_display = Skins.Current ()->DisplayReplay (false);
- }
- if (m_display)
- {
- m_display->SetProgress (current_frame, total_frames);
- m_display->SetCurrent (IndexToHMSF (current_frame));
- m_display->SetTotal (IndexToHMSF (total_frames));
- m_display->SetTitle (buf);
- m_display->SetMode (play, forward, speed);
- m_display->Flush ();
- }
-#else
- int w = Interface->Width ();
- int h = Interface->Height ();
+void mgPlayerControl::ShowHelpButtons(int ShowButtons) {
+ if (!osd) return;
+ int tab;
+ InitLayout();
+ tab = Setup.OSDWidth/4;
+ showbuttons = ShowButtons;
+
+ osd->DrawEllipse( 14, BottomTop + 6, 28 , BottomTop +20, clrStatusRed, 0);
+ osd->DrawEllipse( tab+14, BottomTop + 6, tab + 28 , BottomTop +20, clrStatusGreen, 0);
+ osd->DrawEllipse(2*tab+14, BottomTop + 6, 2*tab + 28 , BottomTop +20, clrStatusYellow, 0);
+ osd->DrawEllipse(3*tab+14, BottomTop + 6, 3*tab + 28 , BottomTop +20, clrStatusBlue, 0);
+ switch(showbuttons) {
+ case 0:
+ osd->DrawText( 30, BottomTop, tr("Commands"), clrStatusTextFG, clrStatusBG, cFont::GetFont(fontSml), 14*fw, fh, taLeft);
+ osd->DrawText( tab + 30, BottomTop, tr("Track-"), clrStatusTextFG, clrStatusBG, cFont::GetFont(fontSml), 14*fw, fh, taLeft);
+ osd->DrawText( 2*tab +30, BottomTop, tr("Track+"), clrStatusTextFG, clrStatusBG, cFont::GetFont(fontSml), 14*fw, fh, taLeft);
+ osd->DrawText( 3*tab +30, BottomTop, tr("More.."), clrStatusTextFG, clrStatusBG, cFont::GetFont(fontSml), 14*fw, fh, taLeft);
+ break;
+ case 1:
+// red was rating, green was cover:
+ osd->DrawText( 30, BottomTop, tr("Commands"), clrStatusTextFG, clrStatusBG, cFont::GetFont(fontSml), 14*fw, fh, taLeft);
+ osd->DrawText( tab + 30, BottomTop, "", clrStatusTextFG, clrStatusBG, cFont::GetFont(fontSml), 14*fw, fh, taLeft);
+ osd->DrawText( 2*tab +30, BottomTop, tr("Jump"), clrStatusTextFG, clrStatusBG, cFont::GetFont(fontSml), 14*fw, fh, taLeft);
+ osd->DrawText( 3*tab +30, BottomTop, tr("Parent"), clrStatusTextFG, clrStatusBG, cFont::GetFont(fontSml), 14*fw, fh, taLeft);
+ break;
+ case 2:
+ osd->DrawText( 30, BottomTop, tr("Delete"), clrStatusTextFG, clrStatusBG, cFont::GetFont(fontSml), 14*fw, fh, taLeft);
+ osd->DrawText( tab + 30, BottomTop, tr("Clear"), clrStatusTextFG, clrStatusBG, cFont::GetFont(fontSml), 14*fw, fh, taLeft);
+ osd->DrawText( 2*tab +30, BottomTop, tr("Copy"), clrStatusTextFG, clrStatusBG, cFont::GetFont(fontSml), 14*fw, fh, taLeft);
+ osd->DrawText( 3*tab +30, BottomTop, tr("Parent"), clrStatusTextFG, clrStatusBG, cFont::GetFont(fontSml), 14*fw, fh, taLeft);
+ break;
+ case 3:
+ osd->DrawText( 30, BottomTop, tr("Parent"), clrStatusTextFG, clrStatusBG, cFont::GetFont(fontSml), 14*fw, fh, taLeft);
+ osd->DrawText( tab + 30, BottomTop, "<<", clrStatusTextFG, clrStatusBG, cFont::GetFont(fontSml), 14*fw, fh, taLeft);
+ osd->DrawText( 2*tab +30, BottomTop, ">>", clrStatusTextFG, clrStatusBG, cFont::GetFont(fontSml), 14*fw, fh, taLeft);
+ osd->DrawText( 3*tab +30, BottomTop, tr("Min/Sec"), clrStatusTextFG, clrStatusBG, cFont::GetFont(fontSml), 14*fw, fh, taLeft);
+ break;
+ }
+}
- Interface->WriteText (w / 2, h / 2, "Muggle is active!");
- Interface->Flush ();
-#endif
- free (buf);
- }
+// --- cProgressBar ------------------------------------------------------------
+
+cProgressBar::cProgressBar(int Width, int Height, int Current, int Total, int fgColor, int BgColor)
+:cBitmap(Width, Height, 2) {
+ char buf[256];
+ cBitmap txt(Width,Height,2);
+ if (Total > 0) {
+ int p = Current * Width / Total;;
+ snprintf(buf,sizeof(buf),Total?"%s: %02d:%02d %s %02d:%02d":"%s: %02d:%02d ",
+ tr("Time"),Current/60,Current%60, "-",Total/60,Total%60);
+ txt.DrawText(0,0,buf,fgColor,BgColor,cFont::GetFont(fontOsd),Width,Height,taCenter);
+ DrawRectangle(0, 0, p, Height, fgColor);
+ DrawRectangle(p + 1, 0, Width, Height, BgColor);
+ for (int x=0;x<Width;x++) {
+ for (int y=0;y<Height;y++) {
+ tIndex newcolor= *(txt.Data(x,y)) ^ *Data(x,y);
+ SetIndex(x,y,newcolor);
+ }
+ }
+ }
}
+void mgPlayerControl::JumpDisplay(void) {
+ char buf[64];
+ const char *j=tr("Jump: "), u=jumpsecs?'s':'m';
+ if(!jumpmm)
+ sprintf(buf, "%s- %c", j,u);
+ else
+ sprintf(buf,"%s%d- %c",j,jumpmm,u);
-void
-mgPlayerControl::Display ()
-{
- if (m_visible)
- {
- if (!m_has_osd)
- {
-// open the osd if its not already there...
-#if VDRVERSNUM >= 10307
-#else
- Interface->Open ();
-#endif
- m_has_osd = true;
- }
-
-// now an osd is open, go on
- if (m_progress_view)
- {
-#if VDRVERSNUM >= 10307
- if (m_menu)
- {
- delete m_menu;
- m_menu = NULL;
- }
-#endif
- ShowProgress ();
- }
- else
- {
-#if VDRVERSNUM >= 10307
- if (m_display)
- {
- delete m_display;
- m_display = NULL;
- }
-#endif
- ShowContents ();
- }
- }
- else
- {
- InternalHide ();
- }
+ DisplayInfo(buf);
}
+void mgPlayerControl::JumpProcess(eKeys Key) {
+ int n=Key-k0, d=jumpsecs?1:60;
+ switch(Key) {
+ case k0 ... k9:
+ if(jumpmm*10+n <= lastTotal/d) jumpmm=jumpmm*10+n;
+ JumpDisplay();
+ break;
+ case kBlue:
+ jumpsecs=!jumpsecs;
+ JumpDisplay();
+ break;
+ case kPlay:
+ case kUp:
+ jumpmm-=lastIndex/d;
+ // fall through
+ case kFastRew:
+ case kFastFwd:
+ case kGreen:
+ case kYellow:
+ player->SkipSeconds(jumpmm*d * ((Key==kGreen) ? -1:1));
+ // fall through
+ default:
+ jumpactive=false;
+ break;
+ }
-void
-mgPlayerControl::Hide ()
-{
- m_visible = false;
+ if(!jumpactive) {
+ if(jumphide) Hide();
+ ShowHelpButtons(0);
+ }
+}
- InternalHide ();
+void mgPlayerControl::Jump(void) {
+ jumpmm=1; jumphide=jumpsecs=false;
+ if(!osd) {
+ ShowTimed(); if(!osd) return;
+ jumphide=true;
+ }
+ JumpDisplay();
+ jumpactive=true; fliptime=0; flip=-1;
}
+void mgPlayerControl::ShowTimed(int Seconds) {
+ if(!osd) {
+ ShowProgress(true);
+ if(Seconds>0) timeoutShow=time(0)+Seconds;
+ }
+}
-void
-mgPlayerControl::InternalHide ()
-{
- if (m_has_osd)
- {
-#if VDRVERSNUM >= 10307
- if (m_display)
- {
- delete m_display;
- m_display = NULL;
- }
- if (m_menu)
- {
- delete m_menu;
- m_menu = NULL;
- }
-#else
- Interface->Close ();
-#endif
- m_has_osd = false;
- }
+mgSelection *
+mgPlayerControl::PlayList() {
+ if (!player) return 0;
+ return player->getPlaylist();
}
+mgItemGd *
+mgPlayerControl::CurrentItem(void) {
+ if (!player) return 0;
+ return dynamic_cast<mgItemGd*>(player->getPlaylist()->getCurrentItem ());
+}
-eOSState mgPlayerControl::ProcessKey (eKeys key)
-{
- if( key != kNone )
- {
- mgDebug (1,"mgPlayerControl::ProcessKey(%u)",key);
- }
-
- if (!Active ())
- {
- return osEnd;
- }
-
- StatusMsgReplaying ();
-
- Display ();
-
- eOSState
- state = cControl::ProcessKey (key);
-
- if (state == osUnknown)
- {
- switch (key)
- {
- case kUp:
- {
- if (m_visible && !m_progress_view && !m_track_view)
- {
- Backward();
- }
- else
- {
- Forward ();
- }
- Display();
- }
- break;
- case kDown:
- {
- if (m_visible && !m_progress_view && !m_track_view)
- {
- Forward ();
- }
- else
- {
- Backward();
- }
- }
- break;
- case kRed:
- {
- if (!m_visible && player)
- {
- mgSelection *
- pl = player->getPlaylist ();
-
- std::string s;
- switch (pl->toggleLoopMode ())
- {
- case mgSelection::LM_NONE:
- {
- s = tr ("Loop mode off");
- }
- break;
- case mgSelection::LM_SINGLE:
- {
- s = tr ("Loop mode single");
- }
- break;
- case mgSelection::LM_FULL:
- {
- s = tr ("Loop mode full");
- }
- break;
- default:
- {
- s = "Unknown loop mode";
- }
- }
-#if VDRVERSNUM >= 10307
- Skins.Message (mtInfo, s.c_str ());
- Skins.Flush ();
-#else
- Interface->Status (s.c_str ());
- Interface->Flush ();
-#endif
- }
- else
- {
-// toggle progress display between simple and detail
- m_progress_view = !m_progress_view;
- Display ();
- }
- }
- break;
- case kGreen:
- {
- if (!m_visible && player)
- {
- mgSelection *
- pl = player->getPlaylist ();
-
- std::string s;
- switch (pl->toggleShuffleMode ())
- {
- case mgSelection::SM_NONE:
- {
- s = tr ("Shuffle mode off");
- }
- break;
- case mgSelection::SM_NORMAL:
- {
- s = tr ("Shuffle mode normal");
- }
- break;
- case mgSelection::SM_PARTY:
- {
- s = tr ("Shuffle mode party");
- }
- break;
- default:
- {
- s = "Unknown shuffle mode";
- }
- }
-#if VDRVERSNUM >= 10307
- Skins.Message (mtInfo, s.c_str ());
- Skins.Flush ();
-#else
- Interface->Status (s.c_str ());
- Interface->Flush ();
+void
+mgPlayerControl::CheckImage() {
+ if (!m_img_provider)
+ return;
+#ifdef USE_BITMAP
+ if (cmdOsd) return;
#endif
- }
- else
- {
-// toggle progress display between playlist and track
- m_track_view = !m_track_view;
- Display ();
- }
- }
- break;
- case kPause:
- case kYellow:
- {
- Pause ();
- }
- break;
- case kStop:
- case kBlue:
- {
- InternalHide ();
- Stop ();
-
- return osEnd;
- }
- break;
- case kOk:
- {
- m_visible = !m_visible;
- Display ();
-
- return osContinue;
- }
- break;
- case kBack:
- {
- InternalHide ();
- Stop ();
-
- return osEnd;
- }
- break;
- case kLeft:
- {
- SkipSeconds( -60 );
- Display();
- } break;
- case kRight:
- {
- SkipSeconds( 60 );
- Display();
- } break;
- default:
- {
- return osUnknown;
- }
- }
- }
- return osContinue;
+ if (m_img_provider) {
+ if (time(0)-m_imageshowtime >= the_setup.ImageShowDuration) {
+ // all n decoding steps
+ m_current_image = m_img_provider->getImagePath(m_current_imagesource);
+
+ // check for TFT display of image
+ if ( !m_current_imagesource.empty() ) {
+ TransferImageTFT( m_current_imagesource );
+ }
+
+ if ( !m_current_image.empty() )
+ player->ShowMPGFile(m_current_image);
+ m_imageshowtime=time(0);
+ }
+ }
}
-
void
-mgPlayerControl::StatusMsgReplaying ()
-{
- MGLOG ("mgPlayerControl::StatusMsgReplaying()");
- char *szBuf = NULL;
- mgSelection * sel = NULL;
- mgItemGd * item = NULL;
- if (player)
- {
- sel = player->getPlaylist();
- item = dynamic_cast<mgItemGd*>(player->getCurrent ());
- }
- if (player && sel && item)
- {
- char cLoopMode;
- char cShuffle;
-
- switch (sel->getLoopMode ())
- {
- default:
- case mgSelection::LM_NONE:
- cLoopMode = '.'; // Loop mode off
- break;
- case mgSelection::LM_SINGLE:
- cLoopMode = 'S'; // Loop mode single
- break;
- case mgSelection::LM_FULL:
- cLoopMode = 'P'; // Loop mode fuel
- break;
- }
-
- switch (sel->getShuffleMode ())
- {
- default:
- case mgSelection::SM_NONE:
- cShuffle = '.'; // Shuffle mode off
- break;
- case mgSelection::SM_NORMAL:
- cShuffle = 'S'; // Shuffle mode normal
- break;
- case mgSelection::SM_PARTY:
- cShuffle = 'P'; // Shuffle mode party
- break;
- }
-
- if (item->getArtist ().length () > 0)
- {
- msprintf (&szBuf, "[%c%c] (%d/%zd) %s - %s",
- cLoopMode,
- cShuffle,
- sel->getItemPosition () + 1,
- sel->items().size(),
- item->getArtist ().c_str (),
- item->getTitle ().c_str ());
- }
- else
- {
- msprintf (&szBuf, "[%c%c] (%d/%zd) %s",
- cLoopMode,
- cShuffle,
- sel->getItemPosition () + 1,
- sel->items().size(),
- item->getTitle ().c_str ());
- }
- }
- else
- {
- msprintf (&szBuf, "[muggle]");
- }
-
-//fprintf(stderr,"StatusMsgReplaying(%s)\n",szBuf);
- if (szBuf)
- {
- if (m_szLastShowStatusMsg == NULL
- || 0 != strcmp (szBuf, m_szLastShowStatusMsg))
- {
- if (m_szLastShowStatusMsg)
- {
- free (m_szLastShowStatusMsg);
- }
- m_szLastShowStatusMsg = szBuf;
-#if VDRVERSNUM >= 10338
- cStatus::MsgReplaying (this, m_szLastShowStatusMsg, 0, true);
-#else
- cStatus::MsgReplaying (this, m_szLastShowStatusMsg);
-#endif
- }
- else
- {
- free (szBuf);
- }
- }
+mgPlayerControl::TransferImageTFT( string cover ) {
+ cPlugin * graphtft = cPluginManager::GetPlugin("graphtft");
+ if ( graphtft ) {
+ graphtft->SetupParse( "CoverImage", cover.c_str() );
+ }
}
diff --git a/vdr_player.h b/vdr_player.h
index d50b768..520694b 100644
--- a/vdr_player.h
+++ b/vdr_player.h
@@ -12,6 +12,8 @@
* Adapted from
* MP3/MPlayer plugin to VDR (C++)
* (C) 2001,2002 Stefan Huelswitt <huels@iname.com>
+ *
+ * and from the Music plugin (c) Morone
*/
#ifndef ___VDR_PLAYER_H
@@ -19,143 +21,240 @@
#include <vdr/player.h>
#include "mg_selection.h"
+#include "mg_item_gd.h"
+#include "mg_playcommands.h"
+
#if VDRVERSNUM >= 10307
class cOsd;
#endif
+extern char coverpicture[256];
// -------------------------------------------------------------------
class mgPCMPlayer;
+class mgImageProvider;
// -------------------------------------------------------------------
+class mgPlayerOsdListItem;
/*!
* \brief exerts control over the player itself
*
* This control is launched from the main menu and manages a link
* to the player. Key events are caught and signaled to the player.
*/
-class mgPlayerControl:public cControl
+class mgPlayerControl:public cControl, cStatus
{
- private:
-
-//! \brief the reference to the player
- mgPCMPlayer * player;
-
-//! \brief indicates, whether the osd should be visible
- bool m_visible;
-
-//! \brief indicates, whether an osd is currently displayed
- bool m_has_osd;
-
- bool m_track_view;
- bool m_progress_view;
-
-#if VDRVERSNUM >= 10307
-//! \brief a replay display to show the progress during playback
- cSkinDisplayReplay *m_display;
- cSkinDisplayMenu *m_menu;
-
- cOsd *osd;
- const cFont *font;
+ private:
+
+ //! \brief the reference to the player
+ mgPCMPlayer * player;
+
+ // copied from music plugin, mp3control.h:
+ cOsd *osd;
+ string imagefile;
+ string _message;
+ int fw, fh;
+ //
+
+ mgPlayOsd *cmdOsd;
+ mgPlayerCommands *cmdMenu;
+
+ bool shown, statusActive, refresh, flush, skiprew, skipfwd;
+ time_t timeoutShow, greentime, oktime;
+ time_t messagetime;
+ int num, number;
+ int lastkeytime, playstatus, timecount;
+ bool selecting, selecthide, artistfirst;
+ //
+ mgItemGd * currItem;
+ mgItemGd * prevItem;
+ unsigned int currPos;
+ unsigned int prevPos;
+ bool orderchanged;
+ time_t fliptime, listtime;
+ int rows;
+ int flip, flipint, osdwidth, osdheight, lh, showbuttons;
+ int x0, x1, depth, CoverX, CoverWidth, TopHeight,BottomTop,PBTop,PBHeight,PBBottom;
+ int InfoTop,InfoWidth,InfoBottom;
+ int lastIndex, lastTotal, prevTop, prevScrollPosition;
+ int framesPerSecond;
+
+ int ScrollPosition;
+
+ //
+ bool jumpactive, jumphide, jumpsecs;
+ int jumpmm, channelsSA, bandsSA, visualization;
+ //
+ static cBitmap bmShutdown , bmShuffle , bmLoop , bmLoopAll, bmStop , bmPlay , bmPause , bmRew , bmFwd , bmCopy;
+
+ static cBitmap bmDelStar,bmRate00,bmRate05,bmRate10,bmRate15,bmRate20,bmRate25,bmRate30,bmRate35,bmRate40,bmRate45,bmRate50;
+
+ // , bmRec;
+ //
+ int clrTopBG1;
+ int clrTopTextFG1;
+ int clrTopBG2;
+ int clrTopTextFG2;
+ int clrTopItemBG1;
+ int clrTopItemInactiveFG;
+ int clrTopItemActiveFG;
+ int clrListBG1;
+ int clrListBG2;
+ int clrListTextFG;
+ int clrListTextActiveFG;
+ int clrListTextActiveBG;
+ int clrInfoBG1;
+ int clrInfoBG2;
+ int clrInfoTextFG1;
+ int clrInfoTitleFG1;
+ int clrInfoTextFG2;
+ int clrProgressBG1;
+ int clrProgressBG2;
+ int clrProgressbarFG;
+ int clrProgressbarBG;
+ int clrStatusBG;
+ int clrStatusRed;
+ int clrStatusGreen;
+ int clrStatusYellow;
+ int clrStatusBlue;
+ int clrStatusTextFG;
+#ifdef USE_BITMAP
+ int imgalpha;
#endif
+ int mpgdif;
+ bool layout_initialized;
+ //
+ void InitLayout(void);
+ void ShowList(void);
+ void ShowTimed(int Seconds=0);
+ void ShowStatus(bool force);
+ void HideStatus(void);
+ void HidePlayOsd(void);
+ void ShowHelpButtons(int ShowButtons);
+ void ShowProgress(bool open=false);
+ void Display();
+ void DisplayInfo(const char *s=0);
+ void JumpDisplay(void);
+ void JumpProcess(eKeys Key);
+ void Jump(void);
+ void LoadCover(void);
+ void CopyTrack(void);
+ inline void Flush(void);
+ void ShutDown(void);
+ int CurrentVolume(void);
+ bool LoadImage(const char* fullname,bool coveronly);
+ char Songname[256];
+ bool CoverChanged();
+ mgSelection *PlayList(void);
+ string TrackInfo(const mgItemGd* item);
+ const cFont * OsdFont(void);
+ const cFont * BigFont(void);
+ const cFont * SmallFont(void);
+ bool SetAreas(const char *caller,const tArea *Areas, int NumAreas);
+ void Scroll(int by);
+ void ShowCommandMenu();
+
+ const char ShuffleChar();
+ const char LoopChar();
+
+ //! \brief the image provider
+ mgImageProvider *m_img_provider;
+ void CheckImage( );
+ void TransferImageTFT( string cover );
+ // background image handling stuff
+ int m_lastshow;
+ string m_current_image;
+ string m_current_imagesource;
+ time_t m_imageshowtime;
+ string oneartist;
+
+ vector<mgPlayerOsdListItem*> pl;
+
+ public:
+
+ mgItemGd * CurrentItem(void);
+
+ /*! \brief construct a control with a playlist
+ *
+ * \param plist - the playlist to be played
+ */
+ mgPlayerControl (mgSelection * plist);
+
+ /*! \brief destructor
+ */
+ virtual ~ mgPlayerControl ();
+
+ //! \brief indicate whether the corresponding player is active
+ bool Active ();
+
+ //! \brief indicate whether the corresponding player is playing
+ bool Playing ();
+
+ //! \brief stop the corresponding player
+ void Stop ();
+
+ //! \brief toggle the pause mode of the corresponding player
+ void Pause ();
+
+ //! \brief start playing
+ void Play ();
+
+ //! \brief skip to the next song
+ void Forward ();
+
+ //! \brief skip to the previous song
+ void Backward ();
+
+ /*! \brief skip a specified number of seconds
+ *
+ * \param seconds - the number of seconds to skip
+ */
+ void SkipSeconds (int seconds);
+
+ /*! \brief goto a certain position in the playlist
+ *
+ * \param index - the position in the playlist to skip to
+ */
+ void Goto (int index);
+
+ //! \brief toggle the shuffle mode of the corresponding player
+ void ToggleShuffle ();
+
+ //! \brief toggle the loop mode of the corresponding player
+ void ToggleLoop ();
+
+ /*! \brief tell the player to reload the play list.
+ * This is needed if we play a collection
+ * and the user changed the collection while playing it
+ */
+ void ReloadPlaylist();
+
+ /*! \brief signal a new playlist
+ *
+ * The caller has to take care of deallocating the previous list
+ *
+ * \param plist - the new playlist to be played
+ */
+ void NewPlaylist (mgSelection * plist);
+
+ /*! \brief signal a new image playlist
+ *
+ * A directory is passed and all images (.jpg, .png) contained in this dir are to be displayed during playback.
+ *
+ * \param directory - the directory containing images to be replayed
+ */
+ void NewImagePlaylist (const char *directory);
+
+
+ //! \brief a progress display
+ void ShowContents ();
-//! \brief Last Message for Statusmonitor
- char *m_szLastShowStatusMsg;
-
- public:
-
-/*! \brief construct a control with a playlist
- *
- * \param plist - the playlist to be played
- */
- mgPlayerControl (mgSelection * plist);
-
-/*! \brief destructor
- */
- virtual ~ mgPlayerControl ();
-
-//! \brief indicate whether the corresponding player is active
- bool Active ();
-
-//! \brief indicate whether the corresponding player is playing
- bool Playing ();
-
-//! \brief stop the corresponding player
- void Stop ();
-
-//! \brief toggle the pause mode of the corresponding player
- void Pause ();
-
-//! \brief start playing
- void Play ();
-
-//! \brief skip to the next song
- void Forward ();
-
-//! \brief skip to the previous song
- void Backward ();
-
-/*! \brief skip a specified number of seconds
- *
- * \param seconds - the number of seconds to skip
- */
- void SkipSeconds (int seconds);
-
-/*! \brief goto a certain position in the playlist
- *
- * \param index - the position in the playlist to skip to
- * \param still - currently unused
- * \todo Goto is currently unused and has an obvious memory leak
- */
- void Goto (int index, bool still = false);
-
-//! \brief toggle the shuffle mode of the corresponding player
- void ToggleShuffle ();
-
-//! \brief toggle the loop mode of the corresponding player
- void ToggleLoop ();
-
- /*! \brief tell the player to reload the play list.
- * This is needed if we play a collection
- * and the user changed the collection while playing it
- */
- void ReloadPlaylist();
-
-/*! \brief signal a new playlist
- *
- * The caller has to take care of deallocating the previous list
- *
- * \param plist - the new playlist to be played
- */
- void NewPlaylist (mgSelection * plist);
-
-/*! \brief signal a new image playlist
- *
- * A directory is passed and all images (.jpg, .png) contained in this dir are to be displayed during playback.
- *
- * \param directory - the directory containing images to be replayed
- */
- void NewImagePlaylist (const char *directory);
-
-//! \brief a progress display
- void ShowProgress ();
-
- void Display ();
-
- void ShowContents ();
-
-//! \brief hide the osd, if present
- void Hide ();
-
-//! \brief hide the osd, if present
- void InternalHide ();
+ //! \brief hide the osd, if present
+ void Hide ();
-//! \brief process key events
- eOSState ProcessKey (eKeys key);
+ //! \brief process key events
+ eOSState ProcessKey (eKeys key);
- protected:
-//! \brief signal a played file to any cStatusMonitor inside vdr
- void StatusMsgReplaying ();
};
-#endif //___VDR_PLAYER_H
+#endif //___VDR_PLAYER_H
diff --git a/vdr_setup.c b/vdr_setup.c
index 382d74a..4e413d0 100644
--- a/vdr_setup.c
+++ b/vdr_setup.c
@@ -21,86 +21,98 @@
#include "vdr_setup.h"
#include "vdr_actions.h"
-#include "i18n.h"
+#include <vdr/i18n.h>
-static char* chars_allowed = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_./";
+static const char* chars_allowed = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_./";
-static const char *bgmodes[3];
+static const char *bgmodes[4];
// --- mgMenuSetup -----------------------------------------------------------
-mgMenuSetup::mgMenuSetup ()
-{
- SetSection (tr ("Muggle"));
-
- // Audio stuff
- Add (new
- cMenuEditBoolItem (tr ("Initial loop mode"),&the_setup.InitLoopMode,
- tr("off"), tr("on") ) );
- Add (new
- cMenuEditBoolItem (tr ("Initial shuffle mode"), &the_setup.InitShuffleMode,
- tr("off"), tr("on") ) );
-
- Add (new
- cMenuEditBoolItem (tr ("Audio mode"), &the_setup.AudioMode,
- tr ("Round"), tr ("Dither")));
-
- Add (new
- cMenuEditBoolItem (tr ("Use 48kHz mode only"), &the_setup.Only48kHz,
- tr("no"), tr("yes") ) );
-
- Add (new
- cMenuEditIntItem (tr ("Normalizer level"),
- &the_setup.TargetLevel, 0, MAX_TARGET_LEVEL));
-
- Add (new
- cMenuEditIntItem (tr ("Limiter level"),
- &the_setup.LimiterLevel, MIN_LIMITER_LEVEL, 100));
-
- // Image/cover display
- bgmodes[0] = tr("Black");
- bgmodes[1] = tr("Image");
- bgmodes[2] = tr("Live");
- Add (new
- cMenuEditStraItem (tr ("Background mode"), &the_setup.BackgrMode,
- 3, bgmodes ) );
- Add (new
- cMenuEditIntItem (tr ("Image show duration (secs)"),
- &the_setup.ImageShowDuration, 1, 100));
- Add (new
- cMenuEditStrItem (tr ("Image cache directory"),
- the_setup.ImageCacheDir, MAX_PATH, chars_allowed ) );
- Add (new
- cMenuEditBoolItem (tr ("Use DVB still picture"), &the_setup.UseDeviceStillPicture,
- tr("no"), tr("yes") ) );
-
- // Synchronization
- Add (new
- cMenuEditBoolItem (tr ("Delete stale references"), &the_setup.DeleteStaleReferences,
- tr("no"), tr("yes") ));
-
- mgAction *a = actGenerate(actSync);
- const char *mn = a->MenuName();
- a->SetText(mn);
- free(const_cast<char*>(mn));
- Add(dynamic_cast<cOsdItem*>(a));
+mgMenuSetup::mgMenuSetup () {
+ SetSection (tr ("Muggle"));
+
+ // Audio stuff
+ Add (new
+ cMenuEditBoolItem (tr ("Initial loop mode"),&the_setup.InitLoopMode,
+ tr("off"), tr("on") ) );
+ Add (new
+ cMenuEditBoolItem (tr ("Initial shuffle mode"), &the_setup.InitShuffleMode,
+ tr("off"), tr("on") ) );
+
+ Add (new
+ cMenuEditBoolItem (tr ("Audio mode"), &the_setup.AudioMode,
+ tr ("Round"), tr ("Dither")));
+
+ Add (new
+ cMenuEditBoolItem (tr ("Use 48kHz mode only"), &the_setup.Only48kHz,
+ tr("no"), tr("yes") ) );
+
+ Add (new
+ cMenuEditIntItem (tr ("Normalizer level"),
+ &the_setup.TargetLevel, 0, MAX_TARGET_LEVEL));
+
+ Add (new
+ cMenuEditIntItem (tr ("Limiter level"),
+ &the_setup.LimiterLevel, MIN_LIMITER_LEVEL, 100));
+
+ // Image/cover display
+ bgmodes[0] = tr("Black");
+ bgmodes[1] = tr("Cover small");
+ bgmodes[2] = tr("Cover big");
+ bgmodes[3] = tr("Live");
+ Add (new
+ cMenuEditStraItem (tr ("Background mode"), &the_setup.BackgrMode,
+ 4, bgmodes ) );
+ Add (new
+ cMenuEditIntItem (tr ("Image show duration (secs)"),
+ &the_setup.ImageShowDuration, 1, 100));
+ Add (new
+ cMenuEditStrItem (tr ("Cache directory"),
+ the_setup.CacheDir, MAX_PATH, chars_allowed ) );
+ Add (new
+ cMenuEditBoolItem (tr ("Use DVB still picture"), &the_setup.UseDeviceStillPicture,
+ tr("no"), tr("yes") ) );
+ Add (new
+ cMenuEditBoolItem (tr ("Show artist first"), &the_setup.ArtistFirst,
+ tr("no"), tr("yes") ));
+ Add (new
+ cMenuEditIntItem (tr ("Fastforward jump (secs)"),
+ &the_setup.Jumptime, 1, 100));
+#ifdef USE_BITMAP
+ Add (new
+ cMenuEditIntItem (tr("Setup.muggle$Transparency for cover"),
+ &the_setup.ImgAlpha,1,255));
+#endif
+ // Synchronization
+ Add (new
+ cMenuEditBoolItem (tr ("Delete stale references"), &the_setup.DeleteStaleReferences,
+ tr("no"), tr("yes") ));
+
+ mgAction *a = actGenerate(actSync);
+ const char *mn = a->MenuName();
+ a->SetText(mn);
+ free(const_cast<char*>(mn));
+ Add(dynamic_cast<cOsdItem*>(a));
}
-
void
-mgMenuSetup::Store (void)
-{
- SetupStore ("InitLoopMode", the_setup.InitLoopMode);
- SetupStore ("InitShuffleMode", the_setup.InitShuffleMode);
- SetupStore ("AudioMode", the_setup.AudioMode);
- SetupStore ("DisplayMode", the_setup.DisplayMode);
- SetupStore ("BackgrMode", the_setup.BackgrMode);
- SetupStore ("TargetLevel", the_setup.TargetLevel);
- SetupStore ("LimiterLevel", the_setup.LimiterLevel);
- SetupStore ("Only48kHz", the_setup.Only48kHz);
- SetupStore ("DeleteStaleReferences", the_setup.DeleteStaleReferences);
- SetupStore ("ImageShowDuration", the_setup.ImageShowDuration);
- SetupStore ("ImageCacheDir", the_setup.ImageCacheDir);
- SetupStore ("UseStillPicture", the_setup.UseDeviceStillPicture );
+mgMenuSetup::Store (void) {
+ SetupStore ("InitLoopMode", the_setup.InitLoopMode);
+ SetupStore ("InitShuffleMode", the_setup.InitShuffleMode);
+ SetupStore ("AudioMode", the_setup.AudioMode);
+ SetupStore ("DisplayMode", the_setup.DisplayMode);
+ SetupStore ("BackgrMode", the_setup.BackgrMode);
+ SetupStore ("TargetLevel", the_setup.TargetLevel);
+ SetupStore ("LimiterLevel", the_setup.LimiterLevel);
+ SetupStore ("Only48kHz", the_setup.Only48kHz);
+ SetupStore ("DeleteStaleReferences", the_setup.DeleteStaleReferences);
+ SetupStore ("ImageShowDuration", the_setup.ImageShowDuration);
+#ifdef USE_BITMAP
+ SetupStore ("ImgAlpha", the_setup.ImgAlpha);
+#endif
+ SetupStore ("CacheDir", the_setup.CacheDir);
+ SetupStore ("ArtistFirst", the_setup.ArtistFirst);
+ SetupStore ("Jumptime", the_setup.Jumptime);
+ SetupStore ("UseStillPicture", the_setup.UseDeviceStillPicture );
}
-
diff --git a/vdr_setup.h b/vdr_setup.h
index 77f5407..32e0e01 100644
--- a/vdr_setup.h
+++ b/vdr_setup.h
@@ -25,15 +25,14 @@
#define MAX_PATH 256
-
/*!
* \brief allow user to modify setup on OSD
*/
class mgMenuSetup : public cMenuSetupPage
{
- protected:
- virtual void Store ();
- public:
- mgMenuSetup ();
+ protected:
+ virtual void Store ();
+ public:
+ mgMenuSetup ();
};
#endif
diff --git a/vdr_sound.c b/vdr_sound.c
index 7dd385f..b2e5288 100644
--- a/vdr_sound.c
+++ b/vdr_sound.c
@@ -19,96 +19,89 @@
// The resample code has been adapted from the madplay project
// (resample.c) found in the libmad distribution
+#define OUT_BITS 16 // output 16 bit samples to DVB driver
+#define OUT_FACT (OUT_BITS/8*2) // output factor is 16 bit & 2 channels -> 4 bytes
+
class mgResample
{
- private:
- mad_fixed_t ratio;
- mad_fixed_t step;
- mad_fixed_t last;
- mad_fixed_t resampled[MAX_NSAMPLES];
- public:
- bool SetInputRate (unsigned int oldrate, unsigned int newrate);
- unsigned int ResampleBlock (unsigned int nsamples, const mad_fixed_t * old);
- const mad_fixed_t *Resampled (void)
- {
- return resampled;
- }
+ private:
+ mad_fixed_t ratio;
+ mad_fixed_t step;
+ mad_fixed_t last;
+ mad_fixed_t resampled[MAX_NSAMPLES];
+ public:
+ bool SetInputRate (unsigned int oldrate, unsigned int newrate);
+ unsigned int ResampleBlock (unsigned int nsamples, const mad_fixed_t * old);
+ const mad_fixed_t *Resampled (void) {
+ return resampled;
+ }
};
-bool mgResample::SetInputRate (unsigned int oldrate, unsigned int newrate)
-{
- if (oldrate < 8000 || oldrate > newrate * 6)
- { // out of range
- esyslog ("WARNING: samplerate %d out of range 8000-%d\n", oldrate,
- newrate * 6);
- return 0;
- }
- ratio = mad_f_tofixed ((double) oldrate / (double) newrate);
- step = 0;
- last = 0;
+bool mgResample::SetInputRate (unsigned int oldrate, unsigned int newrate) {
+ if (oldrate < 8000 || oldrate > newrate * 6) {
+ // out of range
+ esyslog ("WARNING: samplerate %d out of range 8000-%d\n", oldrate,
+ newrate * 6);
+ return 0;
+ }
+ ratio = mad_f_tofixed ((double) oldrate / (double) newrate);
+ step = 0;
+ last = 0;
#ifdef DEBUG
- static mad_fixed_t
- oldratio = 0;
- if (oldratio != ratio)
- {
- printf ("mad: new resample ratio %f (from %d kHz to %d kHz)\n",
- mad_f_todouble (ratio), oldrate, newrate);
- oldratio = ratio;
- }
+ static mad_fixed_t
+ oldratio = 0;
+ if (oldratio != ratio) {
+ printf ("mad: new resample ratio %f (from %d kHz to %d kHz)\n",
+ mad_f_todouble (ratio), oldrate, newrate);
+ oldratio = ratio;
+ }
#endif
- return ratio != MAD_F_ONE;
+ return ratio != MAD_F_ONE;
}
-
unsigned int
-mgResample::ResampleBlock (unsigned int nsamples, const mad_fixed_t * old)
-{
-// This resampling algorithm is based on a linear interpolation, which is
-// not at all the best sounding but is relatively fast and efficient.
-//
-// A better algorithm would be one that implements a bandlimited
-// interpolation.
-
- mad_fixed_t *nsam = resampled;
- const mad_fixed_t *end = old + nsamples;
- const mad_fixed_t *begin = nsam;
-
- if (step < 0)
- {
- step = mad_f_fracpart (-step);
-
- while (step < MAD_F_ONE)
- {
- *nsam++ = step ? last + mad_f_mul (*old - last, step) : last;
- step += ratio;
- if (((step + 0x00000080L) & 0x0fffff00L) == 0)
- step = (step + 0x00000080L) & ~0x0fffffffL;
- }
- step -= MAD_F_ONE;
- }
-
- while (end - old > 1 + mad_f_intpart (step))
- {
- old += mad_f_intpart (step);
- step = mad_f_fracpart (step);
- *nsam++ = step ? *old + mad_f_mul (old[1] - old[0], step) : *old;
- step += ratio;
- if (((step + 0x00000080L) & 0x0fffff00L) == 0)
- step = (step + 0x00000080L) & ~0x0fffffffL;
- }
-
- if (end - old == 1 + mad_f_intpart (step))
- {
- last = end[-1];
- step = -step;
- }
- else
- step -= mad_f_fromint (end - old);
-
- return nsam - begin;
+mgResample::ResampleBlock (unsigned int nsamples, const mad_fixed_t * old) {
+ // This resampling algorithm is based on a linear interpolation, which is
+ // not at all the best sounding but is relatively fast and efficient.
+ //
+ // A better algorithm would be one that implements a bandlimited
+ // interpolation.
+
+ mad_fixed_t *nsam = resampled;
+ const mad_fixed_t *end = old + nsamples;
+ const mad_fixed_t *begin = nsam;
+
+ if (step < 0) {
+ step = mad_f_fracpart (-step);
+
+ while (step < MAD_F_ONE) {
+ *nsam++ = step ? last + mad_f_mul (*old - last, step) : last;
+ step += ratio;
+ if (((step + 0x00000080L) & 0x0fffff00L) == 0)
+ step = (step + 0x00000080L) & ~0x0fffffffL;
+ }
+ step -= MAD_F_ONE;
+ }
+
+ while (end - old > 1 + mad_f_intpart (step)) {
+ old += mad_f_intpart (step);
+ step = mad_f_fracpart (step);
+ *nsam++ = step ? *old + mad_f_mul (old[1] - old[0], step) : *old;
+ step += ratio;
+ if (((step + 0x00000080L) & 0x0fffff00L) == 0)
+ step = (step + 0x00000080L) & ~0x0fffffffL;
+ }
+
+ if (end - old == 1 + mad_f_intpart (step)) {
+ last = end[-1];
+ step = -step;
+ }
+ else
+ step -= mad_f_fromint (end - old);
+
+ return nsam - begin;
}
-
// --- mgLevel ----------------------------------------------------------------
// The normalize algorithm and parts of the code has been adapted from the
@@ -118,8 +111,8 @@ mgResample::ResampleBlock (unsigned int nsamples, const mad_fixed_t * old)
// of a wav file, in case you want to know just how your
// files are being munged:
//
-// The volumes calculated are RMS amplitudes, which corre­
-// spond (roughly) to perceived volume. Taking the RMS ampli­
+// The volumes calculated are RMS amplitudes, which corre-
+// spond (roughly) to perceived volume. Taking the RMS ampli-
// tude of an entire file would not give us quite the measure
// we want, though, because a quiet song punctuated by short
// loud parts would average out to a quiet song, and the
@@ -134,322 +127,288 @@ mgResample::ResampleBlock (unsigned int nsamples, const mad_fixed_t * old)
// power" signal varies too much to get a good measure of the
// original signal's maximum sustained power, so we run a
// smoothing algorithm over the power signal (specifically, a
-// mean filter with a window width of 100 elements). The max­
+// mean filter with a window width of 100 elements). The max-
// imum point of the smoothed power signal turns out to be a
// good measure of the maximum sustained power of the file.
-// We can then take the square root of the power to get maxi­
+// We can then take the square root of the power to get maxi-
// mum sustained RMS amplitude.
class mgLevel
{
- private:
- double maxpow;
- mad_fixed_t peak;
- struct Power
- {
-// smooth
- int npow, wpow;
- double powsum, pows[POW_WIN];
-// sum
- unsigned int nsum;
- double sum;
- } power[2];
-//
- inline void AddPower (struct Power *p, double pow);
- public:
- void Init (void);
- void GetPower (struct mad_pcm *pcm);
- double GetLevel (void);
- double GetPeak (void);
+ private:
+ double maxpow;
+ mad_fixed_t peak;
+ struct Power
+ {
+ // smooth
+ int npow, wpow;
+ double powsum, pows[POW_WIN];
+ // sum
+ unsigned int nsum;
+ double sum;
+ } power[2];
+ //
+ inline void AddPower (struct Power *p, double pow);
+ public:
+ void Init (void);
+ void GetPower (struct mad_pcm *pcm);
+ double GetLevel (void);
+ double GetPeak (void);
};
void
-mgLevel::Init (void)
-{
- for (int l = 0; l < 2; l++)
- {
- struct Power *p = &power[l];
- p->sum = p->powsum = 0.0;
- p->wpow = p->npow = p->nsum = 0;
- for (int i = POW_WIN - 1; i >= 0; i--)
- p->pows[i] = 0.0;
- }
- maxpow = 0.0;
- peak = 0;
+mgLevel::Init (void) {
+ for (int l = 0; l < 2; l++) {
+ struct Power *p = &power[l];
+ p->sum = p->powsum = 0.0;
+ p->wpow = p->npow = p->nsum = 0;
+ for (int i = POW_WIN - 1; i >= 0; i--)
+ p->pows[i] = 0.0;
+ }
+ maxpow = 0.0;
+ peak = 0;
}
-
void
-mgLevel::GetPower (struct mad_pcm *pcm)
-{
- for (int i = 0; i < pcm->channels; i++)
- {
- struct Power *p = &power[i];
- mad_fixed_t *data = pcm->samples[i];
- for (int n = pcm->length; n > 0; n--)
- {
- if (*data < -peak)
- peak = -*data;
- if (*data > peak)
- peak = *data;
- double s = mad_f_todouble (*data++);
- p->sum += (s * s);
- if (++(p->nsum) >= pcm->samplerate / 100)
- {
- AddPower (p, p->sum / (double) p->nsum);
- p->sum = 0.0;
- p->nsum = 0;
- }
- }
- }
+mgLevel::GetPower (struct mad_pcm *pcm) {
+ for (int i = 0; i < pcm->channels; i++) {
+ struct Power *p = &power[i];
+ mad_fixed_t *data = pcm->samples[i];
+ for (int n = pcm->length; n > 0; n--) {
+ if (*data < -peak)
+ peak = -*data;
+ if (*data > peak)
+ peak = *data;
+ double s = mad_f_todouble (*data++);
+ p->sum += (s * s);
+ if (++(p->nsum) >= pcm->samplerate / 100) {
+ AddPower (p, p->sum / (double) p->nsum);
+ p->sum = 0.0;
+ p->nsum = 0;
+ }
+ }
+ }
}
-
void
-mgLevel::AddPower (struct Power *p, double pow)
-{
- p->powsum += pow;
- if (p->npow >= POW_WIN)
- {
- if (p->powsum > maxpow)
- maxpow = p->powsum;
- p->powsum -= p->pows[p->wpow];
- }
- else
- p->npow++;
- p->pows[p->wpow] = pow;
- p->wpow = (p->wpow + 1) % POW_WIN;
+mgLevel::AddPower (struct Power *p, double pow) {
+ p->powsum += pow;
+ if (p->npow >= POW_WIN) {
+ if (p->powsum > maxpow)
+ maxpow = p->powsum;
+ p->powsum -= p->pows[p->wpow];
+ }
+ else
+ p->npow++;
+ p->pows[p->wpow] = pow;
+ p->wpow = (p->wpow + 1) % POW_WIN;
}
-
double
-mgLevel::GetLevel (void)
-{
- if (maxpow < EPSILON)
- {
-// Either this whole file has zero power, or was too short to ever
-// fill the smoothing buffer. In the latter case, we need to just
-// get maxpow from whatever data we did collect.
-
- if (power[0].powsum > maxpow)
- maxpow = power[0].powsum;
- if (power[1].powsum > maxpow)
- maxpow = power[1].powsum;
- }
- // adjust for the smoothing window size and root
- double level = sqrt (maxpow / (double) POW_WIN);
- printf ("norm: new volumen level=%f peak=%f\n", level,
- mad_f_todouble (peak));
- return level;
+mgLevel::GetLevel (void) {
+ if (maxpow < EPSILON) {
+ // Either this whole file has zero power, or was too short to ever
+ // fill the smoothing buffer. In the latter case, we need to just
+ // get maxpow from whatever data we did collect.
+
+ if (power[0].powsum > maxpow)
+ maxpow = power[0].powsum;
+ if (power[1].powsum > maxpow)
+ maxpow = power[1].powsum;
+ }
+ // adjust for the smoothing window size and root
+ double level = sqrt (maxpow / (double) POW_WIN);
+ printf ("norm: new volumen level=%f peak=%f\n", level,
+ mad_f_todouble (peak));
+ return level;
}
-
double
-mgLevel::GetPeak (void)
-{
- return mad_f_todouble (peak);
+mgLevel::GetPeak (void) {
+ return mad_f_todouble (peak);
}
-
// --- mgNormalize ------------------------------------------------------------
class mgNormalize
{
- private:
- mad_fixed_t gain;
- double d_limlvl, one_limlvl;
- mad_fixed_t limlvl;
- bool dogain, dolimit;
+ private:
+ mad_fixed_t gain;
+ double d_limlvl, one_limlvl;
+ mad_fixed_t limlvl;
+ bool dogain, dolimit;
#ifdef DEBUG
-// stats
- unsigned long limited, clipped, total;
- mad_fixed_t peak;
+ // stats
+ unsigned long limited, clipped, total;
+ mad_fixed_t peak;
#endif
-// limiter
+ // limiter
#ifdef USE_FAST_LIMITER
- mad_fixed_t *table, tablestart;
- int tablesize;
- inline mad_fixed_t FastLimiter (mad_fixed_t x);
+ mad_fixed_t *table, tablestart;
+ int tablesize;
+ inline mad_fixed_t FastLimiter (mad_fixed_t x);
#endif
- inline mad_fixed_t Limiter (mad_fixed_t x);
- public:
- mgNormalize (void);
- ~mgNormalize ();
- void Init (double Level, double Peak);
- void Stats (void);
- void AddGain (struct mad_pcm *pcm);
+ inline mad_fixed_t Limiter (mad_fixed_t x);
+ public:
+ mgNormalize (void);
+ ~mgNormalize ();
+ void Init (double Level, double Peak);
+ void Stats (void);
+ void AddGain (struct mad_pcm *pcm);
};
-mgNormalize::mgNormalize (void)
-{
- d_limlvl = (double) the_setup.LimiterLevel / 100.0;
- one_limlvl = 1 - d_limlvl;
- limlvl = mad_f_tofixed (d_limlvl);
- printf ("norm: lim_lev=%f lim_acc=%d\n", d_limlvl, LIM_ACC);
+mgNormalize::mgNormalize (void) {
+ d_limlvl = (double) the_setup.LimiterLevel / 100.0;
+ one_limlvl = 1 - d_limlvl;
+ limlvl = mad_f_tofixed (d_limlvl);
+ printf ("norm: lim_lev=%f lim_acc=%d\n", d_limlvl, LIM_ACC);
#ifdef USE_FAST_LIMITER
- mad_fixed_t start = limlvl & ~(F_LIM_JMP - 1);
- tablestart = start;
- tablesize = (unsigned int) (F_LIM_MAX - start) / F_LIM_JMP + 2;
- table = new mad_fixed_t[tablesize];
- if (table)
- {
- printf ("norm: table size=%d start=%08x jump=%08x\n", tablesize, start,
- F_LIM_JMP);
- for (int i = 0; i < tablesize; i++)
- {
- table[i] = Limiter (start);
- start += F_LIM_JMP;
- }
- tablesize--; // avoid a -1 in FastLimiter()
-
-// do a quick accuracy check, just to be sure that FastLimiter() is working
-// as expected :-)
+ mad_fixed_t start = limlvl & ~(F_LIM_JMP - 1);
+ tablestart = start;
+ tablesize = (unsigned int) (F_LIM_MAX - start) / F_LIM_JMP + 2;
+ table = new mad_fixed_t[tablesize];
+ if (table) {
+ printf ("norm: table size=%d start=%08x jump=%08x\n", tablesize, start,
+ F_LIM_JMP);
+ for (int i = 0; i < tablesize; i++) {
+ table[i] = Limiter (start);
+ start += F_LIM_JMP;
+ }
+ tablesize--; // avoid a -1 in FastLimiter()
+
+ // do a quick accuracy check, just to be sure that FastLimiter() is working
+ // as expected :-)
#ifdef ACC_DUMP
- FILE *out = fopen ("/tmp/limiter", "w");
+ FILE *out = fopen ("/tmp/limiter", "w");
#endif
- mad_fixed_t maxdiff = 0;
- for (mad_fixed_t x = F_LIM_MAX; x >= limlvl; x -= mad_f_tofixed (1e-4))
- {
- mad_fixed_t diff = mad_f_abs (Limiter (x) - FastLimiter (x));
- if (diff > maxdiff)
- maxdiff = diff;
+ mad_fixed_t maxdiff = 0;
+ for (mad_fixed_t x = F_LIM_MAX; x >= limlvl; x -= mad_f_tofixed (1e-4)) {
+ mad_fixed_t diff = mad_f_abs (Limiter (x) - FastLimiter (x));
+ if (diff > maxdiff)
+ maxdiff = diff;
#ifdef ACC_DUMP
- fprintf (out, "%0.10f\t%0.10f\t%0.10f\t%0.10f\t%0.10f\n",
- mad_f_todouble (x), mad_f_todouble (Limiter (x)),
- mad_f_todouble (FastLimiter (x)), mad_f_todouble (diff),
- mad_f_todouble (maxdiff));
- if (ferror (out))
- break;
+ fprintf (out, "%0.10f\t%0.10f\t%0.10f\t%0.10f\t%0.10f\n",
+ mad_f_todouble (x), mad_f_todouble (Limiter (x)),
+ mad_f_todouble (FastLimiter (x)), mad_f_todouble (diff),
+ mad_f_todouble (maxdiff));
+ if (ferror (out))
+ break;
#endif
- }
+ }
#ifdef ACC_DUMP
- fclose (out);
+ fclose (out);
#endif
- printf ("norm: accuracy %.12f\n", mad_f_todouble (maxdiff));
- if (mad_f_todouble (maxdiff) > 1e-6)
- {
- esyslog ("ERROR: accuracy check failed, normalizer disabled");
- delete table;
- table = 0;
- }
- }
- else
- esyslog ("ERROR: no memory for lookup table, normalizer disabled");
-#endif // USE_FAST_LIMITER
+ printf ("norm: accuracy %.12f\n", mad_f_todouble (maxdiff));
+ if (mad_f_todouble (maxdiff) > 1e-6) {
+ esyslog ("ERROR: accuracy check failed, normalizer disabled");
+ delete table;
+ table = 0;
+ }
+ }
+ else
+ esyslog ("ERROR: no memory for lookup table, normalizer disabled");
+#endif // USE_FAST_LIMITER
}
-
-mgNormalize::~mgNormalize ()
-{
+mgNormalize::~mgNormalize () {
#ifdef USE_FAST_LIMITER
- delete[] table;
+ delete[] table;
#endif
}
-
void
-mgNormalize::Init (double Level, double Peak)
-{
- double Target = (double) the_setup.TargetLevel / 100.0;
- double dgain = Target / Level;
- if (dgain > MAX_GAIN)
- dgain = MAX_GAIN;
- gain = mad_f_tofixed (dgain);
-// Check if we actually need to apply a gain
- dogain = (Target > 0.0 && fabs (1 - dgain) > MIN_GAIN);
+mgNormalize::Init (double Level, double Peak) {
+ double Target = (double) the_setup.TargetLevel / 100.0;
+ double dgain = Target / Level;
+ if (dgain > MAX_GAIN)
+ dgain = MAX_GAIN;
+ gain = mad_f_tofixed (dgain);
+ // Check if we actually need to apply a gain
+ dogain = (Target > 0.0 && fabs (1 - dgain) > MIN_GAIN);
#ifdef USE_FAST_LIMITER
- if (!table)
- dogain = false;
+ if (!table)
+ dogain = false;
#endif
-// Check if we actually need to do limiting:
-// we have to if limiter is enabled, if gain>1 and if the peaks will clip.
- dolimit = (d_limlvl < 1.0 && dgain > 1.0 && Peak * dgain > 1.0);
+ // Check if we actually need to do limiting:
+ // we have to if limiter is enabled, if gain>1 and if the peaks will clip.
+ dolimit = (d_limlvl < 1.0 && dgain > 1.0 && Peak * dgain > 1.0);
#ifdef DEBUG
- printf ("norm: gain=%f dogain=%d dolimit=%d (target=%f level=%f peak=%f)\n",
- dgain, dogain, dolimit, Target, Level, Peak);
- limited = clipped = total = 0;
- peak = 0;
+ printf ("norm: gain=%f dogain=%d dolimit=%d (target=%f level=%f peak=%f)\n",
+ dgain, dogain, dolimit, Target, Level, Peak);
+ limited = clipped = total = 0;
+ peak = 0;
#endif
}
-
void
-mgNormalize::Stats (void)
-{
+mgNormalize::Stats (void) {
#ifdef DEBUG
- if (total)
- printf ("norm: stats tot=%ld lim=%ld/%.3f%% clip=%ld/%.3f%% peak=%.3f\n",
- total, limited, (double) limited / total * 100.0, clipped,
- (double) clipped / total * 100.0, mad_f_todouble (peak));
+ if (total)
+ printf ("norm: stats tot=%ld lim=%ld/%.3f%% clip=%ld/%.3f%% peak=%.3f\n",
+ total, limited, (double) limited / total * 100.0, clipped,
+ (double) clipped / total * 100.0, mad_f_todouble (peak));
#endif
}
-
-mad_fixed_t mgNormalize::Limiter (mad_fixed_t x)
-{
-// Limiter function:
-//
-// / x (for x <= lev)
-// x' = |
-// \ tanh((x - lev) / (1-lev)) * (1-lev) + lev (for x > lev)
-//
-// call only with x>=0. For negative samples, preserve sign outside this function
-//
-// With limiter level = 0, this is equivalent to a tanh() function;
-// with limiter level = 1, this is equivalent to clipping.
-
- if (x > limlvl)
- {
+mad_fixed_t mgNormalize::Limiter (mad_fixed_t x) {
+ // Limiter function:
+ //
+ // / x (for x <= lev)
+ // x' = |
+ // \ tanh((x - lev) / (1-lev)) * (1-lev) + lev (for x > lev)
+ //
+ // call only with x>=0. For negative samples, preserve sign outside this function
+ //
+ // With limiter level = 0, this is equivalent to a tanh() function;
+ // with limiter level = 1, this is equivalent to clipping.
+
+ if (x > limlvl) {
#ifdef DEBUG
- if (x > MAD_F_ONE)
- clipped++;
- limited++;
+ if (x > MAD_F_ONE)
+ clipped++;
+ limited++;
#endif
- x =
- mad_f_tofixed (tanh ((mad_f_todouble (x) - d_limlvl) / one_limlvl) *
- one_limlvl + d_limlvl);
- }
- return x;
+ x =
+ mad_f_tofixed (tanh ((mad_f_todouble (x) - d_limlvl) / one_limlvl) *
+ one_limlvl + d_limlvl);
+ }
+ return x;
}
-
#ifdef USE_FAST_LIMITER
-mad_fixed_t mgNormalize::FastLimiter (mad_fixed_t x)
-{
-// The fast algorithm is based on a linear interpolation between the
-// the values in the lookup table. Relays heavly on libmads fixed point format.
+mad_fixed_t mgNormalize::FastLimiter (mad_fixed_t x) {
+ // The fast algorithm is based on a linear interpolation between the
+ // the values in the lookup table. Relays heavly on libmads fixed point format.
- if (x > limlvl)
- {
- int
- i = (unsigned int) (x - tablestart) / F_LIM_JMP;
+ if (x > limlvl) {
+ int
+ i = (unsigned int) (x - tablestart) / F_LIM_JMP;
#ifdef DEBUG
- if (x > MAD_F_ONE)
- clipped++;
- limited++;
- if (i >= tablesize)
- printf ("norm: overflow x=%f x-ts=%f i=%d tsize=%d\n",
- mad_f_todouble (x), mad_f_todouble (x - tablestart), i,
- tablesize);
+ if (x > MAD_F_ONE)
+ clipped++;
+ limited++;
+ if (i >= tablesize)
+ printf ("norm: overflow x=%f x-ts=%f i=%d tsize=%d\n",
+ mad_f_todouble (x), mad_f_todouble (x - tablestart), i,
+ tablesize);
#endif
- mad_fixed_t
- r = x & (F_LIM_JMP - 1);
- x = MAD_F_ONE;
- if (i < tablesize)
- {
- mad_fixed_t *
- ptr = &table[i];
- x = *ptr;
- mad_fixed_t
- d = *(ptr + 1) - x;
-//x+=mad_f_mul(d,r)<<LIM_ACC; // this is not accurate as mad_f_mul() does >>MAD_F_FRACBITS
-// which is senseless in the case of following <<LIM_ACC.
- // better, don't know if works on all machines
- x += ((long long) d * (long long) r) >> LIM_SHIFT;
- }
- }
- return x;
+ mad_fixed_t
+ r = x & (F_LIM_JMP - 1);
+ x = MAD_F_ONE;
+ if (i < tablesize) {
+ mad_fixed_t *
+ ptr = &table[i];
+ x = *ptr;
+ mad_fixed_t
+ d = *(ptr + 1) - x;
+ //x+=mad_f_mul(d,r)<<LIM_ACC; // this is not accurate as mad_f_mul() does >>MAD_F_FRACBITS
+ // which is senseless in the case of following <<LIM_ACC.
+ // better, don't know if works on all machines
+ x += ((long long) d * (long long) r) >> LIM_SHIFT;
+ }
+ }
+ return x;
}
#endif
@@ -460,65 +419,56 @@ mad_fixed_t mgNormalize::FastLimiter (mad_fixed_t x)
#endif
void
-mgNormalize::AddGain (struct mad_pcm *pcm)
-{
- if (dogain)
- {
- for (int i = 0; i < pcm->channels; i++)
- {
- mad_fixed_t *data = pcm->samples[i];
+mgNormalize::AddGain (struct mad_pcm *pcm) {
+ if (dogain) {
+ for (int i = 0; i < pcm->channels; i++) {
+ mad_fixed_t *data = pcm->samples[i];
#ifdef DEBUG
- total += pcm->length;
+ total += pcm->length;
#endif
- if (dolimit)
- {
- for (int n = pcm->length; n > 0; n--)
- {
- mad_fixed_t s = mad_f_mul (*data, gain);
- if (s < 0)
- {
- s = -s;
+ if (dolimit) {
+ for (int n = pcm->length; n > 0; n--) {
+ mad_fixed_t s = mad_f_mul (*data, gain);
+ if (s < 0) {
+ s = -s;
#ifdef DEBUG
- if (s > peak)
- peak = s;
+ if (s > peak)
+ peak = s;
#endif
- s = LIMITER_FUNC (s);
- s = -s;
- }
- else
- {
+ s = LIMITER_FUNC (s);
+ s = -s;
+ }
+ else {
#ifdef DEBUG
- if (s > peak)
- peak = s;
+ if (s > peak)
+ peak = s;
#endif
- s = LIMITER_FUNC (s);
- }
- *data++ = s;
- }
- }
- else
- {
- for (int n = pcm->length; n > 0; n--)
- {
- mad_fixed_t s = mad_f_mul (*data, gain);
+ s = LIMITER_FUNC (s);
+ }
+ *data++ = s;
+ }
+ }
+ else {
+ for (int n = pcm->length; n > 0; n--) {
+ mad_fixed_t s = mad_f_mul (*data, gain);
#ifdef DEBUG
- if (s > peak)
- peak = s;
- else if (-s > peak)
- peak = -s;
+ if (s > peak)
+ peak = s;
+ else if (-s > peak)
+ peak = -s;
#endif
- if (s > MAD_F_ONE)
- s = MAD_F_ONE; // do clipping
- if (s < -MAD_F_ONE)
- s = -MAD_F_ONE;
- *data++ = s;
- }
- }
- }
- }
+ if (s > MAD_F_ONE)
+ // do clipping
+ s = MAD_F_ONE;
+ if (s < -MAD_F_ONE)
+ s = -MAD_F_ONE;
+ *data++ = s;
+ }
+ }
+ }
+ }
}
-
// --- mgScale ----------------------------------------------------------------
// The dither code has been adapted from the madplay project
@@ -529,225 +479,199 @@ enum eAudioMode
class mgScale
{
- private:
- enum
- { MIN = -MAD_F_ONE, MAX = MAD_F_ONE - 1 };
+ private:
+ enum
+ { MIN = -MAD_F_ONE, MAX = MAD_F_ONE - 1 };
#ifdef DEBUG
-// audio stats
- unsigned long clipped_samples;
- mad_fixed_t peak_clipping;
- mad_fixed_t peak_sample;
+ // audio stats
+ unsigned long clipped_samples;
+ mad_fixed_t peak_clipping;
+ mad_fixed_t peak_sample;
#endif
-// dither
- struct dither
- {
- mad_fixed_t error[3];
- mad_fixed_t random;
- } leftD, rightD;
-//
- inline mad_fixed_t Clip (mad_fixed_t sample, bool stats = true);
- inline signed long LinearRound (mad_fixed_t sample);
- inline unsigned long Prng (unsigned long state);
- inline signed long LinearDither (mad_fixed_t sample, struct dither *dither);
- public:
- mgScale();
- void Stats (void);
- unsigned int ScaleBlock (unsigned char *data, unsigned int size,
- unsigned int &nsamples, const mad_fixed_t * &left,
- const mad_fixed_t * &right, eAudioMode mode);
+ // dither
+ struct dither
+ {
+ mad_fixed_t error[3];
+ mad_fixed_t random;
+ } leftD, rightD;
+ //
+ inline mad_fixed_t Clip (mad_fixed_t sample, bool stats = true);
+ inline signed long LinearRound (mad_fixed_t sample);
+ inline unsigned long Prng (unsigned long state);
+ inline signed long LinearDither (mad_fixed_t sample, struct dither *dither);
+ public:
+ mgScale();
+ void Stats (void);
+ unsigned int ScaleBlock (unsigned char *data, unsigned int size,
+ unsigned int &nsamples, const mad_fixed_t * &left,
+ const mad_fixed_t * &right, eAudioMode mode);
};
-mgScale::mgScale()
-{
+mgScale::mgScale() {
#ifdef DEBUG
- clipped_samples = 0;
- peak_clipping = peak_sample = 0;
+ clipped_samples = 0;
+ peak_clipping = peak_sample = 0;
#endif
- memset (&leftD, 0, sizeof (leftD));
- memset (&rightD, 0, sizeof (rightD));
+ memset (&leftD, 0, sizeof (leftD));
+ memset (&rightD, 0, sizeof (rightD));
}
-
void
-mgScale::Stats (void)
-{
+mgScale::Stats (void) {
#ifdef DEBUG
- printf ("mp3: scale stats clipped=%ld peak_clip=%f peak=%f\n",
- clipped_samples, mad_f_todouble (peak_clipping),
- mad_f_todouble (peak_sample));
+ printf ("mp3: scale stats clipped=%ld peak_clip=%f peak=%f\n",
+ clipped_samples, mad_f_todouble (peak_clipping),
+ mad_f_todouble (peak_sample));
#endif
}
-
// gather signal statistics while clipping
-mad_fixed_t mgScale::Clip (mad_fixed_t sample, bool stats)
-{
+mad_fixed_t mgScale::Clip (mad_fixed_t sample, bool stats) {
#ifndef DEBUG
- if (sample > MAX)
- sample = MAX;
- if (sample < MIN)
- sample = MIN;
+ if (sample > MAX)
+ sample = MAX;
+ if (sample < MIN)
+ sample = MIN;
#else
- if (!stats)
- {
- if (sample > MAX)
- sample = MAX;
- if (sample < MIN)
- sample = MIN;
- }
- else
- {
- if (sample >= peak_sample)
- {
- if (sample > MAX)
- {
- ++clipped_samples;
- if (sample - MAX > peak_clipping)
- peak_clipping = sample - MAX;
- sample = MAX;
- }
- peak_sample = sample;
- }
- else if (sample < -peak_sample)
- {
- if (sample < MIN)
- {
- ++clipped_samples;
- if (MIN - sample > peak_clipping)
- peak_clipping = MIN - sample;
- sample = MIN;
- }
- peak_sample = -sample;
- }
- }
+ if (!stats) {
+ if (sample > MAX)
+ sample = MAX;
+ if (sample < MIN)
+ sample = MIN;
+ }
+ else {
+ if (sample >= peak_sample) {
+ if (sample > MAX) {
+ ++clipped_samples;
+ if (sample - MAX > peak_clipping)
+ peak_clipping = sample - MAX;
+ sample = MAX;
+ }
+ peak_sample = sample;
+ }
+ else if (sample < -peak_sample) {
+ if (sample < MIN) {
+ ++clipped_samples;
+ if (MIN - sample > peak_clipping)
+ peak_clipping = MIN - sample;
+ sample = MIN;
+ }
+ peak_sample = -sample;
+ }
+ }
#endif
- return sample;
+ return sample;
}
-
// generic linear sample quantize routine
signed long
-mgScale::LinearRound (mad_fixed_t sample)
-{
-// round
- sample += (1L << (MAD_F_FRACBITS - OUT_BITS));
-// clip
- sample = Clip (sample);
-// quantize and scale
- return sample >> (MAD_F_FRACBITS + 1 - OUT_BITS);
+mgScale::LinearRound (mad_fixed_t sample) {
+ // round
+ sample += (1L << (MAD_F_FRACBITS - OUT_BITS));
+ // clip
+ sample = Clip (sample);
+ // quantize and scale
+ return sample >> (MAD_F_FRACBITS + 1 - OUT_BITS);
}
-
// 32-bit pseudo-random number generator
unsigned long
-mgScale::Prng (unsigned long state)
-{
- return (state * 0x0019660dL + 0x3c6ef35fL) & 0xffffffffL;
+mgScale::Prng (unsigned long state) {
+ return (state * 0x0019660dL + 0x3c6ef35fL) & 0xffffffffL;
}
-
// generic linear sample quantize and dither routine
signed long
-mgScale::LinearDither (mad_fixed_t sample, struct dither *dither)
-{
- unsigned int scalebits;
- mad_fixed_t output, mask, random;
-
-// noise shape
- sample += dither->error[0] - dither->error[1] + dither->error[2];
- dither->error[2] = dither->error[1];
- dither->error[1] = dither->error[0] / 2;
-// bias
- output = sample + (1L << (MAD_F_FRACBITS + 1 - OUT_BITS - 1));
- scalebits = MAD_F_FRACBITS + 1 - OUT_BITS;
- mask = (1L << scalebits) - 1;
-// dither
- random = Prng (dither->random);
- output += (random & mask) - (dither->random & mask);
- dither->random = random;
-// clip
- output = Clip (output);
- sample = Clip (sample, false);
-// quantize
- output &= ~mask;
-// error feedback
- dither->error[0] = sample - output;
-// scale
- return output >> scalebits;
+mgScale::LinearDither (mad_fixed_t sample, struct dither *dither) {
+ unsigned int scalebits;
+ mad_fixed_t output, mask, random;
+
+ // noise shape
+ sample += dither->error[0] - dither->error[1] + dither->error[2];
+ dither->error[2] = dither->error[1];
+ dither->error[1] = dither->error[0] / 2;
+ // bias
+ output = sample + (1L << (MAD_F_FRACBITS + 1 - OUT_BITS - 1));
+ scalebits = MAD_F_FRACBITS + 1 - OUT_BITS;
+ mask = (1L << scalebits) - 1;
+ // dither
+ random = Prng (dither->random);
+ output += (random & mask) - (dither->random & mask);
+ dither->random = random;
+ // clip
+ output = Clip (output);
+ sample = Clip (sample, false);
+ // quantize
+ output &= ~mask;
+ // error feedback
+ dither->error[0] = sample - output;
+ // scale
+ return output >> scalebits;
}
-
// write a block of signed 16-bit big-endian PCM samples
unsigned int
mgScale::ScaleBlock (unsigned char *data, unsigned int size,
unsigned int &nsamples, const mad_fixed_t * &left,
-const mad_fixed_t * &right, eAudioMode mode)
-{
- signed int sample;
- unsigned int len, res;
-
- len = size / OUT_FACT;
- res = size;
- if (len > nsamples)
- {
- len = nsamples;
- res = len * OUT_FACT;
- }
- nsamples -= len;
-
- if (right)
- { // stereo
- switch (mode)
- {
- case amRound:
- while (len--)
- {
- sample = LinearRound (*left++);
- *data++ = sample >> 8;
- *data++ = sample >> 0;
- sample = LinearRound (*right++);
- *data++ = sample >> 8;
- *data++ = sample >> 0;
- }
- break;
- case amDither:
- while (len--)
- {
- sample = LinearDither (*left++, &leftD);
- *data++ = sample >> 8;
- *data++ = sample >> 0;
- sample = LinearDither (*right++, &rightD);
- *data++ = sample >> 8;
- *data++ = sample >> 0;
- }
- break;
- }
- }
- else
- { // mono, duplicate left channel
- switch (mode)
- {
- case amRound:
- while (len--)
- {
- sample = LinearRound (*left++);
- *data++ = sample >> 8;
- *data++ = sample >> 0;
- *data++ = sample >> 8;
- *data++ = sample >> 0;
- }
- break;
- case amDither:
- while (len--)
- {
- sample = LinearDither (*left++, &leftD);
- *data++ = sample >> 8;
- *data++ = sample >> 0;
- *data++ = sample >> 8;
- *data++ = sample >> 0;
- }
- break;
- }
- }
- return res;
+const mad_fixed_t * &right, eAudioMode mode) {
+ signed int sample;
+ unsigned int len, res;
+
+ len = size / OUT_FACT;
+ res = size;
+ if (len > nsamples) {
+ len = nsamples;
+ res = len * OUT_FACT;
+ }
+ nsamples -= len;
+
+ if (right) {
+ // stereo
+ switch (mode) {
+ case amRound:
+ while (len--) {
+ sample = LinearRound (*left++);
+ *data++ = sample >> 8;
+ *data++ = sample >> 0;
+ sample = LinearRound (*right++);
+ *data++ = sample >> 8;
+ *data++ = sample >> 0;
+ }
+ break;
+ case amDither:
+ while (len--) {
+ sample = LinearDither (*left++, &leftD);
+ *data++ = sample >> 8;
+ *data++ = sample >> 0;
+ sample = LinearDither (*right++, &rightD);
+ *data++ = sample >> 8;
+ *data++ = sample >> 0;
+ }
+ break;
+ }
+ }
+ else {
+ // mono, duplicate left channel
+ switch (mode) {
+ case amRound:
+ while (len--) {
+ sample = LinearRound (*left++);
+ *data++ = sample >> 8;
+ *data++ = sample >> 0;
+ *data++ = sample >> 8;
+ *data++ = sample >> 0;
+ }
+ break;
+ case amDither:
+ while (len--) {
+ sample = LinearDither (*left++, &leftD);
+ *data++ = sample >> 8;
+ *data++ = sample >> 0;
+ *data++ = sample >> 8;
+ *data++ = sample >> 0;
+ }
+ break;
+ }
+ }
+ return res;
}
diff --git a/vdr_stream.c b/vdr_stream.c
index 8ec6caf..d083ba3 100644
--- a/vdr_stream.c
+++ b/vdr_stream.c
@@ -24,7 +24,6 @@
#include <vdr/interface.h>
-// #include "setup-mp3.h"
#include "vdr_stream.h"
#include "vdr_network.h"
#include "vdr_config.h"
@@ -37,315 +36,264 @@
#include <sys/mman.h>
#endif
-#define DEFAULT_PORT 80 // default port for streaming (HTTP)
+#define DEFAULT_PORT 80 // default port for streaming (HTTP)
// --- mgStream -----------------------------------------------------------------
-mgStream::mgStream (std::string filename):m_filename (filename)
-{
- m_fd = -1;
- m_ismmap = false;
- m_buffer = 0;
+mgStream::mgStream (std::string filename):m_filename (filename) {
+ m_fd = -1;
+ m_ismmap = false;
+ m_buffer = 0;
}
-
-mgStream::~mgStream ()
-{
- close ();
+mgStream::~mgStream () {
+ close ();
}
-
-bool mgStream::open (bool log)
-{
- if (m_fd >= 0)
- {
- return seek ();
- }
-
-// just check, whether file exists?
- if (fileinfo (log))
- {
-// printf ("mgStream::open: fileinfo == true\n");
-
- if ((m_fd =::open (m_filename.c_str (), O_RDONLY)) >= 0)
- {
- //printf ("mgStream::open: file opened\n");
-
- m_buffpos = m_readpos = 0;
- m_fill = 0;
-
- //printf ("mgStream::open: buffpos, readpos, fill set\n");
-
-/*
- #ifdef USE_MMAP
- if( m_filesize <= MAX_MMAP_SIZE )
- {
- m_buffer = (unsigned char*)mmap( 0, m_filesize, PROT_READ,
- MAP_SHARED, m_fd, 0 );
- if( m_buffer != MAP_FAILED )
- {
- m_ismmap = true;
- return true;
- }
-else
-{
-dsyslog("mmap() failed for %s: %s", m_filename.c_str(), strerror(errno) );
-}
-}
-#endif
-*/
- //printf ("mgStream::open: allocating buffer: %d\n", MP3FILE_BUFSIZE);
- m_buffer = new unsigned char[MP3FILE_BUFSIZE];
- //printf ("mgStream::open: buffer allocated\n");
-
- if (m_buffer)
- {
- //printf ("mgStream::open: buffer allocated, returning true\n");
-
- return true;
- }
- else
- {
- esyslog ("ERROR: not enough memory for buffer: %s",
- m_filename.c_str ());
- }
- }
- else
- {
- if (log)
- {
- esyslog ("ERROR: failed to open file %s: %s",
- m_filename.c_str (), strerror (errno));
- }
- }
- }
-
- close ();
- //printf ("mgStream::open: returning false\n");
- return false;
+bool mgStream::open (bool log) {
+ if (m_fd >= 0) {
+ return seek ();
+ }
+
+ // just check, whether file exists?
+ if (fileinfo (log)) {
+ // printf ("mgStream::open: fileinfo == true\n");
+
+ if ((m_fd =::open (m_filename.c_str (), O_RDONLY)) >= 0) {
+ //printf ("mgStream::open: file opened\n");
+
+ m_buffpos = m_readpos = 0;
+ m_fill = 0;
+
+ //printf ("mgStream::open: buffpos, readpos, fill set\n");
+
+ /*
+ #ifdef USE_MMAP
+ if( m_filesize <= MAX_MMAP_SIZE )
+ {
+ m_buffer = (unsigned char*)mmap( 0, m_filesize, PROT_READ,
+ MAP_SHARED, m_fd, 0 );
+ if( m_buffer != MAP_FAILED )
+ {
+ m_ismmap = true;
+ return true;
+ }
+ else
+ {
+ dsyslog("mmap() failed for %s: %s", m_filename.c_str(), strerror(errno) );
+ }
+ }
+ #endif
+ */
+ //printf ("mgStream::open: allocating buffer: %d\n", MP3FILE_BUFSIZE);
+ m_buffer = new unsigned char[MP3FILE_BUFSIZE];
+ //printf ("mgStream::open: buffer allocated\n");
+
+ if (m_buffer) {
+ //printf ("mgStream::open: buffer allocated, returning true\n");
+
+ return true;
+ }
+ else {
+ esyslog ("ERROR: not enough memory for buffer: %s",
+ m_filename.c_str ());
+ }
+ }
+ else {
+ if (log) {
+ esyslog ("ERROR: failed to open file %s: %s",
+ m_filename.c_str (), strerror (errno));
+ }
+ }
+ }
+
+ close ();
+ //printf ("mgStream::open: returning false\n");
+ return false;
}
-
void
-mgStream::close (void)
-{
+mgStream::close (void) {
#ifdef USE_MMAP
- if (m_ismmap)
- {
- munmap (m_buffer, m_filesize);
- m_buffer = 0;
- m_ismmap = false;
- }
- else
- {
+ if (m_ismmap) {
+ munmap (m_buffer, m_filesize);
+ m_buffer = 0;
+ m_ismmap = false;
+ }
+ else {
#endif
- delete[] m_buffer;
- m_buffer = 0;
+ delete[] m_buffer;
+ m_buffer = 0;
#ifdef USE_MMAP
- }
+ }
#endif
- if (m_fd >= 0)
- {
- ::close (m_fd);
- m_fd = -1;
- }
+ if (m_fd >= 0) {
+ ::close (m_fd);
+ m_fd = -1;
+ }
}
-
-bool mgStream::seek (unsigned long long pos)
-{
- //printf ("mgStream::seek\n");
- if (m_fd >= 0 && pos >= 0 && pos <= m_filesize)
- {
- //printf ("mgStream::seek valid file and position detected\n");
-
- m_buffpos = 0;
- m_fill = 0;
-
- if (m_ismmap)
- {
- m_readpos = pos;
-
- //printf ("mgStream::seek: returning true\n");
- return true;
- }
- else
- {
- if ((m_readpos = lseek64 (m_fd, pos, SEEK_SET)) >= 0)
- {
- if (m_readpos != pos)
- {
- dsyslog ("seek mismatch in %s, wanted %lld, got %lld",
- m_filename.c_str (), pos, m_readpos);
- }
- //printf ("mgStream::seek: returning true\n");
- return true;
- }
- else
- {
- esyslog ("ERROR: seeking failed in %s: %d,%s",
- m_filename.c_str (), errno, strerror (errno));
- }
- }
- }
- else
- {
- //printf ("mp3: bad seek call fd=%d pos=%lld name=%s\n", m_fd, pos,
- //m_filename.c_str ());
- }
-
- //printf ("mgStream::seek: returning false\n");
- return false;
+bool mgStream::seek (unsigned long long pos) {
+ //printf ("mgStream::seek\n");
+ if (m_fd >= 0 && pos >= 0 && pos <= m_filesize) {
+ //printf ("mgStream::seek valid file and position detected\n");
+
+ m_buffpos = 0;
+ m_fill = 0;
+
+ if (m_ismmap) {
+ m_readpos = pos;
+
+ //printf ("mgStream::seek: returning true\n");
+ return true;
+ }
+ else {
+ if ((m_readpos = lseek64 (m_fd, pos, SEEK_SET)) >= 0) {
+ if (m_readpos != pos) {
+ dsyslog ("seek mismatch in %s, wanted %lld, got %lld",
+ m_filename.c_str (), pos, m_readpos);
+ }
+ //printf ("mgStream::seek: returning true\n");
+ return true;
+ }
+ else {
+ esyslog ("ERROR: seeking failed in %s: %d,%s",
+ m_filename.c_str (), errno, strerror (errno));
+ }
+ }
+ }
+ else {
+ //printf ("mp3: bad seek call fd=%d pos=%lld name=%s\n", m_fd, pos,
+ //m_filename.c_str ());
+ }
+
+ //printf ("mgStream::seek: returning false\n");
+ return false;
}
-
bool
mgStream::stream (unsigned char *&data,
-unsigned long &len, const unsigned char *rest)
-{
- if (m_fd >= 0)
- {
- if (m_readpos < m_filesize)
- {
- if (m_ismmap)
- {
- if (rest && m_fill)
- {
- m_readpos = (rest - m_buffer);// take care of remaining data
- }
- m_fill = m_filesize - m_readpos;
- data = m_buffer + m_readpos;
- len = m_fill;
- m_buffpos = m_readpos;
- m_readpos += m_fill;
-
- return true;
- }
- else
- {
- if (rest && m_fill)
- { // copy remaining data to start of buffer
- m_fill -= (rest - m_buffer); // remaing bytes
- memmove (m_buffer, rest, m_fill);
- }
- else
- {
- m_fill = 0;
- }
-
- int r;
- do
- {
- r = read (m_fd, m_buffer + m_fill,
- MP3FILE_BUFSIZE - m_fill);
- }
- while (r == -1 && errno == EINTR);
-
- if (r >= 0)
- {
- m_buffpos = m_readpos - m_fill;
- m_readpos += r;
- m_fill += r;
- data = m_buffer;
- len = m_fill;
-
- return true;
- }
- else
- {
- esyslog ("ERROR: read failed in %s: %d,%s",
- m_filename.c_str (), errno, strerror (errno));
- }
- }
- }
- else
- {
- len = 0;
- return true;
- }
- }
- return false;
+unsigned long &len, const unsigned char *rest) {
+ if (m_fd >= 0) {
+ if (m_readpos < m_filesize) {
+ if (m_ismmap) {
+ if (rest && m_fill) {
+ // take care of remaining data
+ m_readpos = (rest - m_buffer);
+ }
+ m_fill = m_filesize - m_readpos;
+ data = m_buffer + m_readpos;
+ len = m_fill;
+ m_buffpos = m_readpos;
+ m_readpos += m_fill;
+
+ return true;
+ }
+ else {
+ if (rest && m_fill) {
+ // copy remaining data to start of buffer
+ // remaing bytes
+ m_fill -= (rest - m_buffer);
+ memmove (m_buffer, rest, m_fill);
+ }
+ else {
+ m_fill = 0;
+ }
+
+ int r;
+ do {
+ r = read (m_fd, m_buffer + m_fill,
+ MP3FILE_BUFSIZE - m_fill);
+ }
+ while (r == -1 && errno == EINTR);
+
+ if (r >= 0) {
+ m_buffpos = m_readpos - m_fill;
+ m_readpos += r;
+ m_fill += r;
+ data = m_buffer;
+ len = m_fill;
+
+ return true;
+ }
+ else {
+ esyslog ("ERROR: read failed in %s: %d,%s",
+ m_filename.c_str (), errno, strerror (errno));
+ }
+ }
+ }
+ else {
+ len = 0;
+ return true;
+ }
+ }
+ return false;
}
-
-bool mgStream::removable ()
-{
-// we do not handle removable media at this time
- return false;
+bool mgStream::removable () {
+ // we do not handle removable media at this time
+ return false;
}
-
-bool mgStream::fileinfo (bool log)
-{
- struct stat64
- ds;
-
- if (!stat64 (m_filename.c_str (), &ds))
- {
- //printf ("mgStream::fileinfo: stat64 == 0\n");
-
- if (S_ISREG (ds.st_mode))
- {
- m_fsID = "";
- m_fsType = 0;
-
- struct statfs64
- sfs;
-
- if (!statfs64 (m_filename.c_str (), &sfs))
- {
- if (removable ())
- {
- char *
- tmpbuf;
- msprintf (&tmpbuf, "%llx:%llx", sfs.f_blocks, sfs.f_files);
- m_fsID = tmpbuf;
- free (tmpbuf);
- }
- m_fsType = sfs.f_type;
- }
- else
- {
- if (errno != ENOSYS && log)
- {
- esyslog ("ERROR: can't statfs %s: %s", m_filename.c_str (),
- strerror (errno));
- }
- }
-
- m_filesize = ds.st_size;
- m_ctime = ds.st_ctime;
+bool mgStream::fileinfo (bool log) {
+ struct stat64
+ ds;
+
+ if (!stat64 (m_filename.c_str (), &ds)) {
+ //printf ("mgStream::fileinfo: stat64 == 0\n");
+
+ if (S_ISREG (ds.st_mode)) {
+ m_fsID = "";
+ m_fsType = 0;
+
+ struct statfs64
+ sfs;
+
+ if (!statfs64 (m_filename.c_str (), &sfs)) {
+ if (removable ()) {
+ char *
+ tmpbuf;
+ msprintf (&tmpbuf, "%llx:%llx", sfs.f_blocks, sfs.f_files);
+ m_fsID = tmpbuf;
+ free (tmpbuf);
+ }
+ m_fsType = sfs.f_type;
+ }
+ else {
+ if (errno != ENOSYS && log) {
+ esyslog ("ERROR: can't statfs %s: %s", m_filename.c_str (),
+ strerror (errno));
+ }
+ }
+
+ m_filesize = ds.st_size;
+ m_ctime = ds.st_ctime;
#ifdef CDFS_MAGIC
- if (m_fsType == CDFS_MAGIC)
- {
- m_ctime = 0; // CDFS returns mount time as ctime
- }
+ if (m_fsType == CDFS_MAGIC) {
+ m_ctime = 0; // CDFS returns mount time as ctime
+ }
#endif
-// infodone tells that info has been read, like a cache flag
-// InfoDone();
- return true;
- }
- else
- {
- if (log)
- {
- esyslog ("ERROR: %s is not a regular file",
- m_filename.c_str ());
- }
- }
- }
- else
- {
- if (log)
- {
- esyslog ("ERROR: can't stat %s: %s", m_filename.c_str (),
- strerror (errno));
- }
-
- //printf ("mgStream::fileinfo: stat64 != 0 for %s\n",
- // m_filename.c_str ());
- }
-
- return false;
+ // infodone tells that info has been read, like a cache flag
+ // InfoDone();
+ return true;
+ }
+ else {
+ if (log) {
+ esyslog ("ERROR: %s is not a regular file",
+ m_filename.c_str ());
+ }
+ }
+ }
+ else {
+ if (log) {
+ esyslog ("ERROR: can't stat %s: %s", m_filename.c_str (),
+ strerror (errno));
+ }
+
+ //printf ("mgStream::fileinfo: stat64 != 0 for %s\n",
+ // m_filename.c_str ());
+ }
+
+ return false;
}
diff --git a/vdr_stream.h b/vdr_stream.h
index 6b1769c..b5fd808 100644
--- a/vdr_stream.h
+++ b/vdr_stream.h
@@ -25,36 +25,35 @@ class cNet;
// ----------------------------------------------------------------
-class mgStream // : public mgFileInfo
+class mgStream // : public mgFileInfo
{
- private:
- int m_fd;
- bool m_ismmap;
-
-// from cFileInfo
- std::string m_filename, m_fsID;
- unsigned long long m_filesize;
- time_t m_ctime;
- long m_fsType;
-
- bool fileinfo (bool log);
- bool removable ();
-
- protected:
- unsigned char *m_buffer;
- unsigned long long m_readpos, m_buffpos;
- unsigned long m_fill;
- public:
- mgStream (std::string filename);
- virtual ~ mgStream ();
- virtual bool open (bool log = true);
- virtual void close ();
- virtual bool stream (unsigned char *&data, unsigned long &len,
- const unsigned char *rest = NULL);
- virtual bool seek (unsigned long long pos = 0);
- virtual unsigned long long bufferPos ()
- {
- return m_buffpos;
- }
+ private:
+ int m_fd;
+ bool m_ismmap;
+
+ // from cFileInfo
+ std::string m_filename, m_fsID;
+ unsigned long long m_filesize;
+ time_t m_ctime;
+ long m_fsType;
+
+ bool fileinfo (bool log);
+ bool removable ();
+
+ protected:
+ unsigned char *m_buffer;
+ unsigned long long m_readpos, m_buffpos;
+ unsigned long m_fill;
+ public:
+ mgStream (std::string filename);
+ virtual ~ mgStream ();
+ virtual bool open (bool log = true);
+ virtual void close ();
+ virtual bool stream (unsigned char *&data, unsigned long &len,
+ const unsigned char *rest = NULL);
+ virtual bool seek (unsigned long long pos = 0);
+ virtual unsigned long long bufferPos () {
+ return m_buffpos;
+ }
};
-#endif //___STREAM_H
+#endif //___STREAM_H
diff --git a/version.h b/version.h
new file mode 100644
index 0000000..4be66b8
--- /dev/null
+++ b/version.h
@@ -0,0 +1,27 @@
+/*
+ * MP3/MPlayer plugin to VDR (C++)
+ *
+ * (C) 2001-2005 Stefan Huelswitt <s.huelswitt@gmx.de>
+ *
+ * This code 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 code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef ___VERSION_H
+#define ___VERSION_H
+
+#define PLUGIN_NAME "VDR-music"
+#define PLUGIN_VERSION "0.2.0"
+#endif //___VERSION_H